Forum: Mikrocontroller und Digitale Elektronik externer interrupt an Taster


von Frank Sinatra (Gast)


Lesenswert?

NAbend,

ich habe einen Taster an einem externen Interrupt Pin des uc (Interrupt 
0) angebunden. Den Pin habe ich als digitalen Eingang deklariert.

Ich habe den Interrupt 0 aktiviert und er soll auf der steigenden Flanke 
Triggern.

Der Interrupt wird jetzt (vermutlich durch das Prellen) laufend 
aufgerufen, ganz egal ob der Button auf High oder Low gelegt wird.

Ist es durch das Prellen überhaupt eine sinnvolle Lösung einen Taster 
über einen externen Interrupt abzufragen oder mache ich etwas 
grundsätzliches falsch?

von Hubert G. (hubertg)


Lesenswert?

PullDown vorhanden?

von c-hater (Gast)


Lesenswert?

Frank Sinatra schrieb:

> Der Interrupt wird jetzt (vermutlich durch das Prellen) laufend
> aufgerufen, ganz egal ob der Button auf High oder Low gelegt wird.

Das ist völlig normal.

> Ist es durch das Prellen überhaupt eine sinnvolle Lösung einen Taster
> über einen externen Interrupt abzufragen

Das kommt allein auf die Anwendung an.

Wenn der Taster einen einmaligen Vorgang startet, dann geht's. Also 
sowas wie "Not-AUS" etwa.

Wenn der Taster einen langen (deutlich länger als die Prellzeit) 
dauernden Vorgang startet, dann geht's auch. Die Entprellung kann hier 
durch die Laufzeit des Vorgangs selber erfolgen.

Wenn der Taster eine Zustandsänderung bewirkt und der Zielzustand nur 
durch die Betätigung eines anderen Tasters wieder verlassen werden kann, 
dann geht's auch. Hier erfolgt die Entprellung nach dem RS-Prinzip.

In allen anderen Fällen geht's nicht oder zumindest nicht ohne 
komplexere Klimmzüge, die sinnlos sind, weil sie gegenüber den üblichen 
Designmustern zur Entprellung keinerlei Vorteile bringen.

von Frank Sinatra (Gast)


Lesenswert?

Pull Down ist natürlich dran (10K bzw. 3K9)

von Thomas E. (thomase)


Lesenswert?

Da ist er ja wieder. Der wöchentliche Taster-Interrupt-Thread.

Wer Taster per Interrupt abfragt, statt die PeDa-Entprellung mit 
timergesteuertem Polling zu benutzen, gilt hier allgemein als halbgarer 
Vollpfosten.

Frank Sinatra schrieb:
> Ist es durch das Prellen überhaupt eine sinnvolle Lösung einen Taster
> über einen externen Interrupt abzufragen oder mache ich etwas
> grundsätzliches falsch?

Nein, das ist wirklich keine gute Idee.

Da musst du jetzt durch:

http://www.mikrocontroller.net/articles/Entprellung

Aber erstens ist das nicht so schlimm, wie es auf den ersten Blick 
aussieht. Zweitens hast du damit ein für alle Mal alle Probleme, die 
jetzt oder irgendwann mit Tastern, Relais oder anderem Klapperkram 
auftreten können, gelöst.

mfg.

: Bearbeitet durch User
von Frank Sinatra (Gast)


Lesenswert?

Hallo Herr Eckmann,

vielen Dank für den Link.
Habe die "PeDa" Enprellung implementiert. Die gute Nachricht: Es 
funktioniert. Die schlechte Nachricht: So ganz blicke ich den Code und 
die Anwendung noch nicht. Werde da wohl mal Zeile für Zeile durchgehen 
müßen.

Eine Frage habe ich aber noch:
get_key_long reagiert bei mir genauso schnell wie get_key_short obwohl 
ich den Taster in der KEY Maske eingetragen habe. Was muss ich noch 
beachten.

Kann mir Jemand kurz in eigenen Worte erklären was die get_key_rpt 
Funktion macht? Klar ist es ausführlich dokumentiert, mir aber trotzdem 
nicht ganz ersichtlich.

Ich bedanke mich

von Peter D. (peda)


Lesenswert?

Frank Sinatra schrieb:
> get_key_long reagiert bei mir genauso schnell wie get_key_short

Zeig mal einen Testcode.
Quellcode läßt sich nur äußerst schlecht in Prosa darstellen.

von halbgarer Vollpfosten (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Wer Taster per Interrupt abfragt, statt die PeDa-Entprellung mit
> timergesteuertem Polling zu benutzen, gilt hier allgemein als halbgarer
> Vollpfosten.

Dann wollen wir doch nicht verschweigen, was Du meinst: 
Beitrag "EIN-AUS mit Taster per Interrupt, ATtiny25 o.ä."
;-)

von Induktor (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Da ist er ja wieder. Der wöchentliche Taster-Interrupt-Thread.

Starte eine Petition, daß ein absolutes Verbot dieses Vorgehens
beschlossen und in Gesetzesform gegossen wird.

Deine Meinung verprellt die Leser...
;-)

von Karl H. (kbuchegg)


Lesenswert?

halbgarer Vollpfosten schrieb:
> Thomas Eckmann schrieb:
>> Wer Taster per Interrupt abfragt, statt die PeDa-Entprellung mit
>> timergesteuertem Polling zu benutzen, gilt hier allgemein als halbgarer
>> Vollpfosten.
>
> Dann wollen wir doch nicht verschweigen, was Du meinst:
> Beitrag "EIN-AUS mit Taster per Interrupt, ATtiny25 o.ä."
> ;-)

Dann solltest du hier aber auch nicht verschweigen, dass die Beschaltung 
an PB4 ein ganz wesentlicher Bestandteil der Entprellung ist. Ohne diese 
Beschaltung gehts nicht.

von halbgarer Vollpfosten (Gast)


Lesenswert?

Karl Heinz schrieb:
> Dann solltest du hier aber auch nicht verschweigen, dass die Beschaltung
> an PB4 ein ganz wesentlicher Bestandteil der Entprellung ist. Ohne diese
> Beschaltung gehts nicht.

Na und? Ist das nicht in der Schaltung gezeigt und beschrieben?
Oder wollen wir etwa die 1/10 Cent für R+C auf Millionenstückzahlen 
umrechnen?
Luxus hat seinen Preis! ;-)

von Frank Sinatra (Gast)


Lesenswert?

Also es scheint ne Runde Sache zu sein. Vielen Dank schon einmal dafür!
Habe es jetzt erstmal implementiert und mache mich nun ans Verständnis. 
Andersrum wäre vermutlich der sinnvollere Weg :)

was genau ist jetzt der Unterschied zwischen den 3 Funktionen.

get_key_press
get_key_short
get_key_long

Die bewirken bei mir genau das gleiche.
Was sie bewirken SOLLTEN ist natürlich klar.

Beispiel:
1
#define REPEAT_MASK     (1<<KEY_RUCK)
2
if (get_key_press(1<<KEY_RUCK))
3
...
4
if (get_key_short(1<<KEY_RUCK))
5
...
6
if (get_key_long(1<<KEY_RUCK))
7
...

reagiert nicht anderes auf längeren Tastendruck etc.

Wie kann ich überprüfen ob 2 Taster gleichzeitig angesprochen werden?

In etwa so?
1
if (get_key_press(1<<KEY_RUCK) && get_key_press(1<<KEY_VOR))
2
3
// oder besser so
4
5
if (get_key_press(1<<KEY_RUCK)
6
{
7
if (get_key_press(1<<KEY_VOR) wastun();
8
9
}

Mir ist klar, wenn ich den kompletten Code verstanden hätte, würde mir 
das richtige Anwenden auch leichter fallen. Merci

von Peter D. (peda)


Lesenswert?

Frank Sinatra schrieb:
> Wie kann ich überprüfen ob 2 Taster gleichzeitig angesprochen werden?

Es wird Dir schwer fallen, beide Tasten innerhalb 10ms zu drücken.

Aber es gibt auch dafür eine Lösung:
Beitrag "Universelle Tastenabfrage mit 2 Tastenerkennung"

von Peter D. (peda)


Lesenswert?

Frank Sinatra schrieb:
> get_key_press
> get_key_short
> get_key_long
>
> Die bewirken bei mir genau das gleiche.
> Was sie bewirken SOLLTEN ist natürlich klar.

Vermutlich nicht.
Du kannst gleichzeitig nur 2 Sachen unterscheiden und nicht 3!

get_key_short, get_key_long müssen zusammen verwendet werden und 
schließen get_key_press aus.

Also entweder kurz/lang erkennen oder nur drücken.

von Frank Sinatra (Gast)


Lesenswert?

hmm angenommen ich möchte die eine Funktion nur ausführen wenn KEY0 und 
KEY1 gleichzeitig gedrückt sind, also:
1
if( get_key_common( 1<<KEY0 | 1<<KEY1 ))
2
      LED_PORT ^= 1<<LED2;

wie genau muss dann

[c]
#define REPEAT_MASK
[/c)

definiert sein.

So wie ich es jetzt nutze, löst der Taster NICHT bei KEY1 aus, aber bei 
KEY0 und wenn KEY1 und KEY0 gleichzeitig gedrückt.

von Karl H. (kbuchegg)


Lesenswert?

Frank Sinatra schrieb:
> hmm angenommen ich möchte die eine Funktion nur ausführen wenn KEY0 und
> KEY1 gleichzeitig gedrückt sind, also:
>
>
1
> if( get_key_common( 1<<KEY0 | 1<<KEY1 ))
2
>       LED_PORT ^= 1<<LED2;
3
> 
4
>
>
> wie genau muss dann
>
> [c]
> #define REPEAT_MASK
> [/c)
>
> definiert sein.

REPEAT_MASK fungiert in den PeDa Routinen auf 2 Arten.
Zum einen legt es fest, auf welchen Tasten überhaupt ein Autoreapeat 
möglich ist, zum anderen legt es fest, bei welchen Tasten überhaupt eine 
Unterscheidung 'kurz' 'lang' gemacht werden kann.

Das hat historische Ursachen. Ganz ganz früher mal, gab es dieses 
kurz/lang noch nicht. Da konnten Tasten nur eine zusätzlichen 
Autorepeat. Die kurz/lang Unterscheidung ist erst später hinzugekommen 
und es zeigte sich, dass für beides derselbe Basismechanismus benutzt 
werden kann: eine zeitliche Steuerung. Das eine mal ist das die Zeit die 
verstreichen muss, bis der Autorrepeat einsetzt, das andere mal ist es 
die Zeit, ab der ein Tastendruck seinen Status von kurz auf lang ändert.

> So wie ich es jetzt nutze, löst der Taster NICHT bei KEY1 aus, aber bei
> KEY0 und wenn KEY1 und KEY0 gleichzeitig gedrückt.

Ich schätze mal, du hast KEY1 dann nicht in der Repeat Mask mit drinnen. 
Stör dich nicht am Namen 'REPEAT'. Der hat historische Gründe. Die Maske 
legt fest, welche Tasten eine zeitliche Sonderfunktion haben können.

Im Grunde könnte man diese Maske auch komplett aus dem Code rauswerfen, 
weil man ja sowieso durch den Funktionsaufruf festlegt, was man haben 
möchte.

: Bearbeitet durch User
von Frank Sinatra (Gast)


Lesenswert?

hmm, also in der Repeat Maske habe ich es definit mit drin, es 
funktioniert aber leider immer noch nur wie oben gesagt. Ich poste mal 
eben die dafür wichtigen Code Zeilen:
1
// Configure the Timer 1 interrupt handler
2
void __ISR(_TIMER_1_VECTOR, ipl2) Timer1Handler(void)
3
{
4
    // Clear the interrupt flag
5
    INTClearFlag(INT_T1);
6
    start_Timer();
7
    static unsigned int ct0 = 0xFF;
8
    static unsigned int ct1 = 0xFF;
9
    static unsigned int rpt;
10
    unsigned int i;
11
    i = key_state ^ ~KEY_PORT;
12
    ct0 = ~( ct0 & i );                         // Rreset or count ct0
13
    ct1 = ct0 ^ (ct1 & i);                      // Reset or count ct1
14
    i &= ct0 & ct1;                             // Count until roll over ?
15
    key_state ^= i;                             // Then toggle debounced state
16
    key_press |= key_state & i;                 // 0->1: key press detect
17
    key_release |= ~key_state & i;              // 1->0: key release detect
18
19
    if( (key_state & REPEAT_MASK) == 0 )                      // Check repeat function
20
        rpt = REPEAT_START;                     // Start delay
21
22
    if( --rpt == 0 ){
23
        rpt = REPEAT_NEXT;                      // Repeat delay
24
        key_rpt |= key_state & REPEAT_MASK;                   // Active low keys
25
    }
26
27
      //    CloseTimer1();
28
}
29
30
unsigned int get_key_press( unsigned int key_mask ){
31
32
    INTDisableInterrupts();
33
    key_mask &= key_press;                      // Read key(s)
34
    key_press ^= key_mask;                      // Clear key(s)
35
    INTEnableInterrupts();
36
    return key_mask;
37
}
38
39
unsigned get_key_common( unsigned key_mask ){
40
  return get_key_press((key_press & key_mask) == key_mask ? key_mask : 0);
41
}
42
43
44
45
#define REPEAT_MASK     (1 << KEY_OK | 1 << KEY_ON | 1 << KEY_NEXT | 1 << KEY_BACK | 1 << KEY_INFO)
46
47
if(get_key_common( 1 << KEY_BACK | 1 << KEY_NEXT ))
48
        {
49
            tuewas();
50
        }

Wie gesagt wird tewas() auch bei alleinigen Drücken von Next ausgelöst 
und nicht nur bei Drücken von NEXT UND BACK.

Vielen Dank noch für die Erläuterungen bzgl. der REPEATMASKE.

von Jochen (Gast)


Lesenswert?

> Wer Taster per Interrupt abfragt, statt die PeDa-Entprellung mit
> timergesteuertem Polling zu benutzen, gilt hier allgemein als halbgarer
> Vollpfosten.

Warum musst du gleich beleidigend werden? Zwangscharakter? Schreibe 
bitte in Zukunft nicht solche Unwahrheiten, wie " ... gilt hier ...", 
sondern schreibe nur in deinem Namen. Oder fehlt es dir an 
Selbstbewusstsein, dass du dich hinter der Allgemeinheit verstecken 
musst?

Mit deinen Kenntnissen ist auch nicht weit her, sonst wüsstest du, dass 
es mehr als eine Methode der Entprellung gibt.

von Karl H. (kbuchegg)


Lesenswert?

Frank Sinatra schrieb:


> Wie gesagt wird tewas() auch bei alleinigen Drücken von Next ausgelöst
> und nicht nur bei Drücken von NEXT UND BACK.
>

Bist du sicher, dass du vorher nicht sowas stehen hast
1
  if( get_key_press( 1 << KEY_BACK ) )
2
    tuewas();
3
4
  if(get_key_common( 1 << KEY_BACK | 1 << KEY_NEXT ))
5
  {
6
    tuewas();
7
  }


Denn DAS geht so nicht. Wenn ein get_key_press erst mal einen 
Tastendruck geliefert hat, dann ist der sozusagen ausgeliefert und steht 
beim nächsten get_key_irgendwas nicht mehr zur Verfügung.

Wie schon gesagt: Gleichzeitiges Drücken von Tasten zu erkennen ist 
schweirig, weil es dir als Benutzer nicht gelingt, beide Tasten wirklich 
in derselben Millisekunde zu drücken. Für deinen µC ist es aber 
überhaupt kein Problem. Nur erkennt der eben, dass zuerst die eine Taste 
und dann erst die andere Taste gedrückt wurde. Wenn daher das Drücken 
der einen Taste bereits im Programm ausgewertet wurde, dann ist es Essig 
mit dem Erkennen von 2 'gleichzeitigen' Tastendrücken. Für dein Programm 
präsentiert sich das als Nacheinander Drücken von 2 Tasten. Selbst wenn 
dein Benutzer meint, er drückt die beiden "gleichzeitig".

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Jochen schrieb:
>> Wer Taster per Interrupt abfragt, statt die PeDa-Entprellung mit
>> timergesteuertem Polling zu benutzen, gilt hier allgemein als halbgarer
>> Vollpfosten.
>
> Warum musst du gleich beleidigend werden?

Weil dieses Thema jede Woche 5 mal aufkommt. Mit dem immer gleichen 
Ergebnis.

> Mit deinen Kenntnissen ist auch nicht weit her, sonst wüsstest du, dass
> es mehr als eine Methode der Entprellung gibt.

Natürlich gibts die.
Aber die eine Methode 'externer Interrupt' hat keine Vorteile, ausser 
das man den µC damit aus dem Sleep holen kann und man mit externer 
Hardware nachhelfen muss, wenn man kein komplizierten Codemonster 
produzieren will.

: Bearbeitet durch User
von Frank Sinatra (Gast)


Lesenswert?

Karl Heinz schrieb:
> Frank Sinatra schrieb:
>
>> Wie gesagt wird tewas() auch bei alleinigen Drücken von Next ausgelöst
>> und nicht nur bei Drücken von NEXT UND BACK.
>>
>
> Bist du sicher, dass du vorher nicht sowas stehen hast  if(
> get_key_press( 1 << KEY_BACK ) )
>     tuewas();
>
>   if(get_key_common( 1 << KEY_BACK | 1 << KEY_NEXT ))
>   {
>     tuewas();
>   }

Es ist die allererste Tasterabfrage überhaupt!

von Karl H. (kbuchegg)


Lesenswert?

Ich denke, PeDa sollte das
1
unsigned get_key_common( unsigned key_mask ){
2
  return get_key_press((key_press & key_mask) == key_mask ? key_mask : 0);
3
}
nochmal überdenken. (Ich geh davon aus, dass du den Code aus dem von 
PeDa angegebenen Link korrekt übernommen hast)

Denn hier gibt es ein potentielles Problem.

Dieses get_key_press
1
unsigned int get_key_press( unsigned int key_mask ){
2
3
    INTDisableInterrupts();
4
    key_mask &= key_press;                      // Read key(s)
5
    key_press ^= key_mask;                      // Clear key(s)
6
    INTEnableInterrupts();
7
    return key_mask;
8
}
löscht die Drück-Information der ausmaskierten Tasten, selbst wenn nicht 
alle Tasten wirklich gleichzeitig gedrückt sind.

von Frank Sinatra (Gast)


Lesenswert?

Richtig, der Code ist nur angepasst, aber freundlicherweise aus dem 
angepassten Link übernommen. Wie gesagt fehlt mir noch das Verständnis 
des kompletten Codes. Ich habe ihn einfach implementiert und hoffe auf 
ein wenig Learning by doing.

Die Abfrage 2er Tasten gleichzeitig wäre schon toll. Ansonsten bin ich 
begeistert von der gegebenen Funktionalität!

Daumen Hoch dafür.

von Karl H. (kbuchegg)


Lesenswert?

Ich denke, das hier (aus dem Link)
1
Ich weiß nicht, warum alle so scharf auf 2-Tastenbedienung sind.
2
Man muß einfach nur logisch denken, wie ein MC das erkennen soll,
3
ohne Hellseherei.
4
Man kann es mit meinen Funktionen erreichen. Gleichzeitig bedeutet
5
dann, innerhalb der Zeit zur Langdruckerkennung. Man kann also
6
einen Einzeldruck nicht mehr sofort erkennen.
7
8
9
  for(;;){                                              // main loop
10
    switch( get_key_long(  1<<KEY0 | 1<<KEY1 )
11
            get_key_short( 1<<KEY0 | 1<<KEY1 )){

(ein paar Postings über dem Verlinkten) ist der richtige Weg. Der ist 
auch logisch. Denn get_key_press detektiert einen Tastendruck sofort 
beim Niederdrücken. Genau das kann man hier aber nicht brauchen. Um 
gleichzeitiges Drücken zu erkennen, muss man schon eine zeitlang warten, 
ehe feststeht, dass die minimale Wartezeit, in der eine 2.te Taste 
gedrückt werden könnte, verstrichen ist. Das leistet aber get_key_press 
nicht.

von Karl H. (kbuchegg)


Lesenswert?

Frank Sinatra schrieb:
> Richtig, der Code ist nur angepasst, aber freundlicherweise aus dem
> angepassten Link übernommen. Wie gesagt fehlt mir noch das Verständnis
> des kompletten Codes.

Den brauchst du auch nicht wirklich.

Du musst nur wissen, dass in der Variablen key_press für jede Taste 1 
Bit auf 1 geht, wenn der Tastendruck beim Ändern das Portpins als 
zulässig erkannt wurde.

Die 'Black Magic' innerhalb der ISR muss man dazu nicht verstehen. Es 
genügt zu wissen, wie und wann die Bits in den globalen Variablen ihre 
Werte ändern.

> Die Abfrage 2er Tasten gleichzeitig wäre schon toll.

Persönlich bin ich kein Fan davon. Denn das bedeutet automatisch, dass 
der µC nicht mehr beim Niederdrücken einer Taste auf etwas reagieren 
kann, sondern erst beim Loslassen (oder nach einer angemessenen 
Wartezeit). Genau das Problem gibt es in Windows ja auch: wie 
unterscheidet man einen einfachen Mausklick von einem Doppelten? DU 
kannst es erst unterscheiden, wenn nach dem Drückken eine bestimmte Zeit 
vergangen ist. In der Zeit dazwischen, zwischen Niederdrücken und dem 
Ablauf dieser Zeit, hängt alles in der Schwebe. Kommt noch ein weiterer 
Mausklick oder nicht?

von halbgarer Vollpfosten (Gast)


Lesenswert?

Karl Heinz schrieb:
> Aber die eine Methode 'externer Interrupt' hat keine Vorteile, ausser
> das man den µC damit aus dem Sleep holen kann und man mit externer
> Hardware nachhelfen muss, wenn man kein komplizierten Codemonster
> produzieren will.

Gib doch einfach zu, dass Du es nicht begriffen hast - garnicht 
begreifen willst.
Mit "externer Hardware nachhelfen" kann man auch anderns formulieren:
eine externe Schutzbeschaltung vorsehen!

"Kompliziertes Codemonster": absoluter Blödsinn!
Vielleicht bist Du einfach schon zu alt, wenn es Dich irritiert, dass 
eine Enprellung im ms-Bereich stattfinden kann. Damit bekommt man 
Betätigungen von Tastern (insbesondere auch Reedrelais) sicher 
entprellt, während das Pollen per Software nur einen offenen Kontakt 
feststellen kann: Guten Morgen!

Frank Sinatra schrieb:
> Wie gesagt fehlt mir noch das Verständnis
> des kompletten Codes.

Schön zu lesen ;-)

von halbgarer Vollpfosten (Gast)


Lesenswert?

Frank Sinatra schrieb:
> Die Abfrage 2er Tasten gleichzeitig wäre schon toll.

Auch das geht per Interrupt ;-)
Beitrag "Re: EIN-AUS mit Taster per Interrupt, ATtiny25 o.ä."

von Frank Sinatra (Gast)


Lesenswert?

ALso ich finde die Timer Interrupt Routine sehr gut und möchte mich 
gerade bei Herrn Buchegg recht herzlich bedanken, der hier schon 
häufiger mit Engels Geduld geholfen hat. Dabei ist er einer der Wenigen, 
die sich nicht bei der erstbesten dummen Frage auf einen stürzen :)

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Denn das bedeutet automatisch, dass
> der µC nicht mehr beim Niederdrücken einer Taste auf etwas reagieren
> kann, sondern erst beim Loslassen (oder nach einer angemessenen
> Wartezeit).

Um das ganz klar anzusprechen.
Es gibt einen WESENTLICHEN UNterschied zwischen get_key_press und 
get_key_short/long

get_key_press liefert die Information des Tastendrucks beim 
Niederdrücken der Taste. Wird eine Taste gedrückt und bleibt das auch so 
nach der Entprellung, dann kriegst du diesen Tastendruck von 
get_key_press sofort gemeldet (und der entsprechende Tastendruck wird 
intern gelöscht, weil er ja bereits behandelt wurde). Damit kannst du 
aber nicht einen langen von einem kurzen Tastendruck unterscheiden, denn 
zum Zeitpunkt des Niederdrückens steht ja noch nicht fest, welches von 
beiden es werden wird. Das kann man ja erst nach einer angemessenen 
Zeitspanne sagen (bzw. wenn die Taste wieder losgelassen wurde). Ist der 
Tastendruck kürzer als diese Zeitspanne, dann war es ein kurzer. Ist 
hingegen nach dieser Zeitspanne die Taste immer noch gedrückt, dann kann 
es kein kurzer sein, sondern es ist ein langer. Damit das aber 
funktioniert, darf die Information, dass die Taste gedrückt wurde, nicht 
schon von einem get_key_press gelöscht worden sein. Daher können die 
beiden Funktionseinheiten 'Tastendruck erkennen', 'Tastendruck als 
kurz/lang erkennen' auch nicht gemeinsam eingesetzt werden.

Und beim Erkennen von gemeinsamen Tastendrücken ist es nicht anders. Um 
zu erkennen, dass 2 Tasten gleichzeitig gedrückt worden sind, darf die 
jeweils zuerst gedrückte Taste (und das ist unvermeidlich), nicht schon 
mehr oder weniger sofort nach dem Niederdrücken ausgewertet werden.

von Karl H. (kbuchegg)


Lesenswert?

halbgarer Vollpfosten schrieb:
> Karl Heinz schrieb:
>> Aber die eine Methode 'externer Interrupt' hat keine Vorteile, ausser
>> das man den µC damit aus dem Sleep holen kann und man mit externer
>> Hardware nachhelfen muss, wenn man kein komplizierten Codemonster
>> produzieren will.
>
> Gib doch einfach zu, dass Du es nicht begriffen hast - garnicht
> begreifen willst.
> Mit "externer Hardware nachhelfen" kann man auch anderns formulieren:
> eine externe Schutzbeschaltung vorsehen!

Ja. Ist auch wahnsinnig wichtig bei einem Taster der nach Masse 
schaltet.

> "Kompliziertes Codemonster": absoluter Blödsinn!
> Vielleicht bist Du einfach schon zu alt,

Vielleicht hab ich einfach nur schon zuviele sogenannte Entprellungen 
gesehen, die das Papier nicht wert waren, auf dem sie ausgedruckt 
wurden?

von Peter D. (peda)


Lesenswert?

Frank Sinatra schrieb:
> So wie ich es jetzt nutze, löst der Taster NICHT bei KEY1 aus, aber bei
> KEY0 und wenn KEY1 und KEY0 gleichzeitig gedrückt.

Du mußt das Beispiel genauso übernehmen, wie in dem Link, also 3 
Funktionsaufrufe.
Wenn Du wirklich nur "gleichzeitig" haben willst, mußt Du die beiden 
anderen ja nicht auswerten, sondern nur ausführen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:


>> Vielleicht bist Du einfach schon zu alt,


Im übrigen wurde hier
Beitrag "Re: EIN-AUS mit Taster per Interrupt, ATtiny25 o.ä."
schon alles dazu gesagt, was es zu sagen gibt.

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Frank Sinatra schrieb:
>> So wie ich es jetzt nutze, löst der Taster NICHT bei KEY1 aus, aber bei
>> KEY0 und wenn KEY1 und KEY0 gleichzeitig gedrückt.
>
> Du mußt das Beispiel genauso übernehmen, wie in dem Link, also 3
> Funktionsaufrufe.
> Wenn Du wirklich nur "gleichzeitig" haben willst, mußt Du die beiden
> anderen ja nicht auswerten, sondern nur ausführen.


Hmm.
wo versteckt sich da wieder die kleine Teufelei in
Beitrag "Universelle Tastenabfrage mit 2 Tastenerkennung"
:-)

Ich find nicht raus, warum
1
unsigned get_key_common( unsigned key_mask ){
2
  return get_key_press((key_press & key_mask) == key_mask ? key_mask : 0);
3
}
das Gewünschte leisten soll. Selbst wenn ich zuvor 2 Aufrufe von 
get_key_short in Gedanken hinzufüge.

Das hier
1
    switch( get_key_long(  1<<KEY0 | 1<<KEY1 ) |
2
            get_key_short( 1<<KEY0 | 1<<KEY1 )){
3
4
      case 1<<KEY0:             LED_PORT ^= 1<<LED0; break;
5
6
      case 1<<KEY1:             LED_PORT ^= 1<<LED1; break;
7
8
      default:
9
                if((key_press & (1<<KEY0 | 1<<KEY1)) != (1<<KEY0 | 1<<KEY1))
10
                  break;
11
                get_key_press( 1<<KEY0 | 1<<KEY1 );
12
13
      case 1<<KEY0 | 1<<KEY1:   LED_PORT ^= 1<<LED2; break;
14
    }

ist absolut logisch. Das ist leicht zu verstehen. Aber mit der common 
Routine und wie du sie im Beispiel benutzt, komm ich nicht klar.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> ist absolut logisch. Das ist leicht zu verstehen. Aber mit der common
> Routine und wie du sie im Beispiel benutzt, komm ich nicht klar.

Ach, mein Gott.
Stirn auf Tisch klatsch.

Ja, jetzt hab ichs. Ich hab mich in der Klammerung vertan und die ganze 
Zeit was anderes gelesen als tatsächlich dort steht.
Ganz im Gegenteil. Das ist noch simpler als ich die ganze Zeit gedacht 
hab.

: Bearbeitet durch User
von halbgarer Vollpfosten (Gast)


Lesenswert?

Karl Heinz schrieb:
> Ach, mein Gott.
> Stirn auf Tisch klatsch.
>
> Ja, jetzt hab ichs. Ich hab mich in der Klammerung vertan und die ganze
> Zeit was anderes gelesen als tatsächlich dort steht.
> Ganz im Gegenteil. Das ist noch simpler als ich die ganze Zeit gedacht
> hab.

Irren ist menschlich und mache Dinge brauchen ihre Zeit, sie zu 
verstehen. Hier und auch woanders!

von Karl H. (kbuchegg)


Lesenswert?

Frank Sinatra schrieb:

Hmm.

> So wie ich es jetzt nutze, löst der Taster NICHT bei KEY1 aus, aber bei
> KEY0 und wenn KEY1 und KEY0 gleichzeitig gedrückt.

Nachdem ich gefunden habe, wo ich mich im
1
unsigned get_key_common( unsigned key_mask ){
2
  return get_key_press((key_press & key_mask) == key_mask ? key_mask : 0);
3
}

komplett verrannt habe (das ganze hat so erst mal nichts mit Repeat zu 
tun), seh ich aber immer noch nicht, wie es zum beobachteten Effekt 
kommt. Ich denke es wird Zeit für vollständigen Code.
Eigentlich dürfte ein
1
  while( 1 ) {
2
3
    if(get_key_common( 1 << KEY_BACK | 1 << KEY_NEXT ))
4
    {
5
      tuewas();
6
    }
7
  }

nur dann bei tuewas() landen, wenn auch wirklich beide Tasten gedrückt 
sind.

Funktioniert denn ein
1
  while( 1 ) {
2
    if( get_key_press( 1 << KEY_BACK ) )
3
      Toggle Led 1
4
5
    if( get_key_press( 1 << KEY_NEXT ) )
6
      Toggle Led 2
7
  }

so wie es soll?

(Wichtig: In der Hauptschleife jeweils nur sinngemaess genau diese Code. 
Nichts anderes. Insbesondere darf get_key_common nicht gemeinsam mit 
get_key_press in derselben Hauptschleife benutzt werden.)

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Karl Heinz schrieb:

> Aber die eine Methode 'externer Interrupt' hat keine Vorteile, ausser
> das man den µC damit aus dem Sleep holen kann und man mit externer
> Hardware nachhelfen muss, wenn man kein komplizierten Codemonster
> produzieren will.

Das stimmt doch in dieser Allgemeinheit nicht. Ich habe im zweiten oder 
dritten Posting dieses Threads klargestellt, daß es drei Ausnahmen von 
der Regel gibt.

Und das angeblich so hoch"komplizierte Codemonster", was in diesen drei 
Fällen zur Entprellung benutzt wird, reduziert sich in Fall 1 auf exakt 
NULL Zeilen Quelltext (weil hier eben einfach keine Entprellung nötig 
ist), in den beiden anderen Fällen auf je ZWEI Zeilen.

Und zwar ist unabhängig von der verwendeten Programmiersprache hier eine 
"Zeile" wirklich nur eine Zeile, was wohl die absolute Trivialität des 
Sachverhaltes betont.

Wenn du zu blöd bist, den Sachverhalt hinreichend abstrakt zu betrachten 
und/oder die Vorteile zu begreifen, die sich ergeben können, wenn man 
eben einen dieser drei Sonderfälle hat, dann tust du mir einfach nur 
leid...

Wahrscheinlich eine Art Fachidiot. Immer getreu dem Spruch "das haben 
wir doch immer so gemacht, das hat sich bewährt", bar jeder Fantasie, 
bar jedes eigenständigen Denkens.

von Frank Sinatra (Gast)


Lesenswert?

Hallo nochmal

Ein einfaches ...

>
1
> 
2
>   while( 1 ) {
3
> 
4
>     if(get_key_common( 1 << KEY_BACK | 1 << KEY_NEXT ))
5
>     {
6
>       tuewas();
7
>     }
8
>   }
9
>
 fuktioniert nicht nur wenn beide Tasten gedrückt werden, sondern eben 
auch wenn nur KEY_NEXT gedrückt wird.


Hingegen ...
>
1
>   while( 1 ) {
2
>     if( get_key_press( 1 << KEY_BACK ) )
3
>       Toggle Led 1
4
> 
5
>     if( get_key_press( 1 << KEY_NEXT ) )
6
>       Toggle Led 2
7
>   }
8
>

funktioniert einwandfrei.


Was wie Herr Danegger empfohlen hat jedoch funktioniert ist...
1
 if( get_key_short( 1<<KEY_NEXT ))
2
Nop();
3
4
5
    if( get_key_short( 1<<KEY_BACK ))
6
Nop();
7
8
9
    if( get_key_common( 1<<KEY_NEXT | 1<<KEY_BACK ))
10
      tueirgendetwas();

Darf ich dann wie gesagt nicht nochmal in derselben Schleife die 
get_key_press() Funktion verwenden?

Gruss

von Frank SInatra (Gast)


Lesenswert?

Guten Morgen,

nochmal eine Bitte. Gibt es eine einfache Grundregel in welchen 
Zusammenhängen und Kombis die verschiedenen Funktionen get_key_short, 
get_key_long und get_key_press genutzt werden dürfen. Im allgemeinen bin 
ich schon mit der get_key_press Funktion zufrieden, d.h. ich will 
eigentlich gar nicht abfragen ob der Taster kurz oder lang gedrückt 
wurde.

Wenn ich beispielsweise die get_key_press Funktion nutze, darf ich mit 
dieser nach Lust und Laune alle Taster abfragen oder muss bzgl. des Key 
States irgendwas bestimmtes beachtet werden. Ich habe das Gefühl, dass 
beispielsweise durch
1
while(!get_key_press(1<<KEY_BACK))

durch drücken teilweise auch
1
if(get_key_press(1<<KEY_menu1))
2
        {
3
         tuemenu2();
4
        }

tuemenu2(); ausgeführt wird OHNE das KEY_menu1 wirklich gedrückt wird.

Was muss bei der get_key_press Funktion bzgl. des Keystates genau 
beachtet werden?

von Frank SInatra (Gast)


Lesenswert?

Ich spezifiziere mein Problem noch einmal genauer mit etwas Code:
1
if(get_key_press(1<<KEY_BACK))
2
{tuewas();}
3
if(get_key_press(1<<KEY_OK))
4
{
5
while(!get_key_press(1<<KEY_OK))
6
{
7
//wenn hier KEY_BACK gedrückt wird, wird ebenfalls tuewas(); ausgeführt
8
// dies ist aber nicht gewünscht, verstehe ich auch nicht, da wir uns in  einer Endlosschleife befinden und der // taster KEY_BACK ja gar nicht 
9
// abgefragt wird. Vermutlich wieder ein C Problem?
10
}
11
}

Problematik siehe bitte in den Kommentaren!

von Karl H. (kbuchegg)


Lesenswert?

Frank SInatra schrieb:

> Wenn ich beispielsweise die get_key_press Funktion nutze, darf ich mit
> dieser nach Lust und Laune alle Taster abfragen oder muss bzgl. des Key
> States irgendwas bestimmtes beachtet werden.

Solange du nur get_key_press benutzt, kannst du das nach Lust und Laune 
benutzen.

Schwieriger wird es wenn da irgendwelche Zeitdinge ins Spiel kommen. Und 
du musst dir darüber im klaren sein, dass es im Rechner kein 
'gleichzeitig' gibt. Vor allen Dingen dann nicht, wenn Menschen im Spiel 
sind. Egal wie sehr du dich auch bemühst, du schaffst es nicht 2 Tasten 
exakt gleichzeitig zu drücken. Das ist nun mal ein Faktum, das du nicht 
ignorieren kannst.

Wenn du also
1
   if( get_key_press( 1 << KEY1 ) )
2
     ...
3
4
   if( get_key_press( 1 << KEY2 ) )
5
     ...
6
7
   if( get_key_common( 1 << KEY1 | 1 << KEY2 ) )
8
     ...

programmierst, dann wird es praktisch immer so sein, dass einer der 
beiden get_key_press dem get_key_common einen der beiden Tastendrücke 
"wegschnappen" wird. D.h. die Einzelerkennungen werden tadellos 
funktionieren, aber der gemeinsame Tastendruck wird nicht funktionieren.


> Ich habe das Gefühl, dass
> beispielsweise durch

'Gefühl zu haben' ist an dieser Stelle ganz schlecht. Wenn du denkst 
irgendetwas klappt nicht so, wie du denkst, dann überlege dir, wie du 
das testen kannst.

> Was muss bei der get_key_press Funktion bzgl. des Keystates genau
> beachtet werden?

Bis auf die Zeitsache: gar nichts.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Frank SInatra schrieb:
> Ich spezifiziere mein Problem noch einmal genauer mit etwas Code:

Du hast mit ziemlicher Sicherheit irgendein anders Logikproblem im Code.

Gegen die Systematik
1
  while( 1 )
2
  {
3
4
    if( get_key_press( 1 << KEY_BACK ) )
5
      tuewas();
6
 
7
    if( get_key_press( 1 << KEY_OK ) )
8
    {
9
      while( !get_key_press( 1 << KEY_OK ) )
10
      {
11
        // wenn hier KEY_BACK gedrückt wird, wird ebenfalls
12
        // tuewas(); ausgeführt
13
        // dies ist aber nicht gewünscht, verstehe ich auch nicht, da
14
        // wir uns in  einer Endlosschleife befinden und der
15
        // taster KEY_BACK ja gar nicht 
16
        // abgefragt wird. Vermutlich wieder ein C Problem?
17
      }
18
    }
19
  }

ist nichts einzuwenden. Ich schätze mal, die Programmausführung steckt 
in Wirklichkeit gar nicht in der inneren Schleife. Teste das halt mal, 
in dem du dir entsprechende Ausgaben einbaust, die dir das anzeigen
1
  while( 1 )
2
  {
3
4
    if( get_key_press( 1 << KEY_BACK ) )
5
      tuewas();
6
 
7
    if( get_key_press( 1 << KEY_OK ) )
8
    {
9
10
      **** irgendeine LED einschalten ****
11
12
      while( !get_key_press( 1 << KEY_OK ) )
13
      {
14
        .....
15
      }
16
17
      **** die LED wieder ausschalten ****
18
    }
19
  }

: Bearbeitet durch User
von Frank SInatra (Gast)


Lesenswert?

OK vielen Dank. Habe ich einmal eingebaut.
Der Code sieht wie folgt aus.


>   while( 1 )
>   {
>
>     if( get_key_press( 1 << KEY_BACK ) )
>       tuewas();
>
>     if( get_key_press( 1 << KEY_OK ) )
>     {
>
>       **** irgendeine LED einschalten ****
>
>       while( !get_key_press( 1 << KEY_OK ) )
>       {
>         .....
>       }
>
>       **** die LED wieder ausschalten ****
>     }
>   }

Wenn KEY_OK gedrückt wird wird komischerweise die LED eingeschlatet UND 
tuewas(); ausgeführt.
Was jedoch funktioniert ist:
1
>   while( 1 )
2
>   {
3
> 
4
>     if( get_key_press( 1 << KEY_BACK ) )
5
>       tuewas();
6
> 
7
>     if( get_key_press( 1 << KEY_OK ) )
8
>     {
9
> 
10
>       **** irgendeine LED einschalten ****
11
> 
12
>       while( !get_key_press( 1 << KEY_OK ) )
13
>       {
14
>         if( get_key_press( 1 << KEY_BACK )
15
>          tuenochetwasanderes();
16
>       }
17
> 
18
>       **** die LED wieder ausschalten ****
19
>     }
20
>   }

D.h. wenn ich den OK Button nochmal abfrage wird in der TAT NUR die LED 
getoogelt und NICHT mehr tuwas() ausgeführt. Jedoch NUR MIT dieser 
zusätzlichen Abfrage:

von Karl H. (kbuchegg)


Lesenswert?

Zeig deinen echten Problemcode und nicht irgenbdwas fürs Forum her 
gerichtetes.

von Peter D. (peda)


Lesenswert?

Karl Heinz schrieb:
> while( !get_key_press( 1 << KEY_OK ) )
>       {
>         // wenn hier KEY_BACK gedrückt wird, wird ebenfalls
>         // tuewas(); ausgeführt
>         // dies ist aber nicht gewünscht, verstehe ich auch nicht, da
>         // wir uns in  einer Endlosschleife befinden und der
>         // taster KEY_BACK ja gar nicht
>         // abgefragt wird. Vermutlich wieder ein C Probl

Das ist vollkommen korrekt.
Das Bit wird ja im Interrupt gesetzt und wenn Du es nicht abholst, 
bleibt es das auf ewig.
Du mußt also beim Verlassen des While einen Dummyaufruf zum Löschen 
machen.

Am besten läßt Du solche verchachtelten While ganz sein. Bei sowas 
verliert man nämlich ruckzuck den Überblick.

von Frank SInatra (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Du mußt also beim Verlassen des While einen Dummyaufruf zum Löschen
> machen.

OK, sowas in der Art habe ich mir schon gedacht, also sowas hier:
1
>   while( 1 )
2
>   {
3
>
4
>     if( get_key_press( 1 << KEY_BACK ) )
5
>       tuewas();
6
>
7
>     if( get_key_press( 1 << KEY_OK ) )
8
>     {
9
>
10
>       **** irgendeine LED einschalten ****
11
>
12
>       while( !get_key_press( 1 << KEY_OK ) )
13
>       {
14
>         // hier irgendwas tun();
15
>         if(get_key_press(1<<KEY_BACK))
16
>         {// nix machen, nur dummy}
17
>       }
18
>
19
>       **** die LED wieder ausschalten ****
20
>     }
21
>   }

von Karl H. (kbuchegg)


Lesenswert?

Frank SInatra schrieb:
> Peter Dannegger schrieb:
>> Du mußt also beim Verlassen des While einen Dummyaufruf zum Löschen
>> machen.
>
> OK, sowas in der Art habe ich mir schon gedacht, also sowas hier:

Reden wir alle vom selben?

IN dem Code hier, wird beim ersten Drücken von OK in den 'Subzweig' 
gegangen, indem der µC dann in der while-Schleife hängt. In dieser 
Schleife kannst du BACK drücken, ohne das erst mal irgendwas passiert. 
E>rst durch erneutes Drücken von OK kommst du aus der Schleife raus. 
Hast du aber 'innerhalb' der Schleife BACK gedrückt, dann wird der 
Tastendruck registriert aber logischerweise nicht bearbeitet. Er geht 
aber nicht verloren.


Ist es das, was du beobachtet hast? Denn deine Problembeschreibung hat 
sich zumindest für mich ganz anders angehört.


>>         if(get_key_press(1<<KEY_BACK))
>>         {// nix machen, nur dummy}

Find ich so keine gute Lösung.
Kommt ein weiterer Key dazu, dann muss man in allen derartigen Dummy 
Aufrufen diesen Key ergänzen.

von Frank SInatra (Gast)


Lesenswert?

Karl Heinz schrieb:
> Frank SInatra schrieb:

> IN dem Code hier, wird beim ersten Drücken von OK in den 'Subzweig'
> gegangen, indem der µC dann in der while-Schleife hängt. In dieser
> Schleife kannst du BACK drücken, ohne das erst mal irgendwas passiert.
> E>rst durch erneutes Drücken von OK kommst du aus der Schleife raus.
> Hast du aber 'innerhalb' der Schleife BACK gedrückt, dann wird der
> Tastendruck registriert aber logischerweise nicht bearbeitet. Er geht
> aber nicht verloren.


Genau dieses hier. In der while Schleife sollte der Back-Button wenn 
möglich gar nicht beachtet werden, also auch nicht registriert. Dies ist 
mein Problem. Sorry, wenn ich mich da mißverständlich ausgedrückt habe.

von Karl H. (kbuchegg)


Lesenswert?

Frank SInatra schrieb:

> Genau dieses hier. In der while Schleife sollte der Back-Button wenn
> möglich gar nicht beachtet werden, also auch nicht registriert.

Wird er aber. Jeder Tastendruck wird registriert.

> Dies ist
> mein Problem.

Ok. Dann musst du ihn löschen oder dein Programm so formulieren, dass es 
kein Problem ist. Letzters ist der Normalfall, in dem man in seinem 
Programm einen Status einbaut. Denn diese 'warte auf 
Tastendruck'-Schleifen sind auf einem µC sowieso meistens Gift, wie PeDa 
schon sagte. Es ist genau diese 'Wir warten auf etwas' Mentalität, die 
in einem Progrmm verhindert, dass ein µC scheinbar mehrere Dinge 
gleichzeitig macht. Denn auch wenn er gerade auf eine Bestätigung von 
dir wartet, deswegen darf der Codeteil, der das Badewasser überwacht und 
gegebenenfalls den Zulauf abdreht ja trotzdem nicht stillstehen sondern 
muss auch weiterhin ausgeführt werden.
1
  editingValue = false;
2
3
  while( 1 )
4
  {
5
    if( get_key_press( 1 << KEY_BACK ) )
6
    {
7
      if( !editingValue )
8
      {
9
         Taste BACK kann zum jetzigen Zeitpunkt gedrückt werden
10
      }
11
    }
12
13
    if( get_key_press( 1 << KEY_OK ) )
14
    {
15
       if( editingValue )
16
       {
17
         Verändern eines Wertes war im Gang und ist abgeschlossen
18
         editingValue = false;
19
       }
20
       else
21
       {
22
         ein aktueller Wert wird zum Verändern freigegeben
23
         editingValue = true;
24
       }
25
    }
26
27
28
    // alles was weiterlaufen muss, auch wenn der Benutzer
29
    // gerade editiert
30
    if( Badewasser_Sensor == Wasser_Voll )
31
      Ventil_close
32
  }

von Frank SInatra (Gast)


Lesenswert?

OK, das Prinzip ist verstanden. Werde versuchen dies mal umzusetzten. 
Wiederum recht herzlichen Dank für die freundliche Unterstützung!

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.