Forum: Mikrocontroller und Digitale Elektronik Interrupt führt zum Absturz


von Julian (Gast)


Lesenswert?

Moin,
Ich nutze einen ATMEGA1284P. Es hängt an PB3 eine LED gegen Ground. Über 
I2C ist ein LCD angeschlossen. Ich nutze folgenden Code:
1
    #ifndef F_CPU
2
    #define F_CPU 8000000UL
3
    #endif
4
    
5
    #include <util/delay.h>
6
    #include <avr/io.h>
7
    #include <avr/interrupt.h>
8
    #include "lcd-routines.h"
9
     
10
    volatile int zaehler = 0;
11
    
12
    int main(void)
13
    {
14
      DDRB |= (1<<PB3); // Port OC1A mit angeschlossener LED als Ausgang
15
      
16
      PORTB |=1<<PB3;
17
      _delay_ms(1000);
18
      PORTB &=~(1<<PB3);
19
      _delay_ms(1000);
20
    
21
    
22
      lcd_init();  
23
    
24
      //PWM Signal
25
      TCCR0A = (1 << COM0A1) | (1 << WGM10);
26
      TCCR0B = (1 << CS01) | (1 << CS00);
27
      OCR0A = 25; // Duty cycle 50% (Anm. ob 128 oder 127 bitte prüfen)
28
      TIMSK0 |= 1 << OCIE0A;
29
      
30
      //1s-Schleife
31
      TCCR1A = (1 << WGM21);
32
      TCCR1B = (1 << CS01) | (1 << CS00); // 1/1024
33
      OCR1A = 255;
34
      TIMSK1 |= (1<<OCIE1A);                //Enable Compare Interrupt
35
    
36
      //Interrupt Steuerung
37
      DDRD &= ~(1 << PD2);  //EIngang
38
      PORTD &= ~(1 << PD2);  //Pull-Up sicher ausschalten
39
      EIMSK |= 1 << INT0;
40
      
41
      sei();
42
      
43
      while(1);
44
      return 0;
45
    }
46
    
47
    ISR (TIMER1_COMPA_vect)
48
    {
49
      zaehler++;
50
      if(zaehler < 10)
51
        {
52
          char name[16];
53
          sprintf(name, "%d", zaehler);
54
          lcd_string(name);
55
      }
56
    }
57
    
58
    ISR (INT0_vect) 
59
    {
60
    }
Zu Beginn blinkt die LED wie erwünscht einmal. Danach wird sie per PWM 
gedimmt und leuchtet entsprechend dunkler. Darum kümmert sich der Timer 
0. Zusätzlich habe ich nun einen zweiten Timer (1) laufen, der mit die 
ISR aufruft, die das LCD updatet. Das Ganze ist soweit nur ein 
Testaufbau und so wenig sinnvoll.
Das Problem ist nun, dass auf dem LCD bis 9 hochgezählt wird und sich 
der uC anschließend neuzustarten scheint. Um präzise zu sein: Das LCD 
wird kurz nicht aktualisiert und fängt anschließend von vorne an zu 
zählen. Erhöht man die Grenze in der If-Bedingung wird entsprechend 
gezählt. Das interessante dabei ist, dass die LED durchgehend weiter 
gedimmt leuchtet und nicht die 2s Blinkung macht! Lasse ich das Display 
zum Start einen Begrüßungstext anzeigen, wird dieser allerdings anzeigt. 
Drehe ich die If-Bedingung um, also "zaehler > 10" zeigt das LCD nie was 
an.

Es wirkt so, als wäre die ISR zu schnell (?), sodass der uC sich dabei 
neustartet, wenn es nicht weiter zu tun gibt, als den zaehler 
hochzuzählen. Schalte ich die Interrupts vor der Erhöhung ab und danach 
wieder an, ändert sich nichts.

Kann es sein, dass ich durch die Initialisierung der Timer einen 
weiteren Interrupt initialisiert habe, der auslöst und keine 
entsprechende ISR findet und dadurch abstürzt?

Bin aktuell ratlos, wo genau der Fehler liegt :/

Schöne Grüße
Julian

: Bearbeitet durch User
von Lorem Ipsum (Gast)


Lesenswert?

LCD-Routinen, print, etc. in einer ISR sind schon sehr kriminell. Löse 
das erstmal anders.

von tip (Gast)


Lesenswert?

Julian schrieb:
> Kann es sein, dass ich durch die Initialisierung der Timer einen
> weiteren Interrupt initialisiert habe, der auslöst und keine
> entsprechende ISR findet und dadurch abstürzt?

Ja, hast du!

von Thomas E. (thomase)


Lesenswert?

Julian schrieb:
> Bin aktuell ratlos, wo genau der Fehler liegt

> TIMSK0 |= 1 << OCIE0A;
> TIMSK1 |= (1<<OCIE1A);
> EIMSK |= 1 << INT0;

> ISR (TIMER1_COMPA_vect)
> ISR (INT0_vect)

Fällt dir was auf?

mfg.

von Julian (Gast)


Lesenswert?

Lorem Ipsum schrieb:
> LCD-Routinen, print, etc. in einer ISR sind schon sehr kriminell.
> Löse
> das erstmal anders.

Wie gesagt, dass ist ein sehr grobes Testprogramm. Der LCD Kram in der 
ISR ist in der Tat unschön, aber hier ja auch gar nicht das Problem. Das 
Ding ist ja gerade, dass die ISR OHNE dem LCD Arbeiten nicht mehr 
arbeitet! Nämlich sobald die If-Bedingung nicht mehr zutrifft stürzt der 
uC ab.

von S. Landolt (Gast)


Lesenswert?

> TIMSK0 |= 1 << OCIE0A;
Welchem Zweck dient dies, und wo ist die dazugehörende ISR?

von Thomas E. (thomase)


Lesenswert?

Julian schrieb:
> Nämlich sobald die If-Bedingung nicht mehr zutrifft stürzt der
> uC ab.

Dann ist er fertig und will den nächsten Interrupt ausführen, für den es 
keine ISR gibt.

mfg.

von holger (Gast)


Lesenswert?

>Kann es sein, dass ich durch die Initialisierung der Timer einen
>weiteren Interrupt initialisiert habe, der auslöst und keine
>entsprechende ISR findet und dadurch abstürzt?

Ja, bei Timer0:

     TIMSK0 |= 1 << OCIE0A;

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ja, du solltest dich, wenn du
1
TIMSK0 |= 1 << OCIE0A;
benutzt, dazu entschliessen, diesen Interrupt auch zu behandeln :-)

von Julian (Gast)


Lesenswert?

Thomas E. schrieb:
> Julian schrieb:
>> Bin aktuell ratlos, wo genau der Fehler liegt
>
>> TIMSK0 |= 1 << OCIE0A;
>> TIMSK1 |= (1<<OCIE1A);
>> EIMSK |= 1 << INT0;
>
>> ISR (TIMER1_COMPA_vect)
>> ISR (INT0_vect)
>
> Fällt dir was auf?
>
> mfg.

-.- Durchaus... Danke für den Tipp! Ist korrigiert und arbeitet nun brav 
:)

von Tom E. (Gast)


Lesenswert?

Julian schrieb:
> #ifndef F_CPU
> #define F_CPU 8000000UL
> #endif

Mutig, mutig.
Wenn du F_CPU nicht explizit angibst, suchst du dich tot nach dem 
Fehler, falls der µC nicht zufällig mit 8MHz läuft.

Still und heimlich irgendeine Quarzfrequenz anzunehmen, ist keine gute 
Idee.

von Julian (Gast)


Lesenswert?

Tom E. schrieb:
> Julian schrieb:
>> #ifndef F_CPU
>> #define F_CPU 8000000UL
>> #endif
>
> Mutig, mutig.
> Wenn du F_CPU nicht explizit angibst, suchst du dich tot nach dem
> Fehler, falls der µC nicht zufällig mit 8MHz läuft.
>
> Still und heimlich irgendeine Quarzfrequenz anzunehmen, ist keine gute
> Idee.

Was genau meinst du damit? Theoretisch läuft doch der Controller mit ca. 
8Mhz. Wo liegt dann das Problem, dass so zu definieren?

von Tom E. (Gast)


Lesenswert?

Julian schrieb:
> Theoretisch läuft doch der Controller mit ca. 8Mhz.

Wer sagt das? Das hängt doch ganz von der Bestückung deiner jeweiligen 
Schaltung und der Einstellung der Fuses ab.

von Julian (Gast)


Lesenswert?

Das sagen die Fuses und das Datenblatt. Ich nutze den internen Oszilator 
und der läuft meines Wissens mit 8Mhz. Ich verstehe das Problem nicht?

Sollte ich die Frequenz gar nicht angeben?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Julian schrieb:
> Sollte ich die Frequenz gar nicht angeben?

Doch. Aber wenn du einen Mega1284 im Auslieferzustand anwendest, wird er 
mit dem internen 8MHz Oszillator und gesetzter CKDIV8 Fuse betrieben - 
resultierend in 1Mhz CPU Takt.

von c-hater (Gast)


Lesenswert?

Julian schrieb:

> Theoretisch läuft doch der Controller mit ca.
> 8Mhz.

Allein diese Aussage zeigt, dass du keinerlei Ahnung hast, weil du 
niemals ein Datenblatt gelesen hast.

Tatsächlich werden die allermeisten AVR8 (>99.9% des 
Gesamtlieferumfangs) mit einem effektiven Takt von ca. *1*MHz 
ausgeliefert.

Je nach Generation und Device kann es sich unterscheiden, wie diese ca. 
1MHz erreicht werden, und es gibt auch einige (sehr wenige) Ausreißer im 
AVR8-Produktspektrum von Atmel, die sehr stark von dieser Regel 
abweichen, aber der Normalfall ist und bleibt ~*1*MHz, nicht 8MHz.

von Karl H. (kbuchegg)


Lesenswert?

Julian schrieb:
> Das sagen die Fuses und das Datenblatt. Ich nutze den internen Oszilator
> und der läuft meines Wissens mit 8Mhz. Ich verstehe das Problem nicht?
>
> Sollte ich die Frequenz gar nicht angeben?

Doch sollst du.

Aber du sollst deinen Code nicht so schreiben, dass irgendwelcher Code 
(und sei es die UART oder die LCD Routinen) einfach ein F_CPU annehmen 
dürfen, wenn keine Vorgabe (zb im makefile oder in den Projekt Optionen) 
getroffen wurden.

Sorge dafür, dass es dir nicht passieren kann, das verschiedene Teile 
deines Programm mit verschiedenen Werten operieren! Und zwar sorge so 
dafür, dass es bei der kleinsten Unregelmässigkeit (und sei es nur weil 
du in irgendeinem File auf F_CPU vergessen hast) sofort zu einem Fehler 
kommt! Du willst dir keinen Fehler dadurch verschleieren, dass du dann 
stillschweigende Annahmen triffst! Das sieht auf den ersten Blick zwar 
wie 'Komfort' aus, ist aber auf lange Sicht ein ständiges Ärgernis. Wenn 
etwas faul ist, dann willst du, dass der COmpiler dir dafür auf die 
Fingern klopft. Und nicht, dass er sich etwas aus den Fingern saugt.

F_CPU willst du am besten nur an einer einzigen Stelle festlegen: 
nämlich im makefile oder in den Project-Options wenn du eine IDE 
benutzt.
Und du willst, dass solltest du das vergessen, das in einem Fehler 
mündet.

: Bearbeitet durch User
von Julian (Gast)


Lesenswert?

Prinzipiell korrekt, allerdings habe ich die Fuse gelöscht ;)

Aber dazu mal ne Frage. Mit der Definition von 8Mhz ist _delay_ms 
relativ genau. Nun wollte ich sekündlich ein Interrupt haben. Dazu habe 
ich folgenden Timer:

    TCCR1A = (1 << WGM21);
    TCCR1B = (1 << CS01) | (1 << CS00); // 1/64
    OCR1A = 255;
    TIMSK1 |= (1<<OCIE1A);

Müsste rein rechnerisch ~488Hz bringen. Allerdings bekomme ich ziemlich 
genau die Hälfte! Woran liegt das?

"Gemessen mit":

    ISR (TIMER1_COMPA_vect)
    {
  zaehler++;
  if(zaehler > 243)
  {
            //Sekunde um
  }
    }

Habe ich mal 10 Minuten laufen lassen und war damit ca. 6 Sekunden 
daneben. (Ist jetzt nicht gut, aber ja bei weitem nicht die errechneten 
488Hz)

von Julian (Gast)


Lesenswert?

Karl H. schrieb:
> [...] F_CPU willst du am besten nur an einer einzigen Stelle festlegen:
> nämlich im makefile oder in den Project-Options wenn du eine IDE
> benutzt.
> Und du willst, dass solltest du das vergessen, das in einem Fehler
> mündet.

Ergibt Sinn. Habe ich entsprechend geändert. Ist es denn "schlechter" 
Stil, wenn man Projekte antrifft, bei denen das im Code angegeben ist 
oder gibt es dafür dann einen anderen Grund?

von Karl H. (kbuchegg)


Lesenswert?

Julian schrieb:

> Stil, wenn man Projekte antrifft, bei denen das im Code angegeben ist
> oder gibt es dafür dann einen anderen Grund?

historisch gewachsen.

Solange man nur ein einziges C-File hat, ist das ja auch kein Problem.
Sobald man aber anfängt, sich eine Sammlung von immer wiederkehrenden 
FUnktionalitäten zu organisieren, landet man unweigerlich irgendwann an 
dem Punkt, an dem zb die UART Funktionen immer noch mit einem F_CPU von 
zb 16Mhz operieren, nur weil man 2 Jahre zuvor mal so einen Prozessor 
hatte, obwohl für das gerade anstehende Projekt 4Mhz richtig gewesen 
wären.
Und nach dem Fehler, warum seit Jahren bewährte Funktionen plötzlich 
nicht mehr richtig funktionieren suchst du dir einen Wolf. Denn eben 
weil sie schon so lange sauber funktionieren, rechnest du nicht damit, 
dass sich dort eine Zeitbombe versteckt hat.

von Karl H. (kbuchegg)


Lesenswert?

Julian schrieb:

> Müsste rein rechnerisch ~488Hz bringen.

488,28125 um genau zu sein

8000000/64/256

> Allerdings bekomme ich ziemlich
> genau die Hälfte! Woran liegt das?

Wieso
1
 TCCR1A = (1 << WGM21);

das schaltet in den Modus Phase COrrect 9 Bit PWM, mit einem Top Wert 
von 0x1FF. Und das ist fast das doppelte der gewünschten 0xFF

von Karl H. (kbuchegg)


Lesenswert?

Karl H. schrieb:

> Wieso
>
1
>  TCCR1A = (1 << WGM21);
2
>
>
> das schaltet in den Modus Phase COrrect 9 Bit PWM, mit einem Top Wert
> von 0x1FF. Und das ist fast das doppelte der gewünschten 0xFF

Du wolltest eigentlich das Bit WGM12 in TCCR1B für den CTC Modus setzen.

von c-hater (Gast)


Lesenswert?

Julian schrieb:

> Ist es denn "schlechter"
> Stil, wenn man Projekte antrifft, bei denen das im Code angegeben ist

Bis auf wenige Ausnahmen: Ja.

> oder gibt es dafür dann einen anderen Grund?

Das sind halt die Ausnahmen von der Regel. Stell dir z.B. ein System 
vor, welches vom internen RC-Osziallator befeuert wird. Dieser wird aber 
wiederum durch Code via OSCCAL-Register kontrolliert und letztlich z.B. 
anhand einer asynchronen RTC-Referenz eingestellt.

Das ist echt heavy, denn hier gibt es de facto überhaupt nix, auf was 
man sich jederzeit verlassen könnte.

Allein dieses Beispiel zeigt, wie schwachsinnig u.U. starre Regeln 
sind...

Aber für den Normalfall gilt: es gibt nur eine Taktdomain und deren Takt 
sollte an genau einer Stelle deklariert werden.

von Thomas E. (thomase)


Lesenswert?

Julian schrieb:
> Habe ich entsprechend geändert.

Und am Anfang jeder Datei schreibst du:
1
#ifndef F_CPU
2
  #error F_CPU not defined.
3
#endif

Dann kann gar nichts mehr schief gehen.

mfg.

von Julian (Gast)


Lesenswert?

Karl H. schrieb:
> Du wolltest eigentlich das Bit WGM12 in TCCR1B für den CTC Modus setzen.

Karl H. schrieb:
> das schaltet in den Modus Phase COrrect 9 Bit PWM, mit einem Top Wert
> von 0x1FF. Und das ist fast das doppelte der gewünschten 0xFF


In der Tat... Im Datenblatt in den 8-Bit-Timer gerutscht. Nun 
funktioniert es wie gewollt :)

Danke für eure Hilfe!

@c-hater
Klingt interessant, aber da bleibe ich doch erstmal bei einem Takt :D 
Der stiftet (wie man sieht) ja auch schon gerne Verwirrung.

und @Thomas E.
Ist eingefügt ;) danke für den Tipp!

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.