Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage zur einer Debounce-Routine


von Unlucky2012 (Gast)


Lesenswert?

Hi,

dem Wiki-Eintrag zum Thema "Entprellen" folgend bin ich auf folgende 
Seite gestoßen (http://www.ganssle.com/debouncing.pdf). Diese ist recht 
informatik und bietet im zweiten Teil auch einige Beispiele für eine 
Debounce-Routine.

Allerdings habe ich da so meine Verständnisprobleme mit. Vielleicht kann 
mir ja einer von euch auf die Sprünge helfen.

Konkret geht es um Folgenden Code:
1
bool_t DebounceSwitch2() {
2
3
    static uint16_t state = 0;
4
    
5
    state = (state<<1) | !RawKeyPressed() | 0xE000;
6
7
    if (state == 0xF0000) {
8
9
        return true;
10
11
    }
12
13
    return false;
14
15
}

Ich bin mir nicht sicher wie ich diesen Code anwenden muss. Zum Einen 
gibt es ja das Problem, dass die Funktion über einen Timer aufgerufen 
wird (in meinem Fall alle 10 ms). Demnach macht ja ein Rückgabewert 
nicht wirklich Sinn. Hierfür habe ich eine weitere Variable eingeführt 
(und als volatile gekennzeichnet).

Das weitaus größere Problem ist aber, dass die Funktion ja nur einmalig 
true zurückliefert und nicht beständig, solange die Taste gedrückt 
bleibt. Wie würde ich das nun ändern?

Mir ist durchaus klar, dass es hier im Forum zig Entprell-Routinen gibt. 
Aber diese erscheinen mir für meinen Anwendungsfall zu komplex und 
bieten mehr als ich benötige. Ich habe nur einen Taster, welchen ich 
entprellen möchte. Außerdem befinde ich mich noch am Anfang und würde 
das auch gerne nutzen, um selbst nachzudenken und dabei vielleicht etwas 
lernen ;).

Mit freundlichen Grüßen,
Unlucky2012

von Achim M. (minifloat)


Lesenswert?

Unlucky2012 schrieb:
> Hierfür habe ich eine weitere Variable eingeführt
> (und als volatile gekennzeichnet)

Wo?

10ms sind für Tasterprellen schon eine recht lange Zeit, aber angesichts 
der Qualität von diesen Printtastern...

Die einfachste Methode ist, einen Zähler zu verwenden. Bei "Taste unten" 
wird der Zähler inkrementiert, bei "Taste oben" auf 0 zurückgesetzt. Nun 
kann man
1. zu kurze Tastendrücke erkennen: Zähler bleibt unter einem gewissen 
Schwellwert
2. gültige Tastendrücke erkennen: Zähler überschreitet diesen 
Schwellwert
3. längere Tastendrücke erkennen: Zähler überschreitet einen noch 
größeren Schwellwert

Was sich hier im Forum sehr bewährt hat, sind Peter Danneggers 
"Bulletproof"-Entprellroutinen.

mfg mf

von Unlucky2012 (Gast)


Lesenswert?

Mini Float schrieb:
> Wo?

Außerhalb der ISR, sodass ich dann in meiner Endlosschleife Zugriff 
darauf habe.

Mini Float schrieb:
> 10ms sind für Tasterprellen schon eine recht lange Zeit, aber angesichts
> der Qualität von diesen Printtastern...

Naja, ich habe 10ms mal als Mittelwert aufgefasst. Unter anderem findet 
man ja aussagen wie die Folgende:

> Most switches seem to exhibit under 10 msec bounce rates. Coupled with my
> observation that a 50 msec response seems instantaneous, it seems
> reasonable to pick a debounce period in the 20 to 50 msec range.

siehe: http://www.ganssle.com/debouncing-pt2.htm

Wenn ich mir andere Debounce-Routinen anschaue, dann spielen die in der 
gleichen Liga (+/- ein paar Millisekunden).

Mini Float schrieb:
> Die einfachste Methode ist, einen Zähler zu verwenden. Bei "Taste unten"
> wird der Zähler inkrementiert, bei "Taste oben" auf 0 zurückgesetzt.
Dieser Ansatz ist mir durchaus bekannt, aber irgendwie hat mir der 
andere (aufgrund der kürzeren Implementierung) besser gefallen.

Mini Float schrieb:
> Was sich hier im Forum sehr bewährt hat, sind Peter Danneggers
> "Bulletproof"-Entprellroutinen.
Wie gesagt: Halte ich für das Entprellen von nur einem Taster fast für 
ein wenig Overkill. Vor allem, weil ich keine Doppel-Belegung 
(kurz/lang) und auch keine Wiederholfunktionen brauche. Außerdem habe 
ich bloß ein einzigen Taster und würde auch noch ganz gerne etwas 
lernen, anstatt fertigen Code zu nehmen ;).

von Achim M. (minifloat)


Lesenswert?

Unlucky2012 schrieb:
> Mini Float schrieb:
>> Die einfachste Methode ist, einen Zähler zu verwenden. Bei "Taste unten"
>> wird der Zähler inkrementiert, bei "Taste oben" auf 0 zurückgesetzt.
> Dieser Ansatz ist mir durchaus bekannt, aber irgendwie hat mir der
> andere (aufgrund der kürzeren Implementierung) besser gefallen.

Naja, hier ist es auch ein Zähler. Nur dass da Bits "von hinten nach 
vorne" rein geschoben werden.
Was hier noch zusätzlich gemacht wird: eine Fensterung. Vermute ich.

Das hier:
Unlucky2012 schrieb:
> uint16_t state

Und das:
Unlucky2012 schrieb:
> if (state == 0xF0000)

passen nicht zusammen. Vier Bit zuviel...

Angenommen, es heißt...
1
if (state == 0xF000){<...>}
...wird bei jedem Aufruf erstmal nachgesehen, ob der Taster einen Druck 
abliefert. Wenn...
Unlucky2012 schrieb:
> !RawKeyPressed()
...einen Wert von 0xFF bei gedrücktem Taster zurückgibt, wird der bei 
folgenden erkannten Tastendrücken "nach vorne" geschoben, bis in "state"
die vier MSB gesetzt sind. Dann wird true zurückgegeben. Wenn er einfach 
unten rausfällt, eben "false".

mfg mf

von Karl H. (kbuchegg)


Lesenswert?

Unlucky2012 schrieb:

> Das weitaus größere Problem ist aber, dass die Funktion ja nur einmalig
> true zurückliefert und nicht beständig, solange die Taste gedrückt
> bleibt. Wie würde ich das nun ändern?

Schreib dir deine eigene.
Eine Funktion, die eine Variable so lange auf true hält, wie eine Taste 
gedrückt IST, ist schnell geschrieben.

Viel interessanter ist der Fall: Bei einmaligem Niederdrücken soll die 
Funktion auch nur ein einziges mal true liefern. Und zwar auch dann, 
wenn zwischen Tastendruck und Abfrage 5 Stunden liegen. Da wirds dann 
interessant.

Aber ein 'true solange gedrückt' ist nicht wirklich schwer. Das schaffst 
du alleine. Überhaupt dann, wenn du sowieso schon einen Timer am laufen 
hast mit 10ms. In der Interrupt Routine einfach den Tastenzustand mit 
einem gespeicherten Tastenzustand von vor 10ms vergleichen. 
Unterscheiden sich die beiden, dann kannst du keine Aussage darüber 
machen ob die Taste gedrückt ist oder nicht. Sind die beiden aber 
gleich, dann sagt dir der Zustand ob die Taste gedrückt ist oder nicht. 
Sprich: das was du vom Port eingelesen hast, gilt. Das sind 1 
Portabfrage, 3 Zuweisung und 1 Vergleich. Fertig.

von Peter D. (peda)


Lesenswert?

Unlucky2012 schrieb:
> Das weitaus größere Problem ist aber, dass die Funktion ja nur einmalig
> true zurückliefert und nicht beständig, solange die Taste gedrückt
> bleibt. Wie würde ich das nun ändern?

Das ist genau der Sinn einer Entprellung, daß man pro Drücken garantiert 
nur ein Ereignis kriegt.
Willst Du etwas machen, solange man drückt, dann braucht man kein 
Entprellen.

Die Routine ist auf nem 8Bitter sehr ineffektiv, da man umständlich 2 
Bytes zu 16Bit zusammen kleistern muß.
Bis 16 kann man mit nur einem Byte zählen, das ist schneller und 
kleiner.

Gerade bei dem erwähnten 8051 ist das besonders krass. Zählen kann der 
nämlich direkt im SRAM, schieben aber nur im ACC-Register. 16Bit 
schieben sind dann schon 6 Befehle, INC aber nur einer.


Peter

von Unlucky2012 (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Das ist genau der Sinn einer Entprellung, daß man pro Drücken garantiert
> nur ein Ereignis kriegt.
Ok, das sehe ich ein. Aber wie wäre dann eine solche Funktion 
aufzurufen? Der oben skizzierte Ansatz liefert ja nur zu einem Zeitpunkt 
"true" zurück, nämlich wenn die letzte 1 durchgeschoben wird.

Da das Ganze aber in einer ISR läuft, und die eigentliche Verarbeitung 
normalerweise woanders (in einer Endlosschleife) statt findet, kann es 
doch passieren, dass ich den Druck verpasse, weil die ISR nochmal 
aufgerufen worden ist und jetzt false liefert?

Peter Dannegger schrieb:
> Die Routine ist auf nem 8Bitter sehr ineffektiv, da man umständlich 2
> Bytes zu 16Bit zusammen kleistern muß.
> Bis 16 kann man mit nur einem Byte zählen, das ist schneller und
> kleiner.
Naja, man könnte ja 8 Bits nehmen anstatt der 16 und dann eben nur bis 8 
"zählen" bzw. schieben. Auf einem "modernen" AVR müsste es genau dafür 
doch dann auch die "Doppelregister" geben, oder?

von Sebastian (Gast)


Lesenswert?

Richtige Männer Debouncen mit Kondensator und Pullup ;-)

von Chris (Gast)


Lesenswert?

Der Code kann schon sinn machen, und nähmlich:

   state = (state<<1) | !RawKeyPressed() | 0xE000;

    if (state == 0xF0000) {

        return true;

    }

ist irgendwie dasselbe wie
    static  unsigned char debounce=14;

    if(debounce>22) debounce=22;       // repeat rate
    if(!RawKeyPressed()) debounce=14;
    if(!debounce--) return true;
    return false;


Aber, beim zweiten Code wird der Tastendruck nach X Durchläufen wieder
gemeldet, kommt ein repeat gleich, hingegen beim obigen Code wird er nur
einmal gemeldet, und zwar nach exact 120mS. Oft wird auch ein repeat
verlangt, es kommt auf die Specs an. Oft wird auch so ein code 
verwendet:

    static  unsigned char debounce=14;

    if(!RawKeyPressed()) debounce=14;
    if(debounce==-22) debounce=0;       // repeat rate
    if(!debounce--) return true;
    return false;

Hiermit kann dann unterschieden werden, ob es ein einmaliger Tastendruck 
ist oder ein automatisch wiederholter, dank bit7 von debounce.

von Umpa Lumpa (Gast)


Lesenswert?

Chris schrieb:
> und zwar nach exact 120mS.

120milli Siemens?
mS sind ein Leitwert und keine Zeit. 1/R.
Du meinst wohl 120ms.
Bitte die Einheiten beachten.
exact-> exakt.

Gruß

von MaWin (Gast)


Lesenswert?

> Zum Einen gibt es ja das Problem, dass die Funktion über
> einen Timer aufgerufen wird (in meinem Fall alle 10 ms
1
volative key2pressed;
2
3
ISR(TIMER0_OVF_vect) 
4
{
5
    static uint16_t state = 0;
6
    
7
    state = (state<<1) | !RawKeyPressed() | 0xE000;
8
    if (state == 0xF0000) key2pressed=TRUE;
9
}
10
11
Hauptprogramm:
12
13
   if(key2pressed)
14
   {
15
      : // tue was auch immer key2 tun soll
16
      key2pressed=FALSE;
17
   }
> Aber diese erscheinen mir für meinen Anwendungsfall
> zu komplex und bieten mehr als ich benötige.
1
uint8_t tasten,gedrueckt;
2
3
while(1)// die Programm-Hauptschleife
4
{
5
  tasten=PIND; // 8 Taster auf ein mal, liefern 1 wenn gedrückt (sonst ~PIND)
6
  gedrueckt=tasten&~gedrueckt;
7
  if(gedrueckt&1)
8
  {
9
    // Taster 1 wurde gerade runtergedrückt, mach was
10
  }
11
  if(gedrueckt&2)
12
  {
13
    // Taster 2 wurde gerade runtergedrückt, mach was
14
  }
15
  // mach was sonst in der Programm-Hauptschleife passieren muß
16
  gedrueckt=tasten;
17
   _delay_ms(10); // damit sie bestimmt länger dauert als eventuelles Prellen
18
}

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.