http://www.emcu.it/STM32/STM32Discovery-Debounce/STM32Discovery-InputWithDebounce_Output_UART_SPI_SysTick.html Ich habe diese Methode zur Entprellung entdeckt, verstehe allerdings nicht wirklich was er bei der Debounce Routine gemacht hat. Wäre freundlicht, wenn mir jemand die Funktionsweise erklären könnte.
Jede Millisekunde wird der Input gelesen. Es gibt zwei Variablen, die eine speichert die Anzahl an Samples bei der der Input 1 war und die andere bei der der Input 0 war. Jedes mal wenn sich der Status ändert, wird die Variable des anderen Status auf 0 gesetzt. So lange der Input prellt werden also beide Variablen regelmäßig auf 0 zurück gesetzt. Wenn der Input nicht mehr prellt steigt der Wert der einen Variablen bis sie größer oder gleich REFdebounce ist, dadurch wird die Variable StatoIn1 auf den aktuellen Status des Inputs gesetzt. Kleiner Nachteil dieses Ansatzes: Die Eingabe ist etwas verzögert (je nachdem wie lang der Input prellt und was für REFdebounce gewählt wurde) da der Status erst gesetzt wird nachdem der Input REFdebounce Millisekunden nicht mehr prellt.
:
Bearbeitet durch User
In1_0 zählt, wie lange In1 schon am Stück 0 ist. Wenn In1_0 größer als REFdebounce wird, dann wird Statoln1 auf 0 gesetzt. In1_1 zählt, wie lange In1 schon am Stück 1 ist. Wenn In1_1 größer als REFdebounce wird, dann wird Statoln1 auf 1 gesetzt. Mal Dir einfach mal auf, was bei einem prellenden In1 mit In1_0, In1_1 und Statoln1 passiert. Übrigens finde ich die Namensgebung in dem Beispiel etwas unglücklich. Gruß, Stefan
Stefan R. schrieb: > leiner Nachteil dieses Ansatzes: Die Eingabe ist etwas verzögert (je > nachdem wie lang der Input prellt und was für REFdebounce gewählt wurde) > da der Status erst gesetzt wird nachdem der Input REFdebounce > Millisekunden nicht mehr prellt. Danke für die hilfreiche Erklärung. Ein weiterer Nachteil ist dann doch auch, dass die Entprellfunktion jede Sekunde aufgerufen wird und nicht nur wenn man sie benötigt, z.B. erst nach einem externen Interrupt (durch Taster ausgelöst). Stimmts? Wo kann ich noch eine andere/bessere (die den von mir oben beschriebenen Nachteil nicht hat) Entprellung finden, die für Anfänger gut verständlich sind? Am besten für den STM32, da ich die Funktionen von http://www.mikrocontroller.net/articles/Entprellung nicht verstehe, da mir die Befehle des anderen Controllers einfach nichts sagen.
joop13 schrieb: > Ein weiterer Nachteil ist dann doch auch, dass die Entprellfunktion jede > Sekunde aufgerufen wird und nicht nur wenn man sie benötigt, z.B. erst > nach einem externen Interrupt (durch Taster ausgelöst). Stimmts? Nein, stimmt nicht. Du kannst ja mal den "Overhead" an CPU-Last ermitteln, der ist lächerlich klein, da lohnt sich einfach kein zusätzlicher externer Interupt. Ein externer Interrupt ist nur dann notwendig, falls man den MC per Taste aus dem Power-Down aufwecken will. Diese Routine liefert schon exzellente Ergebnisse bezüglich Entprellung und Störunterdrückung bei sehr geringer CPU-Last im Gegensatz zu vielen anderen Lösungen, die so im Web rumgeistern. Man kann sie bestenfalls noch etwas optimieren, insbesondere, wenn mehrere Taste zu entprellen sind. Z.B. reicht ein 2Bit-Zähler bei ~10ms Timerintervall aus. Dann kann man mit nur 2 Variablen bis zu 32 Tasten parallel entprellen. Und man kann im Interrupt gleich das Gedrückt-Ereignis bereitstellen bzw. bei Bedarf auch das Losgelassen-Ereignis.
joop13 schrieb: > Am besten für den STM32, da ich die Funktionen von > http://www.mikrocontroller.net/articles/Entprellung > nicht verstehe, da mir die Befehle des anderen Controllers einfach > nichts sagen. Naja, das ist genauso in C geschrieben wie der Code in deinem Beispiel auch. Die paar "Spezialfunktionen" für AVRs muss man halt austauschen. Wenn man das nicht selber machen will oder kann, guckt man sich Beitrag "Re: Universelle Tastenabfrage" an und nimmt den dortigen Code. Da fehlt dann nur noch die Header-Datei für das genaue Derivat.
Peter D. schrieb: > Man kann sie bestenfalls noch etwas optimieren, insbesondere, wenn > mehrere Taste zu entprellen sind. Wie kann ich diese Funktion für alle Pins verallgemeinern? Ich würde an die Funktion den Pin und Port übergeben (z.B. PD2) und dort eine switch-case Abfrage mit allen möglichen Pins. In dieser Abfrage dann den jeweiligen Pin einlesen. Peter D. schrieb: > Z.B. reicht ein 2Bit-Zähler bei ~10ms > Timerintervall aus. Dann kann man mit nur 2 Variablen bis zu 32 Tasten > parallel entprellen. Wie soll deine Möglichkeit genau funktionieren?
joop13 schrieb: > Wie kann ich diese Funktion für alle Pins verallgemeinern? > Ich würde an die Funktion den Pin und Port übergeben (z.B. PD2) und dort > eine switch-case Abfrage mit allen möglichen Pins. In dieser Abfrage > dann den jeweiligen Pin einlesen. Mir fällt gerade auf, dass dies keinen Sinn macht, weil dann der Rest der debounce Routine auch für jeden Pin extra gemacht werden müsste. Aber wie kann ich das sonst machen?
joop13 schrieb: > Wie soll deine Möglichkeit genau funktionieren? Ist hier auf den STM32 portiert: Micha schrieb: Beitrag "Re: Universelle Tastenabfrage"
Peters Methode (er ist hier unser Chef-Entpreller) funktioniert auch mit mehreren Tasten. Das ist in dem Artikel; unter dem Code; sehr gut erklärt. Achte mal darauf, wenn von "Vertikalen Zählern" die Rede ist. Da wird es spannend.
Ich denke mal für meine Zwecke (Entprellung von bis zu 16 Taster und für Anfänger) wäre wahrscheinlich die Methode "Debounce-Makro von Peter Dannegger" gut geeignet. Allerdings kann ich mit diesem Code überhaupt nichts anfangen. Hier einige Verständnisprobleme (ich komme mit dem AVR nicht klar): 1) Warum ist das Makro (welches laut Titel entscheidend für die Entprellung ist) auskommentiert. 2) Was bedeutet DDRB &= ~(1<<PB0); 3) PORTB |= 1<<PB0; Bedeutet dies, dass ich PB0 auf HIGH setze? PORTB ^= 1<<PB2; Bedeutet dies, dass ich PB2 auf LOW setze? 4) Warum hat man hier die ;;: for(;;){} Hosenmatz schrieb: > Peters Methode (er ist hier unser Chef-Entpreller) funktioniert auch mit > mehreren Tasten. Das ist in dem Artikel; unter dem Code; sehr gut > erklärt. > Achte mal darauf, wenn von "Vertikalen Zählern" die Rede ist. Da wird es > spannend. Welche Erklärung meinst du? Von der vertikalen Variante habe ich auch schon gelesen. Das sollte für mein Projekt allerdings schon zu umfangreich sein.
joop13 schrieb: > Wo kann ich noch eine andere/bessere (die den von mir oben beschriebenen > Nachteil nicht hat) Entprellung finden, die für Anfänger gut > verständlich sind? > > Am besten für den STM32, da... Das ist nicht sinnvoll, sowas zu fragen. Vorgehensweisen zum Entprellen von Tasten sind per se völlig unabhängig von jeglicher Rechnerstruktur. Du solltest danach trachten, den Sinn zu verstehen, dann kannst du damit Tasten an allen Typen von µC entprellen. Hier mal meine Herangehensweise: - abgefragt wird in der Systemuhr, die die Abfrage zumeist so alle 10 ms macht. - gilt eine Taste als ungedrückt und wird sie als gedrückt erkannt, dann wird sofort eine systeminterne Botschaft "Taste_XYZ_gedrückt" (aka "event") veranlaßt und die Taste als gedrückt markiert und ihr Entprellzähler gesetzt. Obendrein wird ein Repetier-Abwärtszähler auf eine erste (lange) Zeit gesetzt. - gilt eine Taste als gedrückt und wird sie als gedrückt erkannt, dann wird der Repetierzähler dekrementiert und der Entprellzähler gesetzt. Wird der Repetierzähler null, dann wird obige Botschaft veranlaßt, der Entprellzähler gesetzt und der Repetierzähler auf eine zweite (kurze) Zeit gesetzt. - gilt eine Taste als gedrückt und wird sie als ungedrückt erkannt, dann wird der Entprellzähler dekrementiert. Wird dieser null, dann wird die Taste als ungedrückt markiert. Mit diesem Verfahren hat man direktemang beim Drücken das Tsten-Ereignis verfügbar, das Entprellen kommt danach und man hat ein freundliches Repetierverhalten: ne längere Zeit beim ersten Repetieren und dann ne kürzere Zeit. So, jetz thoffe ich bloß, daß ich mich weiter oben nicht verschrieben habe... W.S.
joop13 schrieb: > Mir fällt gerade auf, dass dies keinen Sinn macht, weil dann der Rest > der debounce Routine auch für jeden Pin extra gemacht werden müsste. > > Aber wie kann ich das sonst machen? Mit SysTick der alle 5ms aufgerufen wird. Warum aus Entprellen immer wieder Raketenwissenschaft gemacht wird, bleibt für mich unklar. Pro Taster wird eine Variable reserviert, mit 9bit (2,5s) für Zähler und 2 bits als Tasterflags. Man nimmt aber 16bit, ist einfacher. Für 16 Taster ergibt das 16 * 16 bits = 256 Bits oder 16 2Byt Vars. Um es schneller und einfacher zu machen liest man alle Taster auf einmal ein oder in Gruppen a 8, blendet evtl. nicht benötigte bits aus und vergleicht mit vorherigem Zustand. Immer wenn irgendein Taster als gedrückt erkannt wird, wird sein Zähler um eins inkrementiert. Wenn Zähler >= 8 (40ms), wird der Taster als gedrückt geflagt. Wird dagegen Taster als nicht gedrückt erkannt, geht der zugehörige Zähler sofort auf Null, Tasterflags auch, d.h. man setzt die zugehörige Variable auf Null. Ist der Zähler >= 255 (1.28s) und Tasterflag == 1, wird Flag fur längeren Tastendruck gesetzt. Wenn Flag für längeren Tastendruck gesetzt ist, wird sein Zähler nicht mehr inkrementiert. Und das ist schon alles.
:
Bearbeitet durch User
joop13 schrieb: > 2) Was bedeutet DDRB &= ~(1<<PB0); > 3) PORTB |= 1<<PB0; Bedeutet dies, dass ich PB0 auf HIGH setze? > PORTB ^= 1<<PB2; Bedeutet dies, dass ich PB2 auf LOW setze? Warum hängst du dich an dem AVR-Kram auf und warum weigerst du dich vehement dir den schonmal 2x verlinkten Beitrag "Re: Universelle Tastenabfrage" mitsamt dem dortigen Quellcode anzusehen?
Micha schrieb: > Warum hängst du dich an dem AVR-Kram auf und warum weigerst du dich > vehement dir den schonmal 2x verlinkten > Beitrag "Re: Universelle Tastenabfrage" mitsamt > dem dortigen Quellcode anzusehen? Wahrscheinlich weil es einfach zu viel Code fur so wenig Arbeit ist ?
Marc V. schrieb: > Wahrscheinlich weil es einfach zu viel Code fur so wenig Arbeit ist ? Der Code ist absolut minimal und auch die CPU-Last ist lächerlich. Der große Rest sind Kommentare und Defines. Wens stört, der kann die Kommentare doch einfach löschen und die Defines ausschreiben. Wer aber schon einige Tage programmiert, wird wissen, daß Kommentare und Defines das Verstehen ganz enorm erleichtern. Und besonders, wenn man nach einigen Monaten weiter daran arbeiten will, weil einem noch zusätzliche Ideen eingefallen sind.
joop13 schrieb: > Wo kann ich noch eine andere/bessere (die den von mir oben beschriebenen > Nachteil nicht hat) Entprellung finden, die für Anfänger gut > verständlich sind? Ganz im Anfang habe ich in einem Arduino Buch (vom Erfinder selbst geschrieben) gelesen, dass er 10ms Pause einfügt. Hat sicher viele Nachteile, aber in vielen Anwendungen macht sich eine kurze Pause gar nicht bemerkbar. Ich habe das dann mal intensiv ausprobiert und festgestellt, dass bei 3ms schon nichts mehr passiert. Ich selbst füge 4ms Pause ein und bei meinen Sachen hat mir das bis jetzt gereicht.
Peter D. schrieb: > Marc V. schrieb: >> Wahrscheinlich weil es einfach zu viel Code fur so wenig Arbeit ist ? > > Der Code ist absolut minimal [...] Mir kommt es so vor, als müsste man die Auffassung von Marc und die von joop13 auf ein Mißverständnis zurückführen. Der Code von Peter ist in der Lage mehrere Tasten gleichzeitig zu entprellen. Und zwar in dem Sinne "gleichzeitig", dass mehrere Tasten auch tatsächlich zum selben Zeitpunkt betätigt werden können. Wenn der TO das ohnehin möchte, dann ist der Code dafür geeignet. Wieso sollte er "zu" aufwendig sein? Zu aufwendig, wofür? Noch mehr: Wenn man sich mal den Artikel über Entprellung hier durchliest, dann wäre eine "naive" Implementierung eine Schleife über die möglichen Portpins und dann das hochzählen des dem jeweiligen ihm zugeordneten Zählers. Das kann man generalisieren, in dem man die Zähler im Speicher hintereinander anordnet und aus der Bitnummer des Tasters den Offset berechnet. Aber schaut man sich mal Peters Code an, so ist seines das effektivere Verfahren, weil es die Auswahl der hochzuzählenden Zähler (es müssen ja nicht alle Tasten betätigt worden sein) in einem einzigen Ausdruck ohne explizite Bedingung (die Bedingungsprüfung ergibt sich aus der Bitoperation) erledigt. Sie ist zweifellos nicht auf Anhieb zu verstehen, aber wesentlich effizienter, und wenn man sie mal verstanden hat, auch von einfacherer Programmstruktur. Der Code ist also nicht unnötig umfangreich, sondern er macht genau das was er soll. Nicht einen Deut mehr. Er tut es mit wenig Code und effizient. Zugegeben, manche Details sind etwas tricky, aber wenn man mal verstanden hat, wie es geht (und Peter hat bisher viele Fragen zu Details beantwortet), dann ist es sogar interessant. Es lohnt sich wirklich den Artikel zu lesen. Ergänzend auch den Thread in Projekte&Code von Peter, der, wenn ich mich recht erinnere (das ist nun schon über 10 Jahre her, glaube ich), recht gut erklärt was da wie funktioniert und warum. Ich bin nur gerade zu faul zum suchen. Sorry. Peter: Hoffe es ist Dir nicht allzu unangenehm, wenn ich sozusagen für Dich das Wort ergreife.
Peter D. schrieb: > Der Code ist absolut minimal... In deinem Ursprungsprogramm ja, aber nicht unbedingt im verlinktem Beitrag. Für einen Anfänger (und der TO ist einer, wie er selber sagt) ziemlich ungeeignet. Ich habe es schon mehrmals im Forum empfohlen: Wenn jemand was funktionierendes haben will, soll er deine Routine nehmen, aber nicht um die Funktionsweise zu verstehen, sondern um damit zu arbeiten. Oder, andersrum: Wenn jemand eine Library benutzen will, dann braucht er auch nicht zu verstehen wie das alles im einzelnen funktioniert. Wenn aber jemand verstehen will wie etwas funktioniert (und der TO will das), dann ist lesen von Librarys der denkbar schlechteste weg, weil der Code meistens so optimiert ist, dass ein Anfänger sehr wenig davon verstehen wird. Deswegen habe ich versucht, dem TO einen einfachen Weg zu zeigen, wie er das alles alleine schaffen (zumindest probieren) kann. Peter D. schrieb: > Wer aber schon einige Tage programmiert, wird wissen, daß Kommentare und > Defines das Verstehen ganz enorm erleichtern. Und besonders, wenn man Stimme ich absolut zu, bei mir geht das manchmal bis 60:40. Hosenmatz schrieb: > Peter: Hoffe es ist Dir nicht allzu unangenehm, wenn ich sozusagen für > Dich das Wort ergreife. Absolut unnötig, er kann selber antworten falls er das für nötig hält.
:
Bearbeitet durch User
Marc V. schrieb: > Immer wenn irgendein Taster als gedrückt erkannt wird, wird sein Zähler > um eins inkrementiert. Wenn Zähler >= 8 (40ms), wird der Taster als > gedrückt geflagt. > Wird dagegen Taster als nicht gedrückt erkannt, geht der zugehörige > Zähler sofort auf Null, Tasterflags auch, d.h. man setzt die > zugehörige Variable auf Null. Das Vorgehen bei dieser Entprellung habe ich nun verstanden, ich komm nur noch nicht mit mehreren Tastern klar. Ich will das ganze mit 4 Tastern umsetzen. So wie ich das verstanden habe, schreibst du alles in eine deine "Informationen" in eine einzige Variable. z.B wenn Taster 2 High ist bekommt dein integer an der Stelle 8(beispiel) eine 1. Aber wie willst du genau auf diese Stellen zugreifen? Kannst du das bitte nochmal genauer erläutern, wie du dies bei 4 Tastern machen würdest?
joop13 schrieb: > Kannst du das bitte nochmal genauer erläutern, wie du dies bei 4 Tastern > machen würdest? Hier hab ich versucht, etwas ähnliches zu erklären: Beitrag "Re: [AVR] Zwischen einzelnen PCINT-Quellen unterscheiden" Die untere Routine kann auch in deiner Timerroutine stehen. Es wird angenommen, dass deine Taster an PinB.0-PinB.3 sind, mit Pull-up an Vcc und beim draufdrücken auf Log.0 gehen. Auch wieder nicht geprüft, da ich C-Compiler nicht dabei habe. Und auch nicht mag... ;)
1 | ActState = PINB & 0x0F; // Anstatt 0x0F kann auch andere Pinmask stehen |
2 | |
3 | //*** PinB.0 ueberpruefen
|
4 | if (ActState & Pin_0) { |
5 | Pin0_state = 0; //* Taster losgelassen oder prellt, alles auf Null |
6 | }
|
7 | else { |
8 | if (Pin0_state & 0x8000) goto chk1; //* steht schon auf Tastendruck lang, nichts zu tun |
9 | if ((Pin0_state & 0x1FF) >= 0x177) { //* laenger als 1.5s ? |
10 | Pin0_state |= 0x8000; //* setze bit fur Tastendruck lang |
11 | } else { |
12 | Pin0_state++; |
13 | if ((Pin0_state & 0x1FF) == 8) Pin0_state |= 0x4000; //* setze bit fur Tastendruck normal |
14 | }
|
15 | }
|
16 | |
17 | chk1: |
18 | //*** PinB.1 ueberpruefen
|
19 | if (ActState & Pin_1) { |
20 | Pin1_state = 0; //* Taster losgelassen, alles auf Null |
21 | ...
|
22 | }
|
Da du deine Taster nicht mit INT-Routinen sondern in der Timer-Routine abfragst, werden immer alle vier Taster nacheinander geprüft, hat aber den Vorteil, dass auch mehrere Tasten gleichzeitig gedrückt werden können und du weisst sogar immer in welcher Reihenfolge das war. joop13 schrieb: > bekommt dein integer an der Stelle 8(beispiel) eine 1. Aber wie willst > du genau auf diese Stellen zugreifen? Falls du meinst, wie ich in main() darauf zugreife, dann mit einer solchen Abfrage (maskierte Abfrage):
1 | if (Pin0_state & 0x8000) { |
2 | // Tastendruck lang, tu etwas
|
3 | } else if (Pin0_state & 0x4000) { |
4 | // Tastendruck normal, tu etwas
|
5 | }
|
oder so (bit Abfrage):
1 | if (Pin1_state & (1<<LongPress)) { |
2 | // Tastendruck lang, tu etwas
|
3 | } else if (Pin1_state & (1<<ShortPress)) { |
4 | // Tastendruck normal, tu etwas
|
5 | }
|
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.