mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Entprellen von Timerinterrupttasterabfragen


Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

ich bin dabei, ein Programm von mir durch eine Zusatzfunktion zu 
erweitern.

Also, ein Timer fragt alle paar µs einen Pin ab, ob dieser ein LOW hat, 
als der daran angeschlossene Schalter gedrückt ist (gegen Masse). Wenn 
dieser gedrückt ist, soll er etwas machen, also so:

if(!(PINB &(1<<PINB0)))
{
   mache etwas;
}

Das Programm soll nun so erweitert werden, dass beim ersten mal drücken 
etwas gemacht wird und beim zweiten mal drücken etwas anderes gemacht 
wird und beim dritten mal drücken wieder das erste Ereignis ausgeführt 
wird usw, also nach dem Schema:

if(!(PINB &(1<<PINB0)))
{
   if(PORTD &(1<<PORTD0))
   {
      PORTD &= ~(1<<PORTD0);
   }
   else
   {
      PORTD |= (1<<PORTD0);
   }
}

Das Problem dabei stellt die Entprellung dar. Die ganze Abfrage 
geschieht ja innerhalb einer Timer ISR. Da kann ich ja schlecht mit 
Endlosschleifen entprellen oder ein delay einfügen etc., wie ich es 
sonnst außerhalb einer ISR gemacht hätte. Auch das Setzen einer 
Hilfsvariable macht aus meiner Sicht keinen Sinn. Zurzeit rast er eben 
laufend beide Schleifen durch, so dass das Ergebnis einem Zufall 
entspricht.

Hat jemand eine Idee? Leider muss die PIN/PORT-belegung so beibehalten 
werden, weil die Schaltung dazu schon steht. Ich kann also die Taster 
nicht auf einem normalen Interrupt-pin verbinden.

Vielen Dank!

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach so, beide Codebeispiele befinden sich natürlich in:

ISR(TIMER0_OVF_vect)
{
    CODEBEISPIEL
}

Autor: Jadeclaw Dinosaur (jadeclaw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was ist das für ein Controller? AVR?
Wenn ja, dann reicht ein Kondensator vom Eingangspin nach Masse.
AVR-Controller haben an allen Eingängen einen Schmitt-Trigger,
der die Flanke wieder begradigt. Bei Benutzung des internen PullUp
reichen typischerweise 100nF keramisch. Bei niedrigeren PullUps den
Kondensator entsprechend vergrößern, evtl. zusätzlich einen
Serienwiderstand Richtung Taster.

Gruß
Jadeclaw.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein Kondensator am Eingangspin? Das Problem resultiert doch daraus, wie 
das Programm programmiert ist. Der Timer fragt ein paar hundert mal pro 
Sekunde die Eingangspins ab, ob dort ein LOW anliegt. Wenn ich nun die 
Abfrageanweisung so benutze, wie oben beschrieben, dann ists doch 
Zufall, welche Anweisungsbedingung gerade eingegangen wird.

Wie könnte man das Problem denn programmiertechnisch lösen?

Vielen Dank.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach so, ja ist ein AtMega8.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Das Problem dabei stellt die Entprellung dar. Die ganze Abfrage
> geschieht ja innerhalb einer Timer ISR. Da kann ich ja schlecht mit
> Endlosschleifen entprellen oder ein delay einfügen etc., wie ich es
> sonnst außerhalb einer ISR gemacht hätte.

Die Schleife und die Delays stören überhaupt nicht, weil man sie
weglassen kann: Die Schleife etfällt dadurch, dass der Interrupthandler
periodisch aufgerufen wird. Die entfallen delays dadurch, dass zwischen
zwei Interrupts eine gewisse (einstellbare) Zeit vergeht. Du brauchst
dir also keinen grundsätzlich neuen Entprellalgorithmus zu überlegen,
sondern nur die Methode, die du außerhalb des Interrupthandlers benutzen
würdest, etwas anzupassen.

Es gibt aber schon viel Fertiges zu diesem Thema, bspw. dieses hier:

  http://www.mikrocontroller.net/articles/Entprellun...

Insbesondere dann, wenn du mehrere (bis zu 8) Tasten entprellen
möchtest, stellt diese Routine so ziemlich das Optimum dar.

Autor: preller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,

programmiertechnisch ist das eigentlich nicht schwer.

Sagen wir dein Taster hat eine Prell-Zeit von 3 ms.. also bis der 
Low-Pegel "konstant" für die Dauer des Tastendrucks anliegt.

Nun Tastest du eben nur alle 10 ms ab!
Ein "normaler" Tastendruck dauert zwischen 100 ms und 300 ms... je nach 
dem wer und wie gedrückt wird!

Nun, einfach den letzten Zustand merken.

Z.b. letzer Zustand gedrückt.. neuer Zustand auch gedrückt.
-> dann tue etwas!

Das ganze ist dann sehr sicher, da das Signal konstant über 20 ms auf 
low-pegel liegt.

MFG

Autor: preller (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
übrigens... zum merken des letzten Zustands keine globalen variablen, 
sondern statische variablen in der ISR benutzen...

z.b.

static unsigned char letzer_zustand = ZUSTAND_GEDRUECKT;

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also,

ich hab nun nochmal etwas nachgelesen und bemerkt, dass ich ja einfach 
nur auf die Flanke abfragen muss und nicht auf den Zustand, so wie das 
hier beschrieben ist:

http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten

Das Beispiel ist aber leider in asm und nicht c.

@yalu, den Artikel hatte ich mir auch angeschaut, aber ich find den Code 
für diese einfach Anwendung ein wenig zu kompliziert. Geht das nicht 
auch einfacher, so wie in den oben genannten Link mit xor, also dem ^ 
Operator?

@preller, warum static?

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, ich hab mir jetzt mal folgenden Codeschnippsel überlegt, der sich in 
der Timer-ISR befindet:

static uint8_t alt=0, neu=0, onoff=0;

neu=PINB & (1<<PINB0);
if((neu^alt) == (!(PINB &(1<<PINB0))))
{
   if(onoff==0)
   {
  PORTD |= (1<<PORTD0);
        onoff = 1;
   }
   else
   {
        PORTD &= ~(1<<PORTD0);
        onoff = 0;
   }

Leider funktioniert die Abfrage aber nicht immer. Worann könnte das 
liegen? Ist mein Ansatz zur Erkennung der Flanke korrekt?

Danke.

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"alt" wird nirgendwo gesetzt?

...
alt=neu                  <-- ?
neu=PINB & (1<<PINB0);
...

Beachte auch, dass "alt" und "neu" globale Variablen sein müssen; sie 
dürfen nicht beim Verlassen der Routine zerstört werden.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> So, ich hab mir jetzt mal folgenden Codeschnippsel überlegt, der sich
> in der Timer-ISR befindet:

Da wird aber noch nichts entprellt. Nur die Flanke abzufragen, reicht
nicht, da während des Prellens ganz viele Flanken entstehen.

Um das Prellen wegzubekommen, musst du während der maximalen Prellzeit
(z.B. 10 ms) den Eingang mehrfach abfragen. Eine Reaktion erfolgt erst,
wenn der Eingang während dieser Zeit konstant auf low oder konstant auf
high liegt. Wechselt der Eingang während dieser Zeit, ist der
Prellvorgang offensichtlich noch nicht abgeschlossen, deswegen erfolgt
noch keine Reaktion.

> if((neu^alt) == (!(PINB &(1<<PINB0))))

Du vermischt in dieser Abfrage Bitoperationen (^, &) mit einer logischen
Operation (!). Das liefert sicher nicht das gewünschte Ergebnis.

> Beachte auch, dass "alt" und "neu" globale Variablen sein müssen; sie
> dürfen nicht beim Verlassen der Routine zerstört werden.

Global müssen sie nicht unbedingt sein. Es reicht, dass sie als static
deklariert sind, das ist schon ok.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, die ganze Routine befindet sich doch innerhalb eines Timers. Das 
müsste doch zur Entprellung ausreichen. Wenn der Taster nun nach dem 
"Erstkontakt" weiterprellt, geschieht dies in einer Programmphase nach 
der Timer-ISR. Ein weiteres Prellen wird somit ignoriert. Das Problem 
ist ja, dass bei der Routine mit dem xor-Operator z.Zt. irgendwie nicht 
nur die Flanke an sich abgefragt wird, sondern auch der Zustand, sprich, 
das Programm kehrt immer wieder in die Anweisung rein, wie der Taster am 
Eingang gedrückt ist und nicht nur zu dem Zeitpunkt, wenn er gedrückt 
wird.

Ich bin langsam echt am verzweifeln, rätzel hin und her. Ich wäre um 
jeden Tip oder Lösung(sansatz) sehr dankbar.

Autor: AVRFan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich mach das bei Tasten u. ä. in Assembler meistens so, dass ich 
die Statusbits (bei Tasten: gedrückt/nicht gedrückt) über den Befehl 
'rol' (rotate left) in ein Register reinschieb.  Unmittelbar davor wird 
der Registerwert noch mit 1 geANDet.

Das ist in Assembler mit fünf Instruktionen erledigt ("Key" ist das 
besagte Register):
clc
sbic  PORTD, PD3     ; Taste angeschlossen an PD3
sec
; jetzt enthält das C-Flag die Tastenzustandsinformation:
; Taste down <--> C = 0, Taste up <--> C = 1
andi  Key, 1
rol   Key

Was bewirkt das? Nun, das Register "Key" hat dann zu jedem Zeitpunkt 
genau einen von vier möglichen Werten, nämlich 0, 1, 2, oder 3, und 
jeder Wert hat eine bestimmte Bedeutung:

Bei 3 = 0b11 ist die Taste ungeändert up,
bei 0 = 0b00 ungeändert down,
bei 2 = 0b10 wurde sie gerade gedrückt,
bei 1 = 0b01 gerade losgelassen.

Abgefragt wird die Taste natürlich timergesteuert im 10 oder 20 ms 
Zeitraster (nicht zu kurz, damit der µC noch mit der Abarbeitung aller 
Tasks hinterherkommt, aber auch nicht zu lang, damit Reaktionen auf 
Tastendrücke vom Benutzer noch als unmittelbar wahrgenomen werden) 
während der gesamten Programmlaufzeit.

Der "Key"-Wert wird dann irgendwo im Hauptprogramm z. B. auf "2" 
getestet und ggf. die zugehörige Aktion - meistens das Starten 
irgendeines Software-Timers, von dem dann wiederum andere Sachen 
abhängen - ausgelöst.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ja, die ganze Routine befindet sich doch innerhalb eines Timers. Das
> müsste doch zur Entprellung ausreichen.

Dann sind aber die paar µs aus deinem ersten Post viel zu wenig. Die
Prellzeiten bei den meisten Tastern liegen im ms-Bereich.

Ich glaube, ich habe jetzt auch verstanden, was du mit der dubiosen
If-Abfrage erreichen möchtest. Probier's mal mit
  if((neu^alt) && (!(PINB &(1<<PINB0))))

oder besser
  if((neu^alt) && !neu)

Damit, mit einem Timer-Intervall von ein paar ms statt µs und mit einem
  alt = neu;

am Ende des Interrupthandlers sollte die Sache funktionieren und
zumindest das Prellen beim Schließen und Öffnen des Tasterkontakts
beseitigt werden.

Wird bei gedrückter Taste der Finger etwas bewegt, so dass die
Kontaktflächen leicht aufeinander "schaben", kann es passieren, dass der
Kontakt kurzzeitig abreißt, was dann ebenfalls zu einer Fehlauslösung
führt. Dieses Problem lässt sich nur dadurch beseitigen, dass überprüft
wird, ob der Taster wirklich für eine bestimmte Zeitdauer losgelassen
wurde.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thomas wrote:
> Also, ein Timer fragt alle paar µs einen Pin ab, ob dieser ein LOW hat,

Das ist Unfug, kein mechanischer Kontakt schaltet innerhalb weniger µs, 
sondern von ms.
Zusätzlich hat ein Mensch etwa 300ms Reaktionszeit.
Ein optimales Abfrageintervall ist 5..50ms.

Hier mal ein Beispielcode:

Beitrag "Universelle Tastenabfrage"


Peter

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, ich habs mit Hilfe des Codes von yalu hinbekommen. Komischerweise 
reagiert er aber nicht bei jedem Tastendruck, also sagen wir mal im 
Verhältnis, 5:1, als 5 mal funktionierts und beim 6. mal Drücken eben 
nicht.

Die Zeit des Timers beträgt 250us.

Der angeschlossene Taster ist kein mechanischer, sonder ein Piezo 
Taster. Prellen die überhaupt? Könnte mir vorstellen, dass beim kurzen 
Antippen keine 5-50 ms reichen, oder? Jedenfalls hab ich hier schon bei 
meinen 250 us diese Probleme.

Vielleicht zur Info, es läuft noch ein zweiter Timer, der exakt jede 
Sekunde unterbricht. In der dazugehörigen ISR steht aber nur eine Zeile 
drinn, nämlich : d++
mehr nicht. Könnten sich diese beiden Timer irgendwie beeinflussen?

Autor: Kachel - Heinz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Thomas, versuche doch einfach mal Peters Code zu analysieren. Ich nutze 
seinen Algorithmus in verschiedenen Varianten in Assembler (der C-Code 
wird nicht viel anders arbeiten) und behaupte kackfrech, dass es nichts 
gibt, das das Entprellen zuverlässiger und effektiver realisiert. Der 
Timer, in dessen ISR die Entprell-Task läuft, kann nebenher noch andere 
Aufgaben erledigen, auch das Zählen der Zeit. Ein zweiter Timer-Int. mit 
einem Intervall von 1s ist also nicht nötig. Man setzt den Timer zur 
Entprellung auf 10ms und zählt darin nebenbei die Hundertstelsekunden 
für die Zeitzählung hoch.

KH

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peters Code scheint ja wirklich das non plus Ultra zu sein, wenn hier 
jeder davon schwärmt. Ich les mich da mal rein.

Vielen Dank nochmals!

Grüße!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.