Hallo geliebtes Forum!
Ich habe mich dran gesetzt eine 3x4-Tasten-Matrix an meinem XMega zu
betreiben. So weit so gut. Das Ding läuft, nur macht es nicht exakt was
ich will :(
Wenn ich eine Taste gedrückt halte werden alle Nummern der Zeile, in der
sich diese Taste befindet ausgegeben.
Halte ich z.B. die 2 gedrückt sehe ich am LCD 1, 2, 3, 1... durchlaufen.
Das liegt höchstwahrscheinlich am Vergleich in Zeile 11, der in:
if(PORTA.IN & PIN6_bm) {} aufgelöst werden sollte.
PIN6_bm ist per Definition 0x40, was binär 1000000 ergibt.
Ich glaubte also 1000xxx mit 1000000 zu verUNDen, was ich aber scheinbar
nur zu einem Drittel tue.
Weiß jemand wie's richtig geht?
Ich steh gerade ziemlich auf dem Schlauch :(
1
// GET KEYPAD BUTTONS //
2
int8_tkey_col[3]={PIN2_bm,PIN1_bm,PIN0_bm};// Definiert als OUTPUT
3
int8_tkey_row[4]={PIN6_bm,PIN5_bm,PIN4_bm,PIN3_bm};// Definiert als INPUT mit internem PULLDOWN
Danke für den Tipp!
Hab ich prompt probiert und dabei gemerkt, dass es keine gute Idee ist
die Spalten unter Spannung zu setzen, selbige dann aber nicht wieder
spannungsfrei zu schalten.
Also was den oben stehenden Code angeht:
Der kann so bleiben, braucht aber noch ein...
1
PORTA.OUTCLR=key_col[x];// Spalte X abschalten
...am Ende der "x" for-Schleife.
Dann klappt's auch mit dem UND :)
Was mich nun noch interessieren würde ist, ob das guter XMEGA-Stil ist,
ein Nummernpad so auszulesen, oder ob jemandem ein weiteres Haar in der
Suppe auffällt, nur raus damit!
Viele Grüße,
Holger
Mit solchen Formuliereungen
> for (int8_t x=0; x<=2; x++) {
bin ich immer unglücklich.
Die einfachste for-Schleife in C lautet immer
for( i = 0; i < Anzahl Durchläufe; i++ )
d.h. da steht die Anzahl der Durchläufe direkt drinnen und anstelle des
<= steht immer ein <.
Wenn diese beiden Beingungen erfüllt sind, dann ist das eine ganz
normale for-Schleife, wie man sie benutzt, um ein Array abzuarbeiten
int values[5];
for( i = 0; i < 5; ++i )
mach was mit values[i]
da braucht man nicht lange analysieren und sich überlegen ob man jetzt
hinten übers Array rausschiesst, oder ob man alle Werte hat. Das ist
sozusagen ein immer gleiches 'Muster'. Und da sich praktisch alle
C-Programmierer an dieses Muster halten, erleichtert das die Übernahme
von fremden Code. Erst wenn das Muster nicht vorhanden ist, fangen die
Glocken im Kopf zu läuten an.
Selbiges mit der Verwendung von 'magischen Konstanten' mitten im Code
und deren manchmal seltsame Querverbindungen.
Wenn du nicht musst, tus nicht!
Entweder
1
#define NR_COLS 3
2
#define NR_ROWS 4
3
ädefineNR_KEYS12// NR_COLS * NR_ROWS
4
5
int8_tkey_col[NR_COLS]={PIN2_bm,PIN1_bm,PIN0_bm};// Definiert als OUTPUT
6
int8_tkey_row[NR_ROWS]={PIN6_bm,PIN5_bm,PIN4_bm,PIN3_bm};// Definiert als INPUT mit internem PULLDOWN
Holger schrieb:> Was mich nun noch interessieren würde ist, ob das guter XMEGA-Stil ist,> ein Nummernpad so auszulesen, oder ob jemandem ein weiteres Haar in der> Suppe auffällt, nur raus damit!
Man kann sicherlich noch ein paar Dinge verbessern.
1. Du verschwendest 19 Byte RAM für Konstanten. Arrays mit Konstanten
sind im Flash-Speicher besser aufgehoben.
2. Als Schleifenvariablen könnte man direkt die Bitmaske nehmen und in
jedem Schleifendurchung shiften statt inkrementieren.
3. Die Keymap wäre also zweidimensionales Array übersichtlicher.
4. Statt der Keymap mit den Werten 0x00 bis 0x0B könnte man die Tasten
einfach mit x + 4 * y oder (falls Du Tipp 2 umsetzt mit der Veroderung
der beiden aktuellen Bitmasken) repräsentieren. Diesen Werten könnte man
dann per #define oder enum Namen geben. Das hängt aber davon ab, wie Du
die Tasten in der Anwendung nutzen willst.
Also meine Empfehlung wäre Tipp 2 und 4 umzusetzen, dann kommst Du sogar
ganz ohne Arrays aus. :)
@Karl Heinz
Da ist was dran, ab jetzt werd' ich das <= weg lassen.
Das macht die Sache echt übersichtlicher.
Konstanten könnte ich auch mehr spammen, da hast du recht.
@xfr
xfr schrieb:> Arrays mit Konstanten> sind im Flash-Speicher besser aufgehoben.
Flash kann ich noch nicht, ist aber eins meiner nächsten Ziele um mir
gewisse Dinge von einer Steckdose zur nächsten zu merken.
Das Array muss aber auch schnell verfügbar sein, ist der Flash-Speicher
dafür nicht zu langsam?
xfr schrieb:> Also meine Empfehlung wäre Tipp 2 und 4 umzusetzen, dann kommst Du sogar> ganz ohne Arrays aus. :)
Das hat super geklappt :)
Die Arrays sind weg und nur die Null-Taste muss separat betrachtet
werden.
Gefällt mir persönlich sehr viel besser als die erste Variante.
Danke euch beiden!
Eine Frage hätte ich noch:
Die Zählvariablen "x" und "y" und das Ergebnis "key_value" müssen mMn
bei jedem Funktionsaufruf neu angelegt werden.
Wäre es vielleicht schneller die im Hauptprogramm zu deklarieren?
Oder zumindest als static in der Funktion?
1
#define KEY_FIRST_COL PIN2_bm
2
#define KEY_FIRST_ROW PIN6_bm
3
#define KEY_NUM_ROWS 4
4
#define KEY_NUM_COLS 3
5
6
uint8_tget_key_matrix(){
7
intkey_value=0x0F;// Ungültiger Wert
8
for(int8_tx=0;x<KEY_NUM_COLS;x++){
9
PORTA.OUTSET=KEY_FIRST_COL>>x;// Spalte x unter Spannung setzen
10
for(int8_ty=0;y<KEY_NUM_ROWS;y++){
11
if(PORTA.IN&KEY_FIRST_ROW>>y){// Reihe y abfragen
12
key_value=y*KEY_NUM_COLS+x+1;// Wert der x-y-Koordinate berechnen
13
if(key_value==0x0B)key_value=0x00;// Taste NULL
14
}
15
}
16
PORTA.OUTCLR=KEY_FIRST_COL>>x;// Spalte x abschalten
Holger schrieb:> Eine Frage hätte ich noch:> Die Zählvariablen "x" und "y" und das Ergebnis "key_value" müssen mMn> bei jedem Funktionsaufruf neu angelegt werden.
Sofern der Compiler es nicht hinkriegt, die lokalen Variablen überhaupt
in Registern zu halten, bedeutet 'neu angelegt werden' im Regelfall: Der
Stackpointer wird mit einer konstanten Zahl subtrahiert oder (beim
return addiert). D.h. das 'Anlegen' ist im Grunde pipifax und nicht
wert, dass man sich da gross Gedanken drüber macht. (Soferns nicht
hunderte sind, die die Stackbelastung in die Höhe treiben)
> Wäre es vielleicht schneller die im Hauptprogramm zu deklarieren?
In dem Fall nicht.
Diese Variablen haben so einen dermassen großen lokalen Bezug (und nur
lokalen Bezug), dass ich die auf jeden Fall in der Funktion lassen
würde. Dort sind sie lokal und ich brauch mir keine Gedanken darüber
machen, wenn eine andere Funktion ebenfalls Variablen x und y hat und
sich da dubiose Zusammenhänge über mehrere Funktionsaufrufebenen hinweg
ergeben können.
> Oder zumindest als static in der Funktion?
Könnte man machen. Ich würds aber trotzdem nicht tun. Das sind eindeutig
lokale Variable, die nur während der Funktionsausführung eine Bedeutung
haben. Weder davor noch danach noch beim nächsten Funktionsaufruf
spielen ihre Werte irgendeine Rolle. Ein 'static' würde sie für meinen
Geschmack zu sehr betonen.
Und im übrigen gilt (wie immer):
Hast du ein Problem mit der Funktion? Ist sie zu langsam? Hast du
gemessen, dass sie zu langsam ist?
Wenn nein: dann lass sie so wie sie ist und leg dein Hauptaugenmerk auf
die Korrektheit, Sauberkeit und Wartbarkeit der Funktion.
Getreu dem Motto:
* optimiere nicht, wenn du es nicht musst (ausgenommen natürlich
Programmiertechniken die unter das Thema 'allgemeine gute
Programmierrichtlinien' fallen. Also jetzt nicht absichtlich
blödsinnig ineffizienten Code)
* optimiere nicht auf Verdacht. Erst mal messen, wie groß das
Einsparungspotential einer Funktion ist. Durch Messen hat
schon so mancher sein blaues Wunder erlebt.
* eine langsame Funktion, die korrekt arbeitet, ist immer noch
nützlich
eine schnelle Funktion, die falsche Ergebnisse liefert, ist
hingegen niemals nützlich - egal wie schnell sie ist.
Die Reihenfolge lautet daher: erst korrekt, dann schnell.
Und nicht umgekehrt.
Ich hatte das mit dem Shiften noch etwas anderes gemeint. Du kannst
nämlich auch direkt die Bitmaske als Schleifenvariable benutzen. Ich
habe es mal schnell umgeschrieben. Keine Garantie, dass die #defines so
stimmen, aber das Prinzip sollte klar sein:
1
#define COL_FIRST PIN0_bm
2
#define COL_LAST PIN2_bm
3
#define ROW_FIRST PIN3_bm
4
#define ROW_LAST PIN6_bm
5
6
#define KEY_0 (PIN1_bm | PIN3_bm)
7
#define KEY_1 (PIN2_bm | PIN6_bm)
8
#define KEY_2 (PIN2_bm | PIN6_bm)
9
[...]
10
#define KEY_B (PIN0_bm | PIN3_bm)
11
12
uint8_tget_key_matrix()
13
{
14
intkey_value=0x0F;// Ungültiger Wert
15
for(uint8_tcol=COL_FIRST;col<=COL_LAST;col<<=1){
16
PORTA.OUTSET=col;// Aktuelle Spalte unter Spannung setzen
17
for(uint8_trow=ROW_FIRST;row<=ROW_LAST;row<<=1){
18
if(PORTA.IN&row){// Reihe y abfragen
19
key_value=row|col;// Wert zuweisen
20
}
21
}
22
PORTA.OUTCLR=col;// Aktuelle Spalte abschalten
23
}
24
returnkey_value;
25
}
Im Hauptprogramm könntest Du es dann z.B. so auswerten:
1
uint8_tkey=get_key_matrix();
2
switch(key){
3
caseKEY_0:
4
[...]
5
caseKEY_1:
6
[...]
7
}
Das schöne darin ist, dass man die Berechnungen auf ein Minimum
beschränkt. Man braucht nichts addieren oder multiplizieren, nur einmal
pro Schleifengang shiften. Wenn es Dir zu ungewohnt aussieht gilt aber
was Karl Heinz sagt: Im Zweifel lieber die verständlichere Variante als
die schnellere. Der Mikrocontroller dürfte für beide schnell genug sein.
Und wenn Dir wichtig ist, dass der Variableninhalt dem Wert der Tasten
entspricht, musst Du natürlich auch Deine Variante nehmen.
Variablen im Flashspeicher ablegen ist übrigens nicht schwer. Du musst
nur avr/pgmspace.h inkludieren, PROGMEM vor die Variablendefinition
schreiben und z.B. per pgm_read_byte() darauf zugreifen. Langsam ist das
nicht. Der Programmcode liegt ja schließlich auch im Flash. Um sich
Dinge "von einer Steckdose zur nächsten" zu merken, ist der
Flashspeicher aber nicht gedacht, dafür gibt es das EEPROM.