PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 4x3 matrix tastatur abfragen in C!



papuadive
23.11.2008, 10:59
hallo...
kann mir jemand auf die sprünge helfen, nämlich wie fragt man eine 4x3 matrix tastatur ab mit ziffernauswertung damit ich die gedrückten tasten auf ein lcd display anzeigen kann.
weiters weiss ich nicht ob ich mit polling od. es mit den interrupt's angehen muss?
wie löse ich das problem mit dem tasterprellen.
bin in c-noch ein anfänger.
liegt vielleicht irgendwo ein code herum, zum abfragen einer 4x3 matrix tastatur mit auswertung...
ich verwende das avr studio in kombination mit winAVR
danke papua

nil.at
23.11.2008, 11:16
Hallo,

erstmal eine Frage: Groß- Kleinschreibung ist ein Begriff?

Tipp von meiner Seite: Besorg dir das Buch "Roboter selbst gebaut" von Ulli Sommer, da hast du genau deine Fragen beschrieben.
Die Matrixauswertung machst du am einfachsten mit einfachen Widerständen in Serie die sich addieren. Dann brauchst du nur einen A/D-Wandler Anschluss, greifst die anliegende Spannung ab, und schaust anhand einer Referenz (die du natürlich zuerst anlegen musst - durch einfaches ausprobieren und Werte zuweisen) nach, welche Taste gedrückt wurde.

Auch die Frage mit der Entprellung wird im Buch gelöst: Einfach den Tastenwert öfters hintereinander abfragen, um sicherzugehen, dass die Taste tatsächlich gedrückt wurde.

lorcan
23.11.2008, 11:42
http://users.etech.haw-hamburg.de/users/ITLabor/MP_Lab_R880/Laboraufgaben_MP_Lab/Nr_0_Einf.-MP_Lab.pdf
Hoffe das hilft.

Gruß

Lorcan

wkrug
24.11.2008, 07:33
Ich hab das bei einer 4x4 Matrixtastatur so gemacht, das ich auf jeweils einer Spalte über einen Controllerport ein "low" ausgegeben habe.
In den Zeileh hab ich dann die Ports abgefragt, welche Spalte - oder auch keine - Ein "low" anliegen hat.
Danach wurde die aktuelle Spalte wieder auf "high" gelegt und an die nächste wieder ein "low" angeschaltet.
An die Spalten Eingänge sollten Pullups angeschaltet werden, wenn die Verbindungen zum Controller kurz sind, kann man auch die internen des Controllers verwenden.

Durch die Abfrage wird dann eine Variable beschrieben, die sinnvollerweise dem Wert der gedrückten Taste entspricht.
Danach wird eine kurze Zeit gewartet delay_ms(10); und das gleiche Spielchen mit einer anderen Variable wiederholt.

Danach vergleicht man beide Variablen.
Sind sie gleich kann man davon ausgehen, das auch wirklich die entsprechende Taste gedrückt wurde. Somit wäre auch die Entprellung erledigt.

Eventuell muß man nach dem Umschalten der Zeilen- Leitung ein wenig warten, bis sich die parasitären Kapazitäten umgeladen haben um korrekte Ergebnisse zu erhalten.
Das kann man mit #asm("NOP"); oder dely_us(x); machen.

Ich bin der Meinung, das für eine Tastaturabfrage das Polling schnell genug ist, wenn es im Hauptprogramm oft genug ausgeführt wird.

papuadive
24.11.2008, 09:55
super Erklärung,....
verstehe es jetzt...
vG Papua

papuadive
24.11.2008, 10:02
danke nochmals....
frage: Hast du zufällig den Code noch?
vG Papua

thewulf00
24.11.2008, 11:49
Eine Frage @wkrug:

Bei Deiner Methode besteht aber das Problem, dass man beim Drücken von 2 Tasten in der selben Reihe einen Kurzschluss verursacht, korrekt?

papuadive
24.11.2008, 12:36
%...ja, das stimmt, aber wie löse ich dann das Problem....?
Gibt's da nicht schon eine Header-Datei.....?
Danke
Papua

wkrug
24.11.2008, 13:15
Bei Deiner Methode besteht aber das Problem, dass man beim Drücken von 2 Tasten in der selben Reihe einen Kurzschluss verursacht, korrekt?
Wenn ich das genau überlege hast Du recht.
Da muss ich an meine Soft wohl noch mal ran.
Ich geb GND Potential auf eine Spalte, eine andere führt +5V.
Werden dann 2 Tasten in der selben Zeile gleichzeitig gedrückt kommt es zu einem Kurzschluß.
Das könnte man aber verhindern, indem man an die Spaltenausgänge des Controllers Dioden in Richtung Port schaltet.
Dadurch wird verhindert, das aus dem Port der auf +5V hängt ein Stromfluß in Richtung GND möglich ist.

Ein Widerstand am Port Ausgang ~220Ohm sollte das Problem auch effektiv verhindern.

Die Zeilen werden ja durch den Pullup ohnehin auf +5V gehalten.

Eine Softwaremässige Lösung wäre nur das DDR Register des entsprechenden Ports auf Output zu schalten und alle anderen Spalten als Input zu definieren.
Dadurch würden alle inaktiven Ports hochohmig und können kein Problem mehr verursachen.

thewulf00
24.11.2008, 14:13
Oder Einfach alle auf Eingang schalten, mit PullUp, und nur eine Reihe auf Ausgang?

wkrug
24.11.2008, 18:55
Oder Einfach alle auf Eingang schalten, mit PullUp, und nur eine Reihe auf Ausgang?
Und diesen Ausgang schaltet man nach low (GND) damit man die internen Pullups nutzen kann.



// Port C wäre der Tastaturport.
// Portc.6 = Spalte 1
// Portc.5 = Spalte 2
// Portc.4 = Spalte 3

// Initialisierung
...
PORTC=0b01111111;
DDRC=0b00000000;
...

// Tastaturabfrage:
unsigned char read_tast(void)
{
taste1=15; // Tastenparameter vorbelegen -> keine Taste gedrückt
taste2=15;
PORTC.6=0; // Spalte 1 auf low
DDRC.6=1;
uc_tastabfr1=PINC&0b00001111; // Zeilen 1...4 ausfiltern

switch (uc_tastabfr1)
{
case 0b00001110:
taste1=1;
break;
case 0b00001101:
taste1=2;
break;
case 0b00001011:
taste1=3;
break;
case 0b00000111:
taste1=4;
break;
default:
};

PORTC.6=1; // Spalte wieder auf high
DDRC.6=0;
PORTC.5=0; // Spalte auf low
DDRC.5=1;
uc_tastabfr1=PINC&0b00001111; // Zeilen 1...4 ausfiltern
switch (uc_tastabfr1)
{
case 0b00001110:
taste1=5;
break;
case 0b00001101:
taste1=6;
break;
case 0b00001011:
taste1=7;
break;
case 0b00000111:
taste1=8;
break;
default:
};

PORTC.5=1; // Spalte wieder auf high
DDRC.5=0;
PORTC.4=0; // Spalte auf low
DDRC.4=1;
uc_tastabfr1=PINC&0b00001111; // Zeilen 1...4 ausfiltern
switch (uc_tastabfr1)
{
case 0b00001110:
taste1=9;
break;
case 0b00001101:
taste1=10;
break;
case 0b00001011:
taste1=11;
break;
case 0b00000111:
taste1=12;
break;
default:
};
PORTC.4=1; // Spalte wieder auf high
DDRC.4=0;

delay_ms(10);

// 2te Tastaturabfrage:
PORTC.6=0; // Spalte 1 auf low
DDRC.6=1;
uc_tastabfr1=PINC&0b00001111; // Zeilen 1...4 ausfiltern

switch (uc_tastabfr1)
{
case 0b00001110:
taste2=1;
break;
case 0b00001101:
taste2=2;
break;
case 0b00001011:
taste2=3;
break;
case 0b00000111:
taste2=1;
break;
default:
};

PORTC.6=1; // Spalte wieder auf high
DDRC.6=0;
PORTC.5=0; // Spalte auf low
DDRC.5=1;
uc_tastabfr1=PINC&0b00001111; // Zeilen 1...4 ausfiltern
switch (uc_tastabfr1)
{
case 0b00001110:
taste2=5;
break;
case 0b00001101:
taste2=6;
break;
case 0b00001011:
taste2=7;
break;
case 0b00000111:
taste2=8;
break;
default:
};

PORTC.5=1; // Spalte wieder auf high
DDRC.5=0;
PORTC.4=0; // Spalte auf low
DDRC.4=1;
uc_tastabfr1=PINC&0b00001111; // Zeilen 1...4 ausfiltern
switch (uc_tastabfr1)
{
case 0b00001110:
taste2=9;
break;
case 0b00001101:
taste2=10;
break;
case 0b00001011:
taste2=11;
break;
case 0b00000111:
taste2=12;
break;
default:
};
PORTC.4=1; // Spalte wieder auf high
DDRC.4=0;

if(taste1==taste2)
{
return (taste1);
}
else
{
return(255);
};

Ich hab den Code schnell mal aus dem Kopf zusammengezimmert.
Es kann also sein das noch ein paar Fehler drin sind.
Ich meine aber das Prinzip ist erkennbar und somit für Dich umzusetzen.

thewulf00
25.11.2008, 07:20
Somit hast Du Deinen Ansatz sogar noch perfektioniert. Ich wollte Dir nicht "ans bein pinkeln", sondern lediglich meine Bedenken äußern. Und Du hast sie wunderbar umschifft. Gratuliere!

wkrug
25.11.2008, 07:40
Ich wollte Dir nicht "ans bein pinkeln", ....
So hab ich das auch nicht aufgefasst.
Ich hab das mit dem Umschalten auf "LOW" bei meinem Projekt, ohne die Umschaltung der DDR Register, tatsächlich gemacht und bin froh, das ich da jetzt auch eine potentielle Fehlerquelle ausmerzen kann. =D>
Aber es ist so wie ich schon öfter mal sagte:
"Man lernt mit jedem Beitrag den man hier verfasst / beantwortet selber was dazu".

papuadive
25.11.2008, 13:30
Hallo Roboter Genie,
vielen Dank für deine Bemühungen....
Werde es gleich mal versuchen
Danke nochmals
PS: Gibt's da nicht schon fertige Header Datei. Denn die Sprache C ist ja weit verbreitet. Für die LCD, Delay..., I/O's gibt es sie ja auch?

wkrug
25.11.2008, 16:47
Gibt's da nicht schon fertige Header Datei.
Möglich, ich würde mir das Prog aber an Deiner Stelle selber schreiben.
Erstens ist es nicht sonderlich kompliziert.
Zweitens kannst Du fast jeden beliebigen Controllerpin für die Funktionen auswählen.
Drittens können auch noch andere Funktionen ( z.B. eine A/D Wandlerabfrage ) in den Quellcode mit eingefügt werden.
Viertens hast Du volle Kontrolle über die Funktionalität des Quellcodes.

Das ist bei fertigen Library's nicht möglich.

thewulf00
26.11.2008, 07:56
...Fünftens ist eine Einarbeitung in die Benutzung einer Library manchmal nicht ohne.
...Sechstens haben auch Libs Fehler, die Dir viel Frust bringen können.
...Siebtens ist eine Library i.d.R. für allgemeine Lösungen und/oder viele verschiedene Hardware gedacht, so dass Du für Deinen Anwendungsfall mglw. zu viel Code mit reinbekommst.

papuadive
29.11.2008, 07:56
Hallo....!
Wie fügt man eigentlich Assembler-Befehle in einem C- Code ein?
Denn ich möchte einen Wert, den ich mittels Tastendruck ermittle, in ein allgemeines Regíster, zB: r16 (bzw. ins EE-PROM) bringen.
wie sieht das in C aus
wie in Assembler aus (unter C)
Ich versuche es im AVR-Studion (WinAVR)
danke für deine (eure) Hilfe
papua

wkrug
29.11.2008, 08:18
Bei winavr weiß ich nicht wie inline Assembler geht.

Bei CodeVision AVR siehts so aus

// Ein einzelner Befehl
#asm("NOP");

// Mehrere Zeilen inline Assembler
#asm
NOP
SEI
LDI R16,0x12
#endasm

Wie's geht sollte eigentlich im Benutzerhandbuch stehen.
Die Werte, die die Tastaturroutine ermittelt geb ich immer per RETURN zurück.
Also am Ende der Funktion steht die Anweisung
return(uc_taste);

Somit kriegt das aufrufende Programm die Tastaturwerte der Routine zurück geliefert und kann diese in einer eigenen Variable weiter verarbeiten.
Ich mach auch meistens in die aufrufende Routine eine zweite Variable mit dem Namen uc_oldtast auf.
Die beinhaltet den Wert der vorherigen Tastenabfrage.
Nur wenn sich daran was ändert wird eine Auswerte- Steuer- oder sonstige Routine verarbeitet.
Wenn die Ausführungsroutine verarbeitet wurde wird der aktuelle Tastenzustand ( uc_tast ) in die Variable uc_oldtast übernommen und somit verhindert, das die Auswerteroutine ständig anläuft.

magic33
30.11.2008, 15:34
also man kann das auch direkt machen indem man
2 Reihen blockiert und dann den pin statusregister auslist


uint8_t taste_lesen()
{
uint8_t keys = 0;
char wert ='-';
PORTD=0x03;
_delay_us( 10 );
keys = PIND;
if (keys == 115)
wert ='3';
if (keys == 179)
wert ='6';
if (keys == 211)
wert ='9';
if (keys == 227)
wert ='#';
PORTD=0x05;
_delay_us( 10 );
keys = PIND;
if (keys == 117)
wert ='2';
if (keys == 181)
wert ='5';
if (keys == 213)
wert ='8';
if (keys == 229)
wert ='0';
PORTD=0x06;
_delay_us( 10 );
keys = PIND;
if (keys == 118)
wert ='1';
if (keys == 182)
wert ='4';
if (keys == 214)
wert ='7';
if (keys == 230)
wert ='*';
keys =wert;
_delay_us( 10 );
return keys;
}

wkrug
02.12.2008, 12:17
@magic33
Das ist im Prinzip das gleiche wie in meinem Quellcode.
Wobei bei Deiner Variante wieder das Kurzschlußproblem bei 2 gleichzeitig gedrückten Tasten auftritt, wenn es nicht durch Hardwaremassnahmen verhindert wird.


Denn ich möchte einen Wert, den ich mittels Tastendruck ermittle, in ein allgemeines Regíster, zB: r16 (bzw. ins EE-PROM) bringen.

Mit dem direkten Zugriff auf die Register musst Du vorsichtig sein.
Die meisten Register werden durch den C-Compiler belegt und dürfen nicht so ohne weiteres verändert werden.
Auskunft sollte auch hier das Handbuch des Compilers geben.

Der Zugriff aufs EEPROM wird in Codevision schon bei der Initialisierung der Variable gemacht.

eeprom unsigned int ui_test=0;

ui_test=ui_variable; // Schreibt den Wert von ui_variable ins eeprom.
Bei Win AVR hilft nur nachschauen.

Mit dem Beschreiben des EEPROM's solltest Du aber sehr sparsam umgehen.
1. Dauert es ziemlich lange.
2. Ist nur eine beschränkte Anzahl von Schreibvorgängen möglich. Bei den neueren Typen sind es 100.000 soweit ich weiß. Das steht aber im jeweiligen Datenblatt.