Forum: Compiler & IDEs Was ist denn das für eine delay-Routine?!


von Bauform B. (bauformb)


Lesenswert?

Mahlzeit!

Ein Fundstück mit allen Datentypen die es gibt:
1
void delay_us (uint8_t us)
2
{
3
   uint32_t  t0;
4
5
   t0 = TIM6->CNT;  // 1MHz, 16 Bit
6
   while ((uint16_t)(TIM6->CNT - t0) < us) { };
7
   return;
8
}

Gibt es ein Argument für die seltene Kombination von 8, 16 und 32 Bit? 
Oder evt. sogar einen vernünftigen Grund? Ja, intern wird meistens mit 
32 Bit gerechnet, da muss man aufpassen, aber warum die Beschränkung auf 
uint8_t?

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Da kann man nur spekulieren. unit8_t mag daher kommen das wirklich keine 
Verzögerungen > 255 µs benötigt werden. Oder weil eine vorhandene 
Schnittstelle nachgebaut wurde.

unit32_t für den 16-Bit Timer und das Gehampel mit dem Cast der 
Differenz auf uint16_t sieht mir nach einem verzweifelten, aber 
gescheiterten Versuch aus den Timer-Wraparound unter Kontrolle zu 
bekommen.

von Sebastian (Gast)


Lesenswert?

Hannes J. schrieb:
> Da kann man nur spekulieren. unit8_t mag daher kommen das wirklich
> keine Verzögerungen > 255 µs benötigt werden. Oder weil eine vorhandene
> Schnittstelle nachgebaut wurde.
> unit32_t für den 16-Bit Timer und das Gehampel mit dem Cast der
> Differenz auf uint16_t sieht mir nach einem verzweifelten, aber
> gescheiterten Versuch aus den Timer-Wraparound unter Kontrolle zu
> bekommen.

Timer-Wraparound, und zu garantieren dass delay_us(255) auch 
funktioniert.

LG, Sebastian

von Oliver S. (oliverso)


Lesenswert?

Hannes J. schrieb:
> unit32_t für den 16-Bit Timer und das Gehampel mit dem Cast der
> Differenz auf uint16_t sieht mir nach einem verzweifelten, aber
> gescheiterten Versuch aus den Timer-Wraparound unter Kontrolle zu
> bekommen.

Gescheitert ist da nichts. Der cast nach uint16_t ist genau dafür da, 
den Wrap around im Ergebnis zu maskieren. Nur t0 hätte auch ein uint16_t 
sein können. Dann würde die Subtraktion zwar als int gerechnet, und 
nicht als unsigned, der cast fängt das aber wieder ein.

Oliver

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Sebastian schrieb:
> Timer-Wraparound, und zu garantieren dass delay_us(255) auch
> funktioniert.

Das Argument zu delay_us() ist dafür egal. Eine Differenz von 255 (und 
mehr) kannst du problemlos mit einem uint16_t abbilden - der natürlichen 
Größe dieses 16-Bit Timers. Dafür braucht du kein uint32_t.

von Bauform B. (bauformb)


Lesenswert?

Nach den neuesten Forschungsergebnissen stammen die uint32_t aus dem 
offiziellen STM-Header. Weil es auch 32-Bit Timer gibt und man nur eine 
einheitliche struct möchte. "t0 = TIM6->CNT" ist also 32 Bit breit, auch 
wenn die Hardware nur halb so breit ist.

Weil die CPU und C immer mit 32 Bit rechnen, wäre uint16_t fast schon 
unnatürlich. So ist es zwar unübersehbar, dass der Timer nur 16 Bit hat, 
aber es kostet einen Maschinenbefehl mehr:
1
void delay_us (uint16_t us)
2
{
3
   uint16_t  t0;
4
   t0 = (uint16_t)TIM6->CNT;
5
   while (((uint16_t)TIM6->CNT - t0) < us) { };
6
   return;
7
}

Edit: das eigentliche Problem mit dem delay_us war ein ganz anderes: der 
Timer hatte effektiv nur 11½ Bit. Jemand hatte ihn für Experimente 
missbraucht und bei 3333 überlaufen lassen :)

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Ich wage zu bezweifeln, daß der Ansatz sonderlich gut ist. Wenn es um 
Mikrosekunden geht, und man die auch ansatzweise genau haben will, will 
man
nicht einfach C-Code, auch nicht mit einem Timer und auch nicht als 
normale Funktion. Sondern da nimmt man ein ASM-Macro, das kriegt man 
fast taktgenau hin. Das braucht auch keinen Timer, nur die Kenntnis der 
CPU-Frequenz.

von (prx) A. K. (prx)


Lesenswert?

Falk B. schrieb:
> Sondern da nimmt man ein ASM-Macro, das kriegt man fast taktgenau hin.
> Das braucht auch keinen Timer, nur die Kenntnis der CPU-Frequenz.

Nur können solche Schleifen sehr von Randbedingungen wie Waitstates und 
Alignment abhängig sein. Wenn der Prozessor deutlich komplexer ist als 
AVR. Empfehlenswert ist also eine Kalibrierung exakt jener Schleife per 
Timer beim Start. Weshalb eine Funktion auch besser ist als inline Code, 
dessen Alignment variieren kann.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Die Cortexe haben sowieso einen freilaufenden Systick Timer. Den kann 
man nutzen, an Stelle eines dedizierten Timers.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

(prx) A. K. schrieb:
> Empfehlenswert ist also eine Kalibrierung exakt jener Schleife per Timer
> beim Start.

Es geht um us. Klar kann da jemand mit einem 3 MHz-Quarz arbeiten, aber 
wenn selbst sowas zu Dissertation verkommt, dann versteht das irgendwann 
keiner mehr.

Ein delay mit 1us Auflösung liefert irgendwas zwischen 0 und 2us + 
Aufruf wenn man mit 1 aufruft. Das tut es ab ein paar Mhz.

Und an den TO: die uint8 für die Zeit sind der beste Weg, das 
idiotensicher auf kleine Werte zu begrenzen.

von Rolf M. (rmagnus)


Lesenswert?

Falk B. schrieb:
> Sondern da nimmt man ein ASM-Macro, das kriegt man fast taktgenau hin. Das
> braucht auch keinen Timer, nur die Kenntnis der CPU-Frequenz.

Bei dem STM32 wird das in der Regel nicht mehr so einfach klappen. Das 
funktioniert nur bei sehr einfachen Prozessoren.

A. S. schrieb:
> (prx) A. K. schrieb:
>> Empfehlenswert ist also eine Kalibrierung exakt jener Schleife per Timer
>> beim Start.
>
> Es geht um us. Klar kann da jemand mit einem 3 MHz-Quarz arbeiten, aber
> wenn selbst sowas zu Dissertation verkommt, dann versteht das irgendwann
> keiner mehr.

Wir wissen nicht, mit welchem Takt der Prozessor läuft. Können auch 400 
Mhz sein, dann ist eine µs 400 Taktzyklen lang.

von Bauform B. (bauformb)


Lesenswert?

A. S. schrieb:
> Und an den TO: die uint8 für die Zeit sind der beste Weg, das
> idiotensicher auf kleine Werte zu begrenzen.

Wegen Sitte und Moral, technisch wären 16 Bit doch auch kein Problem. In 
irgendeiner Initialisierung warte ich auf den Sekundentakt einer RTC, da 
wäre ein delay_us(eine-halbe-Sekunde) passend.

Wie oft braucht man denn ein so genaues delay, dass sich eine 
Kalibrierung lohnt? Immerhin müsste man in so einem Fall auch die 
Interrupts abschalten. Meistens(?) reicht es doch, wenn man mindestens 
x us wartet. Ansonsten hilft sowieso nur DMA oder Assembler. Aber das 
ist von der Natur schon klug eingerichtet: bei Chips ohne DMA 
funktioniert Zyklen zählen.

von Peter D. (peda)


Lesenswert?

Falk B. schrieb:
> Sondern da nimmt man ein ASM-Macro, das kriegt man
> fast taktgenau hin.

Jeder Interrupt verlängert das Delay zusätzlich. Bei der Timernutzung 
entsteht zwar auch ein Jitter, aber keine Fehlerakkumulation.

von A. S. (Gast)


Lesenswert?

Bauform B. schrieb:
> Wegen Sitte und Moral, technisch wären 16 Bit doch auch kein Problem.

Wenn man es mit 30.000 aufrufen kann, dann tut es jemand, auch im 
Interrupt.

Bei 255 kommt wenigstens der ms-Scheduler noch zwischendurch dran ;-)

von Falk B. (falk)


Lesenswert?

A. S. schrieb:
> Wenn man es mit 30.000 aufrufen kann, dann tut es jemand, auch im
> Interrupt.

When you make something fool proof, someone will invent a better fool.

von chris_ (Gast)


Lesenswert?

Hier die artverwandte Routine nur mit uint8:
Beitrag "Re: rollover save timer in c"

von A. S. (Gast)


Lesenswert?

chris_ schrieb:
> Hier die artverwandte Routine nur mit uint8:
> Beitrag "Re: rollover save timer in c"

Das kann zwar:

Sebastian schrieb:
> garantieren dass delay_us(255) auch funktioniert.

aber nicht bei delay_us(0).

von chris_ (Gast)


Lesenswert?

>aber nicht bei delay_us(0).
Oh, guter Hinweis. Es geht doch nichts über ein Code Review ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.