Forum: Mikrocontroller und Digitale Elektronik ATTINY13 Interrupt INT0


von Stefan O. (Gast)


Lesenswert?

Hallo zusammen,

ich befasse mich gerade mit den Interrupts des ATTINY13 und stehe gerade 
etwas auf dem Schlauch.

Simpler Versuchsaufbau:

-PB0: kann mit Taster gegen GND verbunden werden (INT0).
-PB1: LED gegen GND.

Simpler Code:
1
//********************************************************************
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#define F_CPU 1200000UL  // 1,2 MHz (für delay.h)
5
                         // Standard-Takt 9,6 MHz / 8
6
#include <util/delay.h>
7
8
inline void led_on() {PORTB |= (1 << PB1);}   //LED an PB1 an
9
inline void led_off() {PORTB &= ~(1 << PB1);}   //LED an PB1 aus
10
11
 
12
//********************************************************************
13
14
// Interruptserviceroutine
15
ISR (INT0_vect) {
16
         led_on();   
17
    }
18
 //********************************************************************
19
 
20
int main(void){
21
 
22
    DDRB |= (1<<PB1);          //Datenausgangsregister setzen
23
    PORTB |= _BV(PB0);            //internen Pull-Up-Widerstand an PB0 einschalten
24
    PCMSK |= (1<<PINB0);       //PCMSK – Pin Change Mask Register
25
    GIMSK  |= (1<<INT0);        //External Interrupt Request 0 Enable
26
    MCUCR |=(0<<ISC01) | (0<<ISC00);             // Interrupt auslösen wenn INT0 = GND
27
    sei();                                       // Interrupts erlauben
28
29
      
30
    while (1)  {        
31
      led_on();  _delay_ms(100); led_off();_delay_ms(100);
32
     }
33
        
34
    return 0;
35
}
36
 
37
//********************************************************************

Eigentlich hätte ich folgendes erwartet:

-Taster nicht geschlossen: LED blinkt.
-Taster geschlossen: LED leuchtet dauerhaft.

Tatsächliches Verhalten:

-LED leuchtet immer dauerhaft,d.h. die Interruptroutine wird immer 
ausgeführt, nur warum? Was habe ich da falsch definiert?

LG Stefan

von Stefan O. (Gast)


Lesenswert?

Problem gelöst, INT0 ne PCINTO ;-)

So funktioniert die Sache:
1
//********************************************************************
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#define F_CPU 1200000UL  // 1,2 MHz (für delay.h)
5
                         // Standard-Takt 9,6 MHz / 8
6
#include <util/delay.h>
7
8
inline void led_on() {PORTB |= (1 << PB0);}   //LED an PB0 an
9
inline void led_off() {PORTB &= ~(1 << PB0);}   //LED an PB0 aus
10
11
 
12
//********************************************************************
13
14
// Interruptserviceroutine
15
ISR (INT0_vect) {
16
         led_on();   
17
    }
18
 //********************************************************************
19
 
20
int main(void){
21
 
22
    DDRB |= (1<<PB0);          //Datenausgangsregister setzen
23
    PORTB |= _BV(PB1);            //internen Pull-Up-Widerstand an PB1 einschalten
24
    PCMSK |= (1<<PINB1);       //PCMSK – Pin Change Mask Register
25
    GIMSK  |= (1<<INT0);        //External Interrupt Request 0 Enable
26
    MCUCR |=(0<<ISC01) | (0<<ISC00);             // Interrupt auslösen wenn INT0 = GND
27
    sei();                                       // Interrupts erlauben
28
29
      
30
    while (1)  {        
31
      led_on();  _delay_ms(100); led_off();_delay_ms(100);
32
     }
33
        
34
    return 0;
35
}
36
 
37
//********************************************************************

von Thomas E. (thomase)


Lesenswert?

Stefan Ott schrieb:
> Problem gelöst, INT0 ne PCINTO ;-)
> So funktioniert die Sache:

Aber nur auf den ersten Blick. Eigentlich funktioniert das überhaupt 
nicht.

Stefan Ott schrieb:
> MCUCR |=(0<<ISC01) | (0<<ISC00);

Das ist schonmal Quatsch. Eine Oder-Verküpfung mit 0 bewirkt gar nichts. 
Funktioniert nur zufällig, da das Register nach dem Reset ohnehin 0 ist.

Siehe Tutorial: Bitmanipulation.

Stefan Ott schrieb:
> Interrupt auslösen wenn INT0 = GND

Das macht er dann solange, wie der Taster gedrückt ist. D.h. wenn die 
ISR fertig ist, wird sie sofort wieder aufgerufen, sodaß das Programm 
dann so gut wie nichts anderes macht, als in der Interrupt-Routine zu 
hängen. Das Hauptprogramm bekommt praktisch keine Rechenzeit mehr.

Das ist aber nicht der Sinn eines Interrupts.

Auch wird das Loslassen des Tasters gar nicht (sinnvoll) ausgewertet.

Für deine Anwendung macht das Sinn:
"Any logical change on INT0 generates an interrupt request."

Dann muss deine ISR so aussehen:

volatile char blink = 0;

ISR(INT0_vect)
{
   if (PINB & (1 << PB1)) blink = 0; else blink = 1;
}


Und in der main:


int main(void)
{
.
.
.

  while(1)
  {
    if (blink) Blinken(); else Dauerlicht();
  }
}

Blinken() und Dauerlicht() muß dann entsprechend durch Code ersetzt bzw. 
als Funktionen eingebaut werden.

Ist zwar immer noch ein wenig unelegant, da der Taster nicht entprellt 
ist. Das tut hier aber nichts zur Sache.

Warum das Ganze so?

Wenn der Taster gedrückt wird, wird die ISR aufgerufen, da der Taster 
nicht entprellt ist, mehrmals, aber nur solange bis er sich beruhigt 
hat. Dann wird die ISR, solange die Taste gehalten wird, NICHT mehr 
aufgerufen und der Controller werkelt im Hauptprogramm.

Das ist der Sinn eines Interrupts.
Hauptprogramm unterbrechen, KURZ was machen, Hauptprogramm läuft weiter.

Beim Loslassen des Tasters entsprechend umgekehrt.

Womit dann beide Tasterstellungen auch wirklich ausgewertet werden.


mfg.

von Stefan O. (Gast)


Lesenswert?

Hi Thomas,

danke Dir. Das Beispiel war eigentlich nur zum testen für mich gedacht 
warum in einem anderen Code nicht wie erwartet auf einen Interrupt 
reagiert wurde.

Wenn ich auf Pegeländerungen reagiere will ist laut Datenblatt im 
Register MCUCR auf ISC01=0 und ISC00=1 zu setzen.
Ist
1
MCUCR |=(0<<ISC01) | (1<<ISC00);
dann so richtig?

Ich habe mir das Beispiel 
[http://www.mikrocontroller.net/articles/Bitmanipulation] nochmal zu 
Gemüte geführt. Im Abschnitt "Bits setzen" habe ich das vom Beispiel
1
PORTB |= ((1 << MEINBIT0) | (1 << MEINBIT2)); // setzt Bit 0 und 2 in PORTB auf "1"
zumindest so abgeleitet.

Danke,

LG Stefan

von J.-u. G. (juwe)


Lesenswert?

Stefan Ott schrieb:
> Ich habe mir das Beispiel
> [http://www.mikrocontroller.net/articles/Bitmanipulation] nochmal zu
> Gemüte geführt. Im Abschnitt "Bits setzen" habe ich das vom Beispiel

Dann bitte jetzt noch den Abschnitt "Bits löschen" lesen, denn

> ISC01=0

bedeutet, dass des Bit ISC01 gelöscht werden soll.

von Stefan O. (Gast)


Lesenswert?

Hm,

mal sehen ob ich das richtig verstanden habe. Klar Bit ISC01 muss aus 
dem Register gelöscht werden. Das würde ich also mit
1
MCUCR &= ~(1<<ISC01);
erreichen.

ISC00 möchte ich aber setzen, d.h.
1
MCUCR |= (1 << ISC00);


Stefan

von J.-u. G. (juwe)


Lesenswert?

Stefan Ott schrieb:

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


So gehts.

von Stefan O. (Gast)


Lesenswert?

Danke,

noch eine abschließende Frage: Kann ich beide Codezeilen irgendwie 
vernünftig zusammenfassen?

von J.-u. G. (juwe)


Lesenswert?

Stefan Ott schrieb:
> Kann ich beide Codezeilen irgendwie
> vernünftig zusammenfassen?

Geht bestimmt irgendwie, aber ob man dann noch mit einem Blick erkennen 
kann, was diese Zeile bewirkt?

Ich würde es bei den zwei Zeilen belassen.

von Stefan O. (Gast)


Lesenswert?

Alles klar...

Danke an alle für Eure Unterstützung.

LG Stefan

von Hannes L. (hannes)


Lesenswert?

Stefan Ott schrieb:
> Kann ich beide Codezeilen irgendwie
> vernünftig zusammenfassen?

Da es sich um die Initialisierung handelt, die nur ein einziges mal pro 
Einschalten der Betriebsspannung erfolgt und immer direkt nach dem Reset 
erfolgt, kannst Du Dir die Bitmanipulation mit OR und ggf. AND sparen. 
Es genügt hier völlig, die I/O-Register per direkter Zuweisung zu 
setzen. Und dabei brauchst Du im Term nur die Einsbits zu nennen, der 
Rest ist dann automatisch Null.

Die von Dir aus dem Tutorial abgeschriebenen Bitmanipulationen sind dann 
wichtig, wenn weitere Zugriffe auf die I/O-Register erfolgen, bei denen 
darauf geachtet werden muss, dass zuvor veränderte andere Bits des 
betreffenden Registers unverändert bleiben. Beim Erstzugriff bei der 
Initialisierung ist dies nicht nötig.

...

von Stefan O. (Gast)


Lesenswert?

Dies stimmt auch wieder, ich habe aber trotzdem wieder etwas dazu 
gelernt und darum geht es mir hauptsächlich.

LG Stefan

von J.-u. G. (juwe)


Lesenswert?

Hannes Lux schrieb:
> Und dabei brauchst Du im Term nur die Einsbits zu nennen, der
> Rest ist dann automatisch Null.

Das ist im hier vorliegenden Fall richtig, deshalb hat ja seine 
ursprüngliche Version mit den veroderten Nullen auch funktioniert. Im 
Allgemeinen halte ich es aber für besseren Stil, in 
Initialisierungsroutinen alle Bits die man setzen möchte (sowohl 0 als 
auch 1) auch aufzuschreiben. Es gibt ja schliesslich auch Register, 
deren Bits zum Teil mit 1 vorbelegt sind.

von Hannes L. (hannes)


Lesenswert?

Stefan Ott schrieb:
> ich habe aber trotzdem wieder etwas dazu
> gelernt und darum geht es mir hauptsächlich.

Richtig. Bitmanipulation gehört zum Alphabet der Programmierung. Ohne 
dieses Wissen (in der jeweils benutzten Prog-Sprache) kann man 
eigentlich nicht (auf kleinen Mikrocontrollern) programmieren. Aber man 
muss es nicht immer und überall anwenden, sondern nur da, wo es auch 
sinnvoll ist.

...

von Hannes L. (hannes)


Lesenswert?

J.-u. G. schrieb:
> Version mit den veroderten Nullen auch funktioniert.

VerORte Nullen blähen nur den Code auf und machen ihn nicht unbedingt 
lesbarer.

J.-u. G. schrieb:
> halte ich es aber für besseren Stil, in
> Initialisierungsroutinen alle Bits die man setzen möchte (sowohl 0 als
> auch 1) auch aufzuschreiben.

Auch das bläht nur den Code auf, blockiert Programmspeicher und belastet 
den Lesen mit Selbstverständlichkeiten (müllt das Wesentliche zu). 
Solcher Code liest sich dann wie das Kleingedruckte in Verträgen, man 
wird mit soviel Selbstverständlichkeiten zugemüllt, dass man das 
Wesentliche nicht mehr erkennt.

Wenn man das konsequent durchziehen will, dann muss man auch alle 
I/O-Register, also auch die, die man nicht verändern will, 
initialisieren, was bei kleinen Controllern wie dem Tiny13 schon einen 
erheblichen Teil des Programmspeichers belegt.

Ich gehe davon aus, dass man beim Erstellen des Programmcodes für die 
Initialisierung der I/O-Register ins Datenblatt schaut und dabei sieht, 
welche Bits (Schalter) im jeweiligen Register vorhanden sind und welchen 
Initial-Value sie haben.

Dies ist meine persönliche Meinung, die ich Niemandem aufdrängen will. 
Ich halte mich daher jetzt hier aus einer weiteren Diskussion darüber 
raus.

...

von J.-u. G. (juwe)


Lesenswert?

Hannes Lux schrieb:
> Auch das bläht nur den Code auf, blockiert Programmspeicher und belastet
> den Lesen mit Selbstverständlichkeiten (müllt das Wesentliche zu).
> Solcher Code liest sich dann wie das Kleingedruckte in Verträgen, man
> wird mit soviel Selbstverständlichkeiten zugemüllt, dass man das
> Wesentliche nicht mehr erkennt.

Nunja, es geht doch nur um die eine oder andere Zuweisung, die sowieso 
in irgendeiner Initialisierungsroutine verschwindet. Wenn es tatsächlich 
mit dem Speicher knapp wird, und das Programm deshalb nicht mehr richtig 
läuft, lässt man die Zeilen natürlich weg.

Hannes Lux schrieb:
> Dies ist meine persönliche Meinung, die ich Niemandem aufdrängen will.

War auch nur meine Meinung. Letztendlich wird sich jeder für eine ihm 
genehme Herangehensweise entscheiden.

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.