Forum: Mikrocontroller und Digitale Elektronik Bitte um Erklärung der Entprellroutine


von Markus (Gast)


Lesenswert?

Hallo,

ich hab ein Beispielprogrammg geschrieben (ASM) mit dem ich per Taster
(an PD3 - INT1) eine LED an PB3 ein- und ausschalten kann. Läuft alles
auf einem 2313. Momentan hänge ich beim Entprellen fest. Ich hab in der
Codesammlung das Beispiel von Peter Dannegger gefunden. Es handelt sich
um den hier:
######################################
  in  savesreg, sreg

  bst  key_reg, key_state   ;Bit Store from Register to T
  sbis  key_in, key_pin    ;Skip if Bit in I/O Register is Set
  rjmp  _deb_x0      ;Sprung _deb_x0
  brts  _deb_11      ;Branch if T Flag is Set
  rjmp  _deb_10      ;Sprung _deb_10
_deb_x0:
  brts  _deb_10      ;Branch if T Flag is Set

_deb_00:
_deb_11:        ;key_state == key_pin
  cbr  key_reg, 0x03    ;reset debounce counter -- clear bit in register
_deb_10:
_deb_01:        ;key_state != key_pin
  sbrs  key_reg, key_press  ;Skip if bit in register is set
  inc  key_reg      ;count up

  out  sreg, savesreg
  reti
######################################

Leider will sich mir der Sinn des Codes nicht erschliessen :-( ! Kann
mir das jemand erklären? Das ganze wird ja abgearbeitet sobald der T0
Interrupt auftritt. Das sichern der Statusregister ist mir auch noch
klar... :-). Leider fehlen mir jedoch schon die Deklarationen für
key_reg, key_state, key_in, key_pin, key_press.

Ich bedanke mich schon mal für eure Bemühungen.

Gruss, Markus

von ...HanneS... (Gast)


Lesenswert?

Das hat Peter aber in der Codesammlung erklärt, in dem Beitrag, in dem
er den Code veröffentlicht hat. Es ist nicht leicht zu verstehen, ich
habe auch etwas gebraucht. Das Verhalten der beiden Zählregister ist
mir noch nicht hundertprozentig klar, aber sie zählen halt...

Ansonsten ist es die beste Tastenentprellung (betreffs Aufwand und
Nutzen), die ich je gesehen habe. Ich nutze sie inzwischen auch.

...

von ...HanneS... (Gast)


Lesenswert?

Achja, schieb's mal durch den Simulator und schau den Registern auf die
Bits, vergleiche dabei das, was du erwartest mit dem, was passiert.

Vielleicht helfen dir ja die Kommentare in meiner Version:
;-----
.def scan=r5               ;Scannwert Tastenport
.def tz0=r6                ;Tasten-Prellzähler Bit0
.def tz1=r7                ;Tasten-Prellzähler Bit1
.def tas=r8                ;Tastenstatus (gültig)
.def tfl=r19               ;Flags für Tasten, die gedrückt wurden

;und in der ISR:

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

Selbstverständlichkeiten, wie SREG-Sicherung, Timer-Reload usw. wurden
bewusst weggelassen. Das Programm läuft im Timer0-Interrupt, der alle
10ms auftritt (also alle 10000 Takte).

...

von ...HanneS... (Gast)


Lesenswert?

Da hat natürlich wieder mal der Logikteufel zugeschlagen. Ist genau
umgekehrt:

 in scan,tap               ;Tastenport einlesen (gedrückt=L)
 com scan                  ;invertieren (gedrückt=H)

...

von Markus (Gast)


Lesenswert?

Hi,

danke für deine Erklärungen, ich werd mir das heut abend zu Gemüte
führen. Es bringt mich aber bestimmt weiter.

Gruss, Markus

von Peter D. (peda)


Lesenswert?

@Markus,

ich habe den Eindruck, daß Hannes eine völlig andere Routine meint.

War aber auch zu erwarten, wenn man keine URL angibt, worauf man sich
bezieht und der andere nicht Gedanken lesen kann.

Ich kann jedenfalls nicht wissen, wo ich mal was geschrieben habe, dazu
sind es einfach zu viele Beiträge (>1000).


Peter

von ...HanneS... (Gast)


Lesenswert?

Stimmt...

Da war ich wohl zu voreilig und habe den Programmcode überlesen
(key_state... kenn ich doch, das ist doch... - Isses aber doch
nicht...) Sorry, da werde ich wohl demnächst besser aufpassen
müssen...

Aber ich werde mir die Quelle der obigen Routine mal suchen und
versuchen diesen Code zu verstehen, jetzt auf Anhieb gelingt mir das
auch nicht...

...HanneS...
(der sich jetzt schämt...)

von ...HanneS... (Gast)


Lesenswert?

Hi...

Um die Routine zu verstehen, muss man sich erstmal das Register key_reg
und seine Bits genauer ansehen:

key_reg ist ein oberes Register, von dem nur das untere Nibble
für die Tastenentprellung genutzt wird. Das obere Nibble kann
für andere "Flags" im Programm benutzt werden.

Die benutzten Bits in key_reg:
 0 Prellzähler, unteres Bit,
 1 Prellzähler, oberes Bit,
   wird bei Tastenänderung hochgezählt und bei Gleichheit
   mit dem alten Tastenzustand gelöscht. Daher werden
   Änderungen nur übernommen, wenn diese 4 mal direkt
   hintereinander (also bei Überlauf in Bit 2) auftreten.
 2 key_state, (gültiger) Tastenstatus, wird durch Überlauf
   des Prellzählers getoggelt, also nur wenn der Taster 4
   Runden lang stabil dem entgegengesetzten Zustand von
   key_state entspricht. L bedeutet gedrückte Taste.
 3 key_press, ein Flag, das anzeigt, dass der Taster gedrückt
   wurde. Wird durch Überlauf von Bit 2 (key_state) gesetzt,
   wenn dieses durch Hochzählen des Prellzählers von H nach
   L toggelt und durch das Hauptprogramm beim Abarbeitung des
   Tastendruckereignisses wieder gelöscht.

Dann habe ich versucht, die Routine verständlich zu kommentieren:

_timer0_ovf:     ;Interrupt-Service-Routine Timer0-Überlauf
 in  savesreg, sreg       ;SREG sichern (Exclusiv-Register)
 bst  key_reg, key_state  ;alten (gültigen) Tastenzustand
                          ;zum Vergleichen ins T-Flag, dabei
                          ;ist L=gedrückte Taste
 sbis  key_in, key_pin    ;Ist die Taste gedrückt?
                          ;überspringe, wenn nicht
 rjmp  _deb_x0            ;ja, Taste ist gedrückt (L), weg hier
_deb_x1          ;Debounce bei unbetätigter Taste (1, H)
 brts  _deb_11            ;(Taste ist H) Taste gleich T-Flag??
                          ;springe weg, wenn ja, also wenn
                          ;keine Änderung auftrat
 rjmp  _deb_10            ;Änderung aufgetreten, weg hier...
_deb_x0:         ;Debounce bei gedrückter Taste (0, L)
 brts  _deb_10            ;(Taste ist L) Taste gleich T-Flag??
                          ;springe weg, wenn nicht, also wenn
                          ;eine Änderung auftrat
_deb_00:         ;Debounce alter Wert=0, neuer Wert=0 (Gleichheit)
_deb_11:         ;Debounce alter Wert=1, neuer Wert=1 (Gleichheit)
                 ;key_state == key_pin (also keine Änderung)
 cbr  key_reg, 0x03       ;Prellzähler löschen (Bit 0 und 1)
                          ;reset debounce counter (dasselbe...)
_deb_10:         ;Debounce alt=1, neu=0 (Ungleichheit, Änderung)
_deb_01:         ;Debounce alt=0, neu=1 (Ungleichheit, Änderung)
                 ;key_state != key_pin (also Änderung)
 sbrs  key_reg, key_press ;Tastenflag gesetzt? (also gültigen
                          ;Tastendruck noch nicht vom Hauptprogramm
                          ;"abgeholt") - überspringe, damit der
                          ;Prellzähler bis zum Abarbeiten und
                          ;Quittieren vom Hauptprogramm angehalten
                          ;wird
 inc  key_reg             ;Prellzähler erhöhen

 out  sreg, savesreg      ;SREG wiederherstellen
 reti                     ;fertig...

Betrachtet man das untere Nibble des key_reg als einen
4-Bit-Zähler (0...15) dann kann man den Ablauf so sehen:

0: Tastenflag abgeholt (Bit3=0),
   Tastenstatus = gedrückt (Bit2=0),
   Prellzähler=0...
1: Tastenflag abgeholt (Bit3=0),
   Tastenstatus = gedrückt (Bit2=0),
   Prellzähler=1...
2: Tastenflag abgeholt (Bit3=0),
   Tastenstatus = gedrückt (Bit2=0),
   Prellzähler=2...
3: Tastenflag abgeholt (Bit3=0),
   Tastenstatus = gedrückt (Bit2=0),
   Prellzähler=3...
4: Tastenflag nicht gesetzt (Bit3=0),
   Tastenstatus = nicht gedrückt (Bit2=1),
   Prellzähler=0...
5: Tastenflag nicht gesetzt (Bit3=0),
   Tastenstatus = nicht gedrückt (Bit2=1),
   Prellzähler=1...
6: Tastenflag nicht gesetzt (Bit3=0),
   Tastenstatus = nicht gedrückt (Bit2=1),
   Prellzähler=2...
7: Tastenflag nicht gesetzt (Bit3=0),
   Tastenstatus = nicht gedrückt (Bit2=1),
   Prellzähler=3...
8: Tastenflag gesetzt (Bit3=1),
   Tastenstatus = gedrückt (Bit2=0)
   Prellzähler=0
   Prellzähler ist gesperrt, bis Tastenflag
   vom Hauptprogramm gelöscht wird, was
   Zählerstand 0 entspricht...

Entspricht dabei die tatsächliche Tastenstellung dem gespeicherten
Tastenstatus, so werden Bit0 und Bit1 gelöscht, wodurch der Zähler auf
Zählerstand 0 oder 4 zurückfällt und am Weiterzählen gehindert wird.
Der Taster muss also 4 Abfragen hintereinander stabil auf dem
Gegenpegel liegen, damit seine Änderung übernommen wird.

Korregiert mich, falls ich Blödsinn geschrieben habe...

...HanneS...

von Jörg S. (mitchell)


Lesenswert?

Hallo,
ich bin Neuling im Programmieren, habe vor ca. 14 Tagen damit 
angefangen.

Ich habe da eine Frage zu den Interuppts.
Ich verwende für eine RGB-Steuerung die Timer für die PWM, kann ich da 
noch per Interuppt ( Timer0 Überlauf z.B. ) eine Tastenabfrage 
realisieren?

mein Code für die PWM:

; *** Timer Initialisieren ***

  ldi  r16, 0xC3
  out  TCCR0A, r16  ; TCCR0A init., fast PWM auf OC0A (R)

  ldi  r16, 0x03    ; Vorteiler 1/64
  out  TCCR0B, r16

  ldi  r16, 0xF1
  out  TCCR1A, r16  ; TCCR1A init., fast PWM auf OC1A/OC1B (G,B)

  ldi  r16, 0x0B    ; Vorteiler 1/64
  out  TCCR1B, r16

Ist es damit noch möglich eine Tastenabfrage per Interrupt zu erstellen?

mfg
Jörg

von Stephan H. (stephan-)


Lesenswert?

@mitchell,

Hijacking eines Thread macht man nicht.
Stelle bitte Deine Frage vorne im Forum !
Sonst kann es sein das sie unter geht.

von Hannes L. (hannes)


Lesenswert?

Stephan Henning wrote:
> @mitchell,
>
> Hijacking eines Thread macht man nicht.
> Stelle bitte Deine Frage vorne im Forum !
> Sonst kann es sein das sie unter geht.

Daran bin ich wohl schuld. Ich gab ihm in einem anderen Thread den Link 
auf diesen Thread, da hier seine vorige Frage beantwortet wurde.

---

Einen Überlauf-Interrupt sollte man eigentlich immer unterbringen 
können. Alternativ sollte man sich überlegen, ob LEDs wirklich 
Hardware-PWM brauchen. Software-PWM sollte für LEDs eigentlich 
ausreichend schnell sein.

...

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.