Tastenabfrage bei Menüführung - Ideenblockade

Gast #1748192
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!
#1748279
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
#1748437
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.
#1748444
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
Gast #1748469
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?
Gast #1748481
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.
Gast #1748484
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.
Gast #1748491
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).
Gast #1748501
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.
#1748510
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)
Gast #1748513
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).
#1748516
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
Angehängte Dateien:
Gast #1748523
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?
#1748528
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.
#1748543
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.
Persönliche Seite #1748556
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
#1748565
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'.
Gast #1748637
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
#1748646
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
#1748648
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.
#1748650
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"
#1748666
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
#1748685
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.
Gast #1748713
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
#1748729
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
....

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren