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