Hier jetzt eine Entprellroutine, wie ich sie schon in vielen Geräten
eingesetzt habe.
Diesmal ist das Programm in C geschrieben.
Sie funktioniert im Prinzip genau so, wie die 8-Tasten-Routine. D.h. das
Entprellen erfolgt durch Vergleich zweier aufeinanderfolgender
Tastenzustände.
Auch das gleichzeitige Drücken mehrerer Tasten wird unterstützt, um z.B.
Shift-Tasten zu realisieren.
Bei mehr als 2 gedrückten Tasten, müssen jedoch die Tasten durch Dioden
entkoppelt werden, da sonst in der Matrix keine eindeutige Erkenung mehr
möglich ist.
Um auch das gleichzeitige Betätigen mehrerer Tasten zu erkennen, kann
diese Routine mehrmals aufgerufen werden, wenn ein Tastencode ungleich 0
zurückgeliefert wurde. Es werden unterschiedliche Kodes beim Drücken
bzw. Loslassen erzeugt. Somit ist eine Auswertung von Shift-Tasten oder
Repeatfunktion möglich.
Peter
Guten Tag Herr Dannegger
das ist mir jetzt zwar peinlich aber ich muss es fragen.
die Zeile
1
if(column!=valid_keys[i]&&key_nr==0)
verstehe ich nicht. Mein Dozent meinte immer nur bei solchen konstrukten
soll man klammern setzten damit man weiß was höhere priorität hat wenn
man keine ahnung davon hat.
ich habe jetzt keine ahnung und klammern setzten nützt mir nichts um es
zu verstehen.
Im Array valid_keys[] hat jede Taste ein Bit.
Ändert sich nun eine Taste, ist valid_keys[] ungleich dem eingelesenen
Byte und es muß behandelt werden.
Wurde noch keine Änderung bearbeitet, ist key_nr = 0 und diese Änderung
kann bearbeitet werden.
Anders gesagt, falls 2 Tasten sich exakt gleichzeitig ändern (sehr
selten, aber nicht unmöglich), wird nur die erste bearbeitet (die andere
dann im nächsten Durchlauf).
Peter
Hi
ich habe die routine mal AVR umgeschrieben. bis auf kleine änderungen in
der syntax für avr-gcc musste ich noch etwas an der ansteuerung ändern,
ein 4MHz AVR ist wohl etwas schneller wie ein 8051.
das setzen der reihen habe ich zum ende der schleife geschoben.
1
for(i=0;i<3;++i)
2
{
3
//code.....
4
//die ganzen if-abfragen
5
6
last_keys[i]=column;// for next debouncing
7
PORTE|=0xE0;
8
PORTE&=ROW[i];// set rows
9
}
so hat bleibt ist mehr zeit, dass PINC die zustände annehmen kann. Es
ändert sich natürlich auch der Tasten-Code, weil ja jetzt die letzte
zeile zuerst eingelesen wird.
Andreas W. wrote:
> ich habe die routine mal AVR umgeschrieben. bis auf kleine änderungen in> der syntax für avr-gcc musste ich noch etwas an der ansteuerung ändern,> ein 4MHz AVR ist wohl etwas schneller wie ein 8051.
Ne, das ist geschwindigkeitsunabhängig.
Der AVR latcht den Eingang schon im vorherigen Befehl, setzt die
Ausgänge aber erst im nächsten.
Man muß mindestens ein NOP einfügen, damits klappt.
Peter
ich hatte es mit einen asm volatile ("nop") probiert, was auch nicht
funktioniert hat. deshalb dann der "umweg" mit den erneuten setzen
unten.
ich glaube so wird das programm auch nicht größer.
Martin wrote:
> Hallo Peter,>> Wie genau ist den die Matrix an den µC angeschlossen?
Als 3*8 Matrix: die 8 Spalten an P2 und die 3 Zeilen an P0.0, P0.1,
P0.2.
Peter
Hallo,
erstmal vielen Dank für die Routine.
Ich habe sie auf einem Mega8 laufen - 1A!
Man kann die Routine entweder nach Andreas W. (s.o.) anpassen oder mind.
2 NOPs zwischen setzen und lesen der IO Register einfügen. Ich habe mich
für die erste Möglichkeit entschieden. Wenn man die dadurch entstandene
Verschiebung nicht mag einfach ROW[] anpassen.
Allerdings ist mir im org. Coding ein Fehler aufgefallen.
Beim setzen des Ausgangs heisst es:
1
P0|=~0x07;
2
P0&=ROW[i];
Müsste es nicht aber
1
P0|=0x07;
2
P0&=ROW[i];
sein, also ohne ~ in der ersten Zeile? Ich möchte ja die 3 Outputs auf
High setzen, um danach genau einen Low zu setzen. Die obige Variante
würde die anderen Pins auf dem Port high setzen und die 3 relevanten in
Ruhe lassen...
Schneekönig
> sein
Ja.
Beim AVR darf man auch nicht den Port setzen, sondern das DDR-Register
(Portbit immer low), sonst kommt es zum Kurzschluß, wenn man 2 Tasten
drückt.
Man muß also einen Open-Drain-Ausgang nachbilden.
Peter
Peter Dannegger schrieb:> Martin wrote:>> Hallo Peter,>>>> Wie genau ist den die Matrix an den µC angeschlossen?>> Als 3*8 Matrix: die 8 Spalten an P2 und die 3 Zeilen an P0.0, P0.1,> P0.2.>>> Peter
Hallo Peter,
wie sieht es aus wenn ich meine 4*4 Matrix an verschiedenen Ports habe
(Kolonnen E4:7 und Reihen G0,G3,D4,D7)
hab ich das mit deiner Routine schnell angepasst?
Hallo µC Form,
zur Vollständigkeit folgt noch der Link zu Beitrag "DTMF Encoder -
seriell 19.200 Baud", wo die obige Klasse eingesetzt wird.
http://forum.myluna.de/viewtopic.php?f=8&t=161
_
Rico H. schrieb:> Hallo Peter,>> wie sieht es aus wenn ich meine 4*4 Matrix an verschiedenen Ports habe> (Kolonnen E4:7 und Reihen G0,G3,D4,D7)>> hab ich das mit deiner Routine schnell angepasst?
Ohne Peter da zu nahe treten zu wollen. Ich würde das selbst nochmal
ganz neu schreiben. Diesen veröffentlichten Code halte ich nicht
unbedingt für nachahmenswert
LinWin schrieb:> Diesen veröffentlichten Code halte ich nicht> unbedingt für nachahmenswert
Hast Du Dir überhaupt mal die Beschreibung durchgelesen?
Vermutlich nicht.
Diese Routine kann nämlich erheblich mehr als die üblichen Routinen.
Wenn Du erkennen kannst, daß sie Mist ist, dann mußt Du ja auch wissen,
wie man es "richtig" macht.
Zeig dochmal, wie Du diese Funktionalität (press-, release-code,
multiple keypress) realisieren würdest.
Hallo Peter,
Deine "Magic" funktioniert bei meinen Projekten 100%. Vielen Dank für
solchen wunderbaren Code! Sogar bei Portierungen macht er praktisch
keinerlei Schwierigkeiten. Er lässt zumindest bei mir keine Wünsche
offen und man kann sehr viel damit machen. Meine Letzte Adaptierung
funktioniert jetzt übrigens einwandfrei auf der WSB über eine SPI
MCP23S18 Schnittstelle. Bis jetzt portierte ich es auf STM32, einige
PICs, AVR(Codevision), und ZILOG encore.
Deine Quadratur Encoder Lösung ist auch eine sehr feine Sache.
mfg,
Gerhard
Peter Dannegger schrieb:> Beim AVR darf man auch nicht den Port setzen, sondern das DDR-Register> (Portbit immer low), sonst kommt es zum Kurzschluß, wenn man 2 Tasten> drückt.> Man muß also einen Open-Drain-Ausgang nachbilden.
Es wird also immer nur eine Reihe als Input geschaltet (über DDRx) und
dann alle Spalten angeguckt... aber mit Portbit (PORTx) immer low fehlt
mir doch der interne Pullup bei active low Tasten? ODer brauche ich die
Pullups nur an den Spalten?
So ganz bin ich auch noch nicht dahinter gekommen, wie mehrere
gleichzeitig gedrückte Tasten unterschieden werden. Ab wann braucht man
da Dioden und wo kommen die hin?
Kann vielleicht jemand eine funktionierende Portierung für AVR hier
einstellen?
Danke.
Hallo Werner,
Werner schrieb:> Peter Dannegger schrieb:>> Beim AVR darf man auch nicht den Port setzen, sondern das DDR-Register>> (Portbit immer low), sonst kommt es zum Kurzschluß, wenn man 2 Tasten>> drückt.>> Man muß also einen Open-Drain-Ausgang nachbilden.
Man kann bei die meisten MCUs OPEN-DRAIN Ausgänge simulieren indem man
alle Keypad Ausgangs PORT BITS einmalig auf Null setzt und die Ausgänge
sequenziell nur mit dem DDRn Register als jeweilig eingeschalteten
Ausgang ansteuert. Die jeweilig unbenutzten Ausgänge verhalten sich dann
immer als Eingänge. In anderen Worten, nur der jeweilig angesteuerte
PORT-PIN liegt auf Masse um die Spalten runter zu ziehen und ein
Kurzschluss zwischen irgendwelchen Spaltenausgängen haben keine Wirkung.
>> Es wird also immer nur eine Reihe als Input geschaltet (über DDRx) und> dann alle Spalten angeguckt... aber mit Portbit (PORTx) immer low fehlt> mir doch der interne Pullup bei active-low Tasten? ODer brauche ich die> Pullups nur an den Spalten?
Die Pullups braucht man nur bei den Eingängen. Viele MCUs haben interne
Pullup Widerstände die man einschalten kann. Beim AVR geht das ganz
einfach indem man die PORTn Register auf H setzt und die PINs mit dem
DDRn Register in den Eingang Modus versetzt. Das schaltet automatisch
die Pullups ein. Bei anderen MCUs wie PIC geht das bei vielen älteren
Modellen nur am PORTB. Ansonsten musst Du eben extra 2-10KOHM
Widerstände einbauen.
Diskrete Widerstände im Bereich von 2-10K haben aber auch den Vorteil
dass mehr Strom durch die Tastenschalter fließt und sie dann
zuverlässiger arbeiten. Viele Kontaktmaterialien brauchen einen gewissen
Mindeststrom um zuverlässig funktionieren zu können.
>> So ganz bin ich auch noch nicht dahinter gekommen, wie mehrere> gleichzeitig gedrückte Tasten unterschieden werden. Ab wann braucht man> da Dioden und wo kommen die hin?
Da jedes Spalte einzeln zeitlich sequenziell angesteuert wird lässt sich
das im Code wieder elegant entflechten. Ein Kurzschluss zwischen
gleichen Spalten macht sowieso nichts aus und ein Kurzschluss über ein
oder mehrere Spalten macht auch nichts aus weil sich das in den
eingelesenen Werten ersichtlich ist. Da der ganze Scan Vorgang zeitlich
sequenziell abläuft lässt sich alles richtig ableiten.
>> Kann vielleicht jemand eine funktionierende Portierung für AVR hier> einstellen?
Habe im Augenblick keine Portierung für AVR (Nur PIC). Das Beispiel von
Peter ist aber für den AVR gezeigt und sollte anstandslos funktionieren.
Es ist übrigens interessant zu sehen wie sein Code funktioniert. Wenn Du
auf eine oder mehrere Tasten drückst wird jede Taste richtig
nacheinander angezeigt. So, wenn man mehr als eine Taste drückt wird
nacheinander im Zug der Portsequenz der Keycode ausgegeben:
z.B. Taste 1 (2+3+4) gleichzeitig:
[0x81] [0x82] [0x83] [0x84]...
beim loslassen einer jeweiligen Taste wird das MSB bit geloescht:
Drücken: [0x81] loslassen: [0x01]
Bei mir verwende ich eine lookup table (LUT) um den Keycode um z.B mit
einem 4x4 Tastenfeld die nummern 0-9A-F zu generieren. Mit dieser LUT
lässt sich dann für jede Taste der gewünschte Keycode zuordnen.
Ich hoffe das hilft Dir etwas weiter,
mfg,
Gerhard
>> Danke.
Ich glaube ich habe es verstanden...
Die zu überprüfende Reihe wird per Ausgang auf low gezogen (PORTx.y = 0
und DDRx.y = 1).
Die anderen Reihen sind floatende Eingänge (PORTx.y = 0 und DDRx.y = 0).
Alle Spalten gehen zu Eingängen mit aktiviertem Pullup.
Nun kann ich anhand der Spalten die Taster in der aktiven Reihe
auswerten.
Ein gedrückter Taster in der zu überprüfenden Reihe zieht den
Spalteneingang auf low.
Ein gedrückter Taster in einer anderen Reihe kann über den floatenden
Eingang der Reihe die Spalte nicht auf low ziehen und stört nicht.
Die Reihen werden nun nacheinander ausgewertet -> multiplexing.
Ist das so richtig?
Werner schrieb:> Ich glaube ich habe es verstanden...>> Die zu überprüfende Reihe wird per Ausgang auf low gezogen (PORTx.y = 0> und DDRx.y = 1).> Die anderen Reihen sind floatende Eingänge (PORTx.y = 0 und DDRx.y = 0).
Wenn Du aber (PORTx.y = 1 und DDRx.y = 0) einstellst dann aktivieren
sich die eingebauten Port Pullup Widerstände.
Wie schon gesagt favorisiere ich extra diskrete Pullup Widerstände weil
man dann mehr Strom durch die Tasten fließen lassen kann was die
Zuverlässigkeit der Tastenaktion erhöhen kann.
>> Alle Spalten gehen zu Eingängen mit aktiviertem Pullup.> Nun kann ich anhand der Spalten die Taster in der aktiven Reihe> auswerten.> Ein gedrückter Taster in der zu überprüfenden Reihe zieht den> Spalteneingang auf low.
Der jeweilig vom Code aktivierte Port PIN macht das;-)
> Ein gedrückter Taster in einer anderen Reihe kann über den floatenden> Eingang der Reihe die Spalte nicht auf low ziehen und stört nicht.
Ja. Da die übrigen ROW Output Pins als Eingange wirken macht das nichts
aus.
>> Die Reihen werden nun nacheinander ausgewertet -> multiplexing.>> Ist das so richtig?
Ja. Der Rest passiert durch Peter's Magic Code;-)
Gerhard
Werner schrieb:> Wofür bräuchte man denn dann noch Dioden, wenn man mehrere Tastendrücke> gleichzeitig auswerten möchte?
Wenn man es wie beschrieben macht sind die Dioden überflüssig. Die
Dioden sind nur notwendig wenn man keinen OPEN DRAIN Ausgänge zur
Verfügung hat.
Ich setzte mal ein Shift-Register (74HC164) als ROW Treiber ein. In
diesem Fall waren die Dioden notwendig.
Ok, danke, dann hab ich es jetzt verstanden :-)
(PORTx.y = 1 und DDRx.y = 0) hab ich ja bei der Reihe auch nicht
geschrieben.
Das braucht man dann nur bei der Spalte oder eben externe Pullups.
Und ja, um genau zu sein, der Taster verbindet nur die Reihe mit der
Spalte.
Der Spalteneingang wird dann durch den auf low gestellten (aktiven)
Reihenausgang auf low gezogen.
Habe das mal für AVR angepasst.
Es kompiliert zumindest, kann es aber gerade mangels HW nicht testen.
Es fehlt aber auch noch die Anpassung mit dem NOP.
Kann mir jemand helfen, wo das genau zwischen muss?
Danke!
Hallo,
das hat Peter doch geschrieben:
Beitrag "Re: Tasten-Matrix entprellen"
Wie immer gibt es diese Info zum Lesen im Datenblatt deines µC im
Abschnitt Ports.
Werner schrieb:> Wofür bräuchte man denn dann noch Dioden, wenn man mehrere Tastendrücke> gleichzeitig auswerten möchte?
Sobald mehr als 2 Tasten gleichzeitig.
Ohne Dioden kann es bei 3 Tasten gedrückt zu einer 4. Phantomtaste
kommen.
Kannst du das mit der Phantomtaste genauer erklären, wie das zustande
kommt?
Das verstehe ich noch nicht.
Wegen des NOPs verstehe ich das so, dass vor dem Einlesen des Pins nach
dem Setzen der Ausgänge ein bis zwei Takte gewartet werden muss, also
so:
1
KEYPAD_ROW_DDR|=KEYPAD_ROW_MASK;//simulation of open drain output
2
KEYPAD_ROW_DDR&=ROW[i];// set rows (active row is low output, all other rows are floating input)
uint8_tROW[]={KEYPAD_ROW_0,KEYPAD_ROW_1,KEYPAD_ROW_2};// low active
4
5
for(i=0;i<3;++i){
6
7
KEYPAD_ROW_DDR|=KEYPAD_ROW_MASK;//simulation of open drain output
8
KEYPAD_ROW_DDR&=ROW[i];// set rows (active row is low output, all other rows are floating input)
9
asmvolatile("nop");
10
asmvolatile("nop");
11
column=KEYPAD_COL_PIN;// read column
>
Danke, das mit dem Kurzschluss bei mehr als 2 Tasten gleichzeitig habe
ich jetzt verstanden.
Ich bin der Meinung, dass man die simulierten Open Drain Ausgänge auch
mit Dioden nutzen kann. Nur andersrum, gibt es Probleme, nämlich mit
normalen Ausgängen (über PORT) und keinen Dioden...
Ich meine, dass der Code im Simulator funktioniert hätte, bin aber
gerade verunsichert und würde es gerade so machen:
Werner schrieb:> Kannst du das mit der Phantomtaste genauer erklären, wie das zustande> kommt?
Mal die Schaltung einer 2 * 2 Matrix auf und drücke 3 Tasten. Dann wird
auch die 4. Taste als gedrückt erkannt, da die 3 Tasten den Stromkreis
dafür schließen.
Hat jede Taste eine Diode in Reihe, werden nur die 3 Tasten gemeldet,
die wirklich gedrückt sind.
Ich habe die Funktion gerade mal auf meinen Cortex-M3 umgesetzt mit
einer 2x9 Matrix.
Funktioniert auch soweit wunderbar, nur fehlt mir sowas wie eine
"get_key_pressed" und "get_key_released" Funktion, die ich in der main
nutzen kann.
Also etwas, dass den Tastendruck nur einmal zurückgibt.
GetKey rufe ich im Timer alle 10ms auf und speichere die Rückgabe in
einer Variablen. Daurch evaluiert aber die Abfrage in der Main je nach
Durchlaufzeit vielfach.
Soll ich die Variable zum Zwischenspeichern einfach nach der Evaluierung
'0' setzen?
Wie macht ihr das?
Gruß
Fabian
Fabian B. schrieb:> Daurch evaluiert aber die Abfrage in der Main je nach Durchlaufzeit> vielfach.
Was könnte das bedeuten?
> nach der Evaluierung '0' setzen?
Was "evaluierst" (= "bewertest") du denn wie?
> Daurch evaluiert aber die Abfrage in der Main je nach Durchlaufzeit> vielfach.
Du könntest mal deine "Main" hier anhängen, dann könnte man die mal
evaluieren...
Hallo Lothar,
da gibts nicht anzuhängen ... die Main liest derzeit nur die Taste und
gibt sie über die serielle aus.
Hier setze ich jetzt schon keyp = 0 um eine Taste nur einmal zu
behandeln.
1
while(true){
2
if((keyp>0)){
3
printf("key_nr: %i\n\r",keyp);
4
keyp=0;
5
}
6
}
keyp selbst ist eine globale Variable (volatile uint8_t), die im
10ms-Timer mit
1
keyp=kbd_get_key();
"gefüllt" wird.
So funktioniert das auch, aber mein Frage war ja ob das so gedacht ist
oder ob's da was eleganteres gibt, was ich übersehe. Vergleichbar der
get_key Funktionen aus PeDas "normaler" Tastenentprellung.
Gruß
Fabian
Hallo Peter,
auch nach so vielen Jahren Danke für das Programm. Ich bin
Quereinsteiger und es hat ein wenig gedauert, aber auch ich bin
irgendwann durchgestiegen. Allerdngs habe ich mich auch über die
1
P0|=~0x07;
gestolpert.
Nun habe ich wg. der Pin Kurzschlüsse eine Frage.
Ich möchte die Matrix (momentan Testmatrix 3x4, später 10x8) mittels
Schieberegister realisieren. Dazu nehme ich je einen 595er und einen
165er.
Gibt es dort auch das Problem mit Kurzschlüssen unter den Pins? Sollte
ich
dann direkt mit Dioden arbeiten?
Wenn ja, welche Dioden sind dann zu empfehlen?
Gruß
Jens