Forum: Projekte & Code Universelle Tastenabfrage


von peter dannegger (Gast)


Angehängte Dateien:

Lesenswert?

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

von Fabian (Gast)


Lesenswert?

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

von peter dannegger (Gast)


Lesenswert?

@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

von Fabian (Gast)


Lesenswert?

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

von Micha (Gast)


Lesenswert?

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

von peter dannegger (Gast)


Lesenswert?

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

von andre (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

@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
1
//
2
      // release after short press: task 1
3
      // long press: task 2
4
5
    if( get_key_short( 1<<KEY1 ))
6
      LED_PORT ^= 1<<LED1;
7
8
    if( get_key_long( 1<<KEY1 ))
9
      LED_PORT ^= 1<<LED2;


Peter

von andre (Gast)


Lesenswert?

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!

von andre (Gast)


Lesenswert?

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.

von Karl Kalchgruber (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Karl Kalchgruber (Gast)


Lesenswert?

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

von peter dannegger (Gast)


Lesenswert?

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

von Nils Hoffmann (Gast)


Angehängte Dateien:

Lesenswert?

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?

von Nils Hoffmann (Gast)


Lesenswert?

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

von peter dannegger (Gast)


Lesenswert?

@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

von Nils Hoffmann (Gast)


Lesenswert?

Hi,

Ich bin immer wieder überrascht wie schnell man in diesem Forum Antwort
bekommt ! Daumen hoch !! g


vielen Dank

MfG
Nils

von duundich (Gast)


Lesenswert?

Wiederholung nach langem Tastendruck:
1
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..

von Peter D. (peda)


Lesenswert?

@duundich

Meinst Du ne Shift-Taste ?
1
if( get_key_press( 1<<KEY1 ){
2
  if( key_state & (1<<SHIFT_KEY) ){
3
// do action Shift + KEY1
4
  }else{
5
// do action KEY1
6
  }
7
}


Peter

von duundich (Gast)


Lesenswert?

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:
1
if( get_key_short( 1<<BUTTONA ))
2
3
programm -
4
5
if( get_key_short( 1<<BUTTONB ))
6
7
programm +
8
9
if( get_key_long( 1<<BUTTONA ) || get_key_rpt( 1<<BUTTONA ))
10
11
leiser
12
13
if( get_key_long( 1<<BUTTONB ) || get_key_rpt( 1<<BUTTONB )) 
14
15
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!

von noXe (Gast)


Lesenswert?

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?

von Pete (Gast)


Lesenswert?

Versuch es mal mit 1 Mhz.

von Peter D. (peda)


Lesenswert?

@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

von André (Gast)


Lesenswert?

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.

von dagobert (Gast)


Lesenswert?

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

von Jörg B. (senner05)


Lesenswert?

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

von (geloescht) (Gast)


Lesenswert?

(Dieser Beitrag wurde geloescht)

von Markus (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

Markus wrote:

> Dazu sind doch die Funktionen short und long oder?

Genau.
Siehe Beispiel.
Nicht vergessen, die Taste in REPEAT_MASK einzutragen.


Peter

von Markus (Gast)


Angehängte Dateien:

Lesenswert?

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

von Markus (Gast)


Lesenswert?

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 :)

von Thomas K. (tommes)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

@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

von Markus _. (markush)


Lesenswert?

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:
1
    if( get_key_press( 1<<KEY0 )) {
2
      lcd_gotoxy(0,1);
3
      lcd_puts("T0");            
4
                    }
5
    if( get_key_press( 1<<KEY1 )) {
6
      lcd_gotoxy(0,1);
7
      lcd_puts("T1");            
8
                    }
9
10
    if( get_key_press( 1<<KEY2 )) {
11
      lcd_gotoxy(0,1);
12
      lcd_puts("T2");            
13
                    }

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

von Peter D. (peda)


Lesenswert?

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

von Markus _. (markush)


Lesenswert?

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

von Markus _. (markush)


Lesenswert?

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

von mgiaco (Gast)


Lesenswert?

Was machen wenn man 10 Tasten braucht?
8 Stück funktionieren wunderbar.

danke
mathias

von Falk B. (falk)


Lesenswert?

@ 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

von ben (Gast)


Lesenswert?

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

von Sebastian (Gast)


Lesenswert?

Hallo,

ich probier grade Peters C-Komfortroutine aus:
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29

Mit WinAVR-20080430 funktioniert diese bei mir allerdings nur nach 
Einfügen einiger "volatile":
1
volatile uint8_t key_state;    // debounced and inverted key state:
2
                                  // bit = 1: key pressed
3
volatile uint8_t key_press;       // key press detect
4
 
5
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:
1
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
2
      uint8_t i = LED_PORT;
3
 
4
      i = (i & 0x07) | ((i << 1) & 0xF0);
5
      if( i < 0xF0 )
6
        i |= 0x08;
7
      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):
1
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
2
    
3
  LED_PORT ^=0x08;

Viele Grüße & gn8 !
Sebastian

von Sebastian (Gast)


Lesenswert?

Wenn 's da sonst keine Meinung zu gibt werde ich den Entprellung-Artikel 
mal entsprechend anpassen.
Grüße,
Sebastian

von John S. (student)


Lesenswert?

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

von Werner D. (dorni)


Lesenswert?

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?
1
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
2
      uint8_t i = LED_PORT;
3
 
4
      i = (i & 0x07) | ((i << 1) & 0xF0);
5
      if( i < 0xF0 )
6
        i |= 0x08;
7
      LED_PORT = i;

Vorab vielen Dank; Gruß Werner

von Peter D. (peda)


Lesenswert?

Werner Dornstädter wrote:

> Nun will ich diesen Code besser verstehen. Wer kann mir die Funktion
> erläutern?
>
>
1
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
2
>       uint8_t i = LED_PORT;
3
> 
4
>       i = (i & 0x07) | ((i << 1) & 0xF0);
5
>       if( i < 0xF0 )
6
>         i |= 0x08;
7
>       LED_PORT = i;
>


Lauflicht über die obersten 5 LEDs.

Daran sieht man schön die Wiederholfunktion.


Peter

von Bastler (Gast)


Lesenswert?

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:
1
uint8_t get_key_release( uint8_t key_mask )
2
{
3
cli();
4
key_mask &= key_release;
5
key_release ^= key_mask;
6
sei();
7
return key_mask;
8
}

Zusätzliche Zeile im Entprell-Code:
1
...
2
i &= ct0 & ct1;
3
key_release |= key_state & i;
4
key_state ^= i;
5
...

von Bastler (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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:
1
if( get_key_long( 1<<KEY0 )){
2
  while( keystate & 1<<KEY0 );
3
  sleep();
4
}

Peter

von Bastler (Gast)


Lesenswert?

Auch ne Möglichkeit! :-)

von Hannes S. (hannestum)


Lesenswert?

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?!

von Peter D. (peda)


Lesenswert?

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.
1
1MHz / 1024 / 10 = 97,6Hz = 10,2ms


Peter

von Micha (Gast)


Lesenswert?

> 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"

von Hannes S. (hannestum)


Lesenswert?

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....

von Micha (Gast)


Lesenswert?

@ 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.

von Hannes S. (hannestum)


Lesenswert?

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

von noXe (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von dom (Gast)


Lesenswert?

Hallo,

reicht es aus anstatt sei() und cli() bloß den Timer-Interrupt zu 
sperren?

von Peter D. (peda)


Lesenswert?

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

von dom (Gast)


Lesenswert?

Ich möchte nicht, dass ein bestimmter Interrupt dadurch gesperrt wird.

von Peter D. (peda)


Lesenswert?

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

von tarzan (Gast)


Lesenswert?

Ich hab exakt das gleiche Problem wie noXe mit einem Atmega644, 
irgendeine Idee?

von Philipp (Gast)


Lesenswert?

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.
1
  // release after short press: task 1
2
  // long press: task 2
3
  if(get_key_short(1<<KEY_ONOFFDIM)){
4
    if(geraet == ein)
5
    geraet = aus;
6
  else
7
    geraet = ein;  
8
  }
9
  if(get_key_long(1<<KEY_ONOFFDIM)){
10
    //do nothing
11
  }

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).
1
if(get_key_rpt(1<<KEY_ONOFFDIM)){
2
  if(geraet == ein) {
3
    if(dimmDirection == 0) {
4
      if(Value.m <= 95)
5
        Value.m += 5;
6
      else 
7
        Value.m = 100;
8
    }
9
    else {
10
      if(Value.m >= 5)
11
        Value.m -= 5;
12
      else 
13
        Value.m = 0;
14
    }
15
  }
16
}

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?

von Peter D. (peda)


Lesenswert?

@Philipp

Versuchs mal hiermit:
1
u8 get_key_long2( u8 key_mask )
2
{
3
  return get_key_press( get_key_rpt( key_press^key_mask ));
4
}
5
6
7
u8 get_key_long_rpt( u8 key_mask )
8
{
9
  return get_key_rpt( ~key_press^key_mask );
10
}

get_key_short: ein/aus
get_key_long2: Dimmrichtung wechseln
get_key_long_rpt: dimmem.


Peter

von Tobi (Gast)


Lesenswert?

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

von Jens K. (jensjk)


Lesenswert?

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

von Jens K. (jensjk)


Angehängte Dateien:

Lesenswert?

man kann den anhang im Bearbeiten wohl nicht anhängen, hiermit also noch 
mal anbei.

von Stefan S. (stefan_str)


Lesenswert?

1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
#ifndef F_CPU
6
#define F_CPU           16000000                   // processor clock frequency geändert
7
#warning kein F_CPU definiert
8
#endif
9
10
11
#define KEY_DDR         DDRB
12
#define KEY_PORT        PORTB
13
#define KEY_PIN         PINB
14
#define KEY0            0
15
#define KEY1            1
16
#define KEY2            2
17
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
18
19
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
20
#define REPEAT_START    1000                        //Zeit geändert
21
#define REPEAT_NEXT     500                        // Zeit geändert
22
23
#define LED_DDR         DDRA
24
#define LED_PORT        PORTA
25
#define LED0            0
26
#define LED1            1
27
#define LED2            2
28
29
volatile uint8_t key_state;                                // debounced and inverted key state:
30
                                                  // bit = 1: key pressed
31
volatile uint8_t key_press;                                // key press detect
32
33
volatile uint8_t key_rpt;                                  // key long press and repeat
34
35
36
ISR( TIMER0_OVF_vect )                            // every 10ms
37
{
38
  static uint8_t ct0, ct1, rpt;
39
  uint8_t i;
40
41
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
42
43
  i = key_state ^ ~KEY_PIN;                       // key changed ?
44
  ct0 = ~( ct0 & i );                             // reset or count ct0
45
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
46
  i &= ct0 & ct1;                                 // count until roll over ?
47
  key_state ^= i;                                 // then toggle debounced state
48
  key_press |= key_state & i;                     // 0->1: key press detect
49
50
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
51
     rpt = REPEAT_START;                          // start delay
52
  if( --rpt == 0 ){
53
    rpt = REPEAT_NEXT;                            // repeat delay
54
    key_rpt |= key_state & REPEAT_MASK;
55
  }
56
}
57
58
///////////////////////////////////////////////////////////////////
59
//
60
// check if a key has been pressed. Each pressed key is reported
61
// only once
62
//
63
uint8_t get_key_press( uint8_t key_mask )
64
{
65
  cli();                                          // read and clear atomic !
66
  key_mask &= key_press;                          // read key(s)
67
  key_press ^= key_mask;                          // clear key(s)
68
  sei();
69
  return key_mask;
70
}
71
72
///////////////////////////////////////////////////////////////////
73
//
74
// check if a key has been pressed long enough such that the
75
// key repeat functionality kicks in. After a small setup delay
76
// the key is reported beeing pressed in subsequent calls
77
// to this function. This simulates the user repeatedly
78
// pressing and releasing the key.
79
//
80
uint8_t get_key_rpt( uint8_t key_mask )
81
{
82
  cli();                                          // read and clear atomic !
83
  key_mask &= key_rpt;                            // read key(s)
84
  key_rpt ^= key_mask;                            // clear key(s)
85
  sei();
86
  return key_mask;
87
}
88
89
///////////////////////////////////////////////////////////////////
90
//
91
uint8_t get_key_short( uint8_t key_mask )
92
{
93
  cli();                                          // read key state and key press atomic !
94
  return get_key_press( ~key_state & key_mask );
95
}
96
97
///////////////////////////////////////////////////////////////////
98
//
99
uint8_t get_key_long( uint8_t key_mask )
100
{
101
  return get_key_press( get_key_rpt( key_mask ));
102
}
103
104
int main( void )
105
{
106
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
107
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
108
109
  TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
110
  TIMSK = 1<<TOIE0;        // enable timer interrupt
111
112
  LED_PORT = 0xFF;
113
  LED_DDR = 0xFF;
114
115
  while(1){
116
    if( get_key_short( 1<<KEY1 ))
117
      LED_PORT ^= 1<<LED1;
118
119
    if( get_key_long( 1<<KEY1 ))
120
      LED_PORT ^= 1<<LED2;
121
122
                                                  // single press and repeat
123
124
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
125
      uint8_t i = LED_PORT;
126
127
      i = (i & 0x07) | ((i << 1) & 0xF0);
128
      if( i < 0xF0 )
129
        i |= 0x08;
130
      LED_PORT = i;
131
    }
132
  }
133
}

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

von Stefan S. (stefan_str)


Angehängte Dateien:

Lesenswert?

> //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)

von Peter D. (peda)


Lesenswert?

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

von Stefan S. (stefan_str)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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:
1
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

von Arwed R. (arwedo)


Lesenswert?

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!

von Jens K. (jensjk)


Lesenswert?

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

von Jens K. (jensjk)


Angehängte Dateien:

Lesenswert?

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

von Arwed R. (arwedo)


Lesenswert?

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 ...

von Jens K. (jensjk)


Lesenswert?

[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

von Arwed R. (arwedo)


Lesenswert?

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:
1
ISR( TIMER0_OVF_vect ){
2
  static uint8_t ct0[SPALTENANZAHL], ct1[SPALTENANZAHL], rpt;
3
  uint8_t i;
4
  uint8_t key_mpex;    //die aktive Tasten-Spalte
5
6
  TCNT0 = TIMER0_STARTVALUE;
7
  //jetzt alle Spalten durcharbeiten
8
  for( key_mpex=0; key_mpex < SPALTENANZAHL; ++key_mpex ){
9
    i = 0;
10
    SPALTE_PORT &= ~(1<<(spalten_pin[key_mpex])); //aktive Spalte auf 0 
11
    i = key_state[key_mpex] ^ ~ZEILE_PIN;         // key changed ?
12
    ct0[key_mpex] = ~( ct0[key_mpex] & i );       // reset or count ct0
13
    ct1[key_mpex] = ct0[key_mpex] ^ (ct1[key_mpex] & i); // dito ct1
14
    i &= ct0[key_mpex] & ct1[key_mpex];    // count until roll over ?
15
    key_state[key_mpex] ^= i;             // then toggle debounced state
16
    key_press[key_mpex] |= key_state[key_mpex] & i; // 0->1: key pressed
17
    if( (key_state[key_mpex] & REPEAT_MASK) == 0 )  // check repeat fct.
18
      rpt = REPEAT_START;                           // start delay
19
    if( --rpt == 0 ){
20
      rpt = REPEAT_NEXT;                            // repeat delay
21
      key_rpt[key_mpex] |= key_state[key_mpex] & REPEAT_MASK;
22
    }   
23
    SPALTE_PORT |= (1<<(spalten_pin[key_mpex])); //Spalte deaktivieren
24
  }
25
}
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 ...

von MarkusK (Gast)


Lesenswert?

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

von MarkusK (Gast)


Lesenswert?

Oh, habe das wichtigste Vergessen: Wenn ich zB auf Taster 2 drücke 
flimmern die LEDs nurnoch. Auf Taster 1 funktioniert lang/kurz drücken 
auch nicht so richtig; man muss ganz kurz drauf'hacken', damit man LED 1 
zum Leuchten bringt.

von Peter D. (peda)


Lesenswert?

MarkusK schrieb:
> F_CPU steht auch auf 14745600UL.

Versuchs mal mit XTAL.


Peter

von MarkusK (Gast)


Lesenswert?

wie? ich hab den quarz an XTAL angeschlossen !?

von Peter D. (peda)


Lesenswert?

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

von Marco G. (marcoglatz)


Lesenswert?

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

von Werner B. (werner-b)


Lesenswert?

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"

von Hans-Peter (Gast)


Angehängte Dateien:

Lesenswert?

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

von Hans-Peter (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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:
1
if( get_key_short( xxx )){
2
...
3
}
4
if( get_key_long2( xxx ) || get_key_long_rpt( xxx )){
5
...
6
}

Versuch mal zu beschreiben, warum.


Peter

von eku (Gast)


Lesenswert?

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?

von Micha (Gast)


Lesenswert?

eku schrieb:
> Wie muß man den Source verändern, das mehrere Tasten erkannt werden?
Vielleicht zeigst du uns erstmal deinen Code!?

von Peter D. (peda)


Lesenswert?

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:
1
  if( get_key_press( 1<<USER_KEY0 )){
2
    if( key_state & 1<<SHIFT_KEY ){
3
      // shift + userkey0
4
    }else{
5
      // only userkey0 
6
    }
7
  }


Peter

von eku (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Thomas (Gast)


Lesenswert?

Wie das funktionieren soll verstehe ich leider immer noch nicht:
1
u8 get_key_long2( u8 key_mask )
2
{
3
  return get_key_press( get_key_rpt( key_press^key_mask ));
4
}
5
6
u8 get_key_long_rpt( u8 key_mask )
7
{
8
  return get_key_rpt( ~key_press^key_mask );
9
}
10
11
12
...
13
14
15
if( get_key_short( xxx )){
16
LED_PORT ^= 1<<LED1;
17
}
18
if( get_key_long2( xxx ) || get_key_long_rpt( xxx )){
19
LED_PORT ^= 1<<LED2;
20
}

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

von Micha (Gast)


Lesenswert?

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...

von thomas (Gast)


Lesenswert?

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...

von Peter D. (peda)


Lesenswert?

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:
1
/************************************************************************/
2
/*                                                                      */
3
/*                      Debouncing 8 Keys                               */
4
/*                      Sampling 4 Times                                */
5
/*                      With Repeat Function                            */
6
/*                                                                      */
7
/*              Author: Peter Dannegger                                 */
8
/*                                                                      */
9
/************************************************************************/
10
11
//                              Target: ATtiny13
12
13
#include <avr/interrupt.h>
14
#include <util/atomic.h>
15
16
17
#define F_CPU           1.2e6           // 1.2MHz
18
19
#define KEY_PIN         PINB
20
#define KEY_PORT        PORTB
21
#define KEY0            4
22
23
#define LED_DDR         DDRB
24
#define LED_PORT        PORTB
25
#define LED0            0
26
#define LED1            1
27
#define LED2            2
28
#define LED3            3
29
30
31
#define REPEAT_MASK     (1<<KEY0)       // repeat: key0
32
#define REPEAT_START    50              // after 500ms
33
#define REPEAT_NEXT     20              // every 200ms
34
35
36
uint8_t key_state;                      // debounced and inverted key state:
37
                                                // bit = 1: key pressed
38
uint8_t key_press;                      // key press detect
39
uint8_t key_rpt;                        // key long press and repeat
40
41
42
ISR( TIM0_COMPA_vect )                          // every 10ms
43
{
44
  static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
45
  uint8_t i;
46
47
  i = key_state ^ ~KEY_PIN;                     // key changed ?
48
  ct0 = ~( ct0 & i );                           // reset or count ct0
49
  ct1 = ct0 ^ (ct1 & i);                        // reset or count ct1
50
  i &= ct0 & ct1;                               // count until roll over ?
51
  key_state ^= i;                               // then toggle debounced state
52
  key_press |= key_state & i;                   // 0->1: key press detect
53
54
  if( (key_state & REPEAT_MASK) == 0 )          // check repeat function
55
     rpt = REPEAT_START;                        // start delay
56
  if( --rpt == 0 ){
57
    rpt = REPEAT_NEXT;                          // repeat delay
58
    key_rpt |= key_state & REPEAT_MASK;
59
  }
60
}
61
62
63
uint8_t get_key_press( uint8_t key_mask )
64
{
65
  ATOMIC_BLOCK(ATOMIC_FORCEON){
66
    key_mask &= key_press;                      // read key(s)
67
    key_press ^= key_mask;                      // clear key(s)
68
  }
69
  return key_mask;
70
}
71
72
73
uint8_t get_key_rpt( uint8_t key_mask )
74
{
75
  ATOMIC_BLOCK(ATOMIC_FORCEON){
76
    key_mask &= key_rpt;                        // read key(s)
77
    key_rpt ^= key_mask;                        // clear key(s)
78
  }
79
  return key_mask;
80
}
81
82
83
uint8_t get_key_short( uint8_t key_mask )
84
{
85
  uint8_t i;
86
87
  ATOMIC_BLOCK(ATOMIC_FORCEON)
88
    i = get_key_press( ~key_state & key_mask );
89
  return i;
90
}
91
92
93
uint8_t get_key_long( uint8_t key_mask )
94
{
95
  return get_key_press( get_key_rpt( key_mask ));
96
}
97
98
99
uint8_t get_key_long_r( uint8_t key_mask )      // if repeat function needed
100
{
101
  return get_key_press( get_key_rpt( key_press & key_mask ));
102
}
103
104
105
uint8_t get_key_rpt_l( uint8_t key_mask )       // if long function needed
106
{
107
  return get_key_rpt( ~key_press & key_mask );
108
}
109
110
111
int main( void )
112
{
113
  TCCR0A = 1<<WGM01;                                    // Mode 2. CTC
114
  TCCR0B = 1<<CS02 | 1<<CS00;                           // F_CPU / 1024
115
  OCR0A = F_CPU / 1024.0 * 10e-3 - 0.5;                 // 10ms interrupt
116
  TIMSK0 = 1<<OCIE0A;
117
118
  LED_PORT = 1<<LED0 | 1<<LED1 | 1<<LED2 | 1<<LED3;     // LEDs off
119
  LED_DDR = 1<<LED0 | 1<<LED1 | 1<<LED2 | 1<<LED3;
120
  KEY_PORT |= 1<<KEY0;                                  // Pullup on
121
  sei();
122
123
  for(;;){                                              // main loop
124
125
    if( get_key_short( 1<<KEY0 ))
126
      LED_PORT ^= 1<<LED0;
127
128
    if( get_key_long_r( 1<<KEY0 ))
129
      LED_PORT ^= 1<<LED1;
130
131
    if( get_key_rpt_l( 1<<KEY0 ))
132
      LED_PORT ^= 1<<LED2;
133
  }
134
}


Peter

von eku (Gast)


Lesenswert?

Micha schrieb:
> Kann er das nicht durch eine Kombination aus get_key_press und
> get_key_repeat lösen?

Was ist mit
1
   if (get_key_long (maske_über_alle_tasten) == zwei_bits_gesetzt)
2
   {
3
      aktion für zwei tasten
4
   }

Würde das funktionieren?

von Peter D. (peda)


Lesenswert?

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

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

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.
1
  for(;;){                                              // main loop
2
    switch( get_key_long(  1<<KEY0 | 1<<KEY1 ) |
3
            get_key_short( 1<<KEY0 | 1<<KEY1 )){
4
5
      case 1<<KEY0:             LED_PORT ^= 1<<LED0; break;
6
7
      case 1<<KEY1:             LED_PORT ^= 1<<LED1; break;
8
9
      case 1<<KEY0 | 1<<KEY1:   LED_PORT ^= 1<<LED2; break;
10
    }
11
  }

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

von Peter D. (peda)


Lesenswert?

Verbesserte Version:
1
  for(;;){                                              // main loop
2
    switch( get_key_long(  1<<KEY0 | 1<<KEY1 ) |
3
            get_key_short( 1<<KEY0 | 1<<KEY1 )){
4
5
      case 1<<KEY0:             LED_PORT ^= 1<<LED0; break;
6
7
      case 1<<KEY1:             LED_PORT ^= 1<<LED1; break;
8
9
      default:
10
                if((key_press & (1<<KEY0 | 1<<KEY1)) != (1<<KEY0 | 1<<KEY1))
11
                  break;
12
                get_key_press( 1<<KEY0 | 1<<KEY1 );
13
14
      case 1<<KEY0 | 1<<KEY1:   LED_PORT ^= 1<<LED2; break;
15
    }
16
  }


Peter

von Peter D. (peda)


Lesenswert?

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)
1
uint8_t get_key_common( uint8_t key_mask )
2
{
3
  return get_key_press((key_press & key_mask) == key_mask ? key_mask : 0);
4
}
5
6
// ...
7
8
//#define TIMEOUT
9
10
  for(;;){                                              // main loop
11
#ifdef TIMEOUT
12
    if( get_key_short( 1<<KEY0 ) || get_key_long( 1<<KEY0 ))
13
      LED_PORT ^= 1<<LED0;
14
15
    if( get_key_short( 1<<KEY1 ) || get_key_long( 1<<KEY1 ))
16
      LED_PORT ^= 1<<LED1;
17
#else
18
    if( get_key_short( 1<<KEY0 ))
19
      LED_PORT ^= 1<<LED0;
20
21
    if( get_key_short( 1<<KEY1 ))
22
      LED_PORT ^= 1<<LED1;
23
#endif
24
    if( get_key_common( 1<<KEY0 | 1<<KEY1 ))
25
      LED_PORT ^= 1<<LED2;
26
  }


Peter

von eku (Gast)


Lesenswert?

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.

von thomas (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Hier mal ein funktionierender Code:

Danke Peter, funktioniert perfekt!

Gruß
Thomas

von Heribert (Gast)


Lesenswert?

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!

von Fabian B. (fabs)


Lesenswert?

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

von Fabian B. (fabs)


Lesenswert?

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

von Micha (Gast)


Lesenswert?

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.

von Fabian B. (fabs)


Lesenswert?

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

von Jonas P. (jox)


Lesenswert?

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')
===========================================
1
           Zeitachse ---->
2
                 __      _________________      __
3
keystate    ____/  \____/                 \____/  \____
4
key_press   ----X-------X----------------------X-------
5
key_rpt     --------------------X--X--X--X-------------
6
key_release -------X----------------------X-------X----
7
                        |       |  |  |
8
                        |       |__|__|
9
                        |       | \ /
10
                        |_______|  REPEAT_NEXT
11
                              \
12
                               REPEAT_START


Modus 2 ('get_key_short' mit 'get_key_long')
============================================
1
                 __      _________________      __
2
keystate    ____/  \____/                 \____/  \____
3
key_short   -------X------------------------------X----
4
key_long    --------------------X----------------------
5
key_release -------X----------------------X-------X----


Modus 3 ('get_key_short' mit 'get_key_long_r' und 'get_key_rpt_l')
==================================================================
1
                 __      _________________      __
2
keystate    ____/  \____/                 \____/  \____
3
key_short   -------X------------------------------X----
4
key_long_r  --------------------X----------------------
5
key_rpt_l   -----------------------X--X--X-------------
6
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

von Jonas P. (jox)


Lesenswert?

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
1
volatile uint8_t key_press;   // key press detect
folgendes einfügen:
1
volatile uint8_t key_release; // key release detect


2. In der ISR den buffer aktualisieren:


Nach der Zeile
1
key_press |= key_state & i;    // 0->1: key press detect
diese Zeile einfügen:
1
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:
1
uint8_t get_key_release( uint8_t key_mask )
2
{
3
  cli();                     // read and clear atomic !
4
  key_mask &= key_release;   // read key(s)
5
  key_release ^= key_mask;   // clear key(s)
6
  sei();
7
  return key_mask;
8
}

Gruß
Jonas

von Christian (Gast)


Lesenswert?

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!

von Peter D. (peda)


Lesenswert?

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

von Karsten K. (karsten42)


Lesenswert?

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

von Christian (Gast)


Lesenswert?

> 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?

von Christian (Gast)


Lesenswert?

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

von Christoph (Gast)


Lesenswert?

Wollt mich nur kurz für die Routine bedanken,
funktioniert einwandfrei!

Danke Peter :)

Beste Grüße
Christoph

von Martin S. (sv_martin)


Lesenswert?

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)

von Martin S. (sv_martin)


Lesenswert?

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

von Thomas G. (old_beginner)


Lesenswert?

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

von Hans Peter L. (Gast)


Lesenswert?

Hallo Peter,
ich habe versucht deine universelle Tastenabfrage für einen ARM Cortex 
M3 einzusetzen.
Leider bekomme ich ganz komische Effekte.
Hier der Code:
1
#include "inc/hw_ints.h"
2
#include "inc/lm3s8962.h"
3
#include "inc/hw_memmap.h"
4
#include "inc/hw_types.h"
5
#include "driverlib/debug.h"
6
#include "driverlib/gpio.h"
7
#include "driverlib/interrupt.h"
8
#include "driverlib/systick.h"
9
#include "driverlib/sysctl.h"
10
#include "driverlib/timer.h"
11
#include "drivers/rit128x96x4.h"
12
13
14
#define sei() IntMasterEnable()
15
#define cli() IntMasterDisable()
16
typedef unsigned char  u8;
17
typedef signed short  s16;
18
19
#define KEY_PIN  ((u8)GPIO_PORTE_DATA_R)
20
/*#define GPIO_PORTE_DATA_R       (*((volatile unsigned long *)0x400243FC)),
21
 Falls Taste 0 NICHT gedrückt, ist GPIO_PORTE_DATA_R = 00000111,
22
 falls gedrückt ist GPIO_PORTE_DATA_R = 00000110.
23
 Letztes Bit toggelt also*/
24
25
#define KEY0    0
26
#define KEY1    1
27
#define KEY2    2
28
29
#define REPEAT_MASK  (1<<KEY1^1<<KEY2)  // repeat: key1, key2
30
#define REPEAT_START  50          // after 500ms
31
#define REPEAT_NEXT  20            // every 200ms
32
33
u8 key_state;                  // debounced and inverted key state:
34
                          // bit = 1: key pressed
35
u8 key_press;                  // key press detect
36
37
u8 key_rpt;                    // key long press and repeat
38
39
40
41
#ifdef DEBUG
42
void
43
__error__(char *pcFilename, unsigned long ulLine)
44
{
45
}
46
#endif
47
48
49
void
50
Timer0IntHandler(void)
51
{
52
static u8 ct0, ct1, rpt;
53
  u8 i;
54
  
55
    TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
56
57
58
  i = key_state ^ KEY_PIN;      // key changed ?
59
  //aktvie high, desswegen Änderung ~KEY_PIN!
60
  
61
  ct0 = ~( ct0 & i );      // reset or count ct0
62
  ct1 = ct0 ^ (ct1 & i);    // reset or count ct1
63
  i &= ct0 & ct1;      // count until roll over ?
64
  key_state ^= i;      // then toggle debounced state
65
  key_press |= key_state & i;    // 0->1: key press detect
66
67
  if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
68
     rpt = REPEAT_START;    // start delay
69
  if( --rpt == 0 ){
70
    rpt = REPEAT_NEXT;      // repeat delay
71
    key_rpt |= key_state & REPEAT_MASK;
72
  }
73
74
}
75
76
u8 get_key_press( u8 key_mask )
77
{
78
  cli();                    // Master Interrupts sperren
79
  key_mask &= key_press;            // read key(s)
80
  key_press ^= key_mask;             // clear key(s)
81
82
  sei();              // Master Interrupts freigeben
83
  return key_mask;
84
}
85
86
87
u8 get_key_rpt( u8 key_mask )
88
{
89
  cli();                    // Master Interrupts sperren
90
  key_mask &= key_rpt;              // read key(s)
91
  key_rpt ^= key_mask;               // clear key(s)
92
  sei();              // Master Interrupts freigeben
93
  return key_mask;
94
}
95
96
97
u8 get_key_short( u8 key_mask )
98
{
99
  cli();      // Master Interrupts sperren
100
101
  return get_key_press( ~key_state & key_mask );
102
}
103
104
105
u8 get_key_long( u8 key_mask )
106
{
107
108
  return get_key_press( get_key_rpt( key_mask ));
109
}
110
111
112
int main(void)
113
{
114
115
116
117
   /* CPU Clock*/
118
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
119
                   SYSCTL_XTAL_8MHZ);
120
121
    /* Display Init*/
122
    RIT128x96x4Init(1000000);
123
    
124
    /* Takt an Peripherie*/
125
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
126
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
127
    
128
    /* Setze GPIOs als Eingang*/
129
    GPIODirModeSet(GPIO_PORTE_BASE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_DIR_MODE_IN);
130
    
131
    /* Push-pull with weak pull-up, Strom auf 2mA*/
132
    GPIOPadConfigSet(GPIO_PORTE_BASE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_STRENGTH_2MA,
133
                     GPIO_PIN_TYPE_STD_WPU);
134
                     
135
    /* Master Interrupt einschalten*/               
136
    sei();
137
    
138
    /* Timer 0 - 32 Bit, Periodisch*/
139
    TimerConfigure(TIMER0_BASE, TIMER_CFG_32_BIT_PER);
140
    
141
    /* 10 ms Interrupt generieren*/
142
   TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/100);
143
   
144
   /* Interrup Routine festlegen*/
145
  TimerIntRegister(TIMER0_BASE,TIMER_A,Timer0IntHandler);
146
  
147
  /* Interrupts einschalten*/
148
   IntEnable(INT_TIMER0A);
149
   TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
150
   TimerEnable(TIMER0_BASE, TIMER_A);
151
   
152
   
153
while(1){          // main loop
154
     
155
      
156
   // single press
157
    if( get_key_short( 1<<KEY1 ))
158
    {
159
     RIT128x96x4Clear();
160
    RIT128x96x4StringDraw("short", 50, 40, 15);
161
    }
162
    
163
     
164
  // Repeat
165
    if( get_key_rpt( 1<<KEY1 ))
166
      {
167
    RIT128x96x4Clear();
168
    RIT128x96x4StringDraw("repeat", 50, 40, 15);
169
      
170
      }
171
  }
172
  
173
174
}

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

von Peter D. (peda)


Lesenswert?

Hans Peter L. schrieb:
> i = key_state ^ KEY_PIN;      // key changed ?
>   //aktvie high, desswegen Änderung ~KEY_PIN!

Und wie groß ist der Pulldown?


Peter

von Hans Peter L. (Gast)


Lesenswert?

Ich glaube 10k Ohm.
Das wird über
1
/* Push-pull with weak pull-up, Strom auf 2mA eingestellt.*/
2
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!?

von Peter D. (peda)


Lesenswert?

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

von Hans Peter L. (Gast)


Angehängte Dateien:

Lesenswert?

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

von XMEGA (Gast)


Lesenswert?

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

von Dominik Elbracht (Gast)


Angehängte Dateien:

Lesenswert?

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

von Steffi (Gast)


Lesenswert?

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...

von Uwe S. (de0508)


Lesenswert?

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..

von Steffi (Gast)


Lesenswert?

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

von Uwe S. (de0508)


Lesenswert?

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.
1
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"
1
#define ALL_KEYS        (1<<KEY0)

Es wird auch nur ein Bit als Eingang definiert !

von Steffi (Gast)


Lesenswert?

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

von Uwe S. (de0508)


Lesenswert?

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.

von Steffi (Gast)


Angehängte Dateien:

Lesenswert?

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!

von Uwe S. (de0508)


Lesenswert?

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.

von Steffi (Gast)


Lesenswert?

Weder Fehlermeldung, noch warnings.

Ich verwende das AVR Studio 4.

Konntest du irgendwelche Ungereimtheiten im C-Code finden?

von Helmut B. (helmut70)


Lesenswert?

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

von Steffi (Gast)


Lesenswert?

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

von Tilman (Gast)


Lesenswert?

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
1
key_rpt |= key_state & REPEAT_MASK;
in
1
key_rpt = ~key_rpt & ~(key_state & REPEAT_MASK);
oder (vielleicht einfacher zu erkennen, was gemacht wurde)
1
key_rpt = ~(key_rpt | (key_state & REPEAT_MASK));

Diese Zeilen invertieren einfach key_rpt.

Tschau,
Procyon

von Flozilla (Gast)


Lesenswert?

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 :)
1
/************************************************************************/
2
/*                                                                      */
3
/*                      Debouncing 8 Keys        */
4
/*      Sampling 4 Times        */
5
/*      With Repeat Function        */
6
/*                                                                      */
7
/*              Author: Peter Dannegger                                 */
8
/*                      danni@specs.de                                  */
9
/*                                                                      */
10
/************************************************************************/
11
12
#include <avr/io.h>
13
#include <avr/interrupt.h>
14
#include <avr/signal.h>
15
16
typedef unsigned char  u8;
17
typedef signed short  s16;
18
19
#define  XTAL    1e6    // 1MHz
20
21
#define KEY_DDR    DDRB
22
#define KEY_PIN    PINB
23
#define KEY_PORT  PORTB
24
#define KEY0    4
25
26
#define LED_DDR    DDRB
27
#define LED_PORT  PORTB
28
#define LED0    0
29
#define LED1    1
30
#define LED2  2
31
32
33
#define REPEAT_MASK  (1<<KEY0)  // repeat: key1
34
#define REPEAT_START  50    // after 500ms
35
#define REPEAT_NEXT  20    // every 200ms
36
37
38
u8 key_state;        // debounced and inverted key state:
39
          // bit = 1: key pressed
40
u8 key_press;        // key press detect
41
42
u8 key_rpt;        // key long press and repeat
43
44
45
SIGNAL (SIG_OVERFLOW0)      // every 10ms
46
{
47
  static u8 ct0, ct1, rpt;
48
  u8 i;
49
50
  TCNT1 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);  // preload for 10ms
51
52
  i = key_state ^ ~KEY_PIN;    // key changed ?
53
  ct0 = ~( ct0 & i );      // reset or count ct0
54
  ct1 = ct0 ^ (ct1 & i);    // reset or count ct1
55
  i &= ct0 & ct1;      // count until roll over ?
56
  key_state ^= i;      // then toggle debounced state
57
  key_press |= key_state & i;    // 0->1: key press detect
58
59
  if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
60
     rpt = REPEAT_START;    // start delay
61
  if( --rpt == 0 ){
62
    rpt = REPEAT_NEXT;      // repeat delay
63
    key_rpt |= key_state & REPEAT_MASK;
64
  }
65
}
66
67
68
u8 get_key_press( u8 key_mask )
69
{
70
  cli();          // read and clear atomic !
71
  key_mask &= key_press;                        // read key(s)
72
  key_press ^= key_mask;                        // clear key(s)
73
  sei();
74
  return key_mask;
75
}
76
77
78
u8 get_key_rpt( u8 key_mask )
79
{
80
  cli();          // read and clear atomic !
81
  key_mask &= key_rpt;                          // read key(s)
82
  key_rpt ^= key_mask;                          // clear key(s)
83
  sei();
84
  return key_mask;
85
}
86
87
88
u8 get_key_short( u8 key_mask )
89
{
90
  cli();      // read key state and key press atomic !
91
  return get_key_press( ~key_state & key_mask );
92
}
93
94
95
u8 get_key_long( u8 key_mask )
96
{
97
  return get_key_press( get_key_rpt( key_mask ));
98
}
99
100
101
int main( void )
102
{
103
  TCCR1 = 1<<CS02^1<<CS00;      // divide by 1024
104
  TIMSK = (1<<TOIE1);        // enable timer interrupt
105
  //PB4 als Eingang konfigurieren
106
  KEY_DDR &= ~(1<<KEY0);
107
  //Pullup aktivieren bei PB4
108
  KEY_PORT = (1<<KEY0);
109
  //Ausgänge konfigurieren
110
  LED_DDR |= (1<<LED0)|(1<<LED1)|(1<<LED2);
111
  sei();
112
113
  for(;;){          // main loop
114
      // single press
115
116
    if( get_key_press( 1<<KEY0 ))
117
      LED_PORT ^= 1<<LED0;
118
119
      // release after short press: task 1
120
      // long press: task 2
121
122
    if( get_key_short( 1<<KEY0 ))
123
      LED_PORT ^= 1<<LED1;
124
125
    if( get_key_long( 1<<KEY0 ))
126
      LED_PORT ^= 1<<LED2;
127
128
      // single press and repeat
129
130
    /*if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
131
      u8 i = LED_PORT;
132
133
      i = (i & 0x07) | ((i << 1) & 0xF0);
134
      if( i < 0xF0 )
135
        i |= 0x08;
136
      LED_PORT = i;      
137
    }*/
138
  }
139
}

Weiß zufällig jemand was ich falsch mache?

Gruß Flo

von Uwe S. (de0508)


Lesenswert?

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-src-110422-1.zip


Was dir sofort auffallen sollte beiden Dateien unter tools:
1
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.

von Pascal S. (pascal900198)


Lesenswert?

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

von Vincent H. (vinci)


Lesenswert?

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.

von Felix (Gast)


Lesenswert?

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-/Bausaetze_Module/Bausaetze/Bausatz_Streifen_Punktrasterplatinen_Adapter.html, 
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.
1
/************************************************************************/
2
/*                                                                      */
3
/*                      Debouncing 8 Keys                               */
4
/*                      Sampling 4 Times                                */
5
/*                      With Repeat Function                            */
6
/*                                                                      */
7
/*              Author: Peter Dannegger                                 */
8
/*                      danni@specs.de                                  */
9
/*                                                                      */
10
/************************************************************************/
11
 
12
#include <stdint.h>
13
#include <avr/io.h>
14
#include <avr/interrupt.h>
15
 
16
#define F_CPU 1000000
17
 
18
#ifndef F_CPU
19
#define F_CPU           1000000                   // processor clock frequency
20
#warning kein F_CPU definiert
21
#endif
22
 
23
#define KEY_DDR         DDRB
24
#define KEY_PORT        PORTB
25
#define KEY_PIN         PINB
26
#define KEY0            4
27
#define KEY1            2
28
#define KEY2            3
29
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
30
 
31
#define REPEAT_MASK     ( 1<<KEY2)       // repeat: key1, key2
32
#define REPEAT_START    50                        // after 500ms
33
#define REPEAT_NEXT     20                        // every 200ms
34
 
35
#define LED_DDR         DDRD
36
#define LED_PORT        PORTD
37
#define LED0            1
38
#define LED1            2
39
#define LED2            5
40
#define ALL_LEDS        (1<<LED0 | 1<<LED1 | 1<<LED2)
41
 
42
volatile uint8_t key_state;                                // debounced and inverted key state:
43
                              // bit = 1: key pressed
44
volatile uint8_t key_press;                                // key press detect
45
 
46
volatile uint8_t key_rpt;                                  // key long press and repeat
47
 
48
 
49
ISR( TIMER0_OVF_vect )                            // every 10ms
50
{
51
  static uint8_t ct0, ct1, rpt;
52
  uint8_t i;
53
 
54
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
55
 
56
  i = key_state ^ ~KEY_PIN;                       // key changed ?
57
  ct0 = ~( ct0 & i );                             // reset or count ct0
58
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
59
  i &= ct0 & ct1;                                 // count until roll over ?
60
  key_state ^= i;                                 // then toggle debounced state
61
  key_press |= key_state & i;                     // 0->1: key press detect
62
 
63
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
64
     rpt = REPEAT_START;                          // start delay
65
  if( --rpt == 0 ){
66
    rpt = REPEAT_NEXT;                            // repeat delay
67
    key_rpt |= key_state & REPEAT_MASK;
68
  }
69
}
70
 
71
///////////////////////////////////////////////////////////////////
72
//
73
// check if a key has been pressed. Each pressed key is reported
74
// only once
75
//
76
uint8_t get_key_press( uint8_t key_mask )
77
{
78
  cli();                                          // read and clear atomic !
79
  key_mask &= key_press;                          // read key(s)
80
  key_press ^= key_mask;                          // clear key(s)
81
  sei();
82
  return key_mask;
83
}
84
 
85
///////////////////////////////////////////////////////////////////
86
//
87
// check if a key has been pressed long enough such that the
88
// key repeat functionality kicks in. After a small setup delay
89
// the key is reported being pressed in subsequent calls
90
// to this function. This simulates the user repeatedly
91
// pressing and releasing the key.
92
//
93
uint8_t get_key_rpt( uint8_t key_mask )
94
{
95
  cli();                                          // read and clear atomic !
96
  key_mask &= key_rpt;                            // read key(s)
97
  key_rpt ^= key_mask;                            // clear key(s)
98
  sei();
99
  return key_mask;
100
}
101
 
102
///////////////////////////////////////////////////////////////////
103
//
104
uint8_t get_key_short( uint8_t key_mask )
105
{
106
  cli();                                          // read key state and key press atomic !
107
  return get_key_press( ~key_state & key_mask );
108
}
109
 
110
///////////////////////////////////////////////////////////////////
111
//
112
uint8_t get_key_long( uint8_t key_mask )
113
{
114
  return get_key_press( get_key_rpt( key_mask ));
115
}
116
 
117
int main( void )
118
{
119
  LED_PORT |= ALL_LEDS;
120
  LED_DDR  |= ALL_LEDS;                     
121
 
122
  // Configure debouncing routines
123
  KEY_DDR &= ~ALL_KEYS;                // configure key port for input
124
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
125
 
126
  TCCR0B = (1<<CS02)|(1<<CS00);         // divide by 1024
127
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
128
  TIMSK |= 1<<TOIE0;                   // enable timer interrupt
129
 
130
  sei();
131
 
132
  while(1){
133
   
134
    if( get_key_short( 1<<KEY1 ))
135
      LED_PORT ^= 1<<LED0;
136
 
137
    if( get_key_long( 1<<KEY1 ))
138
      LED_PORT ^= 1<<LED2;
139
 
140
    // single press and repeat
141
 
142
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
143
   LED_PORT ^= 1<<LED0;
144
    }
145
  }
146
}


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 :)

von Felix (Gast)


Lesenswert?

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

von 3RR0R (Gast)


Angehängte Dateien:

Lesenswert?

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

von Patrick (Gast)


Lesenswert?

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

von Uwe S. (de0508)


Lesenswert?

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.

von Micha (Gast)


Lesenswert?

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.

von Peter (Gast)


Lesenswert?

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

von Peter (Gast)


Lesenswert?

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:
1
local = key_state ^ KEYS_PIN_MIXED;            // key changed?
mit folgenden Defines:
1
#define KEY0            0
2
#define KEY1            1
3
#define KEY2            2
4
#define KEY3            3
5
#define KEY4            4
6
#define KEY5            5
7
#define KEY6            6
8
#define KEY7            7
9
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2 | 1<<KEY3 | 1<<KEY4 | 1<<KEY5 | 1<<KEY6 | 1<<KEY7)
10
11
//set according hardware pins
12
#define KEY0_PIN     (PING&(1<<PG0))       //PG0 -> key0
13
#define KEY1_PIN     (PINC&(1<<PC1))       //PC1 -> key1 
14
#define KEY2_PIN    ((PINC&(1<<PC0))<<2)   //PC0 -> key2 
15
#define KEY3_PIN    ((PINC&(1<<PC7))>>4)   //PC7 -> key3
16
#define KEY4_PIN     (PINE&(1<<PE4))       //PE4 -> key4
17
#define KEY5_PIN     (PINE&(1<<PE5))       //PE5 -> key5
18
#define KEY6_PIN     (PINE&(1<<PE6))       //PE6 -> key6
19
#define KEY7_PIN     (PINE&(1<<PE7))       //PE7 -> key7
20
#define KEYS_ACTIVE_HIGH        (KEY4_PIN | KEY5_PIN | KEY6_PIN | KEY7_PIN)
21
#define KEYS_ACTIVE_HIGH_MASK   ((1<<KEY4)|(1<<KEY5)|(1<<KEY6)|(1<<KEY7))   //must set bits according to KEYS_ACTIVE_HIGH
22
#define KEYS_ACTIVE_LOW         (KEY0_PIN | KEY1_PIN | KEY2_PIN | KEY3_PIN)
23
#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:
1
#define ButtonOK  KEY0
2
...

Vielleicht hilft das noch jemandem weiter.

Gruß
Peter

von Michael K. (kichi) (Gast)


Lesenswert?

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:
1
#define NO_OF_ELEM 2
2
3
u8 key_state[NO_OF_ELEM];
4
u8 key_press[NO_OF_ELEM];
5
u8 key_rpt[NO_OF_ELEM];
6
7
void debounce (u8 input, u8 index)
8
{
9
  static u8 ct0[NO_OF_ELEM], ct1[NO_OF_ELEM], rpt[NO_OF_ELEM];
10
  u8 i;
11
12
  TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);
13
14
  i = key_state[index] ^ ~input;
15
  ct0[index] = ~( ct0[index] & i );
16
  ct1[index] = ct0[index] ^ (ct1[index] & i);
17
  i &= ct0[index] & ct1[index];
18
  key_state[index] ^= i;
19
  key_press[index] |= key_state[index] & i;
20
21
  if( (key_state[index] & REPEAT_MASK) == 0 
22
     rpt[index] = REPEAT_START;
23
  if( --rpt[index] == 0 ){
24
    rpt[index] = REPEAT_NEXT;
25
    key_rpt[index] |= key_state[index] & REPEAT_MASK;
26
  }
27
}
28
29
SIGNAL (SIG_OVERFLOW0)
30
{
31
   debounce(PINA, 0);
32
   debounce(PINB, 1);
33
   //...
34
}
<- 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.

von Thomas H (Gast)


Lesenswert?

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

von ratlosER (Gast)


Lesenswert?

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 !

von Jürgen F. (snipor)


Angehängte Dateien:

Lesenswert?

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

von Peter D. (peda)


Lesenswert?

Was hängt an PINA.2 ?

Änder mal die Zeile in:
1
#define REPEAT_MASK     (1<<KEY1)       // repeat: key1

Peter

von Jürgen F. (snipor)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

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

von Jürgen F. (snipor)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Martin (Gast)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

REPEAT_START ist die Zeitschwelle für kurz/lang.

Peter

von Daniel (Gast)


Lesenswert?

Hallo,
danke für die sehr schöne Entprellung. Ich spiele gerade mit einem 
tiny13 herum und die Erkennung der langen und kurzen Tastendrücke 
funktioniert gut.


Mein Problem ist, wenn ich das TCCR0A für PWM konfiguriere funktioniert 
das lang kurz erkennen nicht mehr und wenn ich CTC einstelle dann klappt 
das PWM nicht mehr....
Ist es irgendwie möglich die LEDs mit PWM zu dimmen anstatt nur ein und 
auszuschalten im tiny13?

von Daniel (Gast)


Lesenswert?

Kann ich einfach die iSR in new funktioniert umwandeln und in der main 
loop aufrufen?

von Daniel (Gast)


Lesenswert?

Kann ich die ISR einfach in eine Funktion umwandeln und in der main loop 
aufrufen?

von Peter D. (peda)


Lesenswert?

Der Overflowinterrupt funktioniert auch im PWM-Modus.
Du mußt nur noch eine Variable zählen, bis ~10ms rum sind.

Oder nimm den ADC-Interrupt.
Oder nimm den Watchdoginterrupt.
Oder ein 10ms Delay in der Mainloop.

von Daniel (Gast)


Lesenswert?

Wollte mich nur mal rückmelden, ich habe es nun in den Watchdoginterrupt 
eingebaut und den lasse ich alle 16mS aufrufen.

Habe die repeat_start einfach bisschen reduziert damit die langen 
Tastendrücke nicht so lang sind und nun kann ich superkomfortabel und 
Kugelsicher mit einem Taster auf und abdimmen.

Vielen Dank

von Adrian E. (ahsd)


Lesenswert?

Hallo!

Ist es mit der C_TAST.C Methode möglich, mehr als 8 Taster abzufragen?

Ich stehe am Anfang eines Projektes, bei dem die Bedienoberfläche etwa 
13 Taster hätte.

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo Adrian,

wenn Du dir die 8 Bit Portzugriffe auf z.B. zwei ports Erweiterst.

Wir habe schon einen 8 Port (8Bit) Version realisiert.

von Jens (Gast)


Lesenswert?

Hallo Adrian,

wenn es mehr braucht kannst du das auch als Matrix machen, hab ich 
weiter oben schon mal eine Anpassung gepostet.

vg jens

von Adrian E. (ahsd)


Lesenswert?

Jens schrieb:
> Hallo Adrian,
>
> wenn es mehr braucht kannst du das auch als Matrix machen, hab ich
> weiter oben schon mal eine Anpassung gepostet.
>
> vg jens

Hallo Jens,

ich konnte deine .zip Datei nicht runterladen, vielleicht ist der 
Beitrag schon zu alt?


Mal kurz von meinem Vorhaben mit 13 Tastern abgesehen:
Ich bin gerade immer noch am Schwitzen um die normale Version zum Laufen 
zu bekommen / ansatzweise zu verstehen. Ich glaube ich habe gerade 
gecheckt, dass vorausgesetzt ist, dass man nicht am gleichen Port Taster 
und LEDs verwendet, oder? Das heißt mit einem Attiny13 ist der Code 
nicht ohne weiteres möglich, aber an einem 2313? (Der hat PA, PB, PD.)

Liebe Grüße,
Adrian.

von Adrian E. (ahsd)


Lesenswert?

Oder um die Frage nochmal etwas präziser zu formnulieren: Auf welchen 
AVR Modellen lässt sich dieser Code ausführen?

von Uwe S. (de0508)


Lesenswert?

Hallo Adrian,

das ist nur vom Compiler abhängig, in wieweit der C für dein Zielsystem 
unterstützt!

Und dem Lesen des Datenblattes für deinen µC um
a) die Ports richtig ansprechen zu können und
b) einen Timer mit ca 10ms Interruptzykluszeit muss man auch noch 
aufsetzen können.

von Peter D. (peda)


Lesenswert?

Adrian E. schrieb:
> Ich glaube ich habe gerade
> gecheckt, dass vorausgesetzt ist, dass man nicht am gleichen Port Taster
> und LEDs verwendet,

Nein.
Die Routine liest ja nur ein und gibt nichts auf Pins aus.

von Peter D. (peda)


Lesenswert?

Adrian E. schrieb:
> bei dem die Bedienoberfläche etwa
> 13 Taster hätte.

Hier eine Variante mit Leitungen sparen:
Beitrag "Tastenmatrix auslesen über nur 2 Leitungen"

von Jenny (Gast)


Lesenswert?

Wie wird die Zeit für das langdrücken festgelegt?

von Adrian E. (ahsd)


Lesenswert?

Peter Dannegger schrieb:
> Hier eine Variante mit Leitungen sparen:
> Beitrag "Tastenmatrix auslesen über nur 2 Leitungen"

Super! Danke für den Tipp, Peter.
Ich werde trotzdem erstmal versuchen es mit weniger Tasten anzugehen.

Uwe S. schrieb:
> b) einen Timer mit ca 10ms Interruptzykluszeit muss man auch noch
> aufsetzen können.

Wie schon erwähnt bin ich Anfänger und mir ist daher nicht klar, wie man 
das macht. Ich bitte um etwas Nachsicht :)Ich lerne gerade etwas 
parallel C und AVR. Könnt ihr mir auf die Sprünge helfen, wie ich das 
auf einem Attiny2313 zum laufen bekomme?

LG,
Adrian.

von Uwe S. (de0508)


Lesenswert?

Hallo  Adrian,

ich hatte das auch ganz ohne Wertung geschrieben.

Das Datenblatt zu deinem µC gibt es frische immer bei Atmel.com

und als Wissenbasis kann man auf diese Artikel verweisen:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

Damit kannst du bei weiteren Fragen einen neuen Thread eröffnen.

Immer nur einen pro Frage, sonst fallen die anderen über Dich her.

von Jörg Mauer (Gast)


Lesenswert?

Hallo,
ich habe ein Problem mit der Routine. Das lange und das kurze drücken 
funktioniert, nur verstehe ich nicht wie die anderen Funktionen genutzt 
werden. Konkret möchte ich kurze, lange und sehr lange Tastendrücke 
unterscheiden.
1
//short und long werden zuverlässig erkannt aber die beiden unteren machen nichts
2
  if( get_key_short( 1<<KEY0 )){LED_PORT ^= 1<<LED0;}
3
  
4
  if(get_key_long( 1<<KEY0 )) { LED_PORT ^= 1<<LED3;}               
5
                  
6
  if( get_key_long_r( 1<<KEY0 )) { LED_PORT ^= 1<<LED0;}
7
8
  if( get_key_rpt_l( 1<<KEY0 )) {LED_PORT ^= 1<<LED0;}

Ich bin um jeden hilfreichen Rat dankbar.

Gruß Jörg

von Peter D. (peda)


Lesenswert?


von Jörg Mauer (Gast)


Lesenswert?

getkeylong auskommentieren und dann geht es?

von Seppel (Gast)


Lesenswert?

Peter Dannegger schrieb:
> @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

Das funktioniert nicht so ganz, beim loslassen nach langem drücken wird 
einmal getkeyshort aufgerufen, getkeylong2 wird niemals aufgerufen...

von Marco (Gast)


Lesenswert?

Ja das ist mehrfach im Thread erwähnt, das auf und abdimmen mit einer 
Taste funktioniert nicht...hat der Peda wohl irgendwas vergessen zu 
erwähnen

von Peter D. (peda)


Lesenswert?

Seppel schrieb:
> Das funktioniert nicht so ganz


Richtig ist:
Beitrag "Re: Universelle Tastenabfrage"

von Adrian E. (ahsd)


Lesenswert?

Hallo,

ich bin immer noch dabei mit der Tastenabfrage warm zu werden. Ich 
brauche im Moment nur eine einfache Tastenabfrage, ohne länger Drücken 
usw. Könnt ihr mir erklären, warum bei mir folgende Zeile alleine nicht 
funktioniert?
1
if( get_key_press( 1<<KEY0 ))
2
      LED_PORT ^= 1<<LED0;

Wenn auch folgende Zeile aufgerufen wird, funktioniert es:
1
if( get_key_short( 1<<KEY1 ))
2
      LED_PORT ^= 1<<LED1;

von Peter D. (peda)


Lesenswert?

ändern in:
volatile u8 key_press;        // key press detect

von Adrian E. (ahsd)


Lesenswert?

Peter Dannegger schrieb:
> ändern in:
> volatile u8 key_press;        // key press detect

Danke Peter!!! Die Rettung :)

von Ich (Gast)


Lesenswert?

Da mit der "Komfort-Routine" aus 
http://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_.28nach_Peter_Dannegger.29 
nach Start bzw. Reset sofort ein kurzer Tastendruck erkannt wurde, habe 
ich nochmal den älteren Thread durchforstet und bin zum Thema 
Initialisierung auf diese Posts gestoßen:

Beitrag "Re: Tasten entprellen - Bulletproof"
Beitrag "Re: Tasten entprellen - Bulletproof"
Peter Dannegger schrieb:
> Du hast natürlich recht, CT0 und CT1 müßten mit 0xFF initialisiert
> werden, damit erst nach 4 Durchläufen die Übernahme erfolgt.
>
> Noch besser wäre es, auch key_state mit 0xFF zu initialisieren. Dann
> werden bereits beim Einschalten gedrückte Tasten ignoriert.

Gruß in den Wedding!

von Thomas B. (ewi)


Angehängte Dateien:

Lesenswert?

Ich habe diese geniale Routine zum Tastenentprellen in einer älteren 
Version das erste Mal im Source Code vom OBD2-Analyser NG aus Elektor 
09/2009 gefunden. Dort war sie leider völlig kommentarlos drin und so 
habe ich erst viel später hier den wahren Autor und die Erklärung der 
Funktionsweise entdeckt. Vielen Dank dafür vor allem an Peter Dannegger 
und all die anderen Leute, die Verbesserungen dafür angeregt haben :-)

Inzwischen habe ich mich durch den Source Code des Analysers gewühlt und 
auf der Elektor Labs Seite auch ein paar Firmware Updates dafür 
veröffentlicht. Dabei hab ich mir die Tastenroutine genauer angeschaut 
und zumindest für meinen Fall (AT90CAN128 mit avr-gcc  v4.8.0 und 
Optimierung -s) noch eine Möglichkeit zur Laufzeitverbesserung gefunden 
(siehe Anhang).
Wenn man die volatile Variable keyState zu Beginn der Interruptroutine 
in eine lokale Variable (= sehr wahrscheinlich ein Register) umkopiert, 
dann nur mit der Kopie arbeitet und erst am Ende der Interruptroutine 
wieder in die volatile Variable zurückspeichert, lassen sich in meinem 
Fall zwischen 4-7 Taktzyklen Laufzeit einsparen.
Warum das so ist, steht hier:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Datenaustausch_mit_Interrupt-Routinen
und im englischen Kommentar auch in der angehängten Datei.

von Thomas B. (ewi)


Angehängte Dateien:

Lesenswert?

Nachtrag zu meinem vorigen Beitrag.

Wenn man das Zurückspeichern der lokalen Variable in die volatile 
Variable nicht am Ende der ISR macht, sondern sobald nicht mehr 
schreibend auf die lokale Variable zugegriffen wird (siehe Anhang), 
ermöglicht man dem Compiler/Optimizer ein weiteres MOV (1 Zyklus) 
einzusparen. So dass man mit dieser Änderung dann in meinem Fall 
zwischen 5 bis 8 Taktzyklen Laufzeit einspart.

Soweit die gute Nachricht. Wer aber im Gegensatz zu mir in der ISR nur 
das Tastenentprellen und nichts anderes mehr macht, spart nur 1-4 
Zyklen. Warum? Für die lokale Variable muss ein freies Register zur 
Verfügung stehen. Aufgrund anderer Aktionen in der ISR ist das bei mir 
der Fall. Wie ich inzwischen getestet habe, ist das in der 
Originalroutine nicht der Fall. D.h., dort ist im Prolog der ISR ein 
zusätzliches PUSH (2 Zyklen) nötig, um den aktuellen Registerinhalt für 
das benötigte Register auf den Stack zu speichern. Im Epilog ist dann 
ein zusätzliches POP (2 Zyklen) nötig, um das Register wieder mit dem 
alten Registerinhalt zu laden. Damit ist der Großteil der 
Laufzeitersparnis leider wieder dahin.

Dann noch eine ergänzende Angabe zur möglichen Ersparnis. Wenn ich 
richtig gezählt habe, benötigt die Originalroutine beim AT90CAN128 mit 
avr-gcc v4.8.0 und Optimierung -Os etwa 85-88 Taktzyklen (Minimum 4 
Zyklen zum Einsprung in die ISR + 14 Zyklen Prolog + 50-53 Zyklen 
eigentliche ISR + 17 Zyklen Epilog). Eine Ersparnis von 1-4 Zyklen 
Laufzeit sind also immer weniger als 5%. In meinem Fall sind es maximal 
auch nur wenig mehr als 5%.
Bei anderen Optimierungsstufen ergeben sich vermutlich noch andere 
Ergebnisse. Das muss jeder für sich selbst testen, ob es in seinem 
Anwendungsfall etwas bringt.

von Horst (Gast)


Lesenswert?

Hallo und Danke Peter für die lehrreiche Routine.

Um einen langen Tastendruck zu erkennen muss der jewilige Input ja in 
der REPEAT_MASK eingetragen werden. Sobald jetzt eine Taste davon 
gedrückt wird beginnt rpt mit REPEAT_START abzulaufen (wird die Taste 
wieder losgelassen wird immer mit REPEAT_START rückgesetzt), geschieht 
das wird die jeweilite Taste in key_rpt eingetragen und kann dann 
ausgewertet werden.
Wenn jetzt mehrere Tasten in REPEAT_MASK eingetragen sind kann jede 
einzelne dieser Tasten den "Timer" für key_rpt starten und somit werden 
alle gleichzeitig ausgewertet, sprich ich drücke Taste A lange und B, 
welche später gedrückt wird, wird sofort auch als lang erkannt ohne 
wieder REPEAT_START abzuwarten (beide in der REPEAT_MASK).
Wenn ich dieses Verhalten jetzt aber nicht haben will, sprich jede Taste 
isoliert betrachte, wie müsste man die Sache ähnlich effektiv angehen?
Klar ist das ich für jede Taste einen eigenen Timer brauche der 
unabhängig zu den anderen startet und überläuft.
Kann hier jemand einen Denkanstoß liefern bzw. wurde das gar schon 
irgendwo umgesetzt?

Danke!

von Karl (Gast)


Lesenswert?

würde gerne wenn die LED on ist erst wenn die taste 3 sec lang gedrückt 
wurde in den blink Modus schalten.

short key pressed funktioniert.

doch bei long key pressed passiert nichts.
wie muss ich den code verändern damit es funktioniert?

hab den code an mplabx angepasst. intosc 4mhz pic12f1572


#include <xc.h>
#include <stdbool.h>

//*** PIC12F1572 Configuration Bit Settings
// CONFIG1
#pragma config FOSC = INTOSC    // (INTOSC oscillator; I/O function on 
CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP 
pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection 
(Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out 
Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is 
disabled. I/O or oscillator function on the CLKOUT pin)
//*CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection 
(Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable 
(Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection 
(Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOREN = OFF    // Low Power Brown-out Reset enable bit 
(LPBOR is disabled)
#pragma config LVP = ON         // Low-Voltage Programming Enable 
(Low-voltage programming enabled)

// Pin assignments
#define LED    LATAbits.LATA2  // LED
#define BUTTON PORTAbits.RA4   // BUTTON

//neu
//#include <avr/stdint.h>
//#include <avr/avr/io.h>
//#include <avr/avr/interrupt.h>

//#define KEY_DDR         TRISA   // DDRB
//#define KEY_PORT        LATA    // PORTB
#define KEY_PIN         PORTA  // PINB
#define KEY0            0
#define KEY1            4       // PORTAbits.RA4
#define KEY2            2
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)

#define REPEAT_MASK     (1<<KEY0 | 1<<KEY1 | 1<<KEY2)     // repeat: 
key0, key1, key2
#define REPEAT_START    500                        // after 500 ms
#define REPEAT_NEXT     200                        // every 200 ms

#define LED_DDR         TRISA  // DDRA
#define LED_PORT        LATA   // PORTA
#define LED0            0
#define LED1            2      // LATAbits.LATA2
#define LED2            3

#define word unsigned int

volatile word key_state;                    // debounced and inverted 
key state:
                                            // bit = 1: key pressed
volatile word key_press;                    // key press detect
volatile word key_rpt;                      // key long press and repeat
static word ct0,ct1,rpt;
word i;

// global variables
static word wTMR0_count5 = 0;
static word wTMR0_count3 = 0;
bool boTMRflag5 = false;
bool boTMRflag3 = false;
bool HILF = false;
static word w1ms = 248;         // overflow factor

void interrupt isr(void)
{
    TMR0 = 256 - w1ms;               // increment interrupt count (every 
1000 us)

    ++wTMR0_count5;
    if (wTMR0_count5 == 500)         // if count overflow (every 500 ms)
    {
        wTMR0_count5 = 0;            // reset interrupt 500 ms count
        boTMRflag5 = true;            // set 500 ms Timer Flag
    }

    /*
    ++wTMR0_count3;
    if (wTMR0_count3 == 3000)       // if count overflow (every 3 sec)
    {
        wTMR0_count3 = 0;           // reset interrupt 3 sec count
        boTMRflag3 = true;
    }
    */

    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;
    }

    INTCONbits.TMR0IF = 0;                          // clear interrupt 
flag
}


///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed. Each pressed key is reported
// only once
//
word get_key_press( word key_mask )
{
      di();                        // disable global interrupts
      key_mask &= key_press;       // read key(s)
      key_press ^= key_mask;       // clear key(s)
      ei();                        // enable global interrupts
      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.
//
word get_key_rpt( word key_mask )
{
      di();                        // disable global interrupts
      key_mask &= key_rpt;         // read key(s)
      key_rpt ^= key_mask;         // clear key(s)
      ei();                        // enable global interrupts
      return key_mask;
}

///////////////////////////////////////////////////////////////////
//
// check if a key is pressed right now
//
word get_key_state( word key_mask )
{
      key_mask &= key_state;
      return key_mask;
}

///////////////////////////////////////////////////////////////////
//
word get_key_short( word key_mask )
{
      di();                        // disable global interrupts
      return get_key_press( ~key_state & key_mask );
}

///////////////////////////////////////////////////////////////////
//
word get_key_long( word key_mask )
{
      return get_key_press( get_key_rpt( key_mask ));
}

///////////////////////////////////////////////////////////////////
//
word get_key_long2( word key_mask )
{
      return get_key_press( get_key_rpt( key_mask ));
}

int main(void)
{
    //*** Initialisation
    // configure port
    ANSELA = 0x00;               // set PortA digital
    //LED1 = 1;                    // Set RA2 high  -> LED off
    //TRISA = 0b00011011;          // set RA2 as output and RA4, RA5 as 
input

    LED_PORT = 0b00000100;       // Set RA2 high  -> LED off
    LED_DDR = 0b00011011;        // set RA2 as output and RA4, RA5 as 
input

    /*
    // Configure debouncing routines
    KEY_DDR &= ~ALL_KEYS;                // configure key port for input
    KEY_PORT |= ALL_KEYS;                // and turn on pull up 
resistors

    TCCR0 = (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();
    */

    // configure oscillator
    OSCCONbits.SCS1= 1;          // select Internal oscillator block
    OSCCONbits.IRCF = 0b1101;    // Set Internal Oscillator to 4MHz HF
                                 // -> 1 us / instruction cycle

    // configure Timer0
    OPTION_REGbits.TMR0CS = 0;   // select timer mode
    OPTION_REGbits.PSA = 0;      // assign prescaler to Timer0
    OPTION_REGbits.PS  = 0b001;  // prescaling = 4, 255  1us  4
                                 // -> increment TMR0 every 1024 uss

    // enable interrupts
    INTCONbits.TMR0IE = 1;       // enable Timer0 interrupt
    ei();                        // enable global interrupts

    while(1)
    {

         if( get_key_short( 1<<KEY1 ))
             //LED_PORT ^= 1<<LED1;
             //LED_PORT &= ~(1 << LED1);
             HILF = ~HILF;

         /*
         if( get_key_long2( 1<<KEY1 ))
             //LED_PORT ^= 1<<LED2;
             HILF = 1;
         */

         /*
         // single press and repeat
         if( get_key_press( 1<<KEY1 ) || get_key_rpt( 1<<KEY1 ))
             HILF = 1;
         */

         if(!HILF)
         {
            if(boTMRflag5)
            {
               LED = ~LED;                  // toggle LED
               boTMRflag5 = false;          // clear 500 ms Timer Flag
            }
         }

         if(HILF)
         LED = 0;                          // Set RA2 low  -> LED on

    }

    return 0;
}

von Peter D. (peda)


Lesenswert?

Karl schrieb:
> würde gerne wenn die LED on ist erst wenn die taste 3 sec lang gedrückt
> wurde in den blink Modus schalten.

Sowas macht man am besten als Statemaschine.
Für 3s Wartezeit muß der Timerinterrupt >12ms sein oder man nimmt ein 
uint16_t als Zähler.
1
enum { OFF, ON, BLINK };
2
3
int main()
4
{
5
  uint8_t state = OFF;
6
  for(;;){
7
    switch( state ){
8
      case OFF:
9
                  led_off();
10
                  if( get_key_short( 1<<KEY1 ))
11
                    state = ON;
12
                  if( get_key_long( 1<<KEY1 ))
13
                    state = ON;
14
                  break;
15
      case ON:
16
                  led_on();
17
                  if( get_key_short( 1<<KEY1 ))
18
                    state = OFF;
19
                  if( get_key_long( 1<<KEY1 ))
20
                    state = BLINK;
21
                  break;
22
      case BLINK:
23
                  led_blink();
24
                  if( get_key_short( 1<<KEY1 ))
25
                    state = OFF;
26
                  if( get_key_long( 1<<KEY1 ))
27
                    state = OFF;
28
                  break;
29
    }
30
  }
31
}

von EGS_TI (Gast)


Lesenswert?

Hallo!

Wie müsste man den Code ändern, damit man auf das Loslassen der Taste 
reagiert?
Wäre das so ohne weiteres möglich?

von Uwe (Gast)


Lesenswert?

Hallo EGS_TI,

eine Function dafür ist schon implementiert.

von EGS_TI (Gast)


Lesenswert?

Dann schau ich mir den Code jetzt nochmal an.

von Arwed R. (arwedo)


Lesenswert?

ich benutze den Code für die Abfrage eines Türkontaktes. Dazu frage ich 
mit get_key_long(...) und get_key_release(...) ab. Beim Öffnen mit 
get_key_long(...) klappt das wunderbar: Tür auf, ca. 1s später reagiert 
die Software.
Allerdings reagiert der Release-Aufruf sofort, nicht erst, nachdem -- 
wie bei get_key_long(...) die repeat-Zeit verstrichen ist. Irgendwie 
fehlt mir noch eine Art get_key_release_long(...)-Funktion. Ich habe 
auch schon bissel'rumprobiert, aber das klappte irgendwie nicht. Aber im 
Code drin habe ich keine Funktion gefunden, die das schon machen würde. 
Uwe, welche meintest Du?
Viele Grüße ...

von EGS_TI (Gast)


Lesenswert?

EGS_TI schrieb:
> Hallo!
>
> Wie müsste man den Code ändern, damit man auf das Loslassen der Taste
> reagiert?
> Wäre das so ohne weiteres möglich?

In der original Datei von Peter ist das aber noch nicht implementier, 
oder bin ich blind?

von Uwe S. (de0508)


Lesenswert?

Guten Mittag,

bitte schau mal hier:

Beitrag "Re: Universelle Tastenabfrage"

Ok ?

: Bearbeitet durch User
von Rolf N. (rolfn)


Lesenswert?

Kann mir jemand einen Hinweis geben, wie man Peters Code so erweitern
kann, dass zusätzlich zum langen Drücken einer Taste auch noch das sehr
lange Drücken erkannt wird? (Soll als eine Art "reset" verwendet werden,
also nicht für die normale Bedienung.) Vielen Dank im voraus,

...Rolf

von Peter D. (peda)


Lesenswert?

Rolf Niepraschk schrieb:
> Kann mir jemand einen Hinweis geben

Wo ist Dein Problem?
Niemand hindert Dich daran, das entsprechende Bit in key_state zu testen 
und damit einen Zähler im Timerinterrupt hochlaufen zu lassen.

von Rolf N. (rolfn)


Lesenswert?

Danke. Es funktioniert.

...Rolf

von AnBo (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Sowas macht man am besten als Statemaschine.
> Für 3s Wartezeit muß der Timerinterrupt >12ms sein oder man nimmt ein
> uint16_t als Zähler.
> enum { OFF, ON, BLINK };
>
> int main()
> {
>   uint8_t state = OFF;
>   for(;;){
>     switch( state ){
>       case OFF:
>                   led_off();
>                   if( get_key_short( 1<<KEY1 ))
>                     state = ON;
>                   if( get_key_long( 1<<KEY1 ))
>                     state = ON;
>                   break;
>       case ON:
>                   led_on();
>                   if( get_key_short( 1<<KEY1 ))
>                     state = OFF;
>                   if( get_key_long( 1<<KEY1 ))
>                     state = BLINK;
>                   break;
>       case BLINK:
>                   led_blink();
>                   if( get_key_short( 1<<KEY1 ))
>                     state = OFF;
>                   if( get_key_long( 1<<KEY1 ))
>                     state = OFF;
>                   break;
>     }
>   }
> }

Hallo,
doofe Frage, aber bist du sicher dass das so funktioniert?
Ich habe das meiner Meinung nach genauso, allerdings passiert auf 
Tastendruck nichts. Außerhalb der SM funktioniert es tadellos.
Jemand eine Idee?
Danke!
1
#ifndef F_CPU
2
#define F_CPU 1000000UL
3
#endif
4
5
#define KEY_DDR         DDRA
6
#define KEY_PORT        PORTA
7
#define KEY_PIN         PINA
8
#define KEY0            0
9
#define KEY1            1
10
#define KEY2            2
11
#define KEY3      3
12
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2 | 1<<KEY3)
13
14
#define REPEAT_MASK     (1<<KEY1)       // repeat: key0, key2
15
#define REPEAT_START    50                        // after 500ms
16
#define REPEAT_NEXT     20                        // every 200ms
17
18
#define LED_DDR         DDRA
19
#define LED_PORT        PORTA
20
#define LEDBLUE         4
21
#define LEDYELLOW       5
22
#define LEDRED          6
23
#define LEDGREEN    7
24
25
#include <stdint.h>
26
#include <avr/io.h>
27
#include <stdio.h>
28
#include <util/delay.h>                // for _delay_ms()
29
#include <avr/interrupt.h>
30
#include "lcd.h"
31
32
volatile uint8_t key_state;                                // debounced and inverted key state:
33
                          // bit = 1: key pressed
34
volatile uint8_t key_press;                                // key press detect
35
36
volatile uint8_t key_rpt;                                  // key long press and repeat
37
38
39
ISR( TIMER0_OVF_vect )                            // every 10ms
40
{
41
  static uint8_t ct0, ct1, rpt;
42
  uint8_t i;
43
  
44
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
45
  
46
  i = key_state ^ ~KEY_PIN;                       // key changed ?
47
  ct0 = ~( ct0 & i );                             // reset or count ct0
48
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
49
  i &= ct0 & ct1;                                 // count until roll over ?
50
  key_state ^= i;                                 // then toggle debounced state
51
  key_press |= key_state & i;                     // 0->1: key press detect
52
  
53
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
54
  rpt = REPEAT_START;                          // start delay
55
  if( --rpt == 0 ){
56
    rpt = REPEAT_NEXT;                            // repeat delay
57
    key_rpt |= key_state & REPEAT_MASK;
58
  }
59
}
60
61
///////////////////////////////////////////////////////////////////
62
//
63
// check if a key has been pressed. Each pressed key is reported
64
// only once
65
//
66
uint8_t get_key_press( uint8_t key_mask )
67
{
68
  cli();                                          // read and clear atomic !
69
  key_mask &= key_press;                          // read key(s)
70
  key_press ^= key_mask;                          // clear key(s)
71
  sei();
72
  return key_mask;
73
}
74
75
///////////////////////////////////////////////////////////////////
76
//
77
// check if a key has been pressed long enough such that the
78
// key repeat functionality kicks in. After a small setup delay
79
// the key is reported being pressed in subsequent calls
80
// to this function. This simulates the user repeatedly
81
// pressing and releasing the key.
82
//
83
uint8_t get_key_rpt( uint8_t key_mask )
84
{
85
  cli();                                          // read and clear atomic !
86
  key_mask &= key_rpt;                            // read key(s)
87
  key_rpt ^= key_mask;                            // clear key(s)
88
  sei();
89
  return key_mask;
90
}
91
92
///////////////////////////////////////////////////////////////////
93
//
94
// check if a key is pressed right now
95
//
96
uint8_t get_key_state( uint8_t key_mask )
97
98
{
99
  key_mask &= key_state;
100
  return key_mask;
101
}
102
103
///////////////////////////////////////////////////////////////////
104
//
105
uint8_t get_key_short( uint8_t key_mask )
106
{
107
  cli();                                          // read key state and key press atomic !
108
  return get_key_press( ~key_state & key_mask );
109
}
110
111
///////////////////////////////////////////////////////////////////
112
//
113
uint8_t get_key_long( uint8_t key_mask )
114
{
115
  return get_key_press( get_key_rpt( key_mask ));
116
}
117
118
119
120
int main(void)
121
{  
122
  int displaystate=0;
123
  typedef enum
124
  {
125
    READY=0,
126
    STARTMEASUREMENT
127
  } current_state;
128
129
  current_state state = READY;
130
131
  LED_DDR = 0xFF;
132
133
  // Configure debouncing routines
134
  KEY_DDR &= ~ALL_KEYS;                // configure key port for input
135
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
136
  
137
  TCCR0B = (1<<CS02)|(1<<CS00);         // divide by 1024
138
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
139
  TIMSK0 |= 1<<TOIE0;                   // enable timer interrupt
140
  
141
  sei();
142
  
143
  lcd_init(LCD_DISP_ON);        // Initialize LCD
144
  
145
  while(1){
146
    switch (state)
147
    {
148
      case READY:
149
          if(displaystate==0){
150
            lcd_clrscr();
151
            lcd_puts("Bereit");
152
            displaystate=1;
153
          }
154
          LED_PORT = (1<<LEDGREEN);
155
          if( get_key_short( 1<<KEY3 )){
156
            displaystate=0;
157
            state=STARTMEASUREMENT;
158
          }
159
          if( get_key_long( 1<<KEY3 )){  
160
            displaystate=0;
161
            state=STARTMEASUREMENT;
162
          }
163
          break;
164
      
165
      case STARTMEASUREMENT:
166
          if(displaystate==0){
167
            lcd_clrscr();
168
            lcd_puts("State1");
169
            displaystate=1;
170
          }
171
          LED_PORT = (1<<LEDYELLOW);
172
          if( get_key_short( 1<<KEY2 )){
173
            displaystate=0;
174
            state=READY;
175
          }
176
          if( get_key_long( 1<<KEY2 )){
177
            displaystate=0;
178
            state=READY;
179
          }
180
          break;
181
        }
182
  }
183
}

von Peter D. (peda)


Lesenswert?

AnBo schrieb:
> aber bist du sicher dass das so funktioniert?
> Ich habe das meiner Meinung nach genauso, allerdings passiert auf
> Tastendruck nichts.

Dein Code ist dermaßen verschieden zu meinem Beispiel, es wäre ein 
Wunder, wenn er genauso funktionierte.
Probier doch meinen einfach mal aus.

von AnBo (Gast)


Lesenswert?

Hallo,
ich bin noch mal alles Schritt für Schritt durchgegangen und habe es mit 
deinem Code verglichen. Da ist mir dann letztendlich aufgefallen, dass 
ich die Pullups in einem Schritt wieder ausgeschaltet hatte.

Danke für deine ganzen Mühen und die viele Hilfestellung!

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Peter Dannegger schrieb:
> Probier doch meinen einfach mal aus.

 LOL.

von Mario S. (dachstuhlklaus)


Angehängte Dateien:

Lesenswert?

Guten Tag zusammen.

Ich habe mich die letzen Tage mit dem hier vorgestellten Entprellcode 
auseinandergesetzt und möchte als erstes sagen:

"Danke an Peter Dannegger für diesen tollen code und an alle die ihn 
weiter verbessert haben!"

Zugegeben, ich habe nicht jede Zeile verstanden, aber weiss jetzt wie 
ich ihn anwenden kann. Vielleicht würde ja Zettel und Stift zu besserem 
Verständnis führen ;-)

Als kleines Dankeschön habe ich mir mal die Mühe gemacht alles hier 
zusammengetragene in eine Datei zu packen. Die Nutzung von mehreren 
Eingangsports, für mehr als acht Tasten, habe ich allerdings nicht 
eingebaut.

Da ich programmieren nur mit PICs gelernt habe, kann ich nur die 
Funktion dieses angehängten codes bestätigen (Simuliert mit Proteus). 
Wenn also mal jemand die Version für AVR kontrollieren könnte wäre das 
super. Ich habe es aber nach bestem Gewissen versucht umzusetzen.

Den Simulationsschaltkreis für Proteus habe ich auch angefügt. Also viel 
Spass damit :-)

Schöne Grüsse

Mario

@Peter Dannegger:
Darf ich den Code im CCS Forum posten? Unter Angabe der Quelle 
natürlich.

: Bearbeitet durch User
von Pumba (Gast)


Lesenswert?

Hallo,

bei mir reagieren die Taster nicht ;/
Musste ein paar Anpassungen der Timer für den Attiny841 vornehmen. Die 
ISR wird alle 10ms aufgerufen, dass soweit schon richtig.

Die Leds hab ich direk an PINA 0 - 2 angeschlossen und auf der anderen 
Seite mit einem 470Ohm Widerstand nach GND verbunden.

Die Taster sind direkt mit PINB 0 - 2 verbunden und auf der anderen 
Seite mit GND verbunden.
1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
#define F_CPU 8E6
6
7
#define KEY_DDR         DDRB
8
#define KEY_PORT        PORTB
9
#define KEY_PIN         PINB
10
#define KEY0            0
11
#define KEY1            1
12
#define KEY2            2
13
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
14
15
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
16
#define REPEAT_START    50                        // after 500ms
17
#define REPEAT_NEXT     20                        // every 200ms
18
19
#define LED_DDR         DDRA
20
#define LED_PORT        PORTA
21
#define LED0            0
22
#define LED1            1
23
#define LED2            2
24
25
volatile uint8_t key_state;                                // debounced and inverted key state:
26
// bit = 1: key pressed
27
volatile uint8_t key_press;                                // key press detect
28
29
volatile uint8_t key_rpt;                                  // key long press and repeat
30
31
32
ISR( TIMER0_OVF_vect )                         // every 10ms
33
{
34
  static uint8_t ct0, ct1, rpt;
35
  uint8_t i;
36
  
37
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);
38
39
  i = key_state ^ ~KEY_PIN;                       // key changed ?
40
  ct0 = ~( ct0 & i );                             // reset or count ct0
41
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
42
  i &= ct0 & ct1;                                 // count until roll over ?
43
  key_state ^= i;                                 // then toggle debounced state
44
  key_press |= key_state & i;                     // 0->1: key press detect
45
  
46
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
47
  rpt = REPEAT_START;                          // start delay
48
  if( --rpt == 0 ){
49
    rpt = REPEAT_NEXT;                            // repeat delay
50
    key_rpt |= key_state & REPEAT_MASK;
51
  }
52
53
  static uint8_t ct;
54
  if(++ct == 50) { LED_PORT ^= 1<<LED2; ct= 0;}
55
}
56
57
58
uint8_t get_key_press( uint8_t key_mask )
59
{
60
  cli();                                          // read and clear atomic !
61
  key_mask &= key_press;                          // read key(s)
62
  key_press ^= key_mask;                          // clear key(s)
63
  sei();
64
  return key_mask;
65
}
66
67
uint8_t get_key_rpt( uint8_t key_mask )
68
{
69
  cli();                                          // read and clear atomic !
70
  key_mask &= key_rpt;                            // read key(s)
71
  key_rpt ^= key_mask;                            // clear key(s)
72
  sei();
73
  return key_mask;
74
}
75
76
uint8_t get_key_state( uint8_t key_mask )
77
{
78
  key_mask &= key_state;
79
  return key_mask;
80
}
81
82
uint8_t get_key_short( uint8_t key_mask )
83
{
84
  cli();                                          // read key state and key press atomic !
85
  return get_key_press( ~key_state & key_mask );
86
}
87
88
uint8_t get_key_long( uint8_t key_mask )
89
{
90
  return get_key_press( get_key_rpt( key_mask ));
91
}
92
93
int main( void )
94
{
95
  LED_DDR |= (1<<LED0) | (1<<LED1) | (1<<LED2);
96
97
  // Configure debouncing routines
98
  KEY_DDR &= ~ALL_KEYS;                // configure key port for input
99
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
100
  
101
  TCCR0B = (1<<CS02)|(1<<CS00);         // divide by 1024
102
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
103
  TIMSK0 |= 1<<TOIE0;                   // enable timer interrupt
104
  
105
  sei();
106
  
107
  while(1){
108
    if(get_key_short( 1<<KEY0 ))  // Led2 togglen
109
    LED_PORT ^= 1<<LED0;
110
    
111
    if( get_key_short( 1<<KEY1 ))  // Led1 togglen
112
    LED_PORT ^= 1<<LED1;
113
114
    if( get_key_short( 1<<KEY2 ))  // Leds loeschen
115
    LED_PORT &= ~((1<<LED0) | (1<<LED1));
116
  }
117
}

von Uwe S. (de0508)


Lesenswert?

Nun Pumba?

Der KEY0 fehlt in der Definition.

Vielleicht solltest Du erstmal nur mit get_key_press() arbeiten.

Wenn Du das verstanden hast, kannst Du die zeitabhängigen 
Taster-Funktionen einbauen, konfigurieren und auch nutzen.

Ok ?

von Mathias O. (m-obi)


Lesenswert?

Die Pullups sind beim 841 bei PUE. Steht auch im Datenblatt. Bei mir 
hats damals funktioniert mit dem 841.

: Bearbeitet durch User
von Pumba (Gast)


Lesenswert?

Die key_press Variante macht auch nichts, ausser die Leds direkt auf 
HIGH zu setzen.

von Mathias O. (m-obi)


Lesenswert?

Versuch mal anstatt so
1
key_state ^ ~KEY_PIN
das so zu machen
1
key_state ^ KEY_PIN

von Uwe S. (de0508)


Lesenswert?

Mathias

dein Vorlag ist unsinning, wenn Du nicht weist wie der Code von PeDa 
funktioniert, dann rate bitte nicht.


Mathias O. schrieb:
> key_state ^ ~KEY_PINdas so zu machenkey_state ^ KEY_PIN

von Mathias O. (m-obi)


Lesenswert?

Jo er hat ja die Taster active low. Dachte die sind an VCC 
angeschlossen.

von Pumba (Gast)


Lesenswert?

Das mit der neuen Ansteuerung mit PUEx hatte ich bislang nicht gewusst.

Laut dem Datenblatt werden die Pullups wie folgt gesetzt.

PUEx |= (1<<PUEx1) | (1<<PUEx0);
die einzelnen Bits PUEx0 sind im Atmel Studio nicht per defines gesetzt. 
Spielt aber keine Rolle. Da ich die auch mit 0, 1  etc machen kann. 
Jedoch geht es so wie ich mir gedacht habe nicht.
1
  KEY_DDR &= ~ALL_KEYS;                // configure key port for input
2
  PUEB |= ALL_KEYS;
3
  //KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors

von Mathias O. (m-obi)


Lesenswert?

Mit welchem Wert hast du F_CPU definiert? 2278?

von Pumba (Gast)


Lesenswert?

F_CPU ist 8MHz

von Mathias O. (m-obi)


Lesenswert?

Wo hängt dein Quarz dran? PB0 und PB1 sind doch XTAL.

von Mathias O. (m-obi)


Lesenswert?

Dann schreib mal 8000000UL bei F_CPU

von Pumba (Gast)


Lesenswert?

Der Takt ist hier NICHT das Problem! Das geht. Ob nun "8E6" oder 
"8000000UL" spielt keine Rolle.

Quarz ist keiner im Einsatz. Ich arbeite mit der internen Oscillator. 
Der läuft mit 8MHz

von MikeD (Gast)


Lesenswert?

Das Atomisieren per cli(); sei(); finde ich ein wenig heftig. In meiner 
App läuft parallel ein anderer Interrupt, der alle 256 CPU cycles für 
ein paar Takte  zuschlägt (soft PWM). Der würde regelmäßig und oft 
gekillt.

Es geht ja nur darum, den Tasten - Debouncer für die Dauer der 
Tastenstatus Abfragen lahm zu legen. Andere Interrupts, die die Tastatur 
- Variablen nicht verändern, dürfen ja schadlos laufen ... od'r?

Ich lösche / setze daher selektiv nur das Timer - Interrupt Enable Bit 
des 10ms Timers. Sollte - was sehr unwahrscheinlich, aber immerhin 
möglich ist - genau während dieser Sperre ein Timer Overflow eintreten, 
gibt's in der Tastatur (und nur dort) einen Glitch von 10ms ... das ist 
meiner Meinung nach vertretbar.

von Uwe S. (de0508)


Lesenswert?

Servus,

Wiso ein "Glitch von 10ms" ?

Ich verstehe das Interruptsystem etwas anders:
Wenn der Timerinterrupt wieder aktiviert wurde, was ja nur ein paar µs 
dauert, springt man sofort in Timer Interrupt Routine, wenn das Timer 
Interrupt Flag gesetzt ist.

Klar können auch noch andere Interrupts mit höhere Priorität anstehen, 
die will ich jetzt nicht betrachten.

von MikeD (Gast)


Lesenswert?

@Uwe S

Servus,

wieder mal nachgelesen ... hast recht ... für den Fall, dass der 10ms 
key-Timer während der Atomexplosion überläuft (und das entsprechende 
Timer Interrupt Flag 0 ist) wird der Interrupt gespeichert und nach dem 
erneuten Setzen des Flags ausgeführt (in meinem Fall TIFR2.TOV2)

Liebe Grüsse aus +/- Wien

von Joachim B. (jar)


Lesenswert?

Mario Sachse schrieb:
> "Danke an Peter Dannegger für diesen tollen code und an alle die ihn
> weiter verbessert haben!"

auch ich möchte noch mal Danke sagen für diese Entprellroutine.

Ich nutze sie im AVR, im Arduino

mal zum Tasten entprellen, klappt sogar an einer I2C LCD Schnitte mit 
PCF8574 die ich für 8 Tasten nutze.

Beschreibung,

Timer Interrupt 64µs für IRMP
wenn ein Counter auf 10ms hochgezählt hat wird in die I2C 
Tastaturabfrage gesprungen (100-200µs) dort wird ein Flag gesetzt das es 
ein langer IRQ wird, der Interrupt für IRMP wieder freigegeben und 
solange das Flag gesetzt ist am I2C Tastenabfragen vorbei gesprungen. 
Das Flag wird nur zurückgenommen wenn I2C wieder frei ist, der TWI 
Auftrag erledigt ist.

So entgeht mir kein IRMP Befehl, kein Tastendruck.

Für eine Laser Steuerung wo ausser den regulären 20Hz Impulsen mit 80µs 
Breite zur Lasertriggerung auch leider noch Fremdimpulse kommen unter 
10µs und fremd triggern habe ich nun die IRQ auf 10µs gesetzt wie eine 
Tastaturabfrage und trigger den Laser nur noch wenn ein solider Trigger 
größer 40µs erfolgt. Getestet mit einem Arduino als Pulsgenerator, ich 
kann F und Trigerbreite schön einstellen und sehen wie PeDas 
Entprellroutine wirkt.

: Bearbeitet durch User
von Dave (Gast)


Lesenswert?

Moin,

ich nutze die Entprellroutine auf einem STM32F1 und habe bei einem 
Taster festgestellt, dass er häufiger nicht als gedrückt erkannt wurde.
Die Funktion im IRQ wird alle 10ms aufgerufen und es gab keine atomic 
Blöcke in den Tastenfunktionen.
Nun dachte ich mir, alle IRQs sperren ist etwas zu viel des Guten, es 
geht ja nur um das Auslesen der Tastenpins.
Spricht etwas gegen folgenden Ansatz?
In der debounce.c
1
static volatile uint8_t IRQdoNotReadFlag;
2
3
uint16_t get_button_press(uint16_t button_mask) {
4
  IRQdoNotReadFlag = 1;
5
  button_mask &= button_press;
6
  button_press ^= button_mask;
7
  IRQdoNotReadFlag = 0;
8
  return button_mask;
9
}

in der Funktion die dann im IRQ aufgerufen wird:
1
void call_button_in_IRQ(void)                  // Call this in SysTick-Handler
2
{                                              // Should be called every 10ms
3
  static uint16_t ct0, ct1;
4
  #ifdef USE_KEY_REPEAT
5
    static uint16_t rpt;
6
  #endif
7
  uint16_t i;
8
  uint16_t BUTTON_MASK_NOW = BUTTON_PORT;    // get Stade of the InputDataRegister for the used Port
9
  if (!IRQdoNotReadFlag) {
10
11
    i = button_state ^ BUTTON_MASK_NOW;        // key changed ?
12
    ct0 = ~(ct0 & i);                          // reset or count ct0
13
    ct1 = ct0 ^ (ct1 & i);                     // reset or count ct1
14
    i &= ct0 & ct1;                            // count until roll over ?
15
    button_state ^= i;                         // then toggle debounced state
16
    button_press |= button_state & i;          // 0->1: key press detect
17
    
18
  #ifdef USE_KEY_REPEAT
19
    if ((button_state & REPEAT_MASK) == 0)     // check repeat function
20
      rpt = REPEAT_START;                    // start delay
21
    if (--rpt == 0) {
22
      rpt = REPEAT_NEXT;
23
      button_rpt |= button_state & REPEAT_MASK;
24
    }
25
  #endif
26
  }
27
}

Das scheint schon deutlich besser zu funktionieren, ich habe aber den 
Eindruck, dass gelegentlich "kurze" Tastendrücke doch noch unerkannt 
bleiben. Bin mir aber nicht ganz sicher, ob es nicht evtl. die 
Entprellung ist. Eigentlich müsste aber ein gedrückter Taster deutlich 
länger dauern, als die Entprellzeit.

von Uwe S. (de0508)


Lesenswert?

Hallo,

bei den AVR µC kann man alle Interrupts Sperren und dann wieder 
freigeben, oder nur den zugehörigen Timer-Interrupt.
Die Abarbeitung des Timer-Interrupts wird dann nach seiner Freigabe 
"sofort" nachgeholt. Und geht nicht 'verloren'.

Bei deiner Realisierung in call_button_in_IRQ() geht aber dieser 
Interrupt verloren !
Also hier würde ich mal ansetzen.

von Dave (Gast)


Lesenswert?

Hallo Uwe,

danke für die Antwort. Interessanter Gedanke, das ergibt durchaus Sinn.
Wie macht man das denn bei den STM32? Bei mir sorgt der SysTick für die 
10ms Zeitbasis, aber das wird ja auch noch für andere Dinge benutzt und 
daher möchte ich das nicht aus dem Takt bringen.
Je häufiger ich also Tastenabfragefunktionen benutze, desto häufiger 
würde der SysTick IRQ kurzzeitig gesperrt werden...

Wenn ich über
1
SysTick->CTRL  &= ~SysTick_CTRL_TICKINT_Msk; /* Disable SysTick IRQ */
den SysTick IRQ in den Tastenabfragefunktionen ausschalte und kurz 
danach wieder anschalte, wird er vermutlich nicht nachgeholt.

von Martin L. (schrau_baer)


Lesenswert?

Hallo Forumsmitglieder,

ich habe mich schon versucht an den Codebeispielen für Microchip PIC 
Mikrocontroller aber ich scheitere leider beim Aufruf der Funktionen.

Ich nutze 4 von 8 Pins des PORT A als Eingänge, der Rest sind Ausgänge. 
Compiler ist XC8 in Verbindung mit einem PIC16F628.

Beim Aufruf der Funktion in meinem Main Programm gibt mir der Compiler 
zu verstehen dass er "KEY1" nicht kennt.
1
if ( get_key_short( 1<<KEY1))
2
     i++;

Die Fehlercodes lauten:
1
(361) function declared implicit int
1
(192) undefined identifier "KEY1"

Gerne füge ich noch den Quellcode der "unbounce.c" diesem Post hinzu.

Gruß
Martin

von Peter D. (peda)


Lesenswert?

Dave schrieb:
> Nun dachte ich mir, alle IRQs sperren ist etwas zu viel des Guten, es
> geht ja nur um das Auslesen der Tastenpins.

Wenn Dein IRQ so extrem zeitkritisch ist, daß er nicht mal eine 
Verzögerung um 2 CPU-Befehle verträgt, dann hast Du weitaus größere 
Probleme.

von Martin L. (schrau_baer)


Lesenswert?

So ich nochmal, nachdem ich den Fehler der richtigen Deklaration beheben 
konnte nun eine neue Herausforderung:

Es sind 4 Taster am PIC angeschlossen an RA0, RA1, RA6 und RA7 (alle als 
Input in den TRISA Regsiter gesetzt, der Rest des PortsA ist als Output 
deklariert)). Die Entprellroutine funktioniert auch zu 50% denn nur die 
letzten beiden Taster RB6&7 werden ausgewertet. Ich bin leider etwas 
verunsichert ob ich etwas falsch gemachte habe daher hier dier Code in 
voller Schönheit. Nicht wunder, es handelt sich um einen Microchip 
PIC16F628.

Hier der Inhalt der Headerdatei:
1
#ifndef UNBOUNCE_H
2
#define UNBOUNCE_H
3
4
#include "sw_spi.h"
5
6
#define KEY_PORT        PORTA               // Input register
7
#define KEY0            /*PORTAbits.RA*/1   // KEY 0...3
8
#define KEY1            /*PORTAbits.RA*/0   //
9
#define KEY2            /*PORTAbits.RA*/6   //
10
#define KEY3            /*PORTAbits.RA*/7   //
11
12
                        // Type all keys for repeat and long press function */
13
#define REPEAT_MASK     (1<<KEY0|1<<KEY1|1<<KEY2|1<<KEY3)
14
#define REPEAT_START    50   // After 500ms (start time long press)
15
#define REPEAT_NEXT     50   // Every 200ms (repeat time)
16
#define TIMEOUT              // Define when timout is needed for two
17
                             // keys pressed together
18
19
void interrupt service_interrupt(void);
20
21
uint8_t get_key_press( uint8_t key_mask );
22
23
uint8_t get_key_release(uint8_t key_mask);
24
25
uint8_t get_key_rpt( uint8_t key_mask );
26
27
uint8_t get_key_short( uint8_t key_mask );
28
29
uint8_t get_key_long( uint8_t key_mask );
30
31
uint8_t get_key_long_r( uint8_t key_mask );
32
33
uint8_t get_key_rpt_l( uint8_t key_mask );
34
35
uint8_t get_key_common( uint8_t key_mask );
36
37
#endif

Und hier nachfolgend die Funktionen:
1
#include "unbounce.h"
2
3
volatile uint8_t key_state;                // Debounced and inverted key state:
4
                                           // Bit = 1: key pressed
5
volatile uint8_t key_press;                // Key press detect
6
volatile uint8_t key_release;              // Key release detected
7
volatile uint8_t key_rpt;                  // Key long press and repeat
8
9
//******************************************************************************
10
//                              INTERRUPTS
11
//******************************************************************************
12
//#INT_TIMER0                                     // Timer 0 interrupt
13
void  interrupt service_interrupt(void)
14
{
15
    TMR0=0x0064;                         // 156 x 64µs = 9.984ms (~10ms)
16
                                         // Preset 100 = 256 - 156
17
                                         //Setzt den Zählerstand auf x von 255
18
    static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
19
    uint8_t i;
20
21
    i = key_state ^ ~KEY_PORT;                  // Key changed ? active low key
22
    //i = key_state ^ KEY_PORT;                   // Active high key
23
    ct0 = ~( ct0 & i );                         // Reset or count ct0
24
    ct1 = ct0 ^ (ct1 & i);                      // Reset or count ct1
25
    i &= ct0 & ct1;                             // Count until roll over ?
26
    key_state ^= i;                             // Then toggle debounced state
27
    key_press |= key_state & i;                 // 0->1: key press detect
28
    key_release |= ~key_state & i;              // 1->0: key release detect
29
30
    if( (key_state & REPEAT_MASK) == 0 )        // Check repeat function
31
        rpt = REPEAT_START;                     // Start delay
32
33
    if( --rpt == 0 ){
34
        rpt = REPEAT_NEXT;                      // Repeat delay
35
        key_rpt |= key_state & REPEAT_MASK;     // Active low keys
36
    }
37
    INTCONbits.T0IF=0; //Setzt das Interruptflag nach Zählerüberlauf zurück
38
}
39
40
//******************************************************************************
41
//                              FUNCTIONS
42
//******************************************************************************
43
//______________________________________________________________________________
44
// Button debounce routines
45
uint8_t get_key_press( uint8_t key_mask ){
46
    //disable_interrupts(GLOBAL);
47
    INTCONbits.GIE = 0;
48
    key_mask &= key_press;                      // Read key(s)
49
    key_press ^= key_mask;                      // Clear key(s)
50
    //enable_interrupts(GLOBAL);
51
    INTCONbits.GIE = 1;
52
    return key_mask;
53
}
54
55
uint8_t get_key_release(uint8_t key_mask){
56
    //disable_interrupts(GLOBAL);
57
    INTCONbits.GIE = 0;
58
    key_mask &= key_release;                    // Read key(s)
59
    key_release ^= key_mask;                    // Clear key(s)
60
    //enable_interrupts(GLOBAL);
61
    INTCONbits.GIE = 1;
62
    return key_mask;
63
}
64
65
uint8_t get_key_rpt( uint8_t key_mask ){
66
    //disable_interrupts(GLOBAL);
67
    INTCONbits.GIE = 0;
68
    key_mask &= key_rpt;                        // Read key(s)
69
    key_rpt ^= key_mask;                        // Clear key(s)
70
    //enable_interrupts(GLOBAL);
71
    INTCONbits.GIE = 1;
72
    return key_mask;
73
}
74
75
uint8_t get_key_short( uint8_t key_mask ){
76
    uint8_t i;
77
    //disable_interrupts(GLOBAL);
78
    INTCONbits.GIE = 0;
79
    i = get_key_press( ~key_state & key_mask );
80
    INTCONbits.GIE = 1;
81
    return i;
82
}
83
84
uint8_t get_key_long( uint8_t key_mask ){
85
    return get_key_press( get_key_rpt( key_mask ));
86
}
87
88
uint8_t get_key_long_r( uint8_t key_mask ){           // If repeat function needed
89
    return get_key_press( get_key_rpt( key_press & key_mask ));
90
}
91
92
uint8_t get_key_rpt_l( uint8_t key_mask ){            // If long function needed
93
    return get_key_rpt( ~key_press & key_mask );
94
}
95
96
uint8_t get_key_common( uint8_t key_mask ){
97
  return get_key_press((key_press & key_mask) == key_mask ? key_mask : 0);
98
}
99
;

Irgendwie will der Fehler sich mir nicht offenbaren.

Gruß

: Bearbeitet durch User
von Moritz A. (moritz_a)


Angehängte Dateien:

Lesenswert?

Um das Verhalten der "inneren" Variablen sowie der verschiedenen 
Abfrageroutinen mal genauer zu beobachten sowie damit zu experimentieren 
habe ich die ganze Routine mal in ein PC-taugliches C-Programm 
gewandelt.

Vielleicht kann damit ja noch jemand was anfangen.

von Wuhu W. (wuhuuu2)


Lesenswert?

Hallo,

ich verwendet die original Routine von Peter Dannegger und diese 
funktioniert perfekt.

Bevor ich es für mein Projekt versuche, funktioniert die Routine auch, 
wenn man z.B. 3 PINs als Ausgang schaltet.
Das bedeutet man wertet einen gesamten Port aus, der sowohl Ein als auch 
Ausgänge hat.

Können(und wenn ja welche?) Probleme auftreten?

Liebe Grüße

von Joachim B. (jar)


Lesenswert?

Wuhu W. schrieb:
> funktioniert die Routine auch,
> wenn man z.B. 3 PINs als Ausgang schaltet.

ja

Wuhu W. schrieb:
> Das bedeutet man wertet einen gesamten Port aus, der sowohl Ein als auch
> Ausgänge hat.

aber das Ergebnis wird mit den definierten KEYs (Eingänge) oder bei der 
Abfrage *) verundiert, je nach dem ob high- oder low- aktiv, bei Abfrage 
auf high ver"unden", bei Abfrage auf low negiert ver"unden"

*) ret_key=get_key_press( ALL_KEYS ); // welche Taste(n) will ich 
wissen?
1
// key.h -----------------------
2
3
#ifndef KEY_H
4
#define KEY_H
5
6
uint8_t get_key_press( uint8_t );
7
uint8_t get_key_rpt( uint8_t );
8
uint8_t get_key_short( uint8_t );
9
uint8_t get_key_long( uint8_t );
10
uint8_t test_i2c_key(void);
11
//void timer2_init(void);
12
13
extern volatile uint8_t key_state;  // debounced and inverted key state:
14
                                    // bit = 1: key pressed
15
extern volatile uint8_t key_press;  // key press detect
16
extern volatile uint8_t key_rpt;    // key long press and repeat
17
extern volatile uint8_t ct0, ct1, rpt;
18
extern volatile uint8_t key;
19
20
21
// --------------- KEY --------------- 
22
#if defined(__AVR_ATmega328P__)
23
  // D5 (T1)        PD5
24
  #define KEY_DDR     DDRD
25
  #define KEY_PORT    PORTD
26
  #define KEY_PIN     PIND
27
  #define KEY_UP         5
28
  // D6 (AIN0)      PD6
29
  #define KEY_DOWN       6
30
  // D7 (AIN1)      PD7
31
  #define SET_TASTE      4
32
33
  // D8 (ICP1)      PB0
34
  // D9 (OC1A)      PB1 (PWM)
35
#endif
36
37
#define ALL_KEYS         (1<<KEY_UP | 1<<KEY_DOWN | 1<<SET_TASTE)
38
 
39
#define REPEAT_MASK     (1<<KEY_UP | 1<<KEY_DOWN)
40
#define REPEAT_START    50                        // after 500ms
41
#define REPEAT_NEXT     15                        // every 200ms
42
43
extern volatile uint8_t key_state;  // debounced and inverted key state:
44
                                    // bit = 1: key pressed
45
extern volatile uint8_t key_press;  // key press detect
46
extern volatile uint8_t key_rpt;    // key long press and repeat
47
extern volatile uint8_t ct0, ct1, rpt;
48
49
#endif // #ifndef KEY_H
50
// key.h -----------------------
51
52
// isr -----------------------
53
    ii = key_state ^ ~KEY_PIN;
54
    ct0 = ~( ct0 & ii );                             // reset or count ct0
55
    ct1 = ct0 ^ (ct1 & ii);                          // reset or count ct1
56
    ii &= ct0 & ct1;                                 // count until roll over ?
57
    key_state ^= ii;                                 // then toggle debounced state
58
    key_press |= key_state & ii;                     // 0->1: key press detect
59
               
60
    if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
61
      rpt = REPEAT_START;                          // start delay
62
    if( --rpt == 0 )
63
    {  rpt = REPEAT_NEXT;                            // repeat delay
64
       key_rpt |= key_state & REPEAT_MASK;
65
    }
66
// isr -----------------------
67
68
// loop -----------------------
69
  ret_key=get_key_press( ALL_KEYS );
70
71
  if(ret_key&(1<<SET_TASTE))
72
  { ret_key&=~(1<<SET_TASTE); // ~ Port IN negiert weil low Abfrage
73
// loop Auschnitt -----------------------

von Peter D. (peda)


Lesenswert?

Wuhu W. schrieb:
> Können(und wenn ja welche?) Probleme auftreten?

Die Entprellung ist je Pin unabhängig.
D.h. auch 7 Out-Pins sind dem einen In-Pin völlig wurscht.
Nur die Maske für Repeat-Funktion will nur die aktiven Inputs sehen.

von Christian F (Gast)


Lesenswert?

http://www.compuphase.com/electronics/debouncing.htm

Falls ihr immer noch Schwierigkeiten oder ein Brett vorm Kopf habt, lest 
euch die Erklärung im Link auch mal durch. Etwas kleinschrittiger.
Ich habe etwas gebraucht (also nen guten halben Bleistift) an Hand des 
obigen Codes das Konzept nachzugehen. Man hat, glaub ich, einfach zuerst 
Probleme sich klar zu machen, was Peter wann als logisch '1' und '0' 
speichert (die internen Zustände und aktuellen Ports) und dass das Ding 
halt rückwärts zählt.

von Simpel (Gast)


Lesenswert?

Wenn es nur um das Entprellen geht, reicht es, wenn der Eingang in 
definierten Zeitintervallen (Timer-Int.) abgefragt wird, die größer sind 
als die max. Prellzeit.

Beispiel:
Der Taster wurde gedrückt und prellt noch während der 1. Abfrage.

Möglichkeit 1:
Er wird während des Prellens zufällig als "nicht gedrückt" erfasst. 
Nicht weiter schlimm: Er war vorher nicht gedrückt und wird nun eben für 
eine Intervall-Länge weiterhin als "nicht gedrückt" angesehen. Danach 
hat er ab der 2. Abfrage ausgeprellt und wird fortan statisch als 
"gedrückt" erfasst, solange er gedrückt bleibt.

Möglichkeit 2:
Er wird schon während des Prellens zufällig als "gedrückt" erfasst. 
Prima! Das war ja auch die Absicht beim Drücken. Bei den folgenden 
Abfragen hat er ausgeprellt und wird weiterhin statisch als "gedrückt" 
ausgewertet.

Mehr Aufwand zum reinen Entprellen braucht's nicht.

Alles darüber Hinausgehende dient nicht mehr dem Entprellen des Tasters, 
sondern der prophylaktischen Störunterdrückung von extern eingestreuten 
Spikes oder Bursts auf die mitunter langen Taster-Zuleitungen. Wo diese 
Störquellen ausgeschlossen werden können, reicht die Timer-Entprellung.

von Bastler (Gast)


Lesenswert?

Spart wieviele FLASH-/RAM-Bytes?
Ganz abgesehen von der Implementierungs-/Fehlersuch-/Umbau(weils doch 
mehr Funktion braucht)-Zeit.
Es gibt was, das tut was es soll, wozu also neues erfinden.

von Simpel (Gast)


Lesenswert?

@Bastler

Es ging mir nur um die simple Betrachtung des reinen Entprellens, das 
bzgl. der Komplexität meist 'überkandidelt' interpretiert und "gelöst" 
wird.

Dazu braucht es nicht eine einzige zusätzliche Codezeile. Die 
Eingangsabfrage muss ohnehin gemacht werden. Indem man diese in einen 
zeitlich passenden Timerinterrupt verfrachtet, ist das reine 
"Entprellungsproblem" gelöst, wie ich im vorigen Beitrag gezeigt habe.

Dort wo diese reine Entprell-Funktionalität ausreicht, braucht man keine 
All-inclusive-Routine.

von Karl M. (Gast)


Lesenswert?

Hallo Simpel,

im rein mathematischen Sinn, hast Du leider nichts gezeigt.

Anforderung, ich habe 8 Tastereingänge und möchte verschiedene Events 
von der Entprellroutine erhalten.

1) Wurde eine Taste gedrückt ?
2) Wurde eine Taste losgelassen ?
3) Ist eine Taste immer noch gedrückt (repeat pressed) ?
4) Wurde eine Taste nur kurz gedrückt ?

Bitte schreibe, als Beweis, das Vorgegebene als Code deine bevorzugten 
Programmiersprache nieder.

von Moby A. (moby-project) Benutzerseite


Lesenswert?

Karl M. schrieb:
> Bitte schreibe, als Beweis

Was soll er beweisen? Denn

Simpel schrieb:
> Es ging mir nur um die simple Betrachtung des reinen Entprellens

Mit seiner entsprechenden (simplen) Problemlösung hat er ganz recht, da 
brauchts doch nicht mehr (erwähnte fehlende Störeinstreuungen 
vorausgesetzt).

: Bearbeitet durch User
von T.G. (Gast)


Lesenswert?

Hallo,
ich habe die Entprellung bei mir mit einem PIC implementiert, und sie 
funktioniert auch einwandfrei.

Jetzt habe ich aber ein Problem weil ich einen zweiten Timer habe der 
alle 148 us aufgerufen wird.
Mein Timer für den Entpreller ist auf 10ms gestellt.

der zweite Timer hat eine höhere Priorität, aber selbst ohne die bringt 
er die Routine anscheinend so durcheinander das Tasten als gedrückt 
erkannt werden obwohl diese nicht gedrückt werden. Oder erst nach 
mehrmaligem schnellen drücken erkannt werden.

Kann das an dem schnellen Timer liegen oder habe ich doch irgendwo in 
meinem Code mist gebaut? Sobald ich den Timer deaktiviere oder dessen 
Interrupt funktioniert alles.

von T.G. (Gast)


Lesenswert?

Ich kann meine Frage selber beantworten...

Zitat datasheet:
If both low-priority and high-priority interrupts are
enabled, the Stack registers cannot be used reliably to
return from low-priority interrupts. If a high-priority
interrupt occurs while servicing a low-priority interrupt,
the Stack register values stored by the low-priority
interrupt will be overwritten. In these cases, users must
save the key registers in software during a low-priority
interrupt.

Normalerweise erledigt das ja der Compiler.
Natürlich unter der Voraussetzung das man richtig programmiert.
Auszug aus dem C Compiler Handbuch:
A high-priority interrupt uses the shadow registers to save and restore 
the minimal context, while a low-priority interrupt uses the software 
stack to save and restore the minimal context
1
#pragma code
2
#pragma interrupt high_prior_InterruptHandler
3
4
void high_prior_InterruptHandler(void)
5
{}
6
7
#pragma code
8
#pragma interrupt low_prior_InterruptHandler
9
void low_prior_InterruptHandler(void)
10
{}

Keine Ahnung ob das aus einem Beispiel kopiert wurde oder wo ich das her 
hatte, das Projekt liegt schon etwas länger in der Ecke.
Wenn aber für beide Interrupts das gleiche Register zum speichern 
verwendet wird, tritt denke ich genau das ein was oben beschrieben 
wurde.
1
#pragma code
2
#pragma interruptlow low_prior_InterruptHandler
3
void low_prior_InterruptHandler(void)
4
{}
Das ist also korrekt. Und jetzt funktioniert es auch :)

von AVR-Bastler (Gast)


Lesenswert?

Hallo Peter!

Deine Entprell-Routine nutze ich schon seit ein paar Jahren - vielen 
Dank dafür!

Nun wollte ich die get_key_long() Funktion anwenden (zur Einstellung 
eines Zahlenwertes: kurzer Druck = +1, langer Druck = +10).

Ergebnis: Ein langer Tastendruck wird zuverlässig erkannt. Bei einem 
kurzen gibt es leider nur eine Erfolgsquote von ca. 90%, d.h. etwa jeder 
10te wird falsch als langer Tastendruck erkannt.
Etwas verwirrt habe ich einen Attiny13A nur mit der Entprellroutine 
gefüttert, ein Knopf, zwei LEDs, 9,6MHz/8, verschiedene Werte für 
REPEAT_START und  REPEAT_NEXT, aber dasselbe Problem.
Hat jemand schon einmal Åhnliches beobachtet?


Außerdem ist mir folgendes aufgefallen:
Ich hatte den Sinn von REPEAT_NEXT so verstanden, dass ein weiterer 
langer Druck gezählt wird, wenn der Knopf nach Ablauf dieses 
Zeitintervalls immer noch gedrückt ist.

Nun wird in der get_key_long() aber hier:
1
return get_key_press( get_key_rpt( key_mask ));
key_press durch den Aufruf von get_key_press() gelöscht, und jeder 
folgende Aufruf von get_key_long() liefert 0, auch wenn in key_rpt das 
entsprechende Bit gesetzt ist.
Mit
1
return (key_press & get_key_rpt( key_mask ));
wird es etwas besser, aber dann gibt's nach jedem langen Tastendruck 
einen kurzen "gratis", was wohl auch nicht der "Spezifikation" 
entspricht...
Mir fällt aber nix ein, wie man dies ohne Einführung einer weiteren 
Variable lösen könnte.

von chris (Gast)


Lesenswert?

Folgender Code funktioniert bei mir einwandfrei:


1
static uint8_t lcd_update = 0;
2
static char buffer[20];
3
static uint32_t counter = 0;
4
5
      // decrement
6
      if (get_key_press(1<<KEY_DEC))
7
      {
8
        if (counter > 0)
9
        {
10
          counter--;
11
          lcd_update = 1;
12
        }
13
      }
14
      if (get_key_rpt(1<<KEY_DEC))
15
      {
16
        if (counter >= 10)
17
        {
18
          counter -= 10;
19
        }
20
        else
21
        {
22
          counter = 0;
23
        }
24
        
25
        lcd_update = 1;
26
      }
27
28
29
      if (lcd_update)
30
      {
31
        lcd_update = 0;
32
        lcd_setcursor(0,1);
33
        sprintf(buffer, "%16" PRIu32 "", counter);
34
        lcd_string(buffer);
35
      }


lg
Chris

von AVR-Bastler (Gast)


Lesenswert?

Hallo Chris,

OK, auch da wird natürlich ein langer Druck zusätzlich als kurzer 
gezählt - allerdings gleich am Anfang und nicht erst beim Loslassen.
Das dürfte für den User besser einzuschätzen und komfortabler sein.

Hast Du schon einmal das Problem mit "falsch-langen" gehabt (also einem 
kurzen Druck, der als langer erkannt wurde?)

Mit welchem Takt arbeitest Du? Wieviele Interrupts generierst Du?

In meiner "richtigen" Anwendung habe ich F_CPU = 1MHz. Ich brauche einen 
Millisekunden-Takt, den ich mit TIM0_COMPA generiere.
Bei jedem 10. wird dann per
1
 if (!(msec % 10))
2
  {
3
  debounce();
4
...
die Abfrage der Taster vorgenommen.
Der Tiny13A zum Testen taktet geringfügig höher (9,6/8MHz).

von chris (Gast)


Lesenswert?

Hi,

genau, der erste Tastendruck erhöht um 1 (bzw. erniedrigt in meinem 
Beispiel). Hält man weiter gedrückt, wird in 10er Schritten gezählt. 
Beim Loslassen passiert nichts.

Ich führe die Tastenentprellung im Timerinterrupt alle 10 ms durch.
Wenn ich auch einen ms-Takt brauche, stelle ich den Interrupt auf 1ms 
und zähle im Interrupt eine Variable bis 10 hoch. Die Entprellfunktion 
rufe ich direkt im Interrupt (als inline Funktion) auf.

von Peter D. (peda)


Lesenswert?

AVR-Bastler schrieb im Beitrag #4471676:
> Ergebnis: Ein langer Tastendruck wird zuverlässig erkannt. Bei einem
> kurzen gibt es leider nur eine Erfolgsquote von ca. 90%, d.h. etwa jeder
> 10te wird falsch als langer Tastendruck erkannt.

Dann ist die Zeit für lang zu lurz.

AVR-Bastler schrieb im Beitrag #4471676:
> Ich hatte den Sinn von REPEAT_NEXT so verstanden, dass ein weiterer
> langer Druck gezählt wird, wenn der Knopf nach Ablauf dieses
> Zeitintervalls immer noch gedrückt ist.

Das wird kompliziert, wenn man alle 3 Sachen machen will (kurz, lang, 
repepeat), nicht nur für den Bediener.

Beitrag "Re: Universelle Tastenabfrage"

Beitrag "Re: Universelle Tastenabfrage"

von chris (Gast)


Lesenswert?

AVR-Bastler schrieb im Beitrag #4472189:
> if (!(msec % 10))
>   {
>   debounce();
> ...


Da fällt mir gerade auf:
Wenn dieser Code bei dir in der Hauptschleife steht, kann er innerhalb 
einer Millisekunde öfter aufgerufen werden, wenn deine Schleifenlaufzeit 
kleiner als 1 ms ist. Weil die geprüfte Bedingung ja die ganze Zeit 
erfüllt ist, bis msec erhöht wird.
Das würde das falsch-erkennen der langen/kurzen Tastendrücke erklären.
Mach vielleicht besser einen Zähler, der bis 10 zählt und wieder auf 0 
gesetzt wird. Dann wird wirklich nur jedes 10. mal die Entprellung 
durchgeführt.

von AVR-Bastler (Gast)


Lesenswert?

chris schrieb:
> Da fällt mir gerade auf:
> Wenn dieser Code bei dir in der Hauptschleife steht, kann er innerhalb
> einer Millisekunde öfter aufgerufen werden,

Das ist ein valider Punkt. - Danke!
Ich dachte schon, das war's. Leider nein.

Aber ich habe nun mit den Zeiten noch etwas gespielt.
Wenn ich die debounce() alle 50ms aufrufe, funktioniert es. Dafür fallen 
dann sehr schnelle Tastendrücke hinten runter.

Also kann ich mir aussuchen, ob ich am langen oder am kurzen (Zeit-) 
Ende Fehler habe.
Ich denke, der Taster ist einfach schrottig.

von chris (Gast)


Lesenswert?

AVR-Bastler schrieb im Beitrag #4473359:
> Das ist ein valider Punkt. - Danke!
> Ich dachte schon, das war's. Leider nein.

Wie sieht denn dein aktueller (vollständiger) Code aus?

von AVR-Bastler (Gast)


Lesenswert?

Hallo Peter,

Peter D. schrieb:
> Dann ist die Zeit für lang zu lurz.

Ich habe die REPEAT_START schon auf 255.

Aber danke für die get_key_long_r() und get_key_rpt_l(). Ich werde die 
morgen mal simulieren.

von Peter D. (peda)


Lesenswert?

AVR-Bastler schrieb im Beitrag #4473359:
> Wenn ich die debounce() alle 50ms aufrufe, funktioniert es.

Das glaub ich Dir nicht.

50ms * 255 = 12,75s.
Da mußt Du ja auf der Taste einschlafen für Langdruck.

Also entweder Deine Zeitbasis stimmt nicht, Deine 50ms stimmen nicht 
oder Du hast in der Repeat-Maske Pins drin, die garnicht für Langdruck 
vorgesehen sind.

von AVR-Bastler (Gast)


Lesenswert?

Sorry - hab gerade "Land unter", da bin ich noch nicht wieder dazu 
gekommen.

Deine Rechnung ist natürlich richtig, Peter.
Deine Annahme, ich würde bei der Fehlersuche wahllos alle Parameter 
gleichzeitig ändern, natürlich nicht.

Aber auch 255*10ms = 2,55s ist für einen Anwender zu lang. Trotzdem habe 
ich es damit mal versucht, einfach um herauszufinden, ob dieser 
Parameter das Problem löst. Hat er nicht.

von Johannes (menschenskind)


Lesenswert?

Hi Peter,

Ich habe Deinen Code jetzt integriert.
Woran ich aber gerade grüble ist, dass das "get_key_long" bereits nach 
sehr kurzem "Lang-Drücken" des Tasters registriert wird.

Ich habe in den Defines diesen Wert von 50 auf 100 geändert "#define 
REPEAT_START    100 "
Da sich das Verhalten aber nicht ändert, nehme ich also an, dass ich an 
der falschen Stelle gedreht habe.
Mein Ziel ist es, ein Lang-Drücken von etwa 2 Sekunden zu detektieren.

Danke im Voraus
Hannes

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Johannes H. schrieb:
> Da sich das Verhalten aber nicht ändert, nehme ich also an, dass ich an
> der falschen Stelle gedreht habe.

Du hast hier verschwiegen, dass Du noch woanders dran geschraubt hast, 
wie Du in folgendem Thread schreibst.

Johannes H. schrieb:
> Ebenso habe ich den Vorteilquotienten von 1024 auf 1600 gesetzt da ich
> mit einem höheren Takt als in Deinem Beispiel arbeite.

1600? Zeig mal den Code, wie Du das gemacht hast.

von Johannes (menschenskind)


Lesenswert?

Ja ertappt :)

Also es geht um diese Zeile:
1
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
Hab mir ausgerechnet, welcher Wert für 16MHz da ungefähr stehen muss, um 
10ms zu erhalten-->1600

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Johannes H. schrieb:
> Hab mir ausgerechnet, welcher Wert für 16MHz da ungefähr stehen muss, um
> 10ms zu erhalten-->1600

Dann schau Dir mal TCCR0 im Datenblatt und im Code an! Das wird auf den 
Teiler 1024 gesetzt. Diesen setzt Peter dann für TCNT0 wieder ein.

Du kannst da nicht einfach 1600 hinschreiben. Den Teiler 1600 kann man 
(naturgemäß) gar nicht konfigurieren in TCCR0. Im Datenblatt stehen die 
gültigen Teiler. Wenn, dann solltest Du besser den Faktor 10e-3 
korrigieren. Mit welcher Frequenz läuft Peters Code? 1MHz oder 8MHz? 
Dann überlege, wie Du ihn für 16MHz modifizieren musst.

Im anderen Thread hatte Dich auch schon jemand darauf hingewiesen... 
aber offenbar hast Du das nicht verstanden ;-)

Es kommt bei Peters Code gar nicht so groß auf die Zeiten an. Sie 
sollten jedoch zumindest größenordnungsmäßig passen. Das reicht in den 
meisten Fällen.

: Bearbeitet durch Moderator
von Johannes (menschenskind)


Lesenswert?

Ja den Vorteiler nicht, aber den Zählwert im TCNT-Register kann man doch 
beliebig setzen.

Peters Code nutzt 1MHz, da habe ich mich um Faktor 10 verguckt.
Also wird bei meinen 16MHz der Interrupt 16 Mal häufiger aufgerufen 
(wenn ich den Code so lasse), oder?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Johannes H. schrieb:
> Peters Code nutzt 1MHz, da habe ich mich um Faktor 10 verguckt.
> Also wird bei meinen 16MHz der Interrupt 16 Mal häufiger aufgerufen
> (wenn ich den Code so lasse), oder?

Peter hat das portabel formuliert für TCNT0: Er benutzt F_CPU. Wenn 
dieser bei Dir auf 16000000UL steht, dann ist alles bereits richtig. Der 
Wert

  (F_CPU / 1024 * 10e-3 + 0.5)

wird dann automatisch 16 mal größer.

Du brauchst also in der Regel nichts mehr anzupassen.

: Bearbeitet durch Moderator
von Johannes (menschenskind)


Lesenswert?

Ok, ja seh ich ein, da bin ich einem Denkfehler aufgesessen...

Aber das ursprüngliche Problem dass "get_key_long" nach einer viel zu 
kurzen Zeit auslöst, besteht immer noch.
Hast Du da noch eine Idee?

von Karl M. (Gast)


Lesenswert?

Johannes H.,

bei mir funktioniert das alles, es liegt an deinem Programm.
Wo ist das ?

von Johannes (menschenskind)


Lesenswert?

Hallo,

Da ist der Code:
1
#include "includes/config.h"
2
#include "includes/debounce.h"
3
#include "includes/light_ws2812.h"
4
5
#include <util/delay.h>
6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <avr/sleep.h>
9
10
struct cRGB LED_WHEEL_16[16];
11
struct cRGB COLORS [8];
12
13
#define LEDS 16
14
15
uint8_t value_max = 10;
16
int8_t led_index = 0;
17
int8_t led_dark_step = 2;
18
19
float brightness_red=0.1, brightness_green=0.1, brightness_blue=0.1;
20
21
void dark(uint8_t led_index, uint8_t led_dark_step){
22
  if (led_index - led_dark_step < 0) led_index = LEDS - abs(led_index - led_dark_step);
23
  else led_index -= led_dark_step;
24
  
25
  LED_WHEEL_16[led_index].r=0;
26
  LED_WHEEL_16[led_index].g=0;
27
  LED_WHEEL_16[led_index].b=0;  
28
}
29
30
int main(void){
31
  LED_INIT();  // Set LED pins 
32
  
33
  /**** Set Timer for Debounce Functionality ****/
34
  TCCR0 = (1<<CS02)|(1<<CS00);         // divide by 1024
35
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1600 * 10e-3 + 0.5);  // preload for 10ms
36
  TIMSK |= 1<<TOIE0;                   // enable timer interrupt
37
  
38
  sei();    // Enable global interrupts
39
40
  while(1){  
41
        
42
    if( get_key_short( 1<<KEY2 ))
43
      LED_EVENT(LED2,1);  
44
    if (get_key_long(1<<KEY2))  
45
      LED_EVENT(LED2,5);  
46
      
47
    LED_WHEEL_16[led_index].r=value_max;LED_WHEEL_16[led_index].g=00;LED_WHEEL_16[led_index].b=value_max;// blue
48
    ws2812_setleds(LED_WHEEL_16,LEDS);
49
    _delay_ms(200);
50
    dark(led_index, led_dark_step);
51
    ws2812_setleds(LED_WHEEL_16,LEDS);
52
    
53
    led_index++;
54
    if(led_index == 16){
55
      led_index = 0;
56
    }    
57
  }
58
}

Den ganzen Debounce-Code habe ich eine separate c-Datei ausgelagert.

Karl, wie stellst Du denn ein, nach welcher Zeit "get_key_long" auslöst?

von Jörg (Gast)


Lesenswert?

Johannes H. schrieb:
> wie stellst Du denn ein, nach welcher Zeit "get_key_long" auslöst?

Dort wo Du Repeat_Start definiert hast.

von M. K. (kichi)


Angehängte Dateien:

Lesenswert?

Ich habe mal versucht alle Funktionalitäten aus dem Thread zusammen zu 
tragen und in ein möglichst wiederverwertbares Modul zu packen. 
Vielleicht kann es jemand brauchen.

Die verwendete cm_atomic.h ist unter folgendem Link zu finden: 
Beitrag "Re: atomic-lib für stm32"

von Karl M. (Gast)


Lesenswert?

Hallo Michael !

und hier ist schon der erste Fehler !
1
#define  REPEAT_START         50
2
#define  REPEAT_NEXT          20

Dieser Wert ist direkt abgängig von dem Pollintervall !

von M. K. (kichi)


Lesenswert?

Dass die Werte vom Pollintervall abhängen ist klar, aber warum soll das 
dann gleich falsch sein?

Mag ja sein, dass du recht hast, verstehen tue ich es aber nicht. 
Zugegebenermaßen habe ich die Repeat-Geschichten aber auch noch nie 
genutzt, sondern einfach so übernommen. Daher ist es sehr 
wahrscheinlich, dass ich den entsprechenden Beitrag nicht gelesen habe.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Lieber Herr Dannegger, Vielen Dank!
Der Code arbeitet sofort und perfekt!
Ich sollte nur am Anfang für mehr Freiheit mit Pins für die Tasten etwas 
anpassen:
1
void knopf_abfrage(void){
2
3
  static unsigned char ct0 = 0xFF, ct1 = 0xFF, rpt;
4
    unsigned char i, key_pin;
5
6
  key_pin = 0xff;
7
  if(!T_U) key_pin &= ~(1<<KEY_U);
8
  if(!T_O) key_pin &= ~(1<<KEY_O);
9
  if(!T_M) key_pin &= ~(1<<KEY_M);
10
  if(!T_SP) key_pin &= ~(1<<KEY_SP);
11
  if(!T_START) key_pin &= ~(1<<KEY_START);
12
13
  i = key_state ^ ~key_pin;     // key changed ?
usw.

mit entsprechenden
1
#define PIN_KN_U PINB
2
#define KN_U PB3
3
#define T_U (PIN_KN_U & (1<<KN_U))
4
#define KEY_U 4 
5
6
#define PIN_KN_O PINB 
7
#define KN_O PB4
8
#define T_O (PIN_KN_O & (1<<KN_O))
9
#define KEY_O 3  
10
11
#define PIN_KN_M PINC
12
#define KN_M PC0
13
#define T_M (PIN_KN_M & (1<<KN_M))
14
#define KEY_M 2
15
16
#define PIN_KN_SP PINC
17
#define KN_SP PC1
18
#define T_SP (PIN_KN_SP & (1<<KN_SP))
19
#define KEY_SP 1 
20
21
#define PIN_KN_START PINB
22
#define KN_START PB2
23
#define T_START (PIN_KN_START & (1<<KN_START))
24
#define KEY_START 0

Auch REPEAT_NEXT habe ich auf 10 gesetzt, aber das ist wohl eine 
Geschmackssache.

Vielen Dank für diese Möglichkeit!

: Bearbeitet durch User
von Henning (Gast)


Lesenswert?

Hallo,

ich würde meine C Kenntnisse als sehr bescheiden bezeichnen. Dennoch 
versuche Ich gerade den Code aus 
Beitrag "Universelle Tastenabfrage" zu verstehen.

Ich verstehe den Sinn folgender Anweisung nicht ...

ct0 = ~( ct0 & i );

Unmittelbar zuvor wird der Wert von ct0 initialisiert.

static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;

Wenn nun ct0 mit 0xFF initialisiert wurde und ein bitweises und mit i 
erfolgt, dann kann das Ergebnis doch immer nur gleich dem Wert von i 
sein. Oder?

Demzufolge wäre doch ct0 ~= i; doch viel kürzer und einfacher zu 
verstehen.


Wo liegt mein Denkfehler?

Gruß
Henning

von Karl M. (Gast)


Lesenswert?

Guten Mittag,

in der Deklaration der Variable und ihre "Reichweite".
1
static uint8_t ct0;

von Henning (Gast)


Lesenswert?

Hallo,

was möchtest du mir damit sagen?
Input please ...


Im Beitrag zur Entprellung steht nichts von
static uint8_t ct0;

sondern
static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;

Bedeutet das, das der Wiki Beitrag, der den Anfängern ja erklären soll 
wie es funktioniert, fehlerhaft ist?

Dann sollte das umgehend von einem "Wissenden" korrgiert werden...

Gruß
Henning

von STK500-Besitzer (Gast)


Lesenswert?

Henning schrieb:
> Hallo,
>
> was möchtest du mir damit sagen?
> Input please ...
>
> Im Beitrag zur Entprellung steht nichts von
> static uint8_t ct0;
>
> sondern
> static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
>
> Bedeutet das, das der Wiki Beitrag, der den Anfängern ja erklären soll
> wie es funktioniert, fehlerhaft ist?

Grundlagen!
Das "static" sorgt dafür, dass die Variable nur ein Mal initialisiert 
wird, und nach dem Verlassen der Funktion aber immer noch besteht 
(ähnlich einer globalen Variable, nur dass der Zugriff auf diese nur 
innerhalb dieser Funktion erfolgen kann.
Würde es fehlen, würde die Variable bei jedem Funktionsaufruf neu 
initialisiert werden.
Ob da nur eine defklariert wird, oder fünf, ist dabei egal.

von Bernd N (Gast)


Lesenswert?

Die Antwort von
Karl war schon richtig aber du hast nicht darüber nachgedacht.

https://de.wikibooks.org/wiki/C-Programmierung:_static_%26_Co.

>> Unmittelbar zuvor wird der Wert von ct0 initialisiert.
Deine Annahme stimmt nur für das erste Mal.

von Henning (Gast)


Lesenswert?

Ahhh,

jetzt hab ich's.

Habe aus Karl's erster Antwort einfach nicht entnehmen können, das es um 
das Wörtchen "static" in der Variablendeklaration geht...

Jetzt ist mir natürlich klar, das meine Schlussfolgerungen Unsinn waren.

Vielen Dank für die Erleuchtung.
Henning

von Johannes (menschenskind)


Lesenswert?

Hallo Peter & Andere,

Ich bin bei der Anwendung Der Entprellroutine in meiner Anwendung auf 
ein Problem gestoßen:
Mittels "Kurz Drücken" eines Tasters werden verschiedene Modi 
durchgeschaltet, sprich ein index inkrementiert.
Mittels "Lang Drücken" desselben Tasters wird der µC mit Deiner 
"try_sleep"-Funktion schlafen gelegt und mittels Level Change Interrupt 
wieder aufgeweckt.

Jetzt habe ich das Problem, dass beim Aufwecken auch gleichzeitig ein 
"Kurz Drücken" detektiert wird und damit der nächste Modus ausgewählt 
wird, was nicht erwünscht ist.

Leider habe keine Möglichkeit gefunden, während der Aufwachphase, das 
"get_key_short" temporär zu deaktivieren. Siehst Du/ihr eine 
Möglichkeit?

Danke & Gruß

von Edson (Gast)


Lesenswert?

Johannes H. schrieb im Beitrag #4892788
> Leider habe keine Möglichkeit gefunden, während der Aufwachphase, das
> "get_key_short" temporär zu deaktivieren. Siehst Du/ihr eine
> Möglichkeit?

Dann mach das doch in der Einschlaf-Phase. Also deinen Index um 1 
verringern, so dass er nach dem Aufwecken und dem "get_key_short" wieder 
stimmt.

von Johannes (menschenskind)


Lesenswert?

Hi Edson,
Ja das funktioniert, dass ich darauf nicht gekommen bin :)
Aber ich suche noch nach der eleganten Lösung. Den Index zu verringern 
ist ja nur ne Art Workaround.

Hast Du schon mit Peters Debounce Algorithmus gearbeitet?

von Johannes (menschenskind)


Lesenswert?

Hi, bin jetzt mit dem Workaround an eine weitere Grenze gestoßen und 
muss nun eine andere Lösung finden.
Ich hatte gerade probiert, beim Einschlafvorgang einfach den Timer 0 
Interrupt zu deaktivieren und eine kurze Zeit nach dem Aufwachen wieder 
zu aktivieren, da hängt sich das Programm aber irgendwo auf :(

Peter/Edson, was für Optionen fallen euch denn noch ein?

von Peter D. (peda)


Lesenswert?

Johannes H. schrieb:
> Leider habe keine Möglichkeit gefunden, während der Aufwachphase, das
> "get_key_short" temporär zu deaktivieren. Siehst Du/ihr eine
> Möglichkeit?

https://www.mikrocontroller.net/attachment/115266/Knightrider2.zip
1
    if( stop ){
2
      try_sleep();
3
      get_key_short( 1<<KEY0 );      // ignore short press during stop

von Johannes (menschenskind)


Lesenswert?

Hallo Peter,

Nein das funktioniert leider nicht :(
Es wird weiterhin nach dem Aufwachen das kurze Drücken detektiert.

von Mathias W. (mathias_w)


Lesenswert?

Hallo!

Horst hatte ja schon mal in seinem Beitrag 
Beitrag "Re: Universelle Tastenabfrage" angefragt, wie man 
die Repeat-Funktion nicht nur für eine Taste, sondern für alle Tasten 
unabhängig hinbekommt. Ein Szenario dafür ist z. B. ein Tastmodul mit 
acht Eingängen, an die Taster in acht verschiedenen Räumen angeschlossen 
sind.

Ich dachte mir, dass man dafür einfach einen weiteren vertikalen Zähler 
verwenden könnte, dann allerdings z. B. 3-bit oder sogar noch breiter.

Den Code stelle ich mir so vor:
1
volatile uint8_t key_state;                                // debounced and inverted key state:
2
                                                  // bit = 1: key pressed
3
volatile uint8_t key_press;                                // key press detect
4
 
5
volatile uint8_t key_rpt;                                  // key long press and repeat
6
7
ISR( TIMER0_OVF_vect )                            // every 10ms
8
{
9
  static uint8_t ct0 = 0xFF, ct1 = 0xFF;
10
  static uint8_t rpt_ct0 = 0xFF, rpt_ct1 = 0xFF, rpt_ct2 = 0xFF;
11
  uint8_t i;
12
 
13
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
14
 
15
  i = key_state ^ ~KEY_PIN;                       // key changed ?
16
  ct0 = ~( ct0 & i );                             // reset or count ct0
17
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
18
  i &= ct0 & ct1;                                 // count until roll over ?
19
  key_state ^= i;                                 // then toggle debounced state
20
  key_press |= key_state & i;                     // 0->1: key press detect
21
  key_release |= ~key_state & i;                  // 1->0: key release detect
22
23
  i = key_state
24
  rpt_ct0 = ~( rpt_ct0 & i );                     // reset or count rpt_ct0
25
  rpt_ct1 = rpt_ct0 ^ (rpt_ct1 & i);              // reset or count rpt_ct1
26
  rpt_ct2 = (rpt_ct0 & rpt_ct1) ^ (rpt_ct2 & i);  // reset or count rpt_ct2
27
  i &= rpt_ct0 & rpt_ct1 & rpt_ct2;               // count until roll over ?
28
  key_rpt |= key_state & i;                       // then repeat key
29
30
}

Die restlichen Funktionen sind identisch wie von Peter geposted.

Nur leider funktioniert das bei mir so nicht. Einige Tasten werden 
sofort bei get_key_short erkannt, andere sofort bei get_key_long. 
Zwischendurch ändert sich das mal. Keine der Tasten ändert beim länger 
drücken ihren Repeat-Status.

Wie müsste die Timer-Routine richtig aussehen und wie wäre die korrekte 
Erweiterung für einen 4- bzw. 5-bit vertikalen Counter?

von Mathias W. (mathias_w)


Lesenswert?

Ich muss mich korrigieren. Der von mir gepostete Code funktioniert 
genauso, wie gewünscht. Problem war nur, dass ich ihn gleich in 
Assembler implementiert und sich da ein Fehler eingeschlichen hatte. Im 
Beitrag hatte ich für mich als Kontrolle die C-Version geschrieben.

Ein weiteres Problem war das Timing. Ein 3-bit vertical counter ist doch 
bisl arg wenig. Nun schwanke ich zwischen eine 4-bit oder 5-bit vertical 
counter. Der 4-bit gefällt mir momentan am besten.

Das Schema zur Erweiterung um weitere Bits ist übrigens recht simpel und 
offensichtlich:
1
  i = key_state
2
  rpt_ct0 = ~( rpt_ct0 & i );                               // reset or count rpt_ct0
3
  rpt_ct1 = rpt_ct0 ^ (rpt_ct1 & i);                        // reset or count rpt_ct1
4
  rpt_ct2 = (rpt_ct0 & rpt_ct1) ^ (rpt_ct2 & i);            // reset or count rpt_ct2
5
  rpt_ct3 = (rpt_ct0 & rpt_ct1 & rpt_ct2) ^ (rpt_ct3 & i);  // reset or count rpt_ct3
6
  i &= rpt_ct0 & rpt_ct1 & rpt_ct2 & rpt_ct3;               // count until roll over ?
7
  key_rpt |= key_state & i;                                 // then repeat key

von Andreas B. (bitverdreher)


Lesenswert?

Mathias W. schrieb:
> Problem war nur, dass ich ihn gleich in
> Assembler implementiert und sich da ein Fehler eingeschlichen hatte.

Magst Du diesen Code auch mal veröffentlichen? Dann bräuchte ich mir 
nicht die Mühe machen, die Komfortroutine in Assembler zu übertragen. 
Die Kurz-Lang Erkennung wäre für mich wichtig.

von Wilhelm M. (wimalopaan)


Lesenswert?

Falls man bis zu 64 vertikale Zähler beliebiger Breite (besser: Höhe) 
braucht, kann man das folgende template VCounter nehmen. Es ist genau 
der gleiche vertikale Zähler mit impliziter Rücksetzung.


Vorteile:
- vollkommen generisch
- gekapselt

Nachteile:
- C++ mit TMP
- ein paar Zeilen Code in Header-Datei

Ist also der direkte Ersatz für ct0, ct1, ct2, ... im originalen Code.
1
// -> in Header vertical_counter.h
2
#include <cstdint>
3
#include <utility>
4
#include <array>
5
6
#if __has_include(<etl/type_traits.h>)
7
# include <etl/type_traits.h>
8
# define HAVE_ETL
9
#endif
10
11
template<auto N> using NumberOfCounters = std::integral_constant<size_t, N>;
12
template<auto N> using NumberOfBits     = std::integral_constant<size_t, N>;
13
14
template<typename Counters, typename CounterSize>
15
struct VCounter {
16
    inline static constexpr auto nCounters = Counters::value;
17
    inline static constexpr auto nDigits   = CounterSize::value;
18
    static_assert(nCounters <= (sizeof(uint64_t) * 8));
19
    
20
#ifdef HAVE_ETL
21
    using value_type = etl::typeForBits_t<nCounters>;
22
#else
23
    template<auto Bits>
24
    struct typeForBits {
25
        using type = typename std::conditional<(Bits <= 8), uint8_t, 
26
                         typename std::conditional<(Bits <= 16), uint16_t, 
27
                             typename std::conditional<(Bits <= 32), uint32_t,
28
                                  typename std::conditional<(Bits <= 64), uint64_t, void>::type>::type>::type>::type;
29
    };
30
    using value_type = typename typeForBits<nCounters>::type;
31
#endif
32
    inline constexpr VCounter& operator<<(const value_type which) {
33
        digits[0] = ~(digits[0] & which);
34
        update(std::make_index_sequence<nDigits-1>{}, which);
35
        return *this;        
36
    }
37
    inline constexpr value_type maxReached() {
38
        return [&]<auto... II>(std::index_sequence<II...>){
39
            return (digits[II] & ...);
40
        }(std::make_index_sequence<nDigits>{});
41
    }
42
private:
43
    using digits_t = std::array<value_type, nDigits>;
44
    template<auto... II>
45
    inline constexpr void update(std::index_sequence<II...>, const value_type which) {
46
        value_type r{digits[0]};
47
        (((digits[II + 1] = (r ^ (digits[II + 1] & which))), r &= digits[II + 1]), ...);
48
    }
49
    digits_t digits = [&]{
50
         digits_t d;
51
         for(auto& i : d) {
52
             i = static_cast<value_type>(-1);
53
         }
54
         return d;
55
     }();
56
};
57
// <-- Ende Header
58
59
using VC = VCounter<NumberOfCounters<9>, NumberOfBits<3>>; // hier ggf. ändern
60
61
int main() {
62
    VC c;
63
    
64
    c << VC::value_type{0x01}; // count-up bit 1
65
    c << VC::value_type{0x01};
66
    c << VC::value_type{0x01};
67
    c << VC::value_type{0x01};
68
    c << VC::value_type{0x01};
69
    c << VC::value_type{0x01};
70
    c << VC::value_type{0x01};
71
    c << VC::value_type{0x01};
72
73
//    c << VC::value_type{0x00}; // reset
74
    
75
    return c.maxReached(); // check
76
}

von EliasH (Gast)


Lesenswert?

Im Original Code von Peter Danneger wird die Globale Variable:
volatile uint8_t key_state;
deklariert aber im Code finde ich keine Startinitialisierung.
in der ISR finden dann auch gleich eine Bitoperation damit statt.
Da die Variable keine Initialisierung hat, nehme ich an das der 
undefinierte Zustand für den Start bewusst in kauf genommen wird? Stimmt 
das?
MfG Elias

von Wilhelm M. (wimalopaan)


Lesenswert?

EliasH schrieb:
> Im Original Code von Peter Danneger wird die Globale Variable:
> volatile uint8_t key_state;

globale variable -> zero-initialized

C-Buch oder hier:

https://en.cppreference.com/w/c/language/initialization

: Bearbeitet durch User
von Steffen (Gast)


Lesenswert?

Hallo
Versuche die Tasterabfrage in meinen Atmega 8 zum laufen zu bekommen. Es 
geht so weit alles, komme nur mit der Nutzung der Tasten durcheinander.
Es sthet drin:

- get_key_press()
- get_key_rpt()
- get_key_press() mit get_key_rpt()
- get_key_short() mit get_key_long()
- get_key_short() mit get_key_long_r() und get_key_rpt_l()

Leider komme ich mit der Bezeichnung und der Nutzung nicht klar. Die 
Beschreibungen sind alle relativ kurz geraten.
Welche Taste macht was wann und mit welcher?

von Karl M. (Gast)


Lesenswert?

Hallo,

Sollte man dann ich das Programm "befragen" und den Code der 
Basisfunktionen verstehen?

Als Tipp, alle genannten Funktionen bekommen ein Bitmaske der Keys 
übergeben.

von Steffen (Gast)


Lesenswert?

Das habe ich getan. Kann mit einer Taste einschalten mit einer anderen 
aus, kurz einschalten mit lang ausschalten das geht alles und klar.
Was mach ich aber damit:
get_key_short() mit get_key_long_r() und get_key_rpt_l()
Es werden 3 Tasten angegeben. Das ist mir unklar.

von Karl M. (Gast)


Lesenswert?

Hallo,

ich habe noch ein paar Links für dich:

Beitrag "Re: Universelle Tastenabfrage"

Über dieses Bild / Diagramm sollte sich einiges Aufklären.
Beitrag "Problem mit Tasterentprellung"

https://www.mikrocontroller.net/articles/Entprellung

Dann hängt es immer noch davon ab, wie also durch wen das Programm 
implementiert wurde.
Stichwort: Fehler im eigenen Code und Codetests.

von Karl M. (Gast)


Lesenswert?

Von meiner Seite aus,

hatte ich die Basiscode von Peda (Peter Dannegger), Codeteile und die 
Funktionsnamen in Assembler unter LunaAVR implementiert.

Dann auf beliebige Ports noch erweitert, so dass man über die 
Hochsprache LunaAVR auch einfach zwei oder drei 8 Bit Ports entprellen 
kann.

von Steffen (Gast)


Lesenswert?

Hallo Karl M.
Habe deinen Rat befolgt und die Artikel von Peter durchgelesen. Muss 
ganz ehrlich sein, das ist für mich ein ganzes Stück zu hoch. Da reichen 
meine Verständnisse in C nicht aus.
Habe parallel dazu auf meinem Atmega 8 das Programm installiert und 
verschiedene Versionen geteste. Mit  if(get_key_press(1<<KEY_1)) kann 
ich die LED ohne Probleme ein und mit  if(get_key_press(1<<KEY_2)) 
ausschalten. Das geht. Leider erschliesst sich die Nutzung, wie Peter 
sie beschreibt gar nicht.
Such jetzt nach der Funktion, mit Taster kurz drücken einschalten, mit 
Taster lang drücken ausschalten. Dachte das diese Funktion ist 
get_key_short() mit get_key_long(), geht aber nicht. Da ist wohl noch 
ein grosse Denkfehler von mir drin.

von Steffen (Gast)


Lesenswert?

Habe das nächste in den Griff bekommen. Es funktioniert jetzt auch 
get_key_short und get_key_long Bedienung mit einem Taster ein und aus. 
Hatte den Timer falsch eingestellt.

von Hochspannung (Gast)


Lesenswert?

Ich entprelle hardwaremäßig

von Steffen (Gast)


Lesenswert?

Was macht das get_key_release

von Heiner (Gast)


Lesenswert?

Steffen schrieb:
> Was macht das get_key_release

Guck mal in den C-Code 😉

von Steffen (Gast)


Lesenswert?

Heiner schrieb:
> Guck mal in den C-Code 😉

Das habe ich gemacht. Leider reichen meine Kenntnisse von C dazu nicht 
aus. Den Ablauf verstehe ich nicht (muss ich das?). Möchte den Code 
anwenden und dazu zählt für mich die Nutzung der Tasten.

von Heiner (Gast)


Lesenswert?

Steffen schrieb:
> Heiner schrieb:
>> Guck mal in den C-Code 😉
>
> Das habe ich gemacht. Leider reichen meine Kenntnisse von C dazu nicht
> aus. Den Ablauf verstehe ich nicht (muss ich das?). Möchte den Code
> anwenden und dazu zählt für mich die Nutzung der Tasten.

Damit wirst Du nicht weit kommen - was wirst Du machen, wenn etwas nicht 
funktioniert ( und dieser Fall wird so sicher eintreten wie das Amen in 
der Kirche 😉) - wieder hier laut um Hilfe rufen?
Allein die Frage “muss ich das?” zeigt Deine Unfähigkeit und die 
Ignoranz.

von C-lover (Gast)


Lesenswert?

Steffen schrieb:
> Leider reichen meine Kenntnisse von C dazu nicht
> aus. Den Ablauf verstehe ich nicht (muss ich das?).
Der Ablauf ist unter C etwas kreativer als außerhalb. Wenn du nur 
Hochsprachen kennst dann wäre ein Konzept:
Tasten (Fernbedienung/Kabel) senden Ereignisse: A_down, A_repeat, 
A_up_kurz, A_up_lang
an die Anwendung wesentlich einfacher (Tastatur-Treiber u.U. eher in 
einfachen assembler).

von Micha (Gast)


Lesenswert?

Steffen schrieb:
> Was macht das get_key_release
Signalisiert das Loslassen der Taste, wie der Name schon suggeriert. 
Siehe die Grafiken unter 
Beitrag "Re: Universelle Tastenabfrage"

von Daniel N. (Gast)


Lesenswert?

Guten Abend,
ich möchte die Entprellroutine als Teil einer nicht kommerziellen 
studentischen Projektarbeit verwenden.
Die Funktionen habe ich in eine Source Datei und die Defines, Variablen 
und Prototypen in eine Header Datei geschrieben. Mit den Funktionen 
frage ich in meinem Programm ein paar Taster ab.
Als Kommentar habe ich am Anfang beider Dateien geschrieben:
1
/*
2
 * Entprellroutine von Peter Dannegger
3
 * Von https://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_(nach_Peter_Dannegger)
4
*/

Wäre das so ausreichend gekennzeichnet oder gibt es für (diesen) 
Programmcode auch sowas wie eine GNU Lizenz?
Hat da jemand Informationen zu oder von Herrn Dannegger diesbezüglich 
etwas gelesen?
Ansonsten muss ich mal meinen Betreuer fragen ob sowas in Ordnung ist 
und wie das gekennzeichnet werden muss.


Mit freundlichen Grüßen

von A. S. (Gast)


Lesenswert?

Daniel N. schrieb:
> Hat da jemand Informationen zu oder von Herrn Dannegger diesbezüglich
> etwas gelesen?
Frag ihn einfach, er ist aktiv hier
https://www.mikrocontroller.net/user/show/peda

von Peter D. (peda)


Lesenswert?

@Daniel N.

Ja, kannst Du so machen.

von Flöte (nsolo)


Lesenswert?

Falls man long und repeat parallel auf Tastern nutezen möchte hier meine 
erweiterung im Timer Interrupt
1
static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt[8];
2
static uint8_t long_press[8];
3
uint8_t i, in, nBtn=3;
4
5
...
6
7
8
for (int ii = 0; ii<nBtn; ii++){
9
    if( (key_state & (1 << ii)) == 0 )            // check repeat function
10
    {
11
      long_press[ii] = LONG_START;                // start long press delay
12
      rpt[ii] = REPEAT_START;                     // start repeat delay
13
    }
14
  
15
    if( --rpt[ii] == 0 )
16
    {
17
      rpt[ii] = REPEAT_NEXT;                      // repeat delay
18
      key_rpt |= key_state & (1 << ii);
19
    }
20
21
    if( --long_press[ii] == 0 ){
22
      key_long |= key_state & (1 << ii);
23
    }
24
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.