Forum: Mikrocontroller und Digitale Elektronik Taster Entprellen (gleichzeitige Abfrage)


von rudi (Gast)


Lesenswert?

Hallo zusammen !
Im Tutorial habe ich folgende Routine zum Entprellen von Tastern 
gefunden.
Funktioniert soweit auch ! (Atmega8 und WINAVR)
Jedoch möchte ich jetzt die ganze Sache so ummodellieren, dass ich 
PIND0,PIND1 und PIND2 gleichzeitig abrufen möchte...
wie kann ich das jetzt umschreiben....?
Hintergrund ist, dass mein Programm zu lange dauert , weil jeder PIN 
einzeln abgefragt wird und jedes mal 200ms draufgehen....

Danke für eure HIlfe und Mühe !



//Funktion zum Entprellen der Taster 
--------------------------------------

inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
{
    if ( ! (*port & (1 << pin)) )
    {
        /* Pin wurde auf Masse gezogen, 100ms warten   */
        _delay_ms(100);
        if ( *port & (1 << pin) )
        {
            /* Anwender Zeit zum Loslassen des Tasters geben */
            _delay_ms(100);
            return 1;
        }
    }
    return 0;
}

von Falk (Gast)


Lesenswert?

@rudi

>Im Tutorial habe ich folgende Routine zum Entprellen von Tastern
>gefunden.

Das ist die einfache, aber schlechte Routine. Versuchs mal damit.

http://www.mikrocontroller.net/articles/Prellen

MfG
Falk

von rudi (Gast)


Angehängte Dateien:

Lesenswert?

ok habe mal ein wenig ausprobiert...

Ich möchte einfach nur einen Tastendruck erkennen...

habe mal versucht den Code zu modifizieren...

habe KOmmentare in den Code eingefügt...könnt ihr mir sagen, ob ich das 
so lassen ?

bzw. was kann ich noch weglassen ?

Vielen Dank für eure hilfe

von Karl H. (kbuchegg)


Lesenswert?

rudi wrote:
> ok habe mal ein wenig ausprobiert...
>
> Ich möchte einfach nur einen Tastendruck erkennen...
>
> habe mal versucht den Code zu modifizieren...
>
> habe KOmmentare in den Code eingefügt...könnt ihr mir sagen, ob ich das
> so lassen ?
>

Wenns funktioniert kannst du es so lassen.

> bzw. was kann ich noch weglassen ?

>    if( get_key_press( 1<<KEY0 ))
>      LED_PORT ^= 1<<LED0;
>
> // könnte ich auch schreiben .. anstatt KEY0 -->
>    z.B PB0 oder PB1 oder PD5 ????

Könntest du, aber wozu? KEY0 ist doch aussagekräftiger als PB0

Wenn schon, dann erfinde einen anderen Namen für KEY0.
Zb. Hast du eine Taste, die logisch gesehen "nach links"
bedeutet. Vielleicht hast du auch eine Taste für
"nach rechts". Und noch eine dritte für "Enter".

Also machst du
1
#define KEY_DDR         DDRB
2
#define KEY_PORT        PORTB
3
#define KEY_PIN         PINB
4
5
#define KEY_LEFT        PB0
6
#define KEY_RIGHT       PB1
7
#define KEY_ENTER       PB3
8
#define ALL_KEYS        (1<<KEY_LEFT | 1<<KEY_RIGHT | 1<<KEY_ENTER)
9
 
10
#define REPEAT_MASK     (1<<KEY_LEFT | 1<<KEY_RIGHT)
11
#define REPEAT_START    50                        // after 500ms
12
#define REPEAT_NEXT     20                        // every 200ms
Damit hast du dokumentiert:
DIe Tasten hängen am Port B
Die Taste für "links" hängt auf PB0. Die für "rechts" auf PB1 und
"Enter liegt auf PB3

Weiters hast du ein Makro ALL_KEYS, das du bei der Initialisierung
des Ports benutzen kannst:
1
int main()
2
{
3
  KEY_DDR &= ~(ALL_KEYS);   // Am DDR die entsprechenden 0-en setzen
4
  KEY_PORT |= ALL_KEYS;     // Für die Tasten die Pullup Widerstände ein   
5
6
  ....
Weiters hast du mit dem Makro REPEAT_MASK dokumentiert, dass
sowohl "links" als auch "rechts" einen Autorepeat besitzen,
wohingegen die dritte Tatse "Enter" da nicht auftaucht und daher
auch keinen Autorepeat hat.

Bei der Abfrage machst du dann
1
  if( get_key_press( 1<<KEY_LEFT ) || get_key_rpt( 1<<KEY_LEFT ) )
2
      LED_PORT ^= 1<<LED0;
3
4
  if( get_key_press( 1<<KEY_RIGHT) || get_key_rpt( 1<<KEY_RIGHT) )
5
      ....

Wenn du jetzt eine Änderung in der Hardware machen musst:
* zb alle Tasten vom Port B an den Port D verlegen
1
  #define KEY_DDR         DDRD
2
  #define KEY_PORT        PORTD
3
  #define KEY_PIN         PIND
  alles andere passt sich von alleine daran an
* zb. die Enter Taste von PB3 nach PB6
1
  #define KEY_ENTER       PB6
  alles andere passt sich wieder von alleine an (inklusive
  Port Einstellungen und Pullup Widerstände.


Die Kunst ist nicht auf Biegen und Brechen abzuspecken. Die
Kunst besteht darin den Code so zu schreiben, dass man bei
Änderungen nur an möglichst wenigen Stellen anpassen muss
und der Compiler (Präprozessor) den Rest anpasst.

von rudi (Gast)


Angehängte Dateien:

Lesenswert?

Danke erstmal für deine Hilfe...
also am besten Erkläre ich jetzt auch mal was ich vorhabe !
Ich habe eine Tastenblock mit 12 Tasten. (3x4 Matrix Anschluß)-->also 3 
Spalten und vier Zeilen. Um das Auslesen zuz realisieren habe ich die 
Spalten als Eingänge mit Pull ups und die Zeilen als Ausgänge definiert. 
Ausgangspegel =high ("1"). Das heißt jetzt wenn ich eine Taste z.b die 
Zahl 3 erkennen möchte (Zeile 1 und Spalte 3) , schreibe ich am ausgang 
Zeile 1 eine "0" und überprüfe ob eine "0" in Spalte 3 eingelesen wird.

Habe den Code nochmal geändert...jedoch erhalte ich folgende 
Fehlermeldung

test.c:47: error: expected expression before ')' token
test.c:52: error: expected expression before ')' token

_______________________________________________________

//Zeile 47//wofür ist dieser Teil überhaupt gedacht ?

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

wie kann ich mein vorhaben am besten mit dieser Routine realisieren ??

Vielen dank für eure unterstützung

mfg rudi



von Karl H. (kbuchegg)


Lesenswert?

rudi wrote:
>
> Ich habe eine Tastenblock mit 12 Tasten. (3x4 Matrix Anschluß)

Diese Information wäre schon vor laaaanger, laaanger
Zeit nützlich gewesen.

>
> Habe den Code nochmal geändert...jedoch erhalte ich folgende
> Fehlermeldung
>
> test.c:47: error: expected expression before ')' token
> test.c:52: error: expected expression before ')' token
>
> _________________________________________________________
>
> //Zeile 47//wofür ist dieser Teil überhaupt gedacht ?
>
>  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;
>   }
> }
> //Zeile 47//wofür ist dieser Teil überhaupt gedacht ?

Für den Autorepeat.
Wenn der Benutzer auf der Taste einschläft, dann sorgt dieser
Programmteil dafür, dass ständig neue Tastendrücke
generiert werden. Als du das hier

> _________________________________________________________

geschrieben hast, hast du genau diese Funktionalität
benutzt, oder hast du etwa 30 mal auf _ getippt?

> wie kann ich mein vorhaben am besten mit dieser Routine realisieren ??

Auf die schnelle würde ich sagen: gar nicht.
Das ist das falsche Werkzeug.
Diese Routinen sind super wenn es um Einzeltasten geht.
Für eine Tastenmatrix muss man aber anders auswerten.
Wie?: Das hast du schon beschrieben.

Das Prinzip "Ein Tastendruck gilt erest dann, wenn er 3 oder
4 mal hintereinander als gedrückt erkannt wird", kannst du
ja beibehalten. Aber die Funktionen müsste man derartig
kräftig umschreiben, dass man sie gleich neu schreiben kann.

Du hast ein Array für alle Tasten in dem für jede
Taste ein Zähler realisiert ist.
In deiner Abfrage Funktion gehst du alle Tasten (nach dem
von dir beschriebenen Prinzip) durch und wenn eine Taste
gedrückt ist, erhöhst du den Zähler. Ist die Taste nicht
gedrückt, wird der entsprechende Zähler auf 0 gesetzt.
Erreicht der Zähler 3 oder 4 (such dir was aus), dann gilt
diese Taste als gedrückt und du vermerkst dir dieses in
einem anderen Array.
Diese Abfragefunktion hängst du an einen Timer und lässt sie
alle 10 ms aufrufen.

Damit hast du funktional was ähnliches gebaut wie die PeDa
Tastenabfrage und Entprellung, nur halt angepasst an deine
Matrix.

von rudi (Gast)


Angehängte Dateien:

Lesenswert?

Hallo und vielen Dank dass du versuchst mir zu helfen...

ja so eine Idee hatte ich auch schon mal...habe es mal programmiert , 
jedoch leider mit der 'debounce' Routine zum Entprellen...
habs mal angefügt (ist nur ein Ausschnitt aus meinem Code)

aber man sagte mir hier, dass dies eine schlechte Routine wäre, was ich 
ja einsehe, da mein Programm ja jede Taste einzeln abfragt und ein 
Durchlauf ja dann ziem,lich lange dauert....

deswegen wollte ich eigentlich diese Routine so ändern, dass ich alle 3 
spalten auf einmal abfrage und nachher auswerte was gedrückt wurde....

...mhhh...

was meint ihr dazu ?

Danke

von Karl H. (kbuchegg)


Lesenswert?

rudi wrote:
> Hallo und vielen Dank dass du versuchst mir zu helfen...
>
> ja so eine Idee hatte ich auch schon mal...habe es mal programmiert,
> jedoch leider mit der 'debounce' Routine zum Entprellen...
> habs mal angefügt (ist nur ein Ausschnitt aus meinem Code)

Ich hab dir doch erklärt, wie die bessere Funktion funktioniert.
Warum schusterst du dann immer noch mit _delay rum. Das
führt zu nächst.

Im letzten Post hab ich die Idee entwickelt, nach der die
bessere Entprellung funktioniert. Umsetzen musst du das
schon alleine. Auch wenn dein Code nicht so ausgefuchst
optimiert ist, wie die PeDa Entprellung, so kann sie
doch auf demselben Prinzip basieren. Und damit du das
Prinzip verstehst (das ist aus dem original PeDa Code
nur schwer zu erkennen), hab ich das mal auseinander
genommen.
Da passiert nicht mehr: für jede Taste gibt es einen Zähler.
In einem regelmässigne Interrupt wird jede Taste untersucht
ob gedrückt oder nicht. Ist sie gedrückt -> Zähler um 1 rauf.
Ist sie nicht gedrückt -> Zähler wieder auf 0.
Erreicht der Zähler die 4, dann gilt die Taste als gedrückt.

> deswegen wollte ich eigentlich diese Routine so ändern, dass ich
> alle 3 spalten auf einmal abfrage und nachher auswerte was
> gedrückt wurde....

Ja. Mach das mal.




von Hannes L. (hannes)


Lesenswert?

Denkanstoß:

Die PeDa-Bulletproof-Entprellung ist dafür gedacht, dass jede Taste 
etwas anderes bewirkt und dass mehrere Tasten gleichzeitig etwas 
auslösen können.

Hier soll aber ein Ziffernblock ausgewertet werden. Da ist das 
gleichzeitige Betätigen mehrerer Tasten eigentlich ein Bedienungsfehler. 
Daher bietet sich an, alle 20 ms die komplette Matrix auszuwerten und 
den Tastenwert zu ermitteln. Entprellt wird dann der Tastenwert. Ist 
dieser eine vorgesehene Anzahl von 'Runden' stabil, so wird er 
übernommen, ist er anders als der vorherige übernommene Wert, so wird 
die Aktion ausgelöst, die das Eingeben einer Ziffer bewirken soll.

...

von rudi (Gast)


Angehängte Dateien:

Lesenswert?

Habe es mal versucht zu programmieren...jedoch habe ich noch nicht so 
viel Erfahrung...

Ich glaube dass man an dem Code noch einiges optimieren kann....

- was ich jetzt noch nicht geschafft habe, einen Timer einzubauen, der 
diese Routine all1e 10ms abfragt....

oder wie war das gemeint ?

Vielen Dank füpr eure Hilfe

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

rudi wrote:
> Habe es mal versucht zu programmieren...jedoch habe ich noch nicht so
> viel Erfahrung...

Ich habe es mir nicht angesehen, C ist mir zu kryptisch, ist nicht mein 
Ding.

> - was ich jetzt noch nicht geschafft habe, einen Timer einzubauen, der
> diese Routine all1e 10ms abfragt....
>
> oder wie war das gemeint ?

Bei mir hat (fast) jedes AVR-Programm (mindestens) einen 
Timer-Interrupt, der diverse Aufgaben synchronisiert, so auch das 
Abfragen und Entprellen von Bedienungstasten.

Im Anhang gibt's als Beispiel ein einfaches Programm für Tiny2313, das 
eine Tastenmatrix 3x4 abfragt und entprellt, eine dreistellige 
7-Segmentanzeige betreibt und einen Sirenensound erzeugt, wenn die 
Eieruhr abgelaufen ist. Ich hoffe, es hilft Dir weiter...

...

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.