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.
@ 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
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.
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.
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.
@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...
>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.
@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...
> 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
@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...
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.
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.
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?
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!
@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?
> ... 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.
@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...
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