C Taster / Taste Entprellen?!

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Hallo,

hier bin ich schon wieder einmal...

Meine heutige Frage...

Taste / Taster Entrprellen... Im Internet gibt es "zich" Seiten, wo etwas von Entprellung geschrieben wird. Einige sagen dies sei totaler Mist die anderen hingegen schwören auf diese Entprellung.
Es gibt auf dem anderen Forum "mikrocontroller.net" eine Routine mit der "Pedda" Entprellung, die soll angeblich sehr gut sein.

Wie seht ihr das ? Wie macht ihr das am besten ? Mich würden eure Meinungen dazu gerne mal Interessieren! Würde mich auch freuen, wenn ihr mir mal Ansatzweise helfen könntet, eine "nicht komplizierte" Entprellungsroutine aufbauen.
 
Das kommt immer auf die konkreten Anforderungen und Gegebenheiten an. Auf der einen Seite also zB die nötige "Tastenwiderholfrequenz", Tastenanzahl usw, auf der anderen Seite zB, ob bereits irgendwelche Code-Teile existieren, die das mit wenig Aufwand mit erledigen (ich habe zB statt eines Timers mal die 100Hz-Basis einer Nulldurchgangsdetektion verwendet.)
 
Hallo Janiiix,

Das kommt immer auf die konkreten Anforderungen und Gegebenheiten an.
stimmt.

Je nachdem was man mit dem Taster machen will muß man entprellen oder kann sich das alles sparen und ihn einfach so abfragen. Es kommt also ganz auf die Aufgabenstellung an.

Gruß
Dino
 
Hallo,

hier bin ich schon wieder einmal...

Meine heutige Frage...

Taste / Taster Entrprellen... Im Internet gibt es "zich" Seiten, wo etwas von Entprellung geschrieben wird. Einige sagen dies sei totaler Mist die anderen hingegen schwören auf diese Entprellung.
Es gibt auf dem anderen Forum "mikrocontroller.net" eine Routine mit der "Pedda" Entprellung, die soll angeblich sehr gut sein.

Wie seht ihr das ? Wie macht ihr das am besten ? Mich würden eure Meinungen dazu gerne mal Interessieren! Würde mich auch freuen, wenn ihr mir mal Ansatzweise helfen könntet, eine "nicht komplizierte" Entprellungsroutine aufbauen.

Wenn du eine Flankenabfrage machst, dann wäre er automatisch entprellt ..... (oder seh ich das falsch ? - und das Thema hattest du schonmal angesprochen)

Ansonsten... Hardwaremäßig entprellen - einfach ein RC-Glied verwenden .... manche nehmen auch gleich IC's z.B.
http://www.maximintegrated.com/datasheet/index.mvp/id/1896

Kommt immer drauf an, was du genau machen willst....
 
Hi,

Wenn du eine Flankenabfrage machst, dann wäre er automatisch entprellt ..... (oder seh ich das falsch ? - und das Thema hattest du schonmal angesprochen)
siehst du falsch. Wenn das Programm an der Stelle schnell genug ist, dann erkennt es beim Prellen viele Flanken (das Prellen) :p

Gruß
Dino
 
Wenn du eine Flankenabfrage machst, dann wäre er automatisch entprellt ..... (oder seh ich das falsch ? - und das Thema hattest du schonmal angesprochen)

Ansonsten... Hardwaremäßig entprellen - einfach ein RC-Glied verwenden .... manche nehmen auch gleich IC's z.B.
http://www.maximintegrated.com/datasheet/index.mvp/id/1896

Kommt immer drauf an, was du genau machen willst....

klar kann man das alles mit IC´s machen... Nur wenn ich sowieso schon einen µC in benutzung habe, wieso sollte ich es dann nicht Softwaremäßig machen ?!
Gibt es vill. kleine Code Beispiele wo dran ich mich Orientieren kann?!

Ich habe imoment bei meinem aktuellen Projekt (Taschenlampe) erstmal nur eine Taste die ich Entprellen möchte (diese hängt an INT0) dran...
 
Wie schon angedeutet gibt es unterschiedliche Herangehensweisen, die unterschiedlich aufwändig sind (wobei ein Teil des Aufwandes eventuell eh schon im Programm mitverwendet werden kann), und die unterschiedliche Vor- und Nachteile haben können.
Generell ist zu sagen, daß alle diese Methoden (auch die Hardware-) das Prellen nicht verhindern - bei den Hardwaremethoden wird es rausgefiltert bevor es auf den Controller gelangt, bei den Softwaremethoden geht es darum, das Prellen zu ignorieren.
Beachte: Die Prelldauer kann einerseits von Taster zu Taster unterschiedlich sein - aber sie ändert sich auch in der Lebensdauer des Tasters. Wenn man also die Entprellung auf den Taster zuschneidet, kann das möglicherweise nach ausreichend vielen Betätigungen nicht mehr passen...

Der 2te Punkt, der beim Polling immer irgendwie mitreingeht ist der, daß man generell entscheiden muß was geschehen soll, wenn eine Taste dauerhaft gedrückt ist: soll das ignoriert werden (also nur das eine, erste Event), oder soll wiederholt getriggert werden? Und wenn, dann in welchen zeitlichen Abständen?

Am Beispiel Deiner Lampe hast Du jetzt vielleicht 'n Taster für heller - wenn Du den betätigst, solls heller werden. Und wenn Du den Finger drauf läßt? Solls dann langsam hochfaden, oder willst Du dafür "dauerklicken" müssen?

Das Entprellen selbst beschäftigt sich ja nur damit, aus den zich Flanken beim betätigen/loslassen des Tasters eine zu machen/erkennen/festzulegen.
 
Am Beispiel Deiner Lampe hast Du jetzt vielleicht 'n Taster für heller - wenn Du den betätigst, solls heller werden. Und wenn Du den Finger drauf läßt? Solls dann langsam hochfaden, oder willst Du dafür "dauerklicken" müssen?

Nach jedem betätigen (drücken) der Taste möchte ich eine Helligkeitsstufe höher gehen...
 
Hallo,

also ich benutze je nach Programm verschiedene Entprellmöglichkeiten.

1. Tastendruck erkennen und etwas warten ob die Taste immer noch gedrückt ist (wie Debounce)

2. Zustand des Tastenpins bei jedem Hauptschleifendurchlauf in eine Variable einlesen und beim nächsten Hauptschleifendurchlauf nachsehen ob sie immer noch gedrückt ist.

3. Zustand des Tastenpins bei jedem Hauptschleifendurchlauf in ein Register reinschieben und sehen ob sich noch was ändert. (&H00 oder &HFF = Prellen ist zu Ende) Kann man mit verschieden langen Bit-Ketten machen.

Die Methoden haben verschiedene Vor und Nachteile. Die Schwierigkeit der Methoden nimmt von 1 zu 3 zu. Die Zuverlässigkeit aber auch. Der Zeitverbrauch im Programm nimmt von 1 zu 2+3 ab.

Bei 1. wartet das Programm einfach und tut in der Zeit nix anderes. Also verschwendet man wertvolle Prozessorzeit in der man was anderes erledigen könnte. Dafür ist es die einfachste Methode. Wenn sowieso in dem Moment nichts anderes im Programm ansteht ... warum nicht.

Bei 2. muß der Haupschleifendurchlauf länger dauern als die Prellzeit des Tasters. Es ist aber keine zusätzliche unnötige Wartzyklen im Programm drin bei denen nichts gemacht wird.

Bei 3. kann sich die Routine dynamisch auf die Prellzeit des Tasters einstellen. Es sind keine Wartezyklen im Programm. Dafür ist die Programmierung ein wenig anspruchsvoller.

Alle drei Routinen laufen ohne Interrupts, können aber auch mit Interrupt erweitert werden was aber wieder zu mehr Programmaufwand führt.

Gruß
Dino
 
Hallo dino03,

3. Zustand des Tastenpins bei jedem Hauptschleifendurchlauf in ein Register reinschieben und sehen ob sich noch was ändert. (&H00 oder &HFF = Prellen ist zu Ende) Kann man mit verschieden langen Bit-Ketten machen.



Würdest du das auch in "C" hin bekommen zu erklären ?
 
Hi Janiiix,

Hallo dino03,

3. Zustand des Tastenpins bei jedem Hauptschleifendurchlauf in ein Register reinschieben und sehen ob sich noch was ändert. (&H00 oder &HFF = Prellen ist zu Ende) Kann man mit verschieden langen Bit-Ketten machen.


Würdest du das auch in "C" hin bekommen zu erklären ?
Nein. Mit C stehe ich auf Kriegsfuß :rolleyes:

Ist eigentlich aber ganz einfach ...

Bei jedem Schleifendurchlauf ...

- Du liest das Bit von dem Port ein an dem der Taster hängt.
- Du schiebst das Bit in ein Byteregister ins LSB rein ... [76543210]<--Taster-Bit ... Dabei fällt das oberste Bit ins Nirvana.
- Du siehst nach ob das Byteregister entweder vollständig mit Nullen oder mit Einsen gefüllt ist. Dann ändert sich nämlich der Tastenpin nicht mehr. (Prellen zu Ende)

Du kannst statt einem Register auch eine Bytevariable nehmen. Ich hab damals in Assembler ein Register verwendet.

Wenn du keine 8 Schleifendurchläufe warten willst um zu erkennen ob es noch prellt oder nicht, dann markierst du über AND (logisches UND) die gewünschte untere Anzahl an Bit aus und testest ebenso auf durchgehende Einsen oder Nullen. Also zB ...
[76543210]
[00001111] UND-Maske
[----3210] ---> 00000000=0 / 00001111=1 Also auf Wert 0 oder 15 testen

Das ist wunderbare Bit-Popelei wie man sie in Assembler macht :p
Mit einer trickreichen Anordnung von Bits in Registern oder Variablen kann man sich viele langwierige Berechnungen sparen :cool:

Gruß
Dino
 
Ohje,

Und das als Anfänger in "C" :D
Welches Register kann man denn dazu benutzen ? Aber doch nicht jedes "X" beliebige ?
 
Ohje,

Und das als Anfänger in "C" :D
Welches Register kann man denn dazu benutzen ? Aber doch nicht jedes "X" beliebige ?

Die Bit-Befehle arbeiten mit den oberen 16 Registern (also r16 - r31). Geht aber wie gesagt in Bascom (und auch sicher mit C) mit einer Variablen. Der Compiler macht dann den Rest.

Beschäftige dich mal mit den logischen Verknüpfungen und Bitmanipulationen. Die kann man immer mal gebrauchen ...

AND , OR , XOR , NOT , ...
LSL (Logical Shift Left), LSR (Logical Shift Right), ROL (Rotate Left), ROR (Rotate Right), ...
und solche Dinger.

Ich hoffe mal ich hab jetzt die richtigen Assembler-Mnemomics aufgelistet und nicht die vom Zilog Z80 :rolleyes: gibt es aber in jedem Prozessor in ähnlicher Art.

Ähnlich genannte Befehle (Shift, Totate, ...) findest du auch unter Bascom und bestimmt auch unter C.
 
Hi Janiiix3
im Prinzip prellen alle mechanischen Kontakte. Den Effekt kanst du ganz schnell herausfinden, wenn du ohne Entprellroutine, aber mit Flankenauswertungeine Variable hochzählst. So nach dem Motto: einmal Tasten, einen weiter schalten. Die Routine von PeDa habe ich noch nicht eingesetzt, aber alle schwören drauf, die sich mit C befassen. Ich lebe ja doch ein wenig näher am Herzen der Controller und da bau ich meine eigene Version ein.
Das soll aber nicht heißen, das ich da keine Hilfe geben kann. Mal ganz einfach:
Deine Zykluszeit ist ca. 5 mSek. Du möchtest 20 mSek. stabiles Signal haben, also brauchst du einen Zähler, der von 3 nach 0 zählt (vier Schritte) und in jedem Zyklus den Zähler decrementiert. Das ist dann deine "Debiunce_Time". Korrekt ist das natürlich in der Timer-ISR. Da hast du dann auch definiert feste Zeiten, aber zum Verständnis gehts halt auch mit der Zykluszeit.
Also, du liest einen Eingang und machst eine Exclusiv-Oder Verknüpung mit dem alten Zustand. Keine Änderung, das heißt, das Ergebnis ist 0 fragst du deinen Zählerstand ab, ob er schon 0 ist. Wenn ja, dann ist auch kein Ereignis von einem Eingang registriert und du kannst ander Dinge machen. Wenn nicht, decrementierst du deinen Zähler. Danach prüfst du, ob er jetzt 0 ist, denn dann hast du ein gültiges Signal am Eingang erfasst. In diesem Fall schreibst du den gelesenen Status in die Ablage und erledigst deine Aufgabe.
Hast du einen Unterschied in Ablage und neu eingelesenem Eingang erkannt ( die Exclusiv-Oder Verknüpfung liefert eine "1") dann setzt du den zeitzähler auf 4 und schreibst den eingelesenen Zustand in die Ablage
Etwa so
Main_Loop
Lese Eingang neu
Exclusiv Oder mit alt
Ergebnis "1" (Unterschied)
Setze Zählerr auf 3
Schreibe Eingang Neu nach alt
else
Zeitzähler <> 0
dec Zeitzähler
Zeitzähler =0 Eingang ist Gültig und stabil
Aktion auf Ereignis Eingang
End
..... weitere Programmschritte
end Loop

Du musst das nur in funktionierende C-Programmierung bringen.
Gruß oldmax
 
Taschenlampe.
Taster an/aus. (INT0/1)
Taster heller.
Taster dunkler. (Beide PCInt)
LED am HW-PWM (8Bit?)

ICH(!) würde das so angehen:
Der Controller läuft langsam (1MHz oder so, PWM 100Hz).
Die "Entprellung" wird durch einen 2ten Timer realisiert, welcher als Countdown die Wartezeit runter zählt (später mehr).
Ich würde statt der groben Stufen quasi-stufenlos hoch-/runterregeln.
Im "an-/aus-Interrupt" wird der PWM-Timer gestartet (an die Main-Clock gekoppelt)/gestoppt, ggf der Ausgang umgeschaltet, der jeweils passende Sleep-Modus vorgewählt, Interrupts der anderen Taster Genehmigt/gesperrt, Int0 selbst gesperrt, der "Entprell-Timer" gestartet.
In der ISR der anderen Taster wird das OC-Register manipuliert, Interrupts der 3 Taster analog zu oben manipuliert, der "Entprell-Timer" gestartet.
Im Hauptprogramm wird das Überlauf-Flag des Entprell-Timers gepollt, ist es gesetzt wird selbiger erstmal angehalten, das Flag gelöscht, die Interrupts der Taster freigegeben und der Controller schlafen geschickt.
Die Reichweite des Timers ist ggf in den beiden ISR sinnig zu wählen und zu setzen.
 
Hi Janiiix3
im Prinzip prellen alle mechanischen Kontakte. Den Effekt kanst du ganz schnell herausfinden, wenn du ohne Entprellroutine, aber mit Flankenauswertungeine Variable hochzählst. So nach dem Motto: einmal Tasten, einen weiter schalten. Die Routine von PeDa habe ich noch nicht eingesetzt, aber alle schwören drauf, die sich mit C befassen. Ich lebe ja doch ein wenig näher am Herzen der Controller und da bau ich meine eigene Version ein.
Das soll aber nicht heißen, das ich da keine Hilfe geben kann. Mal ganz einfach:
Deine Zykluszeit ist ca. 5 mSek. Du möchtest 20 mSek. stabiles Signal haben, also brauchst du einen Zähler, der von 3 nach 0 zählt (vier Schritte) und in jedem Zyklus den Zähler decrementiert. Das ist dann deine "Debiunce_Time". Korrekt ist das natürlich in der Timer-ISR. Da hast du dann auch definiert feste Zeiten, aber zum Verständnis gehts halt auch mit der Zykluszeit.
Also, du liest einen Eingang und machst eine Exclusiv-Oder Verknüpung mit dem alten Zustand. Keine Änderung, das heißt, das Ergebnis ist 0 fragst du deinen Zählerstand ab, ob er schon 0 ist. Wenn ja, dann ist auch kein Ereignis von einem Eingang registriert und du kannst ander Dinge machen. Wenn nicht, decrementierst du deinen Zähler. Danach prüfst du, ob er jetzt 0 ist, denn dann hast du ein gültiges Signal am Eingang erfasst. In diesem Fall schreibst du den gelesenen Status in die Ablage und erledigst deine Aufgabe.
Hast du einen Unterschied in Ablage und neu eingelesenem Eingang erkannt ( die Exclusiv-Oder Verknüpfung liefert eine "1") dann setzt du den zeitzähler auf 4 und schreibst den eingelesenen Zustand in die Ablage
Etwa so
Main_Loop
Lese Eingang neu
Exclusiv Oder mit alt
Ergebnis "1" (Unterschied)
Setze Zählerr auf 3
Schreibe Eingang Neu nach alt
else
Zeitzähler <> 0
dec Zeitzähler
Zeitzähler =0 Eingang ist Gültig und stabil
Aktion auf Ereignis Eingang
End
..... weitere Programmschritte
end Loop

Du musst das nur in funktionierende C-Programmierung bringen.
Gruß oldmax

Hallo @oldmax

Habe heute mal deine Routine versucht nach zu bauen. Leider funktioniert das so nicht, mache ich was falsch?



CodeBox C
/* 5[ms] */
void DebounceKey(void)
{
   Debounce.InputNew = ((SWITCH_THREE_PORT & SWITCH_THREE_PIN));
   Debounce.InputOld ^= Debounce.InputNew;
   if (Debounce.InputOld)
   {
       Debounce.Cnt = 3;
       Debounce.InputOld = Debounce.InputNew;
   }
   else if (Debounce.Cnt != 0)
   {
       PORTD.OUTTGL = (1<<4); // state led red
       Debounce.Cnt--;
   }

   if (Debounce.Cnt == 0)
   {
       Debounce.Debounced |= 0x01;
   }
}
 
So klappt es, der Nachteil ist halt nur, dass ich damit nur eine Taste abfragen kann.
Kann man es mit der Methode auch so umbauen, dass es mit mehreren Tasten klappt?!


CodeBox C
/* 5[ms] */
void DebounceKey(uint8_t msk)
{
   Debounce.InputNew  = SWITCH_THREE_PORT & SWITCH_THREE_PIN ;
   Debounce.InputOld ^= Debounce.InputNew;

   if (Debounce.InputOld)
   {
       Debounce.Cnt = 4;
       Debounce.InputOld = Debounce.InputNew;
   }
   if (Debounce.InputNew == Debounce.InputOld)
   {
       Debounce.Cnt--;
   }
   if (Debounce.Cnt == 0)
   {
       Debounce.Debounced |= 0x01; // Switch Three Debounced
       PORTD.OUTTGL = (1<<4); // state led red
   }
}
 
Kann man es mit der Methode auch so umbauen, dass es mit mehreren Tasten klappt?!

Du könntest die notwendigen Variablen (für einen beliebigen Portpin) in einer allgemeinen Struktur anlegen und von dieser dann n Instanzen erstellen (Array), für n Portpins.

(Die Funktionsweise deiner Methode habe ich mir nun nicht genauer angeschaut)

Dirk :ciao:
 
Hallo Janiiix3
Hi @all
Bin seit langer Zeit mal wieder auf dieser Seite. Meine Rentneruhr ist schon seit einem Jahr abgelaufen und nun geht es mir wie allen Rentnern, ich hab keine Zeit mehr...
Das ein so alter Beitrag immer noch aufgegriffen wird erstaunt mich. janiiix3, du solltest nach fast 2 Jahren doch in der Lage sein, Eingänge zu entprellen. Ich weiß jetzt nicht mehr, wo damals meine Gedanken waren, aber wenn du die Eingangsbits auf Bytes legst, so brauchst du doch nur schauen, ob sich in diesen Bytes was geändert hat. Wenn ja, setzt du die Prellzeit. Bei Ablauf übernimmst du diese Bytes in einen Inputspeicher.
In Assembler habe ich z.B. 3 Variablen
New_In: aktuelle Eingänge vom Port, gelesen in jedem Zyklus (pollen)
In_Debounce: vom letzten Durchgang gelesene Eingänge. Bei einem Unterschied zwischen New_In und In_Debounce wird die Prellzeit gesetzt.
Akt_In: Wenn Prellzeit auf 0 läuft, wird derWert aus New_In nach Akt_In kopiert. Natürlich darf die Prellzeit nicht weiterlaufen. Dafür habe ich ein Controllbit, welches den Zähler für die Prellzeit freigibt. Beim Erreichen von 0 wird dieses Controllbit gelöscht und nur in diesem Schritt die Eingänge übernommen.
in Assembler:
Lesen der Eingänge:
Read_IO:
IN r16, pinb ; Beispiel Port B lesen.
ANDI r16,0b00111111 ; unbenutzte Bits evtl. ausmaskieren
STS New_In, r16 ; ablegen,
; weitere Eingänge ebenfalls einlesen, maskieren und ablegen
RET

Entprellen:
Debounce:
LDS r16, New_In ; gelesene Eingänge aus diesem Zyklus
LDS r17, In_Debounce ; zuletzt gelesene Eingänge aus Zyklus davor
EOR r17, r16 ; Ergebnis 0, wenn beide Variablen gleich sind. (keine Änderung)
STS In_Debounce, r16 ; aktuelle Eingänge ablegen
BREQ Count_Down ; Sprung, wenn keine Änderung
LDS r16, Controll ; Controllbyte laden
ORI r16,0b00000001 ; z.B. Controllbit 0 setzen
STS controll, r16 ; und ablegen
LDI r16, 50 ; Prellzeit laden
STS deb_Time ; und ablegen
rjmp End_Debounce ; und beenden
Count_Down:
LDS r16, Controll ; prüfen, ob Prellzeit läuft
ANDI r16,0b00000001 ; z.B. mit Controllbit 0
BREQ End_Debounce ; Routine beenden, wenn keine Zeit läuft
LDS r16, Deb_Time ;Prellzeit laden
DEC r16 ; herunterzählen
STS Deb_Time, r16 ; wieder speichern
BRNE End_Debounce ; und beenden, wenn nicht 0 erreicht
LDS r16, New_In ; darf auch gern In_Debounce sein...
STS akt_In, r16 ; ab hier sind die Eingänge entprellt und sind in Akt_In verfügbar.
LDS r16, Controll ; noch das Controllbit loschen
ANDI r16, 0b11111110
STS Controll, r16

End_Debounce:
Ret


Beide Routinen werden in der Programmschleife aufgerufen

Loop:
....
RCALL red_IO
RCALL debounce
....
rjmp loop

So, ich hoffe nun, entprellen ist klar. Der Wert 50 ist mal so aus der Luft gegriffen. Man kann auch ein zeitereignis benutzen, um diese Routine aufzurufen. Z. B. in jeder msek. Dann ist eventuell schon ein Wert der Entprellzeit von 5 ausreichend.
So, ich hoffe, ich find jetzt öfter wieder zu euch.
Gruß oldmax
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)