PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 4*4 Tastatur (Matrix) an tiny2313



Ineedhelp
23.07.2008, 16:48
HI
habe mir gedacht, ich hänge jetzt mal eine alte 4*4 Matrix Tastatur an meinen Tiny2313 und gebe die Zeichen dann mit der LCD Library von Peter Fleury auf meinem Disply aus.

Meine ersten Überlegungen:
Ich habe gehört, dass man diese Tastaturen mit einer Schleife Zeile für Zeile abfragen muss. Also habe ich mir folgendes überlegt.
Ich lege die 4 Zeilen auf jeweils einen Pin des Tinys (mit 470ohm Widerstand) und definiere sie als Ausgang. Das selbe mach ich mit den 4 Spalten und definiere sie als Eingang.
Jetzt setze ich zu Anfang der Abfrage die erste Zeile auf high und die anderen Zeilen auf low. Jetzt überprüfe ich ob an einem der Eingänge (Spalten) ein high Signal ankommt. Ist dies der Fall, kann ich das zeichen zuordnen und gebe es auf meinem Display aus. Sonst setze ich die Spalte auf low, setze die nächste auf high und schaue ob dort ein Eingang auf high schaltet.
So durchlaufe ich dann die Tastatur (die ganze Zeit).

Funktioniert das so? oder gibt es da noch Tipps oder bessere Ansätze?
Danke
Achja, nachdem der Ansatz fertig ist mache ich mich an den Code und liefere ihn zur überprüfung nach (optimale vorgehensweise, fehler usw)

sternst
23.07.2008, 17:36
Das grundsätzliche Prinzip stimmt. Im Detail aber verbesserungswürdig. ;-)


Jetzt setze ich zu Anfang der Abfrage die erste Zeile auf high und die anderen Zeilen auf low.
Würde ich auf keinen Fall so machen. Wenn es nämlich eine sehr einfache Matrix ohne Dioden ist, kannst du dann durch Drücken von zwei Tasten sehr leicht einen Kurzschluss zwischen einem High- und einem Low-Ausgang produzieren. Deine Serienwiderstände verhindern dann zwar Schlimmeres, aber du hast an den Spalteneingängen keinen eindeutigen Pegel mehr.
Besser:
Alle Zeilenausgänge standardmäßig hochohmig (also als Eingang konfigurieren), und dann immer nur eine Zeile zur Zeit auf Ausgang stellen.

Noch zu beachten:
An den Spalteneingängen brauchst du zwingend Pull-Up/Down-Widerstände, weil die Eingänge ohne gedrückte Taste sonst "in der Luft hängen" würden.

Wie ich es machen würde:
Serienwiderstände an den Ausgängen weglassen. An den Eingängen die internen Widerstände nutzen. Da das Pull-Ups sind, muss der eine "eingeschaltete" Ausgang auf Low sein, und die Eingänge sind dann auch auf Low zu prüfen.

sternst
23.07.2008, 17:45
Nachtrag:


So durchlaufe ich dann die Tastatur (die ganze Zeit).
Auch das würde ich so nicht machen. Zum Entprellen der Tasten wäre es das Beste, die Tastenabfrage nicht die ganze Zeit durchlaufen zu lassen, sondern nur in größeren Zeitabständen (z.B. alle 50 ms).

Ineedhelp
23.07.2008, 18:20
danke für deine hilfe
macht sinn, allerdings glaube ich, dass cih noch nicht alles genau verstanden habe

daher nochmal:
So, ich schalte erstmal alle meine Zeilen auf Eingang (ohne Widerstände), dann meine Spalten auch auf Eingang (mit internen Widerständen).
Nun fängt meine Schleife an und es wird die erste Zeile zuerst auf Ausgang und low gesetzt, wenn nun eine Taste gedrückt wird bekomme ich einen low auf dem "geschalteten" Eingang und kann so meine Taste identifizieren.
Sonst schalte ich die Zeile wieder auf Eingang und die nächste auf Ausgang.

So habe ich es jetzt verstanden, allerdings bin ich ein bisschen irritiert, da ja nirgends Strom fließt
danke nochmals

sternst
23.07.2008, 18:47
So habe ich es jetzt verstanden,
Jap, genau so.


allerdings bin ich ein bisschen irritiert, da ja nirgends Strom fließt
Aber klar doch. Bei gedrückter Taste fließt Strom von VCC über den internen Pull-Up des Spalteneingangs in den Low-Zeilenausgang hinein nach GND.

Ineedhelp
23.07.2008, 20:06
okay danke

jetzt habe ich dazu mal einen Code aufgesetzt
die Spalten sind PD0 bis PD3 und die Zeilen sind PD4 bis PD6 (momentan nur 4*3 Matrix)

wäre nett, wenn da mal jemand drüberschaut (noch nicht getestet, compiliert aber fehlerfrei)
vllt fällt ja was auf oder es gibt vorschläge zur optimierung



void tastaturabfrageLow()
{
DDRD = 0x00; /* PD als Eingaenge */
//DDRA &= ~(1<<PA1) ;
PORTD |= (1<<PD4) | (1<<PD5) | (1<<PD6); /* Interne Pull-Up einschalten */
//PORTA |= (1<<PA1);

while(1)
{
//Zeile A
DDRD &= ~( 1 << DDD3 ); //alten Ausgang wieder auf Eingang schalten
PORTD |= (1<<PD3); //Widerstand aktivieren
DDRD = (1 << DDD0); //neuen Ausgang definieren
PORTD &= ~(1<<DDD0); //neuen Ausgang auf low schalten

if ( !(PIND & (1<<PINB4)) ) // Spalte 1 prüfen
{
lcd_clrscr();
lcd_puts("7");
}
if ( !(PIND & (1<<PINB5)) ) // Spalte 2 prüfen
{
lcd_clrscr();
lcd_puts("8");
}
if ( !(PIND & (1<<PINB6)) ) // Spalte 3 prüfen
{
lcd_clrscr();
lcd_puts("9");
}

//Zeile B
DDRD &= ~( 1 << DDD0 ); //alten Ausgang wieder auf Eingang schalten
PORTD |= (1<<PD0); //Widerstand aktivieren
DDRD = (1 << DDD1); //neuen Ausgang definieren
PORTD &= ~(1<<DDD1); //neuen Ausgang auf low schalten

if ( !(PIND & (1<<PINB4)) ) // Spalte 1 prüfen
{
lcd_clrscr();
lcd_puts("4");
}
if ( !(PIND & (1<<PINB5)) ) // Spalte 2 prüfen
{
lcd_clrscr();
lcd_puts("5");
}
if ( !(PIND & (1<<PINB6)) ) // Spalte 3 prüfen
{
lcd_clrscr();
lcd_puts("6");
}

//Zeile C
DDRD &= ~( 1 << DDD1 ); //alten Ausgang wieder auf Eingang schalten
PORTD |= (1<<PD1); //Widerstand aktivieren
DDRD = (1 << DDD2); //neuen Ausgang definieren
PORTD &= ~(1<<DDD2); //neuen Ausgang auf low schalten

if ( !(PIND & (1<<PINB4)) ) // Spalte 1 prüfen
{
lcd_clrscr();
lcd_puts("1");
}
if ( !(PIND & (1<<PINB5)) ) // Spalte 2 prüfen
{
lcd_clrscr();
lcd_puts("2");
}
if ( !(PIND & (1<<PINB6)) ) // Spalte 3 prüfen
{
lcd_clrscr();
lcd_puts("3");
}

//Zeile D
DDRD &= ~( 1 << DDD2 ); //alten Ausgang wieder auf Eingang schalten
PORTD |= (1<<PD2); //Widerstand aktivieren
DDRD = (1 << DDD3); //neuen Ausgang definieren
PORTD &= ~(1<<DDD3); //neuen Ausgang auf low schalten

if ( !(PIND & (1<<PINB4)) ) // Spalte 1 prüfen
{
lcd_puts("0");
}
if ( !(PIND & (1<<PINB5)) ) // Spalte 2 prüfen
{
lcd_puts(".");
}
if ( !(PIND & (1<<PINB6)) ) // Spalte 3 prüfen
{
lcd_clrscr();
}
}
}


lcd_puts() und lcd_clrscr() sind von der LCD lib

Danke

sternst
23.07.2008, 20:59
In die Funktion eine Endlosschleife zu legen ist keine so gute Idee. Du bist sehr viel flexibler, wenn die Funktion nur einmal die Tasten abfragt. Wenn das dann unbedingt in einer ungebremsten Endlosschleife geschehen soll, dann lege sie außen rum, also z.B. in main:

while (1)
tastaturabfrageLow();
Ich weise allerdings nochmal darauf hin, dass du so einige Probleme mit prellenden Tasten haben wirst. Außerdem beinhaltet dein Programm im Augenblick ein sehr schnelles Autorepeat (zusätzlich zum Prellen). Also Taste 7 einmal kurz gedrückt und gleich 10mal (oder so) eine '7' auf dem Display.

Da ich eigentlich gerade Lust zum Programmieren habe, mache ich mir mal ein paar Gedanken und vielleicht später dann einen Alternativvorschlag.

Ineedhelp
23.07.2008, 21:20
danke
sehr nett von dir

habe den gerade getestet, läuft super

allerdings muss ich die taste kurz über 1 sek gedrückt halten bis die zahl auf dem bildschirm erscheint

liegt aber wohl dadran, dass ich in jeden spaltenwechsel 2 delays von 20ms eingefügt habe

sternst
23.07.2008, 21:35
Also Taste 7 einmal kurz gedrückt und gleich 10mal (oder so) eine '7' auf dem Display.
Sorry, stimmt natürlich nicht, da du jedesmal das LCD löscht.


liegt aber wohl dadran, dass ich in jeden spaltenwechsel 2 delays von 20ms eingefügt habe
Falscher Platz für Delays.

Ineedhelp
23.07.2008, 21:48
hatte ich bei den punkten und der 0 aber auch nicht
und die wurden nicht gelöscht

auf jeden fall wollte ich gerade eine version ohne delays und lcd löschvorgang testen

mal schauen

Ineedhelp
23.07.2008, 21:50
ohh
du hast recht
kurz drauf und die ganze zeile ist voll mit der zahl

sternst
23.07.2008, 22:14
Ok, hier mein (im Simulator getesteter) Alternativvorschlag:

Bei dir divergieren Text und Code bezüglich Spalten und Zeilen. Ich lege es für meinen Code wie folgt fest:
Die oberen 4 Pins (PD4-PD7) sind die Ausgänge (Zeilen), die unteren 4 (PD0-PD3) die Eingänge (Spalten).
(ich glaube, bei dir ist es gerade anders herum)

Der Code funktioniert ungeändert für 4x4 und 4x3, bei 4x3 bleibt einfach nur einer der Eingänge offen.

Das keys-Array enthält im Augenblick nur Platzhalter und muss natürlich angepasst werden. Einfach Taste drücken, schauen welcher Buchstabe kommt, und den Buchstaben im Array durch das für die Taste gewünschte Symbol ersetzen.


void tastaturabfrageLow() {

static const uint8_t keys[16] = {'a','b','c','d',
'e','f','g','h',
'i','j','k','l',
'm','n','o','p'};
static uint8_t last_key = 0;

uint8_t cur_key;
uint8_t line, row;

DDRD = 0x10;
PORTD = 0x0f;

for (line=0;line<4;line++) {
cur_key = ~PIND & 0x0f;
row = 0;
while (cur_key) {
row++;
cur_key >>= 1;
}
if (row) {
cur_key = keys[line*4+row-1];
if (cur_key != last_key) {
lcd_putc(cur_key);
last_key = cur_key;
return;
}
}
DDRD <<= 1;
}
last_key = 0;
}

Und in main dann:

while(1) {
tastaturabfrageLow();
_delay_ms(50);
}

Ineedhelp
23.07.2008, 22:39
riesiges dankeschön
allerdings habe ich da noch eine frage
wie heißt die software mit der man sowas simulieren kann?
würde ja doch einiges vereinfachen ;-)

danke nochmal

sternst
23.07.2008, 22:45
wie heißt die software mit der man sowas simulieren kann?
AVR-Studio von Atmel. Gibt es aber leider nur für Windows.