Forum: Mikrocontroller und Digitale Elektronik sei() verhindert Programmausführung


von Stefan S. (novafly)


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

von Lehrmann M. (ubimbo)


Lesenswert?

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

Ja. Die Endlosschleife fehlt ... Der AVR fährt ja ständig in den Reset 
(sobald das Hauptprogramm einmal durch ist). Trivialste Grundlage. 
Empfehlung: AVR-Tutorial machen!

Viel Erfolg

von Charly B. (charly)


Lesenswert?

i hab von C keine Ahnung (echte Programmierer meiden C :) )
aber laeuft es mit einer leeren Interruptroutine ?
was ist mit dem Stackpointer ???

vlG & viel erfolg
Charly

von Stefan S. (novafly)


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

von Stefan S. (novafly)


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

von Thomas K. (muetze1)


Lesenswert?

Hast du die Variablen mit volatile gekennzeichnet? Ansonsten optimiert 
er dir deine while Schleife auf eine Endlosschleife, da sich die 
Variablen nicht innerhalb der While ändern können...

von MitLeser (Gast)


Lesenswert?

das
TCCR0 |=   (1<<CS00) | (1<<CS01);  // Mode: Normal, Timer 0 Takt: CLK / 
64
reicht nicht.
Es muss auch der Timer auf CTC-Mode eingestellt werden. siehe Datasheet!

von Stefan S. (novafly)


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

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Kann es sein, dass der Overflow-Interrupt ausgelöst wird, wenn der Timer 
Compare auslöst? Das würde ich mal genau im Datenblatt nachlesen. Wenn 
für den Overlow-Interrupt kein gültiger Handler definiert ist, wird ein 
Reset ausgelöst.

Grüße,

Peter

von (prx) A. K. (prx)


Lesenswert?

Wird noch irgendein anderer Interrupt eingeschaltet? Wenn der bei sei() 
aufgibt, dann wird möglicherweise ein Interrupt ausgelöst, für den es 
keinen Handler gibt.

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Bleibt die Programmausführung eigentlich bei sei() stehen, oder macht 
der Controller einen Reset?

Edit zu meinem vorherigen Post: Der Overflow-Interrupt ist ja nicht 
sichtbar bzw. absichtlich eingeschaltet.

von sven (Gast)


Lesenswert?

Ich würde keine LCD-Operation in der ISR ausführen!
Evtl. gibt es dort auch Delayschleifen welche Programausführung 
stören...

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Der Timer muss auf CTC gestellt werden, wie MitLeser bereits geschrieben 
hat. Nur dann ist OCR0 der TOP-Wert.
Also
TCCR0 |= (1<<CS00) | (1<<CS01) | (1<<WGM01);

Das würde aber nur ein falsches Timing erklären, nicht den genannten 
Effekt.
Ist der richtige Prozessor definiert?

Grüße,

Peter

von Peter D. (pdiener) Benutzerseite


Lesenswert?

>Ich würde keine LCD-Operation in der ISR ausführen!
>Evtl. gibt es dort auch Delayschleifen welche Programausführung
>stören...

ISR(TIMER0_COMP_vect)
{
  sysTime++;
  LCD_refreshTime++;
  LCD_startTime++;
}

@sven: Welche LCD-Operationen denn? Das sind nur drei normale Variablen.

von sven (Gast)


Lesenswert?

Oh, sorry,
beim Überfliegen sah ich 'LCD_refr...' und 'LCD_star...' und nahm an 
dass es Funktionen sind.

von Stefan S. (novafly)


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

von Charly B. (charly)


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

von Karl H. (kbuchegg)


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.

von Stefan S. (novafly)


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

von Karl H. (kbuchegg)


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.

von Stefan S. (novafly)


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

von Loonix (Gast)


Lesenswert?

Stefan S. schrieb:
> Zuersteinmal lag es nicht an sei();

Deine Feststellung rundet dies schöne Beispiel für eine fehlgeschlagene 
Thread-Titel-Auswahl ab. Gerade eine der wichtigsten Funktionen der libc 
als Schuldigen auszumachen, tztztz...

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.