www.mikrocontroller.net

Forum: Codesammlung Tasten entprellen - Bulletproof

Important announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: peter dannegger (Gast)
Datum: 20.02.2003 14:34
Angehängte Dateien:

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
Angehängte Dateien:

Ups, da war ich wohl etwas zu hektisch.

Hier nochmal ohne Schreibfehler.


Peter
Autor: peter dannegger (Gast)
Datum: 24.02.2003 17:07
Angehängte Dateien:

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_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
Autor: Peter Dannegger (peda)
Datum: 23.02.2007 20:06

Fabian B. wrote:

> 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.


Hast Du Dir mal diese Zeile angesehen ?
.
#define REPEAT_MASK  (1<<KEY1^1<<KEY2)  // repeat: key1, key2


Peter
Autor: Fabian B. (fabs)
Datum: 24.02.2007 13:18

Keine Fragen mehr...
asche-auf-mein-haupt

die hab ich in der Tat nicht gesehn...aber die ist dann auch für das
long-delay zuständig, richtig?

Danke, Peter!

Gruß
Fabian
Autor: theend (Gast)
Datum: 02.03.2007 19:46

hallo zusammen,

ich bin noch ganz ganz neu auf dem gebiet avr mc. habe noch nichmal das
equiptment hoffe aber das es die nächsten tage bald mit der post kommt
;)

naja wollte aber vorher schonmal anfangen ein testprogramm mit leds und
schaltern zu schreiben und bin dann über die routine von Peter Dannegger
gestossen.
mir ist beim code aufgefallen das es "fälle" gibt wo die routine sprich
overflow interrupt nicht 4 mal durchlaufen wird sondern das es bis zu 7
durchläufen brauch bis key_state und key_press richtig gesetzt sind.

ich benutze mal dezimalzahlen und beziehe mich auf den c-code der
routine http://www.mikrocontroller.net/articles/Entprellung und nehme
ein beispiel wo die routine 7 mal durchlaufen wird
z.B.
ich gehe mal von der situation aus das : KEY_PIN = 1 ist was bedeutet
alle taster bis auf pin0 gesetzt.

wenn nun KEY_PIN während 3 interrupt overflow auf 3 steht (taster pin0
u. pin1 nicht gesetzt) und beim nächsten overflow auf 2 (taster pin1
nicht gesetzt), wechselt key_state auf 3 und nicht auf 2. erst nach 3
weitereren interrupt durchläufen wechselt key_state auf 2 und setzt
key_press wie gewollt.

das geht auch mit anderen fällen wobei der durchlauf von 5 bis max 7
liegen kann je nachdem wielang der kurze KEY_PIN wechsel anliegt.

kann das einer bestättigen oder definitiv verneinen? ich bin linux
benutzer und kann das ganze leider nich ausführlich in avrstudio testen.
hier noch von meiner seite grossen respekt an peter dem dieser
algorithmus eingefallen
Autor: Werbungverschmäher (Gast)
Datum: 02.03.2007 20:41

Ich kenne ja nur die Assembler-Routine, muss aber sagen, dass sie exakt
funktioniert. Dezimale "Zahlenwerte" (Zählerstände) nützen Dir beim
Versuch, den Algorithmus nachzuvollziehen nichts, da die beiden Bits des
jeweiligen Prellzählerstandes in zwei verschiedenen Registern liegen.
Denn jedes involvierte Byte enthält ja die Bits von 8 zu entprellenden
Eingängen parallel.

MfG
Autor: theend (Gast)
Datum: 02.03.2007 21:08

ich benutze nur dezimal in meinem beispiel um die schreibweise
0b00000001 für  dezimal 1 und 0b00000011 für 3 usw zu verkürzen.
Autor: Werbungverschmäher (Gast)
Datum: 02.03.2007 22:30

Hast Du beim "Testen" auch auf die Variablen ct0 und ct1 geachtet? Die
verkörpern nämlich den eigentlichen Prellzähler. Das sind zwei
8-Bit-Variablen, von denen jede je ein Bit der acht vorhandenen
Zweibit-Zähler enthält.

Wenn Du meinst, dass Du auf AVR-Studio verzichten möchtest weil...
Naja, das ist nicht mein Problem.

MfG
Autor: theend (Gast)
Datum: 03.03.2007 00:51

danke erstmal Werbungverschmäher für deine flotten antworten.
ja auf ct0 und ct1 habe ich geachtet denn sonst würde nix passieren.

ich versuche das ganze mal zu erklären so wie ich mir vorstelle das
diese routine funktioniert:

KEY_PIN eingabe : 255
i='0'  ct0='255'  ct1='255'  i='0'  key_state='0'  key_press='0'

KEY_PIN eingabe : 254
i='1'  ct0='254'  ct1='255'  i='0'  key_state='0'  key_press='0'

KEY_PIN eingabe : 254
i='1'  ct0='255'  ct1='254'  i='0'  key_state='0'  key_press='0'

KEY_PIN eingabe : 254
i='1'  ct0='254'  ct1='254'  i='0'  key_state='0'  key_press='0'

KEY_PIN eingabe : 254
i='1'  ct0='255'  ct1='255'  i='1'  key_state='1'  key_press='1'

KEY_PIN eingabe : 254
i='0'  ct0='255'  ct1='255'  i='0'  key_state='1'  key_press='0'

so nun benutze ich mal ein anderes beispiel bei dem 7 durchläufe statt 4
vorkommen:

KEY_PIN eingabe : 254
i='0'  ct0='255'  ct1='255'  i='0'  key_state='1'  key_press='0'

KEY_PIN eingabe : 252
i='2'  ct0='253'  ct1='255'  i='0'  key_state='1'  key_press='0'

KEY_PIN eingabe : 252
i='2'  ct0='255'  ct1='253'  i='0'  key_state='1'  key_press='0'

KEY_PIN eingabe : 252
i='2'  ct0='253'  ct1='253'  i='0'  key_state='1'  key_press='0'

KEY_PIN eingabe : 253
i='3'  ct0='254'  ct1='255'  i='2'  key_state='3'  key_press='2'

KEY_PIN eingabe : 253
i='1'  ct0='255'  ct1='254'  i='0'  key_state='3'  key_press='0'

KEY_PIN eingabe : 253
i='1'  ct0='254'  ct1='254'  i='0'  key_state='3'  key_press='0'

KEY_PIN eingabe : 253
i='1'  ct0='255'  ct1='255'  i='1'  key_state='2'  key_press='0'

KEY_PIN eingabe : 253
i='0'  ct0='255'  ct1='255'  i='0'  key_state='2'  key_press='0'

entweder liege ich total falsch oder er registriert erst nach 7 ints das
richtige key_state
Autor: Werbungverschmäher (Gast)
Datum: 03.03.2007 01:25

Also C ist nicht mein Ding. Ich nutze die Tastenentprellung nach Peter
Dannegger in ASM in verschiedenen Varianten und Modifikationen.
Hier eine Version, die neben dem entprellten Tastenzustand nur die
Flanken des Tastendruckes erfasst:
Tastenabfrage: ;Entprellroutine geklaut bei Peter Dannegger...
 in scan,tap   ;Tastenport einlesen (gedrückt=L)
 com scan      ;invertieren (gedrückt=H)
 eor scan,tas  ;nur Änderungen werden H
 and tz0,scan  ;Prellzähler unveränderter Tasten löschen (Bit0)
 and tz1,scan  ;Prellzähler unveränderter Tasten löschen (Bit1)
 com tz0       ;L-Bit zählen 0,2,->1, 1,3,->0
 eor tz1,tz0   ;H-Bit zählen 0,2,->tz1 toggeln
 and scan,tz0  ;Änderungen nur dann erhalten, wenn im Prellzähler
 and scan,tz1  ;beide Bits gesetzt sind (Zählerstand 3)
 eor tas,scan  ;erhaltene Änderungen toggeln alten (gültigen) Tastenstatus
 and scan,tas  ;nur (neu) gedrückte Tastenbits bleiben erhalten
 or tfl,scan   ;und zugehörige Bits setzen (gelöscht wird nach Abarbeitung)
 ;in "tas" steht jetzt der gültige Tastenzustand,
 ;in "tfl" die Flags der neu gedrückten, noch nicht abgearbeiteten Tasten...

Diese Variante arbeitet mit Registern und im Interrupt. Gelegentlich
nutze ich diesen Algorithmus auch mit temporären Registern in der
Mainloop und halte die Variablen im SRAM. Mit kleinen Zusätzen lässt
sich auch (nur oder zusätzlich) das Loslassen der Taster erfassen und /
oder auch ei Repeat bei Dauerdruck.

Da dieser Algorithmus mit 2-Bit-Prellzählern (bei mir tz0 und tz1)
arbeitet, muss ein veränderter Eingangspegel (also Taster gedrückt oder
losgelassen) exakt 4 mal stabil erfasst worden sein, damit die Änderung
übernommen wird, sich also der Wert von tas ändert.

Da der Controller nur Maschinencode (ASM) kann, der C-Code auch nur in
ASM (bzw. Maschinencode) übersetzt wird, solltest Du das mal in ASM
durchspielen.

MfG
Autor: Peter Dannegger (peda)
Datum: 03.03.2007 10:37

theend wrote:

> KEY_PIN eingabe : 255
> i='0'  ct0='255'  ct1='255'  i='0'  key_state='0'  key_press='0'


Ist ja schön, daß Du die Dezimalentsprechung jedes Bitmusters im Kopf
hast, ich nicht.

Wenn Du also willst, daß man Deine Frage nachvollziehen kann, solltest
Du besser alle Werte binär hinschreiben.


Aber wie Werbungverschmäher schon richtig sagte, es reicht völlig, sich
nur ein Bit anzugucken.
Die logischen Operatoren beeinflussen nur die gleichwertigen Bits und
lassen alle anderen unverändert.


Es kann sein, daß Du das Einschaltverhalten meinst, da habe ich mich
nicht drum gekümmert. Wichtig ist ja nur, daß nicht fälschlich ein Druck
gespeichert wird.

Man kann auch die Initialisierung so ändern, daß erst die Taste
losgelassen werden muß, d.h. ein Druck schon während des Reset keine
Aktion auslöst.


Peter
Autor: theend (Gast)
Datum: 04.03.2007 14:00

@peter nein die initialisierung is schon durch siehe i=0 u. ct0,ct1 =
255
so dann hier noch mal die mundgerechte version in der hoffnung das es
dann mal angeschaut wird ohne gleich ratschläge zu geben:

KEY_PIN eingabe : 11111111
i='0'  ct0='11111111'  ct1='11111111'  i='0'  key_state='0'
key_press='0'

KEY_PIN eingabe : 11111110
i='1'  ct0='11111110'  ct1='11111111'  i='0'  key_state='0'
key_press='0'

KEY_PIN eingabe : 11111110
i='1'  ct0='11111111'  ct1='11111110'  i='0'  key_state='0'
key_press='0'

KEY_PIN eingabe : 11111110
i='1'  ct0='11111110'  ct1='11111110'  i='0'  key_state='0'
key_press='0'

KEY_PIN eingabe : 11111110
i='1'  ct0='11111111'  ct1='11111111'  i='1'  key_state='1'
key_press='1'

KEY_PIN eingabe : 11111110
i='0'  ct0='11111111'  ct1='11111111'  i='0'  key_state='1'
key_press='0'

so nun benutze ich mal ein anderes beispiel bei dem 7 durchläufe statt 4
vorkommen:

KEY_PIN eingabe : 11111110
i='0'  ct0='11111111'  ct1='11111111'  i='0'  key_state='1'
key_press='0'

KEY_PIN eingabe : 11111100
i='10'  ct0='11111101'  ct1='11111111'  i='0'  key_state='1'
key_press='0'

KEY_PIN eingabe : 11111100
i='10'  ct0='11111111'  ct1='11111101'  i='0'  key_state='1'
key_press='0'

KEY_PIN eingabe : 11111100
i='10'  ct0='11111101'  ct1='11111101'  i='0'  key_state='1'
key_press='0'

KEY_PIN eingabe : 11111101
i='10'  ct0='11111110'  ct1='11111111'  i='10'  key_state='11'
key_press='10'

KEY_PIN eingabe : 11111101
i='1'  ct0='11111111'  ct1='11111110'  i='0'  key_state='11'
key_press='0'

KEY_PIN eingabe : 11111101
i='1'  ct0='11111110'  ct1='254'  i='0'  key_state='11'  key_press='0'

KEY_PIN eingabe : 11111101
i='1'  ct0='11111111'  ct1='11111111'  i='1'  key_state='10'
key_press='0'

KEY_PIN eingabe : 11111101
i='0'  ct0='11111111'  ct1='11111111'  i='0'  key_state='10'
key_press='0'
Autor: Hannes Lux (hannes)
Datum: 04.03.2007 14:57

> so dann hier noch mal die mundgerechte version in der hoffnung das es
> dann mal angeschaut wird ohne gleich ratschläge zu geben:

Muss ich das jetzt verstehen? Mir brauchst Du nix "mundgerecht" zu
machen, ich komme auch ohne Deine Aktivitäten zurecht. Wer sucht Hilfe,
Du oder wir? Wir wissen, dass der Algorithmus korrekt ist, ich nutze ihn
mangels C-Kenntnissen aber in Assembler. Ich habe ihn mittels
AVR-Studio-Simulator auch überprüft. Er funktioniert!

Du verwirrst Dich selbst unnötig, indem Du die Variablen als Ganzes
betrachtest..
Es wird übersichtlicher, wenn Du nur mit einem Taster "spielst" und nur
ein Bit (das des Tasters) beachtest. Denn in allen involvierten
Variablen wird pro Taster nur ein Bit verwendet, und zwar das mit der
Wertigkeit des Anschlusspins, an dem der Taster angeschlossen ist. Das
Programm kann bis zu 8 Taster parallel behandeln, jeder Taster nutzt
aber in jeder beteiligten Variable nur 1 Bit. Um das zu verstehen, musst
Du Deine numerische Sicht auf die Bariablen mal deaktivieren, es sind
keine Zahlen, es sind Bitmuster. Zahlen (z.B. die 8 Prellzähler)
entstehen durch gemeinsames Betrachten der gleichwertigen Bits in
unterschiedlichen Variablen.

Nimm AVR-Studio und simuliere die ASM-Routine durch, dann siehst Du es.

...
Autor: Peter Dannegger (peda)
Datum: 04.03.2007 17:10

theend wrote:

> so nun benutze ich mal ein anderes beispiel bei dem 7 durchläufe statt 4
> vorkommen:

Ich kann da nirgends 7 Durchläufe erkennen:

Nach dem 1. Lauf setzt Du KEY_PIN.1 = 0 und voila, nach dem 5. ist
keystate.1 = 1, also 5-1 = 4

Nach dem 4.Lauf setzt Du KEY_PIN.0 = 1 und voila, nach dem 8. ist
keystate.0 = 0, also wieder 8-4 = 4

Ist in der Binärschreibweise sehr schön zu erkennen.


Peter
Autor: jar (Gast)
Datum: 21.06.2007 17:34

alles toll, aber das Problem bleibt

ich kann selten immer 8 Bits eines Ports verwenden, einige Bits sind
immer reserviert, um trotzdem diese Routine zu nutzen hab ich sie
gedoppelt

-------------------------------------------------
#define KEY_PORT        PORTC
#define KEY_PIN         PINC
#define KEY_PIN2        PIND
#define KEY1            2
#define KEY2            3
#define KEY3            4
#define KEY4            5
#define KEY5            6
#define KEY6            7
#define KEY7            1
-------------------------------------------------

  i = key_state ^ ~KEY_PIN;                       // key changed ?
  ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll
over ?
  key_state ^= i;                                 // then toggle
debounced state
  key_press |= key_state & i;                     // 0->1: key press
detect

  if( (key_state & REPEAT_MASK) == 0 )            // check repeat
function
       rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 )
  {  rpt = REPEAT_NEXT;                            // repeat delay
        key_rpt |= key_state & REPEAT_MASK;
    }

  i2 = key_state2 ^ ~KEY_PIN2;                       // key changed ?
  ct02 = ~( ct02 & i2 );                             // reset or count
ct0
  ct12 = ct02 ^ (ct12 & i2);                          // reset or count
ct1
  i2 &= ct02 & ct12;                                 // count until roll
over ?
  key_state2 ^= i2;                                 // then toggle
debounced state
  key_press2 |= key_state2 & i2;                     // 0->1: key press
detect

-------------------------------------------------

uint8_t get_key_press( uint8_t key_mask )
{ static uint8_t key_mask2;
  cli();                                          // read and clear
atomic !
  key_mask2=key_mask;
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  key_mask2 &= key_press2;                          // read key(s)
  key_press2 ^= key_mask2;                          // clear key(s)
  sei();
  return (key_mask || key_mask2);
}
-------------------------------------------------

wüsste gerne ob es eine bessere Möglichkeit gibt verschiedene Ports zu
nutzen und davon jeweils nur die von Tasten benutzten Bits zu maskieren,
damit andere Signale keine Taste simulieren
Autor: Peter Dannegger (peda)
Datum: 21.06.2007 19:11

jar wrote:
> alles toll, aber das Problem bleibt
>
> ich kann selten immer 8 Bits eines Ports verwenden, einige Bits sind
> immer reserviert, um trotzdem diese Routine zu nutzen hab ich sie
> gedoppelt

Ich sag mal, das geht so nicht.


Mach es doch so:

Angenommen, KEY1,2,3 sind auf PINC:
#define KEY_PIN         PINC
#define KEY1            2
#define KEY2            3
#define KEY3I           4      //input bit
#define KEY3            0      //reading bit
#define KEY_PIN2        PIND
#define KEY4            5
#define KEY5            6
#define KEY6            7
#define KEY7            1
#define KEY8            4

...
    i = KEY_PIN2 & (1<<KEY4^1<<KEY5^1<<KEY6^1<<KEY7^1<<KEY8);
    if(KEY_PIN & (1<<KEY1))
      i |= 1<<KEY1;
    if(KEY_PIN & (1<<KEY2))
      i |= 1<<KEY2;
    if(KEY_PIN & (1<<KEY3I))
      i |= 1<<KEY3;
    i ^= ~key_state;                       // key changed ?
    ct0 = ~( ct0 & i );                    // reset or count ct0
usw.


Peter
Autor: Joachim B. (jar)
Datum: 21.06.2007 19:41

>Ich sag mal, das geht so nicht.

bis jetzt lief es so,

>Mach es doch so:
aber toll finde ich meine Version ja nicht, dein Vorschlag probiere ich,
bzw. vielleicht denke ich auch selber noch mal nach

Problem war vorher die Statements KEY_PIN -> PINC PIND , die konnte ich
nicht maskieren weil sie direkt auch die Register wirken

Edit: sorry, so lief es natürlich nicht, aber so:

uint8_t get_key_press( uint8_t key_mask )
{ static uint8_t key_mask2;
  cli();                                        // read and clear atomic
!
  key_mask2=key_mask;
  key_mask &= key_press;                        // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  key_mask2 &= key_press2;                          // read key(s)
  key_press2 ^= key_mask2;                          // clear key(s)
  sei();
  return ((key_mask&(1<<KEY1 | 1<<KEY2 | 1<<KEY3 | 1<<KEY4 | 1<<KEY5 |
1<<KEY6)) | (key_mask2&(1<<KEY7)));
}
Autor: Joachim B. (jar)
Datum: 21.06.2007 20:27

so gefällt mir das besser :-)

ich hoffe das ist ohne Nebenwirkungen, aber im Moment läufts

für
#define KEY_PORT        PORTC
#define KEY_PIN         PINC
#define KEY_PIN2        PIND
#define KEY1            2
#define KEY2            3
#define KEY3            4
#define KEY4            5
#define KEY5            6
#define KEY6            7
#define KEY7            1
#define ALL_KEYS        (1<<KEY1 | 1<<KEY2 | 1<<KEY3 | 1<<KEY4 | 1<<KEY5
| 1<<KEY6)

  i = key_state ^ ( (~KEY_PIN&(1<<KEY1 | 1<<KEY2 | 1<<KEY3 \
                            | 1<<KEY4 | 1<<KEY5 | 1<<KEY6)) \
                         |(~KEY_PIN2&(1<<KEY7)));   // key changed ?
  ct0 = ~( ct0 & i );                        // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                     // reset or count ct1
  i &= ct0 & ct1;                        // count until roll over ?
  key_state ^= i;                    // then toggle debounced state
  key_press |= key_state & i;       // 0->1: key press detect

  if( (key_state & REPEAT_MASK) == 0 )  // check repeat function
       rpt = REPEAT_START;         // start delay
  if( --rpt == 0 )
  {  rpt = REPEAT_NEXT;         // repeat delay
        key_rpt |= key_state & REPEAT_MASK;
    }
Autor: Der Hubert (Gast)
Datum: 22.06.2007 10:24

Mal ne generelle Frage unabhängig der hier vorgestellten Methoden:

Warum per Software entprellen ??

Ich habe mal gelernt, dass man bei einfachen Schaltungen (und bei
Schaltungen ohne µC eh nicht anders lösbar) die Tasten elektronisch
entprellt ....

Das müsste doch per Kondensator oder so recht günstig gehen ....
Autor: Unbekannter (Gast)
Datum: 22.06.2007 10:56

> Warum per Software entprellen ??

Gerät in der Massenproduktion mit vier Tastern:

Du willst 100.000 Stück davon bauen und verkaufen. Ein RC-Glied pro
Taster kostet etwa 4 Cent. Davon 4 Stück pro Gerät mal 100.000 =
16.000,- Euro.

Dabei sind die Kosten für Bestückung, Lagerhaltung, höhere Ausfallrate
(mehr Bauteile!), größere Platinenfläche etc. noch gar nicht
eingerechnet.

Alternative: Software. Eine halbe Stunde für einen Programierer: 100,-
Euro.
Autor: jar (Gast)
Datum: 22.06.2007 10:57

Ergänzung, für jedes eingesparte Bauteil, entflechten, verdrahten, löten
ist der Hobbyist dankbar und wenn eh ein MC on Board ist , wieso nicht
per SW ?
Autor: Hannes Lux (hannes)
Datum: 22.06.2007 11:29

Der Hubert wrote:
> Mal ne generelle Frage unabhängig der hier vorgestellten Methoden:
>
> Warum per Software entprellen ??
>
> Ich habe mal gelernt, dass man bei einfachen Schaltungen (und bei
> Schaltungen ohne µC eh nicht anders lösbar) die Tasten elektronisch
> entprellt ....
>
> Das müsste doch per Kondensator oder so recht günstig gehen ....

Entprell-Hardware muss man jedesmal neu kaufen.
Entprell-Software kann man wiederverwenden (kopieren).

...
Autor: Der Hubert (Gast)
Datum: 22.06.2007 16:21

Ok ok, ....

Ich wusste nicht, dass Ihr gleich 100.000 Stück baut .... gg
Autor: Peter Dannegger (peda)
Datum: 22.06.2007 16:37

Der Hubert wrote:

> Ich wusste nicht, dass Ihr gleich 100.000 Stück baut .... *gg*

Die Routine ist ja schon fertig, also lohnt es sich schon ab dem ersten
Stück.

Und außerdem macht sich noch viel mehr, als nur entprellen.


Peter
Autor: Ulrich (Gast)
Datum: 24.06.2007 21:45

Ich tue in der zwischenzeit komplett alle Ports immer über Interrupts
einlesen. Dann habe ich globale Variablen mit dem namen PORTX_STATE
PORTX_PRESS PORTX_UNPRESS für jeden PORT. Und habe dann alle Eingänge
schön entprellt.
Autor: Sönke Gilbrich (cycrow)
Datum: 29.06.2007 16:58
Angehängte Dateien:

Hallo,
dank Anfängertum und Brett vorm Kopf hab ich natürlich von diesem
Assamblercode erstmal nur Bahnhof verstanden.
Erstmal überhaupt vielen Dank dafür.

Da ich zu faul bin, alles auf dem Papier nachzuvollziehen und ich im
Simulator zu wenig Überblick hatte, hab ich mal eine kleine
Excel-Tabelle gemacht, wo man 9 Durchläufe berechnen lassen kann und
alle variablen Verfolgen kann.

Vielleicht hifts außer mir ja noch jemandem.

Unter Excel hat das bei mir wunderbar geklappt mit der Tabelle, wenn mir
beizeiten jemand verrät, warum das bei OpenOffice nicht will, wäre ich
sehr dankbar.


MfG
Sönke
Autor: Sönke Gilbrich (cycrow)
Datum: 30.06.2007 16:11
Angehängte Dateien:

Nachdem ich nochmal mit Open Office diskutiert habe, läuft das jetzt
auch unter Calc.
Autor: mgiaco (Gast)
Datum: 01.07.2007 11:20

Hallo, ich verwenden auch den Code hier, funktioniert wirklich gut.
Prozessor ARM7 (LPC2148).

Mein Problem, beim ARM hat man ja nur 2 Ports mit je 32 I/O´s ich habe
jetzt einfach den code auf uint32_t geändert, es funktioniert auch aber
gibts auch etwas eleganteres.

Kann man die Pins die man möchte auch irgendwie zusammen mappen auf ein
Byte  (meisten braucht man ja nicht mehr wie 8 Taster)?

Wenn alle nacheinander sind ginge es ja, aber wenn ich zum Beispiel -
P0.14, P0.15, P0.30, P0.1 usw. verwenden will sind diese ja über die 32
I/O´s verteilt, sehe da keine ander Möglichkeit bis jetzt.


Wie so muss man bei den Interrupt immer ein und auschalten, reicht es
nicht wenn man nur die paar Zeilen mit einem Flag ein und ausschaltet?
Wenn man zum Beispiel mit dem Interrupt auch einen digitalen Regler
triggern möchte ist das etwas problematisch. Gibts auch eine ander
Lösung?


danke
Autor: Peter Dannegger (peda)
Datum: 01.07.2007 15:12

mgiaco wrote:

> Wie so muss man bei den Interrupt immer ein und auschalten, reicht es
> nicht wenn man nur die paar Zeilen mit einem Flag ein und ausschaltet?
> Wenn man zum Beispiel mit dem Interrupt auch einen digitalen Regler
> triggern möchte ist das etwas problematisch. Gibts auch eine ander
> Lösung?

Nein.

Die Interuptsperre macht die 2 aufeinanderfolgende Befehle atomar, das
sollte auf nem ARM7 weit unter 1µs dauern.

Wenn selbst so extrem kurze Sperren stören, dann ist an Deinem
Programmaufbau etwas grungsätzlich falsch bzw. derart zeitkritische
Sachen sollte dann ein CPLD oder FPGA machen.


Peter
Autor: mgiaco (Gast)
Datum: 01.07.2007 16:39

okay funktionieren tut es aber trotzdem, wird aber dann mal zu einem
Fehler führen oder.

Aber zu dem andern Problem gibts wohl nichts anderes oder?
Autor: mgiaco (Gast)
Datum: 01.07.2007 17:18

Habe es jetzt so gemacht funktionier weis nur nicht ob das richtig ist!
Das mit den IRQ´s ist ja nicht so einfach wie bei den AVR´s.
uint32_t get_key_press(uint32_t key_mask )
{
    unsigned cpsr;
    
    //cli();
    cpsr = disableIRQ();                  // disable global interrupts
    T0MCR &= ~(TMCR_MR0_I);                 // disable MR0 interrupt
    restoreIRQ(cpsr);                     // restore global interrupts
    
    key_mask &= key_press;            // read key(s)
    key_press ^= key_mask;            // clear key(s)
  
    //sei();
    cpsr = disableIRQ();                  // disable global interrupts
    T0MCR |= TMCR_MR0_I;                  // enable MR0 interrupt
    restoreIRQ(cpsr);                     // restore global interrupts
  
    return key_mask;
}

danke
Autor: Peter Dannegger (peda)
Datum: 01.07.2007 18:42

mgiaco wrote:

>
> uint32_t get_key_press(uint32_t key_mask )
> {
>     unsigned cpsr;
> 
>     //cli();
>     cpsr = disableIRQ();                  // disable global interrupts
>     T0MCR &= ~(TMCR_MR0_I);                 // disable MR0 interrupt
>     restoreIRQ(cpsr);                     // restore global interrupts
> 
>     key_mask &= key_press;            // read key(s)
>     key_press ^= key_mask;            // clear key(s)
> 
>     //sei();
>     cpsr = disableIRQ();                  // disable global interrupts
>     T0MCR |= TMCR_MR0_I;                  // enable MR0 interrupt
>     restoreIRQ(cpsr);                     // restore global interrupts
> 
>     return key_mask;
> }
> 


Kommst Du Dir dabei nicht selber albern vor ?

Du machst 2 Funktionsaufrufe und dazwischen eine Codezeile:
    //cli();
    cpsr = disableIRQ();                  // disable global interrupts
    T0MCR &= ~(TMCR_MR0_I);                 // disable MR0 interrupt
    restoreIRQ(cpsr);                     // restore global interrupts


Und das soll merkbar Zeit einsparen gegenüber 2 Funktionsaufrufen und 2
Codezeilen:
    //cli();
    cpsr = disableIRQ();                  // disable global interrupts
    key_mask &= key_press;            // read key(s)
    key_press ^= key_mask;            // clear key(s)
    //sei();
    restoreIRQ(cpsr);                     // restore global interrupts


Die Interruptdisablezeit dürfte gerade mal einen Zyklus länger sein, da
ja die Variablen für die 2. Codezeile schon in Registern sind.

In den Codezeilen ist doch keine float-Division, die Zeit kostet,
sondern nur simple Logikbefehle.


Peter
Autor: mgiaco (Gast)
Datum: 02.07.2007 10:07

Okay stimmt, ich wollte nur sicher gehen und nur den Timer abschalten,
und die IRQ's gleich wieder aktivieren.

Danke.
Autor: Hannes Lux (hannes)
Datum: 02.07.2007 11:23

mgiaco wrote:
> Okay stimmt, ich wollte nur sicher gehen und nur den Timer abschalten,

Warum Timer abschaslten? Das ist doch Fälschung des Timings. Dann kann
man doch gleich ohne Timer arbeiten...

> und die IRQ's gleich wieder aktivieren.
>
> Danke.

Ich glaube, Du solltest versuchen, die Rourtine zu verstehen, ehe Du sie
modifizierst.

...
Autor: mgiaco (Gast)
Datum: 02.07.2007 18:49

Hallo Hannes,

Ich meinen natürlich den Interrupt des Timers nicht den Timer selbst,
schon klar. Das habe ich doch oben auch gemacht denke ich, ich habe ja
nur den MR0 IRQ ausgeschalten --> wäre das nicht korrekt so? Hab
nochmals im Manual nachgesehen wüsste nicht wie man den IRQ sonst
abschaltet falls man das mal möchte.

>Ich glaube, Du solltest versuchen, die Rourtine zu verstehen, ehe Du sie
>modifizierst.

Hast natürlich volkommen recht, dennoch weis ich nicht was du genau
meinst denn das Beispiel von oben funktioniert, nur ist es wie Herr
Danneger geschrieben hat vollig unsinnig was das Timing betrift. Deshalb
mach ich es jetzt auch so wie vorgeschlagen.

Autor: Joachim B. (jar)
Datum: 20.07.2007 13:46

ich habe aus Portmangel nun auch 'I2C Tastem mit dem PCF8574 umgestellt

nun grübel ich wie ich diese Routine mit dem I2C verheirate...

ich mache im 10ms Timerinterrpt die CT0 und CT1 Verarbeitung

aber klappt das auch mit I2C lesen ? wegen CLI()SEI() ?

als I2C Lib nutze ich die von Peter Fleury

könnte dafür etwas Erkennungscode für PCF8574 (0x40) oder PCF8574A
beisteuern (0x70), hat mich einen halben Tag Fehlersuche gekostet, ich
dachte der Unterschied ist N zu AP aber auch N mit A ist 0x70 und dann
wieder das richtiige Datenblatt finden wenn man die goldene Schrift auf
dem Stein kaum lesen kann, aber tastenlesen klappt nun nur noch nicht
entprellt....
Autor: Hannes Lux (hannes)
Datum: 20.07.2007 14:03

Ich nutze (in ASM) diesen Algorithmus im Mega32 für 8 (lokale) Tasten an
einem Port und 32 Tasten (im ganzen Haus verteilt), die über SPI und
Schieberegister eingelesen werden.

Dem Algorithmus (der Routine) ist es ja egal, wo die Daten herkommen,
anstatt einen Port einzulesen (und ggf zu invertieren) kann man ja auch
eine Variable kopieren, die über irgendeine Schnittstelle eingelesen
wurde.

...
Autor: Joachim B. (jar)
Datum: 20.07.2007 15:02

Hannes Lux wrote:
> Dem Algorithmus (der Routine) ist es ja egal, wo die Daten herkommen,
> anstatt einen Port einzulesen (und ggf zu invertieren) kann man ja auch
> eine Variable kopieren, die über irgendeine Schnittstelle eingelesen
> wurde.

stimmt, ich war unsicher wegen CLI() und SEI() , klappt aber fein:

  if(_i2c_key)
  {  if(_i2c_key=='A')
    {  if(!i2c_start(PCF8574A_0+I2C_READ))  //;  // set device address
and write mode
      i = key_state ^ ( ( ~(unsigned char)i2c_readNak() << 1) );
    }
    else
    {  if(!i2c_start(PCF8574_0+I2C_READ))  //;  // set device address
and write mode
      i = key_state ^ ( ( ~(unsigned char)i2c_readNak() << 1) );
    }
    i2c_stop();
  }
  else
    i = key_state ^ ~KEY_PIN;                       // key changed ?

  ct0 = ~( ct0 & i );                             // reset or count ct0
.
.
.
usw. wie schon bekannt
Autor: Hannes Lux (hannes)
Datum: 20.07.2007 15:19

Joachim B. wrote:

> stimmt, ich war unsicher wegen CLI() und SEI() , klappt aber fein:

Nunja, cli und sei habe ich nicht drin. Erstens mach' ich's in
Assembler, zweitens mach' ich's 8-bittig. Da entsteht kein
16-Bit-Portzugriff, den ich atomar behandeln muss.

Oftmals nutze ich die Routine (mit Exklusiv-Registern) in der Timer-ISR,
oft aber auch als zyklischen Job der Mainloop (per Timer-Int und
Semaphore), dann meist mit temporären Registern und im SRAM gehaltenen
Variablen.

...
Autor: Wiener Würstchen (Gast)
Datum: 19.11.2007 11:29

Hallo Leute,

ich finde den Code schön kompakt (auch wenn man erst eine Weile
nachdenken muss, bevor man ihn versteht :-). Ich hatte das Problem, auch
das Loslassen einer Taste erkennen zu wollen, und habe daher folgende
Ergänzung ersonnen (braucht ein weiteres Register).

Lieben Gruß,
Wiener Würstchen
#define KEY_INPUT  P2

char key_state, key_press;

/* neue Zeile hier...
char key_release;
*/  


void to_int( void ) interrupt INT_T0
{
  static char ct0, ct1;
  char i;

  i = key_state ^ ~KEY_INPUT;  // key changed ?
  ct0 = ~( ct0 & i );    // reset or count ct0
  ct1 = ct0 ^ ct1 & i;    // reset or count ct1
  i &= ct0 & ct1;    // count until roll over 
  key_state ^= i;    // then toggle debounced state
  key_press |= key_state & i;  // 0->1: key pressing detect

/* neue Zeile hier...
  key_release |= ~key_state & i;  // 0->1: key release detect
*/  
}
Autor: Soner (Gast)
Datum: 01.07.2008 14:14

Hallo Leute,
ich weiß zwar nicht, ob das Thema bereits geschlossen ist, aber ich hab
da eine Frage.

Ich benutze diese Entprellung ebenfalls. Gedacht ist es für eine
"selbstgebaute" Fernbedienung. Ich will eine Art Steuerkreuz bauen, d.h.
wenn man z.b. links und oben gleichzeitig drückt, soll es Diagonal
gehen, aber die Entprellung erkennt die Tasten nur einzeln. also wenn
ich eine ausgabe mache kommt erst oben und dann links, oder umgekehrt.

Woran kann das liegen? ich verwende einen AT90CAN128 und arbeite mit
AVR-Studio 4 und WIN-AVR
Autor: Hannes Lux (hannes)
Datum: 01.07.2008 15:00

Die C-Version kenne ich nicht im Detail, bei der ASM-Version kannst auch
auf Tastenkombinationen prüfen, das wird aber in C nicht anders sein.

In Key_press stehen die Bits der Tasten, die (seit dem letzten
Loslassen) erneut gedrückt wurden. Diese nutzt (und löscht) man, wenn
man jeden Tastendruck nur einmal auswerten will/muss.

In Key_state stehen die entprellten Tastenzustände der Tasten. Diese
kann man nutzen, wenn man Aktionen auslösen will, solange die Taste
gedrückt ist. Für eine Fernbedienung, deren Empfänger solange etwas tun
soll, wie eine Taste am Sender betätigt ist, wäre das der bessere Weg.

Man kann die einzelnen Bits in Key_state oder Key_press aber auch
gruppenweise auswerten. Man nimmt (zumindest in ASM) eine Kopie (damit
der Original-Wert durch die Gruppenbildung nicht verändert wird) der
Variable, maskiert die gewünschten Bitmuster aus und prüft das Ergebnis
auf Vollständigkeit.

...
Autor: yalu (Gast)
Datum: 01.07.2008 15:31

Da mit der Routine alle Tasten parallel ausgewertet werden, gibt es
keine Priorisierung, bspw. in der Form, dass bei zwei exakt
gleichzeitig gedrückten Tasten immer eine bestimmte der beiden zuerst
ausgewertet wird.

Allerdings wirst es nur dann schaffen, zwei Tasten (aus Sicht der
Routine) "gleichzeitig" zu drücken, wenn die Zykluszeit, mit der die
Routine aufgerufen wird sehr groß ist. Ansonsten wird praktisch immer
eine Zeitdifferenz und damit eine von zwei Tasten als erste erkannt
werden.

Wie ist denn der genaue Ablauf vom Aufruf der Entprell- und der
Anzaigeroutine?
Autor: Sanane Banane (sanane)
Datum: 01.07.2008 15:41

Also ich hab das obige Beispiel genommen.
http://www.mikrocontroller.net/attachment/12660/C_TAST.C

Hier wird ja zb. mit Taste 0 die Led ein und ausgeschaltet. Wie lautet
die if Anweisung, wenn die Led angehen soll, wenn Taste 0 und Taste 1
gedrückt wurden.

if( get_key_press( 1<<PD0 ))    // Key 0:
      LED_OUTPUT ^= 1<<PB0;      // toggle LED 0

if( get_key_press( 1<<PD1 ))    // Key 0:
      LED_OUTPUT ^= 1<<PB1;      // toggle LED 0

Meine if-Anweisung hieß
if( get_key_press( (1<<PD0) && (1<<PD1) ) )
      LED_OUTPUT ^= 1<<PB2;

Aber die Anweisung wird nicht ausgeführt, d.h. es geht nicht die 3. LED
an sondern die 1. und die 2.

Ich habe mein Problem jetzt auf dieses Beispiel übertragen, aber um eine
ähnliche Problematik geht es bei mir.

PS: Ich arbeite zum ersten Mal mit AVR-Studio und ich hab erst vor einem
Monat das Buch C von a bis z gelesen (nicht komplett) und hab deshalb
keine Programmiererfahrung.
Den Code von Peter hab ich zum Teil verstanden. zb. steht in key_state
und key_press nicht dasselbe?? und key_state ist doch invertiert, wie
der eigentliche eingang?
Autor: Sanane Banane (sanane)
Datum: 01.07.2008 15:57

also die Funktionen und die Definitionen habe ich von
http://www.mikrocontroller.net/articles/Entprellun...

Mein Programm sieht so aus:

int main()
{
while(1)
{
eingabe = KEY_PIN;
help = get_key_press( eingabe );
if (help != 0)
      switch (help)
        {
        case 1:
        {
        print1 (" Taste 1 kurz gedrückt ");
        }
        break;

        case 2:
        {
        print1 (" Taste 2 kurz gedrückt ");
        }
        break;

        case 4:
        {
        print1 (" Taste 3 kurz gedrückt ");
        }
        break;
        case 3:                              //wenn Taste 1 und Taste 2
\                                 //gedrückt dann eingabe = 11111100 =>
\                                 //key_state = 00000011 = 3
        {
        print1 (" Taste 1 und Taste 2 kurz gedrückt ");
        }
        break;
        }
}
}

Ich habe den Ablauf für ein paar Tastenkombinationen auf dem Blatt
aufgeschrieben und eines davon war das im Kommentar
Autor: Sanane Banane (sanane)
Datum: 02.07.2008 15:02

Hallo nochmal,
@yalu ich glaube du hast recht. Ich habe die Zykluszeit für den
Timeroverflow verlängert und es erkennt jetzt, wenn ich zwei Tasten
drücke MANCHMAL.

Also die einzelnen Tasten funktionieren, aber wenn ich zwei auf einmal
drücke, funktioniert es noch nicht so wie ich es will.

Wenn man ein Steuerkreuz als Beispiel nimmt, dann wird wenn ich oben
drücke oben ausgegeben und bei links wird links ausgegeben. Wenn ich
beide drücke sollte nur diagonal ausgegeben werden, aber ich kriege
oben, links und diagonal.
Ich habe versucht das zu unterdrücken, indem ich bei oben und links
erkennen wollte, ob jeweils die andere Taste bereits losgelassen wurde.

Dann kommt die Ausgabe oben nur dann, wenn ich vorher die Taste links
mal gedrückt habe.

Wie lade ich hier eine Datei hoch? kann nur durchsuchen und dann ist die
Datei weg

Nochmal Danke
Autor: Peter Dannegger (peda)
Datum: 02.07.2008 20:33
Angehängte Dateien:

@Soner Aygün

Die Funktion get_key_press ist nicht geeignet, da man kaum innerhalb von
10ms 2 Tasten gleichzeitig drücken kann.

Du mußt die Funktion get_key_long nehmen, wenn Du einen Impuls pro
Betätigung haben willst oder get_key_rpt für Pulse, solange man drückt.

Anbei das Beispiel für beides.

Die Festlegung von REPEAT_START bestimmt, in welcher Zeit die Betätigung
als gleichzeitig erkannt wird.
Um diese Zeit wird natürlich auch die Reaktion verzögert. Im Beispiel
habe ich 10 definiert, also 100ms.

Da man 2 Tasten abfragt, muß man per switch-Anweisung noch testen,
welche oder ob beide gedrückt sind.


Peter
Autor: Sanane Banane (sanane)
Datum: 03.07.2008 09:42

Vielen Dank für die Hilfe, die Tasten funktionieren jetzt.

Habe noch Fragen zum Code

OCR0A = (s16)(XTAL / 1024.0 * 10e-3 - 0.5);  // preload for 10ms

Was bedeutet diese Zeile? und wofür steht das OCR0A und XTAL.

In dem Beispiel hier im Wiki unter Entprellung steht diese Zeile in der
ISR

TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);   // preload
for 10ms

Es hat auch denselben Kommentar.
Ich hab versucht es im Datenblatt zu verstehen, aber mein Englisch
reicht anscheinend dafür nicht.
Das einzige was ich glaube verstanden zu haben ist, das XTAL eine
externe Uhr ist.

Brauche ich beide Zeilen und wo werden die 10ms festgelegt, falls es das
"10e-3" ist, was ist dann die 0,5??

Ich hatte statt XOR-Verknüpfungen einfache OR-Verknüpfungen, ist das
falsch??

Soner
Autor: Hannes Lux (hannes)
Datum: 03.07.2008 10:27

XTAL und F_CPU sind vereinbarte Konstanten und meinen beide die
Quarzfrequenz. Ich nenne sie CLOCK, aber nicht in C, sondern in ASM.

...
Autor: Hannes Lux (hannes)
Datum: 03.07.2008 10:29

Achja, die 0.5 sind für die Rundung des Ergebnisses da.

...
Autor: Peter Dannegger (peda)
Datum: 03.07.2008 14:27

Soner Aygün wrote:
> OCR0A = (s16)(XTAL / 1024.0 * 10e-3 - 0.5);  // preload for 10ms
>
> Was bedeutet diese Zeile? und wofür steht das OCR0A und XTAL.

Die neueren AVRs haben nen CTC-Mode (Clear Timer on Compare), d.h. der
Timer setzt sich nach Gleichheit mit OCR0A automatisch auf Null.

Man muß dann nicht mehr in jedem Interrupt den Timer selber rücksetzen,
sondern man setzt nur einmalig den Vergleichswert in OCR0A.

XTAL ist der CPU-Takt, im AVR-GCC wird das F_CPU genannt.

10e-3s = 10ms (wie beim Taschenrechner).

Techniker schreiben Zenerpotenzen immer als Vielfaches von 3
(p,n,µ,m,k,M,G).


Peter
Autor: Sanane Banane (sanane)
Datum: 03.07.2008 14:54

Vielen Dank für die Antworten.
Ich wusste nur nicht, ob die 10ms in der Zeile berechnet werden, oder ob
sie bereits mit 10e-3 eingetragen wurden.

Und so wie ich das ganze verstanden habe, sind das 2 verschiedene
Interrupts.
Der eine läuft über und der andere vergleicht die Zeit und wird wieder
zurückgesetzt.

Auf alle Fälle funktioniert jetzt alles, hab nur versucht es noch zu
verstehen. Respekt an Peter für den genialen Code

Soner
Autor: Juergen (Gast)
Datum: 17.09.2008 14:11

Hallo!

Ich habe mich mit dem schon ganz oben diskutiertem Code
auseinandergesetzt:
KEY_INPUT=11111110; //Um Tastendruck zu simulieren.

while(1){
  i = key_state ^ ~KEY_INPUT;  // key changed ?
  ct0 = ~( ct0 & i );    // reset or count ct0
  ct1 = ct0 ^ ct1 & i;    // reset or count ct1
  i &= ct0 & ct1;    // count until roll over 
  key_state ^= i;    // then toggle debounced state
  key_press |= key_state & i;  // 0->1: key pressing detect
}

Jetzt stellen sich mir einige Fragen. (Ich arbeite mit cvavr und
simuliere das ganze mit einer schleife im avr studio))
1.Durchlauf:

i = 00000000 ^ ~(11111110) = 1
ct0 = ~(00000000 & 00000001) = ~(00000000) = 11111111
ct1 = 11111111 ^ 00000000 & 00000001 = 11111111 (weil & höhere
Priorität)
bzw = (11111111 ^ 00000000) & 00000001  = 00000001

Ob mit oder ohne Klammersetzung, die Bits Null der Bytes ct0 und ct1
sind bei mir schon bei ersten Durchlauf High, was zur Folge hat, dass
auch key_state nach dem ersten durchlauf High wird!! :-(
Ich bin leider noch auf keinen grünen Zweig gekommen, vielleicht kann
mir das jemand genau erklären, wär super!

Vielen Dank im Voraus!

Mfg Jürgen
Autor: Juergen (Gast)
Datum: 17.09.2008 15:00

Hab den Code jetzt folgendermaßen umgeschrieben:
  i = key_state ^ ~KEY_INPUT;  // key changed ?
  ct0 = (~ct0) & i ;    // reset or count ct0
  damit korrekt mit i verundet werden kann
  ct1 = ((ct0 ^ ct1) ^ i) & i;    // reset or count ct1
  Alt: ct1 = ct0 ^ ct1 & i;
  ct1 UND i würde Null ergeben, ^ ct0 ergibt dann 00000001;
  Falls XOR vor UND kommen würde, was ja glaub ich nicht so ist (oder?)
  würde ebenfalls 00000001 herauskommen. Daher statt
  ct1 = ct0 ^ ct1 UND i;
  ct1 = ct0 ^ ct1 XOR i; Das zusätzliche &
  UND i bewirkt ein rücksetzen des
  Bits im ct1 Byte wenn die entprellung fertig ist!
  i &= ct0 & ct1;    // count until roll over
  key_state ^= i;    // then toggle debounced state
  key_press |= key_state & i;  // 0->1: key pressing detect

Ich hab das ganze getestet und es funktioniert einwandtfrei! :-)
Trotzdem bitte ich euch um eine kurze erklärung warum der Code im obigen
Post nicht funktioniert (bzw wo meine unfähigkeit liegt den Code richtig
zu verwenden, weil richtig is er sicher da er von profis ist! ;-)
Übrigends, Ausgangssituation: Alles Null, dann ein Taster (bit 0)
gedrückt.

Jürgen

PS: Ich bin noch kein Profi, also seid nicht zu streng! ;-)
Autor: Peter Dannegger (peda)
Datum: 17.09.2008 15:45

Juergen wrote:
> Ob mit oder ohne Klammersetzung, die Bits Null der Bytes ct0 und ct1
> sind bei mir schon bei ersten Durchlauf High, was zur Folge hat, dass
> auch key_state nach dem ersten durchlauf High wird!! :-(

In der Regel schaltet man ja Geräte nicht mit gedrückten Tasten ein, so
daß es kaum auffällt.

Du hast natürlich recht, CT0 und CT1 müßten mit 0xFF initialisiert
werden, damit erst nach 4 Durchläufen die Übernahme erfolgt.

Noch besser wäre es, auch key_state mit 0xFF zu initialisieren. Dann
werden bereits beim Einschalten gedrückte Tasten ignoriert.


Peter
Autor: Juergen (Gast)
Datum: 18.09.2008 09:48

Stimmt! War meinerseits nicht optimal getestet, da ich KEY_INPUT immer
"gedrückt" initialisiert habe, sonst hätte ich das selbst merken können.
Allerdings freut es mich sehr zu so einem genialen Code einen nützlichen
Beitrag geleistet zu haben! :-)

Jürgen
Autor: Martin Schneider (Gast)
Datum: 18.09.2008 09:57

> In der Regel schaltet man ja Geräte nicht mit gedrückten Tasten ein, so
> daß es kaum auffällt.

Das kommt schon vor:
Gerät geht bei nicht-Benutzung/per Kommando schlafen und
wird per Tastendruck (IRQ) geweckt bzw. eingeschaltet.

In diesem Einschaltvorgang wird anhand der gedrückten Tasten festgelegt,
was passiert.

Mein Heizgriffregler hat z.B. zwei Tasten, eine davon fungiert via INT0
als Ein-Taste. Wenn die zweite beim Einschalten gedrückt war, kommt man
in den Programmiermodus für Heizleistung und Anheizzeit, wenn nicht,
ist man im Standardbetrieb - Anheizen für programmierte Zeit und danach
programmierte Heizleistung (z.Z. 40% vom Maximum).

Läuft übrigens mit den hier beschriebenen Entprellroutinen.. ;-)

Ahoi, Martin
Autor: Tom Baer (tombaer)
Datum: 21.09.2008 19:12
Angehängte Dateien:

Hallo!

Ich habe versucht, Peter Danneggers ASM-Code aus Beitrag Nr. 3, in
BASCOM umzusetzen.

Man kann zwar ASM-Code einbinden, ich wollte es aber trotzdem erst
einmal in BASCOM versuchen.

Bei mir funktioniert das Basic-Programm ohne Probleme.

Vielleicht können andere BASCOM-Freunde etwas damit anfangen.

Vielen Dank an Peter Dannegger.

Gruß Tom
Autor: Florian Patzer (eckel)
Datum: 26.09.2008 17:38

Hab nochmal ne Frage zur ASM Routine von peter.

und zwar würde ich gerne zum besseren verständnis erklärt haben was mir
nun das register key_state sagt.

MfG Florian
Autor: Kachel - Heinz (Gast)
Datum: 26.09.2008 17:46

> und zwar würde ich gerne zum besseren verständnis erklärt haben was mir
> nun das register key_state sagt.

Key_state wird von der Routine benötigt, um den entprellten Zustand der
Tasten (also den gegenwärtig für den Controller gültigen Zustand) zu
speichern und mit den neu eingelesenen Bitmustern zu vergleichen (EXOR).

Key_state kann vom Programm (nur lesend) genutzt werden, um
Tastenzustände zu überprüfen.

Also key_press meldet (und merkt sich) neue Tastendrücke, key_state
zeigt den gegenwärtigen (aber entprellten, daher leicht verzögerten)
Zustand. Dies kann z.B. für "Shift-Tasten" genutzt werden, also zur
Überprüfung, ob während eines Tastendruckes auch eine weitere Taste
gedrückt war.

KH
Autor: Florian Patzer (eckel)
Datum: 26.09.2008 17:49

Also ist eine 1 im Key_state register = eine enprellte gedrückte taste?

Wie schauts eigentlich mit der 1-->0 erkennung aus kann man die ähnlich
realisieren? Gibt es evtl. auch schon beispiele?
Autor: Florian Patzer (eckel)
Datum: 26.09.2008 17:52

Hab grad nochmal geschaut, wenn im key_state register eine 0 auftaucht
ist der jeweilige taster betätigt und entprellt. wenn man in loslässt,
erscheint nach 2 erkannten "loslassen an dieser stelle im register
wieder eine 1.
Autor: Hannes Lux (hannes)
Datum: 27.09.2008 11:42

Florian Patzer wrote:
> Also ist eine 1 im Key_state register = eine enprellte gedrückte taste?

Ja, wobei der Zustand nur von der Entprellroutine verändert werden darf.

> Wie schauts eigentlich mit der 1-->0 erkennung aus kann man die ähnlich
> realisieren? Gibt es evtl. auch schon beispiele?

Ich nutze Peters genialen Algorithmus auch regelmäßig, allerdings mit
meinen eigenen Variablennamen. "tas" (TAstenStatus) entspricht
key_state, tfl (TastenFLags) entspricht key_press, die Prellzähler
heißen bei mir tz0 und tz1. Da im Fall des Beispiels nur die unteren 4
Bits des Tastenports tap (PinX) mit Tasten belegt sind, werden die
loslass-Merker gleich in die oberen Bits von tfl gelegt. Sind mehr
Tasten zu entprellen, dann ist für die Loslass-Merker natürlich ein
eigenes Register zu verwenden.

Im vorliegenden Fall läuft die Routine auch nicht im Interrupt, sondern
als Job der Mainloop, durch Timer und Jobflag synchronisiert.
Tastenabfrage:  ;Entprell-Algorithmus geklaut bei Peter Dannegger...
 ;die Tasten liegen auf den untern 4 Bits
 cbr flags,1<<upsync;Jobflag löschen (Routine läuft als Mainloop-Job)
 in tmp,tap         ;Tastenport einlesen (gedrückt=L)
 com tmp            ;invertieren (gedrückt=H)
 andi tmp,alltast   ;ungenutzte Bits ausblenden
 eor tmp,tas        ;nur Änderungen werden H
 and tz0,tmp        ;Prellzähler unveränderter Tasten löschen (Bit0)
 and tz1,tmp        ;Prellzähler unveränderter Tasten löschen (Bit1)
 com tz0            ;L-Bit zählen 0,2,->1, 1,3,->0
 eor tz1,tz0        ;H-Bit zählen 0,2,->tz1 toggeln
 and tmp,tz0        ;Änderungen nur dann erhalten, wenn im Prellzähler
 and tmp,tz1        ;beide Bits gesetzt sind (Zählerstand 3)
; Loslasserkennung 
 push tmp           ;Kopie
 and tmp,tas        ;nur die Tasten erhalten, die jetzt losgelassen wurden
 ;entweder:
 swap tmp           ;ins andere Nibble (das ist ja frei)
 or tfl,tmp         ;Bits im anderen Nibble setzen (alternativ)
 ;oder:
; or tlf,tmp         ;Bits im anderen Register setzen (alternativ)
 ;---
 pop tmp            ;alten Wert wiederherstellen
 eor tas,tmp        ;erhaltene Änderungen toggeln alten (gültigen) Tastenstatus
 and tmp,tas        ;nur (neu) gedrückte Tastenbits bleiben erhalten
 or tfl,tmp         ;und zugehörige Bits setzen (gelöscht wird nach Abarbeitung)
 ;in "tas" steht jetzt der gültige Tastenzustand,
 ;in "tfl" die Flags der neu gedrückten, noch nicht abgearbeiteten Tasten.
 ;wobei im unteren Nibble die Tastendrücke liegen und im oberen Nibble
 ;die Loslass-Ereignisse.

;und hier passt noch die Repeat-Erweiterung rein, die allerdings zusammen
;mit der Loslasserkennung nicht sonderlich sinnvoll ist.

 ret                ;fertig...

Ich hoffe, das Prinzip ist zu erkennen.

...
Autor: Florian Patzer (Gast)
Datum: 28.09.2008 17:53

Also wie gesagt hab mir die routine von P.D. nochmal angeschaut und
meine das der übergang taste gedrückt-->losgelassen auch entprellt wird.
Kann mir das jemand bestätigen?

MfG Florian
Autor: Kachel - Heinz (Gast)
Datum: 28.09.2008 19:57

Ja, es wird das Drücken und das Loslassen entprellt.

Es wird in Originalversion nur beim Drücken die Flanke separiert und per
Key-press gemeldet (was ja auch sinnvoll ist).

Manchmal kann es aber auch sinnvoll sein, dass man auch die
Loslass-Flanke meldet, dann greift die Erweiterung, die Hannes
vorgestellt hat.

Und manchmal (z.B. beim Verändern von Zahlenwerten) braucht man eine
Funktion für Autorepeat. Dann greift die Erweiterung, die im Wiki
vorgestellt wurde Entprellung.

KH
Autor: Jupp (Gast)
Datum: 28.09.2008 20:05

Ich glaube, jetzt kann man den Thread eigentlich mal sperren denn es
wurde doch alles gesagt.
Autor: Mathias O. (m-obi)
Datum: 06.11.2008 00:51

hab mir mal den C-Code von Peter angeschaut. Hab aber noch nicht soviel
Erfahrung in C. Wie ist denn das jetzt. Ich habe zum Beispiel PB2 und
möchte den Eingang entprellt haben, wo füge ich den ein. Und wofür ist
reg51.h da und was heißt
#pragma cd pl(9999)
?????
Autor: Mathias O. (m-obi)
Datum: 07.11.2008 00:19

kann mir denn niemand helfen???
Autor: Werner B. (werner-b)
Datum: 07.11.2008 06:57

Wer lesen kann (und es auch tut) ist klar im Vorteil.

> (Keil C51)
Autor: Mathias O. (m-obi)
Datum: 07.11.2008 08:24

sorry aber ich kenn nicht Keil C51.
Autor: Werner B. (werner-b)
Datum: 07.11.2008 08:36

Mathias Obetzhauser wrote:
> sorry aber ich kenn nicht Keil C51.

Eben, der Codeausschnitt ist aber dafür.

Für AVR wird hier MEHRFACH auf den Wiki Entprellung verwiesen.
Autor: Peter Dannegger (peda)
Datum: 07.11.2008 08:41

Mathias Obetzhauser wrote:
> Und wofür ist
> reg51.h da

Das ist das Include um den AT89C51 (Derivat der 8051-Familie)
auszuwählen.

Beim 8051 muß der Compiler nicht wissen, welcher Typ benutzt wird, da
alle 8051 kompatibel sind.
Es reicht daher völlig, die SFRs des verwendeten Derivats per Include
bekannt zu machen.


> und was heißt
#pragma cd pl(9999)
?????

Ein Pragma des Keil C51, um den erzeugten Code in das Listing
einzufügen.


Eine WINAVR-Routine findest Du z.B. hier:

Beitrag "Universelle Tastenabfrage"


Ansonsten über Entprellung in Artikelsammlung/Tutorial nachlesen.


Peter
Autor: Matthias K. (malterix)
Datum: 16.12.2008 16:27
Angehängte Dateien:

Hallo Peter,

deine Entprellroutine hat mich so begeistert, dass ich sie unbedingt
ganz genau verstehen wollte. Daher habe ich mir alle Erklärungen dieses
Forums durchgelesen und die Logik der C-Quelltextzeilen analysiert (s.
Anhang).

Mir ist aufgefallen, dass gemäß einer deiner Erläuterungen der Zähler
nur inkrementiert werden soll, wenn sich der aktuell erfasste
Tastenzustand vom letzten konstanten Zustand unterscheidet. Anderenfalls
sagst du, dass der Zähler zurück gesetzt wird. Ich nehme mal an, dass du
damit "zurück auf 00" meinst. Die Analyse deines Codes führt aber nicht
immer zum Zurücksetzen des Zählers, wenn der Tastenzustand und der
konstante Zustand sich gleichen.

Ich habe im Anhang rot markiert, was ich gemäß deiner Erklärung erwarten
würde. Alles schwarz geschriebene ist das Ergebnis der Auswertung deiner
Codezeilen. Bitte wirf mal einen Blick drauf und sage mir, ob ich einen
Denkfehler mache, oder ob die rot markierten Teile noch besser dem
entsprechen, was deine Entprellroutine leisten soll.
Autor: Peter Dannegger (peda)
Datum: 16.12.2008 18:26

Matthias K. wrote:
> Mir ist aufgefallen, dass gemäß einer deiner Erläuterungen der Zähler
> nur inkrementiert werden soll, wenn sich der aktuell erfasste
> Tastenzustand vom letzten konstanten Zustand unterscheidet. Anderenfalls
> sagst du, dass der Zähler zurück gesetzt wird. Ich nehme mal an, dass du
> damit "zurück auf 00" meinst.

Nein, der Zähler ist invertiert, also der Ruhezustand ist 11.

Dadurch brauche ich keine zusätzlichen Invertierungen, wenn er nach 4
Zählimpulsen wieder 11 ist und AND-verknüpt den entprellten Zustand
umdreht.


Peter
Autor: Matthias K. (malterix)
Datum: 17.12.2008 11:18

> Nein, der Zähler ist invertiert, also der Ruhezustand ist 11.

Okay, also zählst du 11, 00, 01, 10, 11, ...
Wenn ich die Logik richtig verstehe, wird der Zähler nur inkrementiert,
wenn i = 1 ist (aktuell erfasster Tastenzustand unterscheidet sich vom
letzten konstanten Zustand).
Mich verwirren aber die unterschiedlichen Folgezustände deiner Logik,
wenn i = 0 ist. Da würde ich für jeden Folgezustand ein Rücksetzen auf
11 erwarten. Gemäß meiner Logiktabelle sehe ich aber folgendes für deine
Logik:

ct0 = ~( ct0 & i ) und ct1 = ct0 ^ (ct1 & i)

ct1  ct0  i   ct1+  ct0+
------------+-----------
1    1    0 | 1     1      ist erwartungsgemäß
1    0    0 | 0     1      warum 01 und nicht 11???
0    1    0 | 1     1      ist erwartungsgemäß
0    0    0 | 0     1      warum 01 und nicht 11???

Matthias
Autor: Peter Dannegger (peda)
Datum: 17.12.2008 11:33

Matthias K. wrote:
> ct0 = ~( ct0 & i ) und ct1 = ct0 ^ (ct1 & i)

Wenn i = 0:
ct0 = ~( ct0 & i )
ct0 = ~( x & 0 )
ct0 = ~( 0 )
ct0 = 1

und:
ct1 = ct0 ^ (ct1 & i)
ct1 = 1 ^ ( x & 0 )
ct1 = 1 ^ ( 0 )
ct1 = 1


Peter
Autor: Matthias K. (malterix)
Datum: 17.12.2008 11:54

Besten Dank, das Problem lag in meiner Logiktabelle. Da ct0 als erstes
berechnet wird, muss ich den Folgezustand von ct1 natürlich in
Abhängigkeit von ct0+ berechnen. Ich habe aber dafür ct0 genommen.
So wäre es richtig:

ct0  i   ct0+
-------+-----
1    0 | 1
0    0 | 1

ct1  ct0+  i   ct1+  ct0+(bleibt unverändert)
-------------+-----------
1    1    0  | 1     1
0    1    0  | 1     1

Matthias
Autor: Matthias K. (malterix)
Datum: 17.12.2008 15:41
Angehängte Dateien:

Ich habe meinen Denkfehler im Anhang korrigiert. Hier findet ihr jetzt
die Herleitung aller logischen Verknüpfungen des C-Codes.

Matthias
Autor: Peter (Gast)
Datum: 27.12.2008 16:00

@ Peter Dannegger,
habe mit großem Interesse diese Thread und auch noch weitere Beiträge
von Dir gelesen. Besteht die Möglichkeit über direkten Email-Kontakt mit
Dir in Verbindung zu treten ?
Falls ja, sende bitte eine kurze Mail, ich nehme dann Kontakt zu Dir
auf.

Gruß, Peter [ GrzeschikP (ät) AOL Punkt COM ]
Autor: udo (Gast)
Datum: 21.02.2009 00:08

Hallo Experten,

bitte helft mir mal:

Das passende Assembler-Programm "get8keyb.asm" funktioniert auf meinem
MEGA16.

Aber das entsprechende "C-Programm" krieg ich nicht auf die Reihe. Bin
noch nicht solange in "C" unterwegs.

Meine Frage:

Von welchen Stellen im Programm werden die Funktionen
u8 get_key_press( u8 key_mask )

u8 get_key_rpt( u8 key_mask )

und

u8 get_key_short( u8 key_mask )

aufgerufen? Ich weiß nicht, wie der Programmzähler dort hin kommt. In
diesem Thread konnte ich das nicht rauslesen.
Hier noch einmal das Programm von Peter

/************************************************************************/
/*                                                                      */
/*    Debouncing 8 Keys        */
/*        Sampling 4 Times        */
/*    With Repeat Function        */
/*                                                                      */
/*     Author: Peter Dannegger                                 */
/*          danni@specs.de                                  */
/*                                                                      */
/************************************************************************/
// Target: ATMega48

#include <io.h>
#include <interrupt.h>

typedef unsigned char  u8;
typedef signed short  s16;

#define  XTAL    8000000L        // 8MHz

#define KEY_PIN    PINB
#define KEY0    0
#define KEY1    1
#define KEY2    2
#define  KEY3    3

#define LED_DDR    DDRC
#define LED_PORT  PORTC
#define LED0    0
#define LED1    1
#define LED2    2

#define REPEAT_MASK  (1<<KEY3^1<<KEY2^1<<KEY1^1<<KEY0)
#define REPEAT_START  10          // after 100ms
#define REPEAT_NEXT  20          // every 200ms

u8 key_state;                // debounced and inverted key state:
                    // bit = 1: key pressed
u8 key_press;                // key press detect

u8 key_rpt;                // key long press and repeat


ISR( TIMER0_COMPA_vect )          // every 10ms
{
  static u8 ct0, ct1, rpt;
  u8 i;

  i = key_state ^ ~KEY_PIN;          // key changed ?
  ct0 = ~( ct0 & i );              // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);          // reset or count ct1
  i &= ct0 & ct1;              // count until roll over ?
  key_state ^= i;              // then toggle debounced state
  key_press |= key_state & i;          // 0->1: key press detect

  if( (key_state & REPEAT_MASK) == 0 )      // check repeat function
     rpt = REPEAT_START;          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
   }
}



u8 get_key_press( u8 key_mask )
{
  cli();                  // read and clear atomic !
  key_mask &= key_press;                            // read key(s)
  key_press ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}

u8 get_key_rpt( u8 key_mask )
{
  cli();                  // read and clear atomic !
  key_mask &= key_rpt;                              // read key(s)
  key_rpt ^= key_mask;                              // clear key(s)
  sei();
  return key_mask;
}

u8 get_key_short( u8 key_mask )
{
  cli();                // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}

u8 get_key_long( u8 key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}

int main( void )
{
  TCCR0A = 1<<WGM01;              // Mode 2: CTC
  TCCR0B = 1<<CS02^1<<CS00;            // divide by 1024
  OCR0A = (s16)(XTAL / 1024.0 * 10e-3 - 0.5);    // preload for 10ms
  TIMSK0 = 1<<OCIE0A;              // enable timer interrupt

  LED_PORT = 0xFF;
  LED_DDR = 0xFF;
  sei();

  for(;;){                  // main loop

    switch( get_key_long( 1<<KEY1^1<<KEY0 )){

      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;

      case 1<<KEY1:          LED_PORT ^= 1<<LED1; break;

      case 1<<KEY1^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
    }

    switch( get_key_rpt( 1<<KEY3^1<<KEY2 )){

      case 1<<KEY2:          LED_PORT ^= 1<<LED0; break;

      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;

      case 1<<KEY3^1<<KEY2:  LED_PORT ^= 1<<LED2; break;
    }
  }
}
Autor: Tobi (Gast)
Datum: 21.02.2009 13:42
Angehängte Dateien:

Hallo,

habe mal Peters Original der individuellen Tastenabfrage angehangen.

Im Main wird nachgesehen, ob eine entsprechende Taste gedrückt wurde.
Dies geschieht mit dem if, der Abfrageart der Taste und der
entsprechenden Taste die definiert wurde.

//Für kurzen Tastendruck:
if( get_key_short( 1<<KEY1 ))
{
      -Tu was
}
//Für langen Tastendruck:
if( get_key_long( 1<<KEY1 ))
{
      -Tu was
}
//Für die Repeat Funktion:
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 ))
{
      -Tu was
}

Hoffe, konnte helfen.

Gruß Tobi
Autor: Bach Le (bleviet)
Datum: 23.04.2009 13:32

Hi,
ich hab eine Verständnis Frage. In der Beschreibung von Matthias K.
(malterix) hat er geschrieben:

"Da die Tasten low-activ sind, bedeutet eine 1 bei KEY_INPUT: Taste
gedrückt, während eine 1 bei key_state bedeutet: Taste nicht
gedrückt"

Ist das nicht so, dass der Pinpegel beim Tastendrücken auf 0 gezogen
wird? Das bedeutet, das entsprechende Bit im PINx-Register ist 0 => eine
0 bei KEY_INPUT = Taste gedrückt

Gruß
Bach
Autor: Matze Niemand (hupe123)
Datum: 15.05.2009 14:59

Hi,

ich benutze das Bullet-Proof-Protokoll schon erfolgreich mit 8 Tasten.
Nur leider brauche ich jetzt eine neunte und zehnte.
Kann mir mal jemand nen Tipp geben, wie ich das auf einen zweiten Port
erweitern kann.

Gruß und Danke,
die Hupe:)
Autor: yalu (Gast)
Datum: 15.05.2009 15:11

Den Code einfach ein zweites Mal mit geänderten Variablen- und
Port-Namen anhängen, das Ganze evtl. noch etwas nachoptimieren.

Oder: Alle 8-Bit-Variablen in 16 Bit ändern und beim Einlesen der Ports
die beiden 8-Bit-Werte zu einem 16-Bit-Wert zusammenfassen. Dies ist für
die C-Variante sicher der elegantere Weg.
Autor: Peter Stegemann (pst)
Datum: 15.05.2009 15:13

Irgendwie habe ich das nie richtig verstanden, ich pruefe die
Tasten-Ports einfach jede ms und es funktioniert...?
Autor: Micha (Gast)
Datum: 15.05.2009 15:15

>Kann mir mal jemand nen Tipp geben, wie ich das auf einen zweiten Port
>erweitern kann.
Mein erster Ansatz wäre es aus den benötigten Registern (key_xxx, ct0,
ct1, i, ...) Integer (16bit) zu machen. Zusätzlich muss dann noch die
Zeile
i = key_state ^ ~KEY_INPUT
entsprechend angepasst werden. Damit sollte es im Wesentlichen schon
funktionieren. Es kann aber sein, dass ich noch eine Kleinigkeit
vergessen habe...

Siehe auch yalus Beitrag oben.
Autor: DL4BM (Gast)
Datum: 07.09.2009 09:57
Angehängte Dateien:

Welche Version für Assembler ist die aktuelle, die oben angegebene oder
die im Tutorial ? Worin unterscheiden sie sich ?

Ich habe gestern versucht, die im Tutorial aufgeführte Fassung im
Debugger vom AVR Studio nachzuvollziehen und gleichzeitig auf Papier.
Die Werte auf Papier erhielt ich zwar auch im AVR Studio, aber sie
ergeben keinen Sinn (nach dem 4. Durchlauf ist key_press immer noch 0).

Wo findet sich in der Tutorial-Fassung ein Zähler, der bis 4 zählt ? Die
einzigen Variablen, die in jedem Durchlauf übernommen werden, sind
key_old, key_state und key_press, iwr0 und iwr1 werden jedesmal
verworfen (werden sogar extra auf dem Stack geschrieben) ???

P.S.: Die oben angegebene Version liefert im Debugger von AVR Studio
4.12 die Meldung 'Invalid opcode 0xffff at address 0x000899 und springt
nicht mehr aus der Interruptroutine heraus).

Wäre klasse, wenn mir jemand weiterhelfen kann.
Autor: Hannes Lux (Gast)
Datum: 07.09.2009 10:10

> iwr0 und iwr1 werden jedesmal verworfen

Verworfen??? - Das sind doch aber die 8 Zweibit-Zähler...

Bit 0 jedes der 8 Zähler befindet sich in iwr0, Bit 1 in iwr1. Gezählt
wird global (bzw. static), die Werte müssen also zwischen den Aufrufen
der Routine erhalten bleiben. Wenn Du die löscht (verwirfst,
überschreibst), dann kann da ja nix zählen.

...
Autor: Peter Dannegger (peda)
Datum: 07.09.2009 10:13

Die Assemblerversion im Tutorial ist nur mit 2-fach Abtastung.

IWRX soll einfach nur heißen Interrupt_Working_Register_X, d.h. das sind
zerstörbare Arbeitsregister nur für Interrupthandler.
Wenn man im Main und Interrupt unterschiedliche Register verwendet,
braucht man kein PUSH/POP. Das PUSH/POP IWR0/1 ist also Unsinn. Ebenso
beim Register zum Sichern des SREG.


4-Abtastung in Assembler sähe so aus (C-Zeilen als Kommentar):

.include "1200def.inc"

.def    deb_ct0 = r2
.def    deb_ct1 = r3
.def    deb_i = r4
.def    deb_keystate = r5
.def    deb_keypress = r6


debounce:
        ;i = key_state ^ ~KEY_PIN;

        in      deb_i, PINB
        com     deb_i
        eor     deb_i, deb_keystate

        ;ct0 = ~( ct0 & i );

        and     deb_ct0, deb_i
        com     deb_ct0

        ;ct1 = ct0 ^(ct1 & i);

        and     deb_ct1, deb_i
        eor     deb_ct1, deb_ct0

        ;i &= ct0 & ct1;

        and     deb_i, deb_ct0
        and     deb_i, deb_ct1

        ;key_state ^= i;

        eor     deb_keystate, deb_i     ;deb_keystate = debounced key state

        ;key_press |= key_state & i;

        and     deb_i, deb_keystate
        or      deb_keypress, deb_i     ;deb_keypress = key pin has changed from 1->0

        ret



Peter
Autor: DL4BM (Gast)
Datum: 07.09.2009 12:23

Vielen Dank für die Antworten!

@Hannes Lux:
Sie sind der Auslöser, daß ich mir diesen Code antue ;-) Eigentlich gehe
ich gerade Ihren Code zum R/C-Sender durch und bin dabei auf die
Tastenentprellung gestoßen. Wenn es nicht stört, würde ich mich nochmals
gesondert mit Fragen zu Ihrem Code an Sie wenden.

@Peter Dannegger / all:
Die zwei 8-Bit-Zähler iwr0 und iwr1 stelle ich mir bislang wie folgt
vor:

1. Zyklus:
iwr0: 00 00 00 00
iwr1: 00 00 00 00

2. Zyklus:
iwr0: 00 00 10 00
iwr1: 00 00 00 00

3. Zyklus:
iwr0: 00 00 00 00
iwr1: 00 00 10 00

4. Zyklus:
iwr0: 00 00 10 00
iwr1: 00 00 10 00 (jetzt wird key_press gesetzt)
            ^
(für den Fall, daß Bit 3 bei PIND gesetzt ist)

Ist das soweit richtig ?
Ich werde mir das ganze heute abend, nach der Arbeit, nochmals zu Gemüte
führen.
Autor: Hannes Lux (Gast)
Datum: 07.09.2009 13:35

> Wenn es nicht stört, würde ich mich nochmals
> gesondert mit Fragen zu Ihrem Code an Sie wenden.

Nur zu. Antwort kann aber etwas dauern, da ich nicht immer online bin.

> iwr0 und iwr1 ...

Das war natürlich Blödsinn von mir, sorry, ich war auf die
Prellzähler-Bytes aus...

...
Autor: DL4BM (Gast)
Datum: 07.09.2009 17:20

Mal eine ganz dumme Frage:
Worauf bezieht sich "4-Abtastung" ? Bedeutet das, daß die
Interrupt-Routine viermal 'erfolgreich' durchlaufen werden muß oder wird
innerhalb eines Interrupts viermal abgetastet (PINB wird aber ja nur
einmal eingelesen?) ?
Autor: Hannes Lux (Gast)
Datum: 07.09.2009 18:11

> Bedeutet das, daß die
> Interrupt-Routine viermal 'erfolgreich' durchlaufen werden muß

Genau!
Bei Durchlaufen erhöhen "erfolgreiche" Bits ihren Zählerstand (COM,
EOR), während "nicht erfolgreiche Bits" ihren Zählerstand löschen (AND,
AND).

Hier mal zum Vergleich eine anders kommentierte Version, vielleich
helfen die anderen Kommentare beim Verständnis:
Tastenabfrage:  ;Entprell-Algorithmus geklaut bei Peter Dannegger...
 cbr flags,1<<entprell ;Jobflag löschen
 in tmp,tap         ;Tastenport einlesen (gedrückt=L)
 com tmp            ;invertieren (gedrückt=H)
 andi tmp,alltast   ;ungenutzte Bits ausblenden
 eor tmp,tas        ;nur Änderungen werden H
 and tz0,tmp        ;Prellzähler unveränderter Tasten löschen (Bit0)
 and tz1,tmp        ;Prellzähler unveränderter Tasten löschen (Bit1)
 com tz0            ;L-Bit zählen 0,2,->1, 1,3,->0
 eor tz1,tz0        ;H-Bit zählen 0,2,->tz1 toggeln
 and tmp,tz0        ;Änderungen nur dann erhalten, wenn im Prellzähler
 and tmp,tz1        ;beide Bits gesetzt sind (Zählerstand 3)
; Loslasserkennung in diesem Programm deaktiviert
; push tmp           ;Kopie
; and tmp,tas        ;nur die Tasten erhalten, die jetzt losgelassen wurden
; swap tmp           ;ins andere Nibble (das ist ja frei)
; or tfl,tmp         ;Bits im anderen Nibble setzen (alternativ)
; or tlf,tmp         ;Bits im anderen Register setzen (alternativ)
; pop tmp            ;alten Wert
 eor tas,tmp        ;erhaltene Änderungen toggeln alten (gültigen) Tastenstatus
 and tmp,tas        ;nur (neu) gedrückte Tastenbits bleiben erhalten
 or tfl,tmp         ;und zugehörige Bits setzen (gelöscht wird nach Abarbeitung)
 ;in "tas" steht jetzt der gültige Tastenzustand,
 ;in "tfl" die Flags der neu gedrückten, noch nicht abgearbeiteten Tasten...
Tastendauer:
 mov tmp,tas        ;Tastenzustand kopieren
 andi tmp,wietast   ;nur Tasten mit Wiederholfunktion stehen lassen
 tst tmp            ;ist eine Taste betätigt?
 breq Tastendauer0  ;nein, Dauer auf Startwert...
 dec twz            ;ja, Zähler runter
 brne Tastenabfrage_e   ;Dauer abgelaufen? - nein...
 or tfl,tmp         ;ja, noch aktive Tasten übernehmen (alternativ)
; or twf,tmp         ;ja, noch aktive Tasten übernehmen (alternativ)
 ldi twz,twz1       ;und Zähler auf Wiederholwert setzen
Tastenabfrage_e:

...
Autor: Peter Dannegger (peda)
Datum: 07.09.2009 22:15

DL4BM schrieb:
> Mal eine ganz dumme Frage:
> Worauf bezieht sich "4-Abtastung" ?

Sollte 4-fach Abtastung heißen.

Also 4-mal hintereinander muß der gleiche Tasterzustand erkannt worden
sein, damit er übernommen wird.


Peter
Autor: DL4BM (Gast)
Datum: 08.09.2009 22:08

Die Funktionsweise von Hannes` Code kann ich jetzt im Debugger
nachvollziehen, die Entprellung funktioniert tatsächlich...
Faszinierend!
Der im Tutorial wie auch im Artikel über Entprellung angegebene Code
funktioniert bei mir jedoch nicht, auch nach zehnmaligem Durchlaufen
ändert sich der Zustand von key_press nicht (bei dem Code im
Entprellungs-Artikel ist zudem der Stackpointer nicht initialisiert).
Autor: Hannes Lux (Gast)
Datum: 08.09.2009 23:07

> ist zudem der Stackpointer nicht initialisiert

Es gibt inzwischen einige (neuere) AVRs, die den SP beim Reset
automatisch initialisieren.

> ...funktioniert bei mir jedoch nicht...

Dazu kann und werde ich nichts sagen, das Tutorial ist nicht gerade
meine erste Anlaufstelle. Ich habe den Algorithmus aus dem Anhang des
ersten Beitrags dieses Threads hier, also von hier:
Beitrag "Tasten entprellen - Bulletproof"
http://www.mikrocontroller.net/attachment/1925/Get8keyb.asm

Ich habe die Routine allerdings an meinen (nicht gerade professionellen)
Stil angepasst, sowohl die Variablen-Namen als auch die Kommentare. Da
ich den Algorithmus genial finde, nenne ich im Kommentar auch
grundsätzlich den Autor, denn ich will mich nicht mit fremden Federn
schmücken. Meist lasse ich diese Routine auch nicht direkt im Timer-Int
laufen, sondern in einem Job der Mainloop, der vom Timer (nebenbei)
synchronisiert wird. Ich habe auch Varianten im Einsatz, wo die
Variablen im SRAM gehalten werden weil mehrere Tastenports (an
Schieberegistern) entprellt werden müssen. Die Routine hat sich bei
meinen Basteleien in verschiedensten Varianten bestens bewährt. Deshalb
hier nochmal ein besonderes Dankeschön an Peter.

...
Autor: martin (Gast)
Datum: 17.01.2010 22:08

Hallo,

ich verwende die Entprell-Routine von Peter und möchte bei zwei Tastern
zwischen kurzem und langem Drücken unterscheiden, um diese mehrfach zu
verwenden.
Das funktioniert aber nur teilweise. Ich habe exakt das Beispiel unten
aus http://www.mikrocontroller.net/articles/Entprellung kopiert.
REPEAT_START und REPEAT_NEXT sind beide 50, also 500 ms. Der Interrupt
kommt alle 10 ms.
Das Problem ist, dass Gelegentlich ein kurzes Drücken als langes erkannt
wird. Es passiert z.B. bei Tastendrücken von 125 ms (mit DSO gemessen),
dass diese mit get_key_long erkannt werden.

Hast jemand eine Idee, woran das liegen kann? Ich bin verzweifelt. :-(

Gruß
martin
Autor: Peter Dannegger (peda)
Datum: 18.01.2010 00:03

Die lang/kurz/repeat-Funktionen sind nicht unabhängig, d.h. es darf
immer nur eine Taste gleichzeitig gedrückt werden, die für diese
Funktionen enabled ist.

Ich würde auch als zu kompliziertes Bedienkonzept empfinden, wenn das
nötig wäre. Der Mensch ist ja kein guter Multitasker.


Peter
Autor: Hannes Lux (hannes)
Datum: 18.01.2010 12:26

@Martin:

Was hältst Du davon, mit einer Art Shift-Taste(n) zu arbeiten? Das wäre
dann eine Taste, bei der Du Key_Press nicht auswertest, dafür aber beim
Auswerten der anderen Tasten Key_State der Shift-Taste(n) überprüfst?

Nur mal so als Denkanstoß.

Gelegentlich habe ich Peters Entprellung so erweitert, dass sie auch auf
Loslassen der Tasten prüft. Dies nutze ich gerne, um eine Taste sowohl
als Shift-Taste, als auch als direkte Taste nutzen zu können (sinnvoll
z.B. bei Drehgebertasten). Jede andere Taste (oder das Drehen des
Drehgebers) löscht das Shift-Bit in Key_Press, Als Normaltaste wird die
Shift-Taste primär auf Key_Lost geprüft, erst dann auf Key_Press
geprüft, das ja bei Betätigung einer anderen Taste (oder des Drehgebers)
bereits wieder entwertet wurde. So kann man z.B. den Drehgeber in 2
Ebenen nutzen (ungedrückt = kleines Inkrement, gedrückt = großes
Inkrement), den Taster alleine aber zum Sichern des Wertes. Oder man
verändert bei gedrückter Taste den ausgewählten Wert und wählt bei
unbetätigter Taste die Werte aus der Liste aus (Array-Index).
Tastendruck ohne Drehen führt zum Speichern des Wertes im EEP.

Es sind recht viele Bedienkonzepte denkbar, bei vielen davon ist Peters
Entprellalgorithmus eine unverzichtbare Hilfe. Man muss ihn ja nicht
immer exakt so benutzen, wie das Tutorial beschreibt.

...
Autor: martin (Gast)
Datum: 18.01.2010 21:42

Hallo,

vielen Dank für die schnelle Antwort. Leider hat mein Kunde sich keine
Gedanken zur Bedienbarkeit gemacht und einfach 4 Tasten platziert. Die
zwei anderen sind schon anders belegt.
Die beiden Tasten, bei denen zwischen kurzem und langem drücken
unterschieden werden soll, werden nicht gleichzeitig betätigt. Das oben
beschriebene Problem taucht auch bei einer einzelnen Taste auf.

Gruß
Martin
Autor: Peter Dannegger (peda)
Datum: 18.01.2010 22:15

martin schrieb:
> Das oben
> beschriebene Problem taucht auch bei einer einzelnen Taste auf.

Zeig dochmal Dein Testprogramm.

Für die lang/kurz Erkennung müssen beide Funktionen auch oft genug
aufgerufen werden.
Werden sie nicht in der Zeit des Loslassens aufgerufen, kann das
Loslassen nicht erkannt werden und das Gedrückt-Bit bzw. Repeat-Bit
bleibt gesetzt.


Peter
Autor: Peter Dannegger (peda)
Datum: 20.01.2010 19:20

P.S.:
Da meine Mainloops immer sehr kurz sind, habe ich die
kurz/lang-Funktionen der Einfachheit halber als Mainroutine
implementiert.
Dann kann man sogar je nach Kontext zwischen lang/kurz und repeat
umschalten.

Wenn Deine Mainloop zu langsam läuft, kannst Du natürlich auch die
kurz/lang-Erkennung einfach mit in den Interrupt packen und dann in 2
zusätzlichen Variablen die Kurz- und Lang-Bits setzen.
Dann kannst Du die auch viel später im Main abfragen und löschen.

Wichtig ist dann aber, auch ne Maske zu definieren, welche Tasten
kurz/lang erkannt werden müssen, da damit die Drück/Repeat-Funktion für
diese Tasten nicht mehr geht.
Will man dann auch zwischen lang/kurz und repeat umschalten, muß diese
Maske eine Variable sein.


Peter

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!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichungen und Screenshots im PNG- oder GIF-Format hochladen.
Siehe Bildformate

Hinweis: der Originalbeitrag ist mehr als 6 Monate alt.
Mit dem Abschicken erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net