Datum: 20.02.2003 14:34
Auf besonderen Wunsch eines einzelnen habe ich die Routine erweitert, um auch die labrigsten Tasten der Welt entprellen zu können. Diese Version ist also wirklich kugelsicher. Es werden jetzt 4 gleiche Zustände hintereinander benötigt, um als gültiges Drücken oder Loslassen erkannt zu werden. Dadurch hat sich die Ausführungszeit von 10 auf 12 Zyklen verlängert. Außerdem habe ich den Vorteiler verkürzt, da sonst durch das nun 4-malige Abtasten eine deutliche Verzögerung sichtbar wäre. Die Funktionsweise ist folgende: Die Bits der Register CT0, CT1 bilden für jede Taste einen Zähler 0..3. Ist der Tastenzustand mit dem entprellten Zustand identisch, wird dieser 2-Bit Zähler permanent zurückgesetzt. Nur wenn also 4-mal hintereinander der entgegengesetzte Tastenpegel anliegt, kann dieser Zähler einmal rum zählen (= 4 Zustände 0..3) und wenn dann immer noch der entgegengesetzte Tastenpegel anliegt, wird er von dem entprellten Register (key_state) übernommen. Da die Tasten low-activ sind, bedeutet dort eine 1 = Taste gedrückt. Ich habs noch nicht getestet. Das Prinzip sollte das oben beschriebene sein. Falls sich wider Erwarten doch ein Fehler eingeschlichen haben sollte, sind sachliche Kommentare herzlich willkommen. Peter
Datum: 20.02.2003 14:38
P.S.: Beschimpfungen statt sachlichen Kommentaren bitte per E-Mail direkt an mich richten, anstatt damit das Forum zu verunstalten. Peter
Datum: 20.02.2003 16:38
Ups, da war ich wohl etwas zu hektisch. Hier nochmal ohne Schreibfehler. Peter
Datum: 24.02.2003 17:07
Und hier nochmal für die C-Freaks (Keil C51). Peter
Datum: 05.03.2003 16:42
Hier findet man die nötige Theorie dazu: http://www.ece.umd.edu/~msquared/rtas2000.pdf Peter
Datum: 17.07.2003 13:23
und das ganze, wenn ich nur eine taste habe, die einen interrupt auslöst?
Datum: 17.07.2003 13:33
Ja, für eine Taste sind 4 Register etwas viel Speicherverbrauch. Hier im Zip-File: http://www.mikrocontroller.net/forum/read-4-32158.html findest Du die Routine "ovf0int.inc", die nur eine Taste abfragt und dafür auch nur 4 Bits eines Registers benötigt. Das Prinzip ist aber dasselbe. Peter
Datum: 18.07.2003 09:36
@Martin, "die eintastenroutine ist ok, aber ich meine natürlich wieder einmal was anderes: die taste zu entprellen, die den interrupt auslöst!" Wie Du bestimmt auch in den anderen Entprellroutinen gesehen hast, muß man mindestens noch ein weiteres mal den Eingang nach einer Wartezeit abtesten. Warten in einem Interrupt ist jedoch tödlich, da solange ja auch alle anderen Programmroutinen hängen bleiben. Du must also einen Timerinterrupt verwenden, um die Wartezeit zu realisieren. Und daher ist es ziemlich witzlos, noch zusätzlich einen externen Interrupt zur Tastenabfrage zu verwenden. Das kostet Dich nur unnötig Code. So ein Timerinterrupt ist auch ja sehr nützlich, um sämtliche anderen Zeitabläufe zu machen. Kein einziges meiner Programme kommt ohne den Timerinterrupt aus. Peter
Datum: 12.11.2003 22:52
Habe soeben versucht, das Programm Get8key4.c51 (siehe Beitrag weiter oben) nachzuvollziehen. Die Idee, die Zählbits auf verschiedene Bytes zu verteilen und damit eine sehr kompakte und schnelle Tastenentprellung zu realisieren ist wirklich Klasse. Würde mich freuen, wenn jemand bereit ist etwas Hirnschmalz zu investieren um meinen Überlegungen zu folgen. Vielleicht kapier' ich das Programm aber auch einfach nicht. ;-) i = key_state ^ ~KEY_INPUT; // key changed ? Das entsprechende Bit in i ist 0 bei Änderung (Wenn key_state <> KEY_INPUT) ct0 = ~( ct0 & i ); // reset or count ct0 ? toggeln der Bits in ct0 ist Teil des Zählvorgangs, der in ct0 und ct1 abläuft. Das geschieht hier, wenn das entsprechende Bit in i == 1 ist, also solange keine Änderung vorliegt. Ist i(n) = 0 (bei Änderung) wird ct0(n) bei jedem Aufruf wieder auf 1 gesetzt. Laut Beschreibung im ersten Beitrag sollte der Zähler aber permanent zurüchgesetzt werden solange key_state == KEY_INPUT. ct1 = ct0 ^ ct1 & i; // reset or count ct1 ? Wie oben, bei i(n) = 0 (also bei Änderung, wenn key_state <> KEY_INPUT) wird der Zähler zurückgesetzt. ? Beim Zählen würde der Zustand ct1(t) tatsächlich aus ct0(t-1) und ct1(t-1) durch EXOR-Verknüpfung gebildet. Der Befehl oben verwendet aber bereits ct0(t) anstelle ct0(t-1). Damit das wieder passt müsste der Term (ct0 ^ct1) invertiert werden. i &= ct0 & ct1; // count until roll over ? Auch diese Zeile ist mir nicht klar geworden. Im Simulator macht der Programmcode aus Get8key4.c51 nicht was er soll. Hier ist meine Version: i = key_state ^ KEY_INPUT; ct0 = ~ct0 & i; ct1 = ~( ct1 ^ ct0 ) & i; i = ~ct1 & ~ct0 & i; key_state ^= i; Habe den Programmcode durch den Simulator laufen lassen: -> geht! Andi
Datum: 13.11.2003 09:03
@Andi, die Signale CT0 und CT1 sind invertiert. Damit spart man etwas Code ein (in Deinem Code sind 3 Invertierungen mehr). Ansonsten funktiert es gleich. Das ich ~KEY_INPUT nehme, liegt daran, daß man üblicher Weise die Tasten gegen GND schaltet und somit den internen Pull-Up-Widerstand nehmen kann. Man spart also einen Widerstand ein. Also: Taste gedrückt -> KEY_INPUT = 0 -> key_state = 1 In meinem Scheduler Beispiel in der Codesammlung ist das auch auf dem AVR verwendet worden (WINAVR). Peter
Datum: 13.11.2003 09:05
@Andi, aber super von Dir, daß du es nicht nur stumpf abkopiert hast, sondern auch versucht hast, es nachzuvollziehen und das erfolgreich. Peter
Datum: 14.11.2003 14:25
@Peter, Danke für die Hinweise. Habe inzwischen auch herausgefunden warum Dein Programm bei mir nicht richtig simulierte. I. Mein Compiler wollte bei ct1 = ct0 ^ ct1 & i; Klammern haben, die hatte ich falsch gesetzt. Jetzt gehts. Es compiliert auch tatsächlich um zwei Befehle kürzer als meine Version. II. Werden mit static deklarierte lokale Variablen (ct0 und ct1) standardmässig vom Compiler initialisiert? Zumindest der Simulator machts. Dann übernimmt Dein Programm KEY_INPUT bereits beim ersten Durchlauf nach key_state. Meine Variante benötigt zunächst 4 Aufrufe und entprellt so auch unmittelbar nach Programmstart anliegende Schalterzustände. Macht in der Praxis wohl keinen Unterschied. Hat mich aber bei der Simulation zunächst verwirrt. Andi
Datum: 05.04.2004 12:54
ups und das ganze nun nochmal für richtige DAUs :) Ich hab hier nen AT90S8535 in Betrieb und am PortD 8 Taster drann Ich programmiere mit dem CodeVision AVR Compiler und komme mit dem Code selbst nicht zurecht. Ich weiß ja noch ncihtmal wie ich solche Interupts verwende ob nun externe oder interne :( Ich wäre dankbar für jede hilfe
Datum: 05.04.2004 14:43
@Robert, ein komplettes C-Beispiel findest Du hier: http://www.mikrocontroller.net/forum/read-4-49709.html läuft zumindest unter WINAVR (AVR-GCC). Allerdings solltest Du schon mal einen Blick in das Datenblatt werfen, welche Bits man setzen muß, um einen bestimmten Interrupt freizugeben, bzw. einen bestimmten Timermodus zu setzen. Auch kann es sein, daß unter CV Interrupts anders deklariert werden müssen, als unter WINAVR. Peter
Datum: 12.06.2004 16:59
@Peter, was ich noch nicht ganz kapiert habe: Wie oft muß der Schalter gleichen Zustand haben, dass es als solcher erkannt wird? Ist es richtig, daß es nach dem dritten mal soweit ist? Armin
Datum: 12.06.2004 17:16
4 mal. Kannst ja mal den Interrupt auf 1s runtersetzen und dann mitzählen. Peter
Datum: 12.06.2004 20:18
Hi ich habe mal das C-File in Codevision importiert und durchsimuliert. In welcher Variablen steht jetzt eigentlich der entprellte Tasten-Zustand? In "key_press" oder in "key_state"? Ich habe das so herausgefunden, daß eben dieser entprellte Tastenzustand nach 4 Zyklen in "key_state" drinsteht. Das wirft aber die Frage auf, wozu die Variable "key_press" ist?
Datum: 13.06.2004 14:51
Oftmals will man ja etwas machen, wenn eine Taste gedrückt wird und nicht, solange eine Taste gedrückt ist. Und "key_press" reagiert eben genau auf den Vorgang des Drückens, d.h. den Wechsel von losgelassen zu gedrückt. Peter
Datum: 19.10.2004 11:40
Hallo, ich denke ich habe Peters C-Code soweit verstanden. Nochmal zusammengefasst in i steht für Tasten die eine Veränderung seit dem letzten Aufruf erfahren haben eine "1" und für Tasten die ihren Status behalten haben eine "0". Alles darauf bezogen die Entprellung ist beendet. Möchte ich jetzt eine Erkennung für gerade gelöste Tasten hinzufügen müsste es doch wie folgt gehen: i = key_state ^ (~console); // key changed? ct0 = ~(ct0 & i); // reset or count ct0 ct1 = ct0 ^ ct1 & i; // reset or count ct1 i &= ct0 & ct1; // count until roll over? // **** Release Erkennung key_release |= key_state & i; // 1->0: key release detect // **** key_state ^= i; // then toggle debounced state key_press |= key_state & i; // 0->1: key press detect Zur Erklärung, in key_state stehen zu diesem Zeitpunkt der Zuweisung noch die "gedrückten Tasten" vom Aufruf davor. In i die Tasten deren Zustände sich geändert haben. Wenn also der Zustand einer Taste zuvor 1 war und sie sich geändert hat müsste man dies mittels und-Verknüpfung "mitbekommen". Somit müsste key_state AND i die gelösten Tasten ergeben. Sehe ich das richtig ? Grüße Dirk
Datum: 19.10.2004 15:22
faszinierend, wie derart kleine stückchen code so viel interesse hervorrufen; ich wünschte mir, ich könnte auch so kompakt und einfallsreich programmieren... übrigens, die c-version dieses entprell-codes sorgt seit ca. 4 monaten täglich in meinem auto für die entprellung von vier (alten und schlechten) tastern; bis jetzt hat die entprellung immer einwandfrei funktioniert! somit wäre an dieser stelle ein kompliment und dank an peter angebracht!
Datum: 11.05.2005 12:39
Kann es sein, dass nach dem Reset, nach 4 Timer-zyklen key_press einmal auf 255 geht ? Ich hab hier nämlich das phänomen...
Datum: 11.05.2005 13:19
> faszinierend, wie derart kleine stückchen code so viel interesse > hervorrufen; ich wünschte mir, ich könnte auch so kompakt und > einfallsreich programmieren... Das kommt schon, Peter kann es auch nicht von Geburt an.
Datum: 11.05.2005 13:21
>> Das wirft aber die Frage auf, wozu die Variable "key_press" ist?
Das ist wie in Hardware, du kannst direkt auf einen bestimmten Pegel am
Eingang reagieren oder du kannst Flanken-getriggert arbeiten also nur
bei Pegelwechsel eine Aktion ausführen. Flankengetriggert bedeutet
immer Taktbasierend. Wenn der Flankenwechsel so wei bei Peters
Entprellung sauber arbeitet ist eine Flankengetriggerte Auswertung von
Tasten immer vorzuziehen, das ist viel störunanfäliger als das reine
Polling. Denn der Informations-Gehalt einer Flankensteuerung enthält
zum Zeitpunkt des Auslösen ZWEI Informationen statt nur EINER. Nämlich
"jetzt hat sich der Pegel von 0 auf 1 geändert" statt "der Pegel ist
jetzt 1". Ein klemmender Schalter kann so eben nicht permanent schalten
sondern nur einmalig bevor er klemmt.
Gruß Hagen
Datum: 12.05.2005 21:14
Hallo,
ich bin gerade am Bau eines Belichtungstimers mit Atmel 90S2313 mit 4
7-Segmentanzeigen und 3 Taster als Eingang.
Die Taster sind Kurzhubtaster Reichelt 3301D, und ich habe die mit
folgendem Programm Code ohne Probleme in Betrieb.
//--------------------------------
if((PIND.6==0) && (S3==0)) // Start/Stop
{
S3=1;
if(TCCR0==0)
{
TCNT0=0; // Timer löschen
TCCR0=5; // Timer on 4mHz/1024 = 3,906 kHz
PORTB |= 0x80; // Relais PB7 = 1
}
else
{
TCCR0 = 0; // Timer off
PORTB &= ~0x80; // Relais PB7 = 0
}
}
if(PIND.6==1)
S3=0;
P.S ist mit CodeVision Programmiert
Datum: 12.05.2005 21:21
...wie war das denn jetzt mit Key_press ? Ist das Normal, dass es nach dem Reset, nach dem 4.Zyklus einmal auf 255 geht ? Wenn ja, kann man das vermeiden ?
Datum: 12.05.2005 21:36
"Ist das Normal, dass es nach dem Reset, nach dem 4.Zyklus einmal auf 255 geht ?" Nein. Hast Du die Tasten auch gegen GND geschaltet und die internen Pull-Ups an oder externe Pull-ups gegen VCC ? Peter
Datum: 12.05.2005 21:47
Da ich nur zwei Pins gebrauchen kann, hab ich diese am Anfang Deiner
Routine Maskiert, daher wäre eigentlich schon 255 ausgeschlossen.
Testen tue ich auf dem STK 500.
Ist mir auch erst aufgefallen, nachdem ich das key_press in ein
weiteres Register kopiert habe.
in save_sreg,SREG ;Statusregister sichern
in iwr0,key_port ;Port D einlesen
andi iwr0,0b00001100 ;nur Pin 2 und 3 sind interessant
com iwr0
eor iwr0,key_state ;
and key_ct0,iwr0 ;
and key_ct1,iwr0 ;
com key_ct0 ;count
eor key_ct1,key_ct0 ;
and iwr0, key_ct0 ;input AND last counterstate
and iwr0, key_ct1 ;
eor key_state,iwr0 ;toggle state
and iwr0,key_state ;0-1-transition
or key_press,iwr0 ;store key_press detect
or Status,key_press;Tastenergebnis auch in Status speichern
In der Mainloop lösche ich im Grunde nur noch key_press
Datum: 12.05.2005 21:48
@Buffy "... 3 Taster als Eingang." Wie soll das gehen, ich sehe da nur PIND.6 als Eingang ??? Neben dem Vorteil gleichzeitig 8 Tasten zu entprellen, zeichnet sich mein Code doch gerade dadurch aus, daß der Timer durchlaufend ist und somit z.B. als Systemuhr mit verwendet werden kann. Als weiterer Vorteil werden die Tastendrücke gemerkt und können später ausgewertet werden (Ich hasse es bei vielen kommerziellen Geräten, daß man immer so elendig lange warten muß, bis man endlich die nächste Taste drücken darf). Daß daneben noch viele andere Code laufen, die eben nicht diese Vorteile aufweisen, glaube ich Dir gern. Peter
Datum: 12.05.2005 22:27
@Carsten: Key_press kopiert man nicht. Man maskiert es auch nur, wenn während eines bestimmten Programmstatus einige Tasten gesperrt werden müssen, was im Normalfall nicht der Fall ist. Für key_press nutzt man ein oberes Register, um bitweisen Zugriff zu erhalten. Die einzelnen Bits in key_press werden von der Entprellroutine im Timer-Int gesetzt wenn eine Taste (erneut) betätigt wurde und vom Hauptprogramm gelöscht, wenn der zugehörige Job erledigt wird. Will man in der Mainloop auf Tastendrücke reagieren, fragt man die jeweilige Taste einzeln ab: sbrc key_press, tastenbit1 ;überspringe, wenn Bit gelöscht rjmp/rcall tast1 ;zur Abarbeitung, da Bit gesetzt ist sbrc key_press, tastenbit2 ;überspringe, wenn Bit gelöscht rjmp/rcall tast2 ... tast1: cbr key_press,1<<tastenbit1 ;Tastenbit1 löschen, Job wird erledigt, ... ;Job erledigen, den die Taste ausgelöst hat... rjmp mainloop / ret ;fertig... tast2: genauso... ... War ein Bit gesetzt, wird die zugehörige Routine aufgerufen (die den Job erledigt, den ein Tastendruck bewirken soll). Diese löscht erstmal das eine Bit in kex_press, denn der zugehörige Job ist ja nun in Arbeit, und er soll ja nur einmal ausgeführt werden. Dann wird der Job erledigt und die Routine verlassen. Ob man die Routine per rcall/ret aufruft oder per rjmp/rjmp, hängt von anderen Faktoren ab (z.B. Hirarchie/Wichtigkeit der einzelnen Jobs). Das Löschen aller Bits in key_press ist nur dann sinnvoll, wenn ein Taster/Schalter so hohe Priorität hat, dass alle anderen Tastendrücke annulliert werden müssen. Ansonsten löscht jede Tastenjobroutine nur ihr eigenes Bit in key_press. ...
Datum: 13.05.2005 01:36
>>Das Löschen aller Bits in key_press ist nur dann sinnvoll, wenn ein >>Taster/Schalter so hohe Priorität hat, dass alle anderen >Tastendrückeannulliert werden müssen oder, wenn man bulletproof die hektischen und unsinnigen Eingaben des Benutzers löschen möchte, weil der höchst priorisierte Job die anderen Jobs annuliert. Zb. in Key_Press stehen die Tasten für Lautstärke +, Kanalwahl 12 und Fernseher Off drinnen. Fernseher Off ist am höchsten priorisiert und löscht demzufolge die anderen tasten ebenfalls, da diese dann keinen Sinn mehr machen. Daran erkennt man: bei einen guten Code weis der Initial-Programmierer selber nicht sofort was man so alles mit dem Code in Zukunft noch anstellen kann :-) Gruß Hagen
Datum: 13.05.2005 08:24
Nundenn... Key Press wird nach der Abarbeitung gelöscht. Genauso tat ich und es war falsch, weil ich viel zu viel in der main-loop gemacht hab. Da meine Jobs aber nun mal Zeit in Anspruch nehmen (siehe Threat Timing Problem) musste ich mir Flags setzen. >Man maskiert es auch nur, wenn während eines bestimmten >Programmstatus >einige Tasten gesperrt werden müssen, was im Normalfall nicht der >Fall ist. Da bin ich ja mal gespannt, wie die Routine mein UART entprellt und meine LED's. Was spricht dagegen, den Port vor der Routine zu maskieren ??? Ich brauche nun mal nur zwei Eingänge. Das ganze ist M.E. auch niemals nicht der Grund, dass die Routine bei mir Zickt. Fakt ist, wenn immer nur 0b00001100 als maximal gelesene Eingänge kommen dürfen unten nicht 0b11111111 raus kommen oder ? Das key_press nach der Abarbeitng (hier dem kopieren) gelöscht wird, ist genau so richtig. In Peter's Routine ist es auch nicht anders. Wie gesagt, das letzte mal hab ich Mecker bekommen, weil ich den Interrupt so lange unterdrückt habe, bis alles erledigt war. Jetzt hab ich die M.E. einzige konsequenz daraus gezogen und wieder falsch :( ...
Datum: 13.05.2005 09:42
Was heißt nun "falsch"? Was in der einen Situation falsch erscheint, ist in der anderen Situation richtig. Wie Hagen richtig angemerkt hat, hat man bei diesem Profi-Code alle Optionen offen. Im Prinzip meinte ich dasselbe, habe es aber wohl nicht präziese genug formuliert. Nochmals meinen Dank an Peter für diesen Code. Ich habe meinen Beitrag eigentlich nur geschrieben, weil ich den Eindruck hatte, dass du (Carsten) noch Verständnisprobleme mit der Routine bzw. dem Umgang damit hast. Du brauchst nur 2 Bits weil du nur 2 Tasten dran hast. Also brauchst du auch nur 2 Bits in key_press auszuwerten. Machst du das im Ganzen (key_press auf 0 prüfen), dann musst du es natürlich vorher maskieren. Machst du es aber bitweise (so wie ich es beschrieb), dann soll es dich doch nicht interessieren, ob USART und die anderen Portpins die unbenutzten Bits in key_press setzen oder nicht, denn du fragst sie ja nie wieder ab. ...
Datum: 13.05.2005 09:45
Ok, da gebe ich Dir Recht. Generell sollte aber das Maskieren keine probleme für die Routine bedeuten oder ?
Datum: 13.05.2005 10:57
@Carsten: Abstrahiere doch mal... Die ISR setzt die Bits in key_press, wenn eine Taste neu gerückt wurde (Flanke). Damit wird sozusagen ein "Job" angemeldet, den das Hauptprogramm irgendwann ausführen muss. Wie du die "angemeldeten Jobs" abarbeitest und die zugehörigen "Jobflags" (also die Bits in key_press) löscht ist von Fall zu Fall unterschiedlich. Es hat (eigentlich) keine Auswirkungen auf die ISR-Routine. Bei 2 Tasten würde ich sie einzeln abfragen (sbrc) und einzeln löschen (cbr). ...
Datum: 13.05.2005 11:24
Es macht aber doch keinen Unterschied, ob ich ein eigenes Statusregister verwende oder key_press. Im Statusregister habe ich nebenbei noch andere Flags. Das Statusregister ist für mich auch ein Speicher, der mir alle 30min einen neuen Job aufdrückt, unabhängig vom Tastenzustand (Selbsthaltung). Das ist notwendig um der Benachrichtigten Person so lange Messages zu schicken, bis sie reagiert und den Reset-Knopf gedrückt hat. Anders herum wirft diese Diskussion die Frage auf, ob es sinnvoll wäre, eine Alles OK Meldung zu schicken, wenn der betreffende key-press eingang wieder weg ist. Das geht aber mit der Entprellung nicht, da die fallende Flanke nicht ausgewertet wird :( Stellt sich die Frage, wie man das einigermaßen sicher hin bekommt... ...Ein Eingang (entprellt) der ein Signal auslöst wie bei key_press. Bleibt der Eingang High, tut sich nix. Wird der Eingang nach einer gewissen Zeitspanne Low (entprellt) kommt wieder ein Signal. Aber jetzt dämmert's... Was, wenn key_press nicht gelöscht wird? Dann bleibt das auch bis zum nächsten Reset an. <<...denk...denk...>> Dann brauche ich den Speicher garnicht.
Datum: 13.05.2005 13:03
Vergiss nicht, dass dir neben key_press (den Flag, die dir sagen, dass die Tasten erneut gedrückt wurden, also inzwischen mal losgelassen waren) auch key_state zur Verfügung steht, mit dem du den aktuellen Tastenzustand auswerten kannst. Wer hindert dich daran, mit einer eigenen Routine den Flankenwechsel in key_state zu erkennen und darauf zu reagieren? Das Tastenflag in key_press stehen zu lassen halte ich für nicht so gut. Denn dann wird ja der "Job" bei der nächsten Mainloop-Runde erneut augeführt. OT: Mich stört es etwas, dass wir solche banalen Dinge in der Codesammlung diskutieren. ...
Datum: 13.05.2005 14:13
"Mich stört es etwas, dass wir solche banalen Dinge in der Codesammlung diskutieren." Ich denke mal, genau dazu ist die Codesammlung da. Es gehört natürlich etwas Disziplin dazu, die Threads bis zum Ende zu verfolgen, ob sich relevante Zusätze ergeben haben. Zum Thema Ausmaskieren: Das Ausmaskieren mit 0 bedeutet Taske gedrückt und somit ist der Zustand 255 zumindest einmalig möglich. Tritt er aber bei losgelassenen Tasten auf, dann könnte ein fehlender Pull-Up die Ursache sein oder die Register wurden nicht initialisiert. Du hast doch den Code aus dem 3. Posting genommen (das 1. ist buggy) ? Da aber die Bits völlig unabhängig voneinander arbeiten, ist ein Ausmaskieren schlichtweg unnötig, man testet einfach nicht die Bits, die kein Tasten bedeuten. Peter
Datum: 13.05.2005 14:43
Der Zustand tritt nur einmal nach dem Reset auf. Ich denke schon, dass ich das dritte Posting gnommen habe, werde ich aber nochmal prüfen. Danke erstmal
Datum: 13.05.2005 15:27
@Peter: Gut, dann brauche ich also kein schlechtes gewissen wegen Zumüllen der Codesammlung haben... ;-) ...HanneS...
Datum: 02.06.2005 15:07
Kennt ihr schon den? Einfach und genial.
// Get debounced key state, TRUE if key was pressed
BOOL AT_DebouncedKey (void)
{
static UINT16 state = 0;
state = (state<<1) | RAWKEYPRESS | 0xe000;
return (state == 0xf000);
}
Einfach periodisch aufrufen, RAWKEYPRESS ist das Portbit, 0 oder 1
Datum: 02.06.2005 15:53
@pittbull "Kennt ihr schon den? Einfach und genial." Ja kenn ich, der hat aber schon nen sooooo langen Bart: http://www.mikrocontroller.net/forum/read-4-179926.html#new Ist also weder einfach noch genial noch effizient. Peter
Datum: 02.06.2005 16:20
ach, den kanntet ihr schon :-( ich finde den jedenfalls nicht schlecht, ist hardwareunabhängig und muss nicht inner isr laufen. ist übrigens von da: http://www.ganssle.com/debouncing.pdf
Datum: 02.06.2005 16:52
Wenn ich das richtig sehe, ist er auch nicht störsicher während des gedrückt haltens. Ein einziges 1-Bit (Störimpuls) bewirkt den sofortigen Wechsel zu losgelassen. Meine Routine will aber mindestesn 4 Einsen hintereinander bis zum nicht gedrückt, entprellt also beim Drücken und beim Loslassen. "ist hardwareunabhängig und muss nicht inner isr laufen" Trifft auch voll auf meine Routine zu. Man kann sie auch im Main aufrufen und die rein zufällige Programmlaufzeit als Abtastintervall nutzen. Allerdings würde dadurch die Programmierung wesentlich umständlicher, man müßte dann nämlich nach jeder Programmänderung prüfen, ob die Laufzeit noch im optimalen Entprellinterval (2..50ms) liegt. Oder man macht es wie leider so viele: Einfach beide Augen zu und durch. Und dann eben den Kunden sich ärgern lassen, wenns doch mal prellt oder er ne Taste länger drücken muß, ehe sie reagiert. Sowas ist aber nicht mein Stil und genau deshalb nehme ich eben den Timerinterrupt. Peter
Datum: 02.06.2005 17:15
klar ist der code laufzeitabhängig. inner main loop muss man die konstanten anpassen wenn's nicht richtig funzt. um dem problem zu entgehen kann man kann ihn natürlich auch in eine timer-isr packen, geht aber auch ohne ;-) >> "ist hardwareunabhängig und muss nicht inner isr laufen" >> Trifft auch voll auf meine Routine zu. sicher nicht (wenn du diesen assemblercode neinst). der braucht schon hardwareunterstützung.
Datum: 03.06.2005 09:13
Datum: 06.06.2005 13:04
Hi, ein bißchen OT beim austesten der Routine: aber bei mir im Simulator (AVRStudio 4.11SP3) springt er bei reti wieder an die Adresse 0 und fährt damit nochmals die init routine ab. Entsprechend kann ich den Code-Schnipsel nicht nachvollziehen. Kann mir jemand auf die Sprünge helfen was da passiert? Aber zum Code: geschieht das zählen durch die xor und and Verknüpfungen? Wo kann man dies mal nachlesen? Viele Grüße Olaf
Datum: 06.06.2005 15:26
Ha, der Stackpointer wird ja benutzt - alles klar :-)
Datum: 22.08.2005 23:34
hallo hat schon jemand (ich wage es ja fast nicht zu fragen (schäm)) diese routine in basic (bascom) übersetzt bei mir klappt es leider noch nicht PeterL
Datum: 23.08.2005 01:16
Gibt es in BASCOM nicht bereits die DEBOUNCE-Anweisung? Ansonsten kann man doch wohl in BASCOM ASM einbinden, oder? ...
Datum: 10.09.2005 11:25
@Peter und alle anderen die es wissen könnten Ich versuche nun schon seit mehreren Tagen den Code aus dem 3. Post auf meinem STK500 Board mit einem 8515 zum Laufen zu bekommen... Leider ohne Erfolg. Das Prinzip des Codes ist mir (nach seitenweisem malen der Register auf Zettel gg) mittlerweile klar. Wäre dankbar für ein paar Hinweise an welchen Stellen ich anpassen muss um ihn mit meiner Hardware lauffähig machen zu können. Wenn ich mich nicht täusche dann müsste ich die Interrupts anders programmieren oder? Danke Christopher
Datum: 10.09.2005 12:25
@Christopher, der einzige Unterschied dürfte der Hardwarestack des 1200 sein, Du mußt also nur noch den Stackpointer setzen, dann sollte es laufen. Peter
Datum: 04.11.2005 21:56
Interessante Routine, das Prinzip ist aber nicht neu. Das Grundprinzip läßt sich übrigens vielseitig verwenden. Wer die Grundlagen wissen will: vertical counters: http://www.everything2.com/index.pl?node_id=1145892 http://www.dattalo.com/technical/software/pic/vertcnt.html Entprellroutine für PIC: http://www.dattalo.com/technical/software/pic/debounce.html Der eigentliche Entprellalgorithmus läßt sich übrigens auch recht einfach in Hardware implementieren: http://pdfserv.maxim-ic.com/en/ds/MAX6816-MAX6818.pdf (Figure 1, Seite 4) Evtl. interessant für Verwendung mit CPLDs/FPGAs.
Datum: 05.11.2005 11:50
@Marko "Evtl. interessant für Verwendung mit CPLDs/FPGAs." Du wirst lachen. Ich habe zuerst einen Schaltplan gezeichnet, dann so optimiert, daß möglichst wenig AND, OR, EXOR, NOT-Glieder vorkommen und erst dann habe ich den Schaltplan in Software übertragen. Daß sich sowas vertical counter nennt, habe ich dann mal im 8052-Forum gelesen. Eigentlich komisch, daß derart hocheffiziente Algorithmen so selten verwendet werden. Man sieht viel häufiger die Echtzeit vernichtende Warten im Interrupt Methode (grr). Peter
Datum: 06.11.2005 16:01
Hallo danke für deinen Code, er funktioniert sehr gut (mspgcc). Ich habe nur in der Zeile: key_press |= key_state & i; Das |= durch ein = ersetzt. Christian
Datum: 22.11.2005 23:10
Hi Allz, so ganz hab ich Peter Dannegger's grundsätzliche Vorgehensweise zur Tastenentprellung noch nicht verstanden. Vielleicht könnt ihr mir oder Peter mal auf die Sprünge helfen. 1.) Ich lasse keinen Taster mehr einen Interrupt auslösen (und warte dann einige Zeit um danach den Zustand des Tasters nochmal abzufragen). 2.) Ich definiere ein relativ kurzes Timerintervall (~10 ms). In der dazugehörigen Timer Overflow Ereignis hängt die Entprell Routine. 3.) (der Teil den ich überhaupt nicht raffe)Muss dieses Ereignis mindestens viermal eintreten (also die Routine viermal aufgerufen werden) um den vierfach entprellten Zustand festzustellen? 4.) Der gedrückte Taster wird z. B. in der Hauptschleife ständig abgefragt. In meinen Testroutinen funktioniert das ganze wunderbar. Selbst meine uralt Taster prellen überhaupt nicht. Würde mir das Simulieren eventuell helfen das ganze besser zu verstehen (hab ich bisher überhaupt noch nicht benutzt)? Wie integriere ich in sowas eine Auswertung in C wie lange ein Taster gedrückt wurde (vom Prinzip her - ich will jetzt keinen fertigen Code haben)? Fragen über Fragen... Markus
Datum: 22.11.2005 23:37
Simulation bringt auf alle Fälle was. Tastendruck-Dauer brauchte ich noch nicht. Müsste aber gehen, wenn man im Timer-Int nebenbei eine Variable (mit weiterem Vorteiler?) hochzählt, die alle Routinen als Zeit-Referenz für "langsame Dinge" nutzen können. Beim ersten Reagieren auf key_press nimmst du die momentane Zeit, dann fragst du zyklisch (in Mainloop) key_state ab und nimmst den zweiten Zeitstempel, wenn key_state zum ersten mal deaktiviert ist. Die Differenz der Zeitstempel ist dann die gewünschte Tastendruckzeit. Es gibt vermutlich aber noch elegantere Möglichkeiten... Der große Vorteil dieser Entprellung ist ja gerade das Bereitstellen von zwei Flags je Taste, eines für Zustand/Status (key_state) und eines für erneutes Drücken (key_press). Damit kann das Programm sehr flexibel darauf reagieren. ...
Datum: 24.11.2005 08:38
Hi, danke schon mal für deine Ausführungen. Hilft mir auf alle Fälle schon mal, weitere Fragen werden folgen ;-) . Markus
Datum: 24.11.2005 08:42
Um es noch mal zusammenzufassen: Wenn namhafte Hersteller diverser Fernbedienungen endlich mal Peters Algorithmus verwenden würden anstatt einfach zu pollen, dann könnte man sehr schnell bis gleichzeitig den Kanal wechseln und die Lautsträke verändern. Das nervt nämlich wirklich wenn man nach dem Kanalwechseln immer noch 1 Sekunde wartten muss bevor die nächste Eingabe erfolgen kann... :-) Hail to Peter Ich verwende auch seine Routine und bin immer wieder erstaunt über die flexible Lösung, welche er da gezeigt hat... RESPEKT
Datum: 29.12.2005 23:40
Hallo! Ich muss erstmal ganz groß Danke sagen! Die Entprell-Funktion aus der Get8key4.c51 funktioniert wunderbar. In der key_press Variable kann ich wunderbar auf den Tastendruck (0->1) reagieren. Nur hab ich jetzt natürlich wieder ein spezielles Problemchen: Ich müsste auch zusätzlich den Übergang (1->0) erkennen. Damit möchte ich unterscheiden, ob eine Taste nur kurz angetippt wurde (toggelt dann eine LED) oder länger gedrückt gehalten wird (LED soll dann solang leuchten). Der Code ist so herrlich kompakt, dass ich leider Probleme hab, ihn so genau zu verstehen, um diese Funktion hinzuzufügen. Vielleicht kann mir da jemand auf die Sprünge helfen? Viele Grüße, Daniel
Datum: 30.12.2005 10:31
mach mal aus dieser zeile; i = key_state ^ ~KEY_INPUT; einfach das: i = key_state ^ KEY_INPUT; und schon haste das umgekehrt
Datum: 31.12.2005 13:31
Hier mal ein Bespiel mit kurz/lang Erkennung: http://www.mikrocontroller.net/attachment.php/2524... Peter
Datum: 01.03.2006 11:04
Wunderbare Diskussion ! Ich habe aber noch eine weitere Frage, bis jetzt wird ja nur die "0->1: key pressing detect" in der Variable key_press gespeichert. Ich bräuchte aber auch noch die Info, wann die Taste wieder losgelassen worden ist. Hat das schon mal jemand in den obigen Code implementiert ? Gruß Ralph
Datum: 02.03.2006 14:18
Guck mal weiter oben, wenn Du für C suchst: http://www.mikrocontroller.net/forum/read-4-20549.... Die Funktion zur Erkennung läßt sich leicht von der 'press'-Erkennung herleiten.. es gibt aber auch zahlreiche Verweise zu kompletten Codes
Datum: 25.10.2006 09:05
Hallo Peter, ich habe mich mal eine Weile mit Deiner Routine zum Entprellen von Tasten beschäftigt. Größtenteils habe ich sie auch verstanden und nachvollzogen, sie ist nun also schon im Einsatz. Es würde mich allerdings interessieren, ob es dazu auch eine Variante gibt, die all diese Funktionen auch noch bei der Entprellung einer Tastenmatrix (ich suche beispielsweise eine 4 x 8 Matrix Entprellung) ermöglicht. Und da würde ich halt auch gern auf die interrupt-gesteuerte Lösung zurück greifen. Ich dachte ich frage mal nach, bevor ich mir einen Knoten in den Kopf denke ... Man muss das Rad ja nicht immer neu erfinden, und außerdem kann man ja auch mal versuchen von den Fehlern der Anderen zu lernen. Viele Grüße Michael P.s.: Super Homepage und super Forum so allgemein! Und vielen Dank für den Entprellcode. Gutes Werk! P.p.s.: In der Zeile TCCR0 = (1<<CS02 | 1<<CS01 | 1<<CS00); // divide by 1024 habe ich noch CS01 setzen müssen, damit mein ATMega64 auch den 1024er Teiler nutzt. Wird wohl an unterschiedlichen MCs liegen. Ach noch etwas: Zur Erkennung ob eine Taste wieder losgelassen wurde, habe ich die ISR Routine (ehemals Signal) in größeren Teilen kopiert, also mit abgewandelten Variablennamen. Oder kann man die entprellte abfrage auch gleich aus dem Code entnehmen? Und eigentlich müsste man doch durch nutzen von ISR auf cli(); und sei(); verzichten können, oder? Was ja anbei noch gut wäre, da man nicht ausversehen ein nicht gesetztes Interrupt Hauptbit setzt, obwohl es vorher ungesetzt war.
Datum: 23.02.2007 10:14
Hallo Peter, ich nutze deine Routine mit Lang/kurz-erkennung recht gern und viel, muss aber zugeben, dass ich noch nicht ganz durchgestiegen bin ;-) Bisher hat das aber nie zu Problemen geführt. Jetzt hab ich aber mal eine Anwendung wo ich die Taster an PortG vom Mega64 angeschlossen habe. 5 Taster an PG.0-PG.4 Die Funktionen get_key_press und get_key_short funktionieren bei allen Tasten auch einwandfrei, aber get_key_long und get_key_rpt funktionieren nur an PG.1 und PG.2. Ich hab schon alles aus dem Programm rausgenommen was irgenwie stören könnte, aber ich komm einfach nicht dahinter... ist das vieleicht Prinzipbedingt, da der PortG ja nur 5 Bits hat? Wenn ja, warum gehn dann grad bit 1 und 2? Hoffe auf Erleuchtung. Gruß Fabian


