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)
Datum: 27.04.2005 23:12
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>
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
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
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
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
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.
Datum: 12.09.2005 16:50
Die Entprellung funktioniert wirklich sehr gut. Dank an den Autor!
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
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?
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
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
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
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.
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
|
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
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
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?
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...
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.
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
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