Forum: Mikrocontroller und Digitale Elektronik Software-Entprellung via Timer


von MuesLee (Gast)


Lesenswert?

Hallo zusammen,

ich bin noch nicht lange bei der AVR-Gemeinde dabei und stehe momentan 
vor einem kleinen Problem. Ich habe einen Mega32 und hab an den externen 
Interrupt1 einen Taster angeschlossen. Dieser prellt nun doch sehr 
heftig und nun versuche ich mich gerade an einer Software-Entprellung. 
Eine einfache Warteschleife wollte ich nicht nehmen da ja sonst alles 
steht. Also dachte ich mir mach ichs über nen Timer über folgendes 
Schema:
- Wenn ext. Interrupt kommt dann:
  ext. Interrupt aus
  Starte den Timer
- Bei Timerinterrupt:
  Schau ob ext. Interrupt-Bit noch gesetzt, wenn ja reagiere
- Timer wieder stoppen
- ext. Interrupt wieder an

Ich hab das ganze auch in Code umgesetzt, nur leider will es einfach 
nicht klappen :-( Hier das Sniplet:

////

ISR(INT1_vect)
{
  GICR = (0<<INT1);  //Interrupt1 aus
  TIMSK |= (1<<TOIE0); //Timer0 los
}

ISR(SIG_OVERFLOW0)
{
  if (!bit_is_set(PIND,PD3)) //InterruptBit noch gesetzt?
  {
                  //Reagiere
    if (state == UPDOWN || state == TOPLEVEL) actionUDPerformed();
    else if (state == RISW) actionRSPerformed();
    else if (state == SWITCH) actionSWPerformed();
    else if (state == YESNO) actionYNPerformed();
  }

  TIMSK |= (0<<TOIE0); //Timer0 aus
  GIFR = (1<<INTF1); //Leere Flagregister Interrupt1
  GICR = (1<<INT1); //Interrupt1 an
}

/////

In meiner Main initialisiere ich die Interrupts/Timer folgendermaßen:

    MCUCR = ((1<<ISC11) | (0<<ISC10));
  GICR = (1<<INT1); //ext.Int1 an

  TCCR0 |= 0x05; //Prescaler max
  TIMSK |= (0 << TOIE0); //Timer voerst aus

Vielleicht weiß jemand Rat?

Vielen Dank schon mal.

von Falk (Gast)


Lesenswert?

@ MuesLee

Wenn es nicht unbedingt ein externer Interrupt sein muss.

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

Wenn es ein externer Interrupt sein muss (z.B. um aus dem Sleep-Modus 
aufzuwachen) dann hilft nur eine Entprellung per Hardware. Pull-up 10k 
sowie 10nF C gegen Masse. Die AVRs haben Schmitt-trigger Eingänge, das 
passt schon.

MFG
Falk


von johnny.m (Gast)


Lesenswert?

Taster nur dann an externen Interrupt anschließen, wenns unbedingt nötig 
ist (eigentlich nur sinnvoll zum Aufwecken aus dem Sleep). Ansonsten 
Zeittakt erzeugen und z.B. alle 10 oder 20 ms (nicht öfter) die Taster 
abfragen. Peter Dannegger hat da glaub ich mal schöne Routinen zu 
geschrieben, die irgendwo in der Codesammlung zu finden sein müssten. 
Ansonsten ist das Prinzip denkbar einfach:
Alle 20 ms werden alle Taster eingelesen. Der Zustand der Taster wird 
mit dem alten Zustand (der im vorherigen Durchgang gespeichert wurde) 
verglichen. Das geht am sinnvollsten mit einem EXOR, da dann das 
Ergebnis direkt aussagt, wo sich überhaupt was geändert hat. Das 
einzige, was interessant ist, sind die Taster, die von 1 auf 0 
gewechselt haben, weshalb man das ganze noch mit dem Bitkomplement des 
aktuellen Zustandes verUNDen sollte. Dann hat man für jeden Taster, der 
seit der letzten Abfrage gedrückt wurde, ein Bit gesetzt. Alle anderen 
sind 0. Um die Entprellung muss man sich auf die Weise überhaupt nicht 
mehr kümmern.

von Pöhli (Gast)


Lesenswert?

Ich hab meine Taster immer an externen Interrupt da bei langen 
Programmen mit Wartezeit sonst nix passiert bei Tastendruck.

Hab aber nie eine Entprellung vorgesehen und noch nie Probleme gehabt.

Wenn ExtInt

dann Abfrage der Tasten

binäres Ergebnis zB bei 4 Tasten : 1000
bei 0000 ( dürfte nicht sein), nochmal abfragen

speichern in Statusregister welche Taste es war, Interrupt verlassen.


ein erneutes Auslösen passiert nicht solange er in der Interruptroutine 
ist, ggf kann das Interrupt Flag zum Schluss nochmal gelöscht werden.

Wenn er aus der Interruptroutine kommt sollte das Prellen vorbei sein, 
ggf gibt man den Interrupt erst später wieder frei, zB beim nächsten 
Durchlauf.

ich weiß nicht ob meine "Problemlosigkeit" auch damit zu tun hat das ich 
immer Folientastatur bzw Tasten verwende, und nicht so mechanische 
Taster.

von johnny.m (Gast)


Lesenswert?

Hier mal ein kleines Beispiel (In diesem Fall für einen Tiny2313 
geschrieben). An PORTB sind 4 Taster angeschlossen (PB7..4). flags ist 
dabei die Variable, in der die Flags für die gedrückten Taster stehen. 
Die Flags werden im Hauptprogramm abgefragt und gelöscht, wenn sie 
bearbeitet wurden. Das Timing läuft in diesem Bsp. über den 
Compare-Interrupt A von Timer 0, der so konfiguriert ist, dass jede ms 
ein Interrupt auftritt. timer0_cyc sorgt dafür, dass nach jeweils 20 
Zyklen (also 20 ms) die Taster abgefragt werden. Bei der Abfrage wird 
der aktuelle Zustand mit dem alten EXOR-verknüpft. Anschließend wird das 
ganze mit dem Bitkomplement des aktuellen Zustandes verUNDet. Damit in 
"flags" nur die den Tastern entsprechenden Bits gesetzt werden, wird das 
ganze noch mal mit einer Maske aus allen Tastern verUNDet.
1
#define TASTER1 7  //Position der Taster an Port B
2
#define TASTER2 6
3
#define TASTER3 5
4
#define TASTER4 4
5
#define TASTER ((1 << TASTER1) | (1 << TASTER1) | (1 << TASTER1) | (1 << TASTER1))
6
7
volatile unsigned char flags;
8
unsigned char taster_alt = 0xf0, timer0_cyc;
9
10
//...Code...
11
12
ISR(TIMER0_COMPA_vect)
13
{
14
    unsigned char taster_neu;
15
    timer0_cyc++;
16
    if(timer0_cyc > 19)
17
    {
18
  taster_neu = PINB; //Einlesen Taster
19
        //Ermittlung der gedrueckten Taster:
20
  flags |= ((taster_neu ^ taster_alt) & ~taster_neu) & TASTER;
21
  taster_alt = taster_neu;
22
  timer0_cyc = 0;
23
    }
24
    //...Code...
25
}
26
27
int main(void)
28
{
29
    //Hauptprogramm-Code, u.a. Auswertung der Taster
30
}

@Pöhli:
> ...bei langen Programmen mit Wartezeit
Lange Wartezeiten mit absoluter Untätigkeit sollte man eh vermeiden. 
Externe Interrupts sind für den Anschluss mechanischer Schaltkontakte 
nunmal nicht geeignet. Wenn man es dennoch tut, muss man eben aufwändig 
entprellen...

von johnny.m (Gast)


Lesenswert?

Upps, da hat was mit den Einrückungen nicht hingehauen... Hoffe, man 
kann es trotzdem entziffern.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

>Ich hab meine Taster immer an externen Interrupt da bei langen
>Programmen mit Wartezeit sonst nix passiert bei Tastendruck.

>Hab aber nie eine Entprellung vorgesehen und noch nie Probleme gehabt.

Wahrscheinlich laufen Deine Routinen so lange, daß Dir das Prellen gar 
nicht auffällt. Arbeitest Du viel mit Warteschleifen?

Auch Folientastaturen haben im Übergangsbereich zwischen niederem und 
hohem Widerstand undefinierte Übergangswiderstände. Ohne wirksame 
Software-Entprellung ist auf die Dauer mit Fehleingaben zu rechnen.

von johnny.m (Gast)


Lesenswert?

@Pöhli:
> ich weiß nicht ob meine "Problemlosigkeit" auch damit zu tun hat das ich
> immer Folientastatur bzw Tasten verwende, und nicht so mechanische
> Taster.
Folientaster sind zwar auch mechanisch, aber hast schon recht, die 
prellen u.U. nicht so stark. Bei 08/15-Tastern geht das nicht so ohne 
weiteres...

von Falk (Gast)


Lesenswert?

> Ich hab meine Taster immer an externen Interrupt da bei langen
> Programmen mit Wartezeit sonst nix passiert bei Tastendruck.
> Hab aber nie eine Entprellung vorgesehen und noch nie Probleme gehabt.

Peter, das ist glaube ich dein Stichwort. Bühne frei ;-)

MfG
Falk


von johnny.m (Gast)


Lesenswert?

@Travel Rec.:
Hast natürlich recht, wenn er schon so lange Wartezeiten im Programm 
hat, dass er keine Taster mehr abfragen kann, dann will ich nicht 
wissen, was er in seinen ISRs so alles macht...

von johnny.m (Gast)


Lesenswert?

@Falk:
Ich glaub der Peter ist heute net da, sonst hätte der schon längst 
eingegriffen...

von AVRFan (Gast)


Lesenswert?

Kein vernünftiger Mensch betreibt Taster an externen Interrupts, denn 
dafür gibt es keinen Grund [*].  Taster werden über gewöhnliche, als 
Input konfigurierte Portpins in einem geeigneten Zeitraster abgefragt 
und fertig. "Geeignet" heißt, dass man das Raster nicht zu klein wählen 
sollte, damit der Prozessor nicht unnötig viel rechnen muss; und 
andererseits nicht zu groß, um noch (scheinbar) verzögerungsfreie 
Reaktionen auf Tastenbetätigungen zu gewährleisten. Beliebt sind 
Zeitraster von 10 oder 20 ms.

Richte also einen 20 ms-Hardwaretimer ein, starte ihn einmal und lass 
ihn ununterbrochen über die ganze Programmlaufzeit ticken.  Bei jedem 
Timertick kopierst Du den Pin-Zustand ins C-Flag, und schiftest es 
anschließend in ein Byte rein (das "Tastenzustands-Byte").  Die beiden 
niedrigwertigsten Bits in diesem Byte geben Dir dann Auskunft über das 
Geschehen: XXXXXX01 -> Taster wurde gerade gedrückt; XXXXXX10 -> Taster 
wurde gerade losgelassen.   Dies testest Du bei jedem Timertick und 
schaltest entsprechend Deine LED an oder aus.

[*] Ausnahme: Der Controller soll aus dem Power-Down-Sleepmode per 
Tastendruck aufgeweckt werden.  Das ist nur über einen externen, 
pegelgetriggerten Interrupt zu bewerkstelligen.  Dieser Interrupt sollte 
dann aber erst unmittelbar vor dem Schlafenlegen des Controllers 
aktiviert und unmittelbar nach dem Aufwecken des Controllers wieder 
deaktiviert werden.  Zur Tastenabfrage im Normalbetrieb sollte man ihn 
also auch dann nicht verwenden, sondern es wie oben dargestellt  - nach 
der sog. "Polling"-Methode - realisieren.

von klammer Johann (Gast)


Lesenswert?

Hast du im Main die Interrupts enabled (mit einem sei()) ??
Sonst reagiert der nämlich nie darauf.

von MuesLee (Gast)


Lesenswert?

Wow, bin echt begeistert wie fix das hier geht. Vielen Dank schon mal 
für die nützlichen Infos.

@Johann: sei() hab ich natürlich mit drin :-)

von Pöhli (Gast)


Lesenswert?

eventuell ist es nicht rübergekommen  - ich betreibe die Taster nicht 
direkt am Interrupt, sondern der Interrupt sagt mir das eine Taste 
gedrückt wurde und dann wird der entsprechende Port wo die Tastatur 
hängt herkömmlich abgefragt.

Wieso sollte ich ständig Tastaturabfragen laufen lassen wenn das gar 
nicht nötig ist. So wird die Tastatur nur dann abgefragt wenn jemand 
gedrückt hat.

Und ja, die Programme sind tatsächlich ziemlich umfangreich und 
langwierig (u.a. diverse Zeitmessungen), deswegen gibt es erst recht 
keinen Grund zusätzliche Zeit mit unnützen Tastaturabfragen zu 
verschwenden.

Aber ich komme vom Thema ab, wollte oben nur aufzeigen das man das ganze 
auch  "nebenbei" umgehen kann ohne Entprellungssoftware zu benutzen.

von MuesLee (Gast)


Lesenswert?

Hallo ich bin's nochmal :-)

Ne kleine Frage am Rande zum Verständnis. Das Codebeispiel, das ich oben 
angegeben hab startet sobald ein ext. Interrupt vorliegt den Timer und 
deaktiviert ext. Interrupts. Dieser Vorgang wird dann bei einem 
Timerinterrupt wieder rückgängig gemacht. Gemäß der Initialisierung von

MCUCR = ((1<<ISC11) | (0<<ISC10));

in der Main() müsste lediglich eine fallende Flanke einen Interrupt 
auslösen.

Ich habe jetzt aber bemerkt, das in dieser Konstellation der beiden 
Interruptroutinen, eben dies nicht der Fall ist - sprich eben nicht nur 
fallende Flanke sondern "Dauerfeuer". Wie kann das sein?

von johnny.m (Gast)


Lesenswert?

Möglicherweise kriegst Du allein dadurch Probleme, dass Du den Timer gar 
nicht ausschaltest, wie im Kommentar beschrieben. Dein
1
TIMSK |= (0<<TOIE0); //Timer0 aus
Schaltet nämlich gar nichts! Nullen kannst Du schieben, wohin Du willst, 
es bleiben Nullen. Und eine Null mit einer Eins verODERt gibt immer noch 
eine Eins!

von johnny.m (Gast)


Lesenswert?

Ich weiß nicht, wann es sich endlich mal rumspricht:
1. Bits setzen
1
REGISTER |= (1 << BITNAME1) | (1 << BITNAME2) /*usw.*/;

2. Bits löschen
1
REGISTER |= ~((1 << BITNAME1) | (1 << BITNAME2) /*usw.*/);

Nullen schieben bringt ÜBERHAUPT NICHTS, "0 | 1 = 1"!

von johnny.m (Gast)


Lesenswert?

Und da hab ich mich auch schon verschrieben (Sch... Copy&Paste)
Also:

2. Bits löschen mit
1
REGISTER &= ~((1 << BITNAME1) | (1 << BITNAME2) /*usw.*/);
Und nicht "|="

von AVRFan (Gast)


Lesenswert?

@Pöhli:

Ein paar Fragen an Dich:

1. Wenn ich Dir die Aufgabe stelle, acht Taster zu betreiben an einem 
Controller, der nur über zwei externe Interrupts verfügt (ATmega8), wie 
löst Du sie? Ich benötige dazu genau einen Port, und Du?

2. Wenn ich Dir die Aufgabe stelle, aus Kostengründen einen preiswerten 
Taster einzusetzen, der pro Betätigungsvorgang innerhalb 5 ms 10 mal 
(die Werte sind realistisch) prellt, bevor der Zustand mechanisch stabil 
ist, wird Deine Interruptroutine dann auch 10 mal durchlaufen?

3. Da der Taster nur einmal betätigt wurde, soll ein damit verbundener 
Zähler auch nur um 1 weiterschalten.  Bei der Polling-Methode ist dies 
gewährleistet unter der Voraussetzung, dass das Polling-Intervall größer 
als die Prellzeit ist. Wie erreichst Du diese Vorgabe?

4. Wie groß schätzt Du die Prozessorlast ein für die Abfrage einer 
Taste? (µC-Taktfrequenz = 4 MHz, Tasten-Polling-Intervall = 20 ms, 
Rechenaufwand pro Tastenzustandstest = 20 Instruktionen)

5. Gibt es in Deinen Programmen Warteschleifen?

Vielleicht magst Du was dazu sagen?

von AVRFan (Gast)


Lesenswert?

> ... startet sobald ein ext. Interrupt vorliegt den Timer und
>deaktiviert ext. Interrupts. Dieser Vorgang wird dann bei einem
>Timerinterrupt wieder rückgängig gemacht.

Zehnmal umständlicher als nötig...  Einfach Timer mit 20 ms ticken 
lassen
und bei jedem Tick tun was zu tun ist, nämlich Daten einlesen, 
verarbeiten und ausgeben - that's all! :-)

Wenn Du Angst hast, dass der Controller damit überfordert sein könnte, 
seinen Zustand 50 mal pro Sekunde zu aktualisieren, rechne mal aus, 
wieviele Instruktionen ein mit 4 MHz getakteter AVR während 20 ms 
abarbeiten kann.

von johnny.m (Gast)


Lesenswert?

@AVRFan:
Genau. V.a. ist die alle-paar-zig-Millisekunden-Tasterabfrage in den 
meisten Fällen nur ein Nebenprodukt. In vielen Anwendungen braucht man 
eh eine Zeitbasis, und es ist nur ein geringfügiger Zusatzaufwand, alle 
paar Zyklen eben nebenher noch ein paar Taster einzulesen. Wenn man sich 
natürlich alles mit delay() & Co. zumüllt, geht das so halt nicht. Aber 
das muss jeder selbst wissen. Hauptsache, man erzählt nicht einem 
Einsteiger, er solle Taster über externe Interrupts abfragen (evtl. noch 
zusätzlich über ein UND-Gatter usw.). Ich selber habe auch mal an der 
Uni gelernt (mit nem 80C517 damals), Taster über externe Interrupts 
abzufragen. Mittlerweile weiß ich es besser, und meine Studis lernen 
sowas nicht mehr...

von Atmega8 A. (atmega8) Benutzerseite


Lesenswert?

Also ich hatte mir damals auch so ein Programm geschrieben ... welches 
nicht nur "dumm" wartet, sondern bei dem man mit der Zeit was besseres 
anfangen kann.

hab das aber mit " SIGNAL (SIG_OVERFLOW0) " gelöst.

Wenn der Interrupt vom Timer kommt lese ich alle Eingangspins ein ... 
wenn er dann ein zweites mal kommt lese ich nochmal alle ein und 
vergleiche ob jemand den Taster wieder losgelassen hat.

Geht gut das ganze !


lg

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.