mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Mathematisches Problem, wie ms timeout korrekt berechnen? - AVR ASM


Autor: Björn (Gast)
Datum:

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

Autor: Helmut Lenzen (helmi1)
Datum:

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

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

Nach der Subtraktion Carry-Flag auswerten. Wenn gesetzt 1000 zu Ergebnis 
addieren.

MfG Spess

Autor: Michael U. (amiga)
Datum:

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

Autor: Björn (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
  lds XL, aktuelleZEIT_ms
  lds XH, aktuelleZEIT_ms + 1
  lds YL, alteZEIT_ms
  lds YH, alteZEIT_ms + 1

  cp  XL, YL                    ; aktuelleZEIT < alteZEIT ?
  cpc XH, YH
  brlo kleiner

  sub XL, YL                    ; aktuelle Zeit - alte Zeit
  sbc XH, YH

  rjmp  vergleich

 kleiner:
  ldi temp, LOW(1000)           ; (1000 - alte Zeit) + aktuelle Zeit)
  ldi temp_2, HIGH(1000)
  sub temp, YL
  sbc temp_2, YH
  add XL, temp
  adc XH, temp_2

 vergleich:
  lds temp, LOW (timeout)       ; Differenz > timeout ?
  lds temp_2, HIGH (timeout)
  cp  XL, temp
  cpc XH, temp_2
  brlo keinTIMEOUT
  breq keinTIMEOUT

;   .
;   . (Führe etwas aus)
;   .

 keinTIMEOUT:
;   .
;   . (Führe etwas anderes aus)
;   .

Das müsste doch so die Umsetzung von amiga sein?

Autor: spess53 (Gast)
Datum:

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

Autor: Bobby (Gast)
Datum:

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

Autor: Sinusgeek (Gast)
Datum:

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

~

Autor: Björn (Gast)
Datum:

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

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.