Forum: Mikrocontroller und Digitale Elektronik Tastenabfrage bei Menüführung - Ideenblockade


von Jens Ebers (Gast)


Lesenswert?

Morgen zusammen!

Ich habe mal eine Frage: Wie macht ihr die Tastenerkennung bei einer 
Menüführung auf einem LCD?

Die Tastenentprellung a la Peda habe ich mir schon zu Gemüte geführt. 
Ganz dahinter komme ich jedoch leider nicht. Ich verstehe auch die 
Syntax vom AVR nicht ganz, da ich mit MSPs programmiere.

Ich frage nun alle 10ms per Timer einen Taster ab und habe eine 
Variable, die sich beeinhaltet, ob ein Taster kurz, lang oder schon sehr 
lang gedrückt ist.

In der main(), bzw. in meinem Menü kann ich nun diese Variable 
auswerten.

Als pseudo-Quelltext:
1
switch (menupunkt)
2
{
3
  case 1:
4
  {
5
    zeige das und das an
6
7
    if (keystatus == mindestens kurz gedrückt)
8
    {
9
      menupunkt = naechster menupunkt
10
    }
11
  }
12
13
  case 2:
14
  {
15
    zeige was anderes an
16
17
    if (keystatus == mindestens kurz gedrückt)
18
    {
19
      menupunkt = naechster menupunkt
20
    }
21
  }
22
}

Mein Problem ist jetzt, dass ich ja quasi eine Rücksetzvariable haben 
muss, da ich, wenn ich im Menüpunkt 1 bin und die Taste auswerte, er den 
Menüpunkt erhöht und in den nächsten springt. Hier müsste aber jetzt 
erstmal die Taste wieder losgelassen sein, damit er nicht direkt weiter 
in den nächsten Punkt springt.

Mir fehlt da grad die Idee, wie ich das machen kann. Ich könnte 
natürlich am Anfang eines jeden Menüpunktes ein
1
while (taste immernoch gedrueckt)
2
{
3
  mache nichts und warte, bis sie losgelassen wurde...
4
}
aber das ist ja nicht Sinn der Sache, zumal dann erst das Display 
aktualisiert wird, wenn man die Taste wieder loslässt und das dumm ist.

Ich stehe da grad etwas auf dem Schlauch, kann mir da evtl. grad jemand 
unter die Arme greifen?

Danke schonmal!

von Micha (Gast)


Lesenswert?

Jens Ebers schrieb:
> Ich verstehe auch die
> Syntax vom AVR nicht ganz, da ich mit MSPs programmiere.
Was gibt es da 
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29 
groß an besonderer Syntax zu verstehen? Ist doch C-Code. Der lässt sich 
doch auch problemlos portieren. Ich setze den z.B. auf TMS320 ein.

von Micha (Gast)


Lesenswert?

Achso: mit PeDas Code ist auch dein "Loslass-Problem" gelöst...

von Peter D. (peda)


Lesenswert?

Z.B. für eine zweidimensionale Menüführung:
1
  for(;;){
2
    if( get_key_press( 1<<KEY_UP ))
3
      menu_level++;
4
      menu_item = 0;
5
    }
6
    if( get_key_press( 1<<KEY_DOWN ))
7
      menu_level--;
8
      menu_item = 0;
9
    }
10
    if( get_key_press( 1<<KEY_RIGHT ))
11
      menu_item++;
12
    }
13
    if( get_key_press( 1<<KEY_LEFT ))
14
      menu_item--;
15
    }
16
  }


Peter

von Jens Ebers (Gast)


Lesenswert?

Micha schrieb:
> Achso: mit PeDas Code ist auch dein "Loslass-Problem" gelöst..

Kann mir vielleicht jemand in Worten erklären, wie in Pedas Lösung 
umgesetzt wird, dass der Tastendruck nur einmal zurückgegeben wird? Ich 
rall das nicht, sorry!

von Karl H. (kbuchegg)


Lesenswert?

Jens Ebers schrieb:
> Micha schrieb:
>> Achso: mit PeDas Code ist auch dein "Loslass-Problem" gelöst..
>
> Kann mir vielleicht jemand in Worten erklären, wie in Pedas Lösung
> umgesetzt wird, dass der Tastendruck nur einmal zurückgegeben wird? Ich
> rall das nicht, sorry!

Wird bei einer Taste ein 1->0 Übergang erkannt (die Tasten sind als 
Low-Aktiv angenommen) so wird in einer anderen Variable dieser 
Tastendruck mit einem 1 Bit vermerkt. Holst du dir den Tastendruck ab, 
so wird das Bit wieder gelöscht.  -> Du kriegst jeden Tastendruck nur 1 
mal.

von Jens Ebers (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wird bei einer Taste ein 1->0 Übergang erkannt (die Tasten sind als
> Low-Aktiv angenommen) so wird in einer anderen Variable dieser
> Tastendruck mit einem 1 Bit vermerkt.

Ah, jetzt fällt der Groschen, danke schonmal, muss ich mal eben 
versuchen umzusetzen.

von Karl H. (kbuchegg)


Lesenswert?

Jens Ebers schrieb:
> Karl heinz Buchegger schrieb:
>> Wird bei einer Taste ein 1->0 Übergang erkannt (die Tasten sind als
>> Low-Aktiv angenommen) so wird in einer anderen Variable dieser
>> Tastendruck mit einem 1 Bit vermerkt.
>
> Ah, jetzt fällt der Groschen, danke schonmal, muss ich mal eben
> versuchen umzusetzen.

Wozu?
Nimm doch einfach die PeDa Entprellung.

Das einzige KEY_PIN, das du zum Einlesen des Ports anpassen musst.
Hier musst du eingreifen:
1
  i = key_state ^ ~KEY_PIN;                       // key changed ?
und auf deinen Prozessor anpassen.

Der Rest ist Standard-C und geht auf jedem anderen Prozessor genausogut. 
Einen Timer Interrupt mit ca 10ms hast du ja schon, das Nachladen des 
Timer Registers schmeisst du raus und .... fertig

von Jens Ebers (Gast)


Lesenswert?

OK, vielen Dank schonmal! Ich muss die trotzdem erstmal komplett 
verstehen! Habe mir grad schon alles mögliche zum AVR im Internet 
gesucht, um zu sehen, was was bedeutet mit den Ports.

Was ich zum Beispiel nicht verstehe:
1
#define REPEAT_MASK  (1<<KEY1 | 1<<KEY2)
und KEY0 ist definiert zu 0, KEY1 zu 1 und KEY2 zu 2

Soweit ich mir das jetzt angelesen hab, heisst das beim AVR, dass KEY1 
und KEY2 gleichzeitig auf 'high' geprüft/(gesetzt) werden - beim Taster 
natürlich nicht gesetzt.

Aber warum ist die REPEAT_MASK KEY1 und KEY2?

von ybggby (Gast)


Lesenswert?

Das ist m.E. eine Maske, d.h. nur diese beiden Bits im Byte werden 
ausgewertet, hier fuer die Funktion repeat.

Gast

von Micha (Gast)


Lesenswert?

Jens Ebers schrieb:
> OK, vielen Dank schonmal! Ich muss die trotzdem erstmal komplett
> verstehen! Habe mir grad schon alles mögliche zum AVR im Internet
> gesucht, um zu sehen, was was bedeutet mit den Ports.
>
> Was ich zum Beispiel nicht verstehe:
> #define REPEAT_MASK  (1<<KEY1 | 1<<KEY2)
> und KEY0 ist definiert zu 0, KEY1 zu 1 und KEY2 zu 2
Das hat mit AVRs speziell nix tun. Das sind ganz normale Makros. Es wird 
der ganze Port eingelesen (Port B in diesem Fall: PINB -> 
AVR-spezifisch) und KEY1/KEY2 sind einfach nur (sprechende) Namen für 
die Eingänge (C allgemein).

> Soweit ich mir das jetzt angelesen hab, heisst das beim AVR, dass KEY1
> und KEY2 gleichzeitig auf 'high' geprüft/(gesetzt) werden - beim Taster
> natürlich nicht gesetzt.
>
> Aber warum ist die REPEAT_MASK KEY1 und KEY2?
Weil bei diesen beiden Tasten die Repeat-Funktion zur Verfügung stehen 
soll, bei den anderen nicht.

von Jens Ebers (Gast)


Lesenswert?

1
i = key_state ^ ~KEY_PIN;                      // key changed ?
i bekommt den Wert von (key_state ge-XOR-ed mit invertiertem Eingang)
OK.
1
ct0 = ~( ct0 & i );                            // reset or count ct0
ct0 bekommt invertierten Wert von übereinstimmenden "1" von ct0 und i
Warum?
1
ct1 = ct0 ^ (ct1 & i);                         // reset or count ct1
ct1 bekommt Wert von (ct0 ge-XORed mit (übereinstimmenden "1" von ct1 
und i
Warum?
1
i &= ct0 & ct1;                                // count until roll over ?
2
key_state ^= i;                                // then toggle debounced state
3
key_press |= key_state & i;                    // 0->1: key press detect

Das kann ich grad einfach nicht nachvollziehen, was in den ganzen Zeilen 
passiert.

Kann mir das einer erläutern? Sorry, wenn ich damit nerve, aber einfach 
copy paste, dann werd ich es nie verstehen.

von Micha (Gast)


Lesenswert?

Gut - es sind noch andere AVR-spezifische Sachen mit drin, dabei handelt 
es sich aber hauptsächlich um Initialisierungsgeschichten (vor allem in 
der main; TCNT0=... in der ISR setzt die Timerüberlaufzeit auf 10ms).

von Jens Ebers (Gast)


Lesenswert?

Micha schrieb:
> TCNT0=... in der ISR setzt die Timerüberlaufzeit auf 10ms

Ja, hab ich schon gesehen, das ist ja kein Problem, ich blick nur durch 
die C-Zeilen nicht durch.

von Micha (Gast)


Lesenswert?

Jens Ebers schrieb:
> Kann mir das einer erläutern?
Pinsel dir mal 8 Bits auf ein Blatt und geh das Schritt für Schritt, 
sprich Zeile für Zeile durch.

Grobe Erklärung: ct0 und ct1 stellen 8 Stück 2-Bit-Zähler (4 Zustände) 
für maximal 8 Eingänge dar. Es gehört immer ct0.0 & ct1.0 usw. bis ct0.7 
& ct1.7 zusammen. Auf anderen Architekturen lassen sich so auch 16 oder 
32 Eingänge gleichzeitig entprellen. Andere Ansätze nutzen für jeden 
Eingang einen eigenen Zähler - diese Version ist wesentlich 
Ressourcenschonender.

von Jens Ebers (Gast)


Lesenswert?

Micha schrieb:
> Pinsel dir mal 8 Bits auf ein Blatt

Das mach ich grad :)

von Micha (Gast)


Lesenswert?

Micha schrieb:
> ct0 und ct1 stellen 8 Stück 2-Bit-Zähler (4 Zustände)
> für maximal 8 Eingänge dar
... und nur wenn nach 4 Durchläufen noch immer derselbe Pegel am Eingang 
anliegt wird dieser als gültig akzeptiert. Ansonsten wird der Zähler 
zurückgesetzt.

von Jens Ebers (Gast)


Lesenswert?

Micha schrieb:
> und nur wenn nach 4 Durchläufen noch immer derselbe Pegel am Eingang
> anliegt

Und woher kommen hier die vier Durchgänge?

von Karl H. (kbuchegg)


Lesenswert?

Ich habe gerade angefangen im Wiki-Artikel eine kurze Beschreibung der 
Funktionsweise anzufügen.
Ist schon eigenartig. Das ist eine der wenigen Codestellen, bei denen 
ich es OK finde, wenn man nicht im Detail versteht was da abgeht, eben 
weil er so tricky ist. Und ausgerechnet davon will jeder wissen, wie das 
funktioniert andernfalls nimmt er den Code nicht.

Der Schlüssel zum Ganzen ist das Verständnis dessen, das ct0 UND ct1 
zusammengenommen 8 Stück 2-Bit Zähler bilden (siehe Bild)

von Micha (Gast)


Lesenswert?

Jens Ebers schrieb:
> Das mach ich grad :)
Die ganze Sache ist sehr trickreich und am Anfang schwer zu verstehen. 
Auch wenn du es ne Weile nicht genutzt hast, braucht es ein bisschen bis 
du wieder drin bist. Aber du wirst feststellen, dass es funktioniert und 
kaum effizienter realisiert werden könnte.

Ansonsten: vertrau Peter und verwende den Code einfach... ;-)

PS: funktioniert auch mit verteilten Eingängen (Pegel in eine Variable 
kopieren und diese entprellen).

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Karl heinz Buchegger schrieb:
> Ich habe gerade angefangen im Wiki-Artikel eine kurze Beschreibung der
> Funktionsweise anzufügen.
> Ist schon eigenartig. Das ist eine der wenigen Codestellen, bei denen
> ich es OK finde, wenn man nicht im Detail versteht was da abgeht, eben
> weil er so tricky ist. Und ausgerechnet davon will jeder wissen, wie das
> funktioniert andernfalls nimmt er den Code nicht.
>
> Der Schlüssel zum Ganzen ist das Verständnis dessen, das ct0 UND ct1
> zusammengenommen 8 Stück 2-Bit Zähler bilden (siehe Bild)

Bild ist beim editieren verloren gegangen

von Micha (Gast)


Lesenswert?

Jens Ebers schrieb:
> Und woher kommen hier die vier Durchgänge?
Der Pegel wird 4 mal überprüft (mit 10ms Pause) und wenn er bei allen 4 
Durchgängen gleich geblieben ist wird er akzeptiert. Peter hätte auch 2 
oder 8 nehmen können, aber 4 ist wohl ein sehr guter Kompromiss.

von Jens Ebers (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Ich habe gerade angefangen im Wiki-Artikel eine kurze Beschreibung der
> Funktionsweise anzufügen.

Besten Dank! Du bist mein persönlicher Held für heute! Das find ich echt 
super.

Es ist auch blöd das immer wieder rauszukramen, jeder 10. Thread hier 
geht um diese Tastenentprellung und die, die es verstanden haben, die 
nervt es, das kann ich auch vollkommen verstehen, nur leider gehöre ich 
noch zu der anderen Fraktion.

Ich male mir das gerade Bitweise auf und selbst das ist noch nicht so 
leicht zu überblicken im Moment.

Was mich halt auch wundert: Alle Variablen wie ct0, ct1, key_state, 
key_press, key_rpt werden garnicht initialisiert.

Ist es egal, welcher Wert schon drinne steht?

von Karl H. (kbuchegg)


Lesenswert?

Jens Ebers schrieb:
>
1
> i = key_state ^ ~KEY_PIN;                      // key changed ?
2
>
> i bekommt den Wert von (key_state ge-XOR-ed mit invertiertem Eingang)
> OK.

Schon. Aber was ist die Bedeutung eines 1 Bits?

Ein 1 Bit bedeutet an dieser Stelle: Der aktuelle Pin Zustand 
unterscheidet sich vom letzten bekannten, entprellten Zustand (in 
key_state)

(Wenn du so weitermachst, wirst du deine Analysefähigkeiten nicht 
schärfen. Du liest nur vor, was im Code steht. Aber du musst dich immer 
fragen: Was ist die Bedeutung dessen was da steht? Was heißt es, wenn da 
ein 1 Bit oder ein 0 Bit auftaucht? Kann man das in allgemeineren 
Begriffen als 1-Bit / 0-Bit formulieren?
)

>
1
> ct0 = ~( ct0 & i );                            // reset or count ct0
2
>
> ct0 bekommt invertierten Wert von übereinstimmenden "1" von ct0 und i
> Warum?
>
1
> ct1 = ct0 ^ (ct1 & i);                         // reset or count ct1
2
>
> ct1 bekommt Wert von (ct0 ge-XORed mit (übereinstimmenden "1" von ct1
> und i
> Warum?

Die beiden letzten Zeilen muss man zusammen sehen.
Sie erhöhen den vertikalen 2-Bit Zähler um 1. Aus 0b00 wird 0b01 wird 
0b10 wird 0b11
Aber nur dann, wenn an der entsprechenden Bitposition in i eine 1 steht. 
Steht dort eine 0, so wird der Zähler fix auf 0b01 gesetzt.
Der Zähler zählt also eigentlich von 0b01 auf 0b10 auf 0b11

>
1
> i &= ct0 & ct1;                                // count until roll over
2
> ?
3
> key_state ^= i;                                // then toggle debounced
4
> state
5
> key_press |= key_state & i;                    // 0->1: key press detect
6
>
>
> Das kann ich grad einfach nicht nachvollziehen, was in den ganzen Zeilen
> passiert.

Ja?

Die erste Zeile ist die Abfrage ob der Zähler bei 0b11 angelangt ist. 
Wenn ja wird in i ein 1 Bit für diesen Zähler hinterlassen, wenn nein 
dann wird in i eine 0 eingeschrieben. Allerdings findet das ganze nur 
dann statt, wenn in i schon eine 1 war, d.h. (siehe oben) wenn es an 
dieser Taster überhaupt eine Veränderung gab.

Alles zusammengenommen macht also die erste Zeile:
in i bleibt ein 1-Bit erhalten für alle Tasten, die eine Veränderung 
haben (da war i vorher schon i) UND deren 2-Bit Zähler die 3 (0b11) 
erreicht haben.

Hat der Zähler die 3 erreicht, dann wird in key_state vermerkt, dass es 
für dieses Bit eine Veränderung gibt. das macht die mittlere Zeile
Und die letzte wertet zu guter letzt noch aus, welcher Natur diese 
Veränderung war. War es eine 0->1 Veränderung oder eine 1->0 Verändern. 
Wenn 0->1 dann wird in key_press ein 1 Bit gesetzt, die vermerkt, dass 
die entsprechende Taste gedrückt wurde.

von Karl H. (kbuchegg)


Lesenswert?

Jens Ebers schrieb:

> Was mich halt auch wundert: Alle Variablen wie ct0, ct1, key_state,
> key_press, key_rpt werden garnicht initialisiert.

C Regeln lernen!

Diese Variablen sind alle von der Sorte: Werden vom Compiler beim 
Programmstart mit 0 initialisiert.

von Micha (Gast)


Lesenswert?

Das Loslassen der Taste wird übrigens auf die gleiche Weise ebenfalls 
entprellt.

von Jens Ebers (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Diese Variablen sind alle von der Sorte: Werden vom Compiler beim
> Programmstart mit 0 initialisiert.

Na da hab ich doch direkt wieder was gelernt!

von Karl H. (kbuchegg)


Lesenswert?

Wie gesagt:

Der Code ist extrem trickreich. Eine einzige harmlos aussehende Zeile 
mit einer eigentlich simpel anmutenden Bitmanipulation macht viele 
semantische Dinge gleichzeitig und basiert auf dem Wissen was ein 1 Bit 
in diversen anderen Variablen für eine Bedeutung hat, bzw. verwendet 
zeimlich viel Rundumwissen und Wissen aus den vorhergehenden Zeilen. Bei 
diesem Code ist jedes einzelne Bit und jedes AND OR XOR wichtig!

Diesen Code muss man nicht im Detail verstehen. Ja, er ist trickreich. 
Und ja, er funktioniert auch dann, wenn man ihn nicht versteht. Er ist 
ein absoluter Hingucker: zunächst sieht er einfach aus, schaut man 
genauer hin wird er kompliziert, dann wieder einfach etc. Seine 
Funktionsweise zu erklären ist nicht einfach. Man könnte über diese 5 
Zeilen Code ohne Probleme eine 3 seitige Abhandlung schreiben und hätte 
selbst dann noch nicht alle Aspekte und Feinheiten erfasst.

Aber all das ist unwichtig, gegenüber:
Man kann ihn ohne Probleme einfach so verwenden und er tut was er soll 
ohne irgendwelche großartigen AVR-spezifischen Besonderheiten zu 
benutzen. Und zwar jedes mal.

von Micha H. (mlh) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Und ausgerechnet davon will jeder wissen, wie das
> funktioniert andernfalls nimmt er den Code nicht.

Wie ich an anderer Stelle schon schrieb ist das genau das Problem dieser 
Routine: Sie ist "zu gut".
Der Hobbyprogrammierer will aber auch verstehen was er da tut, und genau 
deshalb kommen immer wieder Fragen zu dem Teil und es wird nicht 
benutzt, da kann es noch so gut funktionieren.
IMHO ist das für ein Tutorial nicht ganz das geeignete.

Micha

von Karl H. (kbuchegg)


Lesenswert?

Micha H. schrieb:
> Karl heinz Buchegger schrieb:
>> Und ausgerechnet davon will jeder wissen, wie das
>> funktioniert andernfalls nimmt er den Code nicht.
>
> Wie ich an anderer Stelle schon schrieb ist das genau das Problem dieser
> Routine: Sie ist "zu gut".

LOL
Das ist sie wohl.

Um eine Analogie zu benutzen:
Ich bezweifle, dass die meisten verstehen wie eine Fast Fourier 
Transformation funktioniert bzw. mit welchen Code-Tricks diese schnell 
gemacht wurde. Und trotzdem haben die wenigsten Skrupel, eine FFT 
einzusetzen um bei ihrem MP3 Player auf dem LCD ein Spektrum tanzen zu 
lassen.

Genau so muss man das auch mit diesem Code sehen: Er ist 'ready to use'.

von Jens Ebers (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Genau so muss man das auch mit diesem Code sehen: Er ist 'ready to use'.

Also besten Dank!
Ich werde ihn jetzt anpassen und einsetzen, male parallel trotzdem noch 
an meinen Nullen und Einsen rum :) Und irgendwann werde ich es 
verstehen!

von Jens Ebers (Gast)


Lesenswert?

Was ist noch nicht verstehe: wodurch sind die 4 Abfragen gegeben? Das 
ist mir aus dem Code nicht ersichtlich.

von Karl H. (kbuchegg)


Lesenswert?

Jens Ebers schrieb:
> Was ist noch nicht verstehe: wodurch sind die 4 Abfragen gegeben? Das
> ist mir aus dem Code nicht ersichtlich.

He, he

Peter hat es irgenwo mal bestätigt (wenn mich mein Gedächtnis nicht im 
Stich lässt). Der C-Code macht nur eine 2 fache Samplung. Im Gegensatz 
zum Assembler Code, der macht 4.

Spielt aber keine wirklich bedeutende Rolle.

von Jens Ebers (Gast)


Lesenswert?

Ich mein, der Timer läuft doch immer weiter, es wird doch also 
regelmäßig abgefragt, wo ist der Knackpunkt, dass es dann halt zwei 
gleiche sein müssen?

Hab das halt mal für drei Durchläufe mit nicht-wechselndem Tastenstatus 
gemacht, da ändert sich ja halt garnichts:
1
i = key_state ^ ~KEY_PIN
2
ct0 = ~(ct0 & i)
3
ct1 = ct0 ^ (ct1 & i)
4
i &= ct0 & ct1
5
key_state ^= i
6
key_press |= key_state & i
7
8
9
Eingang: 1011 0010 Taster active low -> '0' bedeutet gedrückt
10
11
-------------------------------------------------------------
12
1. Durchlauf:
13
14
i         = 0000 0000
15
ct0       = 0000 0000
16
ct1       = 0000 0000
17
key_state = 0000 0000
18
key_press = 0000 0000
19
20
i = key_state ^ ~KEY_PIN
21
i = 0000 0000 ^ ~1011 0010
22
i = 0000 0000 ^ 0100 1101
23
i = 0100 1101
24
25
ct0 = ~(ct0 & i)
26
ct0 = ~(0000 0000 & 0100 1101)
27
ct0 = ~0000 0000
28
ct0 = 1111 1111
29
30
ct1 = ct0 ^ (ct1 & i)
31
ct1 = 1111 1111 ^ (0000 0000 & 0100 1101)
32
ct1 = 1111 1111 ^ 0000 0000
33
ct1 = 1111 1111
34
35
i &= ct0 & ct1
36
i &= 1111 1111 & 1111 1111
37
i &= 1111 1111
38
i = 1111 1111 & 0100 1101
39
i = 0100 1101
40
41
key_state ^= i
42
key_state ^= 0100 1101
43
key_state = 0100 1101 ^ 0000 0000
44
key_state = 0100 1101
45
46
key_press |= key_state & i
47
key_press |= 0100 1101 & 0100 1101
48
key_press |= 0100 1101
49
key_press = 0100 1101 | 0000 0000
50
key_press = 0100 1101
51
52
-------------------------------------------------------------
53
2. Durchlauf:
54
55
i         = 0100 1101
56
ct0       = 1111 1111
57
ct1       = 1111 1111
58
key_state = 0100 1101
59
key_press = 0100 1101
60
61
i = key_state ^ ~KEY_PIN
62
i = 0100 1101 ^ ~1011 0010
63
i = 0100 1101 ^ 0100 1101
64
i = 0000 0000
65
66
ct0 = ~(ct0 & i)
67
ct0 = ~(1111 1111 & 0000 0000)
68
ct0 = ~0000 0000
69
ct0 = 1111 1111
70
71
ct1 = ct0 ^ (ct1 & i)
72
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
73
ct1 = 1111 1111 ^ 0000 0000
74
ct1 = 1111 1111
75
76
i &= ct0 & ct1
77
i &= 1111 1111 & 1111 1111
78
i &= 1111 1111
79
i = 1111 1111 & 0000 0000
80
i = 0000 0000
81
82
key_state ^= i
83
key_state ^= 0000 0000
84
key_state = 0000 0000 ^ 0100 1101
85
key_state = 0100 1101
86
87
key_press |= key_state & i
88
key_press |= 0100 1101 & 0000 0000
89
key_press |= 0000 0000
90
key_press = 0000 0000 | 0100 1101
91
key_press = 0100 1101
92
93
-------------------------------------------------------------
94
3. Durchlauf:
95
96
i         = 0000 0000
97
ct0       = 1111 1111
98
ct1       = 1111 1111
99
key_state = 0100 1101
100
key_press = 0100 1101
101
102
i = key_state ^ ~KEY_PIN
103
i = 0100 1101 ^ ~1011 0010
104
i = 0100 1101 ^ 0100 1101
105
i = 0000 0000
106
107
ct0 = ~(ct0 & i)
108
cto = ~(1111 1111 & 0000 0000)
109
ct0 = ~0000 0000
110
ct0 = 1111 1111
111
112
ct1 = ct0 ^ (ct1 & i)
113
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
114
ct1 = 1111 1111 ^ 0000 0000
115
ct1 = 1111 1111
116
117
i &= ct0 & ct1
118
i &= 1111 1111 & 1111 1111
119
i &= 1111 1111
120
i = 1111 1111 & 0000 0000
121
i = 0000 0000
122
123
key_state ^= i
124
key_state ^= 0000 0000
125
key_state = 0000 0000 ^ 0100 1101
126
key_state = 0100 1101
127
128
key_press |= key_state & i
129
key_press |= 0100 1101 & 0000 0000
130
key_press |= 0000 0000
131
key_press = 0000 0000 | 0100 1101
132
key_press = 0100 1101

von Peter D. (peda)


Lesenswert?

Karl heinz Buchegger schrieb:
> Peter hat es irgenwo mal bestätigt (wenn mich mein Gedächtnis nicht im
> Stich lässt). Der C-Code macht nur eine 2 fache Samplung. Im Gegensatz
> zum Assembler Code, der macht 4.

Ne, genau umgekehrt.
Der Assembler im Tutorial macht nur 2-fach.
Die C-Beispiele machen alle 4-fach.

Hier mal Assembler geändert auf 4-fach:
1
.include "1200def.inc"
2
3
.def    deb_ct0 = r2
4
.def    deb_ct1 = r3
5
.def    deb_i = r4
6
.def    deb_keystate = r5
7
.def    deb_keypress = r6
8
9
10
debounce:
11
        ;i = key_state ^ ~KEY_PIN;
12
13
        in      deb_i, PINB
14
        com     deb_i
15
        eor     deb_i, deb_keystate
16
17
        ;ct0 = ~( ct0 & i );
18
19
        and     deb_ct0, deb_i
20
        com     deb_ct0
21
22
        ;ct1 = ct0 ^(ct1 & i);
23
24
        and     deb_ct1, deb_i
25
        eor     deb_ct1, deb_ct0
26
27
        ;i &= ct0 & ct1;
28
29
        and     deb_i, deb_ct0
30
        and     deb_i, deb_ct1
31
32
        ;key_state ^= i;
33
34
        eor     deb_keystate, deb_i     ;deb_keystate = debounced key state
35
36
        ;key_press |= key_state & i;
37
38
        and     deb_i, deb_keystate
39
        or      deb_keypress, deb_i     ;deb_keypress = key pin has changed from 1->0
40
41
        ret

Peter

von Karl H. (kbuchegg)


Lesenswert?

Jens Ebers schrieb:
> Ich mein, der Timer läuft doch immer weiter, es wird doch also
> regelmäßig abgefragt, wo ist der Knackpunkt, dass es dann halt zwei
> gleiche sein müssen?
>
> Hab das halt mal für drei Durchläufe mit nicht-wechselndem Tastenstatus
> gemacht, da ändert sich ja halt garnichts:

Der Fall ist ja auch nicht besonders spannend.
Nimm jetzt den eingeschwungenen Zustand her (dein letztes Ergebnis) und 
ändere genau 1 Pin und dann verfolgst du, nach wievielen Durchgängen 
dieser Pinwechsel in key_press durchschlägt.

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Karl heinz Buchegger schrieb:
>> Peter hat es irgenwo mal bestätigt (wenn mich mein Gedächtnis nicht im
>> Stich lässt). Der C-Code macht nur eine 2 fache Samplung. Im Gegensatz
>> zum Assembler Code, der macht 4.
>
> Ne, genau umgekehrt.
> Der Assembler im Tutorial macht nur 2-fach.
> Die C-Beispiele machen alle 4-fach.

Hmm.
Dann muss ich mich auch noch mal drann setzen und genauer mit einem 
Datendurchlauf analysieren.
Wie Gerda Rogers (österr. Astrologien) immer sagt: "Das hätt ich jetzt 
nicht gesehen"

von Peter D. (peda)


Lesenswert?

Hier ein extra Demobeispiel für eine Taste mit 1s Takt:

http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=1801

Man muß dann 4s gedrückt halten bzw. loslassen, damit hinten was 
passiert.


Peter

von Jens Ebers (Gast)


Lesenswert?

Was ist denn ATOMIC_BLOCK?

von Peter D. (peda)


Lesenswert?

Jens Ebers schrieb:
> Was ist denn ATOMIC_BLOCK?

Das ist ne sehr nützliche Erweiterung des GCC für richtig echte 
Echtzeit, wo man Interrupts braucht.

Du liest eine Variable und willst darin nur ein Bit ändern. Das geht 
beim AVR nicht atomar, d.h er muß lesen, UNDieren und schreiben.
Wenn nun gerade dazwischen ein Interrupt auch auf die selbe Variable 
schreibt, geht dessen Änderung verloren.
Deshalb macht man es atomar, d.h. unter Interruptsperre und schon gehts.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger schrieb:

> Dann muss ich mich auch noch mal drann setzen und genauer mit einem
> Datendurchlauf analysieren.
> Wie Gerda Rogers (österr. Astrologien) immer sagt: "Das hätt ich jetzt
> nicht gesehen"

War erleuchtend

Wenn ct0 das LowBit ist und ct1 das High Bit

dann durchläuft der Zähler aus seinem Grundzustand 0b11 heraus, die 
Zustände
   0b10, 0b01, 0b00
ehe er dann wieder zu 0b11 wird.

Wenn ich das richtige sehe, dann wird da eigentlich runtergezählt und 
nicht rauf :-)
Und der entscheidende Punkt ist dann erreicht, wenn der Zähler von 0b00 
zu 0b11 unterläuft (und wieder im Grundzustand ist). Und damit versteh 
ich dann auch das hier

  i &= ct0 & ct1;                                // count until roll 
over

viel besser, denn in i ist nur beim ersten mal Erreichen des 
Grundzustands eine 1 enthalten. Dadurch wird dann key_state auf 1 
gesetzt und beim nächsten Durchlauf ist dann ganz am Anfang keine 
Änderung mehr, i wird zu 0, wodurch hier dann in weiterer Folge 0 
auftaucht.

Ja tatsächlich, mit runterzählen macht das mehr Sinn.
Hatte mich sowieso schon die ganze Zeit gefragt, wieso hier

ct0 = ~(ct0 & i)
ct1 = ct0 ^ (ct1 & i)

ct0 und ct1 bei einem i von 0 auf 1 gesetzt werden, wo ich doch die 
ganze Zeit 0 erwartet hätte.

von Jens Ebers (Gast)


Lesenswert?

So, also ich checks nicht, sorry!

Wenn es vier Durchgänge macht und dann der entsprechende Wert vorliegt, 
warum ist dann seit drei Durchgängen nichts passiert? Die wichtige 
Variable ist doch die ganze Zeit gleich, wenn ich nach dem zweiten Timer 
abtaste, dann bekomme ich doch das gleiche Ergebnis:
1
i = key_state ^ ~KEY_PIN
2
ct0 = ~(ct0 & i)
3
ct1 = ct0 ^ (ct1 & i)
4
i &= ct0 & ct1
5
key_state ^= i
6
key_press |= key_state & i
7
8
9
Eingang: 1011 0010 Taster active low -> '0' bedeutet gedrueckt
10
11
-------------------------------------------------------------
12
1. Durchlauf:
13
14
i         = 0000 0000
15
ct0       = 0000 0000
16
ct1       = 0000 0000
17
key_state = 0000 0000
18
key_press = 0000 0000
19
20
i = key_state ^ ~KEY_PIN
21
i = 0000 0000 ^ ~1011 0010
22
i = 0000 0000 ^ 0100 1101
23
i = 0100 1101
24
25
ct0 = ~(ct0 & i)
26
ct0 = ~(0000 0000 & 0100 1101)
27
ct0 = ~0000 0000
28
ct0 = 1111 1111
29
30
ct1 = ct0 ^ (ct1 & i)
31
ct1 = 1111 1111 ^ (0000 0000 & 0100 1101)
32
ct1 = 1111 1111 ^ 0000 0000
33
ct1 = 1111 1111
34
35
i &= ct0 & ct1
36
i &= 1111 1111 & 1111 1111
37
i &= 1111 1111
38
i = 1111 1111 & 0100 1101
39
i = 0100 1101
40
41
key_state ^= i
42
key_state ^= 0100 1101
43
key_state = 0100 1101 ^ 0000 0000
44
key_state = 0100 1101
45
46
key_press |= key_state & i
47
key_press |= 0100 1101 & 0100 1101
48
key_press |= 0100 1101
49
key_press = 0100 1101 | 0000 0000
50
key_press = 0100 1101
51
52
-------------------------------------------------------------
53
2. Durchlauf:
54
55
i         = 0100 1101
56
ct0       = 1111 1111
57
ct1       = 1111 1111
58
key_state = 0100 1101
59
key_press = 0100 1101
60
61
i = key_state ^ ~KEY_PIN
62
i = 0100 1101 ^ ~1011 0010
63
i = 0100 1101 ^ 0100 1101
64
i = 0000 0000
65
66
ct0 = ~(ct0 & i)
67
ct0 = ~(1111 1111 & 0000 0000)
68
ct0 = ~0000 0000
69
ct0 = 1111 1111
70
71
ct1 = ct0 ^ (ct1 & i)
72
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
73
ct1 = 1111 1111 ^ 0000 0000
74
ct1 = 1111 1111
75
76
i &= ct0 & ct1
77
i &= 1111 1111 & 1111 1111
78
i &= 1111 1111
79
i = 1111 1111 & 0000 0000
80
i = 0000 0000
81
82
key_state ^= i
83
key_state ^= 0000 0000
84
key_state = 0000 0000 ^ 0100 1101
85
key_state = 0100 1101
86
87
key_press |= key_state & i
88
key_press |= 0100 1101 & 0000 0000
89
key_press |= 0000 0000
90
key_press = 0000 0000 | 0100 1101
91
key_press = 0100 1101
92
93
-------------------------------------------------------------
94
3. Durchlauf:
95
96
i         = 0000 0000
97
ct0       = 1111 1111
98
ct1       = 1111 1111
99
key_state = 0100 1101
100
key_press = 0100 1101
101
102
i = key_state ^ ~KEY_PIN
103
i = 0100 1101 ^ ~1011 0010
104
i = 0100 1101 ^ 0100 1101
105
i = 0000 0000
106
107
ct0 = ~(ct0 & i)
108
ct0 = ~(1111 1111 & 0000 0000)
109
ct0 = ~0000 0000
110
ct0 = 1111 1111
111
112
ct1 = ct0 ^ (ct1 & i)
113
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
114
ct1 = 1111 1111 ^ 0000 0000
115
ct1 = 1111 1111
116
117
i &= ct0 & ct1
118
i &= 1111 1111 & 1111 1111
119
i &= 1111 1111
120
i = 1111 1111 & 0000 0000
121
i = 0000 0000
122
123
key_state ^= i
124
key_state ^= 0000 0000
125
key_state = 0000 0000 ^ 0100 1101
126
key_state = 0100 1101
127
128
key_press |= key_state & i
129
key_press |= 0100 1101 & 0000 0000
130
key_press |= 0000 0000
131
key_press = 0000 0000 | 0100 1101
132
key_press = 0100 1101
133
134
-------------------------------------------------------------
135
3. Durchlauf:
136
137
i         = 0000 0000
138
ct0       = 1111 1111
139
ct1       = 1111 1111
140
key_state = 0100 1101
141
key_press = 0100 1101
142
143
i = key_state ^ ~KEY_PIN
144
i = 0100 1101 ^ ~1011 0010
145
i = 0100 1101 ^ 0100 1101
146
i = 0000 0000
147
148
ct0 = ~(ct0 & i)
149
ct0 = ~(1111 1111 & 0000 0000)
150
ct0 = ~0000 0000
151
ct0 = 1111 1111
152
153
ct1 = ct0 ^ (ct1 & i)
154
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
155
ct1 = 1111 1111 ^ 0000 0000
156
ct1 = 1111 1111
157
158
key_state ^= i
159
key_state ^= 0000 0000
160
key_state = 0000 0000 ^ 0100 1101
161
key_state = 0100 1101
162
163
key_press |= key_state & i
164
key_press |= 0100 1101 & 0000 0000
165
key_press |= 0000 0000
166
key_press = 0000 0000 | 0100 1101
167
key_press = 0100 1101

von Jens Ebers (Gast)


Lesenswert?

Letzte ist natürlich der 4. Durchlauf

von Karl H. (kbuchegg)


Lesenswert?

Hier mein Testprogramm auf dem PC
1
#include <stdio.h>
2
#include <string.h>
3
4
#ifndef TRUE
5
#define TRUE  1
6
#define FALSE 0
7
#endif
8
9
typedef unsigned char uint8_t;
10
11
int main()
12
{
13
  uint8_t ct0 = 0;
14
  uint8_t ct1 = 0;
15
  uint8_t i = 0;
16
  uint8_t key_state = 0;
17
  uint8_t key_press = 0;
18
  uint8_t j;
19
 
20
  uint8_t KEY_PIN = 0xFF;
21
  
22
  for( j = 0; j < 15; ++j ) { 
23
    printf( "Durchgang: %d\n", j );
24
     
25
    i = key_state ^ ~KEY_PIN;
26
    printf( "i = %d\n", i );
27
    
28
    ct0 = ~(ct0 & i);
29
    ct1 = ct0 ^ (ct1 & i);
30
    printf( "ct0 = %d, ct1 = %d\n", ct0, ct1 );
31
    
32
    i &= ct0 & ct1;
33
    printf( "i = %d\n", i );
34
35
    key_state ^= i;
36
    key_press |= key_state & i;
37
    printf( "key_state = %d, key_press = %d\n", key_state, key_press );
38
    
39
    printf( "\n\n" );
40
    
41
    if( j > 3 )
42
      KEY_PIN = 0xFE;
43
  }
44
}

und der Output
1
Durchgang: 0
2
i = 0
3
ct0 = 255, ct1 = 255
4
i = 0
5
key_state = 0, key_press = 0
6
7
8
Durchgang: 1
9
i = 0
10
ct0 = 255, ct1 = 255
11
i = 0
12
key_state = 0, key_press = 0
13
14
15
Durchgang: 2
16
i = 0
17
ct0 = 255, ct1 = 255
18
i = 0
19
key_state = 0, key_press = 0
20
21
22
Durchgang: 3
23
i = 0
24
ct0 = 255, ct1 = 255
25
i = 0
26
key_state = 0, key_press = 0
27
28
29
Durchgang: 4
30
i = 0
31
ct0 = 255, ct1 = 255
32
i = 0
33
key_state = 0, key_press = 0
34
35
36
Durchgang: 5                              <-  Taste wird gedrückt
37
i = 1
38
ct0 = 254, ct1 = 255                      <-  der Counter beginnt zu zählen
39
i = 0
40
key_state = 0, key_press = 0
41
42
43
Durchgang: 6                              <- 2. ter Durchlauf
44
i = 1
45
ct0 = 255, ct1 = 254
46
i = 0
47
key_state = 0, key_press = 0
48
49
50
Durchgang: 7                              <- 3. Durchlauf
51
i = 1
52
ct0 = 254, ct1 = 254
53
i = 0
54
key_state = 0, key_press = 0
55
56
57
Durchgang: 8                              <- 4. Durchlauf
58
i = 1
59
ct0 = 255, ct1 = 255
60
i = 1
61
key_state = 1, key_press = 1             <- jetzt ist der Tastendruck registriert
62
63
64
Durchgang: 9
65
i = 0                                    <- KEY_PIN hat sich nicht verändert, trotzdem ist i hier bereits 0,
66
                                            da ~KEY_PIN und key_state identisch sind.
67
ct0 = 255, ct1 = 255
68
i = 0
69
key_state = 1, key_press = 1
70
71
72
Durchgang: 10
73
i = 0
74
ct0 = 255, ct1 = 255
75
i = 0
76
key_state = 1, key_press = 1
77
78
....

von Jens Ebers (Gast)


Lesenswert?

Also ist meine "Kopfberechnung" falsch :\

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
Noch kein Account? Hier anmelden.