Forum: Mikrocontroller und Digitale Elektronik Tastenentprellung läuft nicht


von __Son´s B. (bersison)


Lesenswert?

Hallo.
Hilfe - bin verzweifelt, da ich ordentliche Entprellung nicht hin 
bekommen...
ENTPRELL() gibt dauerhaft "0" zurück - aber warum?

#define ENTPRELL( port, pin ) /* von Peter Dannegger */ \
({                           /* aktiv-high-Eingang */ \
static uint8_t flag = 0;  \
uint8_t i = 0;             \
if( flag ){                \
for(;;){                 \
if( (port & 1<<pin) ){  \
i = 0;                \
break;                 \
}                      \
_delay_us( 98 );       \
if( --i == 0 ){      \
flag = 0;            \
i = 0;               \
break;                \
}                       \
}                       \
}else{                   \
for(;;){                \
if( !(port & 1<<pin) ){ \
i = 0;               \
break;              \
}                  \
_delay_us( 98 );   \
if( --i == 0 ){   \
flag = 1;        \
i = 1;          \
break;  \
}    \
}    \
}   \
i; \
})

int main(void)
{
DDRB &= ~(1<<PB2);    // Eingang, Taste
DDRA |= ((1<<PA1)|(1<<PA2));  // Ausgang, LED_F, LED_HL
// LED_HL_ON, LED_HL_OFF sind Macros, die gut funktionieren

while(1) // nur um Tastenrückgabe zu testen
{
while ((ENTPRELL(PINB,PB2))==1)  LED_HL_ON;  // auf 1 abfragen
while ((ENTPRELL(PINB,PB2))==0)  LED_HL_OFF; // aus 0 abfragen
}

von Harald (Gast)


Lesenswert?

Nicht abkupfern was nicht funktioniert oder was du nicht verstehst. 
Nachdenken und selbst schreiben und vor allem: nicht jammern.

von fop (Gast)


Lesenswert?

Ich würde den Test so formulieren :
1
    while (1) /* nur um Tastenrueckgabe zu testen */
2
    {
3
        if  ((ENTPRELL(PINB, PB2)) != 0)
4
        {
5
            LED_HL_ON;    /* bei 1 */
6
        }
7
        else
8
        {
9
            LED_HL_OFF;    /* bei 0 */
10
        }
11
    }

Probier mal, was dann passiert.

Wenn's immer noch nicht geht, probier nochmal ohne Entprellung :
1
    while (1) /* nur um Tastenrueckgabe zu testen */
2
    {
3
        if  ((PINB & (1<<PB2)) != 0)
4
        {
5
            LED_HL_ON;    /* bei 1 */
6
        }
7
        else
8
        {
9
            LED_HL_OFF;    /* bei 0 */
10
        }
11
    }
Nicht dass Dir Deine Hardware ein Ei ins Nest legt.

Danach kann man mal in den Tiefen des Makros kramen...

von Dieter F. (Gast)


Lesenswert?

__Son´s B. schrieb:
> DDRB &= ~(1<<PB2);    // Eingang, Taste

Pullup vergessen (lies mal im Artikel nach)

Und was sagt Dir

"Wenn das Makro für die gleiche Taste (Pin) an mehreren Stellen 
aufgerufen werden soll, muss eine Funktion angelegt werden, damit beide 
Aufrufe an die gleiche Zustandsvariable flag auswerten [1]: "

mit diesem Beispiel:
1
// Hilfsfunktion
2
uint8_t debounce_C1( void )
3
{
4
  return debounce(PINC, PC1);
5
}
6
7
// Beispielanwendung
8
int main(void)
9
{
10
  DDRB  |=   1<<PB2;
11
  DDRB  |=   1<<PB3;
12
  DDRC  &= ~(1<<PC1);
13
  PORTC |=   1<<PC1; // Pullup für Taster
14
15
  for(;;){
16
    if( debounce_C1() )  // nicht: debounce(PINC, PC1)
17
      PORTB ^= 1<<PB2;
18
    if( debounce_C1() )  // nicht: debounce(PINC, PC1)
19
      PORTB ^= 1<<PB3;
20
  }
21
}

von __Son´s B. (bersison)


Lesenswert?

fop schrieb:
> Ich würde den Test so formulieren :

Danke für deine konstruktive Rü.!
Deine Vorschläge decken sich mit meinen Versuchen. Hardware iO, mit dem 
Macro ENTPRELL() stimmt etwas nicht.

Dieter F. schrieb:
> Pullup vergessen (lies mal im Artikel nach)

"aktiv-high" wie beschrieben, daher ext.10k-Ableitwid.

Dieter F. schrieb:
> Und was sagt Dir

Das Problem steckt im Macro, nicht im Aufruf. Dein Versuch funktioniert 
genau so wenig!

Harald schrieb:
> Nicht abkupfern was nicht funktioniert oder was du nicht verstehst.
> Nachdenken und selbst schreiben und vor allem: nicht jammern.

...zu blöde, daher...ohne herablassenden Kommentar...

von Erwin D. (Gast)


Lesenswert?

@TO: kein Gemecker, sondern gutgemeinter Hinweis:
Wenn du jede Zeile, also den gesamten Quelltext an den linken Seitenrand 
schiebst, ist der Text schlecht lesbar.
Deshalb bitte in Zukunft formatieren und in code-Tags einschließen.
Ok?

Gruß Erwin

von Kilo K. (kilo81)


Lesenswert?

__Son´s B. schrieb:
> ENTPRELL() gibt dauerhaft "0" zurück - aber warum?

Natürlich, weil debounce beim Tastendruck nicht "rastet"


__Son´s B. schrieb:

> while ((ENTPRELL(PINB,PB2))==1)  LED_HL_ON;  // auf 1 abfragen
> while ((ENTPRELL(PINB,PB2))==0)  LED_HL_OFF; // aus 0 abfragen
> }

debounce entprellt!
Das bedeutet, dass es keinen festen Zustand einnimmt und auch so bleibt!
Durch das debounce wird der Zustand auf 1 gesetzt und dann wieder auf 0.

probiere folgendes:
1
if(debounce(PINB,PB2)) LED_HL ^= 1
2
3
if(LED_HL==0){
4
LED_HL_ON;
5
}else
6
LED_HL_OFF;

So in der Art!

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

__Son´s B. schrieb:
> mit dem
> Macro ENTPRELL() stimmt etwas nicht.

Wenn Du daran nichts geändert hast, dann stimmt es auch. Der Code wurde 
auf dem STK500 getestet.

Du kannst Dir aber das Leben erheblich erleichtern, wenn Du mal den Link 
auf das Original posten würdest. Und da ist bestimmt auch beschrieben, 
was das Macro macht und auch ein Beispielcode dabei. Der sah garantiert 
nicht so aus, wie Dein obiger Code.

Zu erwarten, daß jeder erstmal nach dem Original suchen soll, ist grob 
unhöflich. Ich hab jedenfalls nicht die Zeit dazu.
Oder weißt Du noch alles genau, was Du die letzten 20 Jahre irgendwo mal 
gepostet hast?

: Bearbeitet durch User
von __Son´s B. (bersison)


Lesenswert?

Peter D. schrieb:
> Zu erwarten, daß jeder erstmal nach dem Original suchen soll, ist grob
> unhöflich.

Originalcode;
https://www.mikrocontroller.net/articles/Pollin_Funk-AVR-Evaluationsboard#Tasty_Reloaded

von Kilo K. (kilo81)


Lesenswert?

Frage:

1. Hattest du meinen Codeschnippsel mal getestet?

2. Willst du beim gedrückthalten der Taste LED_HL_ON auslösen
und beim loslassen der Taste LED_HL_OFF auslösen?

von __Son´s B. (bersison)


Lesenswert?

Dominik K. schrieb:
> debounce entprellt!
> Das bedeutet, dass es keinen festen Zustand einnimmt und auch so bleibt!
> Durch das debounce wird der Zustand auf 1 gesetzt und dann wieder auf 0.

DANKE - genau das war mein Denkfehler!
Mit einer Toggle-Anweisung klappts prima.

von __Son´s B. (bersison)


Lesenswert?

Dominik K. schrieb:
> 2. Willst du beim gedrückthalten der Taste LED_HL_ON auslösen
> und beim loslassen der Taste LED_HL_OFF auslösen?

Genau das ist der nächste Schritt.
Und danach den Flankenverlauf. Rückgabe von i dann;
0...Eingang X/Taste low
1...Eingang X/Taste high
2...Eingang X, Flanke ansteigend
3...Eingang X, Flanke abfallend

von Kilo K. (kilo81)


Lesenswert?

__Son´s B. schrieb:
> DANKE - genau das war mein Denkfehler!
> Mit einer Toggle-Anweisung klappts prima.

Dachte ich mir! ;)
Das macro funktioniert nämlich ganz gut.

von __Son´s B. (bersison)


Lesenswert?

Dominik K. schrieb:

> Dachte ich mir! ;)
> Das macro funktioniert nämlich ganz gut.

Durch die Änderung von i=0 auf i=2
(Quelle: 
https://www.mikrocontroller.net/articles/Pollin_Funk-AVR-Evaluationsboard#Tasty_Reloaded 
, untere Bemerkung)
habe ich alle Rückgabewerte zur Weiterverarbeitung.

von __Son´s B. (bersison)


Lesenswert?

__Son´s B. schrieb:
> Und danach den Flankenverlauf. Rückgabe von i dann;
> 0...Eingang X/Taste low
> 1...Eingang X/Taste high
> 2...Eingang X, Flanke ansteigend
> 3...Eingang X, Flanke abfallend

Einfacher gesagt als getan.
Aus dem Macro kann nur ein Wert zurück gegeben werden "i".
Derzeit ist
i=1=ansteigende Flanke,
i=2=abfallende Flanke.

(1)
Nun könnte ich die beiden Rückgabewerte austauschen
i=3=Taste high,
i=4=Taste low.
(2)
Auch könnte ich eine Zeitstufe
i=1, 5ms später i=3,
i=2, 5ms später i=4
einsetzen. Blöde Sache mit der 5ms-Zeitverschwendung.
(3)
Oder die Zustandänderung in eine Funktion auslagern.

Habt jemand eine bessere Idee?

von Kilo K. (kilo81)


Lesenswert?

Nunja, es ist nunmal ein macro für Taster und nicht für Schalter!
Entweder tauscht du deinen Taster gegen einen Schalter aus oder du setzt 
ein Flag.
Also eine Variable togglen so wie oben beschrieben. Dann kannst du einen 
Taster verwenden.

Was genau hast du denn vor???
Ich dachte du willst einfach nur pro Tastendruck zwischen LED_HL_ON und 
LED_HL_OFF umschalten?

von __Son´s B. (bersison)


Lesenswert?

Dominik K. schrieb:

> Was genau hast du denn vor???
> Ich dachte du willst einfach nur pro Tastendruck zwischen LED_HL_ON und
> LED_HL_OFF umschalten?

Ich möchte dieses Entpreller-Macro, als Basisbaustein (All-In-One) so 
umbauen, dass es zukünftig für alle Gelegenheiten zur Eingangskontrolle 
her halten kann.
Egal ob irgend wann nur eine Flanke, oder der Tasten-/Schalterzustand 
benötigt wird. Ob nur eine Taste, oder mehrere Taster/Schalter innerhalb 
eines Programms verwendet werden.

So komme ich im Laufe der Zeit zu guten+stabilen Universal-Modulen, die 
ich nur noch Einfügen muss. Kleinen Mini-Anpassungen -> FERTIG!

von __Son´s B. (bersison)


Lesenswert?

Folgendes klappt schon mal fehlerfrei;

int main(void)
{
... // div.Deklarationen

if ((ENTPRELL (PINB, PB2)) == 1) Sprung = 1; // Taste_gn, Flanke 
ansteigend
if ((ENTPRELL (PINB, PB2)) == 2) Sprung = 2; // Taste_gn, Flanke 
abfallend
if ((ENTPRELL (PINB, PB1)) == 1) Sprung = 3; // Taste_rt, Flanke 
ansteigend
if ((ENTPRELL (PINB, PB1)) == 2) Sprung = 4; // Taste_rt, Flanke 
abfallend

switch(Sprung)
{
 case 1:
  LED_GN_ON; //Led_gn on == Flanke ansteigend
  break;
 case 2:
  LED_GN_OFF; //Led_gn off == Flanke abfallend
  break;
 case 3:
  LED_RT_ON; //Led_rt on == Flanke ansteigend
  break;
 case 4:
  LED_RT_OFF; //Led_rt off == Flanke abfallend
  break;
}}

Auch mit mehreren Tasten, nur nicht bei zeitgleicher Zustandveränderung.

von W.S. (Gast)


Lesenswert?

__Son´s B. schrieb:
> Hilfe - bin verzweifelt, da ich ordentliche Entprellung nicht hin
> bekommen...

...dann fang an, mal _selber zu denken, anstatt blind irgendwelchen 
Fremdcode in deine Projekte zu kopieren.

Ich hab die Prinzipien hier in diesem Forum schon so oft den diversen 
Leuten erläutert, irgendwann mutiert die eigene Zunge zum 
Fransenteppich...

Zur Not guck in die Quellen der Lernbetty, da kannst du sehen, wie man 
ein paar Dutzend Tasten zuverlässig entprellt und dran etwas lernen. 
Dazu war die nämlich da.

W.S.

von Peter D. (peda)


Lesenswert?

__Son´s B. schrieb:
> if ((ENTPRELL (PINB, PB2)) == 1) Sprung = 1; // Taste_gn, Flanke
> ansteigend
> if ((ENTPRELL (PINB, PB2)) == 2) Sprung = 2; // Taste_gn, Flanke
> abfallend

Du hast das Makro immer noch nicht verstanden, es darf je Taste nur an 
einer Stelle aufgerufen werden!

Bei 2 Aufrufen wird völlig unnütz doppelter Code erzeugt, d.h. Du 
entprellst die selbe Taste zweimal nacheinander.
Es kann daher passieren, daß der 1. Aufruf 1 und der 2. Aufruf 2 ergibt, 
und somit Dein Code den 1. Aufruf überschreibt.
Willst Du den Returnwert auf verschiedene Werte testen, schreib ihn eine 
Variable oder werte ihn mit switch/case aus.

Für eine Lib, die auch für größere Projekte verwendbar sein soll, ist 
allerdings die Interruptlösung eindeutig vorzuziehen. Sie verwartet 
keine Laufzeit und verliert auch keine Tastendrücke, wenn die Mainloop 
etwas mehr zu tun hat.

Die Makrolösung sollte man nur für kleine Projekte benutzen, die z.B. 
auf einem ATtiny13 laufen.

von __Son´s B. (bersison)


Lesenswert?

Peter D. schrieb:
> Bei 2 Aufrufen wird völlig unnütz doppelter Code erzeugt, d.h. Du
> entprellst die selbe Taste zweimal nacheinander.
> Es kann daher passieren, daß der 1. Aufruf 1 und der 2. Aufruf 2 ergibt,
> und somit Dein Code den 1. Aufruf überschreibt.

Danke für Info!
Vorerst hatte ich den Entprellaufruf über eine Funktion pro Taste 
aufgerufen, hatte diesen dann aber weg rationalisiert, da der direkte 
Aufruf gut klappt(e).
Ev. doch nur Zufall...

Peter D. schrieb:
> Für eine Lib, die auch für größere Projekte verwendbar sein soll, ist
> allerdings die Interruptlösung eindeutig vorzuziehen. Sie verwartet
> keine Laufzeit und verliert auch keine Tastendrücke, wenn die Mainloop
> etwas mehr zu tun hat.

"Interrupt" - als Anfänger eher "abschreckend" - warum auch immer...
Werde mich dieses Jahr damit auseinander setzen, da es ein mächtiges 
Werkzeug dar stellt - gerade bei reaktionskritischen Änderungen.

von Wilhelm M. (wimalopaan)


Lesenswert?

__Son´s B. schrieb:

> Peter D. schrieb:
>> Für eine Lib, die auch für größere Projekte verwendbar sein soll, ist
>> allerdings die Interruptlösung eindeutig vorzuziehen. Sie verwartet
>> keine Laufzeit und verliert auch keine Tastendrücke, wenn die Mainloop
>> etwas mehr zu tun hat.
>
> "Interrupt" - als Anfänger eher "abschreckend" - warum auch immer...
> Werde mich dieses Jahr damit auseinander setzen, da es ein mächtiges
> Werkzeug dar stellt - gerade bei reaktionskritischen Änderungen.

Du brauchst nicht unbedingt einen Interrupt, sondern Du musst die 
Entprellroutine nur mit einer konstanten Rate aufrufen. Im simpelsten 
Fall würde man einfach die entsprechenden Timerflags dazu verwenden.

von Joachim B. (jar)


Lesenswert?

__Son´s B. schrieb:
> "Interrupt" - als Anfänger eher "abschreckend"

ich bin ja auch kaum über Anfänger hinausgekommen.

Interrupt ist einfach,
ich mache einen 10ms Timerinterrupt um da Tasten zu entprellen 
(Dannegger), oder

IRMP von Frank(ukw) 64µs z.B. da zähle ich eine Hilfsvariable bis ich 
wieder bei 10ms bin um die Tasten nach Dannegger auszuwerten.

oder per Drehencoder Auswertung Interupt alle 1ms, Drehencoder (auch 
Dannegger) bis 10 zählen Tasten auswerten.

Die Tasten wenn alle auf einem Port liegen können es 8 Tasten 
gleichzeitig sein, wenn nicht schiebt man Tasten Bits von Port A Port B 
Port C eben in ein Hilfs Byte welches dann ausgewertet wird und wertet 
wieder 8 Tasten aus 3 Ports aus.

: Bearbeitet durch User
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.