Hallo zusammen, ich benötige mal wieder die geballte Kompetenz des Forums für den Anschub zu einer Problemlösung: Es ist eine Tastatur mit 32 Tasten auszuwerten. Die Daten kommen über eine Schnittstelle (geht also nicht um das Thema Tastenentprellung) und liegen in einem Array mit 4 Bytes vor, jedes Byte für 8 Tasten. Gedrückte Tasten sind logisch 1. Die Auswertung wird zyklisch durchlaufen, wird auf eine Statusmaschine hinauslaufen. Drei Zustände der 32 Tasten sind zulässig, keine Taste gedrückt (das ist trivial), eine Taste gedrückt (das ist auch noch trivial) und zwei Tasten gedrückt. Das ist der Fall, der mich beschäftigt. Hier ist zu ermitteln, welche der beiden Tasten als erste gedrückt wurde und welche die zweite war. Beispielsweise soll die Reihenfolge Taste 1 + Taste 5 eine andere Reaktion als Taste 5 + Taste 1 bewirken. In ASM hätte ich jetzt die 4 Bytes nacheinander durch das Carry rotieren und einen Zähler mitlaufen lassen und mir bei jedem 1-Bit die Anzahl hochgezählt und die Position gemerkt. Wie fängt man das unter C jetzt am besten an? Muss ich mich mit Inline-ASM auseinandersetzen oder wie geht das performant zu lösen? Danke für alle Hinweise Thomas
Thomas P. schrieb: > In ASM hätte ich jetzt die 4 Bytes nacheinander durch das Carry rotieren > und einen Zähler mitlaufen lassen und mir bei jedem 1-Bit die Anzahl > hochgezählt und die Position gemerkt. Wie fängt man das unter C jetzt am > besten an? Muss ich mich mit Inline-ASM auseinandersetzen oder wie geht > das performant zu lösen? Da machst du eine Schleife, in der du ein Bit durch die Gegend schiebst und bitweise und verknüpfst. Das kannst du dann Zählen. Ist im Grunde das gleiche, wie dein ASM Vorschlag.
Thomas P. schrieb: > Wie fängt man das unter C jetzt am besten an? Genau so wie unter ASM: Bei der ersten erkannten Taste anhalten, warten bis sie losgelassen wird, und wenn man dann an der zweiten vorbeikommt ist das halt die nächste gedrückte (n-Key-rollover). Schwieriger wird es, wenn das Niederdrücken der zweiten Taste genau zu dem Zeotpunkt zum aussenden des Codes führen soll, denn man kann sich zwar bei der ersten Taste merken daß sie gedrückt ist und so lange sie gedrückt ist sie nicht nochmal als ausgelöst betrachten, aber wenn man dann die zweite Taste findet, könnte ja genau so gut die dritte, vierte und fünfte gedrückt werden. Irgendwann reicht aber der Speicherplatz für die schon gedrückten nicht mehr aus, man muss also die Tasten die gedrückt werden während die erste niedergehalten und gespeichert wird anders behandeln, nämlich nach der ersten alle wie oben (n-Key-rollover).
Michael B. schrieb: > Irgendwann reicht aber der Speicherplatz für die schon gedrückten nicht > mehr aus, ... µCs mit so wenig Speicher gibt es noch? ;-) Bei einem 32-Tasten Keyboard sollte spätestens mit der 32ten Taste Schluss sein. Das ist vielleicht nicht elegant, aber endlich.
Danke für die Antworten. Mir ist da noch eine Unschärfe aufgefallen. Der Zustand 2 gedrückte Tasten meint immer, dass die erste Taste gedrückt und festgehalten wird und dann die zweite dazu gedrückt wird. Wird die erste Taste vor dem Drücken der zweiten Taste wieder losgelassen, geht alles wieder von vorne los. Das Prinzip umzudrehen und eine Maske über die Variable zu schieben, ist eine Idee. Gibt aber gleich noch ein neues Problem. Ich habe versucht, eine uint32_t-Variable mit dem Arrayinhalt zu füllen: uint8_t gzio_ebuf[10]; uint32_t tastdata; uint32_t mask; mask = 1; tastdata = gzio_ebuf[1] | gzio_ebuf[2] << 8 | gzio_ebuf[3] << 16 | gzio_ebuf[4] << 24; Gibt eine zweifache Warnung vom Compiler mit ../gzio.c:422:7: warning: left shift count >= width of type [enabled by default] und ich verstehe mal wieder nicht, warum? 8 Bit 24 mal links verschieben sollte doch ein uint32_t nicht sprengen? Ist heute nicht mein Tag.... Thomas
Entweder Du oder ich haben Dein Problem nicht verstanden. Geht es Dir nur darum, zu ermitteln welche der 32 Bits gesetzt sind? Für 32-Bit gibt es perfekte Routinen von K&R aus den 70ern, um das nächste gesetzte Bit zu finden. Oder hast Du ein Problem, wenn z.B. das letzte Telegramm kein Bit gesetzt hatte, und das neue nun Bit 1 und 5? Dafür gibt es natürlich keine Lösung. Und wer garantiert Dir eigentlich, dass es nur maximal 2 Tasten sind? Und das die in sinnvoller Reihenfolge kommen? Und warum tritt man dem Designer der Gegenstelle nicht einfach in den Hintern??? Wer überlegt sich ein Protokoll, dass 4 Byte statt 2 überträgt und dann noch jede Menge Umrechnung erfordert, auf beiden Seiten?
Die Idee, eine Maske über die 32 Bit zu schieben statt die 32 Bit durch Carry rotieren zu lassen, hat das erste Problem gelöst (und ein neues hervorgebracht - siehe Vorposting). Garantieren tut mir keiner was. Alles, was für mich nicht sinnvoll auswertbar ist, wird irgnoriert. Kommen die beiden Tastendrücke so zeitnah, dass eine Datenübertragung keine und die nächste zwei Tasten enthält, wird das Ereignis ignoriert. Der letzte Teil geht runter wie Öl - das war ein Profi aus der Industrie, so einer mit richtig hohen Stundensätzen... ;-)) Der hat sich das ausgedacht. Trotzdem ist das so sinnvoll. Das ist ein universelles Modul, von denen bis zu 32 Stück an einem Bus hängen und nur ein Modul muss mit der Bedienung zweier Tasten umgehen können. Ist hier aber nicht beeinflussbar und man kann sich die Diskussion darüber schenken. Thomas
Thomas P. schrieb: > tastdata = gzio_ebuf[1] | gzio_ebuf[2] << 8 | gzio_ebuf[3] << 16 | > gzio_ebuf[4] << 24; > > Gibt eine zweifache Warnung vom Compiler mit > ../gzio.c:422:7: warning: left shift count >= width of type [enabled by > default] > und ich verstehe mal wieder nicht, warum? 8 Bit 24 mal links verschieben > sollte doch ein uint32_t nicht sprengen? Tja, wenn Du mal das tätest, was Du glaubst zu tun. gzio_ebuf[2] zum Beispiel ist doch gleich was für ein Typ? Und der ist wieviele Bits breit? Und soll um wieviele Bits geshifted werden?
Mir fehlt auch noch die zündende Idee, wie man die beiden Tasten am besten registriert. Ist eine Taste gedrückt, steht der "Eins-Bit-Zähler" auf eins und ich habe die Taste, die gedrückt wurde und speichere die. In einem der nächsten Durchläufe kommt eine zweite Taste dazu. Beim Auslesen kann jetzt die erste oder eine neue Taste als erstes auftauchen. Klar, man kann jetzt mit ein paar Vergleichen hantieren, gefällt mir aber nicht so richtig. Vielleicht ist es besser, die Bitmuster zu merken und dann ein xor zu nutzen. Bin mir eben nicht sicher und frage deshalb hier, in der Hoffnung, nicht wieder der Erste mit einem solchen Problem zu sein. Thomas
Hiermit kannst Du die Reihenfolge auswerten: Beitrag "Tasten-Matrix entprellen"
:
Bearbeitet durch User
1 | uint32_t l = *((uint32_t*) &gzio_ebuf[1]); |
2 | int n = 0; |
3 | int Taste1 = 0; /* konvention: 1..32 = Taste 1..32, 0=keine */ |
4 | int Taste2 = 0; |
5 | int i; |
6 | |
7 | static int Taste1_old; |
8 | |
9 | for(i=0; i < 32; i++) |
10 | {
|
11 | if(l & (1<<i)) |
12 | {
|
13 | n++; |
14 | if(!Taste1) {Taste1 =i+1;} |
15 | else if(!Taste2) {Taste2 =i+1;} |
16 | else {n=3;} |
17 | }
|
18 | }
|
19 | switch(n) |
20 | {
|
21 | case 3: /* error, whatever todo */ |
22 | Taste_old = 0; |
23 | break; |
24 | |
25 | case 2: /* melde doppeldruck einmal */ |
26 | if(Taste1 == Taste_old) Event(Taste1, Taste2); |
27 | if(Taste2 == Taste_old) Event(TAste2, Taste1); |
28 | Taste_old = 0; |
29 | break; |
30 | |
31 | case 1: Taste_old = Taste1; |
32 | break; |
33 | |
34 | case 0: Taste_old = 0; |
35 | break; |
36 | }
|
Taste_old enthält hier quasi den Status, ob vorher genau eine Taste gedrückt war. Die Bitschieberei oben geht natürlich schneller, sollte hier aber keine Rolle spielen.
Thomas P. schrieb: > Gibt eine zweifache Warnung vom Compiler mit > ../gzio.c:422:7: warning: left shift count >= width of type [enabled by > default] > und ich verstehe mal wieder nicht, warum? 8 Bit 24 mal links verschieben > sollte doch ein uint32_t nicht sprengen? Markus F. schrieb: > gzio_ebuf[2] zum Beispiel ist doch gleich was für ein Typ? Und der ist > wieviele Bits breit? Und soll um wieviele Bits geshifted werden? Nein. Beim Schieben arbeitet der GCC grundsätzlich mit unsigned int, also 16 Bit. Schiebt man eine Variable, egal ob (unsigned) char oder (unsigned) int, ist sie nach 16 Mal Schieben rausgeschoben und das Ergebnis ist 0. Deswegen haben die Compilerbauer da eine Warnung eingebaut. Die maximale Schiebeweite ist also 15Bit. Normalerweise. Will man >=16 Bit schieben, muß die Variable länger als 16 Bit sein.
1 | unsigned char c[] ={1, 2, 3, 4}; |
2 | unsigned long a, b; |
3 | |
4 | a = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); |
5 | b = c[0] | (c[1] << 8) | ((unsigned long)c[2] << 16) | ((unsigned long)c[3] << 24); |
Variable a enthält als Ergebnis 0x00000201. Das ist natürlich falsch. In Kenntnis der Eigenschaften des GCC ist es aber das erwartete Ergebnis. Egal, unbrauchbar ist es auf jeden Fall. Mit dem Cast auf unsigned long wird der GCC dazu veranlasst, mit 32 Bit zu arbeiten. Variable b enthält somit das richtige Ergebnis 0x04030201. Mit unsigned long long lässt sich sogar bis 63 Bit weit schieben.
Hallo Achim, ganz herzlichen Dank für Deine Mühe, das ist die Lösung des Problems und auch noch schön kurz! @ thomase: Vielen Danbk für die Erläuterung. Dass nur die Verschiebung um 16 bzw 24 Bit den Fehler bringt, hatte ich dann durch Versuche selber entdeckt, aber warum das mit 8 Bit funktioniert, war mir weiterhin unklar, denn das hätte ja nach der Erklärung von mfro auch nicht funktionieren dürfen. Jaja, die Geheimnisse des Compilers... Steht sicherlich irgendwo beschrieben, aber man muss die Stelle erst mal finden. Thomas
Thomas P. schrieb: > und ich verstehe mal wieder nicht, warum? 8 Bit 24 mal links verschieben > sollte doch ein uint32_t nicht sprengen? Das Shiften wird aber noch auf den uint8_t angewandt, und erst danach wird das implizit nach uint32_t gecastet. Einen Datentyp der Länge N um >=N Bits nach links zu shiften ist in C undefined behaviour, das hat mit GCC-Eigenarten nichts zu tun.
Nop schrieb: > Einen Datentyp der Länge N um >=N Bits nach links zu shiften ist in C > undefined behaviour, das hat mit GCC-Eigenarten nichts zu tun. Doch, wie ich oben gerade gelernt habe. thomase hat es doch begründet, dass intern unsigned int verwendet wird; also ist das Schieben von 8 bit um 8 bit nach links problemlos möglich - gab dafür ja auch keine Warnung vom Compiler. Thomas
Thomas P. schrieb: > thomase hat es doch begründet, dass intern unsigned int verwendet wird Ah ja richtig, mein Fehler.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.