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?
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
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.
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 ;-)
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.
...