HI Mein Interrupt 0 ist durch ein Taster gesteuert. Ich möchte die variable "press" inkrementieren, wenn der Taster nur kurz gedrückt ist. Wenn der Taster jedoch 2sec lang gedrückt wurde, soll auf dem Port C (LED's) 0xAA ausgegeben werden! Nun mein Problem: Ich simuliere das Programm im AVR Studio und die while Schlaufe funktioniert nur, wenn ich darin kein Delay aufrufe! Wieso ist das so? THANKS SIGNAL(SIG_INTERRUPT0) { unsigned char s; _delay_loop_2(60000); //entprellen _delay_loop_2(60000); _delay_loop_2(20000); TIMSK = (0<<TOIE0); //Timer0 abschalten do { _delay_loop_2(10000); // 10ms Delay int_flag = bit_is_set(PIND,3); PORTC = int_flag; s++; } while ( (int_flag >= 1) && (s <= 200) ); if (s >= 200) { PORTC = 0xAA; } else { press++; address = press * 1000; if (press >= 15) press = 0; PORTC = ~press; TIMSK = (0<<TOIE0); } GIFR = (1<<INTF0); }
Interruptroutinen macht man so kurz wie möglich. Delays verbieten sich damit automatisch. Benutze einen Timer dafür, den Du aus dem Interrupt nur anschiebst bzw. Software-Timer.
Also die ersten Delay's (140ms) sind Warteroutinen, damit der Taster entprellt ist... Danach wird im "normal" - Betrieb des Tasters nur noch ein 10ms Delay dazu addiert! also 150ms! (ansonsten prellt mein Taster) Wenn dann aber der Taster 2sec lange gedrückt wird, soll mein uC in den Sleep Modus (OFF) versetzt werden! Sollte man das wirklich nicht mit Delays machen? (Ich glaube es liegt daran, dass ich eine Variable (s) in der ISR definiert habe!
Ja, man sollte das wirklich nicht mit delays machen. Laß einen Timer z. B. alle 10 ms ticken, dort kannst Du das dranhängen. Damit lassen sich dann beliebige Vielfache von 10 ms als Verzögerungen realisieren, auch die Entprellung selbst. Ich habe sowas auch schon gemacht, allerdings ist das Projekt nie wirklich fertig geworden (daher mag ich es nicht veröffentlichen). Aber genau diese Aufgabe, unterschiedliche Funktionalität je nachdem, ob Taster kurz oder lange gedrückt, habe ich damit realisiert.
Also sollte ich in der INT0 Routine nur ein Flag setzen und alles andere im Hauptprogramm machen??? Hmm... werde es mal versuchen! Vielen Dank noch...
Genau so. Also Timer-IRQ kurz genug. Dann Taste bei jedem IRQ prüfen, bei gedrückter Taste Flag setzen. Bei jedem folgenden IRQ Zähler hochzählen. Falls Taste nicht mehr gedrückt, wenn der Zähler kleiner als die Prellzeit ist, Flag löschen. Ansonsten weiterzählen und weiterprüfen. Wenn dann die Taste wieder losgelassen wird, Zähler überprüfen und wenn kleiner 2s dann Flag für einfachen Tastendruck setzen oder aber für 2s-Tastendruck.
Eins nur noch: es empfiehlt sich, einen externen Interrupt innerhalb der Interruptroutine als allererstes mal abzuschalten, ansonsten bekommst Du aufgrund des Tastenprellens X Stück davon ausgeliefert. Erst nach Ablauf der Entprellzeit schaltest Du ihn dann wieder zu. Ggf. kann man ihn dann auch ,negiert' zuschalten und das Ganze herumdrehen, dann bekommst Du ein Ereignis geliefert, wenn der Nutzer die Taste wieder losläßt (und mußt danach natürlich die Negation wieder negieren ;-). Aber das Prinzip ist richtig, in der Interruptroutine macht man nur das, was wirklich zeitkritisch ist, für den Rest setzt man ein Flag und macht es später in der Hauptschleife (`volatile' nicht vergessen!).
Erstmal vielen vielen Dank für die vielen Ideen. So wie ich das jetzt verstehe, muss ich in der Interruptroutine folgendes machen: 1. Interrupt ausschalten 2. Taster_flag = 1 3. entprellzeit abwarten (delay) -> wie lange ist das üblicherweise? 4. Interrupt wieder einschalten In der Timer routine mache ich folgendes: 1. zähler inkrementieren 2. Taster - Pin einlesen 3. Timer zurückstellen Im Hauptprogramm Endlosschlaufe, die verlassen wird, wenn der Taster losgelassen wird, oder der Zähler auf dem Endwert ist... Funktioniert das so? -> Vorallem das Entprellen würde mich wunder nehmen, denn jetzt habe ich ja wieder ein Delay in der ISR.
Ich würde den externen IRQ gar nicht nutzen. Port einfach per IO abfragen. In der Timer-IRQ, und dann jeden Durchlauf abfragen. Und in der Hauptschleife den MC in den idle-Mode schicken, wird durch die timer-IRQs jeweils aufgeweckt und falls dann ein Flag gesetzt wurde, dann wird die entsprechende Routine aufgerufen.
Externen Interrupt kann man schon nehmen, aber eben nicht dort warten. Ungefähr so: Timer-Interrupt tickt alle 10 ms und sieht nach, was alles an Aufträgen da ist. Diese werden abgearbeitet. (Bei mir in einer verketteten Liste, aber das ist ziemlich aufwendig und braucht außerdem malloc().) Externer Interrupt läuft ein, klemmt sich erstmal ab, und gibt einen Auftrag für den Timerinterrupt nach 20 ms (was effektiv heißt, irgendwas zwischen 10 und 20 ms) auf. Der Timer-Auftrag prüft, ob die Taste wirklich noch gedrückt ist. Wenn nicht -> alles Spaß, zurück über "Los!". Wenn ja: registrieren des negierten Interrupts (Loslassen der Taste), neuer Zeitgeberauftrag um festzustellen, ob die Taste lange gedrückt worden ist. Je nachdem, ob nun zuerst das Ereignis "Taste losgelassen" oder "langer Zeitgeber" eintrifft, werden die weiteren Aktionen ausgelöst.
> also das mit dem negierten Interrupt pack ich nicht ganz?
Wie ich oben schon mal schrob: Du registrierst den Interrupt auf der
jeweils entgegengesetzten Flanke. Wenn also anfangs die H->L Flanke
den Interrupt ausgelöst hat (Taster gegen Masse), dann regisrierst Du
danach (nach Ablauf der Entprellzeit) einen, der auf die L->H Flanke
reagiert, damit bekommst Du ein Ereignis gemeldet, wenn der Taster
wieder losgelassen wird.
@Jörg Wunsch: Ich verfolge diesen Thread sehr aufmerksam, da es für mich einiges zu lernen gibt. In einen obigen Beitrag hast Du geschrieben: 'volatile nicht vergessen'. Kannst Du das bitte genauer erklären? Besten Dank Tipper
FAQ #1 lesen. Kurz: volatile struct { intbit_0: 1; intbit_1: 1; /* ... */ } intbits; SIGNAL(SIG_FOO) { /* ... */ intbits.intbit_0 = 1; } ... int main(void) { ... for (;;) { if (intbits.intbit_0) { intbits.intbit_0 = 0; /* handle interrupt here */ } } }
Ich staune immer wieder, wie aufwendig und fehleranfällig Leute eine Tastenentprellung und Abfrage hinkriegen, nur weil sie unbedingt den völlig ungeeigneten externen Interrupt verwenden wollen. Am sichersten und einfachsten ist und bleibt doch der Timerinterrupt: http://www.mikrocontroller.net/forum/read-4-20549.html Peter
Sehe ich genauso wie Peter. Wer seine Tasten im Timer-IR abfragt (alle 10 - 20ms), wird die Probleme mit Tastenprellen bald nicht mehr verstehen können. Tasten an IR-Eingängen machen für mich dann Sinn, wenn mit diesen Tasten die CPU aus dem Sleep geweckt werden soll. Stefan
Sorry, wollte natürlcih schreiben: "Sehe ich genauso wie Peter und Michael" :-))) Stefan
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.