Forum: Compiler & IDEs Zustände schalten mit Interrupt


von Michi R. (Gast)


Lesenswert?

Hallo mikrocontroller.net Gemeinde!

Da ich in Sachen yC-Programmierung noch ziemlich grün hinter den Ohren 
bin und mich bestehende Forumeinträge nicht weitergebracht haben, hier 
meine erste Frage:

Mit meinem Atmega8 will ich 3 LEDs mit einem Taster durchschalten 
können. Sprich bei jedem Tastendruck in den nächsten Zustand schalten. 
Das soll per Interrupt realisiert werden.
Was hab ich bei meinem Code vergessen? Die Ausgänge flackern wild hin 
und her... :(
Vielen Dank für eure Mühe.
Gruß Michi

Hier mein Code:

1
#define F_CPU 3686000UL
2
#include <util/delay.h>
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
6
int i=0;
7
8
ISR(INT0_vect)
9
{
10
  _delay_ms(20);
11
  i++;
12
  
13
}
14
15
void eins() 
16
{
17
  PORTD=0b10000000;
18
}
19
20
void zwei()
21
{
22
  PORTD=0b01000000;
23
}
24
25
void drei()
26
{
27
  PORTD=0b00100000;
28
}
29
30
int main(void)
31
{
32
  
33
  DDRD=0b11100000;
34
        PORTD |= ( 1<<PD2 );  //pull up aktivieren
35
  
36
  GICR = ( 1<<INT0 );
37
  MCUCR = ( 0<<ISC01 | 0<<ISC00 );
38
  GIMSK = ( 1<< INT0 );
39
40
  sei();
41
  
42
  
43
  while(1)
44
    {
45
    
46
    if (i==0) 
47
    {  
48
      eins();  
49
    }
50
    else if (i==1)
51
    {
52
      zwei();
53
    }
54
    else if (i==2)
55
    {
56
      drei();
57
    }
58
    else
59
      i=0;
60
  }
61
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Michi R. schrieb:

> Mit meinem Atmega8 will ich 3 LEDs mit einem Taster durchschalten
> können. Sprich bei jedem Tastendruck in den nächsten Zustand schalten.
> Das soll per Interrupt realisiert werden.

Warum?
Taster und externer Interrupt sind eine blöde Idee, auch wenn das immer 
wieder propagiert wird.
Entprellung
die Konmfortroutinen vom PeDa sind alles was man braucht um Tasten 
zuverlässig auszuwerten.

von Karl H. (kbuchegg)


Lesenswert?

1
ISR(INT0_vect)
2
{
3
  _delay_ms(20);

genau deswegen ist das eine blöde Idee.
Ein delay_ms in einer ISR ist ein ziemlicher Supergau.

von Karl H. (kbuchegg)


Lesenswert?

>
1
>   MCUCR = ( 0<<ISC01 | 0<<ISC00 );
2
>

so, so.
Und du denkst also, dass du innerhalb von 20 Millisekunden eine Taste 
drücken und wieder loslassen kannst.

Der Interrupt kommt, solange der Pin auf Low-Pegel ist und auch auf 
Low-Pegel bleibt! Bei dir: solange die Taste gedrückt ist, kommt ein 
Interrupt nach dem anderen.

von Michi R. (Gast)


Lesenswert?

Oh! Ich dachte, der Interrupt kommt nur bei fallender Flanke...

von Karl H. (kbuchegg)


Lesenswert?

Nicht wenn mein AtMega8 Datenblatt korrekt ist.

von Michi R. (Gast)


Lesenswert?

;) So ists besser:

MCUCR = ( 1<<ISC01 | 0<<ISC00 );

Macht aber noch immer nicht, was es soll...
Ich dachte, für diese einfache Anwendung tuts auch ein externer 
Interrupt mit Taster! Bei der "Komfortroutine" blick ich ehrlich gesagt 
nicht wirklich durch...

von Karl H. (kbuchegg)


Lesenswert?

Michi R. schrieb:
> ;) So ists besser:
>
> MCUCR = ( 1<<ISC01 | 0<<ISC00 );
>
> Macht aber noch immer nicht, was es soll...
> Ich dachte, für diese einfache Anwendung tuts auch ein externer
> Interrupt mit Taster!

Wie du siehst, ist das eben nicht einfach.

Einfach wäre ein Pollen in der Hauptschleife mit Vergleich ob sich der 
zustand des Input-Pins verändert hat.

> Bei der "Komfortroutine" blick ich ehrlich gesagt
> nicht wirklich durch...

Das macht nichts. Dazu funktionieren sie zu gut.

PS: Welchen Prozessor hast du wirklich? Ein Mega 8 hat kein Register 
namens GIMSK

von Karl H. (kbuchegg)


Lesenswert?

Im übrigen

>
1
> void eins() 
2
> {
3
>   PORTD=0b10000000;
4
> }
5
>

Gratuliere, du hast dir soeben den notwendigen Pullup Widerstand am Pin 
2 abgeschaltet.
Jetzt weißt du auch, warum man derartige Rundum-Portzuweisungen nicht 
macht.

von Michi R. (Gast)


Lesenswert?

Grrr. Bekomm ich doch gleich die volle Breitseite.

Karl Heinz Buchegger schrieb:
> PS: Welchen Prozessor hast du wirklich? Ein Mega 8 hat kein Register
> namens GIMSK

Ich hab nen Atmega8! Komisch, dass er da nicht rummeckert. ;)

Karl Heinz Buchegger schrieb:
> Gratuliere, du hast dir soeben den notwendigen Pullup Widerstand am Pin
> 2 abgeschaltet.

Wie gesagt, bin ziemlicher Anfänger! Umso dankbarer bin ich für deine 
Korrektur.

von Karl H. (kbuchegg)


Lesenswert?

Michi R. schrieb:
> Grrr. Bekomm ich doch gleich die volle Breitseite.
>
> Karl Heinz Buchegger schrieb:
>> PS: Welchen Prozessor hast du wirklich? Ein Mega 8 hat kein Register
>> namens GIMSK
>
> Ich hab nen Atmega8! Komisch, dass er da nicht rummeckert. ;)

Ist in der Tat interessant.
Könnte ein Hinweis darauf sein, dass deine Projekteinstellungen nicht 
korrekt sind.
Nichts desto trotz: ein Mega 8 hat laut Datenblatt kein GIMSK Register. 
Atmel wird ja wohl wissen, wie die Dinger heißen

von Michi R. (Gast)


Lesenswert?

Wenn auch sehr unprofessionell geschrieben, so läuft das Programm 
tadellos:
Danke für die Vorschläge.
1
#define F_CPU 3686000UL
2
#include <util/delay.h>
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <avr/sleep.h>
6
int i=0;
7
8
ISR(INT0_vect)
9
{
10
   _delay_ms(750);
11
  i++;
12
}
13
14
void eins() 
15
{
16
   PORTD &= ~((1<<PD5) | (1<<PD6));
17
   PORTD |= (1<<PD7);
18
}
19
20
void zwei()
21
{
22
  PORTD &= ~((1<<PD7) | (1<<PD5));
23
  PORTD |= (1<<PD6);
24
}
25
26
void drei()
27
{
28
  PORTD &= ~((1<<PD6) | (1<<PD7));
29
  PORTD |= (1<<PD5);
30
}
31
32
void nichts()
33
{
34
  PORTD &= ~((1<<PD6) | (1<<PD7) | (1<<PD5));
35
}
36
37
int main(void)
38
{
39
  DDRD=0b11100000;
40
        PORTD |= ( 1<<PD2 );    //pull up aktivieren
41
  
42
  GICR = ( 1<<INT0 );
43
  MCUCR = ( (1<<SE) | (1<<SM1) | (0<<ISC01) | (0<<ISC00) );
44
45
  sei();
46
  
47
  
48
  while(1)
49
    {
50
    
51
    if (i==0) 
52
    {  
53
      eins();  
54
    }
55
    else if (i==1)
56
    {
57
      zwei();
58
    }
59
    else if (i==2)
60
    {
61
      drei();
62
    }
63
    else if (i==3)
64
    {
65
      nichts();
66
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
67
      sleep_mode();
68
    }  
69
    else if (i>=4)
70
      i=0;
71
  }
72
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Michi R. schrieb:
> Wenn auch sehr unprofessionell geschrieben

In der Tat

> ISR(INT0_vect)
> {
>    _delay_ms(750);
>   i++;
> }

sowas ist Murks.

Auf der einen Seite benutzt du einen Interrupt, damit nur ja alles 
möglichst schnell reagiert (was eigentlich völlig sinnlos ist, denn für 
deinen µC bewegst du dich sowieso in Zeitlupe), auf der anderen Seite 
vertrödelst du dann genau hier die Zeit, damit dann ja alles, was der µC 
nebenher zu machen hätte (in einem richtigen Programm) zum Stillstand 
kommt.

So ist das Murks. Offen und ehrlich gesagt.
Einen durchgerosteten Kotflügel am Auto kann man auch mit Klebeband 
reparieren. Hält sogar eine Zeit lang. Murks ist es trotzdem.

: Bearbeitet durch User
von Michi R. (Gast)


Lesenswert?

Das weiß ich doch. Ich kanns aber nicht besser. Außerdem ist das ja nur 
der Atmega8 auf dem Übungsboard, und ich will damit lernen.

von Karl H. (kbuchegg)


Lesenswert?

Den Link zum Artikel
Entprellung
hab ich dir schon gegeben.

Ich seh natürlich ein, dass die Komfortroutinen in ihrer vollen Pracht 
nicht leicht zu durchschauen sind. Auf der anderen Seite hab ich dort 
für 1 Taster das ganze aufgerollt und eine einfacher zu durchschauende 
Version dafür geschrieben.

von spontan (Gast)


Lesenswert?

Es sei Dir gestattet, daß Du es nicht besser kannst (Deine Aussage), daß 
Du aber lernen willst (auch Deine Aussgae) kann hier keiner erkennen. Du 
willst nur die LED leuchten sehen, auch wenn Du den größtmöglichen Murks 
ablieferst.

Lernen und Denken geht aber etwas anders.

von Karl H. (kbuchegg)


Lesenswert?

Was ich dir allerdings zu gute halte ist, dass du den Interrupt brauchst 
um den µC damit aus dem Sleep heraus zu holen. Was aber nichts daran 
ändert, dass man Tastenabfragen wegen der Entprellung mittels Polling 
macht.

von Michi R. (Gast)


Lesenswert?

> Du willst nur die LED leuchten sehen, auch wenn Du den größtmöglichen Murks
> ablieferst.

Es tut mir wirklich leid, dass mir das Freude bereitet. Wo ist denn das 
Problem, wenn ich mich langsam an die Sache rantasten möchte?

> Lernen und Denken geht aber etwas anders.

Gerne informiere ich dich über jeden einzelnen meiner Lernfortschritte.


kbuchegg, die Entprellung werde ich umgehend implementieren.

> Du aber lernen willst (auch Deine Aussgae) kann hier keiner erkennen.
---> jetzt aber! ;-)

von ich (Gast)


Lesenswert?

Michi R. schrieb:
> ---> jetzt aber! ;-)

Geht doch! :-))

von Karl H. (kbuchegg)


Lesenswert?

Wobei. Das muss ich heute am Abend direkt mal ausprobieren.
Meiner Meinung nach und ohne das jetzt laufen gelassen zu haben, dürfte 
diese Interrupt Lösung nicht funktionieren.
Das ist ein Level-Interrupt. Sobald die ISR betreten wird, wird das 
zugehörige Interrupt Flag erneut gesetzt, weil ja die Taste noch immer 
gedrückt ist, der Level also immer noch anliegt. Der delay_ms ändert 
daran nichts. Er sorgt mit seinen 750ms lediglich dafür, dass ich als 
Benutzer eine Chance habe, die Taste wieder loszulassen, ohne dass 
gleich alles Amok läuft. Aber die Schaltstufe um lediglich 1 zu erhöhen 
dürfte mir damit nicht gelingen. Mit einmal kurz drauf drücken erhöht 
sich die Stufe um mindestens 2. Das kann ich als Benutzer gar nicht 
verhindern.
Denke ich jetzt mal. Aber wie gesagt: das will ich tatsächlich mal 
ausprobieren, wie das jetzt wirklich ist. Denn da bin ich mir jetzt auch 
unsicher. Ich hab da noch im Hinterkopf, dass die Level Interrupts ein 
bisschen anders funktionieren als die anderen.

: Bearbeitet durch User
von Michi R. (Gast)


Lesenswert?

Wie gesagt, bei mir läufts.

Ich dachte, das Flag ist "geschützt", solange die ISR läuft?

von Karl H. (kbuchegg)


Lesenswert?

Michi R. schrieb:
> Wie gesagt, bei mir läufts.
>
> Ich dachte, das Flag ist "geschützt", solange die ISR läuft?

Nö.
Keines der Interrupt Request Flags ist in irgendeiner Art und Weise 
geschützt. Sobald die ISR ausgelöst wird, werden sie wieder gelöscht.
Wobei beim INTF0 Flag in der Doku steht
1
This flag is always cleared when INT0 is configured
2
as a level interrupt.
Von daher verhält sich das in dem Fall ein wenig anders.


Edit: Du redest vom globalen Interrupt Flag.
Aber der komplette Interrupt Mechanismus ist ein wenig komplexer als nur 
dieses 1 Flag. Es muss ja auch einen Mechanismus geben, der das 
Auftreten eines Ereignisses registriert und festhält, während die CPU 
gerade in einer ISR einen ganz anderen Interrupt bearbeitet. Das 
Auftreten dieses 2-ten Interrupt Ereignisses soll ja deswegen nicht 
verloren gehen.

: Bearbeitet durch User
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.