Hallo, ich habe mir eine riesige 4-Digit 7-Segment Anzeige mittels 8-bit Schieberegistern zusammengebaut. Diese Anzeige soll 60 Min. runterzählen. Dies tut sie auch wunderbar. Das Problem, das ich nun habe, ist dass ich die Uhr gerne jederzeit anhalten oder reseten können möchte. Allerdings sind die 4 Digits in 4 for-Schleifen eingewickelt, die wiederrum in einer if-Bedingung hängen. Dort kommt man scheinbar nicht so leicht wieder raus. Der Code sieht ungefähr so aus: WENN (Knopf gedrückt) FOR (Zähler 10 Min) FOR (Zähler 1 Min) FOR (Zähler 10 Sek) FOR (Zähler 1 Sek) Schieberegisterfunktion delay 500ms Mittelpunkte an delay 500ms Mittelpunkte aus ENDE ENDE ENDE ENDE ENDE Ich würde diesen Ablauf nun gerne jederzeit unterbrechen können. Dazu muss ich permanent in der Lage sein, einen INPUT abfragen zu können. Das wäre hier mit einer IF-Bedingung zwar möglich, wenn auch nicht sehr zuverlässig, da die delays dort ziemlich eingreifen. Könnte mir eventuell jemand einen Tip geben? Danke und liebe Grüße Mike
:
Verschoben durch User
Dann bau doch noch ne Schleife die zB. 1/10s zählt und häng da die Abfrage rein.
Oh mann, dass ich da nicht selbst drauf gekommen bin... man kann die "idle" time der 500ms delays locker in 2 for-schleifen packen, die jeweils 500ms abzählen - klar ! Danke für den Tipp ! btw, kennt sich hier jemand mit interrupt routinen aus und kann mir sagen ob die gut oder schlecht sind? gibt ja beim Programmieren immer fatale Fehler, die man nicht begehen sollte...
Du scheinst noch mehr Fragen zu haben, als auf diesem Weg beantwortet werden könnten. Schade das Du nicht erwähnt hast welchen Prozessor Du verwendest. Falls es ein Pic sein sollte, schau mal hier rein: http://www.sprut.de/ Für AVR: https://www.mikrocontroller.net/articles/Absolute_Beginner-AVR_Steckbrettprojekte Allgemein ist hier das stöbern auch sehr informativ: https://www.mikrocontroller.net/articles/Hauptseite
Mike schrieb: > btw, kennt sich hier jemand mit interrupt routinen aus und kann mir > sagen ob die gut oder schlecht sind? Interrupts sind für die Programmierung von µC (bzw. Computern allgemein) essentiell. Dein "Programm" oben ist das perfekte Beispiel, wie man es nicht macht. Denn obwohl du den Prozessor zu 100% auslastest, sind 99,9% davon unproduktives Warten in delay(). Und auch die Verwendung von delay() zum Aufbau eines Timers ist schon mal grundsätzlich falsch. Denn zu den nominal 1000ms Wartezeit kommt ja noch die Zeit die der µC wirklich arbeiten muß (z.B. Bits in die Schieberegister schieben). Und wenn du diese Zeit nicht kennst (oder schlimmer: wenn die nicht konstant ist) dann kannst du sie auch nicht berücksichtigen und dein Timer läuft (mehr oder weniger) konstant zu langsam. Richtig würde man das mit einem Timerinterrupt machen, der mindestens 2 mal (für das Blinken des Doppelpunkts) pro Sekunde auslösen muß. Flexib- ler wird man, wenn man den Timer etwas öfter auslösen läßt. Vielleicht 10 oder 100 mal pro Sekunde. Und dann zählt man im Interrupt eine Variable hoch und kann bei den entsprechenden Werten den Doppelpunkt ausschalten oder auf die nächste Sekunde weiterzählen. Ob man die Anzeige gleich im Interrupt aktualisiert oder das im Haupt- programm macht, ist Geschmackssache. Aber selbst bei 100 Interrupts pro Sekunde hat man mehr als genug Zeit, das im Interrupt zu machen. Die Tastenabfragelogik kann man dann im Hauptprogramm machen. Meine Empfehlung wäre, das AVR-GCC-Tutorial durchzuarbeiten, insbesondere die Kapitel über Interrupts.
Axel S. schrieb: > Interrupts sind für die Programmierung von µC (bzw. Computern allgemein) > essentiell. Nach so einem Satz hab ich schon lange vergeblich im Internet gesucht. Ich hatte bis jetzt so oft die Situation, in dem ich eine Prozedur oder eine Schleife auf "Knopfdruck" beenden hätte müssen. In den Foren steht dann immer "denk dir eine Abbruchbedingung aus" oder "programmier drum herum", sogar "goto" Befehle wurden empfohlen. Axel S. schrieb: > Denn zu den nominal 1000ms Wartezeit kommt ja noch die Zeit die der µC > wirklich arbeiten muß (z.B. Bits in die Schieberegister schieben). Und > wenn du diese Zeit nicht kennst (oder schlimmer: wenn die nicht konstant > ist) dann kannst du sie auch nicht berücksichtigen und dein Timer läuft > (mehr oder weniger) konstant zu langsam. Ja, die Uhr hinkt auf 60 Min. ca. 25 Sekunden hinterher... Ich hätte nicht gedacht, dass die Verarbeitung (die sich meines Wissens nach ja im Nanosekundenbereich abspielt) einen so großen zeitlichen Einfluss hat. Das Gute ist, dass es bei der Uhr für meinen speziellen Anwendungsfall nicht auf die Minute ankommt. Axel S. schrieb: > Meine Empfehlung wäre, das AVR-GCC-Tutorial durchzuarbeiten, > insbesondere die Kapitel über Interrupts. Ich setze mich sofort dran. Vielen Dank für die ausführliche Antwort !!!
Mike schrieb: > Axel S. schrieb: >> Interrupts sind für die Programmierung von µC (bzw. Computern allgemein) >> essentiell. > > Nach so einem Satz hab ich schon lange vergeblich im Internet gesucht. > Ich hatte bis jetzt so oft die Situation, in dem ich eine Prozedur oder > eine Schleife auf "Knopfdruck" beenden hätte müssen. In den Foren steht > dann immer "denk dir eine Abbruchbedingung aus" oder "programmier drum > herum", sogar "goto" Befehle wurden empfohlen. Alles Quatsch. Sieh dir das einmal an FAQ: Timer und überleg, wie du deine Uhr damit programmieren kannst. Tip. die komplette Uhrenlogik - also das Weiterzählen auf Sekunden, Minuten, Stunden, kommt in die ISR. *Edit*: Bei dir natürlich das Runterzählen. > nicht gedacht, dass die Verarbeitung (die sich meines Wissens nach ja im > Nanosekundenbereich abspielt) Na ja. Nanosekunden sind jetzt ein bischen zu klein gedacht. Aber der springende Punkt ist: zum einen: das alles summiert sich zum anderen: auch wenn auf deinem Quarz zb 16Mhz drauf steht. Der macht keine 16000000.000000000 Schwingungen pro Sekunde! Auch ein Quarz hat Abweichungen. Das besondere bei einem Quarz ist nicht, dass er genau die aufgedruckte Anzahl Schwingungen macht, sondern dass er (temperaturkonstanz vorausgesetzt) diese Anzahl nicht verändert. Die aufgedruckte Anzahl ist ein Fertigungsproblem. Klar könnte man jeden Quarz darauf hintrimmen - aber du könntest das nicht mehr bezahlen. Das alles lässt sich dann so zusammenfassen: dein _delay_ms(1000) wird eine Wartezeit von zirka 1000 Millisekunden aufweisen. Für den Hausgebrauch um eine LED augenscheinlich 1 Sekunde lang blinken zu lassen, ist das gut genug. Aber es sind eben nicht 1000.000000000 Millisekunden sondern ein bischen mehr oder weniger. Und diese Abweichungen summieren sich. Ein Tag hat ca 86000 Sekunden. Ist die Zeitbestimmung im delay auch nur 0.1 Millisekunden daneben, dann hast du über den Tag gerechnet dann eben schon einen enormen Fehler.
:
Bearbeitet durch User
Klasse, danke nochmal für die tollen Antworten ! Ich werde mich nun mal stärker mit diesem Thema befassen.
Axel S. schrieb: > Ob man die Anzeige gleich im Interrupt aktualisiert oder das im Haupt- > programm macht, ist Geschmackssache. Karl H. schrieb: > Tip. die komplette Uhrenlogik - also das Weiterzählen auf Sekunden, > Minuten, Stunden, kommt in die ISR. Eine Frage hätte ich noch. Ich habe gelesen, dass die ISR so kurz wie möglich sein und so schnell wie möglich wieder verlassen werden sollen (ich weiß nicht genau, warum). Wenn ich nun den gesamten Runterzählalgorithmus in die ISR packe, würde das nicht dagegen verstoßen?
@ Mike (Gast) >> Tip. die komplette Uhrenlogik - also das Weiterzählen auf Sekunden, >> Minuten, Stunden, kommt in die ISR. >Eine Frage hätte ich noch. Ich habe gelesen, dass die ISR so kurz wie >möglich sein und so schnell wie möglich wieder verlassen werden sollen >(ich weiß nicht genau, warum). Damit sie sich im Extremfall nicht überschneiden. Eine TIMER-ISR, welche all 10ms aufgerufen wird, darf nicht länger als 10ms dauern. Siehe Interrupt. > Wenn ich nun den gesamten >Runterzählalgorithmus in die ISR packe, würde das nicht dagegen >verstoßen? Das passt schon, man muss es nicht übertreiben.
Mike schrieb: > Eine Frage hätte ich noch. Ich habe gelesen, dass die ISR so kurz wie > möglich sein und so schnell wie möglich wieder verlassen werden sollen > (ich weiß nicht genau, warum). Wenn ich nun den gesamten > Runterzählalgorithmus in die ISR packe, würde das nicht dagegen > verstoßen? So schnell wie möglich. So langsam wie nötig. Du willst haben, dass die Uhrzeit immer integral (= als eine Einheit) betrachtet wird. An dieser Stelle ist die komplette Zeit eine einzige Einheit, die sich "zufällig" in kleinere Einheiten unterteilt. Und mal Hand aufs Herz. 3 Variablen runterzählen mit ein bischen Logik dazwischen ist nicht wirklich lang. In Relation zu den fixen 'Kosten' eines ISR Aufrufs ist das immer noch verschwindend.
Moin! überkam mich gerade so:
1 | TimerInterupt_alle_10ms: |
2 | { Hundertstel -= Step; |
3 | Wenn (Hundertstel < 0) |
4 | { Hundertstel = 99; |
5 | Sekunde--; |
6 | Wenn (Sekunde < 0) |
7 | { Sekunde = 59; |
8 | Minute--; |
9 | Wenn (Minute < 0); |
10 | { Step = 0; |
11 | Minute = 0; |
12 | Sekunde = 0; |
13 | Hundertstel = 0; |
14 | } |
15 | } |
16 | } |
17 | } |
18 | |
19 | Main: |
20 | { Step = 0; |
21 | Minute = 0; |
22 | Sekunde = 0; |
23 | Hundertstel = 0; |
24 | |
25 | Timer auf 10ms setzen und starten |
26 | IRQs einschalten |
27 | |
28 | While true |
29 | { Wenn (Knopf 'Start' gedrückt) |
30 | { Minute = 60; |
31 | Sekunde = 0; |
32 | Hundertstel = 0; |
33 | Step = 1; |
34 | } |
35 | Wenn (Knopf 'Pause' gedrückt) |
36 | { Step = 0; |
37 | } |
38 | Wenn (Knopf 'Weiter' gedrückt) |
39 | { Step = 1; |
40 | } |
41 | Wenn (Knopf 'Reset' gedrückt) |
42 | { Step = 0; |
43 | Minute = 0; |
44 | Sekunde = 0; |
45 | Hundertstel = 0; |
46 | } |
47 | Schieberegisterfunktion; |
48 | Wenn (Hundertstel > 50) |
49 | { Mittelpunkte an; |
50 | } |
51 | sonst |
52 | { Mittelpunkte aus; |
53 | } |
54 | // Irgendwelcher anderer Krempel |
55 | } |
56 | } |
Gruß Jobst
...es wurde alles gesagt. Der Timer ist wirklich eine Sache für einen Interrupt. Wenn Du in C programmierst ist das alles so schnell, das es völlig unproblematisch ist. Der AVR läuft ja wenigstens mit 1 MHz, selbst da ist eine Sekunde eine kleine Ewigkeit. Solltest Du eine ISR für den Timer verwenden, musst Du aber etwas aufpassen bei der Routine die die Zeit in die Bits für die 7-Segmentanzeige umwandelt. Sonst könnte es passieren, das sich während der Bit-Berechnung bzw. Ausgabe die Zeit ändert, im schlimmsten Fall z.B. gerade auf eine andere Minute springt, also sowas - Zeit ist 13:00 - Deine Ausgaberoutine liest die Minuten und fängt and die Bits für die ersten beiden Segmente zu berechnen - Die Ausgaberoutine wird unterbrochen, der Timer-Interrupt wird ausgelöst, Zeit springt auf 12:59 - Deine Routine berechnet die Bits für die Minuten -> Deine Anzeige wäre 13:59, was natürlich falsch ist Um sowas zu verhindern gibt es mehrere Möglichkeiten: - Du kopierst die Werte für Stunden/Minuten in dieser Weise: StundenCopy = Stunden; MinutenCopy = Minuten; if(StundenCopy != Stunden) dann StundenCopy = Stunden; MinutenCopy = Minuten; endif Dabei wird geprüft ob sich nach dem Kopieren des MInutenwertes etwa die Stunde geändert hat (durch einen Timer-Interrupt), wenn ja, müssen die Stunden/Minuten erneut gelesen werden. - Du sperrst die Interrupts während Deiner Ausgaberoutine - Du packst auch die Ausgabe mit in den Timer-Interrupt
:
Bearbeitet durch User
Markus M. schrieb: > Deine Anzeige wäre 13:59, was natürlich falsch ist ... zumindest für eine Millisekunde oder so steht das dann da so ... Das kann man getrost ignorieren. Gruß Jobst
Markus M. schrieb: > Sonst könnte es passieren, das sich während der Bit-Berechnung bzw. > Ausgabe die Zeit ändert, im schlimmsten Fall z.B. gerade auf eine andere > Minute springt Sehr unwahrscheinlich. Denn schließlich wird man die Zeit ja nur dann neu ins Schieberegister schieben, wenn sie sich geändert hat. Man würde also in der ISR ein Flag (volatile uint8_t) setzen, das dem Hauptpro- gramm sagt daß sich die Zeit geändert hat und das Display aktualisiert werden muß. Und dann hat man dazu eine ganze Sekunde Zeit, bis sich die Zeit das nächste Mal ändert. Der hypothetische AVR mit 1MHz Taktfrequenz könnte also ca. 1 Million Instruktionen ausführen. Das ist erheblich mehr als man braucht um 32 Bits rauszuschieben. OK, wenn man den Doppelpunkt blinken lassen will, dann muß man das Display zweimal jede Sekunden updaten. Deswegen wäre es u.U. cleverer den Doppelpunkt direkt mit einem IO-Pin zu steuern. Andererseits gibt es keinen Bonus für eingesparte Taktzyklen. Das Display zweimal pro Sekunde upzudaten wird den AVR nicht umbringen.
Wenn man die Software in Funktionen und Prozeduren unterteilt, die Zähler und die Ausgabe trennt - entsteht erst garnicht so ein unübersichtlicher Code-Haufen. Dann ergeben sich auch die Möglichkeiten zum Anhalten und Resetten quasi von selber ...
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.