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
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
@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
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
@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
@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
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
@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
@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
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?
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
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
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!
> 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.
>> 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
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
...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 ?
"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
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
@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
@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.
...
>>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
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 :(
...
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.
...
@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).
...
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.
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.
...
"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
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
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
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
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
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.
@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
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
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
@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
@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
@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
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
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
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.
...
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
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
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
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
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.
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
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 ?
.
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
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
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
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
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
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
> 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.
...
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
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:
>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)));
}
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 ....
> 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.
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 ?
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).
...
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
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.
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
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
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
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
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.
...
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.
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....
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.
...
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
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.
...
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
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
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.
...
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?
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?
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
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
@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
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
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
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
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
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!
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! ;-)
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
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
> 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
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
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
> 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
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?
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.
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.
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
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
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
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.
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
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.
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
> 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
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
@ 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 ]
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
u8get_key_press(u8key_mask)
2
3
u8get_key_rpt(u8key_mask)
4
5
und
6
7
u8get_key_short(u8key_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
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
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
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:)
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.
>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.
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.
> 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.
...
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
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.
> 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...
...
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?) ?
> 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)
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
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).
> 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.
...
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
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
@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.
...
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
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
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
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
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 ?
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
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
<---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.
...
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
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"
.
<---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
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.
<---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
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.
<---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
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
<---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...
...
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
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...
;-))
...
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.
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
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!
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;
}
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-triggerhttp://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é
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