Forum: Mikrocontroller und Digitale Elektronik Taster Entprellen von Peter Dannegger


von nedim m. (d_caine)


Lesenswert?

Hallo zusammen,
gibt es irgendwo ein Programm von Peter Dannegger das die Tasten 
entprellt aber in ASM ? Wenn möglich für MPLAB.

Grüße caine

von Noname (Gast)


Lesenswert?

Ob gerade in Assembler weiss ich nicht.
Aber such mal hier im Forum.
Der C Code an den ich mich erinnere ist für AVR.
Sollte sich aber leicht in jede andere Sprache umsetzen lassen. (Ist 
halt nichts für die CopyNPaste-Progammierer).

von Michael A. (Gast)


Lesenswert?

Hast du in der Artikelsammlung schon nach "Entprellen" gesucht?
http://www.mikrocontroller.net/articles/Hauptseite

von nedim m. (d_caine)


Lesenswert?

NONAME, den in C für AVR habe ich ja gefunden in der Codesammulung, da 
ich aber totaler Anfänger bin aber Assembler lerne, ist es für mich 
schon problematisch den C Code und dann auch noch für AVR zu verstehen. 
Da ich den 16f877 von Microchip benutze.
Keine Chance den selber umzuändern.

Gruß caine

von Thorsten S. (whitejack) (Gast)


Lesenswert?

1) pin abchecken jede ms
2) wenn pin aktiv zähler runterzählen, wenn nicht zähler gleich 10
3) wenn zähler gleich 0
4) ereignis auslösen


pin checken mit btfss oder btfsc  goto  _LABEL_X
hochzählen mit incf
gleich 0 abchecken mit zerobit (z Bit im Statusregister, ich meine das 
ist STATUS,Z) also btfss STATUS,Z Das bis ist gesetzt wenn im w Register 
eine 0 steht.

Gruß,
TS

von my2ct (Gast)


Lesenswert?

nedim muratovic schrieb:
> Da ich den 16f877 von Microchip benutze.
> Keine Chance den selber umzuändern.

Dann nimm dir nicht zu viel auf einmal vor. Bring erstmal eine LED auf 
deinem µC zum Blinken -
  Version 1: mit Delay
  Version 2: mit Timer IRQ

Aber selbst geschrieben und nicht blind kopiert, gell ;-)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Die Tastenentprellung von PeDa ist ein paralleler 2-Bit-Zähler, der 
gleichzeitg auf einem ganzen Byte zählt. Eigentlich eine ganz einfache 
Sache, aber recht trickreich. Und natürlich kann das auch in Assembler 
realisiert werden. Man muss nur vorher verstanden haben, was da 
passiert...

von nedim m. (d_caine)


Lesenswert?

Lothar Miller schrieb:
> Die Tastenentprellung von PeDa ist ein paralleler 2-Bit-Zähler, der
> gleichzeitg auf einem ganzen Byte zählt. Eigentlich eine ganz einfache
> Sache, aber recht trickreich. Und natürlich kann das auch in Assembler
> realisiert werden. Man muss nur vorher verstanden haben, was da
> passiert...

"Mann muss nur vorher verstanden haben, was da passiert..."

Lothar, das "nur" in dem Satz hättest du aber auch weg lassen können :-)

von nedim m. (d_caine)


Lesenswert?

my2ct schrieb:
> Dann nimm dir nicht zu viel auf einmal vor. Bring erstmal eine LED auf
> deinem µC zum Blinken -
>   Version 1: mit Delay
>   Version 2: mit Timer IRQ
>
> Aber selbst geschrieben und nicht blind kopiert, gell ;-)
>
 my2ct
das habe ich, sogar 4 Programme. 1. led leuchtet. 2.Led leuchtet wenn 
Taster betätigt. 3. Led geht aus oder wenn aus dann ein bei Taster 
betätigung. 4. Programm Nr3 aber das ganze über eine Interrupt Routine 
um mich bischen mit dem INCON Register vertraut zu machen.
Und jetzt möchte ich mich mit der Taster entprellung vertraut machen.

Grüß caine

von Thorsten S. (whitejack) (Gast)


Lesenswert?

...und wie hast du das realisiert, wo ist denn das Problem?

Man prüft den Pin, wenn keine Bewegung mehr ist, dann wird das Ereignis 
gültig. Das läuft im 1ms 10ms Bereich ab, wie ich oben beschrieben 
habe...hat man schnell gemacht....

Gruß,
TS

von Timm T. (Gast)


Lesenswert?

1
.DEF  KEYstA    = r1    ;
2
.DEF  KEYctA1    = r2    ;
3
.DEF  KEYctA2    = r3    ;
4
5
;init Register
6
7
  clr  KEYstA      ;Tasten Status Null
8
  clr  KEYctA1
9
  com  KEYctA1      ;Tasten Counter 0xFF
10
  clr  KEYctA2
11
  com  KEYctA2
12
13
;in der durchlaufenden Mainloop mit 32ms oder im Timer Interrupt
14
15
  in  TEMP1, PINswA    ;Tasten einlesen, low-aktiv
16
17
  ;**** Software-Entprellung 4 Zyklen ****
18
19
  com  TEMP1      ;Tasten invertieren
20
  eor  TEMP1, KEYstA    ;veränderte Tasten high
21
  and  KEYctA1, TEMP1    ;unveränderte Prellzähler Bit0 löschen
22
  and  KEYctA2, TEMP1    ;unveränderte Prellzähler Bit1 löschen
23
  com  KEYctA1      ;Prellzähler Bit0 inc
24
  eor  KEYctA2, KEYctA1  ;Prellzähler Bit1 inc
25
  and  TEMP1, KEYctA1       ;veränderte Tasten nur erhalten, wenn in Prellzähler
26
  and  TEMP1, KEYctA2    ;beide Bits gesetzt
27
  eor  KEYstA, TEMP1    ;veränderte Tasten toggeln, neuer gültiger Status
28
  and  TEMP1, KEYstA    ;nur gedrückte Tasten berücksichtigen
29
  or   KEYchA, TEMP1        ;neue Tasten merken

von nedim m. (d_caine)


Lesenswert?

Jetzt nur mal damit ich das verstehe.
Bei mir ist das so , dass der Taster einen Interrupt auslöst der eine 
LED anmacht wenn aber schon ein , dann eben aus.
Wenn also Taster betätigt wird und der prellt und den Interrupt 
auslöst,(mehrere male anstatt einmal) nutzt es doch nix den  Taster in 
den ISR zu entprellen , da die Prellung mehrere Interrupts auslöst die 
dann von dem Stack abgearbeitet werden.
Oder rede ich wieder Mist.??

von PIC (Gast)


Lesenswert?

Also ich habe schon oft gelesen, dass man Taster generell eigentlich 
nicht über Interrupts auswertet.

Sondern diese werden wohl tatsächlich eher "gepollt" und dann je nach 
dem aktuellen Status des Tasters ihn dann noch ein paar mal mehr 
abfrägt, wie weiter oben ja auch schon mal beschrieben wurde, indem ein 
Zähler (beispielsweise mit 10 initialisiert) jede 1 ms dekrementiert 
wird.
Wenn der Tasterstatus sich innerhalb dieser 10 ms nicht verändert hat 
und konstant (high oder low, je nach Ansteuerung) geblieben ist, kann 
man davon ausgehen, dass der Taster gedrückt wurde und somit entprellt 
ist.

von nedim m. (d_caine)


Lesenswert?

Wäre es dann nicht eleganter die Taster Prellung mittels RC Glied zu 
lösen und Trotzdem die Taster über Interrupts zu machen?
Kann mir nicht vorstellen das die Taster ständig abgefragt werden 
müssen, was bei polling der Fall ist.
Der ideale weg wäre doch die Prellng softwaremäsig zu lösen und die 
Taster über ISR.
Wenn das überhaupt geht.

von al3ko (Gast)


Lesenswert?

nedim muratovic schrieb:
> Wäre es dann nicht eleganter die Taster Prellung mittels RC Glied zu
> lösen

Wie sieht diese Entprellung mittels RC Glied aus?

Gruß

von Noname (Gast)


Lesenswert?

Such mal ein bisschen.
In den Threads zu dem Thema gibt es auch ein Dokument von Peter 
Dannegger resp. Atmel wie das funktioniert.
Wenn Du das verstanden hast, dann kannst Du es auch in Assembler 
schreiben.

Ich weiss das ist jetzt bösartig von mir. Aber ich habe keine Lust für 
Dich zu suchen.

Jedenfalls solltest Du Dir die Tastenerkennung im Interrupt gleich mal 
abschminken. Und das RC-Geraffel brauchst Du nicht.

von PIC (Gast)


Lesenswert?

nedim muratovic schrieb:
> Der ideale weg wäre doch die Prellng softwaremäsig zu lösen und die
> Taster über ISR.
> Wenn das überhaupt geht.

Du meinst wohl hardwaremäßig? Da kenn ich mich nicht wirklich so gut 
aus. Habe glaub ich mal gelesen, dass man die so nicht gnaz entprellen 
kann.

Der Mikrocontroller ist doch im Allgemeinen sowieso nicht wirklich 
ausgelastet. Von daher steht einem ständigem Abfragen doch nichts im 
Weg.

Man müsste halt vermutlich nur darauf achten, dass dies zuverlässig 
zyklisch geschieht.

Viele nutzen ja auch Timer um einen zyklischen Interrupt auszulösen um 
so eine zuverlässige Zeitbasis für ihr Programm zu schaffen. In der ISR 
werden dann Flags gesetzt etc., welche innerhalb der Superloop 
ausgewertet werden und so kann man dann auch bspw. seine Taster 
entprellen.

von PIC (Gast)


Lesenswert?

Noname schrieb:
> Jedenfalls solltest Du Dir die Tastenerkennung im Interrupt gleich mal
> abschminken.

Warum genau?

von nedim m. (d_caine)


Lesenswert?

Ja, warum genau ?

von Peter D. (peda)


Lesenswert?

PIC schrieb im Beitrag #2747324:
> Der Mikrocontroller ist doch im Allgemeinen sowieso nicht wirklich
> ausgelastet. Von daher steht einem ständigem Abfragen doch nichts im
> Weg.

Genau so isses!

Fast jede Anwendung braucht einen Timer als Zeitbasis. Und da noch etwa 
10 Instruktionen mit reinzupacken, da lacht die CPU nur drüber.

Auch habe nur wenige MCs 8 Interrupteingänge.


Peter

von Thorsten S. (whitejack) (Gast)


Lesenswert?


von Hannes L. (hannes)


Lesenswert?

PIC schrieb im Beitrag #2747245:
> Sondern diese werden wohl tatsächlich eher "gepollt"

Richtig, dabei hat sich ein Intervall von 10 bis 30 ms als optimal 
erwiesen. Dies sollte allerdings nicht durch eine Warteschleife, sondern 
durch einen Timer realisiert werden. Dieser kann aber nebenher noch 
andere Dinge tun, geht dem System also nicht verloren.

> und dann je nach
> dem aktuellen Status des Tasters ihn dann noch ein paar mal mehr
> abfrägt, wie weiter oben ja auch schon mal beschrieben wurde, indem ein
> Zähler (beispielsweise mit 10 initialisiert) jede 1 ms dekrementiert
> wird.

Zu umständlich und zu ressourcenfressend.

Es braucht außer einem Temp-Register weitere 4 Register für folgende 
Zwecke (Registernamen gemäß Routine von Timm, ich benutze andere Namen, 
PeDa wieder andere):

KEYstA  ;letzter gültiger Status der 8 Tasten
KEYchA  ;Flags der 8 Tasten mit der Info, dass sie neu betätigt wurden
KEYctA1 ;Bit 0 der 2-Bit-Zähler der 8 Tasten
KEYctA2 ;Bit 1 der 2-Bit-Zähler der 8 Tasten

Ich kommentiere jetzt mal mal die Routine von Timm, wobei mir Timms 
Kommentare verdammt bekannt vorkommen (von PeDa sind sie nicht...):

Timm Thaler schrieb:

> in  TEMP1, PINswA    ;Tasten einlesen, low-aktiv

Hier erfolgt das Einlesen des neuen Zustandes der 8 Tasten in ein 
temporäres Register.

> com  TEMP1           ;Tasten invertieren

Die folgende Logik erwartet H-aktive Eingangssignale von 8 Tastern. Die 
Taster sind aber gegen GND geschaltet, um die internen Ziehwiderstände 
(PullUp) nutzen zu können. Daher muss das Eingangssignal invertiert 
werden.

> eor  TEMP1, KEYstA   ;veränderte Tasten high

Durch EXOR mit dem zuletzt gültigen Zustand werden nur die Tastenbits 1, 
die anders sind als der letzte gültige Zustand. Denn Gleichheit ergibt 
0, Ungleichheit ergibt 1. Es werden 8 Tasten gleichzeitig verglichen. In 
TEMP1 stehen jetzt die Bits auf 1, deren Taster einen anderen Zustand 
haben als der zuletzt als gültig erklärte Status in KEYstA.

> and  KEYctA1, TEMP1  ;unveränderte Prellzähler Bit0 löschen
> and  KEYctA2, TEMP1  ;unveränderte Prellzähler Bit1 löschen

In diesen beiden Zeilen werden beide Prellzählerbits der Tasten 
gelöscht, deren Bits in TEMP1 0 sind, also die denselben Zustand wie der 
zuletzt gültige Status haben. Die Prellzähler der Tasten, die anders 
sind, bleiben erhalten. Die beiden Register enthalten 8 separate Zähler, 
von denen jeweils das Bit0 in dem einen Register liegt, das Bit 1 im 
anderen.

> com  KEYctA1         ;Prellzähler Bit0 inc
> eor  KEYctA2, KEYctA1;Prellzähler Bit1 inc

COM invertiert das Bit 0 aller Zähler, unabhängig vom Zustand der 
Tasten.
EOR übernimmt den Übertrag aus Bit 0 der 8 Zähler in Bit 1. Dabei werden 
alle 8 Zähler erhöht, unabhängig davon, ob eine Taste anders als vorher 
ist. Die Zähler unveränderter Tasten erreichen aber ihren Endwert (3, 
also beide Bits gesetzt) nicht, da sie zuvor immer wieder per AND 
gelöscht wurden.

> and  TEMP1, KEYctA1  ;veränderte Tasten nur erhalten, wenn in
> and  TEMP1, KEYctA2  ;Prellzähler beide Bits gesetzt

Nun werden nur die Tastenänderungen in TEMP1 erhalten, deren Prellzähler 
den Zählerstand 3 erreicht haben, also deren Bits in beiden 
Zählregistern gesetzt sind. Ist nur eines der Zählerbits 0, dann wird 
das Bit in TEMP1 auch 0. Es bleiben also nur die veränderten Tasten 
stehen, deren Prellzählerstand 3 ist, die also 4 mal hintereinander als 
verändert erkannt wurden.

> eor  KEYstA, TEMP1   ;veränderte Tasten toggeln, neuer gültiger Status

Nun werden die Bits im "gültigen Status" getoggelt (gekippt, 
umgeschaltet), die in TEMP1 1 sind, also deren Taster 4 Runden lang 
anders waren als der gültige Zustand.

> and  TEMP1, KEYstA   ;nur gedrückte Tasten berücksichtigen

Jetzt werden die Bits in TEMP1 "rausgeschmissen" (gelöscht), an denen 
KEYstA 0 ist, also deren Taster nicht betätigt sind. Übrig bleiben nur 
die Bits, deren Taster von der Entprellung als "betätigt" erkannt 
wurden. Da nur die veränderten Tastenbits (TEMP1) mit den betätigten 
Tastenbits (KEXstA) verANDet werden, bleiben nur die Bits erhalten, die 
in dieser Entprellrunde (Pollrunde) als "neu betätigt" erkannt wurden. 
Und das pro Betätigung nur 1 mal, egal wie lange der Taster noch 
betätigt bleibt.

> or   KEYchA, TEMP1   ;neue Tasten merken

Und diese Bits, die sagen, dass der Taster JETZT (nach 4 maligem 
Erkennen als betätigt) als AKTIV übernommen wurde, werden jetzt in 
KEYchA gesetzt.

Dieses Register kann nun von der Mainloop oder einem Job davon bitweise 
ausgelesen und darauf reagiert (verzweigt) werden. Dabei führt die 
Mainloop (oder deren Job) die vorgesehene Aktion aus und löscht dieses 
Bit in KEYchA, denn die Arbeit ist ja getan und soll erst dann wieder 
getan werden, wenn der zugehörige Taster losgelassen und wieder betätigt 
wurde.

Das Geniale an dieser Routine ist der geringe Ressourcenberbrauch. Es 
werden zum wirksamen Entprellen von 8 Tasten neben dem Tempregister nur 
4 Register (für 8 Tasten!!!) gebraucht. Der zyklisch aufgerufene Code 
hat nur 13 Instruktionen und braucht (auf dem AVR bei 1 MHz Takt) nur 13 
Takte (mit Interrupt-Aufruf etwa 25 Takte) alle 25000 Takte. Das sind 
etwa 0,1% der Verfügbaren Rechenleistung.

Durch ein paar wenige Zusätze lässt sich auch das Loslassen der Tasten 
separat erkennen, oder eine Repeat-Funktion hinzufügen.

Ich hoffe, diese ausführliche Analyse der Routine hilft etwas zu deren 
Verständnis.

...

von Timm T. (Gast)


Lesenswert?

@Hannes: Danke für die Analyse, so isses.

Der Code von Peter ist für mehrere Tasten extrem effizient. Da ich im 
betreffenden Programm 10 Tasten an 2 Ports einlese, steht da xxA, es 
gibt folglich das Gleiche nochmal mit den Registern xxB.

Für eine oder zwei Tasten kann man einfach je Taste ein Register nehmen, 
dieses bei jedem Timerdurchlauf links schieben und den Pinzustand in Bit 
0 schreiben. Dann prüfen:
Register ist 0x80 => Taste gerade gedrückt (low-aktiv, Pegelwechsel 
high=>low)
Register ist 0x00 => Taste bleibt gedrückt (low-aktiv)
Register ist 0xFF => Taste losgelassen (pull-up)
Damit hat man 8 Durchläufe zum Entprellen, braucht pro Taste ein 
Register / SRAM-Byte, und wenig Code.

Andere Verfahren mit extra Zähler sind immer aufwändiger.

Direkten Tasten-Interrupt macht man nicht, weil:
- nicht so viele ExtInts vorhanden, bei PinChange Interrupt der µC auf 
jeden Mist regiert
- Tasten zu langsam sind, um einen Interrupt zu rechtfertigen
- Störungen auf der Zuleitung den µC über den Interrupt ausbremsen 
könnten
- eventuell noch andere Sachen über die Pins / den Port laufen, die den 
Interrupt auslösen
Tasten an ExtInt sind nur sinnvoll, um den µC per Tastendruck aufwecken 
zu können, danach sollte der ExtInt aber abgeschalten werden (und vorm 
Schlafenlegen natürlich wieder ein)

Externe Entprellung über RC benötigt natürlich zusätzliche Bauteile und 
ist eigentlich nicht zu rechtfertigen, wenn man das Problem so schnell 
und effizient per Code lösen kann.
Ausserdem stören externe RCs eventuell in der Schaltung. Obiger Code ist 
aus einem Programm, wo an den Pins noch LEDs hängen, die die 
Tastenzustände anzeigen, es wird zwischen den Tasten und LEDs gemuxt, wo 
soll da ein RC hin? Auch bei einer Tastenmatrix gehen externe RCs nicht 
sinnvoll, der Entprell-Code aber durchaus.

Ob man die Entprellung im Main oder in der Timer-ISR macht, hängt vom 
Programm und Programmierstil ab. Grundsätzlich gilt: ISRs so kurz wie 
sinnvoll möglich.

von Hannes L. (hannes)


Lesenswert?

Timm Thaler schrieb:
> Für eine oder zwei Tasten kann man einfach je Taste ein Register nehmen,

Für eine Taste gibt es ja noch den hocheffizienten Code von Peter 
Dannegger, der mit 4 Bit auskommt:
1
.def srsk=r2        ;Kopie SREG
2
.def key_reg=r23    ;Entprellzähler
3
    .equ key_state=2    ;Bit Tastenstatus
4
    .equ key_press=3    ;Bit Tastenflag
5
6
...
7
8
takt:           ;ISR Timer...
9
 in srsk,sreg               ;SREG sichern
10
 ...
11
entprellung:    ;Entprellung bei Peter Dannegger abgeguckt
12
 bst key_reg, key_state     ;alten Tastenzustand ins T-Flag
13
 sbis key_in, key_pin       ;Taste betätigt? nein...
14
 rjmp _deb_x0               ;ja...
15
 brts _deb_11               ;nein, vorher auch nicht? nein...
16
 rjmp _deb_10               ;ja, Unterschied
17
_deb_x0:
18
 brts _deb_10               ;vorher auch betätigt? nein, Unterschied...
19
_deb_00:
20
_deb_11:                    ;key_state gleich key_pin
21
 cbr key_reg,63             ;reset Prellzähler
22
_deb_10:
23
_deb_01:                    ;key_state ungleich key_pin
24
 sbrs key_reg, key_press
25
 inc key_reg                ;hochzählen
26
 rjmp takt_ende             ;fertig...
27
 ...
28
takt_ende:      ;Ende der Timer-ISR
29
 out sreg,srsk              ;SREG wiederherstellen
30
 reti
31
32
33
mainloop:       ;Hauptschleife
34
 sbrc key_reg,key_press     ;Taster erneut gedrückt? nein...
35
 rjmp neuimp                ;ja, abarbeiten...
36
 ...
37
 sleep                      ;schlafen gehen
38
 rjmp mainloop              ;nochmal...
39
40
neuimp:         ;Job
41
 ...
42
 rjmp mainloop

Die oberen 4 Bit können dann immernoch für andere Flags genutzt werden.

...

von Timm T. (Gast)


Lesenswert?

Vielleicht hab ich ja den Punkt nicht verstanden, aber im Vergleich zu 
Byte schieben, Bit setzen und Bitmuster auswerten scheint mir der 
Aufwand zur Einsparung von 3 Bits doch recht hoch.

von Hannes L. (hannes)


Lesenswert?

Timm Thaler schrieb:
> scheint mir der
> Aufwand zur Einsparung von 3 Bits doch recht hoch.

Das sehe ich nicht so. Die eigentliche Entprellung kommt mit 9 
Instruktionen daher. Das sieht nur aufgrund der vielen Labels so groß 
aus.

...

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