Hi, Ich hab grad mal versucht, die Tasterentprellung von PeDa in der C-Version nachzuvollziehen und hab zwei Fragen dazu: 1.) Im Worst-Case wird der Tastendruck erst "prelldauer" + 4*10ms später registriert, richtig? Für mich liest sich das so, dass erst, wenn 4x nacheinander der gleiche Zustand gelesen wird, dieser als gültig angenommen wird. Und gelesen wird alle 10ms. Stimmt das so? 2.) cli() wird in jedem Schleifendurchlauf der main 4x aufgerufen - wahrscheinlich in deutlich kürzeren Intervallen, als 10ms. Bei jedem dieser Aufrufe werden doch die Interrupts gestoppt. Wieso wird dann die Interrupt Routine überhaupt mal aufgerufen?! Viele Grüße
:
avr schrieb: > 1.) Im Worst-Case wird der Tastendruck erst "prelldauer" + 4*10ms später > registriert, richtig? Für mich liest sich das so, dass erst, wenn 4x > nacheinander der gleiche Zustand gelesen wird, dieser als gültig > angenommen wird. Und gelesen wird alle 10ms. Stimmt das so? Ja, aber nicht nur im Worst case. > 2.) cli() wird in jedem Schleifendurchlauf der main 4x aufgerufen - > wahrscheinlich in deutlich kürzeren Intervallen, als 10ms. Bei jedem > dieser Aufrufe werden doch die Interrupts gestoppt. Wieso wird dann die > Interrupt Routine überhaupt mal aufgerufen?! Wo steht da ein cli()? Auf welche Beschreibung beziehst Du Dich? Ich verwende die Peda Routine öfters, aber ein cli habe ich nirgends im HP stehen. Gruz Andreas
> Ja, aber nicht nur im Worst case. Naja, es kann ja auch sein, dass durch Zufall während dem Prellen schon ein oder zwei mal der richtige Zustand gelesen wird, dann müsste man nach der Prellzeit nur noch 3 bzw 2 mal 10ms warten. >> 2.) cli() wird in jedem Schleifendurchlauf der main 4x aufgerufen - >> wahrscheinlich in deutlich kürzeren Intervallen, als 10ms. Bei jedem >> dieser Aufrufe werden doch die Interrupts gestoppt. Wieso wird dann die >> Interrupt Routine überhaupt mal aufgerufen?! > > Wo steht da ein cli()? Auf welche Beschreibung beziehst Du Dich? Ich > verwende die Peda Routine öfters, aber ein cli habe ich nirgends im HP > stehen. > Also nicht direkt, aber es wird ja get_key_short aufgerufen, das wiederum get_key_press aufruft, das cli() aufruft. Auf diese Art kommt es zu 4 Aufrufen von cli().
avr schrieb: > ... die Tasterentprellung von PeDa ... Das ist doch mal eine saubere Quellenangabe. :-( Soll jetzt jeder Hilfswille hier suchen, wie die Routine genau aussieht?
> ... Mit den 4 Aufrufen von cli() ..
... nach denen dann immer eine Funktion gerufen wird, die eine sei()
beinhaltet.
Und während der cli()-sei()-Phase werden anfallende Timer-INT's nicht
vergessen, sondern nur (hier nicht mal 1μs) verzögert. Es geht nur
darum, daß die Leseroutine ungestört Änderungen am "Zustand" der
Tastenabfrage machen kann.
Sorry, dachte irgendwie, ich hätte den Link gepostet. Es geht um das hier: http://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_.28nach_Peter_Dannegger.29 @jcw2: Ahh, das wusste ich nicht, dass die Interrupts nur verzögert werden. Danke! Kann ich mir das dann so vorstellen, dass der Timer stehen bleibt, aber nicht zurückgesetzt wird, und bei Aufruf sei() wieder weiter läuft?
Ein cli() verhindert lediglich, daß der Interupt bis zum sei() ausgelöst wird. Gruß Andreas
Okay. Habe jetzt gerade gelesen, dass der Timer ja trotzdem weiter läuft. Wenn der Timer dann also einen Interrupt auslösen würde, aber nicht kann, weil cli() aufgerufen wurde, wird dieser Interrupt gespeichert und sofort ausgeführt, sobald sei() aufgerufen wird, richtig? Was, wenn in der Zeit mehrere Interrupts passieren, zB von 2 verschiedenen Timern? Wird dann einer "vergessen", oder gibts da so ne Art Queue? Wie läuft das?
> Kann ich mir das dann so vorstellen, dass der Timer stehen bleibt, > aber nicht zurückgesetzt wird, und bei Aufruf sei() wieder weiter > läuft? Wenn der Timer überläuft, dann wird lediglich ein Bit im TIFRn-Register gesetzt. Und (stark vereinfacht) prüft die CPU nach jeder Befehlausführung ob das I-Flag gesetzt ist, also Interrupts ausgeführt werden sollen, und dann ob es anstehende Interrupts gibt, also z.B. TIFR0-TOV0 gesetzt ist. Wenn ja wird
1 | cli |
2 | call Timer0_Int_vector |
ausgeführt. Letzteres ist der Bereich ab 0x0000 im Flash, wo dann üblicherweise ein Sprung zur wirklichen ISR steht. Die ISR endet dann mit einen reti, was das I-Flag wieder setzt und zur unterbrochenen Stelle zurückkehrt. Und der Fakt, daß nach Befehlsausführung geprüft wird, führt dazu, daß nach jedem Interrupt mindesten ein Befehl ausgeführt wird, selbst wenn andere Interrupt-Requests anstehen.
Ach ja zur Frage "Queue": stehen mehrere an, dann werden diese in der Reihenfolge der Int-Vektoren abgearbeitet, also INT0 vor Timer0_OVF (so sind die meisten AVR IntVecTabellen aufgebaut)
Vielen Dank schonmal für die Infos. Eine OT Frage hab ich noch: Macht es (von außen betrachtet) einen Unterschied, ob ich a) TCNT0 preloade, überlaufen lasse und dann ISR(TIMER0_OVF_vect) aufrufe, oder b) OCR0 setze, TCNT0 bei Match zurücksetze und dann ISR(TIMER0_COMP_vect) aufrufe?
Naja, wenn es nicht einmalig sein soll, dann hat die Preload-Variante den Nachteil, daß der Preliad für den nächsten Zyklus in der ISR passiert, die ja nicht sofort laufen muß (wegen cli USW.), und auch wenn man den Preload auf TCNTn aufaddiert, bleibt ein Jitter den die Hardware-Variante CTC nicht hat. Nur wenn man einfach nur eine grobe Zeitbasis z.B. für PeDa braucht und/oder den Timer durchlaufen läst, würde ich auf CTC-Mode verzichten. Wenn ich z.B. exakt 1ms brauche, dann 16MHz Quarz, Timer0 CTC /64 OCR0A=249
:
Bearbeitet durch User
damit mir aber kein IRQ auch verzögert durch die Lappen geht, ich mache ja noch IRMP, zähle nach IRMP hoch bis 10ms und verzweige dann bei 10ms in Tastenauslesen, so habe ich keine 2 IRQ. Es können mir dann in der Tastenroutine IRMP IRQ durch die Lappen gehen, hatte mal versucht im long IRQ Teil alle 10ms den IRQ wieder freizugeben mit sei() und bei Wiedereintritt durch ein Flag an der long IRQ 10ms vorbeizuhüpfen, ich habe das aber noch nicht vollständig ergründet, irgendwie funkioniert es.
:
Bearbeitet durch User
Okay, dann werde ich das mit CTC machen. Wenn ich jetzt statt deinen 16MHz nur 8MHz hätte, müsste OCR0A ja nur halb so groß sein. Runde ich dann ab oder auf?
16M 8 250 = 1k Und von 0 ausgehend ist der 250te Takt der mit dem Wert 249 in TCNT0. CTC will den TOP-Wert im OCR0A, also 249. Bei halbem Takt muß bis 125 gezählt werden, TOP ist also 124. Wenn das 8MHz aus dem internen RC-Oszilator sind, dann ist es aber egal ob 124 oder 126 ;-)
:
Bearbeitet durch User
Danke. Ja, ich nutze den internen Takt. Hab auch schon festgestellt, dass die Sekunden damit bisschen kürzer sind ^^ Es ging mir nur um die Theorie dahinter :-)
avr schrieb: > Danke. Ja, ich nutze den internen Takt. Hab auch schon festgestellt, > dass die Sekunden damit bisschen kürzer sind ^^ wenn du ein Oszi hast kannst du ja trimmen auf 10ms +-Fehler
Oszi hab ich noch keins, ist aber unterwegs aus China (DSO138). Naja, zum Taster entprellen muss das ja nicht 100% stimmen. Die Taster prellen ja auch nicht jedes Mal exakt gleich.
Die 10ms von PeDa sind ja auch nur eine Hausmarke. Das ist eher das Raster für eine Zeitsteuerung, die man oft auch noch braucht. Wenn die Taster nicht zu schlecht sind und man keine 40ms Zeit hat zum Drücken (sind ja nur 25 Anschläge/s ;-), dann dürfen es auch 5ms sein, oder eben 20ms.
Carl D. schrieb: > Die 10ms von PeDa sind ja auch nur eine Hausmarke. Das ist eher das > Raster für eine Zeitsteuerung, die man oft auch noch braucht. Wenn die > Taster nicht zu schlecht sind und man keine 40ms Zeit hat zum Drücken > (sind ja nur 25 Anschläge/s ;-), dann dürfen es auch 5ms sein, oder eben > 20ms. genau aus diesem Grund nimmt man ja 10ms Interrupts, nicht wegen der Taster, die funktionieren auch mit 8, 15, 20ms, aber alle 10ms einen Counter hochzählen ist ne schöe Zeitbasis. Ich habe z.B. Aufgaben verteilt, alle 330ms LCD aktualisieren, egal wann ich ins Schattenram schreibe, wozu öfter LCD update machen? alle 250ms WS2812b aktualisieren alle Minute bei >500ms prüfen ob was anderes nicht zeitkritisches zu machen ist
@jar: Also du zählst dann in der ISR eine Variable hoch bis 33 und, wenn die 33 ist, aktualisierst du dein LCD, oder wie soll ich das verstehen? Ergo, du sparst dir einen weiteren Timer
avr schrieb: > @jar: Also du zählst dann in der ISR eine Variable hoch bis 33 und genau, so flimmert das LCD weniger und trotzdem wird die aktuelle Uhrzeit geschrieben und ich brauche nie drüber nachzudenken das zuviele Schreibvorgänge aufs LCD zu verschiedensten Zeiten von verschiedenen Orten kommen, wäre ja auch doof mal Fenster hoch zu schreiben und kurz danach die Uhrzeit zu aktualisieren oder die Temperatur.
avr schrieb: > Ergo, du sparst dir einen weiteren Timer Das ist Zweck der Übung. Aus dem schnellsten notwendigen Timer-Interrupt kann man durch Zählvariablen verschiedene Zeittakte ableiten. Dies spart Interrupts und die damit verbundenen Aufrufzeiten (Registersicherung usw). Setzt man in der ISR für die verschienen abgeleiteten Takte Flags (Semaphores, Merker), so können die Jobs in der Mainloop laufen. Dies spart Rechenzeit in der ISR, was die Verzögerung weiterer Interrupts gering hält. ...
Joachim B. schrieb: > und ich brauche nie drüber nachzudenken das zuviele > Schreibvorgänge aufs LCD zu verschiedensten Zeiten von verschiedenen > Orten kommen, LCD mit Schatten-RAM eignet sich auch hervorragend zum Debuggen, damit kann man sich sogar in schnellen ISRs Daten ausgeben lassen... Mache ich auch so, wenn auch nicht in C, sondern in ASM. ...
:
Bearbeitet durch User
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.