Forum: Mikrocontroller und Digitale Elektronik Timer springt - Sekundentakt hüpft


von Jens (Gast)


Lesenswert?

Meine "void Wait(uint16_t a)" Funktion sieht so aus:

- eine Variable (a) wird in der Interruptroutine runtergezählt bis sie 
gleich Null ist. Timer0 OVF  if(a!=0) a--;

- Die Waitfunktion wartet einfach nur bis a = 0 ist. while(a != 0)
dann ist die Pause zu Ende.

Sobald aber "a" größer als 255, also 16Bit groß ist, kommt es zu 
Aussetzern.

Lasse ich z.B. eine Led blinken, dann geht es 5 bis 10 mal gut, dann 
gibt es einen Sprung, nur kurzes Aufleuchten. WDR ist abgeschaltet, auch 
alle anderen IR habe ich mal weggenommen. Bei 8Bit (<256) geht es 
perfekt.

Könnt Ihr helfen?

von Ingo L. (Gast)


Lesenswert?

Programm zeigen

von Stefan E. (sternst)


Lesenswert?

Du musst den Zugriff auf a in der Waitfunktion atomar machen.

von Ralph (Gast)


Lesenswert?

Na ist relativ einfach zu erklären.

Du hast eine 16 Bit Variable auf einem 8 Bit µC

Fall 1: Der Wert ist zwischen 0 und 255.
Es wird nur ein Byte der Variable genutzt
==> R/W Zugriff auf das höhere Byte ist egal, da eine 0 mit einer 0 
beschrieben / gelesen wird.
==> R/W zugriff auf das niedrige Byte ist eine einzelner Zugriff der 
nicht unterbrochen werden kann.
==> alles gut :-)

Fall 2: Der Wert ist > 255
==> R/W Zugriff auf das höhere Byte erfolgt.
==> und jetzt kommt der böse Interrupt und verändert den Zähler
==> R/W Zugriff auf das niedrige Byte erfolgt dann NACH dem Interrupt.

==> Variablen Wert ist somit korrumpiert worden. ==> Müll

Und das ist dein Problem.


Stichworte zur Lösung:
* Interrupt sperre während der Variablenbearbeitung
* Volatile
* 2 Variablen nutzen mit definierter Werteübergabe

von Jens (Gast)


Lesenswert?

wie atomar?

Das Programm ist ganz simpel.
1
int main(void)
2
{
3
 TCCR0 =  3;  
4
 TIMSK |= (1<<TOIE0);
5
6
 InitPorts();
7
8
 sei();
9
10
 while(1)
11
 {
12
   LedOn;    //Macro
13
   Wait(500);     // bei 255 geht es bestens
14
   LedOff;   //Macro
15
   Wait(500);
16
 }
17
}
18
19
...
20
21
ISR(TIMER0_OVF_vect)
22
{
23
  if (TimerWait != 0) TimerWait --;
24
}
25
...
26
27
void Wait(uint16_t b)
28
{
29
  TimerWait = b;
30
31
  while(TimerWait != 0)
32
  {
33
    b += 1;  // nur so, damit ich beim Simulieren
34
    b -= 1;  // b verändern kann
35
  }
36
}
37
38
...
39
40
volatile uint16_t     TimerWait;

von Stefan E. (sternst)


Lesenswert?

Jens schrieb:
> wie atomar?
>
> Das Programm ist ganz simpel.

Eben ein wenig zuu simpel.
Der Vergleich in dieser Schleife "while(TimerWait != 0)" erfolgt in 
mehreren Schritten. Wenn der Interrupt zwischen drin kommt, kann es 
Probleme geben:
- TimerWait ist 0x0100
- der Vergleich beginnt mit dem Low-Byte, das ist gleich 0
- jetzt kommt der Interrupt und ändert TimerWait auf 0x00ff
- der Vergleich macht weiter mit dem High-Byte, das ist auch gleich 0
=> Bedingung ist false, Schleife bricht ab

von Jens (Gast)


Lesenswert?

ja, habe ich verstanden, Danke, kann mich auch an die Zeiten das 
Assembler-Programmierens erinnern - sehr lange her.

Wie kann man es einfach ändern? Ich dachte volatile für TimerWait 
reicht.

von Falk B. (falk)


Lesenswert?

Siehe Interrupt.

von Jens (Gast)


Lesenswert?

Danke, dann werde ich das so machen, dass nur ein Flag in der IR-Routine 
gesetzt wird und der Timer im Hauptprogramm, wenn Flag=1, ggf. -- 
gesetzt wird.

Bedeutet "atomar" also lediglich, darauf achten, dass es nicht zur 
"Zerspaltung" der 16Bit Varianlen kommt?

von Ralph (Gast)


Lesenswert?

Jens schrieb:
> Bedeutet "atomar" also lediglich, darauf achten, dass es nicht zur
> "Zerspaltung" der 16Bit Varianlen kommt?
>

ja, gilt aber auch genauso für 32 , 64 , .... Bit Variablen

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.