Forum: Mikrocontroller und Digitale Elektronik Interrupt ATtiny45


von Paul (Gast)


Lesenswert?

Hallo,

ich habe nun seit geraumer Zeit mich mit C beschäftigt, etliche Bücher 
und Forumbeiträge gelesen und natürlich viel ausprobiert.

Nun wollte ich mal ein wenig was umsetzen und scheitere leider an meinen 
Code, vermutlich eine Kleinigkeit und ich bekomme das mit eure Hilfe 
gebacken. Wichtig für mich ist aber auch das ich das dann verstanden 
habe :)


Eigentlich ganz einfache Aufgabe:
Watchdog aktivieren und schlafen legen, nach ca. 16 Sekunden aufwachen 
eine Ledgruppe hoch und einmal runter Faden und wieder schlafen und das 
immer so weiter.

Problematik: Die leds sind anfangs sofort an, obwohl sie mit OCR0A=255 
aus sein müssten.
Die leds faden zwar aber pausieren nie wirklich, als ob sie in einer 
Endlosschleife sind, dabei setze ich doch den status=0 ?

Wo liegt mein Denkfehler?
Bin um jede Hilfe dankbar.


1
/*
2
 * main.c
3
  *           RST-|    |-VCC
4
  *     *OC1B,PB3-|    |-PB2, SCK
5
  *      OC1B,PB4-|    |-PB1, MISO, OC0B, OC1A
6
  *     GND-|    |-PB0, MOSI, OC0A, OC1A*
7
  *
8
  *
9
 * Created: 14.04.2019 16:46:46
10
 * Author : Paul
11
 */ 
12
13
/* DEFINES **************************************/
14
#define F_CPU 1000000L
15
#define byte uint8_t
16
#define ledgroup1 OCR0A
17
#define ledgroup2 OCR0B
18
19
/* INCLUDES *************************************/
20
#include <avr/io.h>
21
#include <util/delay.h>
22
#include <avr/sleep.h>
23
#include <avr/interrupt.h>
24
25
/* VARIABLE *************************************/
26
volatile byte inkr= 0;
27
volatile byte status= 0;
28
29
/* FUNCTIONs ************************************/
30
void PWM_Init()
31
{
32
  TCCR0A |= (1<<COM0A1) | (1<<COM0B1);  // Set in Match
33
  TCCR0A |= (1<<WGM00) | (1<<WGM01);      // Fast PWM
34
  TCCR0B |= (1<<CS01);           // ClockSet DIV-8
35
  
36
  ledgroup1= 255;
37
  ledgroup2= 255;
38
  
39
}
40
41
void WDT_On()
42
{
43
  cli();
44
  WDTCR = (1<<WDCE) | (1<<WDE);
45
  WDTCR = (1<<WDIE) | (1<<WDE) | (1<<WDP3) | (1<<WDP0);  // 8s
46
  sei();
47
}
48
49
void WDT_Off()
50
{
51
  cli();
52
  MCUSR = 0x00;
53
  WDTCR |= (1<<WDCE) | (1<<WDE);
54
  WDTCR = 0x00;
55
}
56
57
void IO_Init()
58
{
59
  DDRB |= (1<<PB0);  // PIN5 als Ausgang
60
  PORTB &= ~(1<<PB0);
61
}
62
63
64
65
66
67
68
int main(void)
69
{
70
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
71
72
  IO_Init();
73
  PWM_Init();
74
  WDT_On();
75
  
76
    while (1) 
77
    {
78
    if(status == 1)
79
    {      
80
      for(byte i=255; i>=0; i--)
81
      {
82
        ledgroup1= i;
83
        _delay_ms(10);
84
      }
85
      
86
      ledgroup1= 255;
87
      status= 0;
88
    }          
89
    
90
    
91
    
92
    
93
    WDT_On();
94
    sleep_mode();
95
    }
96
}
97
98
99
ISR(WDT_vect)
100
{
101
  WDTCR = (1<<WDIE);
102
  inkr++;
103
  if(inkr == 2) 
104
  {
105
    WDT_Off();
106
    status= 1;
107
    inkr= 0;
108
  }
109
}

von Stefan F. (Gast)


Lesenswert?

Reduziere dein Programm, um herauszufinden, woran es scheitert. Ich 
würde mal damit beginnen, den den WDT, und den Sleep Modus raus zu 
werfen.

Solltest du weitere fragen haben, zeige uns deinen Schaltplan und ein 
Foto vom Aufbau.

von S. Landolt (Gast)


Lesenswert?

>     for(byte i=255; i>=0; i--)

Ich habe fast keine Ahnung von C, aber kann 'byte' negativ werden? 
Vielleicht mal i>0 versuchen.

von K. S. (the_yrr)


Lesenswert?

Paul schrieb:
> ISR(WDT_vect)
> {
>   WDTCR = (1<<WDIE);
>   inkr++;

was machst du hier? oben hast du noch

Paul schrieb:
> WDTCR = (1<<WDIE) | (1<<WDE) | (1<<WDP3) | (1<<WDP0);  // 8s
definiert, und du vergisst in der isr WDCE zu setzen, mal davon 
abgesehen dass du den prescaler änderst. ich nehme ganz stark and dass 
in dieser Zeile einfach nichts passiert und dir der wdt den µC 
zurücksetzt (vor/im zweiten wdt interrupt), da du WDE auch gesetzt hast.

von leo (Gast)


Lesenswert?

S. Landolt schrieb:
>>     for(byte i=255; i>=0; i--)
>
> Ich habe fast keine Ahnung von C, aber kann 'byte' negativ werden?
> Vielleicht mal i>0 versuchen.

In der Tat, das ist eine Endlosschleife, wie auch der gcc -Wextra 
schildert:
1
$ gcc -Wall -Wextra a.c
2
a.c: In function ‘main’:
3
a.c:5:20: warning: comparison is always true due to limited range of data type [-Wtype-limits]
4
   for(byte i=255; i>=0; i--)

leo

von K. S. (the_yrr)


Lesenswert?

S. Landolt schrieb:
> Ich habe fast keine Ahnung von C, aber kann 'byte' negativ werden?
> Vielleicht mal i>0 versuchen.

byte ist kein C typ, es kann entweder negativ werden oder bis zu 255 
halten. benutze lieber typen wie uint8_t dann wird es ein wenig 
offensichtlicher.

aber so oder so sitzt du hier in einer Endlosschleife und der wdt 
resettet dir alle 16s den tiny, das wirst du aber noch garnicht merken.

von Paul (Gast)


Lesenswert?

WDIE setzt sich nach jeden Interrupt wieder zurück, setzt man in der ISR 
nicht erneut ist der nächste Interrupt ein Reset vom WDT.


Ich habe nun den Code sehr oft reduziert und vieles versucht und habe 
einen funktionierenden Gefunden

So funktioniert er wie gewollt
1
/*
2
 * main.c
3
  *       RST-|    |-VCC
4
  *     *OC1B,PB3-|    |-PB2, SCK
5
  *    OC1B,PB4-|    |-PB1, MISO, OC0B, OC1A
6
  *        GND-|    |-PB0, MOSI, OC0A, OC1A*
7
  *
8
  *
9
 * Created: 14.04.2019 16:46:46
10
 * Author : Paul
11
 */ 
12
13
/* DEFINES **************************************/
14
#define F_CPU 1000000L
15
#define byte uint8_t
16
#define ledgroup1 OCR0A
17
#define ledgroup2 OCR0B
18
19
/* INCLUDES *************************************/
20
#include <avr/io.h>
21
#include <util/delay.h>
22
#include <avr/sleep.h>
23
#include <avr/interrupt.h>
24
25
/* VARIABLE *************************************/
26
volatile byte i= 0;
27
volatile byte status= 1;
28
byte cycle= 0;
29
30
/* FUNCTIONs ************************************/
31
void PWM_Init()
32
{
33
  TCCR0A |= (1<<COM0A1) | (1<<COM0B1);  // Set in Match
34
  TCCR0A |= (1<<WGM00) | (1<<WGM01);    // Fast PWM
35
  TCCR0B &= ~(1<<WGM02);
36
  TCCR0B |= (1<<CS01);          // ClockSet DIV-8
37
  
38
  ledgroup1= 255;
39
  ledgroup2= 255;
40
  
41
  
42
}
43
44
void WDT_On()
45
{
46
  cli();
47
  WDTCR = (1<<WDCE) | (1<<WDE);
48
  WDTCR = (1<<WDIE) | (1<<WDE) | (1<<WDP3) | (1<<WDP0);  // 8s
49
  sei();
50
}
51
52
void WDT_Off()
53
{
54
  cli();
55
  MCUSR = 0x00;
56
  WDTCR |= (1<<WDCE) | (1<<WDE);
57
  WDTCR = 0x00;
58
}
59
60
void IO_Init()
61
{
62
  DDRB |= (1<<PB0) | (1<<PB1);  // PIN5 als Ausgang
63
}
64
65
66
int main(void)
67
{
68
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
69
70
  IO_Init();
71
  PWM_Init();
72
  //WDT_On();
73
  
74
    while (1) 
75
    {
76
    if(status == 1)
77
    {    
78
      
79
      while(cycle <= 4)
80
      {
81
        for(byte i= 255; i>0; i--)
82
        {
83
          ledgroup1= i;
84
          ledgroup2= i;
85
          _delay_ms(1);
86
        }
87
        
88
        _delay_ms(4);
89
        
90
        for(byte a= 0; a<255; a++)
91
        {
92
          ledgroup1=a;
93
          ledgroup2=a;
94
          _delay_ms(5);
95
        }
96
        
97
        cycle++;
98
      }    
99
      
100
      cycle= 0;
101
      ledgroup1= 255;
102
      status= 0;
103
      WDT_On();
104
    }          
105
        
106
    
107
    
108
    sleep_mode();
109
    }
110
}
111
112
113
ISR(WDT_vect)
114
{  
115
  i++;
116
  if(i>=300)    // 8s * 300 = 40 min
117
  {
118
    status= 1;
119
    i= 0;
120
    WDT_Off();
121
  }
122
  else
123
  {
124
  WDTCR |= (1<<WDIE);
125
  }
126
}

von leo (Gast)


Lesenswert?

Paul schrieb:
> if(i>=300)    // 8s * 300 = 40 min

Wie geht das bei deinem byte?

leo

von Paul (Gast)


Lesenswert?

Ja du hast vollkommen recht, zum testen hatte ich dort nur bis 3 
inkrementiert.

Danke für den Hinweis

von K. S. (the_yrr)


Lesenswert?

uint8_t musst und solltest du nicht selber definieren, dafür machst du 
ein
#include <inttypes.h>
da sind 8,16,32 bit signed und unsigend ints nach dem gleichen schema 
definiert (und int_least... int_fast..., intptr_t für leute die das 
brachen) im Endeffekt wird stdint_gcc.h eingebunden, also sollte es z.b. 
in ateml studio auch ohne include gehen.

das Problem ist dass besonders bei µC ein int nicht immer gleich groß 
ist, deswegen nimmt man besser gleich Typen bei denen man die Länge 
kennt:
byte = char => int8_t
als unsigned => uint8_t
ein int (32 bit) => int32_t
wenn dir 16 bit reichen => int16_t
kürzere Typen sind schneller auf einem 8 bit µC, können aber auch nicht 
so große Werte.

was mir noch aufgefallen ist, aber kein Gemecker sein soll, sieh es als 
Tipp den du gerne ignorieren kannst:

Die init funktionen kannst du besonders auf einem Tiny entweder inline 
machen oder als Makro definieren, bei nur einem einzigen Aufruf kann man 
sich den overhead für einen Funktionsaufruf sparen.

warum schaltest du den wdt innerhalb deiner  while(cycle <= 4) Schleife 
an, das ergibt wenig Sinn. anschalten solltest du den entweder erst nach 
der schleife, oder vorher und dann zurücksetzen, sonst läuft der schon 
los während du noch fadest (außer das ist gewünscht).

von HildeK (Gast)


Lesenswert?

Paul schrieb:
> Eigentlich ganz einfache Aufgabe:
> Watchdog aktivieren und schlafen legen, nach ca. 16 Sekunden aufwachen
> eine Ledgruppe hoch und einmal runter Faden und wieder schlafen und das
> immer so weiter.

Könntest du mal erläutern, was du tatsächlich brauchst?
Den Watchdog kann man auf mehrere Arten nutzen:
- WD zum Abfangen von Dead-Locks. Nach Ablauf der WD-Zeit wird ein Reset 
ausgeführt, wenn er nicht regelmäßig zurückgesetzt wird. Scharf machst 
du den mit WDE oder ohne Softwareeinfluss über eine Fuse.
- WD als Timer, der dann nach Ablauf der Zeit die Interruptroutine 
anspringt. Das wird mit WDIE aktiviert. Kann man nutzen als Timer und 
zum Wecken aus dem  Tiefschlaf. Das verwende ich regelmäßig.
- Beides zusammen. Dann wird beim ersten Ablauf der WD-Zeit die ISR 
angesprungen und wenn er dann noch immer nicht zurückgesetzt wird, dann 
wird beim zweiten Ablauf der Reset ausgeführt. Dazu muss WDE und WDIE 
aktiviert werden.

von Peter D. (peda)


Lesenswert?

K. S. schrieb:
> Die init funktionen kannst du besonders auf einem Tiny entweder inline
> machen oder als Makro definieren, bei nur einem einzigen Aufruf kann man
> sich den overhead für einen Funktionsaufruf sparen.

Wenn man die Optimierung nicht abschaltet, macht der AVR-GCC das schon 
von selbst.
Laß also alle einmal aufgerufenen Funktionen als Funktion. Lesbarkeit 
geht vor und kostet hier nichts.
Sogar Funktionen in verschiedenen Compile-Units können werden geinlined 
werden mit entsprechenden Compileroptionen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.