www.mikrocontroller.net

Forum: Codesammlung Entprellen in C, schlanke Routine

Autor: Toni (Gast)
Datum: 27.04.2005 20:15

Hallo

Hier, wie ich denke, eine weitere sehr schlanke Routine zur Entprellung
eines Tasters.

Toni



***************************************************************

uint16_t status;
uint8_t taste0;

void entprell(void)
{
status = ((status<<1) | (!bit_is_clear(PIND,PIND0)) | 0xe000);
if (status==0xf000)  taste0=0;
else taste0=1;
}

***************************************************************


Funktion kann im Hauptloop stehen, oder per Interrupt regelmässig
aufgerufern werden.

Taste gedrückt entspricht Pegel "0".
In 'status' wird eine '1' von rechts nach links geschoben, die die
letze Flanke '1->0' darstellt.
Falls Taste nicht prellt, folgen nur noch'0'.
Im gesamten werden hier 12 Umgänge geprüft, kann mit Maske/Typ für/von
'status' geändert werden !

Also zu Beginn:status      :1110 0000 0000 0000  (Maske 0xe00)
Wärend prellen, z.B        :1110 0010 0100 1001
Entpellt (12 cycles)       :1111 0000 0000 0000  (status==0xf000)
Autor: peter dannegger (Gast)
Datum: 27.04.2005 23:12
Dateianhang: ENTPRELL.LST (2 KB, 277 Downloads)

Du hast Dir bestimmt noch nicht den erzeugten Assembler (56 Byte Code
siehe Anhang) angesehen, sonst würdest Du nicht so leichtfertig von
"schlank" sprechen.

Mit auch 3 Bytes SRAM aber nur 46 Byte Code entprelle ich Dir sogar 8
Tasten gleichzeitig.


Peter


P.S.:
Man sollte schon dem Namen Codesammlung gerecht werden und einen
funktionierenden Code posten.

Dein Fragment erzeugt aber nur tonnenweise Fehlermeldungen.

Also das nächste mal wenigstens etwas compilierbares posten, d.h. mit
den nötigen includes:

#include <sfr_defs.h>
#include <io.h>
Autor: Hagen (Gast)
Datum: 28.04.2005 04:12

@Toni:

wenn mein AVR mit 16 MHz getaktet ist, und ich deine Entprellroutine in
meiner Mainloop ständig aufrufe dann komme ich auf 16.000.000 / 16 =
1Mhz Entprellung. Jedes Prellen von Tastern muß mit einer Frequenz
größer 1 Mhz reinkommen, alles was drunter liegt wird als Tastendruck
erkannt.

wenn mein AVR nun aber mit 32Khz getaktet ist so muß also 32.000 / 16 =
2Khz sein, alles was schneller als 2Khz ist wird als Prellen erkannt,
alles was langsammer als 2Khz ist wird als Tastendruck erkannt. Das
funktioniert also noch fast, wenn nicht viele Taster mit bis zu 10ms =
100Hz prellen würden.

Ergo: damit deine Entprellroutine sauber funktioniert darf ich sie nut
alle 20 mal in der Mainloop aufrufen, bei 32KHz Takt. Bei 16Mhz Takt
darf ich sie nur alle 8000 male aufrufen. Es ist aber nun das Problem
das in der Mainloop noch viele andere Aufgaben abgearbeitet werden, zb.
alle 5 Sekunden für 2 Sekunden eine Datenüertragung die im 250ms Takt in
die Mainloop zurückkehrt. In diesen Momenten würde der 8000'ernder
Zähler aber nur alle 250ms inkremetiert und somit muß ich diesen Zähler
ständig so anpassen das er auf alle diese zusätzlichen aufgaben
umgestellt wird, um ein gleichmäßiges Pollen zu erreichen.

Naja, mein jetziger Sourcecode, rein virtuell gesehen, verbraucht jetzt
fast 1 Kb an zusäzlichen Code damit ich diesen Zähler so kontinuierlich
an die Prozessorlast anpassen kann das deine Entprellroutine wieder
periodisch alle 10 ms aufgerufen wird.

Gruß Hagen
Autor: Marcel (Gast)
Datum: 28.04.2005 08:02

wenn du einen timer frei hast, dann packst du das in die isr von dem
timer (siehe peters code)

von einem in hochsprache geschriebenen programm kann man nicht
erwarten, dass da wenige asm befehle rauskommen. Wenn man das will,
dann muss man dann schon eine menege kohle für einen ordentlich
optimierenden compiler hinlegen. oder schreibt es gleich in assembler
Autor: Hagen (Gast)
Datum: 28.04.2005 10:48

@Marcel,

jo ich weis das, nur zu einem ordentlichen und funktionstüchtigen Code
hier in der CodeLib sollten die kompletten Sourcen und nach Möglichkeit
ein DEMO rein. Dann würde man sehen das sich die Aussage "schlank"
nochmals relativiert.

Gruß Hagen
Autor: peter dannegger (Gast)
Datum: 28.04.2005 11:38

"von einem in hochsprache geschriebenen programm kann man nicht
erwarten, dass da wenige asm befehle rauskommen."

Das hat auch nirgends nicht niemand behauptet.

Aber wenn man int aufm 8-Bitter nimmt und viele Anweisungen in eine
Zeile packt, mag das zwar optisch klein aussehen, nicht jedoch in
realem Code.


So schlecht finde ich den GCC garnicht. Zumindest bei meinen Code ist
selbst in Assembler kaum noch was rauszuholen.
Bei Tonis Code sieht man schön den doppel-RET-Bug, aber das sind ja nur
2 Byte toter Code je Funktion.
Man muß das immer auf das gesamte Programm bezogen sehen, da sind dann
10% mehr Code nicht der Rede wert.


Peter
Autor: hanes (Gast)
Datum: 29.04.2005 13:04

http://www.mikrocontroller.net/articles/Entprellung

hier kannst du deine routine eintragen. Dann gibt es eine übersicht mit
allen entprellroutinen. Mit ihren Vorteilen und nachteilen.
Autor: Markus Mussner (markus81)
Datum: 12.09.2005 16:50

Die Entprellung funktioniert wirklich sehr gut. Dank an den Autor!
Autor: Christian K. (Gast)
Datum: 14.09.2005 21:54

Ich möchte mal eine andere Methode zur Entprellung vorschlagen:

1) Die erste Flanke triggert einen Speicher (z.B. flankengetriggerter
Interrupt)
2) Danach interessiert mich die Taste erstmal nicht, d.h. ich ignoriere
sie für mindestens die Prell-Zeit.
3) Dann werte ich die Taste wieder aus (Interrupt scharf schalten)

Mit dieser Entprellung bekomme ich auch den kürzesten Druck mit, sowie
auch die exaktere Zeit wann die Taste gedrückt wurde.

Natürlich muss man die Prell-Zeit der Taste abschätzen, meistens ist
sie spezifizert. Wenn nicht kann bei Bedienung durch Menschen 20ms-30ms
sicher als kürzestes Intervall zwischen zwei mal drücken angenommen
werden.

Die Methode für eine gewisse Zeit auf einen stabilen Zustand zu warten
macht für mich weniger Sinn, vorausgesetzt natürlich, die Taste prellt
nicht von alleine, d.h. es gibt nur dann eine Flanke wenn auch wirklich
einer versucht darauf rumzudrücken.

Grüße
  Christian
Autor: Bolle (Gast)
Datum: 14.09.2005 22:48

>Mit dieser Entprellung bekomme ich auch den kürzesten Druck mit, sowie
>auch die exaktere Zeit wann die Taste gedrückt wurde.

Zur "exakten Zeit": Die Bedienung einer Taste durch einen Menschen
(nicht nur solche mit Wurstfingern...) ist mit einer gewissen
zeitlichen Ungenauigkeit behaftet, welche so groß ist, daß es schlicht
keine Rolle spielt, ob der µC das Niederdrücken innerhalb von 4
Maschinenzyklen (Externe-Interrupt-Reaktionszeit) mitbekommt, oder erst
10 ms später.  Und selbst wenn er es "sofort" mitbekommt: Speicherst
Du den Tastenstatus zunächst in einer Variablen ab, läßt den µC dann
erstmal noch 10 ms was anderes rechnen, und wertest danach die
Tastenvariable aus, dann hast Du letztlich doch wieder eine um 10 ms
verzögerte Reaktion.

Zum "kürzesten Druck": Ein Mensch ist anatomisch nicht in der Lage,
eine Taste kürzer als eine gewisse Mindestzeitspanne niederzudrücken.
Wie groß die ist, hängt natürlich von dem Mensch und von dem Schalter
ab, aber sie ist in jedem Fall so groß, daß Dir bei 50 Abtastungen/s
mit Sicherheit auch nicht der kürzeste "man-made" Tastendruck
entgeht.

Zu dem, was ich in Deiner Beschreibung vermisse, nämlich die Nennung
des Nachteils Deiner Methode: Du belegst einen wertvollen externen
Interrupt mit einer läppischen Eingabetaste.  Was ist, wenn Du den
Interrupt dringend für eine andere Aufgabe benötigst, bei der es
wirklich auf Speed ankommt?  Und vielleicht hast Du auch nicht nur eine
Taste, sondern eine 4x4-Tastenmatrix.  Wie willst Du die an einen oder
zwei Interrupteingänge anschließen?
Autor: Peter Dannegger (Gast)
Datum: 15.09.2005 12:02

"daß es schlicht keine Rolle spielt, ob der µC das Niederdrücken
innerhalb von 4 Maschinenzyklen (Externe-Interrupt-Reaktionszeit)
mitbekommt, oder erst 10 ms später."


Und nicht nur das, es ist in der Regel auch völlig unerwünscht !!!

Z.B. mit Gummisohlen über Teppich laufen und sich dem Gerät nähern: Ein
Funke springt über, der externe Interrupt löst aus, obwohl man gar
nichts drücken wollte !


Der Fahrstuhl in unserem Haus hat z.B. eine solche saublöde
Entprellroutine. Man muß die Taste nur berühren (nicht mal drücken) und
schon leuchten beide Fahrtrichtungen d.h. die Tür geht immer zweimal auf
und zu.
Abhilfe: Man muß sich vor dem Drücken immer erst an der Fahrstuhltür
erden.

Mit einer richtigen Entprellroutine würde sowas aber nicht passieren.


Peter
Autor: Christian K. (Gast)
Datum: 15.09.2005 12:30

Ich benutze eigentlich immer gerne einen interrupt-fähigen Eingang, da
bei den meisten µC's dieser den µC aus den Sleep Mode holen kann. (ich
habe fast nur mit mobilen Geräten zu tun)

Auch Keypad's werden interrupt enabled (meistens ist die Veroderung
schon im Chip implementiert) betrieben.

ESD sichere Tasten natürlich vorrausgesetzt.

Grüße
  Christian
Autor: Peter Dannegger (Gast)
Datum: 15.09.2005 13:44

"Ich benutze eigentlich immer gerne einen interrupt-fähigen Eingang, da
bei den meisten µC's dieser den µC aus den Sleep Mode holen kann."


Genau dazu ist ja der Pin-Change-Interrupt da. Man läßt damit die CPU
aufwachen, wenn an einem Eingang irgendwas passiert.

Die eigentliche Auswertung, was genau passiert ist, machen dann aber
die entsprechenden Routinen.

Und wenn sich herausstellt, daß nichts passiert ist, sondern es nur ein
Störimpuls war, geht die CPU eben wieder in den Sleep-Mode.


Ein paar richtige Zeilen Software sind bestimmt wesentlich kostenärmer
als teuere ESD-feste Tasten.

Die Tasten am Fahrstuhl sehen auch nicht gerade billig aus, aber sie
tuns eben nicht wegen der fehlerhaften Software.

Nicht alle Softwarefehler lassen sich durch Hardware korrigieren (und
umgekehrt), beides muß richtig sein.


Peter
Autor: Werner Dornstädter (dorni)
Datum: 12.05.2008 11:17

Da die Tasterentprellung doch ein grundlegendes Problem darstellt,
möchte ein Anfänger Tonis Programm als Lernhilfe anwenden
   (ähnelt dem von http://www.ganssle.com).
Nachdem der Code unvollständig ist, macht es erhebliche Probleme dies
nachzuvollziehen!

Wäre nun schön, wenn jemand den kompletten funktionsfähigen Code
reinstellen bzw. posten könnte.
Autor: Sven Pauli (haku) Benutzerseite
Datum: 12.05.2008 11:36

Zum Entprellen folgender Ansatz:
"buttons" enthält den Wert des Portregisters (PINx). Bei jedem Durchlauf
wird die Warteschlange "debouncer" eins weitergeschubst und gleichzeitig
mit den aktuellen Tastenzuständen verunded. Schließlich werden die
aktuellen Tastenzustände an den Anfang der Schlange gestellt.
Ausgewertet wird dann der letzte Eintrag der Schlange.

Resultat: Erst, wenn ein Impuls störungsfrei durch die ganze Schlange
gewandert ist, wird er ausgewertet.

volatile uint8_t debouncer[5];

/* Im Timer-Interrupt wird dann alle paar ms folgendes gemacht: */
for (i = 0; i < 4; i++) {
  debouncer[i + 1] = debouncer[i] & buttons;
}
debouncer[0] = buttons;

Auszug aus dem Listfile (der GCC hat die Schleife aufgewickelt!):
      /* Debounce buttons */
      for (i = 0; i < 4; i++) {
        debouncer[i + 1] = debouncer[i] & buttons;
 18e:  90 91 76 00   lds  r25, 0x0076
 192:  80 91 79 00   lds  r24, 0x0079
 196:  89 23         and  r24, r25
 198:  80 93 7a 00   sts  0x007A, r24
 19c:  80 91 7a 00   lds  r24, 0x007A
 1a0:  89 23         and  r24, r25
 1a2:  80 93 7b 00   sts  0x007B, r24
 1a6:  80 91 7b 00   lds  r24, 0x007B
 1aa:  89 23         and  r24, r25
 1ac:  80 93 7c 00   sts  0x007C, r24
 1b0:  80 91 7c 00   lds  r24, 0x007C
 1b4:  89 23         and  r24, r25
 1b6:  80 93 7d 00   sts  0x007D, r24
      }
      debouncer[0] = buttons;
 1ba:  90 93 79 00   sts  0x0079, r25
Autor: Franz (Gast)
Datum: 12.05.2008 12:05

Hallo

zuerst eine Vorbemerkung: schlank muss nicht mit gut gleichgesetzt
werden.

Ich schreibe die Entprellung immer in einem Timerinterrupt. Dann kann
ich ohne Probleme im Hauptprogramm den Schalter (als Globale, die im
Interupt gesetzt worden ist) abfragen.

Im Timerinterrupt zähle ich ein Variable hoch, falls die Taste gedrückt
ist und ich zähle die Variable herunter, falls die Taste nicht gedrückt
ist.
Dann setze ich noch eine obere Grenze für die Variable, sodass nicht
über 1000 gezählt wird und eine Grenze nach unten, sodass die Variable
den Wert 0 nicht unterschreitet.

Beim Drücken der Taste läuft die Variable, langsam nach oben.

Mit einer oberen und unteren Schwelle, setzt ich (wie bei einem
Schmitt-Trigger) den Schalterwert.

Das Schöne ist, dass alles im Hintergrund läuft.


(Der anagebene C-Code ist "frei" geschrieben und noch nicht über einen
Compiler gelaufen. Kleinere  Fehler können noch vorhanden sein)

Beispiel:



//globale
int z, Schalter;

Timer_intterupt()
{

 if(PORTD.0 == 1)
    {
    z++;
    if(z>= 1000)
          z= 1000;
     }
 else
    {
     z--;
     if (z<=0)
     z=0;
     }

if (z> 950)
   Schalter = 1;
 if (z< 50)
   Schalter =0;
}




Gruß
Franz
Autor: Peter Dannegger (peda)
Datum: 12.05.2008 13:12

Werner Dornstädter wrote:
> Da die Tasterentprellung doch ein grundlegendes Problem darstellt,

Nur für die absolut Lernresistenten, die starrsinnig die im Tutorial
genannte effektive Methode für 1..8 Tasten ignorieren müssen, stellt es
ein Problem dar.


> Wäre nun schön, wenn jemand den kompletten funktionsfähigen Code
> reinstellen bzw. posten könnte.

Warum sollte man etwas komplettieren, was schon im Ansatz deutlich
aufwendiger ist?
Reicht Dir eine effektive und funktionierende Methode denn nicht?

Peter
Autor: Werner Dornstädter (dorni)
Datum: 12.05.2008 19:25

Danke für Eure Antworten und Ratschläge, doch die Aufgabenstellung ist
etwas anders.
Ich will erstmalig in C Programmieren und einen Mikrocontroller
einsetzen.
Und nun habe ich die Aufgabenstellung Schalter entprellen gewählt, um an
dieser Aufgabe die Codes verstehen lernen.
Wie ja jedem bekannt ist, hat man als Anfänger schon Probleme beim
Kompilieren und nun müssen auch noch die entsprechenden Fehlermeldungen
verstanden und behoben werden.
Lange Rede kurzer Sinn: anbei meine ersten Gehversuche. So und nun hängt
es! Aufgrund meiner Recherchen fand ich diesen Beitrag, der mir
weiterhelfen könnte.
#include <stdint.h>      // Definition der Datentypen
#include <avr/io.h>      // Wir brauchen Zugriff auf die I/O's des Controllers
#include <avr/interrupt.h>
#include <stdbool.h>                  //
 
#ifndef F_CPU
#define F_CPU           1000000                   // processor clock frequency
#endif

#define bool_t int

#define ON true
#define OFF false


//----------------------------------------------------------------------------
// Diese Funktion liest den Schalterzustand der Hardware aus. 
// Service routine called by a timer interrupt
// extern bool_t RawKeyPressed(); 
   bool_t RawKeyPressed()
//----------------------------------------------------------------------------
  
   { 

   }


//----------------------------------------------------------------------------
// Service routine called by a timer interrupt
// Routine liefert TRUE, wenn einmal ein entprellter Übergang 
// zum Schalter geschlossen aufgetreten ist.
   bool_t DebounceSwitch2()
//----------------------------------------------------------------------------

   { 
    static uint16_t State = 0;                     // Current debounce status 
    State=(State<<1) | !RawKeyPressed() | 0xe000; 
    if(State==0xf000) return true; 
    return false;
   } 


//----------------------------------------------------------------------------
// Service routine called by a timer interrupt
   int main (void)
//----------------------------------------------------------------------------
   {


   }

//----------------------------------------------------------------------------

Danke für Euer Verständnis.
Sollte das Thread wohl verlegen, da hier ja nichts lauffähiges zu finden
ist?
Autor: Strahlemann (Gast)
Datum: 12.05.2008 21:14

Ich verstehe auch nicht, warum solch ein 0815-Thema immer und immer
wieder durchgekaut wird. Wo ist das Problem?

10ms Timer-Interrupt {

    Wenn Taste gedrückt {
        Zähler um 1 erhöhen
    } else {
        Zähler auf 0 setzen
    }

    Wenn Zähler = 5 {
        Aktion ausführen
    }

    Wenn Zähler > 100 {
        Aktion_bei_lange_gedrückter_Taste ausführen (falls gewünscht)
    }

}

Delays nach Belieben anpassen...
Autor: Gast (Gast)
Datum: 12.05.2008 21:28

> Ich verstehe auch nicht, warum solch ein 0815-Thema immer und immer
> wieder durchgekaut wird. Wo ist das Problem?
Das Problem ist, dass viele meinen, dass ein C-Turorial durchzuarbeiten
verschwendetet Zeit ist.
Man googelt lieber stundenlang bis man irgendwo einen Code-Schnipsel
findet, der sich kompilieren lässt und hält sich danach für den
genialen Programmierer.
Beim nächsten Mal gehr es dann wieder von vorne los.
Autor: Peter Dannegger (peda)
Datum: 12.05.2008 22:13

Werner Dornstädter wrote:
> Wie ja jedem bekannt ist, hat man als Anfänger schon Probleme beim
> Kompilieren und nun müssen auch noch die entsprechenden Fehlermeldungen
> verstanden und behoben werden.

Man kann versuchen, vor den Fehlermeldungen davonzurennen.
Man kann versuchen, vor seinem eigenen Schatten davonzurennen.
Die Erfolgsaussichten sind beidesmal sehr gering.

Wenn Du die erste Fehlermeldung im exakten Wortlaut postest und das
C-File als Anhang, damit man die Zeilennummer zuordnen kann, sollte sich
schnell jemand finden, der Dir weiterhilft.
Bei C-Fragen vorzugsweise im GCC-Forum posten.


Peter
Autor: Hannes Lux (hannes)
Datum: 12.05.2008 22:18

Strahlemann wrote:
> Ich verstehe auch nicht, warum solch ein 0815-Thema immer und immer
> wieder durchgekaut wird. Wo ist das Problem?
>
> 10ms Timer-Interrupt {
>
>     Wenn Taste gedrückt {

Besser:
Wenn Tastenzustand anders ist als der intern gemerkte entprellte Zustand


>         Zähler um 1 erhöhen
>     } else {
>         Zähler auf 0 setzen
>     }
>
>     Wenn Zähler = 5 {

Wenn Zähler = 3, denn ein Zweibit-Zähler kann nunmal nur von 0 bis 3,
und bei kleinen Controllern mit knappen Ressourcen kann man schon Wert
darauf legen, effizient zu programmieren.

>         Aktion ausführen

Intern gemerkten entprellten Zustand toggeln,
prüfen, ob es Tastendruck oder Tastenloslassen war,
bei Tastendruck Key_press-Flag setzen, das in der Mainloop ausgewertet
und gelöscht wird...

>     }
>
>     Wenn Zähler > 100 {
>         Aktion_bei_lange_gedrückter_Taste ausführen (falls gewünscht)

Dabei aber auch noch zwischen erster Verzögerung und Repeat-Verzögerung
unterscheiden...

>     }
>
> }
>
> Delays nach Belieben anpassen...

...

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

  • Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
  • Aussagekräftigen Betreff wählen
  • Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
  • JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
  • Schaltpläne, Screenshots usw. als PNG oder GIF anhängen

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel





Hinweis: der Originalbeitrag ist mehr als 6 Monate alt.

webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net