mikrocontroller.net

Forum: Projekte & Code Entprellen für Anfänger


Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Bei der bekannten Super-Duper-Lösung haben Anfänger oft das Problem, sie 
zu verstehen, da das Vertikal-Counter Prinzip nicht so offensichtlich 
ist. Außerdem haben Anfänger oftmals Angst vor Interrupts.

Man sieht ja oft die abenteuerlichsten Konstrukte, die mehr rein 
zufällig funktionieren. D.h. wenn man sie in weiteren Projekten benutzen 
will, gibt es plötzlich das große Staunen, weil eine vermeintlich 
fertige Lösung nun rumzickt.
Manchmal wird auch nur das Loslassen gemeldet. Man stelle sich mal vor, 
die S-Bahn Tür würde erst bei Loslassen der Öffnungstaste öffnen. Der 
Griff zur Notbremse ist damit vorprogrammiert.


Hier nun eine Lösung, die deutlich einfacher gestrickt ist, d.h. gut zu 
verstehen. Daher kann sie natürlich nicht so leistungsfähig sein. Aber 
die meisten Frickellösungen wird sie trotzdem weit in den Schatten 
stellen.

Da sie keinen Interrupt benutzt, gibt es eine Einschränkung dahingehend, 
daß sie pro Drücken und Loslassen mindestens einmal aufgerufen werden 
muß, sonst geht die Betätigung verloren.

Ansonsten ist die Wirkungsweise identisch:
Man hat ein Flag, wo gemerkt wurde, ob vorher Losgelassen oder Gedrückt 
erkannt wurde. Und dieses Flag entscheidet nun, ob die entgegengsetzte 
Betätigung entprellt werden muß. Damit vermeidet man unnötige 
Wartezeiten, wenn die Taste länger gedrückt wird.
Auch wird während der Entprellzeit 256 mal auf Preller geprüft, d.h. bei 
einem Preller wird nicht die komplette Wartezeit vergeudet.
Damit muß man auch nicht so lange warten, wie bei nur einem Test, da ja 
die Prellperiode (Federkonstante der Kontaktfeder) kürzer ist, als die 
gesammt Prellzeit. Daher sollten 25ms ausreichen.

Sie muß als Macro definiert sein, damit auch für jede Taste eine andere 
Flagvariable angelegt wird. Daher können mehrere Tasten unabhängig 
entprellt werden.


Peter

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> Manchmal wird auch nur das Loslassen gemeldet. Man stelle sich mal vor,
> die S-Bahn Tür würde erst bei Loslassen der Öffnungstaste öffnen. Der
> Griff zur Notbremse ist damit vorprogrammiert.

Ich habe das Beispiel mal umgesetzt (#) und beobachte, dass die LED den 
Zustand erst in dem Moment ändert, wenn der Taster losgelassen wird.

Meine grundsätzliche Änderung ist hardwareseitig: Der Taster auf dem 
Pollinboard ist active-high vorgegeben, d.h. externer Pull-down im 
Ruhezustand und Verbindung zu Vcc im gedrückten Zustand.

Edit: Wenn ich die Logik im Makro für active high anpasse, funktioniert 
das Umschalten wie erwartet direkt beim Drücken der Taste. Prima! Die 
Anpassung ist jetzt auch im Wikiartikel.

(#) 
http://www.mikrocontroller.net/articles/Pollin_Fun...

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan B. schrieb:
> Meine grundsätzliche Änderung ist hardwareseitig: Der Taster auf dem
> Pollinboard ist active-high vorgegeben

Das ist die ungebräuchliche Beschaltung, da man dann nicht die internen 
Pullups benutzen kann.
Aber ich denke, das schaffst Du selber.
Ist wie beim Fernsehquiz: "Legen sie ein ! um, damit die Funktion 
stimmt."
Und die Zeilen zum Enablen des Pullups müssen natürlich auch raus.

Man kann sogar das Programm so ändern, daß beide Flanken zurückgegeben 
werden (als 1 bzw. 2), dazu muß nur eine Zuweisung auf "i = 2;" geändert 
werden.


Peter

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Funktioniert :)

Autor: Felix (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich finde es zwar schön das hier ein Beispiel für Anfänger ist aber ich 
komme mit dem Macro nicht zurecht... wie macht man daraus eine Funktion 
?

Ich habe das jetzt in meine "main" eingefügt und es funktioniert, würde 
das Ganze aber gerne in ein Modul auslagern (tast.c und tast.h).

Das Define gehört ja dann in das .h file und der Code ja in das 
entsprechende c file.

Sorry das ich nachfrage aber ich verstehe Macros noch nicht.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du kannst das Macro in ein *.h File schreiben und dann aufrufen.

Als Macro habe ich es schreiben müssen, damit auch mehrere Tasten 
entprellt werden können. Dazu muß aber für jede Taste eine extra 
Flagvariable angelegt werden.

Wäre es eine Funktion, würde die Variable für alle Tasten die gleiche 
sein und damit das Programm nicht funktionieren.

Ein Macro ist nur eine Textersetzung, d.h. der Macroaufruf wird durch 
den kompletten Text ersetzt und damit wird bei jeder Ersetzung auch eine 
neue static Flagvariable angelegt.
Daher darf man das Macro pro Taste auch nur an einer Stelle aufrufen.

Ein Macro endet am Zeilenende und deshalb müssen die Zeilen der 
Macrodefinition mit dem Zeilenverlängerungszeichen '\' abgeschlossen 
werden. Für den Compiler ist das alles also nur eine einzige Zeile.

Ein "return" darf man im Macro nicht verwenden, es würde sich auf den 
Aufrufer beziehen, d.h. das Main würde verlassen.
Ein Macro hat aber einen Wert, wie jeder C-Ausdruck und das ist der Wert 
der letzen Anweisung. Deshalb das "i;" am Ende.

Ansonsten verhält sich ein Macro wie eine Inline-Funktion.


Peter

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
if( --i == 0 ){
  flag = 1;
  i = 1;
  break;

Kann mir bitte jemand erklären, wann diese if-Bedingung wahr wird? i ist 
doch vorher immer Null und die Bedingung wird doch erst wahr, wenn i=1 
ist, oder hab ich da was falsch verstanden?

Danke

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine 8Bit Variable erreicht nach 256 mal runterzählen wieder 0.

Man könnte auch zu Anfang "i = 255;" schreiben, der eine 
Schleifendurchlauf weniger spielt hier keine Rolle.


Peter

Autor: Jack (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
was ist eigentlich der Unterschied zwischen einer debouce() function und 
der _delay_ms() alleine
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
{
    if ( ! (*port & (1 << pin)) )
    {
        /* Pin wurde auf Masse gezogen, 100ms warten   */
        _delay_ms(50);  // max. 262.1 ms / F_CPU in MHz
        _delay_ms(50); 
        if ( *port & (1 << pin) )
        {
            /* Anwender Zeit zum Loslassen des Tasters geben */
            _delay_ms(50);
            _delay_ms(50); 
            return 1;
        }
    }
    return 0;
}
Beispiel:
debounce(&PIND, PD2);
if(!(PIND & (1<<PIND2)))
    {
   test();
  }
mann hätte ja auch sowas schreiben können
if(!(PIND & (1<<PIND2)))
    {
         test();
  _delay_ms(1000);
  }
denn braucht der Compiler in dem Fall nicht mehr die debounce Funktion, 
um auf die delay header zugreifen.
anderes formuliert was ist der Nutz von so eine Funktion, wenn man das 
gleiche mit einer Zeile erzielen kann
Danke im Voraus
Jack

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jack schrieb:
> was ist eigentlich der Unterschied zwischen einer debouce() function und
> der _delay_ms() alleine
>
> inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)
> 
> ...

Dieses debounce() hat nicht im entferntesten Ähnlichkeit mit meiner 
Funktion.
Es verlangt, daß genau darin innerhalb 100ms losgelassen wird.
Sobald Deine Programme auch nur ein Quentchen größer werden und nicht 
ständig im debounce() rumtrödeln können, werden Dir massiv Tastendrücke 
verloren gehen.
Außerdem werden ständig dem Main 100ms CPU-Zeit geklaut, solange eine 
Taste gedrückt ist.

> Beispiel:
> debounce(&PIND, PD2);
> if(!(PIND & (1<<PIND2)))
>     {
>    test();
>   }

Das ist völliger Quatsch, wo hast Du das her?
So darf mein Debounce nicht aufgerufen werden und es fehlt die 
Auswertung des Returnwertes.
Und danach den Pin nochmal direkt einzulesen, macht das Entprellen ja 
wieder zunichte.
Schau Dir doch bitte erstmal meinen Beispielcode an, da ist es richtig.


> anderes formuliert was ist der Nutz von so eine Funktion, wenn man das
> gleiche mit einer Zeile erzielen kann

Dann zeig dochmal diesen Einzeiler her, der auch sauber entprellt und 
die Flanke auswertet.


Peter

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
eine Zeile kann lang sein...

Autor: Jack (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter schreibt:
>Dann zeig dochmal diesen Einzeiler her, der auch sauber entprellt und
>die Flanke auswertet

der sauber entpreller kann einfach so
_delay_ms(100);
sein, so dass man nicht x-mals nach einem Zustand abfragen braucht

>Dieses debounce() hat nicht im entferntesten Ähnlichkeit mit meiner
>Funktion

ja vielleicht habe ich meine Frage falsch formuliert, der Sourcecode, 
und die Beschreibung stammen aus der
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...
und ich kann nicht verstehen warum man sich die mühe macht und die 
Enprellfunktion schriebt, wenn man an jedem Gebrauch des Entprellens 
einfach
_delay_ms(DEBOUCE_TIME);
 statt
(debounce(&PINx, Pxx))
 schreiben kann

Danke nochmal im Voraus



mfg
Jack

Autor: Fabian B. (fabs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was machst du wenn deine Taste länger als 100ms prellt, oder wenn du ein 
Zeitkritisches Programm hast und nicht 100ms "übrig"?

Gruß
Fabian

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Probier einfach mal das gleiche zu erreichen, wie mein Beispielcode:

Du hast 2 Tasten und 2 LEDs. Jede Taste muß ihre LED umschalten, egal 
wie lange gedrückt wird und ob die andere Taste gedrückt ist.
Das wirst Du mit Deinem Delay nicht schaffen. Delay und Entprellen sind 
2 völlig unterschiedliche Dinge.


Du wirst Dich bestimmt auch schon über Geräte mit schlechtem Entprellen 
geärgert haben.
Nur weil andere schlecht programmieren, braucht man noch lange nicht 
selber schlecht programmieren.


Peter

Autor: Carsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin Jungs. Ich habe eine einfache Frage. Ich möchte eine Überprüfung 
machen, ob an einem Digitaleingang eine Spannung anliegt. Ich prüfe 
allerdings nur, ob keine Spannung anliegt. Dann wird ein ErrorFlag 
gesetzt. Jetzt muss ich beim Aufruf der Funktion nicht mehr
if(debounce(&PINB,PB1))
         ERR=1;

aufrufen, sondern das invertierte von PB1,weil die Flag ja nur gesetzt 
werden soll, wenn nichts anliegt. Wie kann ich PB1 invertieren, bzw was 
muss ich übergeben beim Aufruf?

Grüße und Danke

Autor: ich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich weis ja nicht warum immer no ein Umstand für getrieben wird einen 
Taster zu entprellen... das geht mit nem Widerstand und einem 
Kondesnator deutlich einfacher
:-)

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich schrieb:
> Ich weis ja nicht warum immer no ein Umstand für getrieben wird einen
> Taster zu entprellen... das geht mit nem Widerstand und einem
> Kondesnator deutlich einfacher

Das ist richtig - vor allem für den Hobbybastler, bei dem Platinenfläche 
und Kosten eine eher untergeordnete Rolle spielen. Wenn du allerdings an 
ein kommerzielles Produkt denkst, das zig mal gebaut wird, summieren 
sich die Mehrkosten für die größere Platine und die RC-Kombination und 
irgendwann spielt das u.U. eine Rolle.

Außerdem ist die Software-Lösung flexibler: dort änderst du ein paar 
Parameter und kannst so das zeitliche Verhalten beeinflussen - bei der 
Hardwarelösung musst du dazu schon zum Lötkolben greifen.

Oder denk an ein Projekt mit vielen Tastern/Schaltern: willst du 
wirklich alle Rs & Cs bestücken müssen? Bei einer Tastatur wären das 
immerhin über 200 Bauteile.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich schrieb:
> Ich weis ja nicht warum immer no ein Umstand für getrieben wird einen
> Taster zu entprellen...

Wo siehst Du da einen Umstand?
Einfach nur nen bewährten Code benutzen, sich freuen und gut is.

Umständlich ist immer nur, einem Anfänger zu erklären, das Entprellen 
nötig ist und das ein stumpfes Delay kein Entprellen ist, sondern nur 
die CPU stark belastet.

> das geht mit nem Widerstand und einem
> Kondesnator deutlich einfacher
> :-)

Stimmt, damit kackt dann die CPU deutlich einfacher ab, siehe 
Pollin-Board:
Beitrag "Pollin AVR Board Fehler beim drücken der Taster / Qualität der Bauteile"

Funktionieren ist was anderes.


Peter

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Carsten schrieb:
> if(debounce(&PINB,PB1))
>          ERR=1;

Wenn Du Dich nicht auf den ursprünglichen Code beziehst, mach bitte nen 
eigenen Thread auf.


Peter

Autor: Sam .. (sam1994)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist das wirklich so ein Anfängerproblem?
Ich kann mich eigentlich auch als µC Anfänger bezeichnen. Gut, ich 
konnte davor schon programmieren und ich kannte mich auch mit elektronik 
und so aus.
Trotzdem habe ich für bei meiner Entprellmethode keine Probleme gehabt. 
Also bei meiner wird nur x mal gezählt ob eine Taste auch die x Zyklen 
gedrückt ist. Bei Prellungen wird von vorne angefangen. Braucht man 
einen Zustand erst nach Loslassen wird das im entsprechenden Code dann 
direkt geregelt.

Ich weiß nicht was daran schwer sein soll.

Autor: alakalele (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin neu hier und ein echter Rookie, "muss" mich aber aufgrund einer 
Studienarbeit mit der ganzen µC-Thematik befassen und ein bisschen 
programmieren (es macht an sich Spaß, ist nur echtes Neuland), unter 
anderem eben einen Taster inkl. Entprellung.

An dem debounce-Makro versteh ich größtenteils alles, bis auf die 
if(flag)-Abfrage.
flag ist ja am Anfang 0, d.h. es wird direkt else abgearbeitet. Aber 
selbst dann, wenn der Taster also die 25ms gedrückt bleibt und flag=1 
gesetzt wird, so wird doch das Makro beendet und eine 1 zurückgegeben, 
oder? Das wiederum heißt doch, dadurch dass bei erneutem Aufrufen des 
Makros flag wieder 0 gesetzt wird, dass die if(flag)-Abfrage nie true 
ist und nie abgearbeitet wird ... wozu ist diese dann da?
Oder hab ich da ordentlich was übersehen?

Sorry, ich weiß auch, dass der Thread schon alt ist, aber die Frage 
kommt mir leider erst 2012;)

Dank euch!
Beste Grüße

Autor: Stefan C. (felsnadel)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi!

Du hast übersehen, dass die Variable flag als static deklariert wurde.

Gruß
Stefan

Autor: David G. (david_g10)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
heißt das, dieses macro erstellt mir für jeden Aufruf mit einem andere 
Taster eine neue static flag oder wie ist das gemeint mit: ich muss für 
jeden taster eine neue flag verwenden?

Grüße

Autor: Patrick (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Makros werden vor den Compilieren (durch den PreProcessor) ersetzt. Also 
an der Stelle wo das Makro aufgerufen wurde, steht anschliessend das 
Makro selber (der Code des Makros). Static vor einer Funktions-Varibale 
bedeutet, dass die Lebensdauer global ist. Soll heissen, wird die 
Funktion verlassen und in der Variable steht ein 123, dann steht dieser 
Wert auch noch drinn, wenn die Funktion erneut aufgerufen wird. Die 
Initialisierung der Variable findet nur bei der Deklaration statt.

Ich finde die Zählmethode auch nicht schlecht, habe aber zu wenig 
Erfahrung zu diesem Thema.

Patrick

Autor: Tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo ich habe eine Frage zu dem Code.

ich denke ich habe den Code verstanden, aber eine Zeile kommt mir 
komisch vor:

Das Flag ist zur internen Verarbeitung, ob die Taste vorher gedrückt 
war, oder ob sie nicht gedrückt war. - das wird nicht nach "aussen" 
gegeben.

In der main wird praktisch nur das "i" ausgewertet (0=key release 
debounced und 1=key pressed debounced)

Für den Fall die Taste war nicht betätigt und ist nun betätigt und 
entprellt gibt mir die funktion i=1 zurück und das flag ist 1 (wenn die 
Taste prellt, ist i=0 und die schleife wird mit break unterbrochen)

Für den Fall die Taste war betätigt und ist nun nicht betätigt und 
entprellt gibt mir die funktion i=0 zurück und das flag ist 0 (wenn die 
Taste prellt, ist >>>i=0<<< und die schleife wird mit break 
unterbrochen)
sollt beim prellen dann nicht das i=1 sein? oder habe ich nur einen 
Denkfehler?
if(  (port & 1<<pin) ){  /* ... until key pressed or ... */      \
        i = 0; <-- sollte hier nicht i=1 stehen?   /* 0 = bounce */ 

Autor: Tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tobi schrieb:
> if(  (port & 1<<pin) ){  /* ... until key pressed or ... */      \
>         i = 0; <-- sollte hier nicht i=1 stehen?   /* 0 = bounce */

Ups, hier ist ein ! verloren gegangen.
ich meine die Stelle bei if (flag):
if( flag ){              /* check for key release: */          \
    for(;;){              /* loop ... */                    \
      if(  !(port & 1<<pin) ){  /* ... until key pressed or ... */      \
        i = 0;  <-- sollte hier nicht i=1 stehen? /* 0 = bounce */                      \
        break;                                              \
      }  

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tobi schrieb:
> sollt beim prellen dann nicht das i=1 sein?

Nein.
1 bedeutet Ereignis "Drücken" und das passiert nur an einer einzigen 
Stelle.

Man kann auch das Ereignis "Loslassen" und die Zustände "Gedrückt" und 
"Losgelassen" zurückgeben. Das müssen dann natürlich 4 verschiedene 
Werte sein.

Autor: Tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK,
ich habe das so interpretiert:

für den Fall Flag = 1: (Taster ist gedrückt)

- Der Taster wird losgelassen und prellt: das Makro gibt eine 0 zurück 
(bounced)
- Der Taster wurde losgelassen und ist mitllerweile entprellt: das Makro 
gibt auch eine 0 zurück (key released debounced)

Wie kann ich nun den Zustand bounced und debounced unterscheiden?

Autor: Baby (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich beschäftige mich mal wieder nach ein paar Jehren Pause mit C und 
AVR-Chips. Da ich eine Programm-Code für das Entprellen gesucht habe, 
bin ich auf diesen Artikel gestossen. Ich weiss, das es normal ist. das 
der Code mit englischen Kommentaren versehen ist. Ich hatte auch 
Englisch in der Schule( zu DDR-Zeiten aber max 2h in der Woche), aber 
das macht es als Neueinsteiger nicht einfacher. Damit ich den Code 
besser verstehe, habe ich ihn mal in ein Flussdiagramm umgesetzt -> und 
schon war mir das Prinzip klar. Als Hilfe für andere möchte ich das 
Diagramm hier zur Verfügung stellen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.