mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Programm bleibt in ISR hängen?


Autor: Chris Herch (hergi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich hab ein Problem mit folgendem Programm, ich weiß nicht wirklich was 
da falsch läuft. Schaltung ist diegleiche wie aus dem Thread 
Beitrag "pnp schaltet nicht richtig ab?".


#ifndef F_CPU
#define F_CPU 3686400
#endif

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h> 

uint8_t interrupt_flag = 0;
uint8_t n_digit = 0;

  

void ef_out(uint8_t digit)
{
  switch (digit)
  {
    case 0:
    PORTD = 0xFF;  // Alle digits aus
    PORTB = 0xC6;  // "E" an Port B anlegen
    PORTD = 0xFE;  // Digit 0 an
    break;
  
    case 1:
    PORTD = 0xFF;  // Alle digits aus
    PORTB = 0xCE;  // "F" an Port B anlegen
    PORTD = 0xFD;  // Digit 1 an
    break;  

    case 2:
    PORTD = 0xFF;  // Alle digits aus
    PORTB = 0xFF;  // Alle Segmente aus
    PORTD = 0xFB;  // Digit 2 an
    break;
  }
}

int main(void)
{
  DDRB = 0xFF;  // Port B Ausgang
  DDRD = 0xFF;  // Port D Ausgang

  TCCR0 = 0x03;      // Timer 0 normal mode, prescaler 64

  TIMSK = (1<<TOIE0);  // Overflow Interrupt aktivieren

  
  sei();      // Interrupts global aktivieren

  while (1)
  {
    if (interrupt_flag)
    {
      ef_out(n_digit);
      
      n_digit++;
      if (n_digit > 2)
        n_digit = 0;
      
      interrupt_flag = 0;
    }
  }

  return 0;
}

ISR(TIMER0_OVF_vect) // ISR für Timer0 Overflow
{
  interrupt_flag = 1;
}

In dem anderen Thread hatte ich ja schon ein lauffähiges Programm, 
welches ne Variable hochzählt und ausgibt, deswegen versteh ich nicht, 
warum diese einfachere Version auf einmal nicht funktioniert.

Ich will einfach nur auf der ersten Anzeige ein "E" ausgeben und auf der 
zweiten ein "F", die dritte soll einfach "aus" sein.

Wenn ich das jetzt simuliere, springt das Programm nach der Abfrage des 
interrupt_flags direkt in die ISR und kommt dort scheinbar nicht mehr 
raus, d.h. sobald die ISR beendet wird springt das Programm sofort 
wieder rein.

Ich kann mir das nicht erklären, weiß jemand von euch was da los ist?

Autor: Route_66 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!
Man kann es mit der Länge einer Interrupt-Routine auch untertreiben!
Das bischen

>void ef_out(uint8_t digit)

passt doch locker in die INT.

Autor: Chris Herch (hergi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tatsächlich, wenn ich das mit dem flag lasse und alles direkt in die ISR 
schreibe funktionierts. Nur wieso? Müsste doch anders genauso gehen oder 
nicht?

Autor: Helfer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
FAQ Thema bei Interrupt Stichwort volatile

Autor: Stephan_II (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

mache es so mit der deklaration variable interrupt_flag :
volatile uint8_t interrupt_flag = 0;

sonst merkt er nicht von der veränderung des flags durch die ISR

Gruß

 Stephan

Autor: Andreas H. (anderlh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Chris,
vermutlich wird deine Main-Routine ständig von der 
Interrupt-Service-Routine unterbrochen (Dazu ist die ISR berechtigt). 
Ist das Grundprinzip von Interrupts klar?
Wenn Du was interrupt-getriggertes außerhalb der zugehörigen ISR machen 
möchtest, dann schalte dafür die Interrupts ab.
Macht man eigentlich aber nicht so.
Gruß
Andi

Autor: Chris Herch (hergi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ah, danke für den Hinweis, ich hatte den Artikel zwar schonmal gelesen, 
diese Tatsache aber anscheinend wieder vergessen.

Komisch nur, dass folgendes Programm mit zwei Timern und zwei derartigen 
Flags funktioniert:
#ifndef F_CPU
#define F_CPU 3686400
#endif

#include <avr/io.h>
#include <stdint.h>
#include <util/delay.h>
#include <avr/interrupt.h> 

uint8_t interrupt_flag = 0;
uint8_t timer_flag = 0;  


// Legt das entsprechende Bitmuster der Ziffer ziff an PORTB an
void ziff_out(uint8_t ziff)
{
...
}

// Gibt die Stelle digit der Zahl num an der entsprechenden 7Segment Anzeige aus
void num_out(uint8_t num, uint8_t digit)
{
...
}

int main(void)
{
  DDRB = 0xFF;  // Port B Ausgang
  DDRD = 0xFF;  // Port D Ausgang

  TCCR0 = 0x03;      // Timer 0 normal mode, prescaler 64

  TCCR1A = 0x00;
  TCCR1B = 0x03;      // Timer 1 normal mode, prescaler 64

  TIMSK |= (1<<TOIE0) | (1<<TOIE1);  // Overflow Interrupt aktivieren

  
  sei();      // Interrupts global aktivieren

  uint8_t value = 123;
  uint8_t n_digit = 0;

  while (1)
  {
    if (interrupt_flag)
    {
      n_digit++;
      if (n_digit > 2)
        n_digit = 0;
      num_out(value, n_digit);

      interrupt_flag = 0;
    }

    if (timer_flag)
    {
      value++;

      timer_flag = 0;
    }
  }

  return 0;
}

ISR(TIMER0_OVF_vect) // ISR für Timer0 Overflow
{
  interrupt_flag = 1;
}

ISR(TIMER1_OVF_vect) // ISR für Timer1 Overflow
{
  timer_flag = 1;
}

Autor: Marvin M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Andreas Hoeter:

Kannst Du das bitte nochmal erklären? Ich versteh nur Bahnhof und das 
Problem löst das auch nicht (oben genanntes fehlendes volatile).

Autor: Marvin M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Chris:

Zufall, dass es funktioniert. Manchmal optimiert der Compiler anders.

Autor: I. L. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mach doch mal die Optimierung aus!



Gruß Knut

Autor: Andreas H. (anderlh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Abarbeitung der Main-Routine wird abgebrochen, wenn irgendwo ein 
Interrupt auftritt. Dann arbeitet der Controller die zugehörige ISR ab 
und kehrt nach Abschluß an die Stelle in der Mainroutine zurück, an der 
er abgebrochen hat. Das sollte klar sein.

http://www.mikrocontroller.net/articles/AVR-Tutori...
http://www.mikrocontroller.net/articles/Interrupt

Vermutung:
1.
Wenn jetzt die "Interruptfrequenz" zu hoch ist, hast Du den 
beschriebenen "Fehler". Er springt zu häufig aus der Mainroutine raus. 
Dein Programm funktioniert sobald Du ohne das Flag arbeitest und den 
auszuführenden Programmteil direkt in die ISR reinmachst. Die ISR wird 
nämlich nicht unterbrochen.

Vielleicht mal den Prescaler hochsetzen.

Oder in der Serviceroutine das Interruptflag in TIMSK wegnehmen und nach 
Abarbeitung in der Mainroutine wieder setzen. Damit schaltest Du die 
Interupts während der Abarbeitung in der Mainroutine aus. Zur 
Fehlersuche ist das i.O., allerdings machst Du dir die Zeitbasis, die 
mit den regelmäßigen Interrupts schaffst kaputt. Wie oben schon gesagt 
ist das kein guter Stil.
int main(void)
{
  DDRB = 0xFF;  // Port B Ausgang
  DDRD = 0xFF;  // Port D Ausgang

  TCCR0 = 0x03;      // Timer 0 normal mode, prescaler 64

  TIMSK = (1<<TOIE0);  // Overflow Interrupt aktivieren

  
  sei();      // Interrupts global aktivieren

  while (1)
  {
    if (interrupt_flag)
    {
      ef_out(n_digit);
      
      n_digit++;
      if (n_digit > 2)
        n_digit = 0;
      
      interrupt_flag = 0;
      TIMSK |= (1<<TOIE0); /INT ein
    }
  }

  return 0;
}

ISR(TIMER0_OVF_vect) // ISR für Timer0 Overflow
{
  interrupt_flag = 1;
  TIMSK &= ~(1<<TOIE0); //INT aus
}


2.
wie meine Vorredner schon sagten -> Optimierung

Fehlersuche:
Mal mit Setzen von Portpins die Dauer der relevanten Programmteile 
prüfen und mit der Interruptfrequenz vergleichen.

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.