Forum: Mikrocontroller und Digitale Elektronik ATMega8 Externer Interrupt INT0


von Sid (Gast)


Lesenswert?

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
volatile unsigned char zaehlvariable;
6
7
int main (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
  return 1;
17
  }
18
  
19
void tuwas1 (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
void tuwas2 (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
void tuwas3 (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.

von Karl H. (kbuchegg)


Lesenswert?

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.
1
....
2
  sei();  // global interrupt enable  
3
  zaehlvariable=1
4
5
  while( 1 ) {
6
7
    ....
8
9
    if( zaehlvariable == 1 ) {
10
      PORTC = 0x01;
11
      _delay_ms(20);
12
    }
13
14
    else if( zaehlvariable == 2 ) {
15
      PORTC = 0x02;
16
      _delay_ms(40);
17
    }
18
19
    else {
20
      PORTC = 0x04;
21
      _delay_ms(80);
22
    }
23
24
    PORTC = 0x00;
25
  }
26
27
  return 1;
28
}

von Sid (Gast)


Lesenswert?

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...

von Sid (Gast)


Lesenswert?

...überschneidung der postings. Du hast da nochmal Code nachgelegt : )
Schau's mir erstmal an, danke.

von Düsendieb (Gast)


Lesenswert?

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

von holger (Gast)


Lesenswert?

>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,...

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

> 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.

von sid (Gast)


Lesenswert?

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"?

von Huch (Gast)


Lesenswert?

>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.

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
Noch kein Account? Hier anmelden.