Forum: Compiler & IDEs System Timer und Wait


von chris (Gast)


Lesenswert?

Folgendes Problemchen habe ich:

in tiner Interruptroutine wird ein Systemtimer im Millisekundentakt 
erhöht:
1
uint32_t Systime=0;
2
3
SIGNAL (SIG_OUTPUT_COMPARE2)
4
{
5
  Systime++;
6
}

wenn ich jetzt eine Wartefunktion ins Programm einbaue:
1
  waitTime=Systime+500;    // 500ms
2
3
  while(Systime<waitTime);
kann es ja passieren, dass der Vergleich nicht funktioniert, wenn 
Systime einmal übergelaufen ist, d.h. wieder von 0 angefangen hat zu 
zählen.

Wie würdet ihr diese Problem lösen?

von Karl H. (kbuchegg)


Lesenswert?

chris schrieb:
> kann es ja passieren, dass der Vergleich nicht funktioniert, wenn
> Systime einmal übergelaufen ist, d.h. wieder von 0 angefangen hat zu
> zählen.
>
> Wie würdet ihr diese Problem lösen?

Indem ich zuallererst ausrechne wie lange es dann eigentlich dauert, bis 
ein 32-Bit Wert der jede Millisekunde erhöht wird überläuft :-)

von Peter D. (peda)


Lesenswert?

chris schrieb:
> kann es ja passieren, dass der Vergleich nicht funktioniert, wenn
> Systime einmal übergelaufen ist, d.h. wieder von 0 angefangen hat zu
> zählen.

Ja, das ist die klassische Fallgrube, das hatten sogar mal 
Windowsprogrammierer verpennt. Ein älteres Windows mußte nach xx Tagen 
neu gestartet werden.


> Wie würdet ihr diese Problem lösen?

Mit der Differenzmethode. Differenzen stimmen immer, auch bei einem 
Überlauf.
1
  tmp_time = sys_time;
2
  while( (sys_time - tmp_time) < TIMEOUT );
Zusätzlich gibts aber noch das Atomicity-Problem, wenn die CPU < 32bit 
ist.
Und das volatile Problem.

Peter

von chris (Gast)


Lesenswert?

>Indem ich zuallererst ausrechne wie lange es dann eigentlich dauert, bis
>ein 32-Bit Wert der jede Millisekunde erhöht wird überläuft :-)

Da passt die Antwort von Peter doch wunderbar:

>Ja, das ist die klassische Fallgrube, das hatten sogar mal
>Windowsprogrammierer verpennt. Ein älteres Windows mußte nach xx Tagen
>neu gestartet werden.

soviel zum ausrechnen.

>Atomicity-Problem
Ich nehme an, Du meinst den Interrupt, der möblicherweise beim Vergleich 
dazwischenfunkt. D.h. sollte man vermutlich den Interrupt während des 
Vergleiches sperren, also
1
  tmp_time = sys_time;
2
  cli();
3
  while( (sys_time - tmp_time) < TIMEOUT )
4
  { 
5
    sei();
6
    cli();
7
  }
8
  sei();

???

von Peter D. (peda)


Lesenswert?

chris schrieb:
1
>   tmp_time = sys_time;

Schon zu spät.
1
>     sei();
2
>     cli();

Da muß noch mindestens ein NOP zwischen.

Hier alle 3 Fallgruben ausgemerzt:
1
#include <avr\io.h>
2
#include <util\atomic.h>
3
4
5
volatile uint32_t sys_time;
6
7
8
static inline uint32_t get_sys_time( void )
9
{
10
  uint32_t tmp_time;
11
12
  ATOMIC_BLOCK(ATOMIC_FORCEON)
13
  {
14
    tmp_time = sys_time;
15
  }
16
  return tmp_time;
17
}
18
19
20
void wait( void )
21
{
22
  uint32_t tmp_time = get_sys_time();
23
24
  while((get_sys_time() - tmp_time) < 500);
25
}

Es war nach 49,71026963 Tagen:

http://support.microsoft.com/kb/216641/EN-US/


Peter

von chris (Gast)


Lesenswert?

Uhh ...
danke für Deine Hilfe ...

 <util\atomic.h>

kannte ich ja noch gar nicht. Bringt es einen Vorteil gegenüber
1
cli(); sei();
2
[/sei]
3
?
4
5
Beim compilieren von 
6
7
[c]static inline uint32_t Systime( void )

ergibt sich bei mir

../time.c:14: error: static declaration of 'Systime' follows non-static 
declaration

Warum sollte man die Funktion statisch deklarieren? Ich dachte, bei der 
Verwendung von "inline" wird ohnehin der Funktionsinhalt an die 
ensprechende Stelle des "calls" eingebaut.

>Es war nach 49,71026963 Tagen:
>http://support.microsoft.com/kb/216641/EN-US/
Super, ich glaube das sind die Fehler, mit denen auch Raketen abstürzen 
;-)

von Karl H. (kbuchegg)


Lesenswert?

chris schrieb:

> ergibt sich bei mir
>
> ../time.c:14: error: static declaration of 'Systime' follows non-static
> declaration

Wenn dein Programm noch irgendwelche Ähnlichkeiten mit dem 
Ursprungsposting hat, dann hast du da auch noch eine Variable namens 
Systime.

> Warum sollte man die Funktion statisch deklarieren? Ich dachte, bei der
> Verwendung von "inline" wird ohnehin der Funktionsinhalt an die
> ensprechende Stelle des "calls" eingebaut.

Schon.
Aber durch das static weiß der Compiler, dass diese Funktion aussrhalb 
dieses *.c Files nirgends benutzt werden kann. Er muss daher auch keine 
Funktion dafür erzeugen, selbst wenn der Funktionsinhalt innerhalb 
dieses Files überall geinlined wird.

> Super, ich glaube das sind die Fehler, mit denen auch Raketen abstürzen
> ;-)

Eine Rakte braucht selten 49 Tage, bis sie vom Boden aus ihre 
Einsatzhöhe erreicht hat. Normalerweise ist das in ~15 Minuten 
abgeschlossen, die Raktenstufe ist ausgebrannt und wird nicht mehr 
benutzt. Und damit wäre ein derartiger Überlauf rein hypotetischer Natur 
und man muss sich keine Sorgen machen. Wenn es allerdings eine einfache 
Möglichkeit gibt, das Problem gar nicht erst entstehen zu lassen, wie 
mit der Differenzmethode, gibt es auch keinen Grund die nicht zu 
verwenden. Selbst wenn der Fall nie eintreten wird, weil das Gerät gar 
nicht lang genug eingeschaltet ist.

von geb (Gast)


Lesenswert?

Also für Wartefunktionen setze ich immer die Zeit als Wert ein und lass 
den vom Interrupt runterzählen bis 0(aber nicht weiter). Damit gabs noch 
nie Probleme.

Hier für 8(16) unabhängige Timer
typedef struct
{u16 en;
u32 c[8];
}sys_counters_t;

 if (IRQSIG & RTOS_TIMER_BIT){// Timer 0 Interrupt jede ms
      T0CLRI = 1;   //  ClearTimer0Interrupt

//SW-Timer decrementieren
for(tmp=0;tmp<8;tmp++){
if(sys_counters.en&(0x01<<tmp))if(sys_counters.c[tmp]!=0)
                                           sys_counters.c[tmp]--;
              }

//Funktionen zum Setzen/Lesen/Aktivieren

u32 tw_read(u8 cntr_nr)
{
return sys_counters.c[cntr_nr];
}
void tw_set(u8 cntr_nr,u32 value)
{
sys_counters.c[cntr_nr]=value;
}
void tf_set(u8 cntr_nr)
{
sys_counters.en |= (0x0001<<cntr_nr);
}

Wenn ich in eier State-Maschine eine Verzögerung brauche, oder ein 
Timeout überwachen will dann

tw_set(2,100);//100ms
tf_set(2);//Zähler läuft
.
.
.
.
if((count=tw_read(2))!=0);//noch nicht abgelaufen


Grüße

von chris (Gast)


Lesenswert?

Hi geb,

deine Lösung gefällt mir gut. Vielleicht sollte man daraus mal eine 
eigene Libray machen.

Gruß,
chris

von Peter D. (peda)


Lesenswert?

chris schrieb:
> Hi geb,
>
> deine Lösung gefällt mir gut. Vielleicht sollte man daraus mal eine
> eigene Libray machen.

Vorsicht.
Die Interruptlast ist natürlich höher, auch wenn kein Timer aktiv ist.
Bitzugriffe und besonders das variable Schieben sind beim AVR nämlich 
aufwendig.
Und die Systemzeit hochzählen braucht man meistens trotzdem noch (z.B. 
für Uhrzeit, Datum).


Beim "tw_read" fehlt wieder der atomare Zugriff.
Und eigentlich bei den Timern auch das volatile.
Aber es scheint, daß das ATOMIC_BLOCK-Macro implizit den Zugriff 
volatile macht.
Im Unterschied zum cli/sei !


Peter

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.