Hallo zusammen. Ich hab hier ein Problem, dass ich einfach nicht gelöst bekomme. Ich habe an einem ATmega16 an den beiden Interrupt-Eingängen 2 Taster hängen. Mit dem einen soll eine gewissen anzahl an Wiederholungen festgelegt werden, mit dem anderen ein Ablauf gestartet werde (versteht man später besser). Anders umschrieben, wenn ich 5 mal auf den einen Taster drücke, der an INT1 hängt, dann soll in meinem Programm ein counter um 5 hochgezählt werden. Drücke ich dann auf den Taster an INT2, soll z.B. eine LED 5 mal blinken. Das ist ja auch alles nicht so schwer, ich bekomm es aber nicht hin, den Taster zu entprellen. Hab den Timer und die INT initiallisiert, es funktioniert auch alles. Also sowohl Timer geht, als auch die Interrupts. #include <avr/io.h> #include <avr/delay.h> #include <stdint.h> #include <avr/interrupt.h> #include <avr/signal.h> volatile counter = 0; volatile time = 0; volatile start = 0; Hiermit initiallisiere ich mein Timer: void timer_init(void){ TIMSK=0x01; //Timer/Counter Interrupt Mask TCNT0=0x64; //255-Registerinhalt:=100 TCCR0=0x03; //Timer/Counter Control Register auf CK/64 sei(); //All Interrupt enable } Hier wird beim Überlauf die Variable counter um 1 hochgezählt. Der ATMega läuft mit 1Mhz, => 100 überläufe ~ 1sec SIGNAL(SIG_OVERFLOW0) //IR-Handler für Counter { TCNT0=0x64; //255-Registerinhalt:=100 counter++; //eine Variable um 1 hochzählen } Hier initialisiere ich die Interrupts, wie es im acr_gcc tutorial steht. void interrupt_init(void){ MCUCR = 0xF;//(1<<ISC11) | (1<<ISC10); GICR = (1<<INT0) | (1<<INT1); } Wird Button A gedrückt, wird eine globale Var start auf = 1 gesetzt. SIGNAL(SIG_INTERRUPT0)// signal handler for tcnt0 overflow interrupt { start = 1; } Hier werden die Tastendrücke von Taster B gezählt. SIGNAL(SIG_INTERRUPT1)// signal handler for tcnt0 overflow interrupt { time++; wait_a_little(); } Meine warte-funktion: void wait_a_little(void){ counter = 0; TCNT0=0x64; //255-Registerinhalt:=100 while(counter <= 50){ //wait } } //hauptprogramm... So weit so gut. Warum funktioniert das nicht? Wenn ich Taster B drücke, dann wird die var timer um 1 hoch gezählt und dann sollte das Programm in die Funktion wait_a_litte() springen. Macht sie auch, aber da funktioniert mein Timer dann nicht. Warum nicht??? Wenn ich im Hauptprogramm die Funktion aufrufe, dann funktioniert der Timer. Warum kann ich in einer 'Interrupt-Funktion' nicht einfach auf den Timer-Interrupt zugreifen? Wenn ich folgendes mach, funktioniert das auch nicht: SIGNAL(SIG_INTERRUPT1)// signal handler for tcnt0 overflow interrupt { time++; counter = 0; while ( counter <= 50 ){ //halbe sec warten //wait } } Kann mir jm. sagen, wie ich die Taster mit dem Timer entprellen kann? Hoffe, ihr versteht mein Problem! Danke für eure Hilfe! mfg Andreas
Hallo, Dein ganzer Ansatz ist sehr ungünstig. Grundregel 1: Interruptroutinen so kurz wie möglich. Warum? Weil ein Interrupt den normalen Programmlauf unterbricht und das sollte nur für tatsächlich zeitkritische Sachen benutzt werden. Das heißt letztlich, im IRQ nur festhalten, was nötig ist, und dann im Hauptprogramm diese Ereignisse bearbeiten. Grundregel 2: folgt aus Grundregel 1 -> keine Warteschleifen im IRQ. Zu Warteschleifen allgemein: wenn sie so kurz sind, daß die Benutzung des Timers sich dafür nicht lohnt -> ok. Kurz sind wenige Taktzyklen. Grundsätzlich: laut Datenblatt des AVR werden bei Aufrauf einer ISR die Interrupts generell gesperrt und mit RETI beim Verlassen wieder freigegeben. Damit werden antürlich auch Deine Timer-IRQs nicht mehr bearbeitet... Prinzipiell kann man in einer ISR die Interrupts natürlich geziehlt wieder freigeben, ob das in C ein so guter Weg ist, bezweifle ich allerdings. Also Tasten in der ISR nur merken und dann im Hauptprogramm über Entprellen und Auswertung nachdenken. Oder Tasten im Timerinterrupt abfragen und entprellen. Da gibt es etliche Beispiele und fertige Routinen auch in der Codesammlung. Gruß aus Berlin Michael
Hi, @Michael: die Grundregeln stimmen in den meisten Fällen, doch sind sie kein "Muss". Für ein einfaches System, das aus nicht mehr als den Interrupts besteht, gelten diese sicher nicht. @Andreas: mein sehr einfacher Ansatz zum Entprellen funktioniert so: Taster werden zyklisch gepollt (z.B. alle 1 oder 5ms). Die Poll-Periode liegt deutlich über der Prellzeit eines Tasters, sodass das Prellen kein Problem mehr darstellt. Je nachdem, was du machen möchtest, musst du in der Routine warten, bis der Taster wieder losgelassen wird, also die fallende Flanke pollen. Funktioniert bei mir in etlichen Anwendungen mit minimalem Code- und Laufzeitbedarf problemlos. Ciao, Peter
Hallo, @Peter: solche "Regeln" sind nie ein "Muss", sollte auch nicht so verstanden werden. Sie verhelfen meiner Meinung nach aber immer dazu, darüber nachzudenken, was passiert, wenn man diese "Regeln" "übertritt". :) Wenn man Interrupts intensiv benutzt, sollte man genau wissen, was die konkrete CPU da macht. Man sollte auch genau wissen, was das eigene Programm machen soll. Die Frage, weshalb sein Timer-IRQ nicht ordentlich bearbeitet wird, wenn er innerhalb der Timer-ISR eine halbe Sekunden Busy-Loop drin hat, deutet zumindest darauf hin, daß er genau das eben noch nicht weiß. Deshalb sollte er sich damit befassen. ;) Ich schreibe durchaus auch ASM-Programme mit mehreren IRQ, auch auf AVR, wo das Hauptprogramm nur aus loop: rjmp loop besteht. Dann muß ich aber schon wissen, was die Routinen machen und wie lange sie maximal brauchen. Würde ich trotzdem nicht unbedingt weiterempfehlen... Gruß aus Berlin Michael
Vielen Dank für eure schnelle Hilfe! :) Ich hab das jetzt, wie vorgeschlagen, im Hauptprogramm gelöst, funktionier sogar wunderbar. Hab nicht gewusst, dass ich quasi keine 2 IRQ benutzen kann. Kann mir jetzt vielleicht noch jm. sagen, wie ich die IRQ's wo die beiden Taster dran hängen, deaktivieren kann, wenn ich sie nicht mehr brauch? Damit nix passiert, wenn ich auf einen der beiden Taster drücke. Der Timer muss aber weiter laufen. Geht das? Und wie kann ich die wieder aktivieren? Mit meiner interrupt_init() einfach wieder? Wäre nett, wenn mir das noch jm. sagen könnte! Vielen dank noch mal an euch! Gruß Andreas
@Michael: alles klar, da haben wir das gleiche Verständnis. @Andreas: Aktivieren: GICR |= (1<<INT0) | (1<<INT1); /* enable INT0 and INT1 */ Deaktivieren: GICR &= ~((1<<INT0) | (1<<INT1)); /* disable INT0 and INT1 */ Ciao, Peter
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.