Forum: Mikrocontroller und Digitale Elektronik Vorschlag: C-Funktion für Taster-Entprellung


von Sven H. (majestic1987)


Lesenswert?

Hallo Leute,

habe gerade eine C-Funktion für das Entprellen eines Tasters 
geschrieben.
Ich würde die "alten Hasen" im Forum bitten, sich diese einmal 
anzuschauen und mir zu sagen, was sie davon halten.

Die Tests sagen: Funktioniert. Aber vielleicht gibt es daran ja etwas, 
dass unter bestimmten Bedingungen Probleme verursachen kann.

Beschreibung der Parameter
1
*Port:      Pointer auf Port, an dem der Taster angeschlossen ist
2
Pin:        Pin(nummer) des Tasters
3
Values[3]:  Array mit 3 uint-Werten, Bedeutung wie folgt.
4
5
1. Zahl: Zählvariable für Zustand "betätigt"
6
2. Zahl: Zählvariable für Zustand "unbetätigt"
7
3. Zahl: Zwischenspeicher für entprelltes Signal.

Code
1
uint8_t fDebounce(volatile uint8_t *Port, uint8_t Pin, uint8_t Values[3])
2
{
3
  if(!(*Port & (1<<Pin)))      //Taster betätigt
4
  {
5
    Values[0] = Values[0] + 1;  //Zähle Anz. gleicher Signalwerte
6
    if(Values[0] >= 255)    //Anzahl vertrauenswürdig
7
    {
8
      if(Values[2]==0)
9
      {
10
        Values[2] = 1;    //Status "betätigt"
11
      }
12
      Values[0] = 0;      //Rücksetzen Vertrauenslevel
13
    }
14
  }
15
  else
16
  {
17
    Values[1] = Values[1] + 1;  //Zähle Anz. gleicher Signalwerte
18
    if(Values[1] >= 255)      //Anzahl vertrauenswürdig
19
    {
20
      Values[2] = 0;      //Status "unbetätigt"
21
      Values[1] = 0;      //Rücksetzen Vertrauenslevel
22
    }
23
  }
24
25
  return Values[2];        //Entprelltes Signal
26
}

Aufrufbeispiel
1
uint8_t TasterA[3];
2
uint8_t uPressedUp = 0;
3
4
int main(void)
5
{
6
//Hier Portkonfiguration
7
//(...)
8
      while(1)
9
      {
10
            uPressedUp = fDebounce(&PINB, PINB2, TasterA);
11
12
            if(uPressedUp)
13
                  PORTB |= (1<<PINB1);
14
            else
15
                  PORTB &= ~(1<<PINB1);
16
      }
17
}

von Tom (Gast)


Lesenswert?

Vom Hauptprogramm her gesehen erwartest du ja die Rückmeldung "ist 
Taste(r) gedrückt?". Bei deiner Methode kommt einfach "nein", bis nach 
frühestens 254 (!) Aufrufen dann "ja" kommen kann. Dasselbe dann für den 
Wechsel zurück. Angenommen deine main() tut noch viel anderes 
zwischenzeitlich, dann wird das nix...

Mit gefällt die Entprellung in der (timer-) ISR viel besser, weil ich 
dann im Hauptprogramm jederzeit zeitnah zuverlässig weiss, ob eine Taste 
gedrückt/losgelassen wurde und ggf. wie lange der Zustand schon als 
stabil so erkannt wird.

von MaWin (Gast)


Lesenswert?

> was sie davon halten

Schrott.

Nimm an, der Taster wechselt bei jeder Abfrage den Zustand
(!(*Port & (1<<Pin)))

Dann zählt munter Values[0] bis 255 und Values[1] bis 255,
jeweils abwechselnd.

Natürlich funktioniert das.

Weil ein Entprellen überhaupt nicht nötig ist.

Wenn deine Hauptschleife

while(1)

nur lang genug braucht, länger als eine Taste prellt
(also so 5 ms), dann holen Abfragen der Art

uPressedUp=(!(*Port & (1<<Pin)))

immer einen entprellten Zustand.

Es kann kein Prellen geben, Daher ist deine Funktion
(die zudem üble Paramterliste hat) überflüssig.

Das einzige, was ein

while(1)

eventuell zum erfolgreichen Entprellen braucht ist ein
_delay_ms(5);

von Hauke R. (lafkaschar) Benutzerseite


Lesenswert?

Unnötig kompliziert, speicherhungrig, muss in der main aufgerufen 
werden, damit sie reagieren kann (du musst sie ja in einer Schleife 
aufrufen, damit das debouncing läuft), dadurch hast du keine feste 
Zeitbasis, kein Speichern des Tastendrucks (wenn du die Methode jetzt 
periodisch per Timer aufrufen würdest, könntest du Tastendrücke 
verpassen, weil deine Main gerade was besseres zu tun hat als Taster 
pollen).

Meine Verbesserungsvorschläge:
1.) Ab in nen periodischen Timer damit, also feste Zeitbasis.
2.) Deine Count werte können viel geringer ausfallen. Ich benutze z.b. 
eine Zeitbasis von 10ms und überprüfe ob 3 aufeinanderfolgende Werte 
konstant sind und benutze das als Ausgangssignal meiner entprellroutine.
3.) Speichervariable, die sich Tastendrücke merkt, die beim auslesen 
dann zurückgesetzt werden
4.) Eventuell erkennung von langem Tastendruck.

von Sven H. (majestic1987)


Lesenswert?

Tom schrieb:
> Vom Hauptprogramm her gesehen erwartest du ja die Rückmeldung "ist
> Taste(r) gedrückt?". Bei deiner Methode kommt einfach "nein", bis nach
> frühestens 254 (!) Aufrufen dann "ja" kommen kann. Dasselbe dann für den
> Wechsel zurück. Angenommen deine main() tut noch viel anderes
> zwischenzeitlich, dann wird das nix...
>
> Mit gefällt die Entprellung in der (timer-) ISR viel besser, weil ich
> dann im Hauptprogramm jederzeit zeitnah zuverlässig weiss, ob eine Taste
> gedrückt/losgelassen wurde und ggf. wie lange der Zustand schon als
> stabil so erkannt wird.

Die Anzahl der Durchläufe könnte man anpassen, ist ja quasi ein 
experimenteller Wert.

Der Einwand, dass das im Hinblick auf die Ausführungszeit unpraktisch 
ist, ist natürlich korrekt.


MaWin schrieb:
>Nimm an, der Taster wechselt bei jeder Abfrage den Zustand
>[...]
>nur lang genug braucht, länger als eine Taste prellt[...]
>üble Parameterliste

Ersterer Einwand ist aber sehr akademisch. Wieso sollte ein Taster (der 
i.d.R. von einem Mensche betätigt wird) exakt mit einer Periodendauer 
prellen, die der Ausführungszeit von main() entspricht?

Der zweite Einwand ist vollkommen logisch, hat aber mit der Lösung nix 
zu tun.

Wieso ist die Parameterliste übel? Das ist ein ganz normale 
Parameterliste?

@Hauke Radtki
Das ganze in den Timer zu packen klingt logisch und sinnvoll.
Wie bereits erwähnt sind die Zählwerte natürlich anpassbar, evtl. als 
Parameter.

Wie ist dein 3. Vorschlag gemeint?

von Uwe (de0508)


Lesenswert?

Hallo,

es gibt von Peter PeDa zwei Ansätze:

* Beitrag "Entprellen für Anfänger"

* Beitrag "Re: Universelle Tastenabfrage"

von Hauke R. (lafkaschar) Benutzerseite


Lesenswert?

Du hast eine Variable (die du als bitmap nutzt), jedem Taster weist du 
ein Bit zu, das gesetzt wird, wenn die entprellroutine nen Tasterdruck 
feststellt. Jetzt hast du eine Methode die dir diese Variable ausliest, 
und biem auslesen zurücksetzt. So hast du dein Tastenereignis sicher 
abgefangen.

von Sven H. (majestic1987)


Lesenswert?

Hauke Radtki schrieb:
> Du hast eine Variable (die du als bitmap nutzt), jedem Taster weist du
> ein Bit zu, das gesetzt wird, wenn die entprellroutine nen Tasterdruck
> feststellt. Jetzt hast du eine Methode die dir diese Variable ausliest,
> und biem auslesen zurücksetzt. So hast du dein Tastenereignis sicher
> abgefangen.

Problem dabei: wenn ich beim Auslesen des Zustands diesen zurücksetze, 
dann wird dieser Zustand erst wieder gesetzt, wenn die Entprell-Routine 
erneut ein sicheres Signal festgestellt hat. Also nach 20ms oder sowas.

von Karl H. (kbuchegg)


Lesenswert?

Sven H. schrieb:

> Problem dabei: wenn ich beim Auslesen des Zustands diesen zurücksetze,
> dann wird dieser Zustand erst wieder gesetzt, wenn die Entprell-Routine
> erneut ein sicheres Signal festgestellt hat. Also nach 20ms oder sowas.

Und wie schnell kannst du als Mensch einen Taster 2 mal hintereinander 
betätigen?

Das ganze Konzept der Entprellung beruht darauf, dass echte 
beabsichtigte Pegelwechsel wesentlich seltener und in größeren 
zeitlichen Abständen auftreten als die Prellpulse.

von Hauke R. (lafkaschar) Benutzerseite


Lesenswert?

Achja, hab ich vergessen zu erwähnen, ich setzte dieses Register bei 
steigender Flanke, um einen Tastendruck festzustellen.

Wenn man Funktion wie "gedrückt gehalten" erfassen will muss man sich 
noch was überlegen, möglich wär da eben eine Zählvariable in wievielen 
intervallen der Pin nach der steigenden Flanke noch high war.

Oder einfacher (aber mit ein paar Tücken verbunden) man überprüft ob der 
Pin noch high ist, während man seine Variable ausliest, und entscheidet 
so ob der Pin durchgedrückt wurde. Da hängt jetzt aber das Verhalten 
auch vom Timing es auslesenden Programms ab, das kann problematisch 
sein.

von Sven H. (majestic1987)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Sven H. schrieb:
>
>> Problem dabei: wenn ich beim Auslesen des Zustands diesen zurücksetze,
>> dann wird dieser Zustand erst wieder gesetzt, wenn die Entprell-Routine
>> erneut ein sicheres Signal festgestellt hat. Also nach 20ms oder sowas.
>
> Und wie schnell kannst du als Mensch einen Taster 2 mal hintereinander
> betätigen?
>
> Das ganze Konzept der Entprellung beruht darauf, dass echte
> beabsichtigte Pegelwechsel wesentlich seltener und in größeren
> zeitlichen Abständen auftreten als die Prellpulse.

Das ist mir bewusst. Es geht auch nicht darum, dass man so schnelle 
Betätigungen nicht erfassen könnte sondern um - wie Hauke Radtki bereits 
erwähnt hat - um die Funktion des "gedrückt haltens".

von Karl H. (kbuchegg)


Lesenswert?

Sven H. schrieb:

> Das ist mir bewusst. Es geht auch nicht darum, dass man so schnelle
> Betätigungen nicht erfassen könnte sondern um - wie Hauke Radtki bereits
> erwähnt hat - um die Funktion des "gedrückt haltens".

OK.
Dann musst du hier

> Problem dabei: wenn ich beim Auslesen des Zustands diesen zurücksetze,

deine Stretgie überdenken. Wenn du erfassen willst, dass ein Taster 
gedrückt ist, anstelle von "ist er gedrückt worden", dann erfolgt das 
Zurücksetzen eben erst wenn "Taster wurde losgelassen" detektiert wurde.

Aber eines gleich vorweg: Wensentlich häufiger benötigt man in der 
Praxis das Detektieren "Taster wurde gedrückt" und an dieses 
Niederdrücken ist eine Aktion gekoppelt.

"Solange ein Taster gedrückt ist" braucht man nicht so häufig und wenn 
man das braucht ist eine nicht erfolgte Entprellung meist kein Problem.
Aber "SOlange Taste gedrückt" ereldigt die PeDa Entprellung nebenbei 
mit. Entweder durch eine kleine Erweiterung oder aber in Form eines 
Autorepeats.

von Karl H. (kbuchegg)


Lesenswert?

Deine Enptrellung kannst du enorm vereinfachen, indem du dich von 
'gedrückt' bzw. 'losgelassen' verabschiedest und statt dessen auf "hat 
sich der Pin Zustand verändert" übergehst.

Das Hauptproblem der fehlenden Zeitbasis und damit der Abhängigkeit der 
Entprellung vom restlichen Programm bleibt aber.

von Sven H. (majestic1987)


Lesenswert?

Dass in der Praxis eine Flankenerkennung wesentlich häufiger benötigt 
wird, als eine dauerhafte oder längere Betätigung ist mir bewusst.

Es geht auch eigentlich eher um's Prinzip:

Wenn ich eine Entprellungs-Funktion verwende, dann erwarte ich 
eigentlich, dass ich zu jedem Zeitpunkt der Abfrage exakt den Zustand 
des Signals erhalte (nur eben ohne prellen).

Wenn ich eine Flanke brauche, dann nehme ich eine Funktion zur 
Flankenauswertung.

Habe mich heute morgen mit der PeDa-Lösung befasst, aber Auto-Repeat ist 
ja nicht "Zustand des Tasters" sondern eine Impulsfolge, wenn ich mir 
das ganze richtig angeschaut habe.

Allerdings habe ich diese Variante jetzt angepasst und in eine Funktion 
gepackt. Nun tut sie, was ich erwarte und dennoch ist das Signal bei 
steigender und fallender Flanke entprellt.

Die Speicherausnutzung ist nämlich wirklich effizienter, wenn man den 
ganzen Port entprellt und schöner ist es im Timer-Interrupt auch :-)

Insofern Danke für die Tipps. Ich steige gerade in das gante µC-Thema 
ein, komme nämlich eigentlich aus der SPS-Welt, da muss man sich um 
vieles dann doch nicht mehr so explizit kümmern (auch, wenn es in der 
Praxis durchaus Prellvorgänge gibt, die im Bereich von einigen 100ms 
liegen).

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.