www.mikrocontroller.net

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


Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
switch (menupunkt)
{
  case 1:
  {
    zeige das und das an

    if (keystatus == mindestens kurz gedrückt)
    {
      menupunkt = naechster menupunkt
    }
  }

  case 2:
  {
    zeige was anderes an

    if (keystatus == mindestens kurz gedrückt)
    {
      menupunkt = naechster menupunkt
    }
  }
}

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
while (taste immernoch gedrueckt)
{
  mache nichts und warte, bis sie losgelassen wurde...
}
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!

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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/Entprellun... 
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.

Autor: Micha (Gast)
Datum:

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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Z.B. für eine zweidimensionale Menüführung:
  for(;;){
    if( get_key_press( 1<<KEY_UP ))
      menu_level++;
      menu_item = 0;
    }
    if( get_key_press( 1<<KEY_DOWN ))
      menu_level--;
      menu_item = 0;
    }
    if( get_key_press( 1<<KEY_RIGHT ))
      menu_item++;
    }
    if( get_key_press( 1<<KEY_LEFT ))
      menu_item--;
    }
  }


Peter

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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:
  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

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#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?

Autor: ybggby (Gast)
Datum:

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

Gast

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
i = key_state ^ ~KEY_PIN;                      // key changed ?
i bekommt den Wert von (key_state ge-XOR-ed mit invertiertem Eingang)
OK.
ct0 = ~( ct0 & i );                            // reset or count ct0
ct0 bekommt invertierten Wert von übereinstimmenden "1" von ct0 und i
Warum?
ct1 = ct0 ^ (ct1 & i);                         // reset or count ct1
ct1 bekommt Wert von (ct0 ge-XORed mit (übereinstimmenden "1" von ct1 
und i
Warum?
i &= ct0 & ct1;                                // count until roll over ?
key_state ^= i;                                // then toggle debounced state
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.

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Micha schrieb:
> Pinsel dir mal 8 Bits auf ein Blatt

Das mach ich grad :)

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jens Ebers schrieb:
>
> i = key_state ^ ~KEY_PIN;                      // key changed ?
> 
> 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?
)

>
> ct0 = ~( ct0 & i );                            // reset or count ct0
> 
> ct0 bekommt invertierten Wert von übereinstimmenden "1" von ct0 und i
> Warum?
>
> ct1 = ct0 ^ (ct1 & i);                         // reset or count ct1
> 
> 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

>
> i &= ct0 & ct1;                                // count until roll over
> ?
> key_state ^= i;                                // then toggle debounced
> state
> key_press |= key_state & i;                    // 0->1: key press detect
> 
>
> 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Loslassen der Taste wird übrigens auf die gleiche Weise ebenfalls 
entprellt.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Micha H. (mlh) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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'.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Jens Ebers (Gast)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:

i = key_state ^ ~KEY_PIN
ct0 = ~(ct0 & i)
ct1 = ct0 ^ (ct1 & i)
i &= ct0 & ct1
key_state ^= i
key_press |= key_state & i


Eingang: 1011 0010 Taster active low -> '0' bedeutet gedrückt

-------------------------------------------------------------
1. Durchlauf:

i         = 0000 0000
ct0       = 0000 0000
ct1       = 0000 0000
key_state = 0000 0000
key_press = 0000 0000

i = key_state ^ ~KEY_PIN
i = 0000 0000 ^ ~1011 0010
i = 0000 0000 ^ 0100 1101
i = 0100 1101

ct0 = ~(ct0 & i)
ct0 = ~(0000 0000 & 0100 1101)
ct0 = ~0000 0000
ct0 = 1111 1111

ct1 = ct0 ^ (ct1 & i)
ct1 = 1111 1111 ^ (0000 0000 & 0100 1101)
ct1 = 1111 1111 ^ 0000 0000
ct1 = 1111 1111

i &= ct0 & ct1
i &= 1111 1111 & 1111 1111
i &= 1111 1111
i = 1111 1111 & 0100 1101
i = 0100 1101

key_state ^= i
key_state ^= 0100 1101
key_state = 0100 1101 ^ 0000 0000
key_state = 0100 1101

key_press |= key_state & i
key_press |= 0100 1101 & 0100 1101
key_press |= 0100 1101
key_press = 0100 1101 | 0000 0000
key_press = 0100 1101

-------------------------------------------------------------
2. Durchlauf:

i         = 0100 1101
ct0       = 1111 1111
ct1       = 1111 1111
key_state = 0100 1101
key_press = 0100 1101

i = key_state ^ ~KEY_PIN
i = 0100 1101 ^ ~1011 0010
i = 0100 1101 ^ 0100 1101
i = 0000 0000

ct0 = ~(ct0 & i)
ct0 = ~(1111 1111 & 0000 0000)
ct0 = ~0000 0000
ct0 = 1111 1111

ct1 = ct0 ^ (ct1 & i)
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
ct1 = 1111 1111 ^ 0000 0000
ct1 = 1111 1111

i &= ct0 & ct1
i &= 1111 1111 & 1111 1111
i &= 1111 1111
i = 1111 1111 & 0000 0000
i = 0000 0000

key_state ^= i
key_state ^= 0000 0000
key_state = 0000 0000 ^ 0100 1101
key_state = 0100 1101

key_press |= key_state & i
key_press |= 0100 1101 & 0000 0000
key_press |= 0000 0000
key_press = 0000 0000 | 0100 1101
key_press = 0100 1101

-------------------------------------------------------------
3. Durchlauf:

i         = 0000 0000
ct0       = 1111 1111
ct1       = 1111 1111
key_state = 0100 1101
key_press = 0100 1101

i = key_state ^ ~KEY_PIN
i = 0100 1101 ^ ~1011 0010
i = 0100 1101 ^ 0100 1101
i = 0000 0000

ct0 = ~(ct0 & i)
cto = ~(1111 1111 & 0000 0000)
ct0 = ~0000 0000
ct0 = 1111 1111

ct1 = ct0 ^ (ct1 & i)
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
ct1 = 1111 1111 ^ 0000 0000
ct1 = 1111 1111

i &= ct0 & ct1
i &= 1111 1111 & 1111 1111
i &= 1111 1111
i = 1111 1111 & 0000 0000
i = 0000 0000

key_state ^= i
key_state ^= 0000 0000
key_state = 0000 0000 ^ 0100 1101
key_state = 0100 1101

key_press |= key_state & i
key_press |= 0100 1101 & 0000 0000
key_press |= 0000 0000
key_press = 0000 0000 | 0100 1101
key_press = 0100 1101

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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:

.include "1200def.inc"

.def    deb_ct0 = r2
.def    deb_ct1 = r3
.def    deb_i = r4
.def    deb_keystate = r5
.def    deb_keypress = r6


debounce:
        ;i = key_state ^ ~KEY_PIN;

        in      deb_i, PINB
        com     deb_i
        eor     deb_i, deb_keystate

        ;ct0 = ~( ct0 & i );

        and     deb_ct0, deb_i
        com     deb_ct0

        ;ct1 = ct0 ^(ct1 & i);

        and     deb_ct1, deb_i
        eor     deb_ct1, deb_ct0

        ;i &= ct0 & ct1;

        and     deb_i, deb_ct0
        and     deb_i, deb_ct1

        ;key_state ^= i;

        eor     deb_keystate, deb_i     ;deb_keystate = debounced key state

        ;key_press |= key_state & i;

        and     deb_i, deb_keystate
        or      deb_keypress, deb_i     ;deb_keypress = key pin has changed from 1->0

        ret

Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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"

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier ein extra Demobeispiel für eine Taste mit 1s Takt:

http://www.avrfreaks.net/index.php?module=Freaks%2...

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


Peter

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was ist denn ATOMIC_BLOCK?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:

i = key_state ^ ~KEY_PIN
ct0 = ~(ct0 & i)
ct1 = ct0 ^ (ct1 & i)
i &= ct0 & ct1
key_state ^= i
key_press |= key_state & i


Eingang: 1011 0010 Taster active low -> '0' bedeutet gedrueckt

-------------------------------------------------------------
1. Durchlauf:

i         = 0000 0000
ct0       = 0000 0000
ct1       = 0000 0000
key_state = 0000 0000
key_press = 0000 0000

i = key_state ^ ~KEY_PIN
i = 0000 0000 ^ ~1011 0010
i = 0000 0000 ^ 0100 1101
i = 0100 1101

ct0 = ~(ct0 & i)
ct0 = ~(0000 0000 & 0100 1101)
ct0 = ~0000 0000
ct0 = 1111 1111

ct1 = ct0 ^ (ct1 & i)
ct1 = 1111 1111 ^ (0000 0000 & 0100 1101)
ct1 = 1111 1111 ^ 0000 0000
ct1 = 1111 1111

i &= ct0 & ct1
i &= 1111 1111 & 1111 1111
i &= 1111 1111
i = 1111 1111 & 0100 1101
i = 0100 1101

key_state ^= i
key_state ^= 0100 1101
key_state = 0100 1101 ^ 0000 0000
key_state = 0100 1101

key_press |= key_state & i
key_press |= 0100 1101 & 0100 1101
key_press |= 0100 1101
key_press = 0100 1101 | 0000 0000
key_press = 0100 1101

-------------------------------------------------------------
2. Durchlauf:

i         = 0100 1101
ct0       = 1111 1111
ct1       = 1111 1111
key_state = 0100 1101
key_press = 0100 1101

i = key_state ^ ~KEY_PIN
i = 0100 1101 ^ ~1011 0010
i = 0100 1101 ^ 0100 1101
i = 0000 0000

ct0 = ~(ct0 & i)
ct0 = ~(1111 1111 & 0000 0000)
ct0 = ~0000 0000
ct0 = 1111 1111

ct1 = ct0 ^ (ct1 & i)
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
ct1 = 1111 1111 ^ 0000 0000
ct1 = 1111 1111

i &= ct0 & ct1
i &= 1111 1111 & 1111 1111
i &= 1111 1111
i = 1111 1111 & 0000 0000
i = 0000 0000

key_state ^= i
key_state ^= 0000 0000
key_state = 0000 0000 ^ 0100 1101
key_state = 0100 1101

key_press |= key_state & i
key_press |= 0100 1101 & 0000 0000
key_press |= 0000 0000
key_press = 0000 0000 | 0100 1101
key_press = 0100 1101

-------------------------------------------------------------
3. Durchlauf:

i         = 0000 0000
ct0       = 1111 1111
ct1       = 1111 1111
key_state = 0100 1101
key_press = 0100 1101

i = key_state ^ ~KEY_PIN
i = 0100 1101 ^ ~1011 0010
i = 0100 1101 ^ 0100 1101
i = 0000 0000

ct0 = ~(ct0 & i)
ct0 = ~(1111 1111 & 0000 0000)
ct0 = ~0000 0000
ct0 = 1111 1111

ct1 = ct0 ^ (ct1 & i)
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
ct1 = 1111 1111 ^ 0000 0000
ct1 = 1111 1111

i &= ct0 & ct1
i &= 1111 1111 & 1111 1111
i &= 1111 1111
i = 1111 1111 & 0000 0000
i = 0000 0000

key_state ^= i
key_state ^= 0000 0000
key_state = 0000 0000 ^ 0100 1101
key_state = 0100 1101

key_press |= key_state & i
key_press |= 0100 1101 & 0000 0000
key_press |= 0000 0000
key_press = 0000 0000 | 0100 1101
key_press = 0100 1101

-------------------------------------------------------------
3. Durchlauf:

i         = 0000 0000
ct0       = 1111 1111
ct1       = 1111 1111
key_state = 0100 1101
key_press = 0100 1101

i = key_state ^ ~KEY_PIN
i = 0100 1101 ^ ~1011 0010
i = 0100 1101 ^ 0100 1101
i = 0000 0000

ct0 = ~(ct0 & i)
ct0 = ~(1111 1111 & 0000 0000)
ct0 = ~0000 0000
ct0 = 1111 1111

ct1 = ct0 ^ (ct1 & i)
ct1 = 1111 1111 ^ (1111 1111 & 0000 0000)
ct1 = 1111 1111 ^ 0000 0000
ct1 = 1111 1111

key_state ^= i
key_state ^= 0000 0000
key_state = 0000 0000 ^ 0100 1101
key_state = 0100 1101

key_press |= key_state & i
key_press |= 0100 1101 & 0000 0000
key_press |= 0000 0000
key_press = 0000 0000 | 0100 1101
key_press = 0100 1101

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Letzte ist natürlich der 4. Durchlauf

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier mein Testprogramm auf dem PC
#include <stdio.h>
#include <string.h>

#ifndef TRUE
#define TRUE  1
#define FALSE 0
#endif

typedef unsigned char uint8_t;

int main()
{
  uint8_t ct0 = 0;
  uint8_t ct1 = 0;
  uint8_t i = 0;
  uint8_t key_state = 0;
  uint8_t key_press = 0;
  uint8_t j;
 
  uint8_t KEY_PIN = 0xFF;
  
  for( j = 0; j < 15; ++j ) { 
    printf( "Durchgang: %d\n", j );
     
    i = key_state ^ ~KEY_PIN;
    printf( "i = %d\n", i );
    
    ct0 = ~(ct0 & i);
    ct1 = ct0 ^ (ct1 & i);
    printf( "ct0 = %d, ct1 = %d\n", ct0, ct1 );
    
    i &= ct0 & ct1;
    printf( "i = %d\n", i );

    key_state ^= i;
    key_press |= key_state & i;
    printf( "key_state = %d, key_press = %d\n", key_state, key_press );
    
    printf( "\n\n" );
    
    if( j > 3 )
      KEY_PIN = 0xFE;
  }
}

und der Output
Durchgang: 0
i = 0
ct0 = 255, ct1 = 255
i = 0
key_state = 0, key_press = 0


Durchgang: 1
i = 0
ct0 = 255, ct1 = 255
i = 0
key_state = 0, key_press = 0


Durchgang: 2
i = 0
ct0 = 255, ct1 = 255
i = 0
key_state = 0, key_press = 0


Durchgang: 3
i = 0
ct0 = 255, ct1 = 255
i = 0
key_state = 0, key_press = 0


Durchgang: 4
i = 0
ct0 = 255, ct1 = 255
i = 0
key_state = 0, key_press = 0


Durchgang: 5                              <-  Taste wird gedrückt
i = 1
ct0 = 254, ct1 = 255                      <-  der Counter beginnt zu zählen
i = 0
key_state = 0, key_press = 0


Durchgang: 6                              <- 2. ter Durchlauf
i = 1
ct0 = 255, ct1 = 254
i = 0
key_state = 0, key_press = 0


Durchgang: 7                              <- 3. Durchlauf
i = 1
ct0 = 254, ct1 = 254
i = 0
key_state = 0, key_press = 0


Durchgang: 8                              <- 4. Durchlauf
i = 1
ct0 = 255, ct1 = 255
i = 1
key_state = 1, key_press = 1             <- jetzt ist der Tastendruck registriert


Durchgang: 9
i = 0                                    <- KEY_PIN hat sich nicht verändert, trotzdem ist i hier bereits 0,
                                            da ~KEY_PIN und key_state identisch sind.
ct0 = 255, ct1 = 255
i = 0
key_state = 1, key_press = 1


Durchgang: 10
i = 0
ct0 = 255, ct1 = 255
i = 0
key_state = 1, key_press = 1

....

Autor: Jens Ebers (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ist meine "Kopfberechnung" falsch :\

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.