Forum: Mikrocontroller und Digitale Elektronik Interrupt in C


von SeppElek (Gast)


Lesenswert?

Grüßt euch,

ich wollte gerade eine einfach Interrupt-Routine in C programmieren, 
jedoch will der uC nicht so wie ich :/

Ausgangssituation:
An Port D0 ist ein Signalgeber angeschlossen. An Port D3 (INT0) und Port 
D4 (INT1) ist jeweils ein Schalter gegen Vcc angeschlossen. Bei einer 
Flanke an einem der beiden Interrupt-Anschlüssen, soll nun die Methode 
alarm() ausgeführt werden.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h> 
4
5
void alarm()
6
{
7
  for (int k=0; k<5; k++) {
8
      for (int i=0; i<500; i++) {
9
        PORTD = 0x00;
10
      _delay_ms(10);
11
        PORTD = 0x01;
12
      _delay_ms(10);
13
      }
14
    _delay_ms(500);
15
  }
16
17
}
18
19
int main(void)
20
{
21
  sei();
22
23
  // Set Port D0 pin as output
24
  DDRD = 0x01;
25
26
  while(1) {
27
  }
28
29
  return 1;
30
}
31
32
ISR(INT0_vect)
33
{
34
    //_delay_ms(5000);
35
  alarm();
36
}
37
38
39
ISR(INT1_vect)
40
{
41
    //_delay_ms(15000);
42
  alarm();
43
}


Hab ich was vergessen oder warum funktioniert das nicht?

PS: Die Ports haben alle einen Pull-Up Widerstand. Muss ich dann die 
Schalter der Interrups mit GND verbinden, dass eine Flanke entsteht?

Vielen Dank schonmal für eure Hilfe!

Grüße Sebastian

von Karl H. (kbuchegg)


Lesenswert?

SeppElek schrieb:

> int main(void)
> {
>   sei();
>
>   // Set Port D0 pin as output
>   DDRD = 0x01;
>
>   while(1) {
>   }
>
>   return 1;
> }

> Hab ich was vergessen oder warum funktioniert das nicht?

Ähm.
Im µC gibt es viele Interrupt Quellen.
Wenn du eine benutzen willst, dann musst du die Quelle dafür auch 
freigeben.

sei ist ein Rundumschlagschalter, mit dem alle überhaupt möglichen 
Interrupts unterdrückt bzw. eigentlich freigegeben werden. Das hat aber 
ncihts damit zu tun, dass man einer INterruptquelle selber auch die 
Freigabe erteilen muss.

PS:
Schalter, der nach Vcc schaltet ist eine ganz schlechte Idee.
Da muss man dann einen externen Pulldown Widerstand an den Pin 
anschliessen.
Würde man den Schalter anders rum verdrahten (schaltet nach GND), dann 
könnte man den internen Pullup Widerstand benutzen.

Taster oder Schalter über Interrupts abzufragen ist auch keine so gute 
Idee. Reines Polling reicht da dicke.

AVR-Tutorial
AVR-GCC-Tutorial

von Helfer (Gast)


Lesenswert?

> Hab ich was vergessen oder warum funktioniert das nicht?

Ja, vergessen hast du die komplette Initialisierung der beiden 
Interrupts INT0 und INT1. Ohne Schaltplan und AVR-Typ ist es schwer, da 
genauer etwas zu schreiben.

Die lange Funktion alarm() aus den ISR heraus zu starten ist 
gewöhnungsbedürfig. Du musst bedenken, dass während eine ISR 
abgearbeitet wird andere ISRs gesperrt sind! Deine Variante alarm() 
aufzurufen zählt als innerhalb der ISR. Üblicher ist eher diese Art der 
Programmierung mit einem globalen Flag:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h> 
4
5
volatile uint8_t alarm_geben;
6
7
ISR(INT0_vect)
8
{
9
  alarm_geben = 1;
10
}
11
12
ISR(INT1_vect)
13
{
14
  alarm_geben = 1;
15
}
16
17
static void alarm(void)
18
{
19
  // Set Port D0 pin as output
20
  DDRD = 0x01;
21
  for (int k=0; k<5; k++) {
22
    for (int i=0; i<500; i++) {
23
      PORTD = 0x00;
24
      _delay_ms(10);
25
      PORTD = 0x01;
26
      _delay_ms(10);
27
    }
28
    _delay_ms(500);
29
  }
30
  DDRD = 0;
31
}
32
33
int main(void)
34
{
35
  // Hier fehlt die komplette Initialisierung 
36
  // der Interrupts INT1 und INT1
37
  
38
  // Interrupts global erlauben
39
  sei();
40
41
  while (1) {
42
    if (alarm_geben == 1) {
43
      alarm_geben = 0;
44
      alarm();
45
    }
46
  }
47
}

von SeppElek (Gast)


Angehängte Dateien:

Lesenswert?

Helfer schrieb:
> Ja, vergessen hast du die komplette Initialisierung der beiden
> Interrupts INT0 und INT1. Ohne Schaltplan und AVR-Typ ist es schwer, da
> genauer etwas zu schreiben.

Der uC ist ein Atmel ATtiny88, Schaltplan ist im Anhang. Was genau muss 
ich denn bei den externen Interrupts initialisieren? Bei 
Timer-Interrupts ist klar, da gibts ja einiges zu initialisieren. Aber 
bei den externen..?

Danke für eure Antworten!

von holger (Gast)


Lesenswert?

>Was genau muss
>ich denn bei den externen Interrupts initialisieren?

Steht im Datenblatt.

von Helfer (Gast)


Lesenswert?

> Was genau muss ich denn bei den externen Interrupts initialisieren?

Zum Beispiel musst du die Interrupt Enable Maske passend für die beiden 
Interrupts setzen, damit die auch berücksichtigt werden. Und du musst 
dich entscheiden auf was die Interrupts reagieren sollen. Also auf LOW 
Pegel, Pegelwechsel, steigende Flanke oder fallende Flanke. Das sind 
beim Atmega88 zwei Register EIMSK und EICRA mit ihren entsprechenden 
Bits, die im Datenblatt im Abschnitt 12. External Interrupts beschrieben 
sind.

Im Schaltplan hast du ja die beiden Pins zu INT0 und INT1 universell zu 
der Klemme gezogen. Ohne externe Pull-up oder Pull-Down widerstände, die 
einen  Ruhepegel festlegen. Kann man machen, wenn das Bauteil an der 
Klemme von allein saubere Logikpegel ausgibt. Wenn dort nur ein Taster 
oder ein Transistorschalter fehlt da noch was. OK, man kann durch die 
internen Pull-ups externe ersetzen, aber das wäre z.B. auch ein Teil der 
Initialisierung.

von Helfer (Gast)


Lesenswert?

Sorry verpeilt: Attiny88 hast du, keinen Atmega88. Moment ich muss das 
DB erst holen...

von Helfer (Gast)


Lesenswert?

Gleicher Name EIMSK für das Freischalten. Gleicher Name EICRA für die 
Einstellung der Interruptursache. Abschnitt im Datenblatt 9.2 und 9.3

von SeppElek (Gast)


Lesenswert?

Okay, also die Daten zur Initialisierung hab ich im Datenblatt gefunden 
und auch angewandt. Zu meiner Schande muss ich allerdings zugeben, dass 
ich es einfach nicht hinbekommen habe :/

Auch beim nächsten Versuch, das Problem per pollying zu lösen, scheitere 
ich kläglich :/
1
void alarm()
2
{
3
  for (int k=0; k<5; k++) {
4
      for (int i=0; i<500; i++) {
5
        PORTD &= (0<<6);
6
      _delay_ms(10);
7
        PORTD |= (1<<6);
8
      _delay_ms(10);
9
      }
10
    _delay_ms(2000);
11
  }
12
}
13
14
int main(void)
15
{
16
17
  // PortD6 (Buzzer) output, alles andere input
18
  DDRD = 0x40;
19
20
  while(1) {
21
    if (PORTD2==1) {
22
      alarm();
23
    }
24
  }
25
26
  return 1;
27
}

Die Methode alarm() in der while-Schleife wird einfach nie ausgefüht, 
obwohl PortD2 zu Vcc verbunden wurde. Kann mir hier jemand helfen? :-$

von Helfer (Gast)


Lesenswert?

1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
void alarm(void)
5
{
6
  for (int k=0; k<5; k++) {
7
      for (int i=0; i<500; i++) {
8
        PORTD &= ~(1<<PD6);
9
        _delay_ms(10);
10
        PORTD |= (1<<PD6);
11
        _delay_ms(10);
12
      }
13
    _delay_ms(2000);
14
  }
15
}
16
17
int main(void)
18
{
19
  // PortD6 (Buzzer) output, alles andere input
20
  // PD6 o-----BUZZER-----o GND
21
  // PD2 o--+--TASTER-----o Vcc
22
  //        |
23
  //        +--Pulldown---o GND
24
  //        1k-50k typ. 10kOhm Widerstand
25
  DDRD |= (1<<PD6);
26
27
  while (1) {
28
    if ( (PIND & (1<<PD2)) ) { // active high
29
      alarm();
30
    }
31
  }
32
}

Wenn du den Pulldown einsparen willst:
1
int main(void)
2
{
3
  // PortD6 (Buzzer) output, alles andere input
4
  // PD6 o-----BUZZER-----o GND
5
  DDRD |= (1<<PD6);
6
7
  // PD2 o-----TASTER-----o GND
8
  //     mit internem Pull up
9
  PORTD |= (1<<PD2); 
10
11
  while (1) {
12
    if ( !(PIND & (1<<PD2)) ) { // // active low
13
      alarm();
14
    }
15
  }
16
}

von Karl H. (kbuchegg)


Lesenswert?

SeppElek schrieb:

> Auch beim nächsten Versuch, das Problem per pollying zu lösen, scheitere
> ich kläglich :/

AVR-GCC-Tutorial

Ist weiter oben schon mal gefallen.


>   while(1) {
>     if (PORTD2==1) {

Autsch!
Das sidn Grundlagen. Umsomehr: Blick ins AVR-GCC-Tutorial

von Helfer (Gast)


Lesenswert?

Mit dem MFP1320 als Schiebeschalter an der Klemme bleibt dir praktisch 
nur die zweite Variante mit der active low Schaltung.

http://www.knitter-switch.com/pdf_en_prod11/ss_pss_seriesMFP_MFS2.pdf

von SeppElek (Gast)


Lesenswert?

Hab die zweite Lösung nun übernommen, jedoch kommt es mir so vor, als 
wäre der interne Pull-Up Widerstand defekt. Denn sobald ich den Schalter 
einmal umgelegt habe, führt er alarm() aus, wenn ich ihn zurück schalte, 
hört er aber nicht auf. Also sprich: PIND2 bleibt auf GND.

Der Pull-Up Widerstand wurde mit
1
PORTD |= (1<<PD2);
 aber korrekt aktiviert, oder nicht?
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
void alarm()
5
{
6
  for (int k=0; k<1; k++) {
7
      for (int i=0; i<500; i++) {
8
        PORTD &= (0<<6);
9
      _delay_ms(10);
10
        PORTD |= (1<<6);
11
      _delay_ms(10);
12
      }
13
    _delay_ms(2000);
14
  }
15
16
    _delay_ms(5000);
17
}
18
19
int main(void)
20
{
21
22
  // PortD6 (Buzzer) output, alles andere input
23
  DDRD |= (1<<PD6);
24
25
  PORTD |= (1<<PD2);
26
  
27
28
  while(1) {
29
    if ( !(PIND & (1<<PD2)) ) {
30
      alarm();
31
    }
32
  }
33
34
  return 1;
35
}


Tut mir nochmals leid, dass ich euch zur späten Stunde mit meinen 
Anfängerfragen quälen muss :/

von Karl H. (kbuchegg)


Lesenswert?

Dasss deine Alarm Funktion 17 Sekunden benötigt ehe sie wieder in die 
Hauptschleife zurückgibt hast du bei deiner Überlegung berücksichtigt?

von Karl H. (kbuchegg)


Lesenswert?

>         PORTD &= (0<<6);

So löscht mein kein einzelnes Bit. Gratulation, damit hast du gerade den 
Pullup ausgeschaltet.

Tu dir selbst einen Gefallen und schau ins AVR-GCC-Tutorial !
Es macht nicht wirklich Sinn, wenn wir dir hier das Tutorial in Auszügen 
noch einmal vorbeten müssen nur weil du selbst nicht nachlesen willst.

          PORTD &= ~(1<<6);

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.