sei() verhindert Programmausführung

OP #2062139
Lesenswert?

Guten Morgen,
in meinem Projekt sollen verschiedene Zeiten mit dem Timer0 des Atmega32 
gehandelt werden. Nun ist es so, dass wenn ich die globale 
Interruptfreigabe aktiviere (sei();) mein Programm sich genau an dieser 
Stelle aufhängt - sprich, es wird nicht weiter ausgeführt.

Kommentiere ich sei(); aus, läuft das Programm ganz normal bis zum Ende. 
Die ISR ist meiner Ansicht nach korrekt, wobei hier lediglich drei 
Variablen hochgezählt werden. Die Deklaration der Variablen in der ISR 
sind alle volatile uint8_t !

Anbei einmal der Code der main:
1
#include "dogm163_spi.h"
2
#include "ADC_conf.h"
3
#include "mydefs.h"
4
#include "timer_config.h"
5

6

7
#include <avr/interrupt.h>
8

9

10
FILE lcd_str = FDEV_SETUP_STREAM(write_char, NULL, _FDEV_SETUP_WRITE);
11

12

13

14
//------- Interrupt Service Routine für Timer0 -----------------
15

16

17

18
ISR(TIMER0_COMP_vect)
19
{
20

21
  sysTime++;
22
  LCD_refreshTime++;
23
  LCD_startTime++;
24

25
}
26

27

28
int main()
29
{
30

31

32

33

34
// ------------------ Initialisierungen -------------------------
35

36
adc_init();        // ADC initialisieren
37
SPI_Init();        // SPI initialisieren
38

39

40
display_init();      // LCD initialisieren
41
set_contrast (11);    // LCD Kontrast setzen
42

43
stderr = &lcd_str;
44

45
  
46
// ------------------------- I/O Funktionen ---------------------
47

48

49
DDRD = 0x20;
50

51

52
//-------------------- Interruptparameter ------------------------
53

54

55

56
OCR0  = 125;            // Compare Register mit Wert für Interrupt bei 10ms blegen - 0% Fehler  
57
TIMSK |= (1<<OCIE0);        // Timer 0 Output Compare Interrupt freigeben
58
TCCR0 |=   (1<<CS00) | (1<<CS01);  // Mode: Normal, Timer 0 Takt: CLK / 64  
59
TIFR  |= (1<<OCF0);          // Output Compare Flag
60

61

62
// -------------------------- Hauptprogramm ---------------------
63

64
    
65
    // ------------------------ Startbildschirm --------------- 
66
        
67
      
68
      PORTD |= LCD_backLightOn;        // Hintergrundbeleuchtung ein
69
      
70
      clear_home();
71
      set_cursor(0);
72
      set_doubleheight (0);
73
      fprintf(stderr, "LDO V_Regulator");
74
      set_cursor(16);
75
      fprintf(stderr, "Version 1.0");
76

77
      
78
      _delay_ms(2000);
79

80
sei();              // globale Interruptfreigabe
81

82

83

84
    // --------------------- Temperaturmessung -----------------
85

86

87

88

89

90

91

92
    // ---------------- Grundmenue 1 -----------------------------
93
          
94
      mergVin = 5.5;         // Dummy fuer Anzeige  
95
          
96
      clear_home();
97
      set_doubleheight (0);
98
      set_cursor(0);
99
      vfprintf(stderr, "U_in: %0.2fV", &mergVin);
100
      set_cursor(16);
101
      vfprintf(stderr, "Kapaz: %0.2f mAh", &mergVin);
102
      set_cursor(33);
103
      vfprintf(stderr, "Temp1: %0.1f C", &mergVin);
104

105
}


Ich übersehe bestimmt irgendwas, komme aber seit ein paar Tagen nicht 
drauf. Vielleicht hat mir jemand von euch einen Tipp? :)

Danke im Voraus für die Hilfe!


Gruß
Stefan
OP #2062347
Lesenswert?

Hi Michael,
dass die while(1) derzeit fehlt ist mir schon klar. Das hat aber den 
Grund, dass die erste LCD-Ausgabe einen Startbildschirm darstellt, 
welcher für 2sec nach dem Einschalten angezeigt wird. Anbei nochmals der 
Code, diesmal mit einer Wartezeit, welche mit der ISR generiert bzw. 
verglichen wird. Hier also nochmals der Codeabschnitt, welcher so nicht 
funktioniert (auf dem LCD wird überhaupt nichts angezeigt, die SPI wird 
bei Verwendung von "sei();" auch nicht initalisiert (Keine Zeichen auf 
dem LCD während der Initialisierung)

Anbei der zu verwendende Code:

[c]
#include "dogm163_spi.h"
#include "ADC_conf.h"
#include "mydefs.h"
#include "timer_config.h"


#include <avr/interrupt.h>


FILE lcd_str = FDEV_SETUP_STREAM(write_char, NULL, _FDEV_SETUP_WRITE);



//------- Interrupt Service Routine für Timer0 -----------------



ISR(TIMER0_COMP_vect)
{

  sysTime++;
  LCD_refreshTime++;
  LCD_startTime++;

}


int main()
{




// ------------------ Initialisierungen -------------------------

adc_init();        // ADC initialisieren
SPI_Init();        // SPI initialisieren


display_init();      // LCD initialisieren
set_contrast (11);    // LCD Kontrast setzen

stderr = &lcd_str;


// ------------------------- I/O Funktionen ---------------------


DDRD = 0x20;


//-------------------- Interruptparameter ------------------------



OCR0  = 125;            // Compare Register mit Wert für Interrupt bei 
10ms blegen - 0% Fehler
TIMSK |= (1<<OCIE0);        // Timer 0 Output Compare Interrupt 
freigeben
TCCR0 |=   (1<<CS00) | (1<<CS01);  // Mode: Normal, Timer 0 Takt: CLK / 
64
TIFR  |= (1<<OCF0);          // Output Compare Flag
sei();                // globale Interruptfreigabe




// -------------------------- Hauptprogramm ---------------------


    // ------------------------ Startbildschirm ---------------


      PORTD |= LCD_backLightOn;        // Hintergrundbeleuchtung ein

      clear_home();
      set_cursor(0);
      set_doubleheight (0);
      fprintf(stderr, "LDO V_Regulator");
      set_cursor(16);
      fprintf(stderr, "Version 1.0");


    while(LCD_startTime < LCD_startWait) { }  // Wartezeit 2sec

[c/]

die Zeile mit "_delay_ms()" aus dem ersten Code war nur zum Testen! 
Sorry, wenn das jetzt Verwirrung stiftet.

@ Charly:
die ISR ist ja nicht leer. In der Routine werden nur drei Variablen 
inkrementiert, welche dann wieder zur Überprüfung der 
Startbildschirmzeit hergenommen werden.


Viele Grüße
Stefan
OP #2062350
Lesenswert?

Hi Michael,
dass die while(1) derzeit fehlt ist mir schon klar. Das hat aber den 
Grund, dass die erste LCD-Ausgabe einen Startbildschirm darstellt, 
welcher für 2sec nach dem Einschalten angezeigt wird. Anbei nochmals der 
Code, diesmal mit einer Wartezeit, welche mit der ISR generiert bzw. 
verglichen wird. Hier also nochmals der Codeabschnitt, welcher so nicht 
funktioniert (auf dem LCD wird überhaupt nichts angezeigt, die SPI wird 
bei Verwendung von "sei();" auch nicht initalisiert (Keine Zeichen auf 
dem LCD während der Initialisierung)

Anbei der zu verwendende Code:
1
#include "dogm163_spi.h"
2
#include "ADC_conf.h"
3
#include "mydefs.h"
4
#include "timer_config.h"
5

6

7
#include <avr/interrupt.h>
8

9

10
FILE lcd_str = FDEV_SETUP_STREAM(write_char, NULL, _FDEV_SETUP_WRITE);
11

12

13

14
//------- Interrupt Service Routine für Timer0 -----------------
15

16

17

18
ISR(TIMER0_COMP_vect)
19
{
20

21
  sysTime++;
22
  LCD_refreshTime++;
23
  LCD_startTime++;
24

25
}
26

27

28
int main()
29
{
30

31

32

33

34
// ------------------ Initialisierungen -------------------------
35

36
adc_init();        // ADC initialisieren
37
SPI_Init();        // SPI initialisieren
38

39

40
display_init();      // LCD initialisieren
41
set_contrast (11);    // LCD Kontrast setzen
42

43
stderr = &lcd_str;
44

45
  
46
// ------------------------- I/O Funktionen ---------------------
47

48

49
DDRD = 0x20;
50

51

52
//-------------------- Interruptparameter ------------------------
53

54

55

56
OCR0  = 125;            // Compare Register mit Wert für Interrupt bei 10ms blegen - 0% Fehler  
57
TIMSK |= (1<<OCIE0);        // Timer 0 Output Compare Interrupt freigeben
58
TCCR0 |=   (1<<CS00) | (1<<CS01);  // Mode: Normal, Timer 0 Takt: CLK / 64  
59
TIFR  |= (1<<OCF0);          // Output Compare Flag
60
sei();                // globale Interruptfreigabe
61

62

63

64

65
// -------------------------- Hauptprogramm ---------------------
66

67
    
68
    // ------------------------ Startbildschirm --------------- 
69
        
70
      
71
      PORTD |= LCD_backLightOn;        // Hintergrundbeleuchtung ein
72
      
73
      clear_home();
74
      set_cursor(0);
75
      set_doubleheight (0);
76
      fprintf(stderr, "LDO V_Regulator");
77
      set_cursor(16);
78
      fprintf(stderr, "Version 1.0");
79

80
      
81
    while(LCD_startTime < LCD_startWait) { }  // Wartezeit 2sec

die Zeile mit "_delay_ms()" aus dem ersten Code war nur zum Testen! 
Sorry, wenn das jetzt Verwirrung stiftet.

Also nochmal das Problem zusammengefasst:

1) setze ich sei(); vor die LCD Ausgabe, wird mein Display nicht mehr 
initialisiert und es wird nichts angezeigt!

2) setze ich sei(); nach der LCD Ausgabe, funktioniert soweit die Init 
des LCDs und es wird auch der entspr. Text ausgegeben. Allerdings hängt 
er sich dann an der while-schleife auf....!


@ Charly:
die ISR ist ja nicht leer. In der Routine werden nur drei Variablen 
inkrementiert, welche dann wieder zur Überprüfung der 
Startbildschirmzeit hergenommen werden.


Viele Grüße
Stefan
OP #2062658
Lesenswert?

Hi Thomas,
die Variablen sind als "volatile" deklariert, da sonst die main ja nicht 
mitbekommt, dass sich diese ändern. Die Optimierung hab ich im 
AVR-Studio für die Tests ausgeschalten.

Das WGM01-Bit im TCCR0 hab ich tatsächlich vergessen zu setzen, macht 
aber leider auch keinen Unterschied bezüglich der nicht vorhanden 
Funktion :( Irgendwo spuckt mir die globale Interruptfreigabe rein und 
ich find es nicht. Nur alle Zeiten mit Delays aufzuziehen kommt nicht in 
Frage :)

Ich schreib den Code jetzt mal auf ein Minumum zusammen und deklariere 
alle verwendeten Variablen in der main.c (und nicht über Header). Ich 
weiß, dass das keinen Unterschied machen sollte, aber eventuell geht mir 
ja noch ein Licht auf ;)

Für weitere Ideen bin ich immer dankbar.


Viele Grüße
Stefan
OP #2062762
Lesenswert?

Huch... kaum ist man 10min beim Spülen schon wieder jede Menge Postings. 
Versuche mal alles zu beantworten...

@ Peter
Das Programm bleibt stehen, es gibt keinen Reset (würde man an der 
erneuten Init des SPI bzw. des Displays sehen). Der Overflow ist, wie du 
sagst, nicht aktiviert, daher dürfte auch kein Handler nötig sein. Zum 
Testen hab ich trotzdem einen leeren Handler geschrieben, bringt aber 
keine Veränderung.. :/

Es ist der korrekte Prozessertyp sowie die richtige Taktfrequenz 
eingetragen. Ich kann mir nur vorstellen, dass sei(); sich mit 
irgendeiner anderen Funktion in die Quere kommt. Ich hab das Programm 
Schritt für Schritt im Simulator ablaufen lassen. Er springt korrekt in 
jede Funktion, führt sie aus und kehrt nach main zurück. Bei sei(); ist 
dann Ende der Fahnenstange.

@ Sven
Ich muss sehen wie ich die ganzen LCD-Ausgaben (Menüs) später handle um 
möglichst wenig Zeit und Rechenleistung zu verbraten. Klar kann ich die 
erste Ausgabe auch in die ISR packen, aber es ist, wie gesagt, nur ein 
Startbildschirm, der ein einziges Mal aufgerufen werden soll.

Ich hab heute abend noch etwas Zeit. Werde die Interruptgeschicht ohne 
die adc_init und die Displaygeschichte probieren. Hab noch ein paar 
Status-LEDs auf meinem Projekt und werde die für die Ausgabe 
missbrauchen.

Danke euch für das rege Feedback. Werde weiter berichten....! :)

Gruß
Stefan
#2063470
Lesenswert?

Stefan S. schrieb:

> @ Charly:
> die ISR ist ja nicht leer. In der Routine werden nur drei Variablen
> inkrementiert, welche dann wieder zur Überprüfung der
> Startbildschirmzeit hergenommen werden.

ja das hab i schon gesehen, soviel ahnung hab i dann doch von C

ich meinte du sollst eine 'leere' ISR 'einbauen' dh.
an der ISR Adresse steht ein iret

kannste mal von der 'minimalversion' den hexfile posten ?

was ist mit den SP ?

vlG
Charly
#2063472
Lesenswert?

> Schritt für Schritt im Simulator ablaufen lassen. Er springt korrekt in
> jede Funktion, führt sie aus und kehrt nach main zurück. Bei sei(); ist
> dann Ende der Fahnenstange.

Umso wichtier, dass du
* vollständigen Code postest.
  Und zwar auch die Funktionen, die du bisher nicht gezeigt hast.
  Also ADC_init, spi_init und all die anderen
* in deine main() eine Hauptschleife einbaust, in der sich die
  Programmausführung dann fängt.
OP #2064852
Lesenswert?

Guten Abend zusammen,
melde mich leider etwas später als geplant aber ich hab den Fehler 
gefunden!

Zuersteinmal lag es nicht an sei(); , sondern am 
Inititialisierungsaufruf für meinen ADC, also meine Mainfunktion 
"adc_init();) !

Dieser sieht wie folgt aus (ADC_config.h):
1
void adc_init(void)
2
{
3
  
4
  ADMUX =   (1<<REFS0);  // 5V externe Rev
5

6
  ADCSRA =  (1<<ADPS1) | 1<<ADPS2  // Frequenzteiler von 64
7
      | (1<<ADIE)       // Interrupt am Ende der Wandlung aktivieren
8
      | (1<<ADEN)        // ADC einschalten
9
      | (1<<ADSC);      // 1 Wandlung zur Init durchführen
10
  
11
}

Ich Hirsch führe am Schluss der Initialisierung eine einzige Wandlung 
des ADC0 durch, aber im Hauptprogramm warte ich nicht auf das Ende der 
Wandlung (Interruptflag bei Beendigung der Wandlung)!!

Das sieht man sehr schön hier bei den Initalisierungen (main.c):
1
int main()
2
{
3
  stderr = &lcd_str;    // Übergabe für LCD Ausgabe
4

5

6

7
  // ------------------ Initialisierungen -------------------------
8

9
  SPI_Init();        // SPI initialisieren
10
  display_init();      // LCD initialisieren
11
  set_contrast (11);    // LCD Kontrast setzen
12

13
  adc_init();        // ADC initialisieren
14

15

16
  
17
  // ------------------------- I/O Funktionen ---------------------
18

19
  DDRD |=0x20;
20

21

22
  //-------------------- Interruptparameter ------------------------
23

24

25
  TCCR0 |= (1<<CS02) | (1<<CS00) | (1<<WGM01);  // Mode: Normal, Timer 0 Takt: CLK / 64, CTS-Mode
26
  OCR0  = 125;                  // Compare Register mit Wert für Interrupt bei 10ms blegen - 0% Fehler  
27
  TIMSK |= (1<<OCIE0);              // Timer 0 Output Compare Interrupt freigeben
28
  sei();                      // globale Interruptfreigabe
29

30

31
    
32
    // ------------------------ Startbildschirm --------------- 
33
        
34
      
35
      PORTD |= LCD_backLightOn;        // Hintergrundbeleuchtung ein
36
      
37
      clear_home();
38
      set_cursor(0);
39
      set_doubleheight (0);
40
      fprintf(stderr, "LDO V_Regulator");
41
      set_cursor(16);
42
      fprintf(stderr, "Version 1.0");
43

44
    
45
    while(LCD_startTime < 188) { }      // Wartezeit 3s für Startbildschirm
46
    
47
   
48

49
    // --------------------- Temperaturmessung -----------------


Nehme ich die "Initialisierungswandlung" raus bzw. warte ich auf das 
entsprechende Flag nach der adc_init(); geht das ganze wunderbar! Was 
mir allerdings noch unklar ist, ist der Effekt, dass wenn ich sei(); an 
eine andere Position im Programmcode verschoben habe, das ganz bis 
dorthin "funktioniert" hat. Wohl ein unglückliches Zusammenspiel....... 
!?

Ich danke allen, die sich bei dem Thema beteiligt haben und wie 
Karl-Heinz gesagt hat: besser mal den ganzen Code posten! :)

Viele Grüße
Stefan

p.s die Codekommentare müssen noch angepasst werden, stimmen gerade 
nicht ganz zu den Registerzuständen....
#2064864
Lesenswert?

Stefan S. schrieb:

> Dieser sieht wie folgt aus (ADC_config.h):
>
> [c]
> void adc_init(void)
> {
>
>   ADMUX =   (1<<REFS0);  // 5V externe Rev
>
>   ADCSRA =  (1<<ADPS1) | 1<<ADPS2  // Frequenzteiler von 64
>       | (1<<ADIE)       // Interrupt am Ende der Wandlung aktivieren

Man gibt NIEMALS einen Interrupt frei, für den man keine ISR hat.
NIEMALS!

Denn sobald mit einem sei die Interrupt Behandlung global freigegeben 
wird, wird die ISR aufgerufen. Ob du sie selber implementiert hast oder 
nicht.
Nur: für den Fall, dass du sie nicht selber implementiert hast, kommt 
die gcc-Default Routine zum Einsatz, welche macht: den Prozessor 
resetten.


> entsprechende Flag nach der adc_init(); geht das ganze wunderbar! Was
> mir allerdings noch unklar ist, ist der Effekt, dass wenn ich sei(); an
> eine andere Position im Programmcode verschoben habe, das ganz bis
> dorthin "funktioniert" hat. Wohl ein unglückliches Zusammenspiel.......
> !?

Nicht wirklich.
Ist alles erklärbar.

Und genau aus dem Grund hatte ich auch nach komplettem Code gefragt. Ich 
wollte den durchsuchen ob du irgendwo einen Interrupt freigibst für den 
du keine ISR hast. Denn wenn nach einem sei() seltsame Dinge passieren, 
hat man mit der Hypothese einer fehlenden ISR eine 95% 
Trefferwahrscheinlichkeit.
OP #2065100
Lesenswert?

Hallo Karl-Heinz,
mir ist inzwischen schon klar was du meinst. Ich war gedanklich einfach 
auf der falschen Baustelle, nämlich ausschließlich bei den Timern in 
Verbindung mit der globalen Interruptfreigabe, unterwegs und hab die 
Geschichte mit der ADC Initialisierung vollkommen vergessen.

Wie gesagt, danke nochmals an alle, die sich der Sache angenommen haben 
:) Hoffe, es wird weiterhin keine solchen blöden Fehler geben ;)

Gruß
Stefan

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren