mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Interrupt in C


Autor: SeppElek (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> 

void alarm()
{
  for (int k=0; k<5; k++) {
      for (int i=0; i<500; i++) {
        PORTD = 0x00;
      _delay_ms(10);
        PORTD = 0x01;
      _delay_ms(10);
      }
    _delay_ms(500);
  }

}

int main(void)
{
  sei();

  // Set Port D0 pin as output
  DDRD = 0x01;

  while(1) {
  }

  return 1;
}

ISR(INT0_vect)
{
    //_delay_ms(5000);
  alarm();
}


ISR(INT1_vect)
{
    //_delay_ms(15000);
  alarm();
}


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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> 

volatile uint8_t alarm_geben;

ISR(INT0_vect)
{
  alarm_geben = 1;
}

ISR(INT1_vect)
{
  alarm_geben = 1;
}

static void alarm(void)
{
  // Set Port D0 pin as output
  DDRD = 0x01;
  for (int k=0; k<5; k++) {
    for (int i=0; i<500; i++) {
      PORTD = 0x00;
      _delay_ms(10);
      PORTD = 0x01;
      _delay_ms(10);
    }
    _delay_ms(500);
  }
  DDRD = 0;
}

int main(void)
{
  // Hier fehlt die komplette Initialisierung 
  // der Interrupts INT1 und INT1
  
  // Interrupts global erlauben
  sei();

  while (1) {
    if (alarm_geben == 1) {
      alarm_geben = 0;
      alarm();
    }
  }
}

Autor: SeppElek (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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!

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Was genau muss
>ich denn bei den externen Interrupts initialisieren?

Steht im Datenblatt.

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Helfer (Gast)
Datum:

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

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: SeppElek (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :/
void alarm()
{
  for (int k=0; k<5; k++) {
      for (int i=0; i<500; i++) {
        PORTD &= (0<<6);
      _delay_ms(10);
        PORTD |= (1<<6);
      _delay_ms(10);
      }
    _delay_ms(2000);
  }
}

int main(void)
{

  // PortD6 (Buzzer) output, alles andere input
  DDRD = 0x40;

  while(1) {
    if (PORTD2==1) {
      alarm();
    }
  }

  return 1;
}

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

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#include <avr/io.h>
#include <util/delay.h>

void alarm(void)
{
  for (int k=0; k<5; k++) {
      for (int i=0; i<500; i++) {
        PORTD &= ~(1<<PD6);
        _delay_ms(10);
        PORTD |= (1<<PD6);
        _delay_ms(10);
      }
    _delay_ms(2000);
  }
}

int main(void)
{
  // PortD6 (Buzzer) output, alles andere input
  // PD6 o-----BUZZER-----o GND
  // PD2 o--+--TASTER-----o Vcc
  //        |
  //        +--Pulldown---o GND
  //        1k-50k typ. 10kOhm Widerstand
  DDRD |= (1<<PD6);

  while (1) {
    if ( (PIND & (1<<PD2)) ) { // active high
      alarm();
    }
  }
}

Wenn du den Pulldown einsparen willst:
int main(void)
{
  // PortD6 (Buzzer) output, alles andere input
  // PD6 o-----BUZZER-----o GND
  DDRD |= (1<<PD6);

  // PD2 o-----TASTER-----o GND
  //     mit internem Pull up
  PORTD |= (1<<PD2); 

  while (1) {
    if ( !(PIND & (1<<PD2)) ) { // // active low
      alarm();
    }
  }
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: SeppElek (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
PORTD |= (1<<PD2);
 aber korrekt aktiviert, oder nicht?
#include <avr/io.h>
#include <util/delay.h>

void alarm()
{
  for (int k=0; k<1; k++) {
      for (int i=0; i<500; i++) {
        PORTD &= (0<<6);
      _delay_ms(10);
        PORTD |= (1<<6);
      _delay_ms(10);
      }
    _delay_ms(2000);
  }

    _delay_ms(5000);
}

int main(void)
{

  // PortD6 (Buzzer) output, alles andere input
  DDRD |= (1<<PD6);

  PORTD |= (1<<PD2);
  

  while(1) {
    if ( !(PIND & (1<<PD2)) ) {
      alarm();
    }
  }

  return 1;
}


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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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);

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.