Forum: Compiler & IDEs Atmega8 löst Timerinterrupt nicht aus


von Lorenz H. (lolo)


Lesenswert?

Hi,

ich habe die vergangenen Stunden damit verbracht, im Internet und diesem 
Forum nach einer Lösung für dieses Problem zu suchen:
Ich habe ein myAVR-Board v1.52 mit einem ATMega8. An Port D hängt eine 
LED. Folgendes Programm sollte eigentlich alle 710ms die LED an bzw 
ausschalten. Es tut sich aber nichts, die LED brennt dauernd. Woran kann 
das liegen?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#define F_CPU 3686400 
4
#include <util/delay.h>
5
volatile uint8_t counter;
6
int main() {
7
    DDRD = 0xFF;
8
    TIMSK |= (1 << TOIE0);            // Timer Overflow Interrupt freischalten  
9
    TCCR0 |= (1 << CS02) | (1<<CS00);  // Vorteiler 1024 -> Timerfrequenz 3,6 kHz -> 71ms Überlaufperiode
10
// Interrupts freigeben
11
    sei();
12
// Endlose Hauptschleife
13
    PORTD = 0xFF;
14
    while(1) {
15
        if (counter >= 10) { // Neuer Timerzyklus ?
16
            counter = 0;
17
 
18
            // hier steht jetzt in Normalfall ein grosser Programmblock ;-) 
19
            PORTD ^= 0xff;    // LED toggeln
20
        }
21
    }
22
}
23
// Timer0 overflow Interrupt
24
// hier wird der Hauptschleife ein neuer Timerinterrupt signalisiert
25
ISR(TIMER0_OVF_vect) {
26
    counter += 1;
27
}
Ich kompiliere den Code mit
1
avr-gcc -Os -mmcu=atmega8 timer3.c
 (Compilerversion ist 4.3.0) und brenne ihn mit
1
avrdude -p m8 -c sp12 -U flash:w:a.out:r -v -E noreset,vcc

Danke,
Lorenz

von Wolfgang Horn (Gast)


Lesenswert?

Hi, Lorenz,

Im Programmkode erkenne ich keinen Fehler.

Anamnese:
1. Läuft der Oszillator?
2. Verhält sich ein anderer Chip genauso?
3. Stromlauf ohne Fehler?

Ciao
Wolfgang Horn

von Lorenz H. (lolo)


Lesenswert?

Hi,
der Oszillator läuft. Folgender Code lässt die LED - wie erwartet - 
schnell blinken:
1
#include <avr/io.h>
2
#define F_CPU 3686400 
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
volatile uint8_t counter;
6
int main() {
7
    DDRD = 0xFF;
8
    TIMSK |= (1 << TOIE0);            // Timer Overflow Interrupt freischalten  
9
    TCCR0 |= (1 << CS02) | (1<<CS00);  // Vorteiler 1024 -> Timerfrequenz 3,6 kHz -> 71ms Überlaufperiode
10
// Interrupts freigeben
11
//    sei();
12
    PORTD = 0xFF;
13
    while(1) {
14
  if (TIFR & (1 << TOV0)) {  //Wenn Overflow-Bit gesetzt
15
            PORTD ^= 0xff;    // LED toggeln
16
      TIFR |= (1 << TOV0);  // Overflow-Bit löschen -> Auf logisch 1 Stellen
17
  }
18
    }
19
}
Sobald ich aber sei() nicht mehr auskommentiert habe, bleibt die LED an. 
Verhängt sich mein ATMega8 auf der Suche nach einer Interruptfunktion?

Ich habe nur diesen eine Controller, evtl tut mir jemand, der auch im 
Besitz eines ATMega8 ist, den Gefallen, das mal auszuprobieren?

Was du mit Stromfluss meinst, kann ich nicht nachvollziehen. Aber ein 
allzu schwerer Fehler kann nicht vorliegen, denn alle meine Programme, 
die keine Interrupts verwenden, laufen anstandslos. Oder?

Danke für die Mühe,
Lorenz

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Lorenz Hopfmüller wrote:

> Sobald ich aber sei() nicht mehr auskommentiert habe, bleibt die LED an.
> Verhängt sich mein ATMega8 auf der Suche nach einer Interruptfunktion?

Richtige Vermutung. Die ISR für den Timer0 Overflow fehlt.
1
#include <avr/io.h>
2
#define F_CPU 3686400 
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
volatile uint8_t counter;
7
8
int main(void) 
9
{
10
    DDRD = 0xFF;
11
12
    // Timer Overflow Interrupt freischalten  
13
    TIMSK |= (1 << TOIE0);  
14
    // Vorteiler 1024 -> Timerfrequenz 3,6 kHz -> 71ms Überlaufperiode
15
    TCCR0 |= (1 << CS02) | (1<<CS00);  
16
    // Interrupts freigeben
17
    sei();
18
19
    PORTD = 0xFF;
20
    
21
    while(1) 
22
    {
23
      // für immer...
24
    }
25
}
26
27
ISR(TIMER0_OVF_vect)
28
{
29
    PORTD ^= 0xff;    // LED toggeln
30
}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Mit dem counter funktioniert das im Simulator:
1
#include <avr/io.h>
2
#define F_CPU 3686400 
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
volatile uint8_t counter;
7
8
int main(void) 
9
{
10
    DDRD = 0xFF;
11
12
    // Timer Overflow Interrupt freischalten  
13
    TIMSK |= (1 << TOIE0);  
14
    // Vorteiler 1024 -> Timerfrequenz 3,6 kHz -> 71ms Überlaufperiode
15
    TCCR0 |= (1 << CS02) | (1<<CS00);  
16
    // Interrupts freigeben
17
    sei();
18
19
    PORTD = 0xFF;
20
    
21
    while(1) 
22
    {
23
        if (counter >= 10)
24
        {
25
            counter = 0;
26
            PORTD ^= 0xff;    // LED toggeln
27
        }
28
    }
29
}
30
31
ISR(TIMER0_OVF_vect)
32
{
33
    counter++;
34
}

Testen könnte ich es auf einem Atmega8, allerdings mit 12 MHz. Hilft dir 
das?

von Lorenz H. (lolo)


Angehängte Dateien:

Lesenswert?

Hi,

dein Code bringt leider auch nichts, die LED bleibt in beiden Fällen an.
Ich habe aber heute Morgen nochmal das Datenblatt gewälzt, und bin dabei 
auf folgendes gestoßen:

Mit meinen Fuse-Bits, die noch auf Werkseinstellung sind, ergibt sich 
laut dem Datenblatt 
http://atmel.com/dyn/resources/prod_documents/doc2486.pdf , Tabelle 19, 
für die Interrupt-Vektor-Startadresse 0x001. Ich habe folgendes 
Minimalbeispiel mit "avr-gcc -mmcu=atmega8 miniinterrupt.c" kompiliert 
und den Disassembler avrdisas 
(http://www.johannes-bauer.com/avruc/index.php) drüberlaufen lassen. Der 
Output ist angehängt, der Code kommt hier:
1
#include <avr/interrupt.h>
2
int main() { }
3
ISR(INT0_vect) { }
4
ISR(INT1_vect) { }
Die Interrupt-Vektoren, zu erkennen an der langen Reihe von rjmps, von 
denen der 2. und der 3., also INT0 und INT1 (-> Datenblatt S.47) auf 
"eigene" Funktionen zeigen, sind nicht etwa an 0x01, sondern an 0x54! 
Kein Wunder, dass der ATMega8 beim Sprung dahin, wo er die Vektoren 
vermutet, in einen undefinierten Zustand übergeht.
Ich habe das natürlich auch mit "vernünftigen" Code ausprobiert, mit dem 
Beispiel aus meinem ersten Posting sind die Vektoren an 0x74.

Entweder habe also ich etwas mit den Interrupt-Vektoren-Adressen 
gründlich missverstanden, oder mein avr-gcc schert sich keinen Deut 
darum, dass diese Vektoren an einer bestimmten Adresse stehen müssen, 
und nicht einfach irgendwo im Speicher. Wie kann ich dem gcc in dieser 
Hinsicht Manieren beibringen? Ist vielleicht meine avr-libc-Version eine 
verkorkste?

Ich wäre dir, Stefan, sehr dankbar, wenn du das mal auf deinem ATMega8 
testen könntest. Toll wäre es auch, wenn du die Binary hier mal posten 
könntest, dann kann ich das mal mit einer anderen gcc-Version testen.

Danke für die Mühe,
Lorenz

von Stefan B. (stefan) Benutzerseite


Angehängte Dateien:

Lesenswert?

Lorenz Hopfmüller wrote:

> Ich wäre dir, Stefan, sehr dankbar, wenn du das mal auf deinem ATMega8
> testen könntest. Toll wäre es auch, wenn du die Binary hier mal posten
> könntest, dann kann ich das mal mit einer anderen gcc-Version testen.

Binary zu diesem Quellcode im Anhang:
(AVR-Studio 4.12 SP2, WinAVR 20071221)

Ich habe nur eine LED an PD5 auf dem [[Pollin 
Funk-AVR-Evaluationsboard]] zur Anzeige benutzt.
1
// MCU = atmega8
2
// F_CPU 1000000 in AVR Studio oder Makefile eingestellt
3
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
7
volatile uint8_t counter;
8
9
int main(void) 
10
{
11
    DDRD = (1<<PD5);
12
13
    // Timer Overflow Interrupt freischalten  
14
    TIMSK |= (1 << TOIE0);  
15
    // Vorteiler 1024 -> Timerfrequenz 3,6 kHz -> 71ms Überlaufperiode
16
    TCCR0 |= (1 << CS02) | (1<<CS00);  
17
    // Interrupts freigeben
18
    sei();
19
20
    PORTD = (1<<PD5);
21
    
22
    while(1) 
23
    {
24
        if (counter >= 10)
25
        {
26
            counter = 0;
27
            PORTD ^= (1<<PD5);    // LED toggeln
28
        }
29
    }
30
}
31
32
ISR(TIMER0_OVF_vect)
33
{
34
    counter++;
35
}

Die LED blinkt fröhlich vor sich hin. Etwas hektisch, da das Board 12 
MHz hat. Ändere ich den Vergleich im if auf >= 10*12, ist es ein 
schönes, langsames Blinken Pi*Daumen im 1s/1s Takt.

von Stefan E. (sternst)


Lesenswert?

@ Lorenz Hopfmüller:

Das a.out ist kein reines BIN-File (das müsste man daraus erst noch 
extrahieren). Sieht so aus, also würde dein Disassembler damit nicht 
klar kommen. Er versucht wohl, die Zusatzinhalte in der Datei ebenfalls 
zu disassemblieren.

von Lorenz H. (lolo)


Lesenswert?

Hi,

@Stefan B.: Blinkt auch bei mir, danke!
@Stefan Ernst: Uups, das hatte ich nicht gewusst. Ich hatte bisher immer 
die a.out auf den ATMega8 gebrannt, kein Wunder, dass das nicht ging. 
Ein wenig peinlich...
Ich kompiliere jetzt mit
1
avr-gcc -Os -mmcu=atmega8 $1.c
2
avr-objcopy -O ihex a.out a.hex
 und brenne mit
1
avrdude -p m8 -c sp12 -U flash:w:a.hex -v -E noreset,vcc

Danke für die Fehlersuche, das Problem befand sich etwa 40cm vom 
Bildschirm entfernt. Was mich noch wundert: Wieso funktionierten alle 
Programme, die keine Interrupts benutzen?

Danke,
Lorenz

von Stefan E. (sternst)


Lesenswert?

Lorenz Hopfmüller wrote:

> Was mich noch wundert: Wieso funktionierten alle
> Programme, die keine Interrupts benutzen?

Zufall. Der Prozessor "wurschtelt" sich anscheinend irgendwie durch den 
Müll am Anfang (was macht ein AVR eigentlich bei einem ungültigen 
Opcode?). Der eigentliche Code enthält nur relative Sprünge, so dass es 
unproblematisch weitergeht, wenn er diesen erstmal erreicht hat. 
Initialisierte globale Variablen gibt es auch nicht (die würden nämlich 
mit falschen Werten initialisiert werden). Stört also eigentlich "nur", 
dass die Interruptvektortabelle nicht an der richtigen Stelle sitzt. ;-)

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.