Hallo,
habe ein Problem damit einen timeout von 100ms zuverlässig berechnen zu
lassen.
Ich lasse einen timerinterrupt jede millisekunde geschehen, wo dann ein
millisekunden Zähler um 1 erhöht wird. Sobald 1000 ms, also 1 Sekunde
erreicht ist, wird ein Sekunden zähler erhöht und der millisekunden
Zähler wieder auf Null gesetzt.
In meinem Programm messe ich den timeout, in dem ich die aktuelle
ms-Zeit bei einem Ereignis in einer Variable abspeichere und bei einem
weiteren Eregnis die gespeicherte ms-Zeit von der aktuelle ms-Zeit
subtrahiere und das Ergebnis (die Differenz) mit dem festgesetzten
Timeout-Wert vergleiche.
Das funktioniert z.B. unter folgender Bedingung:
Timeout: 100ms
alte Zeit: 450ms
neue Zeit: 650ms
Differenz: 650ms - 450ms = 200ms ==> timeout erreicht
Es funktioniert allerdings nicht z.B. unter dieser Bedingung:
Timeout: 100ms
alte Zeit: 980ms
neue Zeit: 60ms
Differenz: 60ms - 980ms = -920ms ==> timeout erreicht obwohl erst 80ms
verstrichen sind.
Hat jemand eine Idee, wie ich dieses Problem möglichst elegant umgehen
kann, ohne auch noch jedesmal die Sekunden vergleichen zu müssen und
dann entsprechend alles berechnen zu lassen?
Ich habe den Quellcode jetzt nicht eingefügt, weil der nichts zur Sache
tun sollte, geht ja eigentlich eher um ein mathematische Problem...
Du must abfragen ob die alte Zeit groesser als die neue Zeit ist.
if (alteZeit > neueZeit)
{
Zeit = 1000-alteZeit + neueZeit;
} else
{
Zeit = neueZeit - alteZeit;
}
Gruss Helmi
Hallo,
kommt jetzt zwar auf die Umgebung an, aber ich nahem für sowas ein
Zählregister, das in jedem Interrupt um 1 erhöht wird.
Eintretende Ereignisse setzen das Register einfach auf 0.
Die auf das Timeout wartende Routine vergleicht nur mit der gewünschten
timeout-Zeit und wenn erreicht oder größer -> Timeout.
Gerade in ASM; sehr sparsam
IRQ-Routine: inc timeout
Bearbeitungsroutine: clr timeout
Testroutine: cpi timeout, WARTEZEIT
brsh timeout_da
Wäre bei Dir bei 1ms-IRQ und 100ms Timeout passend für ein Register.
Aber selbst im SRAM kosten
push temp
lds temp,time_reg
inc temp
sts time_reg,temp
pop temp
in einer Interruptroutine meist verkraftbar.
Gruß aus Berlin
Michael
Danke für die 3 Lösungsansätze.
@amiga
Das hatte ich auch erst überlegt, bzw. habe ich mit dieser Methode
vorher überprüft, ob es tatsächlich an dem überlauf liegt.
Das ist auch prinzipiell durchaus eine Idee, allerdings ist das in
meinem Fall etwas blöd, da ich eine weitere Variable einführen muss
(auch wenn diese, wie meine anderen auch im SRAM abgelegt wird). Den ms
und s Zähler in der ISR brauche ich auch noch als Zeitbasis für andere
Dinge, daher wollte ich diesen auch für den timeout verwenden.
@spess53
Die Idee mit dem Carry Flag ist auch eine Möglichkeit allerdings eher
für 8-Bit Operationen geeignet, oder irre ich mich?.
@helmi1
Das schien mir in meinem Fall die beste Möglichkeit zu sein, ist auch
sehr einleuchtend allerdings habe ich nun ein Problem, dass das ganze
nicht so hinhaut, daher hier mal ein Codeschnipsel für den relevanten
Bereich:
Hi
>@spess53>Die Idee mit dem Carry Flag ist auch eine Möglichkeit allerdings eher>für 8-Bit Operationen geeignet, oder irre ich mich?.
Nein. geht auch bei 16 Bit.
ldi r16,Low(20)
ldi r17,High(20) ; neue Zeit
Ldi r18,Low(980)
ldi r19,High(980) ; alte Zeit
sub r16,r18
sbc r17,r19 ; Subtrakion
brcc cccc
ldi r18,Low(1000)
ldi r19,High(1000)
add r16,r18
adc r17,r19 ; 1000 addieren
cccc: cpi r16,100 ; fertig
MfG Spess
Lass als Zeitgeber einfach (zusätzlich) ein Int hochzählen.
Das wird zwar irgendwann überlaufen, aber der Windowstimer macht das
auch.
Du brauchst zwei Funktionen:
1) timer_start speichert den aktuellen Wert.
2) timeout (x) sagt, ob seit dem Start die Zeit x verstrichen ist
oder noch nicht.
Dazu bildest Du einfach nur die Differenz zwischen
gespeichertem Zeitpunkt und dem aktuellen Zeitgeberwert und
prüfst ab, ob der Wert <= 0 ist. Das
funktioniert auch bei einem Zählerüberlauf.
Nachteil ist eigentlich nur, dass die Auflösung durch die
Zählerbreite beschränkt ist.
So meine bescheidene Erfahrung...
> allerdings ist das in> meinem Fall etwas blöd, da ich eine weitere Variable einführen muss> (auch wenn diese, wie meine anderen auch im SRAM abgelegt wird). Den ms> und s Zähler in der ISR brauche ich auch noch als Zeitbasis für andere> Dinge, daher wollte ich diesen auch für den timeout verwenden.
Diese Variable brauchst Du doch sowiso. Dabei ist es unerheblich, ob Du
darin den Termin des Timeouts (als feste Zeit zum Vergleichen) oder
einen weiteren Zähler führst.
Ich würde es ähnlich wie Spess machen, vermutlich im 16ms-Raster, da ich
aus dem 1ms-Int (nutze ich meist für LCD-update und Drehgeber-Abfrage)
oft noch einen 16ms-Takt zur Tasten-Entprellung ableite. Dadurch reicht
es dann meist auch achtbittig.
~
@spess53
Danke, habe vorerst deinen Vorschlag eingebaut!!!
Funktioniert sogar jetzt beides, der Schnippel, den ich gepostet hatte
(war noch ein Flüchtigkeitsfehler an anderer Stelle :-) ) und das was du
geschrieben hast, wobei dein Schnippel noch um einiges kürzer ist.
@Bobby und sinusgeek
Auch euch nochmal vielen Dank. Natürlich führen mehrere Wege nach Rom,
die Hauptsache ist, man kommt an. Ich möchte jetzt gar nicht beurteilen
welche Methode besser ist (sofern man das überhaupt kann), funktionieren
tun sie sicher alle.
Meine Entscheidung für helmis und spess Lösung war hauptsächlich die,
dass sich das ungefähr mit dem deckt, was ich mir "vorgestellt" hatte.
Außerdem möchte ich die ISR vorerst wirklich nicht weiter auf blähen,
auch wenn eine weitere Variable nicht all zu viel Ausführungszeit und
SRAM Speicher belegt, vielleicht kommt da später noch was anderes mit
rein.
Falls trotzdem noch jemand veranschaulichen kann, dass es eine
wesentlich effizientere Lösung gibt, bin ich dafür offen. Ansonsten habt
ihr mir schonmal ein ganzes Stück weiter geholfen, zumindest
funktioniert nun dieser Teil einwandfrei - Dankeschön!