Datum:
Angehängte Dateien:Unter "Tasten entprellen - Bulletproof" ist eine zuverlässige Entprellung und gedrückt Erkennung für 1 .. 8 Tasten beschrieben. Aber oftmals will man Tasten sparen, indem man unterschiedliche Aktionen bei kurzem oder langem Drücken ausführt. Außerdem ist eine Wiederholfunktion z.B. bei Eingabe von Werten wünschenswert. All das kann man mit wenigen weiteren Kodezeilen erreichen. Im Interrupthandler wird eine Variable runtergezählt, sobald eine für die Wiederholerkennung vorgesehene Taste gedrückt wurde. Nach der Startzeit bzw. Wiederholzeit wird dann das Tastenbit in der Variable key_rpt gesetzt. Die Startzeit und Wiederholzeit können unterschiedlich gewählt werden. Um nun einen Stellwert hochzuzählen, wird einfach die Funktion get_key_rpt() aufgerufen, die dann das Tastenbit in key_rpt abfragt und zurücksetzt. Damit zwischen Abfragen und Zurücksetzen kein Timerinterrupt reinhaut, muß beides unter Interruptsperre erfolgen. Die nächste neue Funktion ist get_key_short() für die Erkennung des kurz Drückens. Das kann aber erst nach dem Loslassen erkannt werden. Dazu wird der key_state ausgewertet, d.h. erst wenn dort das Tastenbit wieder 0 ist, wird get_key_press() ein Wert übergeben, und kann ausgeführt werden. Damit dazwischen kein Timerinterrupt erfolgt, muß vor der Abfrage von key_state der Interrupt gesperrt werden, get_key_press() enabled dann wieder die Interrupts. Wird nur get_key_short() aufgerufen, d.h. nicht mit get_key_long() im Wechsel, dann liefert es auch einen Wert zurück nach langem Drücken. get_key_long() wiederum liefert erst dann einen Wert, wenn beide Funktionen get_key_rpt() und get_key_press() das Tastenbit gesetzt haben. Anbei ein Programmbeispiel für alle 4 Funktionen. Peter
Datum:
Hallo, Klasse! Ich benutze deine "normale" Entprellroutine schon ne Weile...funktioniert einwandfrei. Die neue Funktionalität ist das einzige was ich bisher vermisst hab... Nur nochmal ne Frage dazu: die Zeile TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5); // preload for 10ms tut was genau? Wäre klasse wenn du die etwas "aufschlüsseln" könntest. Gruß Fabian
Datum:
@Fabian "die Zeile TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5); // preload for 10ms tut was genau?" Nun T0 hat ja kein Compareregister. Um ihn zu verkürzen, muß man ihn in jedem Interrupt neu laden. Ein Laden mit -9 bedeutet, daß der nächste Interrupt nach 9 Zählschritten erfolgt. XTAL ist die Quarzfrequenz (z.B. 1MHz) 1024 ist der Vorteiler 10e-3 = 10ms +0.5 rundet (s16) convertiert den float-Wert nach integer. (u8) schneidet das High-Byte ab, da TCNT0 ja nur 8-bittig ist. Peter
Datum:
Alles klar...der erste Overflow passiert aber ganz normal nach 255ms oder? also 1Mhz/1024 * 255 ... erst beim ersten overflow stellste ja auf 10ms "um". Richtig verstanden? Gruß Fabian
Datum:
Hallo Peter, möcht nen FIFO anlegen, um Tastendrücke auch dann nicht zu verlieren, wenn die Anwendung mit einer anderen Funktion bissel länger beschäftigt ist. Im Timer0-INT könnte ich dazu doch auch gleich die Bodys der get_key_xx()-Funktionen unterbringen, die so erweitert werden, daß sie ins FIFO schreiben können und cli() / sei() weglassen oder ist das schlechtes Design??? cli() / sei() dann natürlich in der Funktion, die den FIFO ausliest wegen der FIFO-"head" u. "tail"-indizies, die sonst fehlerhaft werden könnten. THX Micha
Datum:
Ne FIFO würde ich aber nur für die einfache Druckerkennung machen. Bei lang/kurz/wiederholung braucht man unbedingt ein direktes Feedback, es sei denn, man ist Morsefunker. Peter
Datum:
Hallo, ich blick nicht ganz durch den Code durch... Wie muss ich vorgehen, wenn ich mit einem Taster bei kurzem und langem (ca. 1 sek) zwei verschiedene Aktionen durchführen möchte? andre
Datum:
@andre "Wie muss ich vorgehen, wenn ich mit einem Taster bei kurzem und langem (ca. 1 sek) zwei verschiedene Aktionen durchführen möchte?" short = kurz long = lang
// // release after short press: task 1 // long press: task 2 if( get_key_short( 1<<KEY1 )) LED_PORT ^= 1<<LED1; if( get_key_long( 1<<KEY1 )) LED_PORT ^= 1<<LED2; |
Peter
Datum:
Ich hab es so geändert:
#define REPEAT_MASK (1<<KEY0)
#define REPEAT_START 100
#define REPEAT_NEXT 20
....
if( get_key_long( 1<<KEY0 ))
value+=10;
if( get_key_short( 1<<KEY0 ))
value++;
Aber value wird immer 10 hochgezählt!
Datum:
Hab den Fehler gefunden... der Vorteiler vom Teiler war falsch eingestellt... Allerdings muss ich mehr als 2,5 Sekunden einstellen... da muss wohl ein 16 bit int her.
Datum:
Hallo Peter, ich hätte mal eine Frage zu deinem Code. Wieso muß das lesen und zurücksetzen der Bits 'atomic' ausgeführt werden? (z.B.: in Routine 'get_key_press') Kommt das von der AVR-Architektur? (kenne diese nicht!) /Koarl
Datum:
atomic meint, nicht durch Interrupts unterbrechbar und das bewirkt man mit der Klammerung durch cli() + sei(). Nur C-Instruktionen, die in eine einzige Assemblerinstruktion übersetzt werden, sind von Haus aus atomic (nicht teilbar). Peter
Datum:
Hallo, mir ist schon klar was mit atomic gemeint. Was ich mich frage was würde schief gehen wenn ich das 'atomic' weglasse. z.B.: so key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) Meiner Meinung nach kann da falls ein Interrupt dazwischen kommt nichts schief gehen. Da im Interrupt in der Variable 'key_press' nur Bit gesetzt werden. (Oder ist auf dem AVR der 'AND' bzw. 'EXOR' Befehl nicht 'atomic') /Koarl
Datum:
key_press ^= key_mask; Diese Instruktion besteht aus 3 Befehlen, ist also nicht atomar. Wenn also genau zwischen lesen, exoren und zurückschreiben von key_press der Timerinterrupt eine Taste erkennt, geht diese verloren. Peter
Datum:
Angehängte Dateien:Hi, ich habe heute den ganzen Tag damit verbracht (zu versuchen) den code zu verstehen .... leider nur mit teilerfolgen ;) vielleicht kann mir einer von euch weiter helfen. Was mir am meisten Kopfschmerzen bereitet sind diese Shiftoperantionen wie z.B. #define REPEAT_MASK (1<<KEY1^1<<KEY2) auch dies hantieren mit den KEYs versteh ich nicht so ganz ... spricht man damit einzelne Bits an oder sind das einfach Konstanten? Falls es Konstanten sind warum macht man dann soetwas ? if( get_key_long( 1<<KEY1 )) könnte man da nicht einfach if( get_key_long( 3 )) Mit #define KEY_PIN PINB werden doch die ganzen 8 Bit mit KEY_PIN verknüpft oder ? Dann müsste es doch auch funktionieren wenn ich nur ein Bit des Ports mit KEY_PIN verknüpfe oder nicht ? Folgendes ich habe einen Drehimpusgeber an meinen Port0 (Atmel 8052)angeschlossen. Dieser hat 3 Adern ... 2 zur Ermittlung der Drehrichtung und eine für den Drucktaster. Das Auswerten des Drehimpulses habe ich mit Hilfe aus diesem Forum schon hinbekommen. Vielen Dank nochmal ! *ich schweife schon wieder ab ;)* Also Der Drehimpulsgeber ist folgendermaßen angeschlossen P0^0 und P0^1 für die Drehrichtung und P0^2 für den Drucktaster Deswegen möchte ich auch nur dieses Bit abfragen und mit der Entprellroutine auswerten ... also habe ich #define KEY_PIN PINB einfach durch sbit KEY_PIN = P0^2; ersetzt. Die Tastenerkennung funktioniert jetzt auch, aber die Auswertung ob lang oder kurz gedrückt worden ist leider nicht .... Ich Programmiere in Keil und bin neu in der MC-Welt ... vielleicht kann mir ja einer von euch etwas auf die Sprünge helfen?
Datum:
Hm der Beitrag war eigentlich noch nicht fertig ... bin versehentlich auf "submit" gekommen ! Leider kann man seine eigenen Beiträge nicht nachträglich Editieren ... jedenfalls hab ich nicht gefunden wo ... Naja ich hoffe man versteht ihn ! Ich habe die KEIL version des Entprellcodes nochmal angehängt vielleicht hab ich ja da schon einen Fehler drin.... MfG Nils
Datum:
@Nils, der Code ist so geschrieben, daß immer ein ganzer Port entprellt wird. Welche der Portpins einen nun aber wirklich interessieren, übergibt man den Abfragefunktionen als Bitmaske, die der Compiler aus der Bitnummer per (1<<Bitnummer) erzeugt. In meinem Beispiel werden 3 Pins abgefragt (key0..key2). Peter
Datum:
Hi, Ich bin immer wieder überrascht wie schnell man in diesem Forum Antwort bekommt ! Daumen hoch !! g vielen Dank MfG Nils
Datum:
Wiederholung nach langem Tastendruck:
if( get_key_long( 1<<BUTTON ) || get_key_rpt( 1<<BUTTON )) |
Fehlt noch die Erkennung von gleichzeitig gedrückten Tasten! Falls da jemand ne Idee hat, ich krieg's nicht hin..
Datum:
@duundich Meinst Du ne Shift-Taste ?
if( get_key_press( 1<<KEY1 ){ if( key_state & (1<<SHIFT_KEY) ){ // do action Shift + KEY1 }else{ // do action KEY1 } } |
Peter
Datum:
Ich fürchte nicht, da in diesem Fall der 'shift key' eine normale Funktion hat: ich habe zwei Tasten, die jeweils bereits auf kurzen, und langen||rpt Tastendruck geprüft werden, etwa so:
if( get_key_short( 1<<BUTTONA )) programm - if( get_key_short( 1<<BUTTONB )) programm + if( get_key_long( 1<<BUTTONA ) || get_key_rpt( 1<<BUTTONA )) leiser if( get_key_long( 1<<BUTTONB ) || get_key_rpt( 1<<BUTTONB )) lauter |
Jetzt hätte ich gerne noch die Prüfung auf gleichzeitigen Tastendruck, kurz oder lang (funktion: aus). D.h., der Zustand müsste vor (oder/und nach) der Schwellzeit erkannt werden, ohne, daß die anderen Prüfungen eingreifen. Das habe ich leider nicht hinbekommen. A xxxxxxxxxxxxxx B xxxxxxxxxxxxxxxxx T-> | Würde mich über einen Hinweis in die richtige Richtung sehr freuen!
Datum:
Hi, also ich habe so eben versucht den Code vom peter dannegger an einem mega8 auszuprobieren. Habe den PORTA durch PORTD ersetz und die XTAL auf 16Mhz eingestellt....bekomme aber nichts sinnvolles raus. Die drei LEDs läuchtet permanent und das wars. Ne Idee was ich falsch gemacht haben könnte?
Datum:
@noXe "Ne Idee was ich falsch gemacht haben könnte?" Tja, was soll man dazu sagen ? Wenn wenigstens die komplette Source als Anhang dabei wäre, dann könnte man schonmal Softwarefehler suchen. Aber hellsehen kann ich leider nicht. Und so ins Blaue: "Versuch es mal mit 1 Mhz." bringt meistens nichts. Peter
Datum:
Danke an Peter für den Quelltext samt ausführlicher Kommentierung. Hab erstmal ein bisschen Anlauf gebraucht um das Programm richtig zu deuten, da jeder einen etwas anderen Programmierstil hat. Hab es auf meine Bedürfnisse angepasst und es funktioniert wunderbar. Besten Dank nochmal dafür.
Datum:
Hallo! Bei dem von Peter realisierten Code ist ein konstanter Tastendruck (long) nicht wirklich möglich, oder sehe ich das falsch? Bei längerem Drücken reagiert das System wie bei kurzem Tastendruck. Lediglich bei schnellem aufeinanderfolgenem Tastendruck kann eine Änderung (der Routine) festgestellt werden. Ist dem so, oder habe ich etwas falsch gemacht? Danke für Eure Hilfe
Datum:
Hallo zusammen, ich hab nochmal eine Frage zum deaktivieren der Interrups. Folgendes: Ich hab den Code "missbraucht" um eine Tastenabfrage über RS232 zu realisieren. Das sieht im Prinzip so aus: Jedesmal wenn eine Taste gedrückt wird (oder losgelassen) wird der keystate in einem byte gesendet. Jedes Bit steht dabei für eine Taste. Dieses Byte wird dann einfach für den "ursprünglichen" Tastenport verwendet! Ich weiss, dass das garantiert auch eleganter gelöst werden kann aber mir gefällt es da ich dann auch ohne weiteres die zusätzlichen Tastenfunktionen nutzen kann (außerdem bin ich ein absoluter Programier-Anfänger so dass ich froh bin wenn es für etwas schon eine fertige Lösung gibt ;-)) - und es funktioniert soweit! Das Problem das ich jetzt seh ist, wenn die Interrups deaktiviert sind kann ja auch kein neuer keystate empfangen werden - wie wahrscheinlich das ganze ist sein mal dahingestellt (der keystate wird zuerst bei einem receive-interrupt in einen Buffer geschrieben...) aber es ist nunmal Möglich! Deshalb nochmal die Frage (ich habs oben nicht ganz verstanden): Warum verlier ich den Tastendruck (nehmen wir mal an, dass die Tasten an einem Port sind)? Und kann ich z.B. einfach alle Interrups außer dem UART-Receive-Interrupt deaktivieren? Ich mein das sind doch ganz andere Zeitdimensionen (empfangen -> in Buffer schreiben -> Buffer auswerten)... Ich hoffe ihr könnt mir weiterhelfen Gruß Jörg
Datum:
Hallo! Ich versuche die Entprellroutine zu verwenden. Leider hab ichs noch nicht ganz geschafft. Ich hab einen ATTiny26 und versuche folgendes zu realisieren: 3 LEDs an PORTA 1,3,4 1 Taster an PINA 7 Das Programm soll wie folgt funktionieren: - beim einschalten, alle LEDs aus. - bei jedem kurzen tastendruck die nächste LED einschalten und bei der letzten bleiben bis.... - bei langem Tastendruck wieder auf 0 gesetzt wird. Ich hoffe das war verständlich. Dazu sind doch die Funktionen short und long oder? mit der Funktion get_key_press funktioniert das raufzählen, aber wie realisiere ich den langen tastendruck mit dem rücksetzen? Ich hoffe mir kann da wer helfen.
Datum:
Markus wrote:
> Dazu sind doch die Funktionen short und long oder?
Genau.
Siehe Beispiel.
Nicht vergessen, die Taste in REPEAT_MASK einzutragen.
Peter
Datum:
Angehängte Dateien:Ich wollte eigentlich die Datei anhängen, hat aber scheinbar nicht geklappt. Zum Problem: Bei kurzem Tastendruck leuchtet die erste LED, danach geht sie gleich wieder aus. Drücke ich mehrmals hintereinander funktioniert es auch. Sobald ich aber nicht mehr drücke gehen alle LEDs wieder aus.(sollte eigentlich erst bei key_press_long sein) Also kann was an dieser Routine nicht stimmen. Kann es sein, dass er da probleme hat, weil auch die LEDs am selben PORT liegen? mfg markus
Datum:
Hab den Fehler gefunden. Es lag daran, dass in dieser Routine die Taster aktive low sind. Meiner ist jedoch aktvie high. Habe folgende Zeile geändert: i = key_state ^ ~KEY_PIN; -> i = key_state ^ KEY_PIN; Das war hoffentlich richtig. Zumindest funktioniert es jetzt :)
Datum:
Hallo Peter, ich habe mich gerade mit deinem Code beschäftigt. Ich glaube ich habe dabei nen Fehler gefunden: und zwar einen allgemeinen Fehler bei der Behandlung von lange gedrückten Tasten. Wenn ich deine Routine in der ISR richtig verstehe, dann setzt du die rpt Zählvariable immer wieder auf den Ausgangswert solange keine Taste gedrückt wird. Sobald eine Taste gedrückt ist, geschieht dies nicht mehr und die rpt wird heruntergezählt. Jedoch unterscheidet deine Routine dann nicht welche Taste gedrückt ist und wie lange bereits. Kann nicht im Moment der Fall eintretten, dass (Annahme REPEAT_START = 50) Taste 1 die ersten 40 Interrupts gedrückt ist und nach Interrupt 30 auch noch Taste 2 hinzukommt. Nun wird im Code nach 50 Interrupts bei Taste 2 ein langes drücken festgestellt, obwohl sie nur 20 Interrupts gedrückt war. Ich hoffe das ist so weit verständlich. Und sollte auch nur als Anregung dienen. Gruß Thomas
Datum:
@Thomas, das ist absichtlich so. In der Praxis ist es so, daß man nur eine Taste lange drückt, z.B. count up und losläßt, wenn der gewünschte Wert erreicht ist. Würde man gleichzeitig count down drücken, würde der Wert immer +1,-1 machen, was ja ziemlich sinnlos ist. Damit man eine Shift-Taste definieren kann (z.B. count +1, oder +10), gibt es die Maske mit der nur die Repeat-Tasten definiert werden. Zu entprellende Eingänge ohne Repeat können also gleichzeitig aktiv sein (z.B. Endlagenschalter). Peter
Datum:
Hi Leutz, ich versuche grad die Routine auf einem Mega32 mit 16MHz Takt zu benutzen. Ich hab das Original C-Beispiel aus der Artikelsammlung benutzt (http://www.mikrocontroller.net/articles/Entprellung). Zur Auswertung geb ich einen Text auf ein LCD aus:
if( get_key_press( 1<<KEY0 )) {
lcd_gotoxy(0,1);
lcd_puts("T0");
}
if( get_key_press( 1<<KEY1 )) {
lcd_gotoxy(0,1);
lcd_puts("T1");
}
if( get_key_press( 1<<KEY2 )) {
lcd_gotoxy(0,1);
lcd_puts("T2");
}
|
Soweit funktioniert das auch, nur zeigt bei mit der erste Taster T0 oft fälschlicherweise T1 an. Und zwar immer wenn vorher eine andere Taste gedrückt war. Ich hab schon die verschiedensten Sachen probiert, weiss mir aber mittlerweile nicht mehr weiter. Any hints? Markus
Datum:
Markus _neu wrote: > Soweit funktioniert das auch, nur zeigt bei mit der erste Taster T0 oft > fälschlicherweise T1 an. Und zwar immer wenn vorher eine andere Taste > gedrückt war. Ich hab schon die verschiedensten Sachen probiert, weiss > mir aber mittlerweile nicht mehr weiter. Any hints? Wackler, Kurzschluß, unterbrochene Pullups, ... Schreib mal die LCD-Texte hintereinander, dann sieht mans besser. So weißt Du ja nicht, ob ein Text von einem anderen überschrieben wurde und wie oft er geschrieben wird. Peter
Datum:
Peter Dannegger wrote: > > > Schreib mal die LCD-Texte hintereinander, dann sieht mans besser. > > So weißt Du ja nicht, ob ein Text von einem anderen überschrieben wurde > und wie oft er geschrieben wird. > > > Peter Ja, gute Idee. Werd ich heute abend gleich mal probieren. Markus
Datum:
Hi, danke für deine Tipps Peter. Das Problem waren nicht aktivierte interne Pullups !?! Hab ich völlig übersehen. Durch das hinternander schreiben der Ausgabe konnte ich sehen dass da tatsächlich teilweise mehrere Tasterdrücke auf einmal registriert wurden. Gruß - Markus
Datum:
Was machen wenn man 10 Tasten braucht? 8 Stück funktionieren wunderbar. danke mathias
Datum:
@ mgiaco (Gast)
>Was machen wenn man 10 Tasten braucht?
Da wird man wohl oder übel den Code anpassen müssen und auf zwei Ports
zugreifen müssen. Oder gleich ne Matrix aufbauen, ist auch in der
Codesamlung verfügbar.
MFG
Falk
Datum:
hallo, fang grad erst an und bräuchte nur ne ganz normale interrupt-tastenentprellung für einen taster. würde mich über eine antwort sehr freuen. ich hab nen AT90CAN64 grade dran... gruß, ben
Datum:
Hallo, ich probier grade Peters C-Komfortroutine aus: http://www.mikrocontroller.net/articles/Entprellun... Mit WinAVR-20080430 funktioniert diese bei mir allerdings nur nach Einfügen einiger "volatile":
volatile uint8_t key_state; // debounced and inverted key state: // bit = 1: key pressed volatile uint8_t key_press; // key press detect volatile uint8_t key_rpt; // key long press and repeat |
Anderer Punkt: Der letzte Code-Abschnitt in dem Beispiel soll wohl den Status der unteren drei LEDs erhalten und das Leuchten einer weiteren LED über die höheren fünf LEDs schieben:
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){ uint8_t i = LED_PORT; i = (i & 0x07) | ((i << 1) & 0xF0); if( i < 0xF0 ) i |= 0x08; LED_PORT = i; |
Dieser Teil geht bei mir im Simulator mit AVR-Studio 4.14 (auch anschaulich mit hapsim) sehr gut. In der Realität passiert beim Drücken von KEY2 allerdings nichts. Ich habe LED_PORT PORTB und KEY_PORT PORTD, also die Standardschaltung aus dem ASM-Tutorial. Ins Blaue vermutet könnte das daran liegen, das mein Quarzoszillator mit an PortB hängt. Folgendes funktioniert nämlich (einfaches an/aus der vierten oder Blinken durch Wiederholungserkennung):
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){ LED_PORT ^=0x08; |
Viele Grüße & gn8 ! Sebastian
Datum:
Wenn 's da sonst keine Meinung zu gibt werde ich den Entprellung-Artikel mal entsprechend anpassen. Grüße, Sebastian
Datum:
Falk Brunner wrote: > @ mgiaco (Gast) > >>Was machen wenn man 10 Tasten braucht? > > Da wird man wohl oder übel den Code anpassen müssen und auf zwei Ports > zugreifen müssen. Oder gleich ne Matrix aufbauen, ist auch in der > Codesamlung verfügbar. > > MFG > Falk Hallo, drei!!! Ports (einer für Matrix, zwei für je 8 Tasten) würde mich auch (in gleichzeitiger Interrupt Abfrage!!!) interessieren. Habe leider nichts hierzu gefunden - liegt das an mir - gibt es so etwas schon ? Habe versucht Peter's Beispiel dahingehend zu erweitern ... aber anscheinend funzt meine Erweiterung manchmal nicht, an anderen Tagen problemlos ....! Würde mich daher lieber auf professionellen Code verlassen! Peter, hast Du vielleicht einen Hinweis, wie man so etwas professionell implementiert ? Wie würdest Du Deine Routine erweitern ? Matrix an sich ist kein Problem. Problem ist der Portwechsel ... Tasten verschiedener Ports im Wechsel ....!!!! Vielen Dank im voraus, Grüsse
Datum:
Hallo, auch ich hab Peters Code "Debouncing" eingesetzt und meiner Anwendung Pollin Atmel Evaluation-Board V2) angepasst. Dies Funktioniert soweit ganz gut mit einer Ausnahme von KEY2. Vielleicht liegt es daran, weil ich mit einem Pull-Down Widerstand arbeite und der Taster gegen VCC schaltet! Nun will ich diesen Code besser verstehen. Wer kann mir die Funktion erläutern?
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){ uint8_t i = LED_PORT; i = (i & 0x07) | ((i << 1) & 0xF0); if( i < 0xF0 ) i |= 0x08; LED_PORT = i; |
Vorab vielen Dank; Gruß Werner
Datum:
Werner Dornstädter wrote: > Nun will ich diesen Code besser verstehen. Wer kann mir die Funktion > erläutern? > >
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){ > uint8_t i = LED_PORT; > > i = (i & 0x07) | ((i << 1) & 0xF0); > if( i < 0xF0 ) > i |= 0x08; > LED_PORT = i; |
> Lauflicht über die obersten 5 LEDs. Daran sieht man schön die Wiederholfunktion. Peter
Datum:
Funktioniert wie ne 1! Aber kann mir jemand verraten, was man machen muss, damit eine Bedingung erst nach dem Loslassen eines langen Tastendruckes wahr wird ? also wie kombiniert man get_key_long(1<<KEY) mit get_key_release(1<<KEY) Zusätzliche Funktion:
uint8_t get_key_release( uint8_t key_mask )
{
cli();
key_mask &= key_release;
key_release ^= key_mask;
sei();
return key_mask;
}
|
Zusätzliche Zeile im Entprell-Code:
... i &= ct0 & ct1; key_release |= key_state & i; key_state ^= i; ... |
Datum:
Das Problem ist nämlich, dass der controller durch einen Tastendruck in den power-down mode versetzt werden soll, und mit einem Flankeninterrupt wieder aufwacht. D.h. bei einem langen Tastendruck wacht er durch die Flanke beim Loslassen sofort wieder auf. Umgekehrt (get_key_short) ist es so, dass dadurch, dass der Tastendruck nach dem aufwachen sofort erkannt wird, gleich wieder ein power-down ausgelöst wird.
Datum:
Bastler wrote: > Das Problem ist nämlich, dass der controller durch einen Tastendruck in > den power-down mode versetzt werden soll, und mit einem Flankeninterrupt > wieder aufwacht. > D.h. bei einem langen Tastendruck wacht er durch die Flanke beim > Loslassen sofort wieder auf. Na dann wartste eben bis zum Loslassen:
if( get_key_long( 1<<KEY0 )){ while( keystate & 1<<KEY0 ); sleep(); } |
Peter
Datum:
Geniales Programm ,aber kann mir nochmal wer die Bedeutung der Zeile TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); erklären. Falls ich eine Taktfrequenz von 1Mhz habe dann lautet das Ganze ja wie folgt: 1000000/1024 * 10e-3 + 0.5 = 1.47 ich verstehe den Zusammenhang zwischen 1.47 und 10ms nicht?!
Datum:
Hannes S. wrote: > Geniales Programm ,aber kann mir nochmal wer die Bedeutung der Zeile > > TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); > > erklären. Falls ich eine Taktfrequenz von 1Mhz habe dann lautet das > Ganze ja wie folgt: > > 1000000/1024 * 10e-3 + 0.5 = 1.47 Nö, da kommt 10,26 raus, also Teilerwert 10.
1MHz / 1024 / 10 = 97,6Hz = 10,2ms |
Peter
Datum:
> aber kann mir nochmal wer die Bedeutung der Zeile > TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); > erklären. Lies mal den ganzen Thread, dann findest du diesen Beitrag von Peter: Beitrag "Re: Universelle Tastenabfrage"
Datum:
Danke Micha - hab den ganzen Thead schon gelesen - war mir aber immer noch nicht klar - soll vorkommen! @Peter Ja es is aber auch ein Unterschied ob ich: "1MHz/1024 * 10e-3 + 0.5" oder "1MHz 1024 10" rechne. Bei der Zweiten Rechnung kommt schon 97,6Hz raus. Aber eben bei der oberen nicht. Dennoch funktionierts richtig. Irgendwie muss ich gerade voll am Schlauch stehen....
Datum:
@ Hannes S. OK - dann nehme ich das zurück. Es gibt halt auch solche, die nicht alles lesen... > Bei der Zweiten Rechnung kommt schon 97,6Hz raus. Aber eben bei der oberen > nicht. Was/wie rechnest du? Wenn ich das "1MHz/1024 * 10e-3 + 0.5" (ich gebe ein: 1e6/1024*10e-3+.5) mit meinem Taschenrechner rechne kommt 10.265625 raus.
Datum:
Mhmhhm, gott. Der Fehler lag mal wieder im Detail. Hab wohl die Scientific Notation schon zu lange nichtmehr benutzt.. 10e-3 ist ja 10*10^-3 und nicht 10^-3 Danke für die Erklärungen ;-)
Datum:
Angehängte Dateien:Hallo! Ich versuche den das Programm von Peter Dannegger für mich anzupassen sodass es auf einem atmega644p läuft. Den Quellcode habe ich angehängt. Mein Problem ist jetzt dass das Programm nur auf KEY0 reagiert, nicht aber auf KEY1, bzw. es reagiert nur auf den 0ten Bit des Eingangsports. Habe es in AVR Studio simuliert kann aber dieses komisches Verhalten nicht einmal beschreiben. An der Verdrahtung dürfte eigentlich nichts sein. Habe alle Anschlüsse schon mehrmals kontrolliert. Kann einer bitte den Quellcode überfliegen, vielleicht habe ich ja nur einen dummen Fehler gemacht den ich selber nicht sehe.
Datum:
noXe wrote:
> Kann einer bitte den Quellcode überfliegen
sieht o.k. aus.
Gib doch einfach mal den Input-Port direkt aus, ob das geht.
Peter
Datum:
Hallo, reicht es aus anstatt sei() und cli() bloß den Timer-Interrupt zu sperren?
Datum:
dom wrote: > reicht es aus anstatt sei() und cli() bloß den Timer-Interrupt zu > sperren? Was soll denn das bringen, außer mehr Code? Peter
Datum:
Ich möchte nicht, dass ein bestimmter Interrupt dadurch gesperrt wird.
Datum:
dom wrote:
> Ich möchte nicht, dass ein bestimmter Interrupt dadurch gesperrt wird.
Das läßt sich aber nicht verhindern.
Immer wenn ein Interrupthandler ausgeführt wird, sind alle Interrupts
gesperrt. Dagegen sind die 4 Zyklen für den atomaren Zugriff nur
Pillepalle.
Jedes Programm enthält unweigerlich kurze Sperren und bisher hat das
auch noch keinen Programmierer gestört.
Peter
Datum:
Ich hab exakt das gleiche Problem wie noXe mit einem Atmega644, irgendeine Idee?
Datum:
Vielen Dank für diese Routine! Wär ich früher schon auf dieses Routine gestossen hätt ich mir ne Menge Arbeit und Bastelei ersparen können. Ich habe aber noch folgendes Problem: ich benutze eine Taste um eine Leuchte ein- und auszuschalten.
// release after short press: task 1 // long press: task 2 if(get_key_short(1<<KEY_ONOFFDIM)){ if(geraet == ein) geraet = aus; else geraet = ein; } if(get_key_long(1<<KEY_ONOFFDIM)){ //do nothing } |
Gleichzeitig möchte ich über einen langen Tastendruck wiederholt die Leuchte dimmen. Jeweils abwechslungsweise hoch- und runterdimmen. Dazu muss ich aber erkennen, wann die Taste lang gedrückt bzw. im Repeatmode war und wieder released wurde (um die Dimmrichtung zu switchen).
if(get_key_rpt(1<<KEY_ONOFFDIM)){ if(geraet == ein) { if(dimmDirection == 0) { if(Value.m <= 95) Value.m += 5; else Value.m = 100; } else { if(Value.m >= 5) Value.m -= 5; else Value.m = 0; } } } |
Wenn ich nach dem get_key_short auch noch get_key_long abfrage, so erkenne ich nie einen Repeatstate. Lasse ich aber die Abfrage get_key_long weg, so erkenne ich nach dem loslassen immer ein Release der Taste und get_key_short liefert mir einen Wert, obwohl ja die Taste zuvor lange gedrückt war. Also ich muss folgende Zustände bzw. Zustandswechsel erkennen können: 1. Release der Taste wenn sie kurz gedrückt war 2. Repeat bei gedrückter Taste 3. Release der Taste wenn sie lange gedrückt war (und Repeats erkannt wurden) kann mir da jemand helfen?
Datum:
@Philipp Versuchs mal hiermit:
u8 get_key_long2( u8 key_mask )
{
return get_key_press( get_key_rpt( key_press^key_mask ));
}
u8 get_key_long_rpt( u8 key_mask )
{
return get_key_rpt( ~key_press^key_mask );
}
|
get_key_short: ein/aus get_key_long2: Dimmrichtung wechseln get_key_long_rpt: dimmem. Peter
Datum:
Hallo Peter, zwei kleine Fragen habe ich nochmal zu deiner Routine. 1. Wenn ich an einem Port 3 Tasten und an einem anderen Port 6 Endschalter habe, wie kann ich diese 9 Taster gemeinsam entprellen? Ich denke, ich muss dann beide Ports komplett einlesen, also alle Variablen 2x erstellen, ist das so richtig? 2. Wie kann ich abfragen, "solange Taste gedrückt", bzw. "solange Taste nicht gedrückt"? Das wäre die Abfrage der 6 Endschalter, die ich brauche. Gruß Tobi
Datum:
Hallo, ich hab das Entprellen von 8 Tasten mal als Fingerübung (nach 20 Jahren Einchip und C-Pause) auf eine Matrix mit Multiplexansteuerung der LED's erweitert (Ziel: XPressNet Schaltpult für die Modellbahn). So können mit 2 Port's 64 Schalter und 64 LED's mit wenig Aufwand angesteuert werden. Die LED's können parallel zum Taster liegen, jeweils an Zeilen und Spalten sollte ein Vorwiderstand zur Einhaltung der max. Ströme am AVR vorgeschaltet sein. Ich hab je 470 Ohm genommen, da sind bei 8 Zeilen die LED's hell genug. Die Tasten landen nun in dieser Version in einem Array, die LED's werden ebenfalls in ein Array eingetragen und dann per Interrupt ausgegeben. Um Flackern zu vermeiden wurde der Timer-Int auf 2,5ms verkürzt. Eine Erweiterung auf mehr Spalten ist ohne Probleme, evtl. kann man da Schieberegister verwenden. bei den Zeilen würde ich bei max. 8 bleiben, da sonst die Helligkeit der LED's wohl zu gering wird, auch wird das Einlesen und Handhaben der Tasten mit mehr 8bit schwieriger. vg jens
Datum:
Angehängte Dateien:man kann den anhang im Bearbeiten wohl nicht anhängen, hiermit also noch mal anbei.
Datum:
#include <stdint.h> #include <avr/io.h> #include <avr/interrupt.h> #ifndef F_CPU #define F_CPU 16000000 // processor clock frequency geändert #warning kein F_CPU definiert #endif #define KEY_DDR DDRB #define KEY_PORT PORTB #define KEY_PIN PINB #define KEY0 0 #define KEY1 1 #define KEY2 2 #define ALL_KEYS (1<<KEY0 | 1<<KEY1 | 1<<KEY2) #define REPEAT_MASK (1<<KEY1 | 1<<KEY2) // repeat: key1, key2 #define REPEAT_START 1000 //Zeit geändert #define REPEAT_NEXT 500 // Zeit geändert #define LED_DDR DDRA #define LED_PORT PORTA #define LED0 0 #define LED1 1 #define LED2 2 volatile uint8_t key_state; // debounced and inverted key state: // bit = 1: key pressed volatile uint8_t key_press; // key press detect volatile uint8_t key_rpt; // key long press and repeat ISR( TIMER0_OVF_vect ) // every 10ms { static uint8_t ct0, ct1, rpt; uint8_t i; TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms i = key_state ^ ~KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } /////////////////////////////////////////////////////////////////// // // check if a key has been pressed. Each pressed key is reported // only once // uint8_t get_key_press( uint8_t key_mask ) { cli(); // read and clear atomic ! key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) sei(); return key_mask; } /////////////////////////////////////////////////////////////////// // // check if a key has been pressed long enough such that the // key repeat functionality kicks in. After a small setup delay // the key is reported beeing pressed in subsequent calls // to this function. This simulates the user repeatedly // pressing and releasing the key. // uint8_t get_key_rpt( uint8_t key_mask ) { cli(); // read and clear atomic ! key_mask &= key_rpt; // read key(s) key_rpt ^= key_mask; // clear key(s) sei(); return key_mask; } /////////////////////////////////////////////////////////////////// // uint8_t get_key_short( uint8_t key_mask ) { cli(); // read key state and key press atomic ! return get_key_press( ~key_state & key_mask ); } /////////////////////////////////////////////////////////////////// // uint8_t get_key_long( uint8_t key_mask ) { return get_key_press( get_key_rpt( key_mask )); } int main( void ) { KEY_DDR &= ~ALL_KEYS; // konfigure key port for input KEY_PORT |= ALL_KEYS; // and turn on pull up resistors TCCR0 = (1<<CS02)|(1<<CS00); // divide by 1024 TIMSK = 1<<TOIE0; // enable timer interrupt LED_PORT = 0xFF; LED_DDR = 0xFF; while(1){ if( get_key_short( 1<<KEY1 )) LED_PORT ^= 1<<LED1; if( get_key_long( 1<<KEY1 )) LED_PORT ^= 1<<LED2; // single press and repeat if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){ uint8_t i = LED_PORT; i = (i & 0x07) | ((i << 1) & 0xF0); if( i < 0xF0 ) i |= 0x08; LED_PORT = i; } } } |
Hallo Atmega128 16Mhz (code-blocks-ubuntu.9.04-libc1.6.2.) bei mir funktioniert nur Eingang PINB2 (0und 1nicht) bei gedrücktem Taster läuft die schleife durch!! wie ein lauflicht mit 500ms andere Anwendungen (blinken Standard funktionieren auf den ports
Datum:
Angehängte Dateien:> //Hallo > // Atmega128 16Mhz (code-blocks-ubuntu.9.04-libc1.6.2.) > //bei mir funktioniert nur Eingang PINB2 (0und 1nicht) > //bei gedrücktem Taster läuft die schleife durch!! wie ein lauflicht mit > //500ms > //andere Anwendungen (blinken Standard funktionieren auf den ports Kann den Beitrag nicht mehr ändern als Quellcode mache es jetzt (habs zuspät gelesen)
Datum:
Stefan Strauß schrieb: > #define REPEAT_START 1000 //Zeit geändert > #define REPEAT_NEXT 500 // Zeit geändert Eine Repeatfunktion mit 10s / 5s Wartezeit ist ziemlich witzlos. In ein uint8_t passen außerdem nur max 255. Max 2,55s bei 10ms Interrupt sollte wohl reichen. Peter
Datum:
Hallo Peter der 128er läuft mit 16Mhz wenn ich hier nichts ändere kann ich nicht kontrolliert die LED`s weiter tasten ( rennt einfach durch) jetzt hat er eine Schaltzeit von ca 250ms bei dem wert wo 1000 steht. muß ich die Zeiten wo anders ändern? gruß Stefan
Datum:
Stefan Strauß schrieb:
der 128er läuft mit 16Mhz
Lies Dir den Thread in Ruhe durch, wo die Quarzfrequenz festgelegt wird.
> bei dem wert wo 1000 steht.
Ich hab Dir doch schon gesagt, daß das Mumpitz ist.
Nimm auch mal die Compilerwarnungen ernst:C_TAST.C:58: warning: large integer implicitly truncated to unsigned type |
> muß ich die Zeiten wo anders ändern?
Warum willst Du 10s?
Sind Deine Tasten gegen GND?
Daß KEY0 nicht geht, liegt einfach daran, daß Du in garnicht verwendest.
Ich hab den Eindruck, Du verstehst überhaupt nicht, was Du da schreibst.
Hast Du vorher schonmal in C programmiert?
Peter
Datum:
Hallo (vor allem Jens), der Thread ist genau, was ich suchte. Vor allem das Feature mit "Lange-Druecken-Erkennung" finde ich cool. Ich will auch eine kleine Tastaturmatrix ansteuern, deshalb habe ich mir die Codeanpassung von Jens Kuehn (vom Februar) hergenommen. Bei mir funktioniert's aber (nach Murphy) natuerlich noch nicht so ganz. Zum Verstaendnis habe ich an Jens folgende Fragen: 1) KEY0...2 und ZEILE0...2 sind doch m.E. synonym; hast Du die wegen des Bezugs zu Peters Originalcode dringelassen? 2) Meine Zeilenleitungen sind PD2...PD7, also muss ich doch auch bei KEY0...5 und bei ZEILE0...5 die 2...7 eintragen, oder? Hintergrund: Ich will ueber Kippschalterchen, die eine Ruhestellung haben und nach oben und unten getastet werden und einen ATMega(8) und Servos die Weichen meiner Modellbahn schalten. (Die Schalter-Bedienung finde ich ziemlich intuitiv und Vollautomatisierung will ich (noch) nicht.) Dafuer wollte ich "oben" und "unten", also PB0 und PB1, als Spalten an die Schalterchen anlegen und die jeweiligen mittleren Schalterkontakte als Zeileneingaenge, also PD2...PD7, abfragen. Der Effekt ist jetzt ganz komisch: zum Testen habe ich einen solchen Schalter angeschlossen, der eine Lumi nach oben an- und nach unten ausschalten soll. An geht bei jedem dritten mal, manchmal auch bei jedem vierten, manchmal geht's aber da auch aus, wo's eigentlich nur angehen soll ... Muss wohl noch etwas 'rumdebuggen. Dankeschoen auf alle Faelle schonmal an Peter und Jens fuer den coolen Code. Viele Gruesse!
Datum:
Hallo Arwed, die lange Tastenerkennung hab ich nicht mehr drin, ansonsten habe ich aber an dem Code noch viel geändert, so wie er oben steht kann er nicht so richtig gehen. Ich muss es aber noch mal aufbereiten (bisher gab es nicht so viel Interesse) und stelle es dann hier noch mal ein. ich hoffe, ich schaff es am Wochenende. Ich setze es übrigens auch für die Moba ein, hab es allerdings als Schaltpult an einer Zentrale (opendcc.de) hängen und kann dann auch ohne PC weiter meine Signale und Weichen schalten. viele Grüße jens
Datum:
Angehängte Dateien:hab es gleich noch gemacht ... in der zip ist das AVR-projekt und eine PDF als Schaltplan. Sorry, der Code ist etwas unordentlich. Es gibt nun noch ein Array welches einen Blinkmodus für die LED's einbaut. Bei einem muss ich Dich aber enttäuschen: Kippschalter gehen nicht, weil Du damit eine Spalte/Zeile 'tot legst' das geht bei einer Matrix halt nicht. vg jens
Datum:
Danke Jens, das werde ich probieren. Dass die Erkennung des lange Drueckens nicht mehr drin ist, ist natuerlich schade. Ich werde mich mal dran versuchen. Muesste beim Vergleich mit Peters Originalcode ja 'rauszufinden sein. Aber dass Dein (alter) Code nicht geht, kann ich nicht bestaetigen. Mittlerweile habe ich den Fehler gefunden: Ich hatte ein Quarz-Gehaeuse so dicht an den Pins des ATMega, dass der "so'n bissel" Kurzschluss gemacht hat ... Sonst tut's aber nun! Die Kippschalter sind eigentlich Kipptaster, also mit Nullstellung und je einer Taststellung oben und unten. Das passt schon. Die Mitte eines solchen Tasters kommt an je eine Zeile und "oben" und "unten" gemeinsam an die beiden Spalten. Danke auch fuer den Schaltplan; mit den LED's war mir das noch nicht ganz klar. Viele Gruesse von Arwed p.s.: Aber kann man nicht doch die KEY-Definitionen weglassen und stattdessen ueberall die ZEILE-Definitionen? Also codetechnisch ja sowieso, aber ich meine auch inhaltlich? Was bei Peter die KEYs waren, sind doch nun die ZEILEn, dachte ich ...
Datum:
[quote]Aber dass Dein (alter) Code nicht geht, kann ich nicht bestaetigen. [/quote] ja, prinzipiell ging er schon. Geändert hab ich glaub ich noch etwas weil sonst der Interrupt meine Datentransferroutinen blockierte und in dem alten code flackern die LED's glaub ich noch. [quote]Die Kippschalter sind eigentlich Kipptaster, also mit Nullstellung und je einer Taststellung oben und unten. Das passt schon. Die Mitte eines solchen Tasters kommt an je eine Zeile und "oben" und "unten" gemeinsam an die beiden Spalten.[/quote] das passt gut, der Verdrahtungsaufwand wird dadurch ja noch geringer [quote]Aber kann man nicht doch die KEY-Definitionen weglassen und stattdessen ueberall die ZEILE-Definitionen? Also codetechnisch ja sowieso, aber ich meine auch inhaltlich? Was bei Peter die KEYs waren, sind doch nun die ZEILEn, dachte ich ...[/quote] das ist doch nur eine Frage was für Dich besser verständlich ist. Ich hatte es dann doch auf den KEY0 ... gelassen (so nach dem Motto: Taste 0 in der Spalte X). Du kannst einfach alle KEY0 durch ZEILE0 ersetzen, das wird auch gehen. Und die Definition selbst isst ja kein Brot ... da kann man auch beide Varianten definieren und im Code dann je nach besserer Verständlichkeit verwenden. Zum Schaltplan evtl. noch ein Hinweis: die Vorwiderstände kannst Du evtl. noch größer machen, wenn Deine LED's hell genug sind. Ich hab Taster mit eingebauter LED, die funzeln recht trübe. Gegenüber einer normalen Ansteuerung brauchst Du aber immer etwas mehr Strom, da die ja nur 1/8 der Zeit Strom bekommen. viel Spaß jens
Datum:
Hallo, ich habe mir nochmal Jens' Code hergenommen und mit Peters verglichen, weil ich (noch) keine Lumis ueber den Tasten brauche, aber die Long-Funktion haben wollte. Dabei habe ich festgestellt, dass Jens die ct0 und ct1 bei jdem ISR-Durchlauf auf 0 gesetzt hat; geht denn das in Ordnung? Eigentlich hatte ich es so verstanden, dass die ueber die ISR hinaus ihre Werte behalten sollen, oder? Und die muessen daher doch auch einmal je Spalte dasein?! Ich habe mich nun nochmal dran versucht und in Zeitlupe debuggt und habe nun folgende ISR:
ISR( TIMER0_OVF_vect ){
static uint8_t ct0[SPALTENANZAHL], ct1[SPALTENANZAHL], rpt;
uint8_t i;
uint8_t key_mpex; //die aktive Tasten-Spalte
TCNT0 = TIMER0_STARTVALUE;
//jetzt alle Spalten durcharbeiten
for( key_mpex=0; key_mpex < SPALTENANZAHL; ++key_mpex ){
i = 0;
SPALTE_PORT &= ~(1<<(spalten_pin[key_mpex])); //aktive Spalte auf 0
i = key_state[key_mpex] ^ ~ZEILE_PIN; // key changed ?
ct0[key_mpex] = ~( ct0[key_mpex] & i ); // reset or count ct0
ct1[key_mpex] = ct0[key_mpex] ^ (ct1[key_mpex] & i); // dito ct1
i &= ct0[key_mpex] & ct1[key_mpex]; // count until roll over ?
key_state[key_mpex] ^= i; // then toggle debounced state
key_press[key_mpex] |= key_state[key_mpex] & i; // 0->1: key pressed
if( (key_state[key_mpex] & REPEAT_MASK) == 0 ) // check repeat fct.
rpt = REPEAT_START; // start delay
if( --rpt == 0 ){
rpt = REPEAT_NEXT; // repeat delay
key_rpt[key_mpex] |= key_state[key_mpex] & REPEAT_MASK;
}
SPALTE_PORT |= (1<<(spalten_pin[key_mpex])); //Spalte deaktivieren
}
}
|
Das tut soweit wunderbar -- solange ich nur mit get_key_press() abfrage; aber get_key_rpt liefert nix und get_key_long verhaelt sich entweder wie get_key_press oder gar nicht. Ich habe den Repeat-Trick noch nicht ganz verstanden; muss da nicht auch noch eine Repeat-Variable je Spalte rein, also so aehnlich wie key_state? Viele Gruesse ...
Datum:
Hallo, eigendlich ein super Teil :) Leider funktioniert es überhaupt nicht mehr, seit dem ich einen 14,7456MHz Quarz an meinem ATmega128 habe. Im Simulator (2) wird die Timer0 Routine, laut Stopwatch, nicht alle 10ms sondern alle 1.25ms aufgerufen. F_CPU steht auch auf 14745600UL. Mit 1MHz hat es super funktioniert. MfG Markus
Datum:
MarkusK schrieb:
> F_CPU steht auch auf 14745600UL.
Versuchs mal mit XTAL.
Peter
Datum:
Versuchs mal mit XTAL ... statt F_CPU. Du darfst Dir das Programm ruhig durchlesen. Dann wirst Du merken, daß ich XTAL verwende, aber nirgends F_CPU. Peter
Datum:
hallo, hab heute mein avr starterboard von pollin bekommen und damit etwas rumgespielt (verwende einen atmega32). wenn ich nun eine taste extrem schnell nacheinander drücke (man muss sich schon etwas anstrengen um das hinzubekommen) macht mein atmega einen reset!? gruß, marco
Datum:
Das liegt an der "Buggy" Beschaltung der Tasten auf dem Pollin Board. Siehe Beitrag "Pollin AVR Board Fehler beim drücken der Taster / Qualität der Bauteile"
Datum:
Angehängte Dateien:Hallo, ich wollt mich erstmal für die gute Entprellroutine bei Peter bedanken. Um kurz zu erklären was ich vorhabe, es sollen mit 2 Tasten ein PWM Kanal bei kurzem Tastendruck geschaltet und bei langen Tastendruck gedimmt werden. Also Taster 0 für Einschalten und aufwärts Dimmen und Taster 1 für Ausschalten und abwärts Dimmen. Das Problem auf das ich gestoßen bin ist, dass immer beim Loslassen nach dem Dimmen weiterhin ein kurzer Tastendruck registriert wird und so nach dem Dimmen das schalten ausgeführt wird. Beim aufwärts Dimmen ist das nicht schlimm, weil die Leds ja bereits an sein sollten, aber beim Abwärts dimmen werden diese ja dann ausgeschaltet was ich vermeiden möchte. Aus einem vorherigen Beitrag, der ähnliche Wünsche wie ich beschreibt, habe ich die Funktion get_key_long_rpt benutzt. Leider hat der Fragesteller (Philipp) kein Feedback auf die von Peter gepostete Lösung gegeben, deswegen weiss ich nicht ob sie bei Ihm funktioniert hat. Ich würde mich freuen wenn Ihr mir helfen könntet. Meinen Quellcode hab ich angehängt. Zum Device, es handelt sich um einen Atmega 48A mit 16MHz Clock. Ich dank euch schonmal für die Antworten. Grüße Hans-Peter
Datum:
Hallo, ich bins nochmal, hat denn keiner eine Idee wieso nach dem Erkennen des wiederholten langen Drucks auch ein kurzer Tastendruck erkannt wird? Grüße Hans-Peter
Datum:
Hans-Peter schrieb: > Hallo, > > ich bins nochmal, hat denn keiner eine Idee wieso nach dem Erkennen des > wiederholten langen Drucks auch ein kurzer Tastendruck erkannt wird? > > Grüße > Hans-Peter Du mußt dann alle 3 Funktionen aufrufen:
if( get_key_short( xxx )){ ... } if( get_key_long2( xxx ) || get_key_long_rpt( xxx )){ ... } |
Versuch mal zu beschreiben, warum. Peter
Datum:
Ich entprelle 4 tasten mit Peters Routine. Allerdings schaffe ich es nicht, mehr als eine Taste gleichzeitig erkannt zu bekommen obwohl ich zwei nebeneinander liegende Tasten praktisch mit einem Finger betätige. Wie muß man den Source verändern, das mehrere Tasten erkannt werden?
Datum:
eku schrieb: > Wie muß man den Source verändern, das mehrere Tasten erkannt werden? Vielleicht zeigst du uns erstmal deinen Code!?
Datum:
eku schrieb: > Ich entprelle 4 tasten mit Peters Routine. Allerdings schaffe ich es > nicht, mehr als eine Taste gleichzeitig erkannt zu bekommen obwohl ich > zwei nebeneinander liegende Tasten praktisch mit einem Finger betätige. Du müßtest beide innerhalb des gleichen 10ms-Fensters drücken, das ist recht sportlich. Für den MC drückst Du sie also nacheinander. > Wie muß man den Source verändern, das mehrere Tasten erkannt werden? Du könntest eine als Shifttaste definieren, d.h. kurz vorher gedrückt halten:
if( get_key_press( 1<<USER_KEY0 )){ if( key_state & 1<<SHIFT_KEY ){ // shift + userkey0 }else{ // only userkey0 } } |
Peter
Datum:
Peter Dannegger schrieb: > Du müßtest beide innerhalb des gleichen 10ms-Fensters drücken, das ist > recht sportlich. Für den MC drückst Du sie also nacheinander. Genau das ist mein Problem. Ich möchte aber nicht eine Taste mit einer Spezialfunktion (SHIFT) belegen, denn es soll egal sein, welche zwei Tasten aus meiner Sicht "gleichzeitig" gedrückt werden. Der MC soll nur zwischen einer Taste und zwei Tasten unterscheiden, d.h. wenn zwei Bits bei get_key_press() gesetzt sind.
Datum:
Das Problem ist, die CPU kann nicht Deine Gedanken lesen. Woher soll sie den wissen, daß Du etwa 300ms später eine 2.Taste drücken wirst und das dann als 2-Tastendruck erkannt werden soll und der 300ms vorher erkannte 1-Tastendruck ungültig gewesen war? Du mußt einen eindeutige Regel definieren, wie die CPU etwas unterscheiden soll. Mach Dir mal nen Programmablaufplan und dann erst kann man den implementieren. Bedenke auch, daß ältere Menschen deutlich länger benötigen werden, 2 Tasten gleichzeitig (innerhalb eines Zeitintervalls) zu drücken. Bzw. ob das überhaupt benutzerfeundlich (ergonomisch) ist. Du wirst deshalb in kommerziellen Geräten keine solche Bedienung finden. Da haben sich für Mehrfachbelegung die Sondertasten bewährt, deren Funktion nur durch die Reihenfolge eindeutig bestimmt ist und damit die unterschiedlichen motorischen Fähigkeiten verschiedener Menschen die Bedienung nicht behindern/verhindern. Peter
Datum:
Wie das funktionieren soll verstehe ich leider immer noch nicht:
u8 get_key_long2( u8 key_mask )
{
return get_key_press( get_key_rpt( key_press^key_mask ));
}
u8 get_key_long_rpt( u8 key_mask )
{
return get_key_rpt( ~key_press^key_mask );
}
...
if( get_key_short( xxx )){
LED_PORT ^= 1<<LED1;
}
if( get_key_long2( xxx ) || get_key_long_rpt( xxx )){
LED_PORT ^= 1<<LED2;
}
|
Ein langer Tastendruck lässt LED2 blinken, aber schaltet beim Loslassen noch die LED1 um. Wenn ich die vorangegangen Beiträge richtig verstanden habe, sollte das in diesem Beispiel doch nicht passieren, oder? Kann mir jemand nochmal einen Tip dazu geben? Danke, Thomas
Datum:
Kann er das nicht durch eine Kombination aus get_key_press und get_key_repeat lösen? Aber trotzdem gilt PAP zeichnen und entsprechend programmieren...
Datum:
Micha schrieb: > Kann er das nicht durch eine Kombination aus get_key_press und > get_key_repeat lösen? Nun ja, die Lösung oben ist ja eine Kombination aus diesen beiden Routinen. Sie funktioniert ja auch soweit, nur wenn ich die Taste nach langem Drücken loslasse, wird offensichtlich nochmal ein kurzer Tastendruck augelöst. Ich verstehe einfach nicht warum das so ist...
Datum:
thomas schrieb: > Sie funktioniert ja auch soweit, nur wenn ich die Taste nach langem > Drücken loslasse, wird offensichtlich nochmal ein kurzer Tastendruck > augelöst. Ja, da hat sich ein Schreibfehler eingeschlichen. Das EXOR ist Unsinn, muß ein AND sein. Hier mal ein funktionierender Code:
/************************************************************************/ /* */ /* Debouncing 8 Keys */ /* Sampling 4 Times */ /* With Repeat Function */ /* */ /* Author: Peter Dannegger */ /* */ /************************************************************************/ // Target: ATtiny13 #include <avr/interrupt.h> #include <util/atomic.h> #define F_CPU 1.2e6 // 1.2MHz #define KEY_PIN PINB #define KEY_PORT PORTB #define KEY0 4 #define LED_DDR DDRB #define LED_PORT PORTB #define LED0 0 #define LED1 1 #define LED2 2 #define LED3 3 #define REPEAT_MASK (1<<KEY0) // repeat: key0 #define REPEAT_START 50 // after 500ms #define REPEAT_NEXT 20 // every 200ms uint8_t key_state; // debounced and inverted key state: // bit = 1: key pressed uint8_t key_press; // key press detect uint8_t key_rpt; // key long press and repeat ISR( TIM0_COMPA_vect ) // every 10ms { static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt; uint8_t i; i = key_state ^ ~KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } uint8_t get_key_press( uint8_t key_mask ) { ATOMIC_BLOCK(ATOMIC_FORCEON){ key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) } return key_mask; } uint8_t get_key_rpt( uint8_t key_mask ) { ATOMIC_BLOCK(ATOMIC_FORCEON){ key_mask &= key_rpt; // read key(s) key_rpt ^= key_mask; // clear key(s) } return key_mask; } uint8_t get_key_short( uint8_t key_mask ) { uint8_t i; ATOMIC_BLOCK(ATOMIC_FORCEON) i = get_key_press( ~key_state & key_mask ); return i; } uint8_t get_key_long( uint8_t key_mask ) { return get_key_press( get_key_rpt( key_mask )); } uint8_t get_key_long_r( uint8_t key_mask ) // if repeat function needed { return get_key_press( get_key_rpt( key_press & key_mask )); } uint8_t get_key_rpt_l( uint8_t key_mask ) // if long function needed { return get_key_rpt( ~key_press & key_mask ); } int main( void ) { TCCR0A = 1<<WGM01; // Mode 2. CTC TCCR0B = 1<<CS02 | 1<<CS00; // F_CPU / 1024 OCR0A = F_CPU / 1024.0 * 10e-3 - 0.5; // 10ms interrupt TIMSK0 = 1<<OCIE0A; LED_PORT = 1<<LED0 | 1<<LED1 | 1<<LED2 | 1<<LED3; // LEDs off LED_DDR = 1<<LED0 | 1<<LED1 | 1<<LED2 | 1<<LED3; KEY_PORT |= 1<<KEY0; // Pullup on sei(); for(;;){ // main loop if( get_key_short( 1<<KEY0 )) LED_PORT ^= 1<<LED0; if( get_key_long_r( 1<<KEY0 )) LED_PORT ^= 1<<LED1; if( get_key_rpt_l( 1<<KEY0 )) LED_PORT ^= 1<<LED2; } } |
Peter
Datum:
Micha schrieb: > Kann er das nicht durch eine Kombination aus get_key_press und > get_key_repeat lösen? Was ist mit
if (get_key_long (maske_über_alle_tasten) == zwei_bits_gesetzt)
{
aktion für zwei tasten
}
|
Würde das funktionieren?
Datum:
Anmerkung: Erlaubte Funktionen einer Taste: - 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() Peter
Datum:
Angehängte Dateien:Ich weiß nicht, warum alle so scharf auf 2-Tastenbedienung sind. Man muß einfach nur logisch denken, wie ein MC das erkennen soll, ohne Hellseherei. Man kann es mit meinen Funktionen erreichen. Gleichzeitig bedeutet dann, innerhalb der Zeit zur Langdruckerkennung. Man kann also einen Einzeldruck nicht mehr sofort erkennen.
for(;;){ // main loop switch( get_key_long( 1<<KEY0 | 1<<KEY1 ) | get_key_short( 1<<KEY0 | 1<<KEY1 )){ case 1<<KEY0: LED_PORT ^= 1<<LED0; break; case 1<<KEY1: LED_PORT ^= 1<<LED1; break; case 1<<KEY0 | 1<<KEY1: LED_PORT ^= 1<<LED2; break; } } |
Für 2-Tastendruck muß also immer lange gedrückt werden. Kurze Drücke werden als einzeln gewertet. Will man das nicht, muß man get_key_short aus dem Switch herausnehmen, aber man muß es trotzdem aufrufen. Es wird nur nicht ausgewertet. Aber wie schon gesagt, es ist weder sinnvoll, noch bedienerfreundlich. Ich kann mir eigentlich nur eine einzige Anwendung denken: Man hat eine gefährliche Maschine und will verhindern, daß der Bediener hineingreift und zu Schaden kommt. Deshalb muß er mit je einer Hand eine Taste drücken. Damit der Bediener nun aber nicht eine Taste aus Bequemlichkeit blockiert, muß er sie innerhalb einer bestimmten Zeit gleichzeitig drücken. Peter
Datum:
Verbesserte Version:
for(;;){ // main loop switch( get_key_long( 1<<KEY0 | 1<<KEY1 ) | get_key_short( 1<<KEY0 | 1<<KEY1 )){ case 1<<KEY0: LED_PORT ^= 1<<LED0; break; case 1<<KEY1: LED_PORT ^= 1<<LED1; break; default: if((key_press & (1<<KEY0 | 1<<KEY1)) != (1<<KEY0 | 1<<KEY1)) break; get_key_press( 1<<KEY0 | 1<<KEY1 ); case 1<<KEY0 | 1<<KEY1: LED_PORT ^= 1<<LED2; break; } } |
Peter
Datum:
So, jetzt noch einfacher, mit ner extra Funktion für die 2 Tasten Erkennung. Man kann auch auswählen, ob der Druck beider Tasten innerhalb einer bestimmte Zeit erfolgen soll (#define TIMEOUT)
uint8_t get_key_common( uint8_t key_mask )
{
return get_key_press((key_press & key_mask) == key_mask ? key_mask : 0);
}
// ...
//#define TIMEOUT
for(;;){ // main loop
#ifdef TIMEOUT
if( get_key_short( 1<<KEY0 ) || get_key_long( 1<<KEY0 ))
LED_PORT ^= 1<<LED0;
if( get_key_short( 1<<KEY1 ) || get_key_long( 1<<KEY1 ))
LED_PORT ^= 1<<LED1;
#else
if( get_key_short( 1<<KEY0 ))
LED_PORT ^= 1<<LED0;
if( get_key_short( 1<<KEY1 ))
LED_PORT ^= 1<<LED1;
#endif
if( get_key_common( 1<<KEY0 | 1<<KEY1 ))
LED_PORT ^= 1<<LED2;
}
|
Peter
Datum:
Peter, Peter Dannegger schrieb: > So, jetzt noch einfacher, mit ner extra Funktion für die 2 Tasten > Erkennung. vielen Dank. Du hast mir sehr geholfen.
Datum:
Peter Dannegger schrieb: > Hier mal ein funktionierender Code: Danke Peter, funktioniert perfekt! Gruß Thomas
Datum:
Peter Dannegger schrieb: > Man kann auch auswählen, ob der Druck beider Tasten innerhalb einer > bestimmte Zeit erfolgen soll (#define TIMEOUT) Wo wird denn im Quelltext der Wert von TIMEOUT ausgewertet? Also wo wird berücksichtigt, ob ich 10 oder 100 oder 1000 eingebe? Ansonsten prima! Die Funktion fehlte noch - deine Tastenentprellung ist wirklich der Hammer, PeDa! Danke sehr!
Datum:
Hallo, @peda: Du hattest ja beschrieben welche Funktionen zusammen zulässig sind: Peter Dannegger schrieb: > Erlaubte Funktionen einer Taste: > > - 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() Sehe ich das richtig, dass *get_key_long_r()* nur die long-Funktion ist wenn man auch noch *get_key_rpt_l()* (Repeat während lang gedrückt) nutzen möchte? Ich suche nach einer Möglichkeit das Loslassen einer Taste, die zuvor lang gedrückt war zu detektieren. Ein Repeat beim lang gedrückt halten müsste ich hier nicht mehr detektieren können. Das müsste doch möglich sein, oder? Vielleicht hat jemand das ja schonmal in eine Funktion gegossen oder kann mir sagen wie ich dazu die Aufrufe kombinieren muss? Vielen Dank schonmal! Gruß Fabian
Datum:
Gibts dazu keine Lösung? Ich hab nun mittlerweile alles hier im Thread an Funktionskombinationen gefundene ausprobiert, aber ohne Erfolg. Nicht mal die "fehlerhafte" Variante von "Thomas" ( Beitrag "Re: Universelle Tastenabfrage" ), die ja eigentlich das richtige tun sollte, geht nicht -> sie funktioniert bei mir gar nicht. Nochmal zusammengefasst muss ich auf einer Taste folgendes detektieren. 1) Taste kurz gedrückt -> ok 2) Taste lang gedrückt -> ok 3) Taste losgelassen nach lang gedrückt -> geht nicht Alternative wäre noch einfach das loslassen (egal ob nach kurz oder lang) der Taste detektieren zu können, dann könnte ich immernoch anhand interner Variablen die Aktion korrekt auswählen. Ich wäre dankbar, wenn's doch noch eine Möglichkeit gibt, das mit Pedas Standardfunktionen umzusetzen. Gruß Fabian
Datum:
Was willst du denn damit erreichen? 3 Funktionen per Taste? Geht eh nicht, da du die Taste sonst nach einem langen Druck nicht mehr loslassen darfst.
Datum:
Nein eigentlich gibts nur zwei Funktionen für den Nutzer pro Taste: kurz gedrückt und lang gedrückt. Es wird ein externes Gerät angesteuert (CD-Player). Auf einer Taste soll nun bei kurzem Druck Skip und bei langem Druck Spulen liegen. Soweit so einfach. Es ist aber so, dass ich beim Spulen nicht immer wieder "spulen" schicke sondern nur einmal "fang an zu spulen" sobald ein langer Druck erkannt wurde und ein "spiel wieder normal" wenn dann wieder losgelassen wird. Hoffe das ist verständlich. Ich habe es jetzt aber über eine "illegale Funktionskombination" gelöst: get_key_short und get_key_rpt. Hier muss ich mir nur über eine interne Variable merken ob ich spule (damit beim repeat nicht immer wieder das Kommando gesendet wird. Da get_key_rpt den Key nicht zurücksetzt wird beim loslassen direkt nochmal get_key_short ausgelöst. Hier kann ich nun ebenfalls über die interne Variable erkennen ob ich skippen soll oder das Spulen beende. Schöner wäre natürlich eine eigene Funktion "get_key_release_after_long_press" und dann die Nutzung von get_key_short und get_key_long. Aber ich habe dafür keine Möglichkeit sehen können. Gruß Fabian
Datum:
Hallo Leute,
erstmal vielen Dank an Peter für den erstklassigen Code!
Um mir und hoffentlich noch anderen die Entscheidung zu erleichtern
welche Kombination von Abfragen die geeignete ist, habe ich
mal ein paar Grafiken gemacht, welche die verschiedenen Funktionsweisen
veranschaulichen. Im Grunde sind es 3 Modi, die man verwenden kann. Die
Grafiken stellen den Ablauf einer Folge von 3 Tastendrücken dar. Ein
kurzer, ein langer und wieder ein Kurzer. In der ersten Grafik sind auch
die Zeitintervalle REPEAT_START und REPEAT_NEXT gekennzeichnet.
Anmerkungen:
- keystate ist bei gedrückter Taste oben, ansonsten unten.
- "X" bedeutet ein Tastenereignis wurde erkannt (die entsprechende
Funktion liefert != 0).
- Der aufmerksame Beobachter wird feststellen, dass eine Funktion
gelistet ist, die nicht existiert. Nämlich 'key_release'.
Die lässt sich mit ein paar Handgriffen in den Code von Peter einbauen.
Wie das geht zeige ich in seperatem Post.
Modus 1 ('get_key_press' mit 'get_key_rpt')
===========================================
Zeitachse ---->
__ _________________ __
keystate ____/ \____/ \____/ \____
key_press ----X-------X----------------------X-------
key_rpt --------------------X--X--X--X-------------
key_release -------X----------------------X-------X----
| | | |
| |__|__|
| | \ /
|_______| REPEAT_NEXT
\
REPEAT_START
|
Modus 2 ('get_key_short' mit 'get_key_long')
============================================
__ _________________ __
keystate ____/ \____/ \____/ \____
key_short -------X------------------------------X----
key_long --------------------X----------------------
key_release -------X----------------------X-------X----
|
Modus 3 ('get_key_short' mit 'get_key_long_r' und 'get_key_rpt_l')
==================================================================
__ _________________ __
keystate ____/ \____/ \____/ \____
key_short -------X------------------------------X----
key_long_r --------------------X----------------------
key_rpt_l -----------------------X--X--X-------------
key_release -------X----------------------X-------X----
|
Wichtig zu wissen ist vielleicht noch, dass 'get_key_long_r' und 'get_key_rpt_l' immer zusammen abgefragt werden müssen, auch wenn für ein Fall keine Verwendung ist. Für sich alleine liefern die Funktionen keine brauchbaren Ergebnisse (auch nicht zusammen mit 'get_key_short'). 'get_key_release' kann man in allen Modi verwenden. Grüße Jonas
Datum:
Wie angekündigt hier die 'get_key_release' Funktion. Drei Schritte sind nötig. 1. Zu den globalen Variablen den release buffer hinzufügen: Nach der Zeile
volatile uint8_t key_press; // key press detect |
folgendes einfügen:
volatile uint8_t key_release; // key release detect |
2. In der ISR den buffer aktualisieren: Nach der Zeile
key_press |= key_state & i; // 0->1: key press detect
|
diese Zeile einfügen:
key_release |= ~key_state & i; // 1->0: key release detect
|
3. Und zu guter Letzt die Funktion um den release abzufragen: Bei den anderen Funktionen einfügen:
uint8_t get_key_release( uint8_t key_mask )
{
cli(); // read and clear atomic !
key_mask &= key_release; // read key(s)
key_release ^= key_mask; // clear key(s)
sei();
return key_mask;
}
|
Gruß Jonas
Datum:
Hallo! Wenn der Timer0 schon durch PWM belegt ist, kann man dann einfach einen der beiden anderen nehmen oder gibt es einen besonderen Grund für die Verwendung des "Nullers"? Ich habe das Tutorial gelesen und etwas experimentiert und auch im Datenblatt gestöbert - so richtig schlau bin ich aber noch nicht draus geworden... Dankeschön!
Datum:
Christian schrieb: > Wenn der Timer0 schon durch PWM belegt ist, kann man dann einfach einen > der beiden anderen nehmen oder gibt es einen besonderen Grund für die > Verwendung des "Nullers"? Wie kommst Du denn darauf, daß es nicht ginge? Du brauchst ein Zeitintervall von etwa 5..50ms. Peter
Datum:
Moin Moin! Ich möchte die get_key_common() Funktion nutzen um durch langes drücken zweier Tasten in den "Programmier-mode" zu gelangen. Die Funktion reagiert aber auch schon auf kurzen Tastendruck. Wie kann ich die Funktion erweitern/verändern, sodaß nur auf langes drücken zweier Tasten reagiert wird? 10000000 * 10^12 Dank für eure Hilfe Karsten
Datum:
> Wie kommst Du denn darauf, daß es nicht ginge? Was jetzt? Den Timer trotzdem auch dafür nutzen oder einen anderen Timer? Der PWM-Kram ist ja relativ komplex, da frage ich lieber mal nach, bevor da was kollidiert. Und ich will mich da ja richtig einarbeiten und es verstehen und nicht einfach rum frickeln... > Du brauchst ein Zeitintervall von etwa 5..50ms. Ich kläre erstmal mit der bereits laufenden Software, welche (und wie) Timer genutzt und ob welche frei sind. Da die Timer ja auf zig Wegen angesprochen und gesteuert werden und ich da noch nicht soo viel gemacht habe, ist das etwas schwierig für mich. Aber ich beisse mich da schon durch. Gibt es zur Analyse von fremden Programmen z.B. in Eclipse ein Plugin oder gar was Eingebautes, um die genutzten Ressourcen eines AVR anzeigen zu lassen? So Interessehalber: wie machen das eigentlich die Hersteller z.B. von großen Lichtsteuerungen? Gibt es da Spezialbauteile für die Entprellung von 200 Tastern oder macht das der uC auf einer A...backe nebenher?
Datum:
Also bei mir hat sich das Thema erstmal erledigt bzw. "verunwichtigt", ich lege es erstmal auf Eis. Für andere Noobs wie mich wäre eine Antwort bei Gelegenehit allerdings sicher trotzdem interessant... Wie für mich, wenn ich doch mal wieder damit anfange ;)
Datum:
Wollt mich nur kurz für die Routine bedanken, funktioniert einwandfrei! Danke Peter :) Beste Grüße Christoph
Datum:
Hallo an alle, Ich habe den Code (Orginal Peter) an einen ATmega 8515 angepasst die KEY's liegen auf dem DDRC, die LED's auf dem DDRA Die Funktion ist bei diesem µC auch gegeben. KEY0 keine Funktion; KEY1 kurz/lang die Aktivierung von LED0/1; KEY2 bei Betätigung laufen die LED's 2....7 je nach Takt und prescaler. Nun hab ich den 8515 gegen einen ATmega 16 getauscht, weil ich einen weiteren TIMER benötige, die alle nötigen Bekanntmachungen in AVRStudio auf "ATmega16" geändert. Die KEY's 0 und 1 sind in ihrer FUnktion erhalten geblieben nur die LED2 leuchtet nun sofort und das Lauflicht beginnt, nach einer noch nicht quantifizierten Zeit, von selbst. Das Betätien der KEY2 Taste hat keinen Einfluss auf die Funktion der LED's. Laut Datenblatt unterscheiden sich die relevanten Register(TIMER0) des ATmega16 bzw. 8515 "überhaupt" nicht. Ausnahme die Adresse des OCR0 dieses sollte meine ich in der <avr/io.h> definiert sein. Hää? ich bin für jeden Tipp dankbar.(Schlauch)
Datum:
Martin Sv schrieb: > Nun hab ich den 8515 gegen einen ATmega 16 getauscht Nur wer eine Frage formulieren kann, kann sie sich ach selbst beantworten http://www.mikrocontroller.net/articles/AVR_Fuses Einige Prozessoren haben eine Werkseinstellung, die zu Problemen führen kann. Wenn der Prozessor JTAG unterstützt (ab Mega16 aufwärts), so ist ab Werk die JTAG Schnittstelle eingeschaltet, was dazu führt, daß einige Portpins nicht wie gewohnt funktionieren (nämlich die an denen das JTAG Interface herausgeführt wird). In so einem Fall ist es das Beste, die entsprechende Fuse JTAGEN umzusetzen, um so das JTAG Interface abzuschalten, wenn es nicht gebraucht wird. das war's, einen großen Dank dem Autor des o.g.Artikel
Datum:
Hallo Peter, auch von mir ein Dankeschön für das Veröffentlichen dieses hilfreichen Codes. Nach über 20 Jahren Pause hat mich wieder das "Mikrocomputer-Fieber" erfasst, damals hatte ich, allerdings nur oberflächlich, den Z80 auf HEX-Ebene gelangweilt, hat aber Spaß gemacht. Viele Funktionalitäten, so auch die Entprellung, erfolgten in HW (RS-FF). Nun versuche ich, dem Controller (Mega8) mit C "Beine zu machen". C ist für mich völliges Neuland, insofern sind funktionierende Module, wie auch diese Entprellroutine, wichtig für die ersten kleinen Erfolgserlebnisse. Viele Grüße Thomas
Datum:
Hallo Peter, ich habe versucht deine universelle Tastenabfrage für einen ARM Cortex M3 einzusetzen. Leider bekomme ich ganz komische Effekte. Hier der Code:
#include "inc/hw_ints.h" #include "inc/lm3s8962.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "driverlib/debug.h" #include "driverlib/gpio.h" #include "driverlib/interrupt.h" #include "driverlib/systick.h" #include "driverlib/sysctl.h" #include "driverlib/timer.h" #include "drivers/rit128x96x4.h" #define sei() IntMasterEnable() #define cli() IntMasterDisable() typedef unsigned char u8; typedef signed short s16; #define KEY_PIN ((u8)GPIO_PORTE_DATA_R) /*#define GPIO_PORTE_DATA_R (*((volatile unsigned long *)0x400243FC)), Falls Taste 0 NICHT gedrückt, ist GPIO_PORTE_DATA_R = 00000111, falls gedrückt ist GPIO_PORTE_DATA_R = 00000110. Letztes Bit toggelt also*/ #define KEY0 0 #define KEY1 1 #define KEY2 2 #define REPEAT_MASK (1<<KEY1^1<<KEY2) // repeat: key1, key2 #define REPEAT_START 50 // after 500ms #define REPEAT_NEXT 20 // every 200ms u8 key_state; // debounced and inverted key state: // bit = 1: key pressed u8 key_press; // key press detect u8 key_rpt; // key long press and repeat #ifdef DEBUG void __error__(char *pcFilename, unsigned long ulLine) { } #endif void Timer0IntHandler(void) { static u8 ct0, ct1, rpt; u8 i; TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); i = key_state ^ KEY_PIN; // key changed ? //aktvie high, desswegen Änderung ~KEY_PIN! ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } u8 get_key_press( u8 key_mask ) { cli(); // Master Interrupts sperren key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) sei(); // Master Interrupts freigeben return key_mask; } u8 get_key_rpt( u8 key_mask ) { cli(); // Master Interrupts sperren key_mask &= key_rpt; // read key(s) key_rpt ^= key_mask; // clear key(s) sei(); // Master Interrupts freigeben return key_mask; } u8 get_key_short( u8 key_mask ) { cli(); // Master Interrupts sperren return get_key_press( ~key_state & key_mask ); } u8 get_key_long( u8 key_mask ) { return get_key_press( get_key_rpt( key_mask )); } int main(void) { /* CPU Clock*/ SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ); /* Display Init*/ RIT128x96x4Init(1000000); /* Takt an Peripherie*/ SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); /* Setze GPIOs als Eingang*/ GPIODirModeSet(GPIO_PORTE_BASE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_DIR_MODE_IN); /* Push-pull with weak pull-up, Strom auf 2mA*/ GPIOPadConfigSet(GPIO_PORTE_BASE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); /* Master Interrupt einschalten*/ sei(); /* Timer 0 - 32 Bit, Periodisch*/ TimerConfigure(TIMER0_BASE, TIMER_CFG_32_BIT_PER); /* 10 ms Interrupt generieren*/ TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/100); /* Interrup Routine festlegen*/ TimerIntRegister(TIMER0_BASE,TIMER_A,Timer0IntHandler); /* Interrupts einschalten*/ IntEnable(INT_TIMER0A); TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); TimerEnable(TIMER0_BASE, TIMER_A); while(1){ // main loop // single press if( get_key_short( 1<<KEY1 )) { RIT128x96x4Clear(); RIT128x96x4StringDraw("short", 50, 40, 15); } // Repeat if( get_key_rpt( 1<<KEY1 )) { RIT128x96x4Clear(); RIT128x96x4StringDraw("repeat", 50, 40, 15); } } } |
Wenn die Taste 1 gedrückt wird, erscheint auf dem Display short. Kurz danach erscheint dann dauerhaft repeat. Wenn ich eine andere Taste betätige, welche ich aber nicht ab in der While-Schleife abfrage, erscheint auf dem Display repeat. Hat jemand eine Ahnung woran das liegen könnte? Deine Routine ist wirklich sehr schlank und effizient. Der uC kann Interrupts durch Tastendrücke auslösen. Ich bin auch gerade am überlegen ob ich das nicht mit Tasteninterrupts und Timer aufziehen sollte, also entprellen und tastenabfrage. Viele Grüße Hans
Datum:
Hans Peter L. schrieb: > i = key_state ^ KEY_PIN; // key changed ? > //aktvie high, desswegen Änderung ~KEY_PIN! Und wie groß ist der Pulldown? Peter
Datum:
Ich glaube 10k Ohm. Das wird über
/* Push-pull with weak pull-up, Strom auf 2mA eingestellt.*/
GPIOPadConfigSet(GPIO_PORTE_BASE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU);
|
festgelegt. Interessant ist auch, dass nach 10 Sekunden nach dem Start bereits "repeat" auf dem Display ausgegeben wird, obwohl noch keine Taste gedrückt wurde. Vielleicht liegt es gar nicht an dem Programm!?
Datum:
Hans Peter L. schrieb: > Das wird über/* Push-pull with weak pull-up, Strom auf 2mA eingestellt.*/ > GPIOPadConfigSet(GPIO_PORTE_BASE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU); > festgelegt. Aha, "weak pull-up" bedeutet also Pull-down? Peter
Datum:
Angehängte Dateien:Ach du sch***. Ich bin ein Eierkopf. Danke Peter, habe es im Datenblatt (siehe Anhang) falsch abgelesen. Deswegen habe ich es in der Routine verdreht. Jetzt funktioniert alles, so wie es soll und das mit einer "schlanken" Routine Vielen Dank! Grüße
Datum:
Hallo, Hans Peter L. schrieb: > Jetzt funktioniert alles, so wie es soll und das mit einer "schlanken" > Routine Ich verwende seit langem mit Erfolg Peters Tasten-Entprellung (Atmega/Atxmega). Bin jetzt aber dran, mich ein wenig in die ARM Cortex M3 (LPC1769) einzuarbeiten. Ich habe das Wichtigste dieses Thread verfolgt, wie hast die Initialisierung der Ports jetzt realisiert? Könntest du das nochmals in Netz stellen? Danke XMEGA
Datum:
Angehängte Dateien:Hallo, ich versuche seit einigen Stunden die Funktionen auf einem Attiny2313 zum laufen zu bekommen. Ich habe nun quasi den Code kopiert und an den Tiny abgepasst. Ich habe eine LED an PB0 angeschlossen. Nun, wenn ich das Programm starte, leuchtet die LED kurz (vermutlich bis der Timer überläuft), dann erlischt sie. Bei anderen Versuchen konnte ich die LED einmal mit der Funktion get_key_short ausschalten, danach lies sie sich allerdings nicht wieder einschalten. Ich benutze den internen Oszi bei 8MHz und habe CKDIV8-Fuse gesetzt. Wäre toll, wenn mir jemand einen Stoß in die richtige Richtung geben könnte. MfG
Datum:
noXe schrieb im Beitrag #1015280 am 13.10.2008: > Hallo! > Ich versuche den das Programm von Peter Dannegger für mich anzupassen > sodass es auf einem atmega644p läuft. Den Quellcode habe ich angehängt. > Mein Problem ist jetzt dass das Programm nur auf KEY0 reagiert, nicht > aber auf KEY1, bzw. es reagiert nur auf den 0ten Bit des Eingangsports. > Habe es in AVR Studio simuliert kann aber dieses komisches Verhalten > nicht einmal beschreiben. > An der Verdrahtung dürfte eigentlich nichts sein. Habe alle Anschlüsse > schon mehrmals kontrolliert. > Kann einer bitte den Quellcode überfliegen, vielleicht habe ich ja nur > einen dummen Fehler gemacht den ich selber nicht sehe. Genau das selbe Problem ist auch mir aufgetreten. Das Programm reagiert nur auf KEY0. Hat jemand eine Idee, wie man das beheben kann? Im Original-Beitrag (s.o.) ist der Quellcode angehängt. Vielen Dank...
Datum:
Hallo Steffi, hier hatte ich mal eine Beispielprogramm geschrieben: Beitrag "Re: 3fach Timer (einschaltverzögerung)" Wenn Du dir bitte mal die Module in den Verzeichnissen timer und tools ansiehst, wird es bestimmt klarer..
Datum:
Uwe S. schrieb: > Wenn Du dir bitte mal die Module in den Verzeichnissen timer und tools > ansiehst, wird es bestimmt klarer.. Danke für die Hilfe, Uwe. Im C-Code entprellen.c hab ich die "key release detect" entdeckt, welche du eingebaut hast. Könnte das auch mir bei meinem Problem helfen? Bevor ich etwas ausprobier und meinen C-Code umgestalte, würde ich gerne wissen, dass ich nicht komplett auf dem Holzweg bin. Vielen Dank und Gruß Steffi
Datum:
Hallo, was ist das Problem ? Ich denke bevor man nicht den gesamten CODE versteht, sollte man versuchen Stück für Stück dem Verständnis näher zu kommen und immer nur kleine Änderungen an Programmen machen. Nur so lerne ich etwas.. Also nun ran an den Speck.
uint8_t get_key_release( uint8_t key_mask ); |
wurde irgendwann mal im Thread diskutiert und veröffentlicht. Beitrag "Re: Universelle Tastenabfrage" Als Ergänzung noch dieser Link: Beitrag "Re: Universelle Tastenabfrage" Noch eine Tipp zum Programm: Beitrag "Re: Universelle Tastenabfrage"
#define ALL_KEYS (1<<KEY0) |
Es wird auch nur ein Bit als Eingang definiert !
Datum:
Uwe S. schrieb: > Also nun ran an den Speck Bin schon dabei. Leider ist so ein C-Code zur Taster-Entprellung nicht so trivial, besonders für Anfänger wie mich. Habe schon eine Tastenentprellung von Peter Dannegger auf mein Programm angepasst. Leider tritt dann das Problem auf, dass das Programm nur auf den ersten Taster reagiert und den zweiten "ignoriert". Ein Tipp, wie ich hier weiter vorgehen könnte, würde mir sehr helfen. Vielen Dank Steffi
Datum:
Peter und andere lesen auch hier mit, also hänge doch deine komplettes Programm bitte hier an ! Dann sehen wir alle schneller wo es hängt.
Datum:
Angehängte Dateien:Uwe S. schrieb: > hänge doch deine komplettes Programm bitte hier an ! > > Dann sehen wir alle schneller wo es hängt. Danke für die Unterstützung!
Datum:
Hallo Steffi, schon mal das Programm übersetzt und wenn ja, was sind die Fehlermeldungen ? Wie Arbeitest Du mit dem avr-gcc - ich meine mit welcher Umgebung ? Ich verwende klassisch nur ein Makefile, make in einer bash-shell.
Datum:
Weder Fehlermeldung, noch warnings. Ich verwende das AVR Studio 4. Konntest du irgendwelche Ungereimtheiten im C-Code finden?
Beitrag #2298913 wurde vom Autor gelöscht.
Datum:
Steffi schrieb: > Konntest du irgendwelche Ungereimtheiten im C-Code finden? Dein C-Code sieht recht ordentlich aus. Natürlich ist er umfangreicher, als nötig, aber sicher ist sicher :) Für deine Taster benutzt du PINC1 und PINC2, aber nur der PINC1 "funktioniert". Versuch doch einfach mal einen anderen PIN zu nehmen anstatt PINC2. Vielleicht hilft es ja... Gruß Helmut
Datum:
Oh, sieh an. Auf einem anderen PIN funktioniert der Taster einwandfrei! Ich weiß zwar noch nicht genau wieso, aber das werd ich schon noch herausfinden :) Danke, Helmut
Datum:
Heyho, für active high muss noch eine zweite Zeile geändert werden, sonst liefert get_key_rpt und damit auch get_key_long für jede nicht gedrückte Taste das Signal, dass sie gedrückt sei. Ändere
key_rpt |= key_state & REPEAT_MASK; |
in
key_rpt = ~key_rpt & ~(key_state & REPEAT_MASK); |
oder (vielleicht einfacher zu erkennen, was gemacht wurde)
key_rpt = ~(key_rpt | (key_state & REPEAT_MASK)); |
Diese Zeilen invertieren einfach key_rpt. Tschau, Procyon
Datum:
Hallo Peter und Co, ich versuche gerade die Entrpellung für einen Attiny25 zu verwenden. Irgendwie bekomme ich es im Moment nicht gebacken. Trotzdem schon mal danke an Peter für diese Routine :)
/************************************************************************/ /* */ /* Debouncing 8 Keys */ /* Sampling 4 Times */ /* With Repeat Function */ /* */ /* Author: Peter Dannegger */ /* danni@specs.de */ /* */ /************************************************************************/ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> typedef unsigned char u8; typedef signed short s16; #define XTAL 1e6 // 1MHz #define KEY_DDR DDRB #define KEY_PIN PINB #define KEY_PORT PORTB #define KEY0 4 #define LED_DDR DDRB #define LED_PORT PORTB #define LED0 0 #define LED1 1 #define LED2 2 #define REPEAT_MASK (1<<KEY0) // repeat: key1 #define REPEAT_START 50 // after 500ms #define REPEAT_NEXT 20 // every 200ms u8 key_state; // debounced and inverted key state: // bit = 1: key pressed u8 key_press; // key press detect u8 key_rpt; // key long press and repeat SIGNAL (SIG_OVERFLOW0) // every 10ms { static u8 ct0, ct1, rpt; u8 i; TCNT1 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5); // preload for 10ms i = key_state ^ ~KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } u8 get_key_press( u8 key_mask ) { cli(); // read and clear atomic ! key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) sei(); return key_mask; } u8 get_key_rpt( u8 key_mask ) { cli(); // read and clear atomic ! key_mask &= key_rpt; // read key(s) key_rpt ^= key_mask; // clear key(s) sei(); return key_mask; } u8 get_key_short( u8 key_mask ) { cli(); // read key state and key press atomic ! return get_key_press( ~key_state & key_mask ); } u8 get_key_long( u8 key_mask ) { return get_key_press( get_key_rpt( key_mask )); } int main( void ) { TCCR1 = 1<<CS02^1<<CS00; // divide by 1024 TIMSK = (1<<TOIE1); // enable timer interrupt //PB4 als Eingang konfigurieren KEY_DDR &= ~(1<<KEY0); //Pullup aktivieren bei PB4 KEY_PORT = (1<<KEY0); //Ausgänge konfigurieren LED_DDR |= (1<<LED0)|(1<<LED1)|(1<<LED2); sei(); for(;;){ // main loop // single press if( get_key_press( 1<<KEY0 )) LED_PORT ^= 1<<LED0; // release after short press: task 1 // long press: task 2 if( get_key_short( 1<<KEY0 )) LED_PORT ^= 1<<LED1; if( get_key_long( 1<<KEY0 )) LED_PORT ^= 1<<LED2; // single press and repeat /*if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){ u8 i = LED_PORT; i = (i & 0x07) | ((i << 1) & 0xF0); if( i < 0xF0 ) i |= 0x08; LED_PORT = i; }*/ } } |
Weiß zufällig jemand was ich falsch mache? Gruß Flo
Datum:
Hallo, schau dir bitte die angepassten Routinen von PeDa in diesem Beitrag an: Beitrag "Re: 3fach Timer (einschaltverzögerung)" http://www.mikrocontroller.net/attachment/107805/c... Was dir sofort auffallen sollte beiden Dateien unter tools:
volatile uint8_t key_state; |
Es wurden also noch einige wichtige Änderungen hier im Thread gemacht, die dann eingeflossen sind. Also lese bitte alles von Oben nach.
Datum:
Moin alle zusammen! Erstmal frohe Weihnachten :) Ich versuche gerade zu verstehen, wie die Methode get_key_long() funktoniert. Ich bin mir nicht sicher, ob dies hier schon einmal erklärt worden ist. Ich habe mich bemüht den ganzen Thread durchzugucken, habe aber nichts gefunden. So, nun zur eigentlichen Frage. Zuerst wird ja get_key_rpt() aufgerufen und das wird nur eins, wenn die Taste min. 500ms gedrückt wurde. Dann kommt die Methode get_key_press() ins Spiel. Wenn die Taste noch nicht 500ms gedrückt wurde, wird ihr ja die ganze Zeit als Parameter 0x00 übergeben und es kommt immer 0x00 zurück. Nach 500ms wird ihr dann eben die 1 an der jeweiligen Position übergeben. Aber nach 500ms wurde das Bit in key_press doch schon längst gelöscht, weil das ja sofort beim Timeroverflow nach dem setzen wieder gelöscht wird. Also müsste get_key_press() dann doch immer eine 0 liefern? Aber sie gibt ja eine 1 zurück, warum funktiniert das? Danke schon mal und noch ein fröhliches Weihnachtsfest. Gruß Pascal
Datum:
Tilman schrieb: > Heyho, > > für active high muss noch eine zweite Zeile geändert werden, sonst > liefert get_key_rpt und damit auch get_key_long für jede nicht gedrückte > Taste das Signal, dass sie gedrückt sei. > > Änderekey_rpt |= key_state & REPEAT_MASK; > inkey_rpt = ~key_rpt & ~(key_state & REPEAT_MASK); > oder (vielleicht einfacher zu erkennen, was gemacht wurde)key_rpt = ~(key_rpt | (key_state & REPEAT_MASK)); > > Diese Zeilen invertieren einfach key_rpt. > > Tschau, > Procyon Servus Verwende Hr Danneggers Code nun schon eine Weile und bin äußerst zufrieden damit. Hab ihn nun auch das erste mal mit active high Tastern in Verbindung und mir hierzu die Foreneinträge angesehen. Meiner Meinung nach muss für active high Schalter lediglich das KEY_PIN Makro invertiert werden (siehe Beitrag "Re: Universelle Tastenabfrage"), der Rest bleibt exakt gleich! Schließlich wird ja der Port auch nur an dieser Stelle "eingelesen". Die restliche Logik ändert sich ja nicht. Da ich eine faule Natur bin und die Logik nur kurz überflogen hab, hab ich das ganze einem Praxistest unterzogen und tatsächlich, key_rpt funktioniert in der Standardversion einwandfrei. Kein Grund also, key_rpt in der ISR umzuschreiben? (geschweige denn die long_... Funktion. lg Vincent ps: Ich ändere den Artikel erst, sofern dies jemand bestätigt hat, da ich Teile des Codes stark modifiziert hab. (mehrere Ports, Pins verschiedener Ports, usw.) Ev. lieg ich ja doch falsch.
Datum:
Danke für den Super Code!, leider funktioniert bei mir die get_key_long funktion nicht (es togglet ausschließlich LED0 mit KEY1 (beim loslassen)). Ich arbeite mit einem ATTiny 2313 auf dem Pollinboard, IDE-Kabel und dem Adapter (http://www.pollin.de/shop/dt/MjQ5OTgxOTk-/Bausaetz..., alle Jumper gezogen). Ich weiß auch nicht ob der Takt richtig eingestellt habe, eigentlich hab ich ja nen externen 8 MHz quarz der standardmäßig akiviert sein müsste, oder? Nur dann Toggelt es bei KEY2 zu langsam.
/************************************************************************/ /* */ /* Debouncing 8 Keys */ /* Sampling 4 Times */ /* With Repeat Function */ /* */ /* Author: Peter Dannegger */ /* danni@specs.de */ /* */ /************************************************************************/ #include <stdint.h> #include <avr/io.h> #include <avr/interrupt.h> #define F_CPU 1000000 #ifndef F_CPU #define F_CPU 1000000 // processor clock frequency #warning kein F_CPU definiert #endif #define KEY_DDR DDRB #define KEY_PORT PORTB #define KEY_PIN PINB #define KEY0 4 #define KEY1 2 #define KEY2 3 #define ALL_KEYS (1<<KEY0 | 1<<KEY1 | 1<<KEY2) #define REPEAT_MASK ( 1<<KEY2) // repeat: key1, key2 #define REPEAT_START 50 // after 500ms #define REPEAT_NEXT 20 // every 200ms #define LED_DDR DDRD #define LED_PORT PORTD #define LED0 1 #define LED1 2 #define LED2 5 #define ALL_LEDS (1<<LED0 | 1<<LED1 | 1<<LED2) volatile uint8_t key_state; // debounced and inverted key state: // bit = 1: key pressed volatile uint8_t key_press; // key press detect volatile uint8_t key_rpt; // key long press and repeat ISR( TIMER0_OVF_vect ) // every 10ms { static uint8_t ct0, ct1, rpt; uint8_t i; TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms i = key_state ^ ~KEY_PIN; // key changed ? ct0 = ~( ct0 & i ); // reset or count ct0 ct1 = ct0 ^ (ct1 & i); // reset or count ct1 i &= ct0 & ct1; // count until roll over ? key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect if( (key_state & REPEAT_MASK) == 0 ) // check repeat function rpt = REPEAT_START; // start delay if( --rpt == 0 ){ rpt = REPEAT_NEXT; // repeat delay key_rpt |= key_state & REPEAT_MASK; } } /////////////////////////////////////////////////////////////////// // // check if a key has been pressed. Each pressed key is reported // only once // uint8_t get_key_press( uint8_t key_mask ) { cli(); // read and clear atomic ! key_mask &= key_press; // read key(s) key_press ^= key_mask; // clear key(s) sei(); return key_mask; } /////////////////////////////////////////////////////////////////// // // check if a key has been pressed long enough such that the // key repeat functionality kicks in. After a small setup delay // the key is reported being pressed in subsequent calls // to this function. This simulates the user repeatedly // pressing and releasing the key. // uint8_t get_key_rpt( uint8_t key_mask ) { cli(); // read and clear atomic ! key_mask &= key_rpt; // read key(s) key_rpt ^= key_mask; // clear key(s) sei(); return key_mask; } /////////////////////////////////////////////////////////////////// // uint8_t get_key_short( uint8_t key_mask ) { cli(); // read key state and key press atomic ! return get_key_press( ~key_state & key_mask ); } /////////////////////////////////////////////////////////////////// // uint8_t get_key_long( uint8_t key_mask ) { return get_key_press( get_key_rpt( key_mask )); } int main( void ) { LED_PORT |= ALL_LEDS; LED_DDR |= ALL_LEDS; // Configure debouncing routines KEY_DDR &= ~ALL_KEYS; // configure key port for input KEY_PORT |= ALL_KEYS; // and turn on pull up resistors TCCR0B = (1<<CS02)|(1<<CS00); // divide by 1024 TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms TIMSK |= 1<<TOIE0; // enable timer interrupt sei(); while(1){ if( get_key_short( 1<<KEY1 )) LED_PORT ^= 1<<LED0; if( get_key_long( 1<<KEY1 )) LED_PORT ^= 1<<LED2; // single press and repeat if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){ LED_PORT ^= 1<<LED0; } } } |
Nebenbei ist mir noch aufgefallen, dass PD0 und 5 immer high und PD6 immer low sind, aber ich denke da hab ich was beim löten versaut, da mach im mich jetzt mal auf die suche :)
Datum:
Felix schrieb: > Nebenbei ist mir noch aufgefallen, dass PD0 und 5 immer high und PD6 > immer low sind, aber ich denke da hab ich was beim löten versaut, da > mach im mich jetzt mal auf die suche :) den fehler hab ich schonmal gefunden, Am IDE-Kabel sind die GND leitungen verbunden :( ätzend
Datum:
Angehängte Dateien:In Anlehnung vom Code des Herrn Peter Dannegger, habe ich meine eigene "Tasten Entprellung" mit "0-1 Flanken" dedection geschrieben, da ich den Code vom Herrn Peter Danneger nicht so ganz verstanden habe. Aus diesem Grunde wollte ich die "Profis" hier mal fragen ob "mein code" blödsin ist oder nicht. Die Beschreibung der Funktionsweise ist in der Datei vorhanden Danke für eure Mühe und Antworten 3RR0R
Beitrag #2751740 wurde von einem Moderator gelöscht.
Datum:
Hallo und schönen guten Abend, ich nutze auch den Code von Peter. Hab aber ein Problem, da ich zwei Tasten von DDRB und DDRC nutzen muss. Geht leider nicht anders. #define DDR_Key DDRC DDRB Also wie muss ich das richtig definieren? Kann mir da jemand auf die Sprunge helfen? Vielen Dank schon mal im Voraus. Gruß Patrick
Datum:
Hallo, dann musst Du die gesamte Routine zwei mal verwenden und die Funktion des Codes entsprechend (Namen) anpassen. Also auch in der ISR die beiden Ports nach einander einlesen und entprellen.
Datum:
Du kannst alle Datentypen auf uint16_t erweitern und die oberen 8 Bits für den einen und die unteren 8 für den anderen Port verwenden. In der ISR musst du dann halt noch die beiden Ports einlesen und an die entsprechenden Positionen schieben.
Datum:
Hallo Forum, ich möchte den Code gerne für eine konfigurierbare Anzahl Ports erweitern. Dabei habe ich aber ein paar Schwierigkeiten. Meine Idee ist, die Varbiablen mit dem Keystatus als Array zu erweitern. Aus "volatile uint8_t key_state" wird "volatile uint8_t key_state[x]" Wobei x der Anzahl der Ports entspricht. Das kann ich auch in der ISR als Schleife gut durchlaufen. Jetzt brauche ich aber für jeden Port ja auch die Angabe, welcher Port es nun genau ist. Also muss ich für jeden Port noch soetwas haben: #define KEY_PORT PORTG #define KEY_PIN PING Aber dann müsste ich ständig unterscheiden und habe doch wieder ganz viel Code doppelt. Dann kommen dazu noch mehr Defines, die zu noch mehr Unterscheidungen führen würden. Für jeden Port: #define USE_KEY_RELEASE #define USE_KEY_REPEAT #define ACTIVE_STATE (active high or low) Möchte man es noch flexibler, müsste man sogar jeden Portpin einzeln als active high oder low definieren können. Ein weiterer Punkt ist dann, dass ich jeder Funktion zur Tastenabfrage (get_key_press() etc.) dann auch noch den zugehörigen Port bzw. x (von oben) übergeben müsste. Das macht die Nutzung im Programm umständlich und fehleranfällig. Wie geht man an sowas ran und setzt das vernünftig um? Oder ist das viel zu aufwändig und man kopiert besser allen Code für jeden Port und vergibt neue Namen? Ich benötige beispielsweise 4 Ports. Vincent Hamp schrieb: > ps: Ich ändere den Artikel erst, sofern dies jemand bestätigt hat, da > ich Teile des Codes stark modifiziert hab. (mehrere Ports, Pins > verschiedener Ports, usw.) Ev. lieg ich ja doch falsch. Dein Code Vincent würde mich sehr interessieren! Danke und Gruß Peter
Datum:
Schade, keine Hinweise zur konzeptionellen Umsetzung... Da ich jetzt mit 8 Tasten auf 3 verschiedenen Ports auskomme, habe ich es in Anlehnung an Beitrag "Re: Entprellung mit Dannegger-Code auf unterschiedlichen Ports" gelöst. Allerdings mit "active low" und "active high" Tasten gemischt. Einzige Änderung im Code:
local = key_state ^ KEYS_PIN_MIXED; // key changed? |
mit folgenden Defines:
#define KEY0 0 #define KEY1 1 #define KEY2 2 #define KEY3 3 #define KEY4 4 #define KEY5 5 #define KEY6 6 #define KEY7 7 #define ALL_KEYS (1<<KEY0 | 1<<KEY1 | 1<<KEY2 | 1<<KEY3 | 1<<KEY4 | 1<<KEY5 | 1<<KEY6 | 1<<KEY7) //set according hardware pins #define KEY0_PIN (PING&(1<<PG0)) //PG0 -> key0 #define KEY1_PIN (PINC&(1<<PC1)) //PC1 -> key1 #define KEY2_PIN ((PINC&(1<<PC0))<<2) //PC0 -> key2 #define KEY3_PIN ((PINC&(1<<PC7))>>4) //PC7 -> key3 #define KEY4_PIN (PINE&(1<<PE4)) //PE4 -> key4 #define KEY5_PIN (PINE&(1<<PE5)) //PE5 -> key5 #define KEY6_PIN (PINE&(1<<PE6)) //PE6 -> key6 #define KEY7_PIN (PINE&(1<<PE7)) //PE7 -> key7 #define KEYS_ACTIVE_HIGH (KEY4_PIN | KEY5_PIN | KEY6_PIN | KEY7_PIN) #define KEYS_ACTIVE_HIGH_MASK ((1<<KEY4)|(1<<KEY5)|(1<<KEY6)|(1<<KEY7)) //must set bits according to KEYS_ACTIVE_HIGH #define KEYS_ACTIVE_LOW (KEY0_PIN | KEY1_PIN | KEY2_PIN | KEY3_PIN) #define KEYS_PIN_MIXED ( KEYS_ACTIVE_HIGH | (uint8_t)~(KEYS_ACTIVE_LOW | KEYS_ACTIVE_HIGH_MASK) ) |
Die KEYS_ACTIVE_HIGH_MASK ist wichtig, da sonst durch die Invertierung der "active low" Taster die "active high" Taster gesetzt werden. In den Defines oben ist die Benennung mit KEYx übersichtlicher. Dann kann man sich für die Tasten immer noch sprechende Defines anlegen:
#define ButtonOK KEY0
... |
Vielleicht hilft das noch jemandem weiter. Gruß Peter
Datum:
Naja wie bereits geschrieben hättest du die Typen erweitern können. So kannst du mit uint32 bspw. 32 Tasten entprellen, sprich 4 Ports beim AVR8. Oder du machst Arrays aus allen Variablen und baust dir bspw. eine Funktion der du die Eingangsdaten und einen Index mitgibst und die dir dann speziell diese Indices entprellt. In der ISR rufst du dann diese Funktion entsprechend (oft) auf:
#define NO_OF_ELEM 2 u8 key_state[NO_OF_ELEM]; u8 key_press[NO_OF_ELEM]; u8 key_rpt[NO_OF_ELEM]; void debounce (u8 input, u8 index) { static u8 ct0[NO_OF_ELEM], ct1[NO_OF_ELEM], rpt[NO_OF_ELEM]; u8 i; TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5); i = key_state[index] ^ ~input; ct0[index] = ~( ct0[index] & i ); ct1[index] = ct0[index] ^ (ct1[index] & i); i &= ct0[index] & ct1[index]; key_state[index] ^= i; key_press[index] |= key_state[index] & i; if( (key_state[index] & REPEAT_MASK) == 0 rpt[index] = REPEAT_START; if( --rpt[index] == 0 ){ rpt[index] = REPEAT_NEXT; key_rpt[index] |= key_state[index] & REPEAT_MASK; } } SIGNAL (SIG_OVERFLOW0) { debounce(PINA, 0); debounce(PINB, 1); //... } |
<- auf die schnelle aus dem Code aus dem ersten Beispiel gebastelt ohne es zu testen. Für REPEAT_MASK, REPEAT_START & REPEAT_NEXT müsste man sich noch was überlegen falls die abweichen (sollen). Die anderen Funktionen müssen dann natürlich auch Indices unterstützen. Vorteil von letzterer Lösung wäre, dass du mit unterschiedlichen Intervallen entprellen kannst.
Datum:
Karsten K. schrieb: > Moin Moin! > Ich möchte die get_key_common() Funktion nutzen um durch langes drücken > zweier Tasten in den "Programmier-mode" zu gelangen. > Die Funktion reagiert aber auch schon auf kurzen Tastendruck. Wie kann > ich die Funktion erweitern/verändern, sodaß nur auf langes drücken > zweier Tasten reagiert wird? > > 10000000 * 10^12 Dank für eure Hilfe > > Karsten Das würde mich auch interessieren. Gibt es da eine Möglichkeit? Gruß, Thomas
Beitrag #2872618 wurde von einem Moderator gelöscht.
Beitrag #2872627 wurde von einem Moderator gelöscht.
Datum:
Das war gestern wohl etwas zu spät !! Bitte meine letzten beiden postings löschen !!!! Habe es heute morgen noch einmal aufgemalt und die ISR jetzt verstanden. Sorry !
Datum:
Angehängte Dateien:Ich habe folgendes Problem. Die Erkennung von kurzem und langem Tastendruck funktioniert nicht immer. Mein Testaufbau besteht aus einem Attiny 2313 auf einem STK500. Ich verwende die internen 8MHz und den 8er Teiler. Folgendes Szenario: Ich drücke die Taste kurz, ein kurzer Tastendruck wird erkannt. Sofort drücke ich die Taste nochmal, ein langer Tastendruck wird erkannt. Dieses Verhalten tritt des öfteren auf. Wenn ich also die Taste dauernd kurz drücke wird meistens ein kurzer Druck erkannt, jedoch manchmal auch einer langer Tastendruck. Ich habe auch REPEAT_START mittlerweile auf 2s, jedoch wird der lange Druck schon viel früher erkannt. Nach meinem empfinden ist die Erkennungszeit für einen langen Tastendruck nicht immer gleich. Was könnte da falsch sein? Stimmt da evtl. was mit dem Timerüberlauf nicht? Danke
Datum:
Was hängt an PINA.2 ? Änder mal die Zeile in:
#define REPEAT_MASK (1<<KEY1) // repeat: key1 |
Peter
Datum:
Hallo Peter, danke für Deine Antwort. An PINA.2 hängt der Reset des Attiny. Das dürfte aber doch keinen Einfluss haben, oder?
Datum:
Jürgen F. schrieb: > An PINA.2 hängt der Reset des Attiny. Das dürfte aber doch keinen > Einfluss haben, oder? Doch. Die Repeat-Maske ist nicht einfach so da, weil ich Codezeilen schinden wollte. Sie hat auch eine Funktion: Sobald ein Pin in der Repeat-Maske betätigt (low) ist, fängt die Repeat-Zeit an zu zählen. Die Repeat-Maske darf daher nur die Pins beinhalten, die auch repeatet werden sollen! Peter
Datum:
Hallo Peter, aber der Reset ist doch in der Regel auf High, solange er nicht betätigt wurde, also dürfte er doch das System nicht beeinflussen? Oder habe ich da irgendwo einen Denkfehler? Aber natürlich hast Du recht, dass der Resetpin nichts in der Maske verloren hat. Es funktioniert auch mit der von Dir empfohlenen Änderung. Danke Gruß Jürgen
Datum:
Jürgen F. schrieb: > aber der Reset ist doch in der Regel auf High, solange er nicht betätigt > wurde, also dürfte er doch das System nicht beeinflussen? Nö. Das Datenblatt schweigt sich dazu leider aus. Eine genaue Portbeschreibung ist nur für PORT B und D vorhanden. PORT A wurde vergessen. Nimmt man mal das Datenblatt des ATtiny25 und den Pin PB5, dann steht in Table 10-4. Overriding Signals for Alternate Functions in PB[5:3] daß der Eingang durch /RSTDISBL abgeschaltet wird und somit 0 zurück gelesen wird. Und vermutlich verhält sich PINA2 des Attiny2313 auch so. Peter
Datum:
Ich hätte dazu auch noch eine Frage. Wann wird ein Tastendruck als kurz und wann als lang erkannt. Ich habe es so verstanden, dass wenn die Repeat-Zeit überschritten wurde (mind. einmal), so wird lang erkannt. Kurz dann, wenn noch kein Repeat statt gefunden hat. Ist das so richtig?
