Forum: Projekte & Code Tasten entprellen - Bulletproof


von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

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

von Peter D. (peda)


Lesenswert?

P.S.:
Beschimpfungen statt sachlichen Kommentaren bitte per E-Mail direkt an 
mich richten, anstatt damit das Forum zu verunstalten.


Peter

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ups, da war ich wohl etwas zu hektisch.

Hier nochmal ohne Schreibfehler.


Peter

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

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


Peter

von Peter D. (peda)


Lesenswert?

Hier findet man die nötige Theorie dazu:

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


Peter

von mbj (Gast)


Lesenswert?

und das ganze, wenn ich nur eine taste habe, die einen interrupt
auslöst?

von Peter D. (peda)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

@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

von Andi (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

@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

von Peter D. (peda)


Lesenswert?

@Andi,

aber super von Dir, daß du es nicht nur stumpf abkopiert hast, sondern
auch versucht hast, es nachzuvollziehen und das erfolgreich.


Peter

von Andi (Gast)


Lesenswert?

@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

von Robert Nägele (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

@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

von Armin Kniesel (Gast)


Lesenswert?

@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

von Peter D. (peda)


Lesenswert?

4 mal.

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


Peter

von Armin Kniesel (Gast)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

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

von Dirk E. (Gast)


Lesenswert?

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

von emil (Gast)


Lesenswert?

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!

von Carsten (Gast)


Lesenswert?

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

von Thorsten (Gast)


Lesenswert?

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

von Hagen (Gast)


Lesenswert?

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

von buffy (Gast)


Lesenswert?

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

von Carsten (Gast)


Lesenswert?

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

von Peter Dannegger (Gast)


Lesenswert?

"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

von Carsten (Gast)


Lesenswert?

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

von Peter Dannegger (Gast)


Lesenswert?

@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

von ...HanneS... (Gast)


Lesenswert?

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

...

von Hagen (Gast)


Lesenswert?

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

von Carsten (Gast)


Lesenswert?

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

von ...HanneS... (Gast)


Lesenswert?

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.

...

von Carsten (Gast)


Lesenswert?

Ok, da gebe ich Dir Recht.

Generell sollte aber das Maskieren keine probleme für die Routine
bedeuten oder ?

von ...HanneS... (Gast)


Lesenswert?

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

...

von Carsten (Gast)


Lesenswert?

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.

von ...HanneS... (Gast)


Lesenswert?

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.

...

von Peter Dannegger (Gast)


Lesenswert?

"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

von Carsten (Gast)


Lesenswert?

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

von ...HanneS... (Gast)


Lesenswert?

@Peter:

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

...HanneS...

von pittbull (Gast)


Lesenswert?

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

von Peter Dannegger (Gast)


Lesenswert?

@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

von pittbull (Gast)


Lesenswert?

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

von Peter Dannegger (Gast)


Lesenswert?

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

von pittbull (Gast)


Lesenswert?

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.

von Peter Dannegger (Gast)


Lesenswert?

@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

von ope (Gast)


Lesenswert?

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

von ope (Gast)


Lesenswert?

Ha, der Stackpointer wird ja benutzt - alles klar :-)

von PeterL (Gast)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

Gibt es in BASCOM nicht bereits die DEBOUNCE-Anweisung?

Ansonsten kann man doch wohl in BASCOM ASM einbinden, oder?

...

von Christopher (Gast)


Lesenswert?

@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

von Peter D. (peda)


Lesenswert?

@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

von Marko B. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

@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

von Christian (Gast)


Lesenswert?

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

von Markus H. (markus-94209-)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

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.

...

von Markus H. (markus-94209-)


Lesenswert?

Hi,

danke schon mal für deine Ausführungen.

Hilft mir auf alle Fälle schon mal, weitere Fragen werden folgen ;-) .

Markus

von Christopher (Gast)


Lesenswert?

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

von Daniel Bambeck (Gast)


Lesenswert?

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

von pittbull (Gast)


Lesenswert?

mach mal aus dieser zeile;
 i = key_state ^ ~KEY_INPUT;
einfach das:
 i = key_state ^ KEY_INPUT;
und schon haste das umgekehrt

von peter dannegger (Gast)


Lesenswert?

Hier mal ein Bespiel mit kurz/lang Erkennung:

http://www.mikrocontroller.net/attachment.php/252480/C_TAST.C


Peter

von Ralph Holzner (Gast)


Lesenswert?

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

von Ich B. (ichbin)


Lesenswert?

Guck mal weiter oben, wenn Du für C suchst:
http://www.mikrocontroller.net/forum/read-4-20549.html#120296

Die Funktion zur Erkennung läßt sich leicht von der 'press'-Erkennung
herleiten.. es gibt aber auch zahlreiche Verweise zu kompletten Codes

von Michael D. (micheagle)


Lesenswert?

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.

von Fabian B. (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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 ?
.
1
#define REPEAT_MASK  (1<<KEY1^1<<KEY2)  // repeat: key1, key2


Peter

von Fabian B. (fabs)


Lesenswert?

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

von theend (Gast)


Lesenswert?

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

von Werbungverschmäher (Gast)


Lesenswert?

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

von theend (Gast)


Lesenswert?

ich benutze nur dezimal in meinem beispiel um die schreibweise 
0b00000001 für  dezimal 1 und 0b00000011 für 3 usw zu verkürzen.

von Werbungverschmäher (Gast)


Lesenswert?

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

von theend (Gast)


Lesenswert?

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

von Werbungverschmäher (Gast)


Lesenswert?

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:
1
Tastenabfrage: ;Entprellroutine geklaut bei Peter Dannegger...
2
 in scan,tap   ;Tastenport einlesen (gedrückt=L)
3
 com scan      ;invertieren (gedrückt=H)
4
 eor scan,tas  ;nur Änderungen werden H
5
 and tz0,scan  ;Prellzähler unveränderter Tasten löschen (Bit0)
6
 and tz1,scan  ;Prellzähler unveränderter Tasten löschen (Bit1)
7
 com tz0       ;L-Bit zählen 0,2,->1, 1,3,->0
8
 eor tz1,tz0   ;H-Bit zählen 0,2,->tz1 toggeln
9
 and scan,tz0  ;Änderungen nur dann erhalten, wenn im Prellzähler
10
 and scan,tz1  ;beide Bits gesetzt sind (Zählerstand 3)
11
 eor tas,scan  ;erhaltene Änderungen toggeln alten (gültigen) Tastenstatus
12
 and scan,tas  ;nur (neu) gedrückte Tastenbits bleiben erhalten
13
 or tfl,scan   ;und zugehörige Bits setzen (gelöscht wird nach Abarbeitung)
14
 ;in "tas" steht jetzt der gültige Tastenzustand,
15
 ;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

von Peter D. (peda)


Lesenswert?

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

von theend (Gast)


Lesenswert?

@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'

von Hannes L. (hannes)


Lesenswert?

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

...

von Peter D. (peda)


Lesenswert?

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

von jar (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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:
1
#define KEY_PIN         PINC
2
#define KEY1            2
3
#define KEY2            3
4
#define KEY3I           4      //input bit
5
#define KEY3            0      //reading bit
6
#define KEY_PIN2        PIND
7
#define KEY4            5
8
#define KEY5            6
9
#define KEY6            7
10
#define KEY7            1
11
#define KEY8            4
12
13
...
14
    i = KEY_PIN2 & (1<<KEY4^1<<KEY5^1<<KEY6^1<<KEY7^1<<KEY8);
15
    if(KEY_PIN & (1<<KEY1))
16
      i |= 1<<KEY1;
17
    if(KEY_PIN & (1<<KEY2))
18
      i |= 1<<KEY2;
19
    if(KEY_PIN & (1<<KEY3I))
20
      i |= 1<<KEY3;
21
    i ^= ~key_state;                       // key changed ?
22
    ct0 = ~( ct0 & i );                    // reset or count ct0
23
usw.


Peter

von Joachim B. (jar)


Lesenswert?

>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)));
}

von Joachim B. (jar)


Lesenswert?

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;
    }

von Der Hubert (Gast)


Lesenswert?

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

von Unbekannter (Gast)


Lesenswert?

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

von jar (Gast)


Lesenswert?

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 ?

von Hannes L. (hannes)


Lesenswert?

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

...

von Der Hubert (Gast)


Lesenswert?

Ok ok, ....

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

von Peter D. (peda)


Lesenswert?

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

von Ulrich (Gast)


Lesenswert?

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.

von Sönke G. (cycrow)


Angehängte Dateien:

Lesenswert?

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

von Sönke G. (cycrow)


Angehängte Dateien:

Lesenswert?

Nachdem ich nochmal mit Open Office diskutiert habe, läuft das jetzt 
auch unter Calc.

von mgiaco (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von mgiaco (Gast)


Lesenswert?

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?

von mgiaco (Gast)


Lesenswert?

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.
1
uint32_t get_key_press(uint32_t key_mask )
2
{
3
    unsigned cpsr;
4
    
5
    //cli();
6
    cpsr = disableIRQ();                  // disable global interrupts
7
    T0MCR &= ~(TMCR_MR0_I);                 // disable MR0 interrupt
8
    restoreIRQ(cpsr);                     // restore global interrupts
9
    
10
    key_mask &= key_press;            // read key(s)
11
    key_press ^= key_mask;            // clear key(s)
12
  
13
    //sei();
14
    cpsr = disableIRQ();                  // disable global interrupts
15
    T0MCR |= TMCR_MR0_I;                  // enable MR0 interrupt
16
    restoreIRQ(cpsr);                     // restore global interrupts
17
  
18
    return key_mask;
19
}

danke

von Peter D. (peda)


Lesenswert?

mgiaco wrote:

>
1
> uint32_t get_key_press(uint32_t key_mask )
2
> {
3
>     unsigned cpsr;
4
> 
5
>     //cli();
6
>     cpsr = disableIRQ();                  // disable global interrupts
7
>     T0MCR &= ~(TMCR_MR0_I);                 // disable MR0 interrupt
8
>     restoreIRQ(cpsr);                     // restore global interrupts
9
> 
10
>     key_mask &= key_press;            // read key(s)
11
>     key_press ^= key_mask;            // clear key(s)
12
> 
13
>     //sei();
14
>     cpsr = disableIRQ();                  // disable global interrupts
15
>     T0MCR |= TMCR_MR0_I;                  // enable MR0 interrupt
16
>     restoreIRQ(cpsr);                     // restore global interrupts
17
> 
18
>     return key_mask;
19
> }
20
>


Kommst Du Dir dabei nicht selber albern vor ?

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


Und das soll merkbar Zeit einsparen gegenüber 2 Funktionsaufrufen und 2 
Codezeilen:
1
    //cli();
2
    cpsr = disableIRQ();                  // disable global interrupts
3
    key_mask &= key_press;            // read key(s)
4
    key_press ^= key_mask;            // clear key(s)
5
    //sei();
6
    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

von mgiaco (Gast)


Lesenswert?

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

Danke.

von Hannes L. (hannes)


Lesenswert?

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.

...

von mgiaco (Gast)


Lesenswert?

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.


von Joachim B. (jar)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

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.

...

von Joachim B. (jar)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

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.

...

von Wiener Würstchen (Gast)


Lesenswert?

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
1
#define KEY_INPUT  P2
2
3
char key_state, key_press;
4
5
/* neue Zeile hier...
6
char key_release;
7
*/  
8
9
10
void to_int( void ) interrupt INT_T0
11
{
12
  static char ct0, ct1;
13
  char i;
14
15
  i = key_state ^ ~KEY_INPUT;  // key changed ?
16
  ct0 = ~( ct0 & i );    // reset or count ct0
17
  ct1 = ct0 ^ ct1 & i;    // reset or count ct1
18
  i &= ct0 & ct1;    // count until roll over 
19
  key_state ^= i;    // then toggle debounced state
20
  key_press |= key_state & i;  // 0->1: key pressing detect
21
22
/* neue Zeile hier...
23
  key_release |= ~key_state & i;  // 0->1: key release detect
24
*/  
25
}

von Soner (Gast)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

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.

...

von yalu (Gast)


Lesenswert?

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?

von Sanane B. (sanane)


Lesenswert?

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?

von Sanane B. (sanane)


Lesenswert?

also die Funktionen und die Definitionen habe ich von
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29

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

von Sanane B. (sanane)


Lesenswert?

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

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

@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

von Sanane B. (sanane)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

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

...

von Hannes L. (hannes)


Lesenswert?

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

...

von Peter D. (peda)


Lesenswert?

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

von Sanane B. (sanane)


Lesenswert?

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

von Juergen (Gast)


Lesenswert?

Hallo!

Ich habe mich mit dem schon ganz oben diskutiertem Code 
auseinandergesetzt:
1
KEY_INPUT=11111110; //Um Tastendruck zu simulieren.
2
3
while(1){
4
  i = key_state ^ ~KEY_INPUT;  // key changed ?
5
  ct0 = ~( ct0 & i );    // reset or count ct0
6
  ct1 = ct0 ^ ct1 & i;    // reset or count ct1
7
  i &= ct0 & ct1;    // count until roll over 
8
  key_state ^= i;    // then toggle debounced state
9
  key_press |= key_state & i;  // 0->1: key pressing detect
10
}

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

von Juergen (Gast)


Lesenswert?

Hab den Code jetzt folgendermaßen umgeschrieben:
1
  i = key_state ^ ~KEY_INPUT;  // key changed ?
2
  ct0 = (~ct0) & i ;    // reset or count ct0
  damit korrekt mit i verundet werden kann
1
  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!
1
  i &= ct0 & ct1;    // count until roll over
2
  key_state ^= i;    // then toggle debounced state
3
  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! ;-)

von Peter D. (peda)


Lesenswert?

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

von Juergen (Gast)


Lesenswert?

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

von Martin Schneider (Gast)


Lesenswert?

> 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

von Thomas B. (tombaer)


Angehängte Dateien:

Lesenswert?

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

von Florian P. (eckel)


Lesenswert?

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

von Kachel - Heinz (Gast)


Lesenswert?

> 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

von Florian P. (eckel)


Lesenswert?

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?

von Florian P. (eckel)


Lesenswert?

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.

von Hannes L. (hannes)


Lesenswert?

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.
1
Tastenabfrage:  ;Entprell-Algorithmus geklaut bei Peter Dannegger...
2
 ;die Tasten liegen auf den untern 4 Bits
3
 cbr flags,1<<upsync;Jobflag löschen (Routine läuft als Mainloop-Job)
4
 in tmp,tap         ;Tastenport einlesen (gedrückt=L)
5
 com tmp            ;invertieren (gedrückt=H)
6
 andi tmp,alltast   ;ungenutzte Bits ausblenden
7
 eor tmp,tas        ;nur Änderungen werden H
8
 and tz0,tmp        ;Prellzähler unveränderter Tasten löschen (Bit0)
9
 and tz1,tmp        ;Prellzähler unveränderter Tasten löschen (Bit1)
10
 com tz0            ;L-Bit zählen 0,2,->1, 1,3,->0
11
 eor tz1,tz0        ;H-Bit zählen 0,2,->tz1 toggeln
12
 and tmp,tz0        ;Änderungen nur dann erhalten, wenn im Prellzähler
13
 and tmp,tz1        ;beide Bits gesetzt sind (Zählerstand 3)
14
; Loslasserkennung 
15
 push tmp           ;Kopie
16
 and tmp,tas        ;nur die Tasten erhalten, die jetzt losgelassen wurden
17
 ;entweder:
18
 swap tmp           ;ins andere Nibble (das ist ja frei)
19
 or tfl,tmp         ;Bits im anderen Nibble setzen (alternativ)
20
 ;oder:
21
; or tlf,tmp         ;Bits im anderen Register setzen (alternativ)
22
 ;---
23
 pop tmp            ;alten Wert wiederherstellen
24
 eor tas,tmp        ;erhaltene Änderungen toggeln alten (gültigen) Tastenstatus
25
 and tmp,tas        ;nur (neu) gedrückte Tastenbits bleiben erhalten
26
 or tfl,tmp         ;und zugehörige Bits setzen (gelöscht wird nach Abarbeitung)
27
 ;in "tas" steht jetzt der gültige Tastenzustand,
28
 ;in "tfl" die Flags der neu gedrückten, noch nicht abgearbeiteten Tasten.
29
 ;wobei im unteren Nibble die Tastendrücke liegen und im oberen Nibble
30
 ;die Loslass-Ereignisse.
31
32
;und hier passt noch die Repeat-Erweiterung rein, die allerdings zusammen
33
;mit der Loslasserkennung nicht sonderlich sinnvoll ist.
34
35
 ret                ;fertig...

Ich hoffe, das Prinzip ist zu erkennen.

...

von Florian Patzer (Gast)


Lesenswert?

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

von Kachel - Heinz (Gast)


Lesenswert?

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

von Jupp (Gast)


Lesenswert?

Ich glaube, jetzt kann man den Thread eigentlich mal sperren denn es 
wurde doch alles gesagt.

von Mathias O. (m-obi)


Lesenswert?

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
1
#pragma cd pl(9999)
?????

von Mathias O. (m-obi)


Lesenswert?

kann mir denn niemand helfen???

von Werner B. (werner-b)


Lesenswert?

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

> (Keil C51)

von Mathias O. (m-obi)


Lesenswert?

sorry aber ich kenn nicht Keil C51.

von Werner B. (werner-b)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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
1
#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

von Matthias K. (malterix)


Angehängte Dateien:

Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Matthias K. (malterix)


Lesenswert?

> 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

von Peter D. (peda)


Lesenswert?

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

von Matthias K. (malterix)


Lesenswert?

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

von Matthias K. (malterix)


Angehängte Dateien:

Lesenswert?

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

Matthias

von Peter (Gast)


Lesenswert?

@ 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 ]

von udo (Gast)


Lesenswert?

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
1
u8 get_key_press( u8 key_mask )
2
3
u8 get_key_rpt( u8 key_mask )
4
5
und
6
7
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
1
/************************************************************************/
2
/*                                                                      */
3
/*    Debouncing 8 Keys        */
4
/*        Sampling 4 Times        */
5
/*    With Repeat Function        */
6
/*                                                                      */
7
/*     Author: Peter Dannegger                                 */
8
/*          danni@specs.de                                  */
9
/*                                                                      */
10
/************************************************************************/
11
// Target: ATMega48
12
13
#include <io.h>
14
#include <interrupt.h>
15
16
typedef unsigned char  u8;
17
typedef signed short  s16;
18
19
#define  XTAL    8000000L        // 8MHz
20
21
#define KEY_PIN    PINB
22
#define KEY0    0
23
#define KEY1    1
24
#define KEY2    2
25
#define  KEY3    3
26
27
#define LED_DDR    DDRC
28
#define LED_PORT  PORTC
29
#define LED0    0
30
#define LED1    1
31
#define LED2    2
32
33
#define REPEAT_MASK  (1<<KEY3^1<<KEY2^1<<KEY1^1<<KEY0)
34
#define REPEAT_START  10          // after 100ms
35
#define REPEAT_NEXT  20          // every 200ms
36
37
u8 key_state;                // debounced and inverted key state:
38
                    // bit = 1: key pressed
39
u8 key_press;                // key press detect
40
41
u8 key_rpt;                // key long press and repeat
42
43
44
ISR( TIMER0_COMPA_vect )          // every 10ms
45
{
46
  static u8 ct0, ct1, rpt;
47
  u8 i;
48
49
  i = key_state ^ ~KEY_PIN;          // key changed ?
50
  ct0 = ~( ct0 & i );              // reset or count ct0
51
  ct1 = ct0 ^ (ct1 & i);          // reset or count ct1
52
  i &= ct0 & ct1;              // count until roll over ?
53
  key_state ^= i;              // then toggle debounced state
54
  key_press |= key_state & i;          // 0->1: key press detect
55
56
  if( (key_state & REPEAT_MASK) == 0 )      // check repeat function
57
     rpt = REPEAT_START;          // start delay
58
  if( --rpt == 0 ){
59
    rpt = REPEAT_NEXT;            // repeat delay
60
    key_rpt |= key_state & REPEAT_MASK;
61
   }
62
}
63
64
65
66
u8 get_key_press( u8 key_mask )
67
{
68
  cli();                  // read and clear atomic !
69
  key_mask &= key_press;                            // read key(s)
70
  key_press ^= key_mask;                            // clear key(s)
71
  sei();
72
  return key_mask;
73
}
74
75
u8 get_key_rpt( u8 key_mask )
76
{
77
  cli();                  // read and clear atomic !
78
  key_mask &= key_rpt;                              // read key(s)
79
  key_rpt ^= key_mask;                              // clear key(s)
80
  sei();
81
  return key_mask;
82
}
83
84
u8 get_key_short( u8 key_mask )
85
{
86
  cli();                // read key state and key press atomic !
87
  return get_key_press( ~key_state & key_mask );
88
}
89
90
u8 get_key_long( u8 key_mask )
91
{
92
  return get_key_press( get_key_rpt( key_mask ));
93
}
94
95
int main( void )
96
{
97
  TCCR0A = 1<<WGM01;              // Mode 2: CTC
98
  TCCR0B = 1<<CS02^1<<CS00;            // divide by 1024
99
  OCR0A = (s16)(XTAL / 1024.0 * 10e-3 - 0.5);    // preload for 10ms
100
  TIMSK0 = 1<<OCIE0A;              // enable timer interrupt
101
102
  LED_PORT = 0xFF;
103
  LED_DDR = 0xFF;
104
  sei();
105
106
  for(;;){                  // main loop
107
108
    switch( get_key_long( 1<<KEY1^1<<KEY0 )){
109
110
      case 1<<KEY0:          LED_PORT ^= 1<<LED0; break;
111
112
      case 1<<KEY1:          LED_PORT ^= 1<<LED1; break;
113
114
      case 1<<KEY1^1<<KEY0:  LED_PORT ^= 1<<LED2; break;
115
    }
116
117
    switch( get_key_rpt( 1<<KEY3^1<<KEY2 )){
118
119
      case 1<<KEY2:          LED_PORT ^= 1<<LED0; break;
120
121
      case 1<<KEY3:          LED_PORT ^= 1<<LED1; break;
122
123
      case 1<<KEY3^1<<KEY2:  LED_PORT ^= 1<<LED2; break;
124
    }
125
  }
126
}

von Tobi (Gast)


Angehängte Dateien:

Lesenswert?

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

von Bach L. (bleviet)


Lesenswert?

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

von Matze N. (hupe123)


Lesenswert?

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:)

von yalu (Gast)


Lesenswert?

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.

von P. S. (Gast)


Lesenswert?

Irgendwie habe ich das nie richtig verstanden, ich pruefe die 
Tasten-Ports einfach jede ms und es funktioniert...?

von Micha (Gast)


Lesenswert?

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

von DL4BM (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Hannes Lux (Gast)


Lesenswert?

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

...

von Peter D. (peda)


Lesenswert?

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):
1
.include "1200def.inc"
2
3
.def    deb_ct0 = r2
4
.def    deb_ct1 = r3
5
.def    deb_i = r4
6
.def    deb_keystate = r5
7
.def    deb_keypress = r6
8
9
10
debounce:
11
        ;i = key_state ^ ~KEY_PIN;
12
13
        in      deb_i, PINB
14
        com     deb_i
15
        eor     deb_i, deb_keystate
16
17
        ;ct0 = ~( ct0 & i );
18
19
        and     deb_ct0, deb_i
20
        com     deb_ct0
21
22
        ;ct1 = ct0 ^(ct1 & i);
23
24
        and     deb_ct1, deb_i
25
        eor     deb_ct1, deb_ct0
26
27
        ;i &= ct0 & ct1;
28
29
        and     deb_i, deb_ct0
30
        and     deb_i, deb_ct1
31
32
        ;key_state ^= i;
33
34
        eor     deb_keystate, deb_i     ;deb_keystate = debounced key state
35
36
        ;key_press |= key_state & i;
37
38
        and     deb_i, deb_keystate
39
        or      deb_keypress, deb_i     ;deb_keypress = key pin has changed from 1->0
40
41
        ret


Peter

von DL4BM (Gast)


Lesenswert?

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.

von Hannes Lux (Gast)


Lesenswert?

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

...

von DL4BM (Gast)


Lesenswert?

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?) ?

von Hannes Lux (Gast)


Lesenswert?

> 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:
1
Tastenabfrage:  ;Entprell-Algorithmus geklaut bei Peter Dannegger...
2
 cbr flags,1<<entprell ;Jobflag löschen
3
 in tmp,tap         ;Tastenport einlesen (gedrückt=L)
4
 com tmp            ;invertieren (gedrückt=H)
5
 andi tmp,alltast   ;ungenutzte Bits ausblenden
6
 eor tmp,tas        ;nur Änderungen werden H
7
 and tz0,tmp        ;Prellzähler unveränderter Tasten löschen (Bit0)
8
 and tz1,tmp        ;Prellzähler unveränderter Tasten löschen (Bit1)
9
 com tz0            ;L-Bit zählen 0,2,->1, 1,3,->0
10
 eor tz1,tz0        ;H-Bit zählen 0,2,->tz1 toggeln
11
 and tmp,tz0        ;Änderungen nur dann erhalten, wenn im Prellzähler
12
 and tmp,tz1        ;beide Bits gesetzt sind (Zählerstand 3)
13
; Loslasserkennung in diesem Programm deaktiviert
14
; push tmp           ;Kopie
15
; and tmp,tas        ;nur die Tasten erhalten, die jetzt losgelassen wurden
16
; swap tmp           ;ins andere Nibble (das ist ja frei)
17
; or tfl,tmp         ;Bits im anderen Nibble setzen (alternativ)
18
; or tlf,tmp         ;Bits im anderen Register setzen (alternativ)
19
; pop tmp            ;alten Wert
20
 eor tas,tmp        ;erhaltene Änderungen toggeln alten (gültigen) Tastenstatus
21
 and tmp,tas        ;nur (neu) gedrückte Tastenbits bleiben erhalten
22
 or tfl,tmp         ;und zugehörige Bits setzen (gelöscht wird nach Abarbeitung)
23
 ;in "tas" steht jetzt der gültige Tastenzustand,
24
 ;in "tfl" die Flags der neu gedrückten, noch nicht abgearbeiteten Tasten...
25
Tastendauer:
26
 mov tmp,tas        ;Tastenzustand kopieren
27
 andi tmp,wietast   ;nur Tasten mit Wiederholfunktion stehen lassen
28
 tst tmp            ;ist eine Taste betätigt?
29
 breq Tastendauer0  ;nein, Dauer auf Startwert...
30
 dec twz            ;ja, Zähler runter
31
 brne Tastenabfrage_e   ;Dauer abgelaufen? - nein...
32
 or tfl,tmp         ;ja, noch aktive Tasten übernehmen (alternativ)
33
; or twf,tmp         ;ja, noch aktive Tasten übernehmen (alternativ)
34
 ldi twz,twz1       ;und Zähler auf Wiederholwert setzen
35
Tastenabfrage_e:

...

von Peter D. (peda)


Lesenswert?

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

von DL4BM (Gast)


Lesenswert?

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

von Hannes Lux (Gast)


Lesenswert?

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

...

von martin (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

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

...

von martin (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Marc H. (bauerpilot)


Angehängte Dateien:

Lesenswert?

Hallo Ihr,
ich suche eine einfache Routine in C zum entprellen von Tastern.
Ich habe die umfangreiche Routine von Peter versucht auf meine 
Verhältnisse für einen ATMega8 anzupassen. Leider funktioniert nichts.
Kann jemand sich erbarmen und den Code mal durchschauen?
Danke Gruß Marc

von Frank (Gast)


Lesenswert?

Ist es eigentlich Relativ schnell möglich die Tastenentprellung von 8 
Tasten (1Port) auf 16 Tasten (2Ports) zu erhöhen ?

Könnte ich dafür einfach die Entprell-zählvariablen in 16 bit werte 
ändern und in der Variable Keypin stehen dann 2 Ports?


Ist in meinem Gedankengang irgendein Haken ?

von <---Tom ----.....wusch~~ (Gast)


Lesenswert?

Hi
erstmal vielen Dank an dich Peter für diesen riesen betribenen Aufwand.

Ich suche zurzeit eine Tasten-entprellung für meinen MC TMS320S2833...
Zwar behersche ich die Grundlagen der C-Programierung und kann auch die 
Grundidee nachvollziehen. Doch im Programmcode stellen sich noch die ein 
oder anderen Fragen.

1. Zählt das ct1 ct0 Zählersystem von 0-3 durchgehend wenn der zustand 
bleibt oder wenn ersich jeweils in den nächsten Zustands bewegt 
(erkennen des tastens dann nur bei fallender flanke?)?

2.Wo kann ich die nötge Theorie zum entprellen finden.
nicht theorie für den Counter
und  nicht Wiki,sondern Theorie mit verschiedenen ansätzen  gerne auch 
Buch tips

Ich will das Sysstem nicht einfach übernehmen sondenr wirklich 
verstanden haben sonst bringt mir das nix.

Vielen Dank im Vorraus

mfg Tom

von Hannes L. (hannes)


Lesenswert?

<---Tom ----.....wusch~~ schrieb:
> 2.Wo kann ich die nötge Theorie zum entprellen finden.

Beitrag "Re: Tasten entprellen - Bulletproof"

...

von <---Tom ----.....wusch~~ (Gast)


Lesenswert?

Danke Hannes ,
aber diesen link hatte ich mir bereits angeguckt und die entsprechende 
pdf scheitn ofline zusein zumindest wird mir der beliebete 404 Error 
gezeigt.

auch eine suche nach dem Dokument "rtas2000.pdf" auf der angegeben seite 
ergab keine ergebnisse

von Hannes L. (hannes)


Lesenswert?

<---Tom ----.....wusch~~ schrieb:
> scheitn ofline zusein

Sorry, hatte ich (jetzt) nicht nochmal überprüft. Ich habs auch auf 
meiner Platte nicht gefunden, entweder umbenannt oder doch wieder 
gelöscht, weil zu theoretisch.

Ich nutze und verstehe Peters Entprellung, allerdings nur in ASM. Links 
auf Diskussionen im Forum findet man im Wiki unter Entprellung.

...

von Bernhard M. (boregard)


Lesenswert?


von <---Tom ----.....wusch~~ (Gast)


Lesenswert?

Vielen Dank nochmal euch beiden.

habe heute noch folgende NEtte seite gefunden:
http://www.loetstelle.net/praxis/entprellen/entprellen.php

sind noch mal in anderen worten die üblichen
Hardware- und Softwaremethoden mit Vor- und Nachteilen erklärt.
Aufangenhemen anfänger Niveu.


für meine Problem lösung habe ich mich jetzt gegen den Speichersparenden 
vertikalcounter entschieden, da ich die zustände meiner Buttons in einer 
Buttonklasse über einen Funktionspointer abfrage müsste ich den counter 
auch über pointer ansteuern. und da ist der Speicherplatz schon wieder 
weg.

falls ich später merke das meine Buttons synchron abgefratg werden 
müssen weil ich sonst probleme mit zeitdiskreten funktionne kriege werd 
ich mich wohl nocheinmal darum kümmern müssen.

mfg Tom

von Uwe S. (de0508)


Lesenswert?

Hallo Tom,

ich rate Dir dringend den Code von PeDa zu verwenden.

- Beitrag "Universelle Tastenabfrage"

Er läuft problemlos und sicher, auch eine Implementierung auf deinen µP 
wird kein Problem für dich sein.

Man muss nur verstehen, dass die Lib die Tasten-Ereignisse liefert und 
das ist für das weitere Programmieren ein sehr großer Vorteil!

Siehe dazu diesen Link:

- Beitrag "Re: Universelle Tastenabfrage"


.

von Peter D. (peda)


Lesenswert?

<---Tom ----.....wusch~~ schrieb:
> da ich die zustände meiner Buttons in einer
> Buttonklasse über einen Funktionspointer abfrage müsste

Das klingt ja nach "von hinten durch die Brust ins Auge".
Man muß nicht alles unnötig kompliziert machen, erst recht nicht als 
Anfänger.
Spare Dir die Funktionspointer besser für sinnvolle Einsatzgebiete auf.

Ich habe auch gemerkt, daß der Zustand einer Taste eigentlich niemanden 
interessiert.
Der Programmierer will Ereignisse haben und deshalb kriegt er die auch. 
Dadurch wird das Programmieren erheblich einfacher.

Will er z.B. das Ereignis "lang gedrückt", kann er die entsprechende 
Funktion aufrufen und kriegt dann auch genau nur ein Ereignis.
Den ganze low-Level Schrunz, um dieses Ereignis zu extrahieren, kann er 
vergessen, er interessiert ihn nicht mehr.
Er muß also das Rad nicht jedesmal neu erfinden, er benutzt es einfach 
nur.


<---Tom ----.....wusch~~ schrieb:
> falls ich später merke das meine Buttons synchron abgefratg werden
> müssen weil ich sonst probleme mit zeitdiskreten funktionne kriege werd
> ich mich wohl nocheinmal darum kümmern müssen.

Das ist genau der große Nachteil vieler anderer Lösungen. Irgendwann 
kneift es und man muß wieder ran.


Peter

von <---Tom ----.....wusch~~ (Gast)


Lesenswert?

Danke Peter
mit den Funktionspointern bin ich selber nicht so glücklich leider habe 
ich da keine andre Lösung gesehen, da meine i/o's nicht über den 
Adressoperator abgefragt werden können sondern nur die Registeradresse, 
auch das Problem hätte ich nur recht umständlich und unübersichtlich 
lösen können.

Im Moment versuche ich erstmal mit dem MC warm zuwerden bevor ich mich 
an die eigentliche Aufgabe Herantraue.

Jetzt habe ich mich aber nochmal erneut durch meine Mc configuration 
gewälz und eher zufällig gesehen das dieser bereits eine vorgefertigte 
Entprellung anbietet diese werde ich nun verständlicherweise nutzen.

Trozdem war mit das "Herummspielen" und Nachahmen an/von einer
Software-Entprellung leiber als das übernehmen.

das deine Funktion Zierlicher und eleganter als meine erstellte Lösung 
ist daran besteht keinzweifel ebensowenig wie das deine SW entprellung 
bei mir vorerst in meiner SW-bib bleibt.

von Sam .. (sam1994)


Lesenswert?

Ich hab auch mal probiert eine ähnliche Routine zu schreiben. Weniger 
als 12 Takte schaffe ich aber auch nicht.
1
.def temp = r16
2
.def c1 = r17  ;Zähler1
3
.def c2 = r18  ;Zähler2
4
.def res = r19 ;Ergebnis
5
6
in res, PINB
7
com res
8
and c1, res
9
and c2, res
10
mov temp, res
11
and temp, c1
12
or c1, res
13
eor c1, temp
14
15
mov res, temp
16
and res, c2
17
or c1, res
18
or c2, temp


Ich wollte grad F7 drücken um den Beitrag abzusenden lol

von Peter D. (peda)


Lesenswert?

<---Tom ----.....wusch~~ schrieb:
> mit den Funktionspointern bin ich selber nicht so glücklich leider habe
> ich da keine andre Lösung gesehen, da meine i/o's nicht über den
> Adressoperator abgefragt werden können sondern nur die Registeradresse,

Ich verwende nirgends den Adreßoperator, sondern lese das Inputregister 
direkt ein.
Vorzugsweise sollten alle Tasten auf dem gleichen Port liegen, müssen 
aber nicht.

Ich wüßte echt nicht, warum man zum Entprellen einen Funktionspointer 
bräuchte.
Um welchen MC und welche Entwicklungsumgebung geht es denn bei Dir, daß 
Du solche speziellen Zugriffe benötigst?


Peter

von <---Tom ----.....wusch~~ (Gast)


Lesenswert?

Zum entprellen brauche ich den Funktionspointer nichtdirekt.
allerdings wollte ich eine eine Button Klasse erstellen. Mit hilfe eines 
Konsturktors wäre es mir möglich auch für spätere Projekte schnell einen 
button zu inizialisieren.

im knostruktor wollte ich zuerst nur die adresse meines eingangs, den 
Modus des button festlegen und evtl. eine funktionspointer für die nach 
dem button auszuführende aktion. Da ich aber nicht direkt die adresse 
über den adressoperator auslesen konnte musste habe ich statt dessen 
einen funktionspointer genutz. Die aufgerufen funktion gibt nur den 
zustand des buttons aus wird aber außeralb meiner klasse erstellt und 
dann über den pointer in die klasse eingefügt. Damit die ausgabe des 
Buttons richtig funktioniert muss ich natürlich auch entprellen.

im porjekt selber sollen mit hilfe von 3-4 buttons ein LCD-display 
gesteuert werden. (vorerst)

leider musste ich heute feststellen das die hardware entprellung des Mcs 
nicht ausreicht. Muss mir noch mal in ruhe überlegen was ich wirklich 
benötige. wenn ich deine methode und die klassen definiton nutzen möchte 
bräuchte ich natürlcih 4 buttons pro klasse, aber da geht natürlich viel 
speicher verlohren.

von Peter D. (peda)


Lesenswert?

<---Tom ----.....wusch~~ schrieb:
> wenn ich deine methode und die klassen definiton nutzen möchte
> bräuchte ich natürlcih 4 buttons pro klasse, aber da geht natürlich viel
> speicher verlohren.

Nö, die Buttons benötigen überhaupt keinen Speicherplatz.

Es wird immer der ganze Port entprellt. Welcher Pin davon nun eine Taste 
ist, wird durch die Bitmaske bestimmt, die man der gewünschten 
Ereignisfunktion übergibt.
Ein Button ist also einfach nur eine Maske (0x01, 0x02, ... 0x80).

Desweiteren gibt es noch eine Maske für alle Tasten mit Sonderfunktionen 
(Repeat usw.). Mit dieser Maske kann man z.B. Ausgänge auf dem gleichen 
Port ausschließen.
Eine Besonderheit dabei ist, daß die Sonderfunktionen nicht gleichzeitg 
für mehrere Tasten funktionieren (nur ein Zeitzähler für alle 
Sondertasten).
Eine derart komplizierte Bedienung würde aber eh die meisten Menschen 
überfordern, daher ist das keine wirkliche Einschränkung.

Man kann auch verschiedene Ereignisfunktionen in verschiedenen Kontexten 
aufrufen (z.B. Menü 1: lang/kurz Erkennung, Menü 2: Repeatfunktion).
Das Einrichten von Callbacks ist also unnötig und wäre auch nicht 
universell möglich, da sich manche Ereignisse gegenseitig aussschließen.


Ich hab überhaupt keine Ahnung von C++. Du mußt versuchen, die 
Funktionen meiner C-Routine in C++ abzubilden, nicht umgekehrt.


Peter

von <---Tom ----.....wusch~~ schrieb: (Gast)


Lesenswert?

mit :

"
> wenn ich deine methode und die klassen definiton nutzen möchte
> bräuchte ich natürlcih 4 buttons pro klasse, aber da geht natürlich viel
> speicher verlohren.
"

meinte ich das ich nur 3 tasten im moment benötige wenn ich in einer 
klasse immer 4 buttons iniziiere aber nur 3 oder 2 benötige war das 
ganze erstmal für die katz oder sehe ich das falsch? ausgenommen 
natürlich die synchrone entrpällung die ist super aber nicht 100% 
notwenig

habe in erwägung gezogen die entrpäll funktion so zuschreiben das sie 
zummaen mit der der ausgabe funktion meines buttons im interrupt läuft

quasi wie bei dir::

status wechsel wird nach intterupt in der Ausgabefunktion erkannt.
-->entrpellen vergleicht X interrupts den Stauts des Buttons
wenn er gleich bleibt --> aufruf des Aktions Funktionpointers.
--> beliebige Aktionsfunktion

nur das ich nicht synchron alle 4 pointer gleichzeitig sonder 
hintereinander abfragen werde.

sollte bei 3 buttons aber vertkraftbar sein

von Hannes L. (hannes)


Lesenswert?

<---Tom ----.....wusch~~ schrieb: schrieb:
> immer 4 buttons iniziiere aber nur 3 oder 2 benötige

Bei der ASM-Variante von Peter werden auf einem 8-Bit-System wie dem AVR 
(zwangsläufig) immer 8 Eingänge behandelt, einfach deshalb, weil ein 
Byte 8 Bits hat und die logischen Verknüpfungen (des Zählens und des 
Löschens der Zähler) eben gleichzeitig auf alle 8 Bits wirken. Selbst 
wenn nur 2 Tasten benötigt werden ist diese Methode effizienter als das 
einzelne Behandeln der beiden Tasten. Dies wird hauptsächlich dadurch 
erreicht, dass die 2-Bit-Zähler ihre Bits in unterschiedlichen Registern 
haben, jedes der beiden Zähl-Register enthält eben nur 1 Bit von 8 
separaten Zählern.
Das kommt der Tatsache entgegen, dass der Tastenzustand aller 8 Eingänge 
auch in einem 8-Bit-Register gehalten wird, ebenso wie das 
Ereignisregister (Key_press) die Ereignisse (Taste wurde erneut 
betätigt) von 8 Tasten enthält.

In ASM arbeite ich zwar nicht mit "Funktionen" und erst recht nicht mit 
"Klassen", aber bei der Abfrage auf evtl. neue Tastendrücke muss ich 
mich auch nicht um die Low-Level-Routinen kümmern, denn die laufen im 
Hintergrund, entweder im Timer-Interrupt oder in einem vom Timer 
synchronisierten Job der Mainloop.

Die Abfrage auf neue Tastendrücke erfolgt ganz einfach durch Lesen der 
(global erreichbaren) Variable Key_press und anschließendem Zurücksetzen 
der überprüften Bits. Dabei wird nichtmal immer das komplette Byte 
ausgewertet, sondern nur das zur Taste gehörende Bit (cbrs/sbrs machts 
auf dem AVR ohne AND-Orgie günstig möglich), das dann leicht mit cbr 
zurückgesetzt werden kann. Das erfordert schonmal weniger Rechenleistung 
als das Aufrufen eines Unterprogramms (als Äquivalent zum 
Funktionsaufruf). Ich frage also nur den Zustand des Bits in einem 
Register ab, um zu sehen, ob seit der letzten Abfrage eine Taste erneut 
betätigt wurde. Genauso läuft es mit den Erweiterungen dieser 
Entprellung, z.B. ein Register mit dem Ereignis "Taste wurde 
losgelassen" der 8 Tasten oder ein Register mit dem Ereignis "langer 
Tastendruck" der separat selektierbaren Tasten. Tasten-Repeat bei 
längerer Betätigung lässt man am besten auf Key_press wirken, es ist 
aufgrund separater Parametrierbarkeit der Verzögerungs-Zeiten des ersten 
Zuschlagens und des wiederholten Zuschlagens sehr angenehm bedienbar. 
Und wenn man mal eine "Shift-Taste" braucht, dann ignoriert man eben das 
entsprechende Bit in Key_press und wertet dafür Key_state aus, in dem 
der entprellte Zustand aller Tasten ausgelesen werden kann.

Diese Entprell-Routinen sind dermaßen flexibel nutzbar (und an die 
unterschiedlichen Bedingungen anpassbar), ich möchte darauf nicht 
verzichten. Dieser Algorithmus ist einfach genial, auch ohne Funktionen 
und Klassen...

...

von Hannes L. (hannes)


Lesenswert?

Hannes Lux schrieb:
> cbrs/sbrs

soll natürlich sbrc/sbrs heißen... - Traurig, dass das keiner merkt.

...

von Peter D. (peda)


Lesenswert?

Hannes Lux schrieb:
> Hannes Lux schrieb:
>> cbrs/sbrs
>
> soll natürlich sbrc/sbrs heißen... - Traurig, dass das keiner merkt.

Da siehste mal, wie wenige noch Assembler benutzen.
Viele haben nach "Bei der ASM-Variante ..." aufgehört zu lesen.

Gib Dir mal nen Ruck, C nimmt einem wirklich ne Menge Arbeit ab.


Peter

von Hannes L. (hannes)


Lesenswert?

Peter Dannegger schrieb:
> Gib Dir mal nen Ruck, C nimmt einem wirklich ne Menge Arbeit ab.

Zu spät...

Im Alter lernt es sich nicht mehr so leicht, es ist schon eine Weile 
her, dass ich mal sechzig war...

Meine Basteleien sind allesamt klein genug, um in ASM den Überblick zu 
behalten. Und auch in ASM kann man halbwegs strukturiert programmieren, 
ich habe diesbezüglich allerhand von Dir gelernt.

ASM ist eineindeutig, ich kann jede Instruktion genau nachvollziehen.

C wäre (für mich) Lotto, da ich nicht vorhersagen kann, wie der Compiler 
(und Linker) den Code umsetzt. Ich sehe doch hier im Forum, in welche 
Fallen die C-Einsteiger so alles rein tappen, nein, das will und muss 
ich mir nicht antun.

Ich arbeite gern mal mit einer 24-Bit-Zahl, wenn 16 Bit nicht mehr 
ausreicht. C würde mich zur 32-Bit-Zahl zwingen.

C ist mir einfach zu kryptisch, ich kann mich auch nicht damit abfinden, 
dass jeder Hauptprogramm main.c heißen muss.

Nee, Peter, ich gehöre zu einer aussterbenden Spezies, die paar Jahre, 
die meine Augen und Zitterfinger noch mitmachen, komme ich noch ganz gut 
mit AVR-Assembler, Tonertransfer-Platinen und ICs in DIL-Gehäusen 
zurecht.

Gruß auch vom Hartmut aus Apen, der mich immer zu Bascom drängen will...
;-))

...

von <---Tom ----.....wusch~~ (Gast)


Lesenswert?

Hannes ich habe leider nie Assembler glernt,
dies lieget vorallem daran das ich mit meinen 23 jahren selbst im 
Studium nur die grundlagen von C und dann direkt in C++ eingewiesen 
wurde.
Ich verstehe zwar die Herrkunft dieser befehle aber ich bin nicht in der 
lage dise nachzuvollziehen geschweige denn selbst zu erstellen.
Daher wäre ich auch niemals auf die Bitcounter-fariante gekommen.
Für das Verständnis und die Erleichterung meines späteren Programierens 
ist das erstellen und nutzen von Klassen, Opjekten und Funktionen sehr 
wichtig sie halten den Programmcode kruz und übersichtlich. Naturlich 
leidet darunter die efizienz des Kompiliertencodes.

Ich denke und hoffe das bei meinem 150 MHz Prozessor diese Grobheiten 
durch bloße rechengewalt kompensiert werden kannn.

Es ist wird wohl nicht das Filigrane kunstwerk eines erfahren 
Programierers aber es wird auch nicht die Brechstangenmethodik eines 
Anfängers werden.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ich hab das nicht in Assembler entwickelt, sondern noch eine Ebene 
tiefer, in Hardware.
Und dann habe ich gemerkt, daß es zu Logikgattern äquivalente Befehle in 
Assembler bzw. C gibt.
Das Umsetzen in Code ist also ganz leicht gewesen.
Und da Logikoperatoren immer über die volle Wortbreite der CPU gehen, 
hat man gleich mehrere Tasten erschlagen.

Die reinen Softwerker nehmen da lieber Arrays, Schleifen und Vergleiche, 
die alle Tasten nacheinander abarbeiten.
Schleifen, indirekte Zugriffe und bedingte Sprünge sind für eine CPU 
aber deutlich teurer, als einfache logische Operatoren.

Letztendlich wird das Entprellen kaum der Flaschenhals sein.
Aber wenn es klein und schnell ist, dann hat man mehr Zeit für andere 
Dinge.
Und als willkommenen Nebeneffekt hat man bequeme Zugriffsfunktionen, die 
man einfach nur benutzt, ohne irgendwelche Seiteneffekte. Das Thema 
Entprellen muß man nie wieder anfassen.


Peter

von Mr.Burns (Gast)


Lesenswert?

Hallo,

zunächstmal großen Dank an alle die hier geschrieben haben.
Habe mal 'ne bescheidene Frage.
Ich habe das Posting nun von Anfang an durchgelesen, aber den Überblick 
verloren.
Kann mir jemand sagen, wo nun der Quellcode (in C wäre mir lieber) der 
letzten bzw. aktuellen Version der Tastenentprellung zu finden ist?

Brauche eine Entprellung für kurzes Drücken eines Taster (oder mehrere) 
und auch Erkennung (entprellt) wenn Taste losgelassen wurde.
Ist das in der letzten Version integriert?

Danke für Infos!

von debounce (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

hier noch eine ähnliche Methode die Tasten zu entprellen, allerdings 
wird hier ein binär kodierter Zustandsregister (q0,q1) verwendet um die 
Verzögerung zu realisieren.
l dient nur dazu den Release- und Press-Zustand einmalig auszulösen, 
möchte man platz sparen, braucht man kein einmaliges auslösen, kann man 
l aus der gleichung nehmen.
Im Anhang ein kleines C-Programm zum Testen der Methoden.


uint8_t press, release;
inline void debounce(uint8_t state){
  static uint8_t q0=0, q1=0, l=0;
  uint8_t t;
  t = state & (q0 | q1);
  q0 = state & (~q0 | q1);
  q1 = t;
  release |= ~(q0 | q1) & l;
  press |=  (q0 & q1) & ~l;
  l = q0 & q1;
}

inline void debounce_hyst(uint8_t state){
  static uint8_t q0=0, q1=0, l=0;
  uint8_t t;
  t = (state & (q0 | q1)) | (q0 & q1);
  q0 = (state & (~q0 | q1)) | (~q0 & q1);
  q1 = t;
  release |= (q0 ^ q1) & l;
  press |=  (q0 & q1) & ~l;
  l = q0 & q1;
}

von Rene (Gast)


Lesenswert?

Hallo,

ich entprelle Tasten mit einem digitalen Filter, welcher ein RC Glied 
gefolgt von einem Schmitt-Trigger bildet. Die Routine zum entprellen 
wird immer bei der Timer ISR jede 1ms aufgerufen. Im Main loop frage ich 
die Zustände ab dort läuft meine Statemachine. Zusätzlich kann ich auch 
erkennen ob ein Knopf länger als 1 Sekunde gedrückt wurde. Das nutze ich 
beim Druckknopf eines Drehencoders um zwischen kurzen und langen Klicks 
zu unterscheiden sehr praktisch spart mir wieder einen Taster.
Artikel die ich zur Umsetzung genommen habe.
http://www.edn.com/design/analog/4324067/Contact-debouncing-algorithm-emulates-Schmitt-trigger
http://web.engr.oregonstate.edu/~traylor/ece473/lectures/debounce.pdf

// globals
unsigned char output = 0;
unsigned char knob_up = 0; //knob_up indicates event after the button 
has been pressed for >1000ms
unsigned char y_old = 0;
unsigned char flag = 0;
unsigned char long_pressed = 0;
unsigned int  long_counter = 0;

// called in Timer ISR each ms
// p1_3 is MCU specific Input Pin state
void debounceSwitches(void)
{
  unsigned char temp = 0;
  temp=(y_old>>2);
  y_old=y_old-temp;
  if(p1_3==1)
  {
    y_old=y_old+0x3F;
  }
  if((y_old > 0xF0)&&(flag==0))
  {
    flag=1;
    output=1;
    long_pressed = 0;
    long_counter = 0;
  }
  if((y_old < 0x0F)&&(flag==1))
  {
    if(long_pressed == 0)
    {
      knob_up = 1;
    }
    flag=0;
    output=0;
    long_pressed = 0;
    long_counter = 0;
  }
  if( flag == 1)
  {
    long_counter++;
  }
  else
  {
    long_counter = 0;
  }

  if(long_counter > 1024)
  {
    long_pressed = 1;
  }
}




René

von Wim (Gast)


Lesenswert?

Hannes Lux schrieb:
> <---Tom ----.....wusch~~ schrieb:
>> scheitn ofline zusein
>
> Sorry, hatte ich (jetzt) nicht nochmal überprüft. Ich habs auch auf
> meiner Platte nicht gefunden, entweder umbenannt oder doch wieder
> gelöscht, weil zu theoretisch.
>
> Ich nutze und verstehe Peters Entprellung, allerdings nur in ASM. Links
> auf Diskussionen im Forum findet man im Wiki unter Entprellung.
>
> ...

Zur Info: Die Datei ist hier immer noch verfügbar:

http://web.archive.org/web/20040329052843/http://www.ece.umd.edu/~msquared/rtas2000.pdf

Beitrag #5968657 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.