mikrocontroller.net

Forum: Compiler & IDEs Probleme Int Vergleich Timer


Autor: Michael Kentschke (mad_axe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

heute mal wieder ein Problem. Ich habe einen Timer, der genau alle 1ms 
einen Interupt auslöst. Dort wird eine Zahl, die vorher einen Wert 
bekommen hat heruntergezählt.
Hier die Codeausschnitte dazu :
//Timer1 Interupt Compare

int timerCounter = 0;

ISR (SIG_OUTPUT_COMPARE1A)
{
  if(timerCounter > 0)
    timerCounter--;

}

 ....

void waitMs(int ms)
{
  timerCounter = ms;
  printf("WarteZeit = %i\r\n", timerCounter);
  while(timerCounter > 0)  {}
  printf("Ende = %i\r\n", timerCounter);
}

Folgendes Problem. Wenn ich die Funktion waitMs() aufrufe, kommt es alle 
3-4 mal dazu, das die letzte printf-Ausgabe in der Funktion(also "Ende=" 
) den Wert 255 ausgibt, die Variable timerCounter also nicht bis 0 
heruntergezählt ist, was ja aber durch den Vergleich (timerCounter > 0) 
eigentlich nicht sein kann.

Vielleicht einer eine Idee ...

Gruß
Micha

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erstens: volatile int timerCounter = 0;

Zweitens: Wenn das ein AVR ist, dann kann das auch dann nicht sauber 
funktionieren. Weil in waitMs die Variable als 2 getrennte Bytes 
verarbeitet wird, und wenn dazwischen der Interrupt reinläuft, ist hat 
das eine schon geladene Byte den Zustand von vorher, das andere danach 
geladene den Zustand nachher.

Autor: Fred S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Micha,

die Variable, die ja von einer Funktion und der ISR benutzt wird, sollte 
als "volatile" deklariert werden:
volatile int timerCounter = 0;

Gruß

Fred

PS: Andreas war schneller!

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aus 
http://www.mikrocontroller.net/articles/Interrupt#... 
"Ein ähnliches Problem entsteht bei Variablen, deren Grösse die 
Wortbreite der Maschine übersteigt. Bei 8-Bit-Prozessoren wie AVR oder 
i51 also bereits bei normalen "int" Variablen. Diese Variablen werden 
zwangsläufig byteweise verarbeitet. Wenn genau dazwischen ein Interrupt 
erfolgt, wird ein falscher Wert gelesen. Wenn beispielsweise eine 
Interrupt-Routine einen 16-Bit-Zähler verwendet und von 0x00FF auf 
0x0100 hochzählt, dann kann das Hauptprogramm auch schon mal 
versehentlich die Werte 0x01FF oder 0x0000 lesen."

Autor: Michael Kentschke (mad_axe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Danke erstmal für die schnellen Antworten !!

das mit dem volatile hatte ich schon drin, nur gestern Abend beim 
herumprobieren irgendwie wieder draußen gelassen.

Ich kann mir schon vorstellen das es genau das ist, was ihr mir hier 
beschreibt, nur wie mache ich es dann am besten.. na mal sehen ob mir 
auch was einfällt.

Gruß
Micha

Autor: mo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin,

du musst den Vergleich timerCounter > 0 einkapseln mit cli(); und 
sei();, damit der Interrupt nicht dazwischenfunken kann (siehe 
Beschreibung von Andreas)

Gruß
mo

Autor: Michael Kentschke (mad_axe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aber damit sollte doch dann mein Zeitzähler ziemlich ungenau werden oder 
sehe ich das falsch. Brauche schon ziemlich genaue Zeit, da das ganze 
von einem Kameraauslöser ist ...

Gruß
Micha

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

Bewertung
0 lesenswert
nicht lesenswert
Michael Kentschke wrote:
> Aber damit sollte doch dann mein Zeitzähler ziemlich ungenau werden

definiere 'ungenau'.

Spielt es wirklich eine Rolle, wenn der Vergleich ein paar
Nanosekunden zu spät  kommt, weil der Interrupt warten muss
bis der int einmal auf 0 geprüft wurde?

> oder
> sehe ich das falsch. Brauche schon ziemlich genaue Zeit, da das ganze
> von einem Kameraauslöser ist ...

Ich denke nicht, dass da ein paar Nanosekunden eine Rolle spielen.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>du musst den Vergleich timerCounter > 0 einkapseln mit cli(); und
>sei();

was aber bei einem while-Konstrukt nicht geht.

Am kleinsten wird die "Ungenauigkeit", wenn du die volatile-Variable in 
eine lokale Variable umkopierst, und nur das mit cli()/sei() kapselst.

Aus
cli();
int a = timerCounter ;
sei();

wird
cli
lds  r24, 0x0060
lds  r25, 0x0061
sei

mit maximal 5 Taktzyklen Verzögerung. Macht selbst bei langsamen 1MHz 
nur 5 Mikrosekunden Ungenauigkeit.

Oliver

Autor: Michael Kentschke (mad_axe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, vielleicht verstehe ich das falsch, aber ich versuches es mal zu 
Re-Konstruieren.

Ich könnte ja jetzt folgendes Machen:
int a = timerCounter;  
while(a> 0)  
{
cli();
a = timerCounter ;
sei();
}


Dann habe ich doch aber jedesmal eine Verzögerung. z.B. 1000ms warten, 
da wird der TimerInterrupt doch ziemlich oft ab und angeschaltet werden, 
oder verstehe ich das falsch.

Gruß
Micha

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da steckt der gleiche Fehler ja immer noch drin (in a = ...). Besser:
int a;
do {
  cli();
  a = timerCounter;
  sei();
} while(a > 0)


> da wird der TimerInterrupt doch ziemlich oft ab und angeschaltet werden,
> oder verstehe ich das falsch.

Nö, das verstehts du richtig. Und was stört dich daran? Der geht dadurch 
nicht verloren und wird nur unmerklich verzögert (wenige CPU-Takte).

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

Bewertung
0 lesenswert
nicht lesenswert
Andreas Kaiser wrote:

> Nö, das verstehts du richtig. Und was stört dich daran? Der geht dadurch
> nicht verloren.


Ich denke Michael hat Angst, dass ihm hier irgendwie Zeit verloren
geht und sich das aufsummiert. Dem ist aber nicht so. Der Interrupt
wird trotzdem immer zeitgerecht ausgelöst, weil dieses Auslösen ja von
der Hardware gemacht wird. Lediglich die Abarbeitung der Interrupt
Funktion kann sich unter Umständen ein klein wenig verzögern.
Das ist so, wie wenn du eine Uhr hast, bei der der Sekundenzeiger
jedesmal wenn er bei 0 ist einen Summton auslöst. Wenn du zufällig
mal bei einem Summton keine Zeit hast und 5 Sekunden später auf
die Uhr schaust, dann siehst du das eine mal etwas zu spät auf die
Uhr. Aber der nächste Summton kommt wieder pünktlich zur nächsten
vollen Minute.

Autor: Michael Kentschke (mad_axe)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke @Karl heinz Buchegger, du hattest schon recht mit der Vermutung 
das ich dachte, es summiert sich auf.
Aber ich denke nun habe ich es verstanden und baue dann mal mein 
Programm um.

Danke @all

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.