Forum: Mikrocontroller und Digitale Elektronik 4x4 Matrixtastatur mit Tasterentprellung


von Marc L. (marc_l)


Angehängte Dateien:

Lesenswert?

Hallo,

da ich mich demnächst mit einer 4x4-Matrixtastatur beschäftigen werde, 
habe ich Peter Danneggers Entprellroutine für eine 4x4 Matrixtastatur 
samt ein paar Änderungen, die hier im Forum zu finden sind, für das 
AVR-Studio 4 mit AVRgcc und dem ATmega8 zusammengefügt.
Hoffentlich bekomme ich von ihm keine Haue... ;)
Vielen Dank, Peter, für die Entprellroutine!
Die ist so tricky, dass ich noch nicht ganz dahinter gekommen bin, aber 
dazu gibt es ja unmengen an Erklärungen hier im Forum!

Nach dem Compilieren erhalte ich:
1
AVR Memory Usage
2
----------------
3
Device: atmega8
4
5
Program:     828 bytes (10.1% Full)
6
(.text + .data + .bootloader)
7
8
Data:         15 bytes (1.5% Full)
9
(.data + .bss + .noinit)
10
11
12
Build succeeded with 0 Warnings...
Keine Warnungen, keine Fehler, das ist schon mal sehr gut! :)

Aber: Würde der Code auch so funktionieren?
Bin mir nicht 100%-ig sicher, ob vor allem die Funktion key_scan(void) 
richtig funktioniert.

Im Anhang ist der Quellcode von Version 0.1 samt Kommentar.

Besten Dank,
Marc

von MaWin (Gast)


Lesenswert?

Im Prinzip ist das Peter Daneggers Code.
Soll er sich um seinen Verhau kümmern.

Der Code basiert darauf, daß Tasten, die abgefragt wurden,
abgeholt worden sind, d.h. es ist nicht mehr vermerkt daß
es ein Ereignis mit ihnen gab.

Wenn man also Taste KEY1 auf key_press abfragte, muss man sich nicht 
mehr wundern, wenn sie auf key_short keine Antwort mehr liefert.

Der Code an sich funktioniert also, bloss ob du ihn richtig 
verwendest...

Das Windows API mit Ereignissen wie WM_KEYDOWN etc. zeigt, wie man es 
besser gemacht hätte.

von Henry Daus (Gast)


Lesenswert?

MaWin schrieb:
> Im Prinzip ist das Peter Daneggers Code.
> Soll er sich um seinen Verhau kümmern.
Ja, genau! Den Code wollte ich so wenig wie möglich verändern.
Dieser Code ist nicht aus meinem Hirnschmalz entstanden, sondern er 
gehört zum größten Teil PeDa.
Aber auf die Haue von ihm freue ich mich schon! ;)

Ich wollte mit dem Code etwas universelles schreiben, denn hier im Forum 
habe ich zu seiner Entprell-Routine viele Fragen und Antworten gelesen.
Um es einfacher zu machen, habe ich versucht die relevanten Funktionen 
in eine C-Datei zu packen.
Leider habe ich keinen Code für eine 4x4-Matrixtastatur gefunden, darum 
musste ich mir diesen zusammenbasteln (copy & paste).

MaWin schrieb:
> Der Code basiert darauf, daß Tasten, die abgefragt wurden,
> abgeholt worden sind, d.h. es ist nicht mehr vermerkt daß
> es ein Ereignis mit ihnen gab.
Das macht Sinn, habe es auch so verstanden.

MaWin schrieb:
> Wenn man also Taste KEY1 auf key_press abfragte, muss man sich nicht
> mehr wundern, wenn sie auf key_short keine Antwort mehr liefert.
Okay, dann ist die for(;;)-Schleife in der main wohl etwas unglücklich 
gestaltet:
1
  for(;;)
2
  {
3
    if( get_key_press( 1<<KEY0 ))
4
      ;;  // do something @ single press
5
6
    if( get_key_short( 1<<KEY1 ))
7
      ;;  // release after short press: task 1,  long press: task 2
8
9
    if( get_key_long( 1<<KEY1 ))
10
      ;;  // do something @ single press
11
12
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 ))
13
      ;;  // do something @ single press and repeat     
14
  }
15
}
Allerdings, wenn man eine Eingabe-Funktion schreibt, benötigt man doch 
sowieso nicht alle if-Bedingungen, die oben erwähnt werden.
Eine davon reicht doch z.B. für eine do-while-Schleife aus?!

> Der Code an sich funktioniert also, bloss ob du ihn richtig
> verwendest...
Ja, genau das ist die Frage...

Man könnte doch versuchen gemeinsam den Code 100%-ig lauffähig zu machen 
und ihn dann in die Codesammlung stellen, damit die geschätzen 1 Mio. 
(Such-)Einträge zu dieser Funktion nicht auf 2 Mio. anwachsen! ;)

Gruß,
Marc

von Peter D. (peda)


Lesenswert?

MaWin schrieb:
> Das Windows API mit Ereignissen wie WM_KEYDOWN etc. zeigt, wie man es
> besser gemacht hätte.

Besser nicht unbedingt, aber erheblich verschwenderischer.

Man muß schon unterscheiden, ob etwas auf einem zich GByte-System laufen 
soll oder auch auf einem ATtiny13 mit nur 0,000.000.064 GByte RAM.
Und mit 64Byte SRAM wird es eben knapp mit OS, Multitasking und 
Sanduhren.

Deshalb der Verzicht auf ne Queue die sich uralte Ereignisse merkt, 
sondern die Beschränkung auf ein Ereignis pro Taste.
In der Regel können MC-Mainloops auch hinreichen kurz gehalten werden, 
daß man keine Sanduhren einblenden muß und die Ereignisse sich stapeln.


Peter

von Marc L. (Gast)


Lesenswert?

Shit, ist man schon bei einem Kumpel ma fremden Rechner, dann sollte man 
auch auf den Namen achten! grmpf

Übrigens:
1
#include <avr/pgmspace.h>
...das kann man getrost entfernen.
Werde das im nächsten veröffentlichten Quelltext vornehmen!

von Peter D. (peda)


Lesenswert?

MaWin schrieb:
> Wenn man also Taste KEY1 auf key_press abfragte, muss man sich nicht
> mehr wundern, wenn sie auf key_short keine Antwort mehr liefert.

Tut er ja auch  nicht.
Er nimmt verschiedene Tasten und das funktioniert einwandfrei.


Peter

von Marc L. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Er nimmt verschiedene Tasten und das funktioniert einwandfrei.
Später soll mit einer do-while-Schleife immer nur ein Tastendruck 
erkannt werden, eine Repeat-Funktion brauche ich eigentlich gar nicht.
Habe diese aber mit übernommen, damit das Ganze universeller wird.

@PeDa:
Bekomme ich noch Haue von dir?
Tut mir leid, dass ich deinen Code etwas erweitert habe hier 
"verteile"...

Gruß,
Marc

von Peter D. (peda)


Lesenswert?

1
  KEY_DDR = 1<<KEY_ROW1;
mal überdenken.

Der Rest sieht vollkommen o.k. aus.

Peter

von Marc L. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> KEY_DDR = 1<<KEY_ROW1;
> mal überdenken.
Hhm, ich wollte mich bzw. dich nicht verwirren und fange (aus 
menschlicher Sicht) bei Reihe 1 an zu zählen, also nicht von KEY_ROW0 
bis KEY_ROW3, sondern von KEY_ROW1 bis KEY_ROW4:
1
#define KEY_ROW1  PORTC0
2
#define KEY_ROW2  PORTC1
3
#define KEY_ROW3  PORTC2
4
#define KEY_ROW4  PORTC3
5
#define KEY_COL1  PORTC4
6
#define KEY_COL2  PORTC5
7
#define KEY_COL3  PORTC6
8
#define KEY_COL4  PORTC7
Somit dürfte doch KEY_DDR = 1<<KEY_ROW1; richtig sein, oder nicht?
Weiß nicht, worauf du hinaus möchtest.

Peter Dannegger schrieb:
> Der Rest sieht vollkommen o.k. aus.
Danke! Hoffentlich kann ich es bald testen.
Bin ja gerade noch bei meinem Kumpel und er hat eine 4x4-Tastaturmatrix 
rumliegen.
Werde es morgen mal an der Hardware testen, sofern ich dazu komme.

Kann man bereits jetzt schon bestimmen, welches Bit zu welcher Taste 
gehört?
1
#define KEY0       0  // Taste:
2
#define KEY1       1  // Taste:
3
#define KEY2       2  // Taste:
4
#define KEY3       3  // Taste:
5
#define KEY4       4  // Taste:
6
#define KEY5       5  // Taste:
7
#define KEY6       6  // Taste:
8
#define KEY7       7  // Taste:
9
#define KEY8       8  // Taste:
10
#define KEY9       9  // Taste:
11
#define KEY10     10  // Taste:
12
#define KEY11     11  // Taste:
13
#define KEY12     12  // Taste:
14
#define KEY13     13  // Taste:
15
#define KEY14     14  // Taste:
16
#define KEY15     15  // Taste:

Gruß und danke,
Marc

von Peter D. (peda)


Lesenswert?

1
#define KEY_ROW1  PORTC0
2
...
3
  KEY_DDR = 1<<KEY_ROW1;  
4
...
5
    keys = (keys << 4) | (KEY_PIN & 0x0F);

Was denkst Du, liest Du damit ein?


Peter

von Marc L. (marc_l)


Lesenswert?

Peter Dannegger schrieb:
> #define KEY_ROW1  PORTC0
> ...
>   KEY_DDR = 1<<KEY_ROW1;
> ...
>     keys = (keys << 4) | (KEY_PIN & 0x0F);
>
> Was denkst Du, liest Du damit ein?
Die Spalten, hoffe ich doch, von PC4 bis PC7.

Hhm, die Variable keys ist ja 16 Bit breit und wird auf 0 initialisiert.
Also steht am Anfang:
keys = 0b0000000000000000:
Mit (keys << 4) schiebe ich die niederwertigsten Bits um 4 nach links 
und verodere mit 0x0F von PINC.
Okay, irgendwas sagt mir jetzt, dass da etwas mit der Anweisung
1
keys = (keys << 4) | (KEY_PIN & 0x0F);
nicht stimmt.

Ich möchte sowas in der der Art haben:
1
keys = 0b 0000 0000 0000 0000
2
          COL4 COL3 COL2 COL1
Muss ich erstmal um 0 nach links verschieben, danach erst jeweils um 4?

Also quasi:
1
static inline uint16_t key_scan(void)
2
{
3
  uint16_t keys = 0;
4
  uint8_t i;
5
6
  KEY_DDR = 1<<KEY_ROW1;  
7
  for( i = 4; i; i-- ){
8
    if(i == 4) { keys = (KEY_PIN & 0x0F); }
9
    keys = (keys << 4) | (KEY_PIN & 0x0F);
10
    KEY_DDR <<= 1;
11
  }
12
  return ~keys;
13
}

Bin bestimmt auf dem Holzweg...

von Peter D. (peda)


Lesenswert?

Marc L. schrieb:
>>     keys = (keys << 4) | (KEY_PIN & 0x0F);
>>
>> Was denkst Du, liest Du damit ein?
> Die Spalten, hoffe ich doch, von PC4 bis PC7.

Die sind aber nach (KEY_PIN & 0x0F) weg.


Peter

von Marc L. (marc_l)


Angehängte Dateien:

Lesenswert?

Marc L. schrieb:
> Muss ich erstmal um 0 nach links verschieben, danach erst jeweils um 4?
Ich muss mich selbst korrigieren. Es ist doch Quatsch, wenn ich am 
Anfang auf das Verschieben verzichte, denn ob ich nun alle Nullen 
verschiebe oder nicht, ist doch egal. Nullen bleiben Nullen.

Peter Dannegger schrieb:
> #define KEY_ROW1  PORTC0
> ...
>   KEY_DDR = 1<<KEY_ROW1;
> ...
>     keys = (keys << 4) | (KEY_PIN & 0x0F);
Ich hoffe, ich hatte in der Nacht den richtigen Einfall und müsste den 
Code folgendermaßen ändern:
1
static inline uint16_t key_scan(void)
2
{
3
  uint16_t keys = 0;
4
  uint8_t i;
5
6
  KEY_DDR = 1<<KEY_ROW1;  
7
  for( i = 4; i; i-- ){
8
    keys = (keys << 4) | (KEY_PIN & 0xF0);   // <----------
9
    KEY_DDR <<= 1;
10
  }
11
  return ~keys;
12
}

Das meintest du, nicht wahr, PeDa?

Außerdem ist mir ein ganz großer Fehler im ersten Posting passiert.
Der ATmega8 hat gar kein PC7, geht nur bis PC6, und das ist auch noch 
der RESET-Pin.
Im Anhang ist eine geänderte Version des Quelltextes aus dem Urposting: 
PortD statt PortC wird nun verwendet.

Gruß,
Marc

von Marc L. (marc_l)


Angehängte Dateien:

Lesenswert?

Hallo,

anbei die Version 0.3 - diesmal für einen ATmega16.

Diesen Code habe ich mit Proteus (Softwaresimulator) getestet, 
funktioniert 1A und sollte also auch auf der Hardware laufen.

Vielleicht kann es ja einer gebrauchen...
Danke PeDa für die Hilfestellung und deinen tricky Code! :)

Gruß,
Marc

PS: Für Hinweise und weitere Tipps habe ich immer ein Ohr offen!

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.