mikrocontroller.net

Forum: Compiler & IDEs System Timer und Wait


Autor: chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Folgendes Problemchen habe ich:

in tiner Interruptroutine wird ein Systemtimer im Millisekundentakt 
erhöht:
uint32_t Systime=0;

SIGNAL (SIG_OUTPUT_COMPARE2)
{
  Systime++;
}

wenn ich jetzt eine Wartefunktion ins Programm einbaue:
  waitTime=Systime+500;    // 500ms

  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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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.

  tmp_time = sys_time;
  while( (sys_time - tmp_time) < TIMEOUT );

Zusätzlich gibts aber noch das Atomicity-Problem, wenn die CPU < 32bit 
ist.
Und das volatile Problem.

Peter

Autor: chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
  tmp_time = sys_time;
  cli();
  while( (sys_time - tmp_time) < TIMEOUT )
  { 
    sei();
    cli();
  }
  sei();  

???

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
chris schrieb:
>   tmp_time = sys_time;

Schon zu spät.

>     sei();
>     cli();

Da muß noch mindestens ein NOP zwischen.

Hier alle 3 Fallgruben ausgemerzt:

#include <avr\io.h>
#include <util\atomic.h>


volatile uint32_t sys_time;


static inline uint32_t get_sys_time( void )
{
  uint32_t tmp_time;

  ATOMIC_BLOCK(ATOMIC_FORCEON)
  {
    tmp_time = sys_time;
  }
  return tmp_time;
}


void wait( void )
{
  uint32_t tmp_time = get_sys_time();

  while((get_sys_time() - tmp_time) < 500);
}


Es war nach 49,71026963 Tagen:

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


Peter

Autor: chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uhh ...
danke für Deine Hilfe ...

 <util\atomic.h>

kannte ich ja noch gar nicht. Bringt es einen Vorteil gegenüber
cli(); sei();
[/sei]
?

Beim compilieren von 

[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 
;-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: geb (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi geb,

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

Gruß,
chris

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.