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


von Chris H. (hergi)


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?".


1
#ifndef F_CPU
2
#define F_CPU 3686400
3
#endif
4
5
#include <avr/io.h>
6
#include <stdint.h>
7
#include <avr/interrupt.h> 
8
9
uint8_t interrupt_flag = 0;
10
uint8_t n_digit = 0;
11
12
  
13
14
void ef_out(uint8_t digit)
15
{
16
  switch (digit)
17
  {
18
    case 0:
19
    PORTD = 0xFF;  // Alle digits aus
20
    PORTB = 0xC6;  // "E" an Port B anlegen
21
    PORTD = 0xFE;  // Digit 0 an
22
    break;
23
  
24
    case 1:
25
    PORTD = 0xFF;  // Alle digits aus
26
    PORTB = 0xCE;  // "F" an Port B anlegen
27
    PORTD = 0xFD;  // Digit 1 an
28
    break;  
29
30
    case 2:
31
    PORTD = 0xFF;  // Alle digits aus
32
    PORTB = 0xFF;  // Alle Segmente aus
33
    PORTD = 0xFB;  // Digit 2 an
34
    break;
35
  }
36
}
37
38
int main(void)
39
{
40
  DDRB = 0xFF;  // Port B Ausgang
41
  DDRD = 0xFF;  // Port D Ausgang
42
43
  TCCR0 = 0x03;      // Timer 0 normal mode, prescaler 64
44
45
  TIMSK = (1<<TOIE0);  // Overflow Interrupt aktivieren
46
47
  
48
  sei();      // Interrupts global aktivieren
49
50
  while (1)
51
  {
52
    if (interrupt_flag)
53
    {
54
      ef_out(n_digit);
55
      
56
      n_digit++;
57
      if (n_digit > 2)
58
        n_digit = 0;
59
      
60
      interrupt_flag = 0;
61
    }
62
  }
63
64
  return 0;
65
}
66
67
ISR(TIMER0_OVF_vect) // ISR für Timer0 Overflow
68
{
69
  interrupt_flag = 1;
70
}

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?

von Route_66 (Gast)


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.

von Chris H. (hergi)


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?

von Helfer (Gast)


Lesenswert?

FAQ Thema bei Interrupt Stichwort volatile

von Stephan_II (Gast)


Lesenswert?

Hallo

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

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

Gruß

 Stephan

von Andreas H. (anderlh)


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

von Chris H. (hergi)


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:
1
#ifndef F_CPU
2
#define F_CPU 3686400
3
#endif
4
5
#include <avr/io.h>
6
#include <stdint.h>
7
#include <util/delay.h>
8
#include <avr/interrupt.h> 
9
10
uint8_t interrupt_flag = 0;
11
uint8_t timer_flag = 0;  
12
13
14
// Legt das entsprechende Bitmuster der Ziffer ziff an PORTB an
15
void ziff_out(uint8_t ziff)
16
{
17
...
18
}
19
20
// Gibt die Stelle digit der Zahl num an der entsprechenden 7Segment Anzeige aus
21
void num_out(uint8_t num, uint8_t digit)
22
{
23
...
24
}
25
26
int main(void)
27
{
28
  DDRB = 0xFF;  // Port B Ausgang
29
  DDRD = 0xFF;  // Port D Ausgang
30
31
  TCCR0 = 0x03;      // Timer 0 normal mode, prescaler 64
32
33
  TCCR1A = 0x00;
34
  TCCR1B = 0x03;      // Timer 1 normal mode, prescaler 64
35
36
  TIMSK |= (1<<TOIE0) | (1<<TOIE1);  // Overflow Interrupt aktivieren
37
38
  
39
  sei();      // Interrupts global aktivieren
40
41
  uint8_t value = 123;
42
  uint8_t n_digit = 0;
43
44
  while (1)
45
  {
46
    if (interrupt_flag)
47
    {
48
      n_digit++;
49
      if (n_digit > 2)
50
        n_digit = 0;
51
      num_out(value, n_digit);
52
53
      interrupt_flag = 0;
54
    }
55
56
    if (timer_flag)
57
    {
58
      value++;
59
60
      timer_flag = 0;
61
    }
62
  }
63
64
  return 0;
65
}
66
67
ISR(TIMER0_OVF_vect) // ISR für Timer0 Overflow
68
{
69
  interrupt_flag = 1;
70
}
71
72
ISR(TIMER1_OVF_vect) // ISR für Timer1 Overflow
73
{
74
  timer_flag = 1;
75
}

von Marvin M. (Gast)


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).

von Marvin M. (Gast)


Lesenswert?

@Chris:

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

von I. L. (Gast)


Lesenswert?

Mach doch mal die Optimierung aus!



Gruß Knut

von Andreas H. (anderlh)


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-Tutorial:_Interrupts
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.
1
int main(void)
2
{
3
  DDRB = 0xFF;  // Port B Ausgang
4
  DDRD = 0xFF;  // Port D Ausgang
5
6
  TCCR0 = 0x03;      // Timer 0 normal mode, prescaler 64
7
8
  TIMSK = (1<<TOIE0);  // Overflow Interrupt aktivieren
9
10
  
11
  sei();      // Interrupts global aktivieren
12
13
  while (1)
14
  {
15
    if (interrupt_flag)
16
    {
17
      ef_out(n_digit);
18
      
19
      n_digit++;
20
      if (n_digit > 2)
21
        n_digit = 0;
22
      
23
      interrupt_flag = 0;
24
      TIMSK |= (1<<TOIE0); /INT ein
25
    }
26
  }
27
28
  return 0;
29
}
30
31
ISR(TIMER0_OVF_vect) // ISR für Timer0 Overflow
32
{
33
  interrupt_flag = 1;
34
  TIMSK &= ~(1<<TOIE0); //INT aus
35
}

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.

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.