Hallo, ich arbeite mit dem AvrStudio und einem µC und ich möchte gerne die Delay Funktion umgehen, dazu habe ich einen Timer erstellt. Dieser Timer ist so eingestellt das er für einen takt 1ms braucht. Ich möchte jetzt das einer meiner while Schleifen den Programmfluss mit dem Timer für 100ms unterbricht oder verzögert. Mein Problem ist jetzt, das ich nicht weiß, wie ich mit dem Timer auf diesen bestimmten Zeitpunkt warte. Wie programmiere ich das am besten? Danke
Stefan M. schrieb: > Ich möchte jetzt das einer meiner while Schleifen den Programmfluss mit > dem Timer für 100ms unterbricht oder verzögert. > > Mein Problem ist jetzt, das ich nicht weiß, wie ich mit dem Timer auf > diesen bestimmten Zeitpunkt warte. Wie programmiere ich das am besten? Der Timer generiert einen Interrupt. z.B. alle 1 ms. In der ISR wird gezählt und bei 100ms eine Variable auf 1 gesetzt. Im Hauptprogramm wird auf diese Variable gewartet (und dann auf 0 gesetzt).
Stefan M. schrieb: > Mein Problem ist jetzt, das ich nicht weiß, wie ich mit dem Timer auf > diesen bestimmten Zeitpunkt warte. Wie programmiere ich das am besten? Mit einer FSM. Der Timer inkrementiert bei jedem Tick einen Zähler und den fragst du in der FSM ab. Wenn der Zählerstand deines Tick-Zählers sich seit Start der Wartezeit um 100 erhöht hat, sind die 100ms um und es Zeit, etwas zu unternehmen.
Stefan M. schrieb: > Ich möchte jetzt das einer meiner while Schleifen den Programmfluss mit > dem Timer für 100ms unterbricht oder verzögert. Cyblord -. schrieb: > Im Hauptprogramm wird auf diese Variable gewartet ...dann stellt sich aber insgesamt die Frage, wo jetzt der Unterschied zu einem Delay ist... Das Ganze macht doch nur Sinn, wenn der MC auch noch etwas anderes macht
Cyblord -. schrieb: > > Der Timer generiert einen Interrupt. z.B. alle 1 ms. > In der ISR wird gezählt und bei 100ms eine Variable auf 1 gesetzt. > > Im Hauptprogramm wird auf diese Variable gewartet (und dann auf 0 > gesetzt). Ja genau, ich habe zum Beispiel dieses Programm von der Seite https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29 while( 1 ) // Endlosschleife { PORTB ^= ( 1 << PB0 ); // Toggle PB0 z. B. angeschlossene LED delay_ms(1000); // Eine Sekunde warten... } Jetzt möchte ich gerne dieses Delay mit meiner Globalen Counter Variable welches im ISR zählt ersetzen. Aber wie sage ich dem Programm das er in der while-Schleife auf diese Variable warten soll? if (counter == 100) { counter = 0; } Das hatte bei mir nicht funktioniert
50c schrieb: > ...dann stellt sich aber insgesamt die Frage, wo jetzt der Unterschied > zu einem Delay ist... Der Unterschied ist, dass du ein präzises Timing hast. Wenn du Messungen mit Zeitbezug hast, reicht ein Delay nicht. Dann musst du auch nicht mit einem while warten. Die Main Schleife kann durchgehend laufen, und je nach Zähler/Gate Variable verschiedene Dinge tun. Aber die Grundlage ist eben ein Timer, der in der ISR zählt, und das Ergebnis dem Main Programm zugänglich macht. Das ganze kann man dann noch beliebig generalisieren, mit Timer Strukturen und Callbacks und sich ein ganzes Posix Timer Sammelsurium zusammenprogrammieren. Wenn man das will. Meist reicht aber ein Zuschnitt auf die jeweilige konkrete Anwendung aus. > Das Ganze macht doch nur Sinn, wenn der MC auch noch etwas anderes macht Ja, das schließt sich nicht aus.
:
Bearbeitet durch User
Stefan M. schrieb: > Das hatte bei mir nicht funktioniert Mach die Variable mal volatile. Und "hat nicht funktioniert" ist ne üble Fehlerbeschreibung. Warum sollte es nicht funktionieren? Also Debugge es ordentlich.
Stefan M. schrieb: > ich möchte gerne die Delay Funktion umgehen, ... > Ich möchte jetzt das einer meiner while Schleifen den Programmfluss mit > dem Timer für 100ms unterbricht oder verzögert. Den Timer zu nehmen anstatt mit Delays zu arbeiten ist meist sinnvoll. Wenn du aber nur unterbrichst und eine bestimmte Zeit wartest und im Programm derweil nichts anderes tun willst, dann ist Delay genau so gut und einfacher zu programmieren. Außer du willst zum Strom sparen den µC in den Schlaf schicken und nach der Zeit wieder wecken; dann hat der Timer wieder die Nase vorn.
Cyblord -. schrieb: > Der Unterschied ist, dass du ein präzises Timing hast. > Wenn du Messungen mit Zeitbezug hast, reicht ein Delay nicht. Humbug. delay ist so genau wie der Prozessortakt, alsp ggf. quartzgenau. Man möchte kein delay, weil es den Programmablauf blockiert, der uC also nichts anderes machen kann (nicht Mal Interrupts bearbeiten, das wurde die delay-Zeit verlängern). Stefans while wird zu
1 | int start=millis(); |
2 | while(1) // bzw. loop() |
3 | {
|
4 | if(millis()>=start+1000) |
5 | {
|
6 | PORTB ^= ( 1 << PB0 ); |
7 | start+=1000; // keine Akkumulation von Fehlern |
8 | }
|
9 | // else anderes bearbeiten wenn nötig
|
10 | }
|
Arduino-millis ggf. durch eigenes ersetzen.
MaWin schrieb: > delay ist so genau wie der Prozessortakt, alsp ggf. quartzgenau. Ja Bubi, aber nach dem Delay kommt ja noch Code. Der auch Zeit braucht. Wenn du also eine Messung alle 100ms machen willst, bringt dich ein 100ms delay nicht weiter. Ein 100ms "Timer-Delay" aber eben schon.
Stefan M. schrieb: > Wie programmiere ich das am besten? Ich gehe davon aus dass deine Timer ISR eine uint32_t Variable im ms Intervall hochzählt. Ansonsten musst den Code entsprechend anpassen. Nennen wir sie "millis".
1 | void delay(uint32_t milliseconds) |
2 | {
|
3 | uint32_t start=millis; |
4 | while (millis-start < milliseconds) |
5 | {
|
6 | // waste time
|
7 | }
|
8 | }
|
9 | |
10 | while(1) // Endlosschleife |
11 | {
|
12 | PORTB ^= ( 1 << PB0 ); // Toggle LED an PB0 |
13 | delay(1000); |
14 | }
|
Wichtig ist, dass du eine Subtraktion und unsigned Integer benutzt werden. Dann liefert die Rechnung sogar ein korrektes Ergebnis, wenn der Zähler zwischendurch einmal überläuft. Also nicht
1 | while (millis < start+milliseconds) |
das würde beim Überlauf nicht funktionieren.
Stefan ⛄ F. schrieb: > MaWin schrieb: >> if(millis()>=start+1000) > > Das ist genau die falsche Methode! Ja, MaWin zeigt eindrucksvoll dass er nichts kann.
Erklärung, warum es falsch ist: Der maximale Wert einer uint32 Variable ist 4294967295. Danach kommt wieder die 0. Mal angenommen, wir sind kurz vor diesem Überlauf und warten dann 100ms, dann ergibt die Bedingung beim ersten Schleifendurchlauf: > if(4294967291 >= 4294967291+1000) das entspricht: > if(4294967291 >= 995) Und das ist sofort wahr, schon bevor überhaupt auch nur eine Millisekunde gewartet wurde. Mit einer Subtraktion funktioniert es hingegen.
Hallo, ich empfehle einen etwas anderen Ansatz. Erstelle dir eine globale Variable. Diese Variable dekrementierst du im Timer-Interrupt, solange sie nicht null ist(!) In deinem Programm setzt du die Variable nun auf 100, was 100 ms entspricht. Du kannst nun in deinem Programm darauf warten, dass die Variable den Wert null hat oder alternativ etwas anderes machen, solange sie eben nicht null ist. Wenn du mehrere Variablen anlegst (bzw. ein Array), dann hast du einen Satz Softwaretimer, die flexibel einsetzbar sind. Das Dekrementieren hat den Vorteil, dass man bei Ablauf der Zeit immer bei null landet, das wäre bei einer inkrementierten Variablen m.E. schwieriger zu handhaben. Ein Programm könnte dann ungefähr so aussehen:
1 | #define NUM_SOFTTMR 3
|
2 | volatile uint16_t SoftTmr[NUM_SOFTTMR]; |
3 | |
4 | void Timer_ISR(void) { |
5 | uint8_t I; |
6 | for(I = 0; I < NUM_SOFTTMR; I++) { |
7 | if(SoftTmr[I] > 0) |
8 | SoftTmr[I]--; |
9 | }
|
10 | }
|
11 | |
12 | void main(void) { |
13 | //Initialisierung Timer, etc.
|
14 | |
15 | SoftTmr[0] = 100; |
16 | SoftTmr[1] = 250; |
17 | SoftTmr[2] = 10000; |
18 | |
19 | while(SoftTmr[2] > 0) { |
20 | if(SoftTmr[0] = 0) { |
21 | SoftTmr[0] = 100; |
22 | LED0 = ~LED0; |
23 | }
|
24 | if(SoftTmr[1] = 0) { |
25 | SoftTmr[1] = 250; |
26 | LED1 = ~LED1; |
27 | }
|
28 | }
|
29 | while(1); //Programmende |
30 | }
|
Das ist ein generisches Beispiel, es sollte das Prinzip aber klar machen (ich hoffe da hat sich jetzt kein Fehler eingeschlichen). Wichtig ist, dass die Timervariable(n) als 'volatile' markiert sind, sonst wird evtl nicht der richtige Wert herangezogen (ich kenne AVRs nicht, aber da gilt das vermutlich auch). Zu beachten ist auch, dass (unabhängig von Dekrementieren/Inkrementieren) die Toleranz +0/-1 betragen kann, d.h. wenn du mindestens 100ms benötigst, dann musst du 101 als Wert verwenden. Das ist bei "schnellen" Softwaretimern nicht so kritisch, aber bei bspw. 10ms Auflösung kann das schon ins Gewicht fallen. Gruß Ralf
1 | #define INTERVAL 100
|
2 | volatile unsigned char timerflag = 0; |
3 | |
4 | ISR(1ms-Timer) |
5 | {
|
6 | timerflag = 1; |
7 | }
|
8 | __attribute__((OS_main)) int main(void) |
9 | {
|
10 | unsigned char interval = INTERVAL; |
11 | //Init alles noetige...
|
12 | |
13 | while(1) |
14 | {
|
15 | //tu was mit voller Geschwindigkeit
|
16 | |
17 | if(timerflag) |
18 | {
|
19 | timerflag = 0; |
20 | //tu was alle 1ms
|
21 | if(interval)interval--; |
22 | else
|
23 | {
|
24 | interval = INTERVAL; |
25 | //tu was alle 100ms...
|
26 | }
|
27 | }
|
28 | }
|
Stefan M. schrieb: > if (counter == 100) > { counter = 0; } if (counter >= 100) ... HildeK schrieb: > Den Timer zu nehmen anstatt mit Delays zu arbeiten ist meist sinnvoll. Das sehen ich SO nicht. Wenn überhaupt ein Timer, dann sollte man sich auch ein paar Gedanken machen über einen klügeren Grundentwurf seiner Firmware. Dann sollte der Timer auch gleich in einer firmwareinternen Uhr verwendet werden und diese Uhr sollte Delay-Funktionalität im ms und s Bereich erledigen. Also wenn man schon ein dickeres Kaliber zum Schießen auf Spatzen erwägt, dann bitte richtig. W.S.
W.S. schrieb: > Wenn überhaupt ein Timer, dann sollte man sich auch ein > paar Gedanken machen über einen klügeren Grundentwurf seiner > Firmware Ich denke, die delay_ms() durch eine eigene zu ersetzen, ist der erste Schritt in die Richtung. Man soll ja nicht zu viel auf einmal ändern, sonst verliert man schnell den Durchblick.
W.S. schrieb: > Also wenn man schon ein dickeres Kaliber zum Schießen auf Spatzen > erwägt, dann bitte richtig. Nö. Immer so dick wie gerade benötigt.
Stefan ⛄ F. schrieb: > Ich denke, die delay_ms() durch eine eigene zu ersetzen, ist der erste > Schritt in die Richtung. Das sehe ich so nicht. Der erste Schritt ist das gründliche Überdenken des Grundgerüstes. Und überhaupt: was soll das Ersetzen einer Delay-Funktion durch eine andere Delay-Funktion bringen? Den Stolz, auch mal was eigenes geschrieben zu haben und nicht nur per copy&paste gelebt zu haben? Oder? W.S.
er soll doch gerade statt der delay-Funktion, in der er blöd rumwartet, etwas sinnvolles machen können und nur, wenn die Intervallzeit abgelaufen ist, eben das vorgesehene "tun". Sonst kann man ja "delay_ms()" auch stehen lassen. Gibt es ja schon. Also eher einen Zustandsautomaten aufbauen oder eben kurz nachsehen, ob zeit schon um ist und was wichtigeres abarbeiten... keine Ahnung, wie man das richtig ausdrückt.
Dyson schrieb: > if(timerflag) > { > timerflag = 0; > //tu was alle 1ms > if(interval)interval--; > else > { > interval = INTERVAL; > //tu was alle 100ms... > } > } > [/c] ja sowas hier in der Art (hatte sich überschnitten)
Cyblord -. schrieb: > Nö. Immer so dick wie gerade benötigt. Ein Sleepmodi kann Faktor 100 oder mehr beim Stromverbrauch ausmachen. Selbst ein schöndes Idle bringt schon viel. Ein nicht zu unterschätzender Faktor, speziell wenn man auf Batterie läuft, aber generell auch dann, wenn man z.B. auf der Platine Temperaturen messen will. Da heißt dann 1% CPU-Last statt 100% 99% weniger Stromverbrauch oder Wärme. Die Grundstruktur schreibt man sich einmal und das wars dann. Vom Vorteil, dass man in der 99% Zeit noch etwas anstellen kann, kommt noch dazu. Warten durch Verbrennen von Strom und CPU-Zyklen wäre in etwa so, wie wenn man beim Auto das Gaspead weglässt, den Motor immer auf Vollgas laufen lässt, und die Geschwindigkeit nur durch Bremsen anpasst.
ohje schrieb: > Warten durch Verbrennen von Strom und CPU-Zyklen wäre in etwa so, wie > wenn man beim Auto das Gaspead weglässt, den Motor immer auf Vollgas > laufen lässt, und die Geschwindigkeit nur durch Bremsen anpasst. Jaja, nur implementieren die wenigstens normale Wartefunktionen zusammen mit Sleep Modes. Du kannst dir dann auch gerne ausrechnen ob die 1 Stunde mehr an Implementierungszeit, nicht mehr Strom gekostet hat, als dein Sleep Mode im ganzen Leben spart.
W.S. schrieb: > Und überhaupt: was soll das Ersetzen einer > Delay-Funktion durch eine andere Delay-Funktion bringen? Das AVR Libc delay_ms() verlängert sich bei jedem Interrupt um dessen Laufzeit. Das Arduin delay() tut das nicht. Das ist ein Unterschied!
W.S. schrieb: > HildeK schrieb: >> Den Timer zu nehmen anstatt mit Delays zu arbeiten ist meist sinnvoll. > > Das sehen ich SO nicht. Ich meinte, ich hätte erklärt, wann man ein einfaches Delay (_delay_ms(nn)) nehmen kann und wann ein Timer sinnvoller ist. Ein Delay ist halt blockierend; macht nichts, wenn ich es mir leisten kann. Oft jedoch nicht. Und den Timer kann man auch blockierend verwenden, wenn man will: while (!timer_expired()); Der TO wollte den Programmablauf unterbrechen bzw. verzögern; da ist das einfache Delay durchaus sinnvoll, einfach und ausreichend und der Aufwand, das mit einem Timer lösen zu wollen höchstens lehrreich.
Aber wenn er von Anfang an den Timer verwendet (in welcher der hier angesprochenen Varianten auch immer), dann hat er den Vorteil dass er beim ersten Programm, bei dem er häufig und auf verschiedene Dinge warten muss immer noch was anderes machen kann. Denn das erste was passieren wird ist, dass das Programm vollgestopft mit Delays und extrem träge ist. Ich finde, der Zustand eines "kontrollierten" Nichtstuns ist vorzuziehen anstatt an verschiedenen (und später unüberschaubaren) Stellen Zeit zu verdröseln. Zustandmaschine und ähnliches wurden ja dafür schon genannt. Das könnte er sich ja als nächstes Lernziel setzen.
>... das mit einem Timer lösen zu wollen höchstens lehrreich. Nicht "höchstens lehrreich", sondern "höchst lehrreich". >Zustandmaschine Meint hier eigentlich das, was man "kooperatives Multitasking" nennt.
Habe ich irgendwas verpasst? Bisher war es hier immer so, dass man für ein Delay gekreuzigt wurde. Demnächst kommt bestimmt noch LED ohne Vorwiederstand[sic!] und Taster ohne Entprellung.
Dyson schrieb: > Bisher war es hier immer so, dass man für > ein Delay gekreuzigt wurde. Nöö.... Gegen ein notwendiges Delay ist nicht zu sagen. Wenn es zu einer Kugel am Bein entwickelt, wird es zu Recht verurteilt. Die Medaille hat also 2 Seiten.
Multitasking! Ja, das Problem ist nicht das delay(). Das Problem ist, du willst während der Wartezeit etwas anderes machen. Ohne Multitasking Betriebssystem wird das aufwendig. Übrigens - auch MC-Programme, die für Multitasking konzipiert sind, benutzen gelegentlich ein delay(). Z.B. beim initialisieren eines Displays. Hier lohnt sich der Aufwand ohne delay() nicht.
Noch ein Kommentar schrieb: > Multitasking! > > Ja, das Problem ist nicht das delay(). Das Problem ist, du willst > während der Wartezeit etwas anderes machen. Ohne Multitasking > Betriebssystem wird das aufwendig. Quark. Lies den Artikel.
Noch ein Kommentar schrieb: > Ja, das Problem ist nicht das delay(). Das Problem ist, du willst > während der Wartezeit etwas anderes machen. Ohne Multitasking > Betriebssystem wird das aufwendig. Das ist nicht ganz richtig. Selbst die AVR Arduino Welt erlaubt ein Leben neben delay() Also Nebenläufigkeit, ohne RTOS oder vergleichbares. Wenn Arduino das schon kann, dann bekommt man das auf jedem µC hin.
Noch ein Kommentar schrieb: > Ja, das Problem ist nicht das delay(). Das Problem ist, du willst > während der Wartezeit etwas anderes machen. Ohne Multitasking > Betriebssystem wird das aufwendig. Das ist ja nun mal echt Unsinn.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.