PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : kleine Entprellroutine - läuft nicht wie gedacht



registriert
16.10.2014, 09:20
Hallo,

ich habe eine kleine Routine zum Entprellen eines Tastersignals geschrieben. Ich benutze das MK2 Übungsboard mit einem 2MHz µC. Der Taster ist an Port D.3 angeschlossen. An den Ports B.1 bis B.3 sind die LEDs angeschlossen, an B.0 der Tongeber, der bei jedem Schalten einmal kurz piept. Über den externen Interrupt (bei fallender Flanke) werden die LEDs umgeschaltet (quasi in Form eines kleinen Binärzählers). Beim Drücken des Tasters wird leider öfter mehrere Male geschaltet, ich kann mir aber nicht erklären, warum das so ist.

Hier die Entprellroutine:



#define bit_3 3

.
.
.

void entprellen()
{
uint8_t zustand, alterzustand;
uint16_t countdown;
countdown = 255;

while (countdown > 0)
{
zustand = PIND & (1<<bit_3); // aktueller Schaltzustand (maskiert)
if (alterzustand != zustand) countdown = 255; // bei Änderung zum vorherigen: Countdown von vorne
else countdown--; // sonst: runterzählen
alterzustand = zustand; // Schaltzustand merken
}

return;
}


Zur Vollständigkeit die Interrupts:



ISR(INT1_vect)
{
entprellen();

temp = (PORTB>>1);
if (++temp == 8) temp = 0;
PORTB = (temp<<1);

// Ton einschalten (Timer 0 Interrupt bei Überlauf aktiv)
TIMSK0 = 1;
}

ISR(TIMER0_OVF_vect)
{
// aktuelles Ausgangssignal: Bits 1 bis 7 maskieren, bleiben unverändert
maske = PORTB & 0b11111110;

// aktuelles Ausgangssignal: Bit 0 maskieren und umkehren
temp = PORTB & 1;
temp ^= 1;

// Ausgabe des neuen Signals
PORTB = maske | temp;

// Timer zurücksetzen
TCNT0 = 190;

// Ton unterbrechen
if (++zaehler == 0) TIMSK0 = 0;
}



Ich hatte das ganze vorher schon in Assembler umgesetzt. Da funktioniert das Entprellen, beim Drücken des Tasters wird genau einmal weiter geschaltet. Aber meiner Meinung sind die C- und die Assembler-Version absolut gleichwertig.



entprellen: ; Register sichern
push r16
push r17
push r18

ldi r16, 255

debounce: ; Zustand von PORT D einlesen und maskieren
in r17, PIND
andi r17, 0b00001000

; Debounce-Zeit zurücksetzen, wenn der Schaltzustand anders ist
cpse r18, r17
ldi r16, 255
mov r18, r17

; Countdown bis 0
dec r16
brne debounce

; Register wiederherstellen
pop r18
pop r17
pop r16

ret




Wer weiß Rat?

oberallgeier
16.10.2014, 11:08
... kleine Routine zum Entprellen eines Tastersignals ... Beim Drücken des Tasters wird leider öfter mehrere Male geschaltet ...Bei meinen Tastern (die 6Zent-Klasse ebenso wie die 20Zent-Klasse) stelle ich praktisch nie ein Prellen fest (seltsamerweise??) obwohl ich selten oder nicht bewußt irgendwelche Maßnahmen gegen EMV-Einflüsse treffe. Trotzdem habe ich manchmal Entprellmaßnahmen getroffen - allereinfachste Art, ich frage den Taster mehrfach hintereinander ab. Hier ein paar Codeschnippsel, das sollte reichen um den Vorgang zu verdeutlichen. Anm.: die Taster sind gegen GND geschaltet, daher gilt: PIN ist low => Taster gedrückt.


#define IsBitSet(ADDR,BIT) (((ADDR) & (1<<BIT))?1:0)
// Fragt Bit = 1?
#define IsBitClr(ADDR,BIT) (!((ADDR) & (1<<BIT))?1:0)
// Fragt Bit ab

#define PRTtstLCD PIND
#define Tst_A 6

#define TAan IsBitClr (PRTtstLCD, Tst_A)
// Taster A gedrückt ??

if ( TAan && TAan )
// Das ist nun die eigentliche Abfrage
// .. kann natürlich öfter ver&&det werden

Und ich kann über Fehlfunktionen wirklich nicht klagen *gg* .. dh es treten keine auf.

RoboHolIC
16.10.2014, 13:35
Ich verwende diese Miniaturtaster (nicht diese gewölbten Bleche ("Knackfrösche") direkt auf der Leiterplatte) auch, aber stets mit Unbehagen.
Über das Prellverhalten habe ich noch keine Untersuchungen angstellt, aber ich bemerke häufig, dass diese Teile unter meinen Fingern trotz 1x knack-rein und 1x knack-raus gar keinen oder auch mehrere Schließereignisse produzieren.

witkatz
16.10.2014, 21:50
Aber meiner Meinung sind die C- und die Assembler-Version absolut gleichwertig.
Auch wenn du guten C-Code geschrieben hättest, sind C und Assemblerroutinen bestimmt nicht gleichwertig. Der Maschinencode, den der Compiler aus der C-Routine gemacht hat ist sicherlich länger und dauert in der Ausführung auch länger als der Assembler-Code.
Außerdem hast du im Assembler ein 8-bit Register verwendet, in C aber uint16_t. Weißt du was der Compiler aus 16-Bit Zuweisungen, Arithmetik- und Vergleichsoperatoren für einen unnötigen Maschinencode - Overhead erzeugt und was das für die Ausführungszeit bedeutet? Vielleicht liegt in der Ausführungszeit der Routine, die im Interrupt aufgerufen wird die Ursache des Problems?

registriert
20.10.2014, 18:01
Ein (spätes) Danke für die ersten Beiträge.




Der Maschinencode, den der Compiler aus der C-Routine gemacht hat ist sicherlich länger und dauert in der Ausführung auch länger als der Assembler-Code.
Außerdem hast du im Assembler ein 8-bit Register verwendet, in C aber uint16_t. Weißt du was der Compiler aus 16-Bit Zuweisungen, Arithmetik- und Vergleichsoperatoren für einen unnötigen Maschinencode - Overhead erzeugt und was das für die Ausführungszeit bedeutet? Vielleicht liegt in der Ausführungszeit der Routine, die im Interrupt aufgerufen wird die Ursache des Problems?


Das sind natürlich gute Punkte. Die Zählvariable war zuerst eine 8-Bit-Zahl. Ich hab dann eine 16-Bit-Zahl draus gemacht, um die Entprellzeit verlängern zu können. Aber in beiden Fällen macht mein Schalter das gleiche.

Und wenn die Ausführungszeit der C-Variante deutlich länger ist: Sollte das dem Entprellen nicht zu gute kommen? Das ist ja hier keine zeitkritische Anwendung, kann meinetwegen auch ne halbe Sekunde dauern :)

witkatz
04.11.2014, 10:01
Das ist ja hier keine zeitkritische Anwendung, kann meinetwegen auch ne halbe Sekunde dauern :)Da wäre ich mir eben nicht sicher. Die Entprellfunktion wird in dem C-Projekt in einer Interrupt-Routine aufgerufen, wenn ich das richtig deute. Interrupt-Routinen sollten immer zeitkritisch betrachtet werden, v.a. wenn die MCU gleichzeitig noch andere Interrupts wie z.B. die Timer-Interrupts noch bedienen soll.