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.
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...
Upps, da hat was mit den Einrückungen nicht hingehauen... Hoffe, man kann es trotzdem entziffern.
>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...
@Falk: Ich glaub der Peter ist heute net da, sonst hätte der schon längst eingegriffen...
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.
Hast du im Main die Interrupts enabled (mit einem sei()) ?? Sonst reagiert der nämlich nie darauf.
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 :-)
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!
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"!
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 "|="
@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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.