www.mikrocontroller.net

Forum: Codesammlung Tasten entprellen - Bulletproof

Autor: peter dannegger (Gast)
Datum: 20.02.2003 14:34
Dateianhang: Get8keyb.asm (1,5 KB, 1830 Downloads) | formatierter Code

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
Autor: peter dannegger (Gast)
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
Autor: peter dannegger (Gast)
Datum: 20.02.2003 16:38
Dateianhang: Get8key4.asm (1,6 KB, 1526 Downloads) | formatierter Code

Ups, da war ich wohl etwas zu hektisch.

Hier nochmal ohne Schreibfehler.


Peter
Autor: peter dannegger (Gast)
Datum: 24.02.2003 17:07
Dateianhang: Get8key4.c51 (1,1 KB, 2583 Downloads) | formatierter Code

Und hier nochmal für die C-Freaks (Keil C51).


Peter
Autor: peter dannegger (Gast)
Datum: 05.03.2003 16:42

Hier findet man die nötige Theorie dazu:

http://www.ece.umd.edu/~msquared/rtas2000.pdf


Peter
Autor: mbj (Gast)
Datum: 17.07.2003 13:23

und das ganze, wenn ich nur eine taste habe, die einen interrupt
auslöst?
Autor: peter dannegger (Gast)
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
Autor: peter dannegger (Gast)
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
Autor: Andi (Gast)
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
Autor: peter dannegger (Gast)
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
Autor: peter dannegger (Gast)
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
Autor: Andi (Gast)
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
Autor: Robert Nägele (Gast)
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
Autor: peter dannegger (Gast)
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
Autor: Armin Kniesel (Gast)
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
Autor: peter dannegger (Gast)
Datum: 12.06.2004 17:16

4 mal.

Kannst ja mal den Interrupt auf 1s runtersetzen und dann mitzählen.


Peter
Autor: Armin Kniesel (Gast)
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?
Autor: peter dannegger (Gast)
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
Autor: Dirk E. (Gast)
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
Autor: emil (Gast)
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!
Autor: Carsten (Gast)
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...
Autor: Thorsten (Gast)
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.
Autor: Hagen (Gast)
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
Autor: buffy (Gast)
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
Autor: Carsten (Gast)
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 ?
Autor: Peter Dannegger (Gast)
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
Autor: Carsten (Gast)
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
Autor: Peter Dannegger (Gast)
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
Autor: ...HanneS... (Gast)
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.

...
Autor: Hagen (Gast)
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
Autor: Carsten (Gast)
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 :(
...
Autor: ...HanneS... (Gast)
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.

...
Autor: Carsten (Gast)
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 ?
Autor: ...HanneS... (Gast)
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).

...
Autor: Carsten (Gast)
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.
Autor: ...HanneS... (Gast)
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.

...
Autor: Peter Dannegger (Gast)
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
Autor: Carsten (Gast)
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
Autor: ...HanneS... (Gast)
Datum: 13.05.2005 15:27

@Peter:

Gut, dann brauche ich also kein schlechtes gewissen wegen Zumüllen der
Codesammlung haben... ;-)

...HanneS...
Autor: pittbull (Gast)
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
Autor: Peter Dannegger (Gast)
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
Autor: pittbull (Gast)
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
Autor: Peter Dannegger (Gast)
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
Autor: pittbull (Gast)
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.
Autor: Peter Dannegger (Gast)
Datum: 03.06.2005 09:13

@pittbull

"sicher nicht (wenn du diesen assemblercode neinst"

Nein, ich meinte den C-Code darunter (4.Posting), hier mit 8051, im
Shedulerbeispiel mit AVR.
Die Unterschiede (8051/AVR) sind nur in der anderen
Timerinitialisierung und der anderen Interrupt-Syntax.


Peter
Autor: ope (Gast)
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
Autor: ope (Gast)
Datum: 06.06.2005 15:26

Ha, der Stackpointer wird ja benutzt - alles klar :-)
Autor: PeterL (Gast)
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
Autor: Hannes Lux (hannes)
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?

...
Autor: Christopher (Gast)
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
Autor: peter dannegger (Gast)
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
Autor: Marko B. (Gast)
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.
Autor: peter dannegger (Gast)
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
Autor: Christian (Gast)
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
Autor: Markus Hiller (markus-94209-)
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
Autor: Hannes Lux (hannes)
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.

...
Autor: Markus Hiller (markus-94209-)
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
Autor: Christopher (Gast)
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
Autor: Daniel Bambeck (Gast)
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
Autor: pittbull (Gast)
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
Autor: peter dannegger (Gast)
Datum: 31.12.2005 13:31

Hier mal ein Bespiel mit kurz/lang Erkennung:

http://www.mikrocontroller.net/attachment.php/2524...


Peter
Autor: Ralph Holzner (Gast)
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
Autor: Ich Bin (ichbin)
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
Autor: Michael Danz (micheagle)
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.
Autor: Fabian B. (Gast)
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_r