Liebe Gemeinde,
folgender Code soll dazu dienen, auf Tastendruck (INT0) jeweils eine von
drei LEDs mit unterschiedlichen Zyklen zu togglen. Dabei soll der Taster
die LEDs der reihe nach durchschalten, also 1-2-3-1-2-3-1 usw usf.
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include<avr/interrupt.h>
4
5
volatileunsignedcharzaehlvariable;
6
7
intmain(void){
8
DDRB=0xFF;
9
DDRC=0xFF;
10
DDRD=0x00;
11
MCUCR|=(1<<ISC01)|(1<<ISC00);//INT 0 rising edge
12
GICR|=(1<<INT0);// INT 0 ENABLE
13
sei();// global interrupt enable
14
zaehlvariable=1
15
tuwas1();
16
return1;
17
}
18
19
voidtuwas1(void){// toggle c0 20msec
20
sei();
21
PORTC=0;
22
while(1){
23
PORTC=0x01;
24
_delay_ms(20);
25
PORTC=0x00;
26
_delay_ms(20);
27
}
28
}
29
30
voidtuwas2(void){//toggle c1 40 msec
31
sei();
32
PORTC=0;
33
while(1){
34
PORTC=0x02;
35
_delay_ms(40);
36
PORTC=0x00;
37
_delay_ms(40);
38
}
39
}
40
41
voidtuwas3(void){// toggle c2 80 msec
42
sei();
43
PORTC=0;
44
while(1){
45
PORTC=0x04;
46
_delay_ms(80);
47
PORTC=0x00;
48
_delay_ms(80);
49
}
50
}
51
// interrupt-handler für ext 0
52
ISR(INT0_vect)
53
{
54
_delay_ms(100);
55
zaehlvariable++;
56
if(zaehlvariable>3)zaehlvariable=1;
57
if(zaehlvariable==1)tuwas1();
58
if(zaehlvariable==2)tuwas2();
59
if(zaehlvariable==3)tuwas3();
60
61
}
Zur Schaltung:
Atmega 8, Interner RC-OSC 1MHz, Vcc 5V, 100 nF Abblock, Taster nach Vcc
an INT0 mit 10k pull-down, LED nach GND mit Rv an C0..C2, keine weitere
Beschaltung.
Zu dem Code oben folgende fragen:
Der Interrupt funktioniert überhaupt nur öfter als einmal, wenn ich zu
beginn jeder der tuwas-Funktionen die Interrupts global aktiviere
(sei()). Warum ist das so, ich habe sie global ja nie deaktiviert. Oder
passiert das in der ISR automatisch?
Obwohl ich ein dickes busy-waiting-delay in der ISR habe, scheint die
IST zu prellen und die Sequenz auf Tastendruck (0->5V an INT0) ist nicht
1-2-3-1-2-3... sondern was ganz wildes.
So wie du das hast, geht das nicht.
* Tasten fragt man nicht per Interrupt ab, das geht fast immer in
die Hose.
Entprellung
* In einer Interrupt Funktion wird grundsätzlich nicht gewartet
(ausser vielleicht mal ganz kurz ein paar µs, aber alles im
Bereich Millisekunden und darüber ist vollkommen tabu)
* Deine Interrupt Routine muss irgendwann auch mal beendet werden.
Und zwar nicht durch den nächsten Interrupt. Zu diesem Zeitpunkt
muss die Kontrolle schon längst wieder zum unterbrochenene Programm-
teil zurückgegangen sein.
Eine Endlosschleife durch einen Interrupt zu 'abzuwürgen' um daraufhin
dann innerhalb der Interrupt Funktion die nächste Endlosschleife
anzuleiern, ist daher ein Schritt in die falsche Richtung (und das ist
jetzt freundlich ausgedrückt)
Der ganze Ansatz ist untauglich
Du brauchst eine Hauptschleife in main, die die ganze Arbeit macht. Nach
dem Erkennen eines Tastendrucks schaltest du eine Variable im Zyklus
immer um 1 weiter. Und abhängig von dieser Variablen, wird dann in main
entschieden, welche LED zu leuchten hat und welche nicht.
Vielen Dank für Deine Hinweise, aber da die Tu-Was-Funktionen bis ans
ende aller Tage laufen sollen (es sei denn, jmd drückt den Taster), sehe
ich leider keine andere Möglichkeit, als das durch einen Interrupt zu
erledigen. Oder habe ich Deinen Hinweis nicht verstanden?
Zyklisch in eine Port-Auswertungs-Funktion zu gehen bringt leider auch
nichts, da ich die 20 40 80 msec-Toggles möglichst exakt benötige
und ich durch zyklischen Abfragen des Pinstatus das Timing knicken kann
(Ich weiss, das _delay_ms dafür nur bedingt geeignet ist, es geht
erstmal ums Prinzip).
Entprellung ist natürlich irgendwann auch noch nötig, aber ich könnte
beim "Umaschalten" durchaus auch einen Moment warten und Zeit
vertrödeln...
Sid schrieb:> aber da die Tu-Was-Funktionen bis ans ende aller Tage laufen sollen
Bei Karl Heinz ist es die "Warte auf eine Tastenänderung" Funktion die
bis ans Ende aller Tage läuft
>Zyklisch in eine Port-Auswertungs-Funktion zu gehen bringt leider auch>nichts, da ich die 20 40 80 msec-Toggles möglichst exakt benötige
Und dann den internen RC Osci benutzen? Ts,ts,...
Sid schrieb:> ...überschneidung der postings. Du hast da nochmal Code nachgelegt : )> Schau's mir erstmal an, danke.
Seh gerade, dass nach dem Abschalten der LED natürlich noch ein _delay
nachgelegt werden muss. Natürlich wieder abhängig vom zaehlerstand.
> Entprellung ist natürlich irgendwann auch noch nötig,
nicht irgendwann. Jetzt!
Hol dir die Komfortroutinen aus Entprellung und du bist das Problem
der Tasten auf einen Schlag los. Noch komfortabler gehts nicht.
Hallo,
nochmal danke für Deine Hinweise, Karlheinz.
Habe es dank Deiner Anstöße auch ohne Interrupt hinbekommen und muss
mich jetzt (JETZT!) nur noch mit den Entprell-Routinen befassen.
Allerdings ist das vermutlich nicht ganz so trivial wie "hol dir dir
Komfortroutinen" sich anhört...
@Holger: Ja, und dann den internen RC-Oszi benutzen.
Noch was für die Philosophen: Was ist denn so falsch an dem Gedanken,
eine Funktion mit einer Endlosschleife durch einen Interrupt zu
unterbrechen, dessen ISR eine Funktion aufruft, die widerum eine
Endlosschleife enthält? Ist es ein Problem, wenn die ISR formal nie bis
zum Ende abgearbeitet wird? Oder stößt man sich da nur am Stil, weil
"sowas macht man nicht"?
>Ist es ein Problem, wenn die ISR formal nie bis zum Ende abgearbeitet wird?
Nun, lies nochmal im Datenblatt, was geschieht, wenn ein Interrupt
ausgelöst wird.
Die Adresse des Befehls, der auf den während des Interrupts
abgearbeiteten folgt wird auf dem Stack abgelegt, damit der uC später an
dieser Stelle weitermachen kann.
Das heisst, das bei jedem Interrupt Dein Stack weiter wächst, da Du die
vorhergehenden Rückkehradressen nie entfernst.
Man könnte sich nun ganz dumm stellen (da is n Kessel und der hat n
Loch) und die Rückkehradresse selbst vom Stack entfernen, aber: Legt man
die Adresse nun wider der Logik eines Programmierschemas (das von Karl
Heinz erklärte) ab oder erlaubt bzw. erzwingt man damit nun dieses
Programmierschema? Das solltest Du mal durchdenken.
Z.B.: Im einen Fall hast Du durch die zentrale Abarbeitung der
Ereignisse in der main-Schleife auch zentrale Kontrolle über Abfolge und
Priorität der Abarbeitung. Im anderen Fall müsste jede weitere
Interruptroutine diese Logik implementieren. Ein UART-Interrupt,
Timer-Interrupt etc. müsste sich auch mit dem evtl. Eintreten eines
SPI-Interrupts abgeben und so weiter. Je mehr Interrupts möglich sind,
desto komplizierter wird die Behandlung.
Manche Interrupts müssten ja z.B. evtentuell weiter abgearbeitet werden,
wenn der neueste erledigt ist. Du kannst also nicht pauschal die
Rückkehradressen entfernen sondern erst nach Entscheidungen die auf
Daten beruhen, die der vorherige Interrupt besitzt. Das wird kompliziert
und würde die Code-Grösse dramatisch erhöhen.