Kann mir jemand einen Hinweis geben, wie man Peters Code so erweitern
kann, dass zusätzlich zum langen Drücken einer Taste auch noch das sehr
lange Drücken erkannt wird? (Soll als eine Art "reset" verwendet werden,
also nicht für die normale Bedienung.) Vielen Dank im voraus,
...Rolf
Rolf Niepraschk schrieb:> Kann mir jemand einen Hinweis geben
Wo ist Dein Problem?
Niemand hindert Dich daran, das entsprechende Bit in key_state zu testen
und damit einen Zähler im Timerinterrupt hochlaufen zu lassen.
Peter Dannegger schrieb:> Sowas macht man am besten als Statemaschine.> Für 3s Wartezeit muß der Timerinterrupt >12ms sein oder man nimmt ein> uint16_t als Zähler.> enum { OFF, ON, BLINK };>> int main()> {> uint8_t state = OFF;> for(;;){> switch( state ){> case OFF:> led_off();> if( get_key_short( 1<<KEY1 ))> state = ON;> if( get_key_long( 1<<KEY1 ))> state = ON;> break;> case ON:> led_on();> if( get_key_short( 1<<KEY1 ))> state = OFF;> if( get_key_long( 1<<KEY1 ))> state = BLINK;> break;> case BLINK:> led_blink();> if( get_key_short( 1<<KEY1 ))> state = OFF;> if( get_key_long( 1<<KEY1 ))> state = OFF;> break;> }> }> }
Hallo,
doofe Frage, aber bist du sicher dass das so funktioniert?
Ich habe das meiner Meinung nach genauso, allerdings passiert auf
Tastendruck nichts. Außerhalb der SM funktioniert es tadellos.
Jemand eine Idee?
Danke!
AnBo schrieb:> aber bist du sicher dass das so funktioniert?> Ich habe das meiner Meinung nach genauso, allerdings passiert auf> Tastendruck nichts.
Dein Code ist dermaßen verschieden zu meinem Beispiel, es wäre ein
Wunder, wenn er genauso funktionierte.
Probier doch meinen einfach mal aus.
Hallo,
ich bin noch mal alles Schritt für Schritt durchgegangen und habe es mit
deinem Code verglichen. Da ist mir dann letztendlich aufgefallen, dass
ich die Pullups in einem Schritt wieder ausgeschaltet hatte.
Danke für deine ganzen Mühen und die viele Hilfestellung!
Guten Tag zusammen.
Ich habe mich die letzen Tage mit dem hier vorgestellten Entprellcode
auseinandergesetzt und möchte als erstes sagen:
"Danke an Peter Dannegger für diesen tollen code und an alle die ihn
weiter verbessert haben!"
Zugegeben, ich habe nicht jede Zeile verstanden, aber weiss jetzt wie
ich ihn anwenden kann. Vielleicht würde ja Zettel und Stift zu besserem
Verständnis führen ;-)
Als kleines Dankeschön habe ich mir mal die Mühe gemacht alles hier
zusammengetragene in eine Datei zu packen. Die Nutzung von mehreren
Eingangsports, für mehr als acht Tasten, habe ich allerdings nicht
eingebaut.
Da ich programmieren nur mit PICs gelernt habe, kann ich nur die
Funktion dieses angehängten codes bestätigen (Simuliert mit Proteus).
Wenn also mal jemand die Version für AVR kontrollieren könnte wäre das
super. Ich habe es aber nach bestem Gewissen versucht umzusetzen.
Den Simulationsschaltkreis für Proteus habe ich auch angefügt. Also viel
Spass damit :-)
Schöne Grüsse
Mario
@Peter Dannegger:
Darf ich den Code im CCS Forum posten? Unter Angabe der Quelle
natürlich.
Hallo,
bei mir reagieren die Taster nicht ;/
Musste ein paar Anpassungen der Timer für den Attiny841 vornehmen. Die
ISR wird alle 10ms aufgerufen, dass soweit schon richtig.
Die Leds hab ich direk an PINA 0 - 2 angeschlossen und auf der anderen
Seite mit einem 470Ohm Widerstand nach GND verbunden.
Die Taster sind direkt mit PINB 0 - 2 verbunden und auf der anderen
Seite mit GND verbunden.
Nun Pumba?
Der KEY0 fehlt in der Definition.
Vielleicht solltest Du erstmal nur mit get_key_press() arbeiten.
Wenn Du das verstanden hast, kannst Du die zeitabhängigen
Taster-Funktionen einbauen, konfigurieren und auch nutzen.
Ok ?
Mathias
dein Vorlag ist unsinning, wenn Du nicht weist wie der Code von PeDa
funktioniert, dann rate bitte nicht.
Mathias O. schrieb:> key_state ^ ~KEY_PINdas so zu machenkey_state ^ KEY_PIN
Das mit der neuen Ansteuerung mit PUEx hatte ich bislang nicht gewusst.
Laut dem Datenblatt werden die Pullups wie folgt gesetzt.
PUEx |= (1<<PUEx1) | (1<<PUEx0);
die einzelnen Bits PUEx0 sind im Atmel Studio nicht per defines gesetzt.
Spielt aber keine Rolle. Da ich die auch mit 0, 1 etc machen kann.
Jedoch geht es so wie ich mir gedacht habe nicht.
1
KEY_DDR&=~ALL_KEYS;// configure key port for input
2
PUEB|=ALL_KEYS;
3
//KEY_PORT |= ALL_KEYS; // and turn on pull up resistors
Der Takt ist hier NICHT das Problem! Das geht. Ob nun "8E6" oder
"8000000UL" spielt keine Rolle.
Quarz ist keiner im Einsatz. Ich arbeite mit der internen Oscillator.
Der läuft mit 8MHz
Das Atomisieren per cli(); sei(); finde ich ein wenig heftig. In meiner
App läuft parallel ein anderer Interrupt, der alle 256 CPU cycles für
ein paar Takte zuschlägt (soft PWM). Der würde regelmäßig und oft
gekillt.
Es geht ja nur darum, den Tasten - Debouncer für die Dauer der
Tastenstatus Abfragen lahm zu legen. Andere Interrupts, die die Tastatur
- Variablen nicht verändern, dürfen ja schadlos laufen ... od'r?
Ich lösche / setze daher selektiv nur das Timer - Interrupt Enable Bit
des 10ms Timers. Sollte - was sehr unwahrscheinlich, aber immerhin
möglich ist - genau während dieser Sperre ein Timer Overflow eintreten,
gibt's in der Tastatur (und nur dort) einen Glitch von 10ms ... das ist
meiner Meinung nach vertretbar.
Servus,
Wiso ein "Glitch von 10ms" ?
Ich verstehe das Interruptsystem etwas anders:
Wenn der Timerinterrupt wieder aktiviert wurde, was ja nur ein paar µs
dauert, springt man sofort in Timer Interrupt Routine, wenn das Timer
Interrupt Flag gesetzt ist.
Klar können auch noch andere Interrupts mit höhere Priorität anstehen,
die will ich jetzt nicht betrachten.
@Uwe S
Servus,
wieder mal nachgelesen ... hast recht ... für den Fall, dass der 10ms
key-Timer während der Atomexplosion überläuft (und das entsprechende
Timer Interrupt Flag 0 ist) wird der Interrupt gespeichert und nach dem
erneuten Setzen des Flags ausgeführt (in meinem Fall TIFR2.TOV2)
Liebe Grüsse aus +/- Wien
Mario Sachse schrieb:> "Danke an Peter Dannegger für diesen tollen code und an alle die ihn> weiter verbessert haben!"
auch ich möchte noch mal Danke sagen für diese Entprellroutine.
Ich nutze sie im AVR, im Arduino
mal zum Tasten entprellen, klappt sogar an einer I2C LCD Schnitte mit
PCF8574 die ich für 8 Tasten nutze.
Beschreibung,
Timer Interrupt 64µs für IRMP
wenn ein Counter auf 10ms hochgezählt hat wird in die I2C
Tastaturabfrage gesprungen (100-200µs) dort wird ein Flag gesetzt das es
ein langer IRQ wird, der Interrupt für IRMP wieder freigegeben und
solange das Flag gesetzt ist am I2C Tastenabfragen vorbei gesprungen.
Das Flag wird nur zurückgenommen wenn I2C wieder frei ist, der TWI
Auftrag erledigt ist.
So entgeht mir kein IRMP Befehl, kein Tastendruck.
Für eine Laser Steuerung wo ausser den regulären 20Hz Impulsen mit 80µs
Breite zur Lasertriggerung auch leider noch Fremdimpulse kommen unter
10µs und fremd triggern habe ich nun die IRQ auf 10µs gesetzt wie eine
Tastaturabfrage und trigger den Laser nur noch wenn ein solider Trigger
größer 40µs erfolgt. Getestet mit einem Arduino als Pulsgenerator, ich
kann F und Trigerbreite schön einstellen und sehen wie PeDas
Entprellroutine wirkt.
Moin,
ich nutze die Entprellroutine auf einem STM32F1 und habe bei einem
Taster festgestellt, dass er häufiger nicht als gedrückt erkannt wurde.
Die Funktion im IRQ wird alle 10ms aufgerufen und es gab keine atomic
Blöcke in den Tastenfunktionen.
Nun dachte ich mir, alle IRQs sperren ist etwas zu viel des Guten, es
geht ja nur um das Auslesen der Tastenpins.
Spricht etwas gegen folgenden Ansatz?
In der debounce.c
1
staticvolatileuint8_tIRQdoNotReadFlag;
2
3
uint16_tget_button_press(uint16_tbutton_mask){
4
IRQdoNotReadFlag=1;
5
button_mask&=button_press;
6
button_press^=button_mask;
7
IRQdoNotReadFlag=0;
8
returnbutton_mask;
9
}
in der Funktion die dann im IRQ aufgerufen wird:
1
voidcall_button_in_IRQ(void)// Call this in SysTick-Handler
2
{// Should be called every 10ms
3
staticuint16_tct0,ct1;
4
#ifdef USE_KEY_REPEAT
5
staticuint16_trpt;
6
#endif
7
uint16_ti;
8
uint16_tBUTTON_MASK_NOW=BUTTON_PORT;// get Stade of the InputDataRegister for the used Port
if((button_state&REPEAT_MASK)==0)// check repeat function
20
rpt=REPEAT_START;// start delay
21
if(--rpt==0){
22
rpt=REPEAT_NEXT;
23
button_rpt|=button_state&REPEAT_MASK;
24
}
25
#endif
26
}
27
}
Das scheint schon deutlich besser zu funktionieren, ich habe aber den
Eindruck, dass gelegentlich "kurze" Tastendrücke doch noch unerkannt
bleiben. Bin mir aber nicht ganz sicher, ob es nicht evtl. die
Entprellung ist. Eigentlich müsste aber ein gedrückter Taster deutlich
länger dauern, als die Entprellzeit.
Hallo,
bei den AVR µC kann man alle Interrupts Sperren und dann wieder
freigeben, oder nur den zugehörigen Timer-Interrupt.
Die Abarbeitung des Timer-Interrupts wird dann nach seiner Freigabe
"sofort" nachgeholt. Und geht nicht 'verloren'.
Bei deiner Realisierung in call_button_in_IRQ() geht aber dieser
Interrupt verloren !
Also hier würde ich mal ansetzen.
Hallo Uwe,
danke für die Antwort. Interessanter Gedanke, das ergibt durchaus Sinn.
Wie macht man das denn bei den STM32? Bei mir sorgt der SysTick für die
10ms Zeitbasis, aber das wird ja auch noch für andere Dinge benutzt und
daher möchte ich das nicht aus dem Takt bringen.
Je häufiger ich also Tastenabfragefunktionen benutze, desto häufiger
würde der SysTick IRQ kurzzeitig gesperrt werden...
Wenn ich über
Hallo Forumsmitglieder,
ich habe mich schon versucht an den Codebeispielen für Microchip PIC
Mikrocontroller aber ich scheitere leider beim Aufruf der Funktionen.
Ich nutze 4 von 8 Pins des PORT A als Eingänge, der Rest sind Ausgänge.
Compiler ist XC8 in Verbindung mit einem PIC16F628.
Beim Aufruf der Funktion in meinem Main Programm gibt mir der Compiler
zu verstehen dass er "KEY1" nicht kennt.
1
if(get_key_short(1<<KEY1))
2
i++;
Die Fehlercodes lauten:
1
(361) function declared implicit int
1
(192) undefined identifier "KEY1"
Gerne füge ich noch den Quellcode der "unbounce.c" diesem Post hinzu.
Gruß
Martin
Dave schrieb:> Nun dachte ich mir, alle IRQs sperren ist etwas zu viel des Guten, es> geht ja nur um das Auslesen der Tastenpins.
Wenn Dein IRQ so extrem zeitkritisch ist, daß er nicht mal eine
Verzögerung um 2 CPU-Befehle verträgt, dann hast Du weitaus größere
Probleme.
So ich nochmal, nachdem ich den Fehler der richtigen Deklaration beheben
konnte nun eine neue Herausforderung:
Es sind 4 Taster am PIC angeschlossen an RA0, RA1, RA6 und RA7 (alle als
Input in den TRISA Regsiter gesetzt, der Rest des PortsA ist als Output
deklariert)). Die Entprellroutine funktioniert auch zu 50% denn nur die
letzten beiden Taster RB6&7 werden ausgewertet. Ich bin leider etwas
verunsichert ob ich etwas falsch gemachte habe daher hier dier Code in
voller Schönheit. Nicht wunder, es handelt sich um einen Microchip
PIC16F628.
Hier der Inhalt der Headerdatei:
1
#ifndef UNBOUNCE_H
2
#define UNBOUNCE_H
3
4
#include"sw_spi.h"
5
6
#define KEY_PORT PORTA // Input register
7
#define KEY0 /*PORTAbits.RA*/1 // KEY 0...3
8
#define KEY1 /*PORTAbits.RA*/0 //
9
#define KEY2 /*PORTAbits.RA*/6 //
10
#define KEY3 /*PORTAbits.RA*/7 //
11
12
// Type all keys for repeat and long press function */
Um das Verhalten der "inneren" Variablen sowie der verschiedenen
Abfrageroutinen mal genauer zu beobachten sowie damit zu experimentieren
habe ich die ganze Routine mal in ein PC-taugliches C-Programm
gewandelt.
Vielleicht kann damit ja noch jemand was anfangen.
Hallo,
ich verwendet die original Routine von Peter Dannegger und diese
funktioniert perfekt.
Bevor ich es für mein Projekt versuche, funktioniert die Routine auch,
wenn man z.B. 3 PINs als Ausgang schaltet.
Das bedeutet man wertet einen gesamten Port aus, der sowohl Ein als auch
Ausgänge hat.
Können(und wenn ja welche?) Probleme auftreten?
Liebe Grüße
Wuhu W. schrieb:> funktioniert die Routine auch,> wenn man z.B. 3 PINs als Ausgang schaltet.
ja
Wuhu W. schrieb:> Das bedeutet man wertet einen gesamten Port aus, der sowohl Ein als auch> Ausgänge hat.
aber das Ergebnis wird mit den definierten KEYs (Eingänge) oder bei der
Abfrage *) verundiert, je nach dem ob high- oder low- aktiv, bei Abfrage
auf high ver"unden", bei Abfrage auf low negiert ver"unden"
*) ret_key=get_key_press( ALL_KEYS ); // welche Taste(n) will ich
wissen?
1
// key.h -----------------------
2
3
#ifndef KEY_H
4
#define KEY_H
5
6
uint8_tget_key_press(uint8_t);
7
uint8_tget_key_rpt(uint8_t);
8
uint8_tget_key_short(uint8_t);
9
uint8_tget_key_long(uint8_t);
10
uint8_ttest_i2c_key(void);
11
//void timer2_init(void);
12
13
externvolatileuint8_tkey_state;// debounced and inverted key state:
Wuhu W. schrieb:> Können(und wenn ja welche?) Probleme auftreten?
Die Entprellung ist je Pin unabhängig.
D.h. auch 7 Out-Pins sind dem einen In-Pin völlig wurscht.
Nur die Maske für Repeat-Funktion will nur die aktiven Inputs sehen.
http://www.compuphase.com/electronics/debouncing.htm
Falls ihr immer noch Schwierigkeiten oder ein Brett vorm Kopf habt, lest
euch die Erklärung im Link auch mal durch. Etwas kleinschrittiger.
Ich habe etwas gebraucht (also nen guten halben Bleistift) an Hand des
obigen Codes das Konzept nachzugehen. Man hat, glaub ich, einfach zuerst
Probleme sich klar zu machen, was Peter wann als logisch '1' und '0'
speichert (die internen Zustände und aktuellen Ports) und dass das Ding
halt rückwärts zählt.
Wenn es nur um das Entprellen geht, reicht es, wenn der Eingang in
definierten Zeitintervallen (Timer-Int.) abgefragt wird, die größer sind
als die max. Prellzeit.
Beispiel:
Der Taster wurde gedrückt und prellt noch während der 1. Abfrage.
Möglichkeit 1:
Er wird während des Prellens zufällig als "nicht gedrückt" erfasst.
Nicht weiter schlimm: Er war vorher nicht gedrückt und wird nun eben für
eine Intervall-Länge weiterhin als "nicht gedrückt" angesehen. Danach
hat er ab der 2. Abfrage ausgeprellt und wird fortan statisch als
"gedrückt" erfasst, solange er gedrückt bleibt.
Möglichkeit 2:
Er wird schon während des Prellens zufällig als "gedrückt" erfasst.
Prima! Das war ja auch die Absicht beim Drücken. Bei den folgenden
Abfragen hat er ausgeprellt und wird weiterhin statisch als "gedrückt"
ausgewertet.
Mehr Aufwand zum reinen Entprellen braucht's nicht.
Alles darüber Hinausgehende dient nicht mehr dem Entprellen des Tasters,
sondern der prophylaktischen Störunterdrückung von extern eingestreuten
Spikes oder Bursts auf die mitunter langen Taster-Zuleitungen. Wo diese
Störquellen ausgeschlossen werden können, reicht die Timer-Entprellung.
Spart wieviele FLASH-/RAM-Bytes?
Ganz abgesehen von der Implementierungs-/Fehlersuch-/Umbau(weils doch
mehr Funktion braucht)-Zeit.
Es gibt was, das tut was es soll, wozu also neues erfinden.
@Bastler
Es ging mir nur um die simple Betrachtung des reinen Entprellens, das
bzgl. der Komplexität meist 'überkandidelt' interpretiert und "gelöst"
wird.
Dazu braucht es nicht eine einzige zusätzliche Codezeile. Die
Eingangsabfrage muss ohnehin gemacht werden. Indem man diese in einen
zeitlich passenden Timerinterrupt verfrachtet, ist das reine
"Entprellungsproblem" gelöst, wie ich im vorigen Beitrag gezeigt habe.
Dort wo diese reine Entprell-Funktionalität ausreicht, braucht man keine
All-inclusive-Routine.
Hallo Simpel,
im rein mathematischen Sinn, hast Du leider nichts gezeigt.
Anforderung, ich habe 8 Tastereingänge und möchte verschiedene Events
von der Entprellroutine erhalten.
1) Wurde eine Taste gedrückt ?
2) Wurde eine Taste losgelassen ?
3) Ist eine Taste immer noch gedrückt (repeat pressed) ?
4) Wurde eine Taste nur kurz gedrückt ?
Bitte schreibe, als Beweis, das Vorgegebene als Code deine bevorzugten
Programmiersprache nieder.
Karl M. schrieb:
> Bitte schreibe, als Beweis
Was soll er beweisen? Denn
Simpel schrieb:
> Es ging mir nur um die simple Betrachtung des reinen Entprellens
Mit seiner entsprechenden (simplen) Problemlösung hat er ganz recht, da
brauchts doch nicht mehr (erwähnte fehlende Störeinstreuungen
vorausgesetzt).
Hallo,
ich habe die Entprellung bei mir mit einem PIC implementiert, und sie
funktioniert auch einwandfrei.
Jetzt habe ich aber ein Problem weil ich einen zweiten Timer habe der
alle 148 us aufgerufen wird.
Mein Timer für den Entpreller ist auf 10ms gestellt.
der zweite Timer hat eine höhere Priorität, aber selbst ohne die bringt
er die Routine anscheinend so durcheinander das Tasten als gedrückt
erkannt werden obwohl diese nicht gedrückt werden. Oder erst nach
mehrmaligem schnellen drücken erkannt werden.
Kann das an dem schnellen Timer liegen oder habe ich doch irgendwo in
meinem Code mist gebaut? Sobald ich den Timer deaktiviere oder dessen
Interrupt funktioniert alles.
Ich kann meine Frage selber beantworten...
Zitat datasheet:
If both low-priority and high-priority interrupts are
enabled, the Stack registers cannot be used reliably to
return from low-priority interrupts. If a high-priority
interrupt occurs while servicing a low-priority interrupt,
the Stack register values stored by the low-priority
interrupt will be overwritten. In these cases, users must
save the key registers in software during a low-priority
interrupt.
Normalerweise erledigt das ja der Compiler.
Natürlich unter der Voraussetzung das man richtig programmiert.
Auszug aus dem C Compiler Handbuch:
A high-priority interrupt uses the shadow registers to save and restore
the minimal context, while a low-priority interrupt uses the software
stack to save and restore the minimal context
1
#pragma code
2
#pragma interrupt high_prior_InterruptHandler
3
4
voidhigh_prior_InterruptHandler(void)
5
{}
6
7
#pragma code
8
#pragma interrupt low_prior_InterruptHandler
9
voidlow_prior_InterruptHandler(void)
10
{}
Keine Ahnung ob das aus einem Beispiel kopiert wurde oder wo ich das her
hatte, das Projekt liegt schon etwas länger in der Ecke.
Wenn aber für beide Interrupts das gleiche Register zum speichern
verwendet wird, tritt denke ich genau das ein was oben beschrieben
wurde.
1
#pragma code
2
#pragma interruptlow low_prior_InterruptHandler
3
voidlow_prior_InterruptHandler(void)
4
{}
Das ist also korrekt. Und jetzt funktioniert es auch :)
Hallo Peter!
Deine Entprell-Routine nutze ich schon seit ein paar Jahren - vielen
Dank dafür!
Nun wollte ich die get_key_long() Funktion anwenden (zur Einstellung
eines Zahlenwertes: kurzer Druck = +1, langer Druck = +10).
Ergebnis: Ein langer Tastendruck wird zuverlässig erkannt. Bei einem
kurzen gibt es leider nur eine Erfolgsquote von ca. 90%, d.h. etwa jeder
10te wird falsch als langer Tastendruck erkannt.
Etwas verwirrt habe ich einen Attiny13A nur mit der Entprellroutine
gefüttert, ein Knopf, zwei LEDs, 9,6MHz/8, verschiedene Werte für
REPEAT_START und REPEAT_NEXT, aber dasselbe Problem.
Hat jemand schon einmal Åhnliches beobachtet?
Außerdem ist mir folgendes aufgefallen:
Ich hatte den Sinn von REPEAT_NEXT so verstanden, dass ein weiterer
langer Druck gezählt wird, wenn der Knopf nach Ablauf dieses
Zeitintervalls immer noch gedrückt ist.
Nun wird in der get_key_long() aber hier:
1
returnget_key_press(get_key_rpt(key_mask));
key_press durch den Aufruf von get_key_press() gelöscht, und jeder
folgende Aufruf von get_key_long() liefert 0, auch wenn in key_rpt das
entsprechende Bit gesetzt ist.
Mit
1
return(key_press&get_key_rpt(key_mask));
wird es etwas besser, aber dann gibt's nach jedem langen Tastendruck
einen kurzen "gratis", was wohl auch nicht der "Spezifikation"
entspricht...
Mir fällt aber nix ein, wie man dies ohne Einführung einer weiteren
Variable lösen könnte.
Hallo Chris,
OK, auch da wird natürlich ein langer Druck zusätzlich als kurzer
gezählt - allerdings gleich am Anfang und nicht erst beim Loslassen.
Das dürfte für den User besser einzuschätzen und komfortabler sein.
Hast Du schon einmal das Problem mit "falsch-langen" gehabt (also einem
kurzen Druck, der als langer erkannt wurde?)
Mit welchem Takt arbeitest Du? Wieviele Interrupts generierst Du?
In meiner "richtigen" Anwendung habe ich F_CPU = 1MHz. Ich brauche einen
Millisekunden-Takt, den ich mit TIM0_COMPA generiere.
Bei jedem 10. wird dann per
1
if(!(msec%10))
2
{
3
debounce();
4
...
die Abfrage der Taster vorgenommen.
Der Tiny13A zum Testen taktet geringfügig höher (9,6/8MHz).
Hi,
genau, der erste Tastendruck erhöht um 1 (bzw. erniedrigt in meinem
Beispiel). Hält man weiter gedrückt, wird in 10er Schritten gezählt.
Beim Loslassen passiert nichts.
Ich führe die Tastenentprellung im Timerinterrupt alle 10 ms durch.
Wenn ich auch einen ms-Takt brauche, stelle ich den Interrupt auf 1ms
und zähle im Interrupt eine Variable bis 10 hoch. Die Entprellfunktion
rufe ich direkt im Interrupt (als inline Funktion) auf.
AVR-Bastler schrieb im Beitrag #4471676:
> Ergebnis: Ein langer Tastendruck wird zuverlässig erkannt. Bei einem> kurzen gibt es leider nur eine Erfolgsquote von ca. 90%, d.h. etwa jeder> 10te wird falsch als langer Tastendruck erkannt.
Dann ist die Zeit für lang zu lurz.
AVR-Bastler schrieb im Beitrag #4471676:
> Ich hatte den Sinn von REPEAT_NEXT so verstanden, dass ein weiterer> langer Druck gezählt wird, wenn der Knopf nach Ablauf dieses> Zeitintervalls immer noch gedrückt ist.
Das wird kompliziert, wenn man alle 3 Sachen machen will (kurz, lang,
repepeat), nicht nur für den Bediener.
Beitrag "Re: Universelle Tastenabfrage"Beitrag "Re: Universelle Tastenabfrage"
AVR-Bastler schrieb im Beitrag #4472189:
> if (!(msec % 10))> {> debounce();> ...
Da fällt mir gerade auf:
Wenn dieser Code bei dir in der Hauptschleife steht, kann er innerhalb
einer Millisekunde öfter aufgerufen werden, wenn deine Schleifenlaufzeit
kleiner als 1 ms ist. Weil die geprüfte Bedingung ja die ganze Zeit
erfüllt ist, bis msec erhöht wird.
Das würde das falsch-erkennen der langen/kurzen Tastendrücke erklären.
Mach vielleicht besser einen Zähler, der bis 10 zählt und wieder auf 0
gesetzt wird. Dann wird wirklich nur jedes 10. mal die Entprellung
durchgeführt.
chris schrieb:> Da fällt mir gerade auf:> Wenn dieser Code bei dir in der Hauptschleife steht, kann er innerhalb> einer Millisekunde öfter aufgerufen werden,
Das ist ein valider Punkt. - Danke!
Ich dachte schon, das war's. Leider nein.
Aber ich habe nun mit den Zeiten noch etwas gespielt.
Wenn ich die debounce() alle 50ms aufrufe, funktioniert es. Dafür fallen
dann sehr schnelle Tastendrücke hinten runter.
Also kann ich mir aussuchen, ob ich am langen oder am kurzen (Zeit-)
Ende Fehler habe.
Ich denke, der Taster ist einfach schrottig.
AVR-Bastler schrieb im Beitrag #4473359:
> Das ist ein valider Punkt. - Danke!> Ich dachte schon, das war's. Leider nein.
Wie sieht denn dein aktueller (vollständiger) Code aus?
Hallo Peter,
Peter D. schrieb:> Dann ist die Zeit für lang zu lurz.
Ich habe die REPEAT_START schon auf 255.
Aber danke für die get_key_long_r() und get_key_rpt_l(). Ich werde die
morgen mal simulieren.
AVR-Bastler schrieb im Beitrag #4473359:
> Wenn ich die debounce() alle 50ms aufrufe, funktioniert es.
Das glaub ich Dir nicht.
50ms * 255 = 12,75s.
Da mußt Du ja auf der Taste einschlafen für Langdruck.
Also entweder Deine Zeitbasis stimmt nicht, Deine 50ms stimmen nicht
oder Du hast in der Repeat-Maske Pins drin, die garnicht für Langdruck
vorgesehen sind.
Sorry - hab gerade "Land unter", da bin ich noch nicht wieder dazu
gekommen.
Deine Rechnung ist natürlich richtig, Peter.
Deine Annahme, ich würde bei der Fehlersuche wahllos alle Parameter
gleichzeitig ändern, natürlich nicht.
Aber auch 255*10ms = 2,55s ist für einen Anwender zu lang. Trotzdem habe
ich es damit mal versucht, einfach um herauszufinden, ob dieser
Parameter das Problem löst. Hat er nicht.
Hi Peter,
Ich habe Deinen Code jetzt integriert.
Woran ich aber gerade grüble ist, dass das "get_key_long" bereits nach
sehr kurzem "Lang-Drücken" des Tasters registriert wird.
Ich habe in den Defines diesen Wert von 50 auf 100 geändert "#define
REPEAT_START 100 "
Da sich das Verhalten aber nicht ändert, nehme ich also an, dass ich an
der falschen Stelle gedreht habe.
Mein Ziel ist es, ein Lang-Drücken von etwa 2 Sekunden zu detektieren.
Danke im Voraus
Hannes
Johannes H. schrieb:> Da sich das Verhalten aber nicht ändert, nehme ich also an, dass ich an> der falschen Stelle gedreht habe.
Du hast hier verschwiegen, dass Du noch woanders dran geschraubt hast,
wie Du in folgendem Thread schreibst.
Johannes H. schrieb:> Ebenso habe ich den Vorteilquotienten von 1024 auf 1600 gesetzt da ich> mit einem höheren Takt als in Deinem Beispiel arbeite.
1600? Zeig mal den Code, wie Du das gemacht hast.
Johannes H. schrieb:> Hab mir ausgerechnet, welcher Wert für 16MHz da ungefähr stehen muss, um> 10ms zu erhalten-->1600
Dann schau Dir mal TCCR0 im Datenblatt und im Code an! Das wird auf den
Teiler 1024 gesetzt. Diesen setzt Peter dann für TCNT0 wieder ein.
Du kannst da nicht einfach 1600 hinschreiben. Den Teiler 1600 kann man
(naturgemäß) gar nicht konfigurieren in TCCR0. Im Datenblatt stehen die
gültigen Teiler. Wenn, dann solltest Du besser den Faktor 10e-3
korrigieren. Mit welcher Frequenz läuft Peters Code? 1MHz oder 8MHz?
Dann überlege, wie Du ihn für 16MHz modifizieren musst.
Im anderen Thread hatte Dich auch schon jemand darauf hingewiesen...
aber offenbar hast Du das nicht verstanden ;-)
Es kommt bei Peters Code gar nicht so groß auf die Zeiten an. Sie
sollten jedoch zumindest größenordnungsmäßig passen. Das reicht in den
meisten Fällen.
Ja den Vorteiler nicht, aber den Zählwert im TCNT-Register kann man doch
beliebig setzen.
Peters Code nutzt 1MHz, da habe ich mich um Faktor 10 verguckt.
Also wird bei meinen 16MHz der Interrupt 16 Mal häufiger aufgerufen
(wenn ich den Code so lasse), oder?
Johannes H. schrieb:> Peters Code nutzt 1MHz, da habe ich mich um Faktor 10 verguckt.> Also wird bei meinen 16MHz der Interrupt 16 Mal häufiger aufgerufen> (wenn ich den Code so lasse), oder?
Peter hat das portabel formuliert für TCNT0: Er benutzt F_CPU. Wenn
dieser bei Dir auf 16000000UL steht, dann ist alles bereits richtig. Der
Wert
(F_CPU / 1024 * 10e-3 + 0.5)
wird dann automatisch 16 mal größer.
Du brauchst also in der Regel nichts mehr anzupassen.
Ok, ja seh ich ein, da bin ich einem Denkfehler aufgesessen...
Aber das ursprüngliche Problem dass "get_key_long" nach einer viel zu
kurzen Zeit auslöst, besteht immer noch.
Hast Du da noch eine Idee?
Ich habe mal versucht alle Funktionalitäten aus dem Thread zusammen zu
tragen und in ein möglichst wiederverwertbares Modul zu packen.
Vielleicht kann es jemand brauchen.
Die verwendete cm_atomic.h ist unter folgendem Link zu finden:
Beitrag "Re: atomic-lib für stm32"
Dass die Werte vom Pollintervall abhängen ist klar, aber warum soll das
dann gleich falsch sein?
Mag ja sein, dass du recht hast, verstehen tue ich es aber nicht.
Zugegebenermaßen habe ich die Repeat-Geschichten aber auch noch nie
genutzt, sondern einfach so übernommen. Daher ist es sehr
wahrscheinlich, dass ich den entsprechenden Beitrag nicht gelesen habe.
Lieber Herr Dannegger, Vielen Dank!
Der Code arbeitet sofort und perfekt!
Ich sollte nur am Anfang für mehr Freiheit mit Pins für die Tasten etwas
anpassen:
1
voidknopf_abfrage(void){
2
3
staticunsignedcharct0=0xFF,ct1=0xFF,rpt;
4
unsignedchari,key_pin;
5
6
key_pin=0xff;
7
if(!T_U)key_pin&=~(1<<KEY_U);
8
if(!T_O)key_pin&=~(1<<KEY_O);
9
if(!T_M)key_pin&=~(1<<KEY_M);
10
if(!T_SP)key_pin&=~(1<<KEY_SP);
11
if(!T_START)key_pin&=~(1<<KEY_START);
12
13
i=key_state^~key_pin;// key changed ?
usw.
mit entsprechenden
1
#define PIN_KN_U PINB
2
#define KN_U PB3
3
#define T_U (PIN_KN_U & (1<<KN_U))
4
#define KEY_U 4
5
6
#define PIN_KN_O PINB
7
#define KN_O PB4
8
#define T_O (PIN_KN_O & (1<<KN_O))
9
#define KEY_O 3
10
11
#define PIN_KN_M PINC
12
#define KN_M PC0
13
#define T_M (PIN_KN_M & (1<<KN_M))
14
#define KEY_M 2
15
16
#define PIN_KN_SP PINC
17
#define KN_SP PC1
18
#define T_SP (PIN_KN_SP & (1<<KN_SP))
19
#define KEY_SP 1
20
21
#define PIN_KN_START PINB
22
#define KN_START PB2
23
#define T_START (PIN_KN_START & (1<<KN_START))
24
#define KEY_START 0
Auch REPEAT_NEXT habe ich auf 10 gesetzt, aber das ist wohl eine
Geschmackssache.
Vielen Dank für diese Möglichkeit!
Hallo,
ich würde meine C Kenntnisse als sehr bescheiden bezeichnen. Dennoch
versuche Ich gerade den Code aus
Beitrag "Universelle Tastenabfrage" zu verstehen.
Ich verstehe den Sinn folgender Anweisung nicht ...
ct0 = ~( ct0 & i );
Unmittelbar zuvor wird der Wert von ct0 initialisiert.
static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
Wenn nun ct0 mit 0xFF initialisiert wurde und ein bitweises und mit i
erfolgt, dann kann das Ergebnis doch immer nur gleich dem Wert von i
sein. Oder?
Demzufolge wäre doch ct0 ~= i; doch viel kürzer und einfacher zu
verstehen.
Wo liegt mein Denkfehler?
Gruß
Henning
Hallo,
was möchtest du mir damit sagen?
Input please ...
Im Beitrag zur Entprellung steht nichts von
static uint8_t ct0;
sondern
static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
Bedeutet das, das der Wiki Beitrag, der den Anfängern ja erklären soll
wie es funktioniert, fehlerhaft ist?
Dann sollte das umgehend von einem "Wissenden" korrgiert werden...
Gruß
Henning
Henning schrieb:> Hallo,>> was möchtest du mir damit sagen?> Input please ...>> Im Beitrag zur Entprellung steht nichts von> static uint8_t ct0;>> sondern> static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;>> Bedeutet das, das der Wiki Beitrag, der den Anfängern ja erklären soll> wie es funktioniert, fehlerhaft ist?
Grundlagen!
Das "static" sorgt dafür, dass die Variable nur ein Mal initialisiert
wird, und nach dem Verlassen der Funktion aber immer noch besteht
(ähnlich einer globalen Variable, nur dass der Zugriff auf diese nur
innerhalb dieser Funktion erfolgen kann.
Würde es fehlen, würde die Variable bei jedem Funktionsaufruf neu
initialisiert werden.
Ob da nur eine defklariert wird, oder fünf, ist dabei egal.
Ahhh,
jetzt hab ich's.
Habe aus Karl's erster Antwort einfach nicht entnehmen können, das es um
das Wörtchen "static" in der Variablendeklaration geht...
Jetzt ist mir natürlich klar, das meine Schlussfolgerungen Unsinn waren.
Vielen Dank für die Erleuchtung.
Henning
Hallo Peter & Andere,
Ich bin bei der Anwendung Der Entprellroutine in meiner Anwendung auf
ein Problem gestoßen:
Mittels "Kurz Drücken" eines Tasters werden verschiedene Modi
durchgeschaltet, sprich ein index inkrementiert.
Mittels "Lang Drücken" desselben Tasters wird der µC mit Deiner
"try_sleep"-Funktion schlafen gelegt und mittels Level Change Interrupt
wieder aufgeweckt.
Jetzt habe ich das Problem, dass beim Aufwecken auch gleichzeitig ein
"Kurz Drücken" detektiert wird und damit der nächste Modus ausgewählt
wird, was nicht erwünscht ist.
Leider habe keine Möglichkeit gefunden, während der Aufwachphase, das
"get_key_short" temporär zu deaktivieren. Siehst Du/ihr eine
Möglichkeit?
Danke & Gruß
Johannes H. schrieb im Beitrag #4892788
> Leider habe keine Möglichkeit gefunden, während der Aufwachphase, das> "get_key_short" temporär zu deaktivieren. Siehst Du/ihr eine> Möglichkeit?
Dann mach das doch in der Einschlaf-Phase. Also deinen Index um 1
verringern, so dass er nach dem Aufwecken und dem "get_key_short" wieder
stimmt.
Hi Edson,
Ja das funktioniert, dass ich darauf nicht gekommen bin :)
Aber ich suche noch nach der eleganten Lösung. Den Index zu verringern
ist ja nur ne Art Workaround.
Hast Du schon mit Peters Debounce Algorithmus gearbeitet?
Hi, bin jetzt mit dem Workaround an eine weitere Grenze gestoßen und
muss nun eine andere Lösung finden.
Ich hatte gerade probiert, beim Einschlafvorgang einfach den Timer 0
Interrupt zu deaktivieren und eine kurze Zeit nach dem Aufwachen wieder
zu aktivieren, da hängt sich das Programm aber irgendwo auf :(
Peter/Edson, was für Optionen fallen euch denn noch ein?
Hallo!
Horst hatte ja schon mal in seinem Beitrag
Beitrag "Re: Universelle Tastenabfrage" angefragt, wie man
die Repeat-Funktion nicht nur für eine Taste, sondern für alle Tasten
unabhängig hinbekommt. Ein Szenario dafür ist z. B. ein Tastmodul mit
acht Eingängen, an die Taster in acht verschiedenen Räumen angeschlossen
sind.
Ich dachte mir, dass man dafür einfach einen weiteren vertikalen Zähler
verwenden könnte, dann allerdings z. B. 3-bit oder sogar noch breiter.
Den Code stelle ich mir so vor:
1
volatileuint8_tkey_state;// debounced and inverted key state:
2
// bit = 1: key pressed
3
volatileuint8_tkey_press;// key press detect
4
5
volatileuint8_tkey_rpt;// key long press and repeat
rpt_ct1=rpt_ct0^(rpt_ct1&i);// reset or count rpt_ct1
26
rpt_ct2=(rpt_ct0&rpt_ct1)^(rpt_ct2&i);// reset or count rpt_ct2
27
i&=rpt_ct0&rpt_ct1&rpt_ct2;// count until roll over ?
28
key_rpt|=key_state&i;// then repeat key
29
30
}
Die restlichen Funktionen sind identisch wie von Peter geposted.
Nur leider funktioniert das bei mir so nicht. Einige Tasten werden
sofort bei get_key_short erkannt, andere sofort bei get_key_long.
Zwischendurch ändert sich das mal. Keine der Tasten ändert beim länger
drücken ihren Repeat-Status.
Wie müsste die Timer-Routine richtig aussehen und wie wäre die korrekte
Erweiterung für einen 4- bzw. 5-bit vertikalen Counter?
Ich muss mich korrigieren. Der von mir gepostete Code funktioniert
genauso, wie gewünscht. Problem war nur, dass ich ihn gleich in
Assembler implementiert und sich da ein Fehler eingeschlichen hatte. Im
Beitrag hatte ich für mich als Kontrolle die C-Version geschrieben.
Ein weiteres Problem war das Timing. Ein 3-bit vertical counter ist doch
bisl arg wenig. Nun schwanke ich zwischen eine 4-bit oder 5-bit vertical
counter. Der 4-bit gefällt mir momentan am besten.
Das Schema zur Erweiterung um weitere Bits ist übrigens recht simpel und
offensichtlich:
1
i=key_state
2
rpt_ct0=~(rpt_ct0&i);// reset or count rpt_ct0
3
rpt_ct1=rpt_ct0^(rpt_ct1&i);// reset or count rpt_ct1
4
rpt_ct2=(rpt_ct0&rpt_ct1)^(rpt_ct2&i);// reset or count rpt_ct2
5
rpt_ct3=(rpt_ct0&rpt_ct1&rpt_ct2)^(rpt_ct3&i);// reset or count rpt_ct3
6
i&=rpt_ct0&rpt_ct1&rpt_ct2&rpt_ct3;// count until roll over ?
Mathias W. schrieb:> Problem war nur, dass ich ihn gleich in> Assembler implementiert und sich da ein Fehler eingeschlichen hatte.
Magst Du diesen Code auch mal veröffentlichen? Dann bräuchte ich mir
nicht die Mühe machen, die Komfortroutine in Assembler zu übertragen.
Die Kurz-Lang Erkennung wäre für mich wichtig.
Falls man bis zu 64 vertikale Zähler beliebiger Breite (besser: Höhe)
braucht, kann man das folgende template VCounter nehmen. Es ist genau
der gleiche vertikale Zähler mit impliziter Rücksetzung.
Vorteile:
- vollkommen generisch
- gekapselt
Nachteile:
- C++ mit TMP
- ein paar Zeilen Code in Header-Datei
Ist also der direkte Ersatz für ct0, ct1, ct2, ... im originalen Code.
Im Original Code von Peter Danneger wird die Globale Variable:
volatile uint8_t key_state;
deklariert aber im Code finde ich keine Startinitialisierung.
in der ISR finden dann auch gleich eine Bitoperation damit statt.
Da die Variable keine Initialisierung hat, nehme ich an das der
undefinierte Zustand für den Start bewusst in kauf genommen wird? Stimmt
das?
MfG Elias
Hallo
Versuche die Tasterabfrage in meinen Atmega 8 zum laufen zu bekommen. Es
geht so weit alles, komme nur mit der Nutzung der Tasten durcheinander.
Es sthet drin:
- get_key_press()
- get_key_rpt()
- get_key_press() mit get_key_rpt()
- get_key_short() mit get_key_long()
- get_key_short() mit get_key_long_r() und get_key_rpt_l()
Leider komme ich mit der Bezeichnung und der Nutzung nicht klar. Die
Beschreibungen sind alle relativ kurz geraten.
Welche Taste macht was wann und mit welcher?
Hallo,
Sollte man dann ich das Programm "befragen" und den Code der
Basisfunktionen verstehen?
Als Tipp, alle genannten Funktionen bekommen ein Bitmaske der Keys
übergeben.
Das habe ich getan. Kann mit einer Taste einschalten mit einer anderen
aus, kurz einschalten mit lang ausschalten das geht alles und klar.
Was mach ich aber damit:
get_key_short() mit get_key_long_r() und get_key_rpt_l()
Es werden 3 Tasten angegeben. Das ist mir unklar.
Von meiner Seite aus,
hatte ich die Basiscode von Peda (Peter Dannegger), Codeteile und die
Funktionsnamen in Assembler unter LunaAVR implementiert.
Dann auf beliebige Ports noch erweitert, so dass man über die
Hochsprache LunaAVR auch einfach zwei oder drei 8 Bit Ports entprellen
kann.
Hallo Karl M.
Habe deinen Rat befolgt und die Artikel von Peter durchgelesen. Muss
ganz ehrlich sein, das ist für mich ein ganzes Stück zu hoch. Da reichen
meine Verständnisse in C nicht aus.
Habe parallel dazu auf meinem Atmega 8 das Programm installiert und
verschiedene Versionen geteste. Mit if(get_key_press(1<<KEY_1)) kann
ich die LED ohne Probleme ein und mit if(get_key_press(1<<KEY_2))
ausschalten. Das geht. Leider erschliesst sich die Nutzung, wie Peter
sie beschreibt gar nicht.
Such jetzt nach der Funktion, mit Taster kurz drücken einschalten, mit
Taster lang drücken ausschalten. Dachte das diese Funktion ist
get_key_short() mit get_key_long(), geht aber nicht. Da ist wohl noch
ein grosse Denkfehler von mir drin.
Habe das nächste in den Griff bekommen. Es funktioniert jetzt auch
get_key_short und get_key_long Bedienung mit einem Taster ein und aus.
Hatte den Timer falsch eingestellt.
Heiner schrieb:> Guck mal in den C-Code 😉
Das habe ich gemacht. Leider reichen meine Kenntnisse von C dazu nicht
aus. Den Ablauf verstehe ich nicht (muss ich das?). Möchte den Code
anwenden und dazu zählt für mich die Nutzung der Tasten.
Steffen schrieb:> Heiner schrieb:>> Guck mal in den C-Code 😉>> Das habe ich gemacht. Leider reichen meine Kenntnisse von C dazu nicht> aus. Den Ablauf verstehe ich nicht (muss ich das?). Möchte den Code> anwenden und dazu zählt für mich die Nutzung der Tasten.
Damit wirst Du nicht weit kommen - was wirst Du machen, wenn etwas nicht
funktioniert ( und dieser Fall wird so sicher eintreten wie das Amen in
der Kirche 😉) - wieder hier laut um Hilfe rufen?
Allein die Frage “muss ich das?” zeigt Deine Unfähigkeit und die
Ignoranz.
Steffen schrieb:> Leider reichen meine Kenntnisse von C dazu nicht> aus. Den Ablauf verstehe ich nicht (muss ich das?).
Der Ablauf ist unter C etwas kreativer als außerhalb. Wenn du nur
Hochsprachen kennst dann wäre ein Konzept:
Tasten (Fernbedienung/Kabel) senden Ereignisse: A_down, A_repeat,
A_up_kurz, A_up_lang
an die Anwendung wesentlich einfacher (Tastatur-Treiber u.U. eher in
einfachen assembler).
Guten Abend,
ich möchte die Entprellroutine als Teil einer nicht kommerziellen
studentischen Projektarbeit verwenden.
Die Funktionen habe ich in eine Source Datei und die Defines, Variablen
und Prototypen in eine Header Datei geschrieben. Mit den Funktionen
frage ich in meinem Programm ein paar Taster ab.
Als Kommentar habe ich am Anfang beider Dateien geschrieben:
1
/*
2
* Entprellroutine von Peter Dannegger
3
* Von https://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_(nach_Peter_Dannegger)
4
*/
Wäre das so ausreichend gekennzeichnet oder gibt es für (diesen)
Programmcode auch sowas wie eine GNU Lizenz?
Hat da jemand Informationen zu oder von Herrn Dannegger diesbezüglich
etwas gelesen?
Ansonsten muss ich mal meinen Betreuer fragen ob sowas in Ordnung ist
und wie das gekennzeichnet werden muss.
Mit freundlichen Grüßen