Hi
Ich möchte gerne die verstrichene Zeit mittels SysTick ermitteln.
Allerdings bin ich ein wenig verwirrt, wie ich einen Überlauf
berücksichtige.
Wenn wir annehmen, dass SysTick von 0 bis 5 läuft und dann überläuft:
1
[0,1,2,3,4,5]
Und dann zwei Zeitpunkte annehmen:
1
uint32_talteZeit=3
2
uint32_tneueZeit=1
Bin ich mit folgendem Programm auf dem richtigen Trichter? Wenn nein,
wie könnte man es eleganter/besser lösen?
Alex -. schrieb:> Mein Bauchgefühl sagt mir, dass ein erfahrener Programmierer eine> wesentlich elegantere Lösung auf Lager hat ;) :)
(neueZeit - alteZeit) MOD 5
Wenn du sowohl den Wert von SysTick als auch die Anzahl Interrupts
kombinierst, gibt es eine kleine Gemeinheit namens Race Condition. Wenn
zur Laufzeit der Funktion, in der man diese beiden Zählerwerte liest,
der SysTick von 0 auf 0x00FFFFFF (oder einem anderen gesetzten
Reload-Wert) springt, dann kann es passieren, dass die beiden
Zählerwerte nicht zueinander gehören. Also beispielsweise der SysTick
schon auf 0x00FFFFFF steht und eigentlich ein Interrupt ausgelöst wurde,
den der gelesene msTick Wert aber noch nicht widerspiegelt. Oder
umgekehrt.
(prx) A. K. schrieb im Beitrag #7048283:
> Die Sache oben ist dann doch etwas komplizierter, denn er hat zwei> Zähler, die er kombiniert. Die Variable msTick zählt die Anzahl> SysTick-Interrupts und dazu kommt noch der Wert des SysTick selbst.
Die Situation, dass ich zwei Zähler kombiniere, ist nicht zwingend. Ich
dachte lediglich, es wäre klug :)
Ich könnte im Prinzip aber genauso gut folgenden Ansatz wählen:
Ich schrieb:> Einfach so eine Auflösung nehmen, daß der Überlauf erst in zig Jahren> ist, oder du immer so abfragst, daß max 1 Überlauf stattfindet.
Irgendwie dachte/denke ich jedoch, dass ich sicherlich nicht der erste
bin, der in solch einer Situation stand. Wie wird sowas normalerweise
gelöst?
Ich glaube, dass mein obiger Ansatz im Ursprungspost funktionieren
könnte (bei steigendem Counter - hatte vergessen, dass SysTick herunter
zählt). Ich finde aber, dass meine Lösung ein wenig kompliziert
aussieht.
Mache ich es mir unnötig schwer?
Alex -. schrieb:> Die Situation, dass ich zwei Zähler kombiniere, ist nicht zwingend. Ich> dachte lediglich, es wäre klug :)
Es macht die Sache deutlich komplexer. Wenn du das nicht brauchst, ist
es klüger, es bleiben zu lassen.
Ich verwende gerne einen permanent laufenden Takt mit z.B. 1 KHz oder 10
KHz, der bei den Cortex M naheliegenderweise aus dem SysTick Interrupt
kommt. Und für alle zeitabhängigen Aktivitäten, die nicht genauer als
dieser Takt sein müssen, verwende ich nur den. Dann tut es die
beschriebene MOD Variante. Die sich bei Typen ab sizeof(unsigned) ganz
von selbst ergibt, da die Arithmetik vorzeichenloser Typen in C so
definiert ist.
Wenn Du bei 1,2 oder 4 Byte überlaufen lässt, brauchst Du den Überlauf
nicht berücksichtigen, solange Du zuerst die Differenz in gleicher
Auflösung bildest.
Wenn Du Mal us, ms und s brauchst, nimm 3 parallele timer die atomar
lesbar sind.
Bei 32 Bit signed sind ms mehr als 20 Tage. Programmier sauber und mach
dann trotzdem nach 20 Tagen einen Reset.
2 Zähler mixen erfordert meist durch den notwendigen Lock mehr
Ungenauigkeit und arbeit als einfach 2 verschiedener zu nehmen
Mein STM32 hat auch eine Real-Time Clock.
__HAL_RCC_RTC_CLK_ENABLE();
__HAL_RCC_RTC_ENABLE();
Warum kann man die nicht benutzen, wenn es mal länger dauert?
Modulo schrieb:> Norbert schrieb:>> (neueZeit - alteZeit) MOD 5>> ... bei einem Zähler, der von 0 bis 5 zählt, ist das nicht wirklich> zielführend.
Da hat er Recht, muss natürlich MOD 6 heißen!
Immer diese Flüchtigkeitsfehler zu später Stunde, da habe ich kein
Verständnis…
Ah, und vergessen:
Alex -. schrieb:> msTicks = 0;
Mache das niemals. Denk nicht Mal dran.
Mit (atomaren) free running countern fällt jede Sonderbehandlung weg.
Kein if, kein zurücksetzen, kein Lock. Einfach (jetzt-alt) und fertig.
Alex -. schrieb:> Ich möchte gerne die verstrichene Zeit mittels SysTick ermitteln.> Allerdings bin ich ein wenig verwirrt, wie ich einen Überlauf> berücksichtige.>> Wenn wir annehmen, dass SysTick von 0 bis 5 läuft und dann überläuft:
Also mal ne ernste Frage: Zu welchem Zweck willst du die seit welchem
Startzeitpunkt verstrichene Zeit wissen?
Normalerweise ist es so, daß man z.B. irgend einen Ablauf programmieren
will und dazu nach einer bestimmten Verzögerung irgend ein Signal setzen
muß, oder daß man eine Zeitbegrenzung setzen will, damit man nicht bei
irgend einer Aktion festklebt und alles blockiert. Das führt regelmäßig
dazu, daß man eine Systemuhr braucht, die nach einer bestimmten Zeit ein
Signal abgibt (eben sowas wie ein 'event'), so daß man darauf reagieren
kann und in der Zwischenzeit sich um etwas anderes kümmern kann.
Die gewünschte Zeitspanne gibt man vor und nach Verstreichen derselben
klingelt es quasi. Da sehe ich keinen Grund, eine verstrichene Zeit zu
ermitteln.
Und wieso denkst du an Überläufe? Eine Systemuhr kennt allenfalls den
Überlauf, der um Mitternacht stattfindet, wenn das Datum weitergezählt
wird und die Uhr wieder auf 0 gestellt wird. Zumeist wird man den
Systick so programmieren, daß er alle 1 ms oder alle 10 ms überläuft und
damit einen Interrupt für die Systemuhr liefert. Wie kommst du auf einen
Zählbereich von 0..5 ?
Mir kommt das Ganze ziemlich wirr vor. Zumindest kann ich da noch kein
tragfähiges System drin erkennen.
W.S.
W.S. schrieb:> Also mal ne ernste Frage: Zu welchem Zweck willst du die seit welchem> Startzeitpunkt verstrichene Zeit wissen?
Ein Beispiel bei mir: Sensordaten kriegen diesen durchlaufenden
Tick-Counter als Alter ins Record geschrieben. Wenn zu alt, dann
ungültig. Seine Methode, msTicks stets zurück zu setzen, ist dafür
freilich ungünstig. Den durchlaufen lassen und die Differenz zu
berechnen ist besser.
W.S. schrieb:> Wie kommst du auf einen Zählbereich von 0..5 ?
Wieviele Finger 🖐️ hat eine Hand?
Natürlich werden solche Überlegungen auf binärer Ebene einfacher, wenn
man eine Zweierpotenz nimmt. Und ein uC ist halt nun ein binäres Ding.
Und zum Problem selber empfehle ich die Betrachtung der millis()
Funktion aus der Arduino-Ecke und ihrer Anwendung. Dort wird einfach ein
möglicher Überlauf durch geschickte Anwendung von Integer-Arithmetik der
Überlauf "herausgerechnet".
W.S. schrieb:> Und wieso denkst du an Überläufe?
Bei seiner Kombination zweier Timer sind die zu berücksichtigen, beim
Hardware SysTick Counter. Nur ist dieser Ansatz i.d.R. unnötig
kompliziert.
> Mir kommt das Ganze ziemlich wirr vor.
Er übt noch. Da ist das normal.
(prx) A. K. schrieb:> Ein Beispiel bei mir: Sensordaten kriegen diesen durchlaufenden> Tick-Counter als Alter ins Record geschrieben. Wenn zu alt, dann> ungültig.
Das heißt, du kannst die aktuelle Uhrzeit in deinen Record schreiben.
Wer will, kann dann die Zeitspanne zwischen "jetzt" und der
Record-Uhrzeit selber berechnen. Dazu braucht es ne Uhrzeit, die da
normalerweise von selbst mit abfällt.
Lothar M. schrieb:> Wieviele Finger 🖐️ hat eine Hand?
Und wieviele Hände hat ein Mikrocontroller?
(prx) A. K. schrieb:> Er übt noch. Da ist das normal.
Deshalb hab ich ja auch nicht geschimpft. Also Nachdenken ist zuerst
angesagt.
W.S.
> Wenn wir annehmen, dass SysTick von 0 bis 5 läuft und dann überläuft
Wenn das so ist, hat schon vorher jemand großen Bockmist gebaut. Mit so
einer Idee schießt man sich früher oder später ins eigene Knie. Und mit
Sicherheit bereitet man seinen Nachfolgern, die das Projekt fortsetzen,
unnötigen Frust.
-------------------------------------------
Herbert Frickelmann, gestorben am 11.12.2021
Wir liebten seine Grillkünste, aber seine Software treibt uns in den
Wahnsinn. Möge er ruhen in Frieden und nicht wieder kommen.
-------------------------------------------
Normalerweise lässt man den Systick Counter durchlaufen. Zeitspannen
ermittelt man mit der Subtraktion. Angenommen, der Systick Counter hat
32 Bit und wird im ms Intervall hochgezählt, dann so:
1
uint32_tstart=systick();
2
...
3
uint32_telapsed_time_ms=systick()-start;
Oder für Warteschleifen:
1
uint32_tstart=systick();
2
uint32_twait_ms=1000;
3
4
while(systick()-start<wait_ms){
5
...
6
}
Oder für Abbrüche bei Zeitüberschreitungen:
1
uint32_tstart=systick();
2
uint32_tmax_ms=1000;
3
4
while(waiting_for_response){
5
...
6
if(systick()-start>max_ms){
7
returnERROR_CODE_TIMEOUT;
8
}
9
}
Der entscheidende Punkt ist die Subtraktion. Mit einer Addition
funktioniert es bei Überlauf nicht.
W.S. schrieb:> Das heißt, du kannst die aktuelle Uhrzeit in deinen Record schreiben.
Eine echte Uhrzeit ist dafür nicht nötig. Eine einfache Zählvariable
reicht.
(prx) A. K. schrieb:> Eine echte Uhrzeit ist dafür nicht nötig.
Bei einer wenigstens so einigermaßen passabel gemachten Systemuhr hat es
die ohnehin, egal ob hier grad mal unbedingt nötig oder nicht.
Wenn ich mich recht erinnere, dann hatte ich hier schon mal meine
Systemuhr und Event-System gepostet. So etwas trägt kaum auf und man hat
damit Events und delayed Events. Das reicht für alle Zwecke, bei denen
man mit Zeiten von so etwa 1 ms und mehr arbeiten muß.
Recht oft würde eine Zeitauflösung von 10 ms auch ausreichen.
W.S.
(prx) A. K. schrieb:> Eine echte Uhrzeit ist dafür nicht nötig.
Eine echte Uhrzeit mit SZ/WZ-Umstellung und Schaltsekunden ist dafür
sogar ausgesprochen ungeeignet.