Forum: Mikrocontroller und Digitale Elektronik Bootloader und Interrupts


von Guile L. (sphinx)


Lesenswert?

Hallo liebes Forum,
ich schreibe gerade einen Bootloader für meinen Roboter.
Die Software läuft auf einem ATmega2560.

Die Fuses habe ich auf:
- Boot Flash section size: 4096
- Boot Reset vector enabled
gesetzt.

Bei der Ausführung des Bootloaders tritt leider das folgende Problem 
auf:
Über USB (RS232) sende ich ein hex file an den uC, der Bootloader 
dekodiert das hex file und flasht den uC. Anschliessend schaltet der 
Bootloader die Interrupt Vektoren auf die Anwendung und startet die 
Anwendung jmp 0x0000.

Die Interrupt Vektoren werden mit den folgenden Befehlen umgesetzt:
MCUCR  = (1<<IVCE);  // Enable change of Interrupt Vectors
MCUCR  = (0<<IVSEL); // Move interrupts to Application

Der Disassembler im AVR Studio zeigt, dass das Programm korrekt ins 
Flash geschrieben wurde (vergleich des Assemblercodes an willkürlichen 
Stellen).

Es scheint so, als ob immer noch die Interruptvektortabelle des 
Bootloaders verwendet wird und nicht die der Applikation.

Hat jemand vieleicht ein ähnliches Problem oder einen Hinweis was ich 
falsch gemacht haben könnte?

Vielen Dank im voraus!

von Karl H. (kbuchegg)


Lesenswert?

So
1
MCUCR  = (0<<IVSEL); // Move interrupts to Application

kann man nicht gezielt ein Bit löschen.

Eine 0 kannst du beliebige oft nach links schieben, es
bleibt immer noch eine 0.
Deine Schreibweise ist im Grunde nur eine verschleierte
Form für
1
MCUCR = 0;

und das wirst du wohl kaum so haben wollen.

Um 1 Bit gezielt auf 0 zu setzen:
1
MCUCR &= ~( 1 << IVSEL );

von Guile L. (sphinx)


Lesenswert?

Hallo Karl Heinz,
da war ich wohl mit Blindheit geschlagen! Vielen Dank!

Leider startet die Applikation immer noch nicht korrekt. Wie es scheint 
landet der uC immer noch in den Interrupttabellen des Bootloaders obwohl 
die Anwendung schon läuft!!!

Mein Code sieht jetzt so aus:
1
// Auf die Interrupttabelle der Applikation umschalten
2
// ===================================================
3
MCUCR  =  (1<<IVCE); // Enable change of Interrupt Vectors
4
MCUCR &= ~(1<<IVSEL); // Move interrupts to Application
5
6
// Die Applikation anspringen
7
// ==========================
8
((void (*)())0x0000)();

von Jörg X. (Gast)


Lesenswert?

Ich verstehe das Datasheet ("... Within four cycles, write the desired 
value to IVSEL while writing a zero to IVCE. ..." )so:
1
MCUCR  =  (1<<IVCE); // Enable change of Interrupt Vectors
2
MCUCR &= ~((1<<IVSEL)|(1<<IVCE)); // clear both bits
hth. Jörg

ps.: Der Beispielcode verwendet eine Temp-Variable, wahrscheinlich um 
das 4-Takt-Zeitfenster sicher einzuhalten.

von Guile L. (sphinx)


Lesenswert?

Hallo Jörg,
danke für den Hinweis! Ich habe den Code jetzt angepasst.
1
// Vorsichtshalber um 4 Takt Vorgabe einzuhalten
2
unsigned char moveApplication = ~((1<<IVSEL)|(1<<IVCE));
3
    
4
// Enable umschalten
5
MCUCR  =  (1<<IVCE);
6
7
// Interruptvektortabelle umschalten
8
MCUCR &= moveApplication;
9
10
// Applikation anspringen
11
void (*applicationPointer)( void ) = 0x0000;
12
applicationPointer();

Aus einem mir immer noch unerklärlichem Grund landet der uC jedoch immer 
wieder im Bootloader nach dem jmp 0x0000.

Eine Kleinigkeit habe ich wohl noch übersehen.

von Guile L. (sphinx)


Lesenswert?

Also so wie es aussieht, führt der uC kurz nachdem er die Applikation 
anspringt einen erneuten reset durch und landet wieder im Bootloader.
Ist mir im Moment leider unerklärlich. Die Anwendung selbst habe ich 
bereits getestet. Die läuft einwandfrei.

Im assembler sieht das umschalten der Interruptvektortabellen so aus:
1
485:            unsigned char moveApplication = ~((1<<IVSEL)|(1<<IVCE));
2
+0001F425:   EF0C        LDI     R16,0xFC         Load immediate
3
+0001F426:   2E80        MOV     R8,R16           Copy register
4
487:            MCUCR  =  (1<<IVCE); // Enable change of Interrupt Vectors
5
+0001F427:   E001        LDI     R16,0x01         Load immediate
6
+0001F428:   BF05        OUT     0x35,R16         Out to I/O location
7
488:            MCUCR &= moveApplication;  // Move interrupts to Application
8
+0001F429:   B705        IN      R16,0x35         In from I/O location
9
+0001F42A:   2108        AND     R16,R8           Logical AND
10
+0001F42B:   BF05        OUT     0x35,R16         Out to I/O location
11
492:            void (*applicationPointer)( void ) = 0x0000;       // Set up function pointer to RESET vector
12
+0001F42C:   E000        LDI     R16,0x00         Load immediate
13
+0001F42D:   E010        LDI     R17,0x00         Load immediate
14
+0001F42E:   E020        LDI     R18,0x00         Load immediate
15
+0001F42F:   0128        MOVW    R4,R16           Copy register pair
16
+0001F430:   2E62        MOV     R6,R18           Copy register
17
493:            applicationPointer();
18
+0001F431:   01F2        MOVW    R30,R4           Copy register pair
19
+0001F432:   BE6C        OUT     0x3C,R6          Out to I/O location
20
+0001F433:   9519        EICALL                   Extended indirect call to (Z)
21
+0001F434:   CD54        RJMP    PC-0x02AB        Relative jump

Bei der IN Anweisung bin ich mir nicht ganz sicher aber ich denke, die 
benötigt auch nur einen Takt. Daher sollten beim Umschalten die 4 Takte 
eigentlich eingehalten werden.

von TorstenS (Gast)


Lesenswert?

Hallo Jan,

ich habe ein ähnliches Phänomen beobachtet (sowohl mit ATmega168er als 
auch mit ATmega128) und hab's bisher so umgangen, dass ich im Bootloader 
keine Interrupts verwende. Dazu habe ich den Empfang von Zeichen über 
den UART auf Polling umgestellt.
Genauere Untersuchungen zur eigentlichen Ursache habe ich noch nicht 
angestellt.

Tschüss
Torsten

von Guile L. (sphinx)


Lesenswert?

Hallo Torsten,
dann bist Du schon der Zweite, der mir von diesem Phänomen berichtet.
Ich werde dann wohl oder übel auf Polling der RS232 Schnittstelle 
umstellen müssen.

Mittlerweile sitze ich hier schon seit zwei Tagen und versuche dieses 
Problem in den Griff zu bekommen.
Vieleicht weiss noch jemand wie man es umgeht?

Vielen Dank!
Gruss
Jan

von Jörg (Gast)


Lesenswert?

Ich habe einen Bootloader für Mega8 geschrieben der auch Interrupts 
verwendet. Da klappt das Umschalten. Die Interrupts sollten zu dem 
Zeitpunkt natürlich disabled sein, mit cli() ode so.

Von wegen landet wieder im Bootloader: Schlägt da vielleicht aus 
irgendwelchen Gründen der Watchdog zu?

Gibt es noch Fuses, die dabei eine Rolle spielen können?

von Guile L. (sphinx)


Lesenswert?

Hallo Jörg,
die Interrupts habe ich vorher disabled => cli(). Sorry habe wegen der 
Übersichtlichkeit nicht alles gepostet.

Bei den Fuses gibt es eine Option "Watchdog Timer always on" die habe 
ich aber ausgeschaltet.

Wenn ich die Applikation mit dem AVR Studio direkt auf den 2560 brenne, 
läuft sie ohne Probleme nur wenn ich mit dem Bootloader das Programm 
aufspiele, springt er zum Anfang des Bootloaders "0x1F000 so ca. nach 4 
Sekunden.

Merkwürdig...

von Peter D. (peda)


Lesenswert?

Jan Raddatz wrote:

> Wenn ich die Applikation mit dem AVR Studio direkt auf den 2560 brenne,
> läuft sie ohne Probleme nur wenn ich mit dem Bootloader das Programm
> aufspiele, springt er zum Anfang des Bootloaders "0x1F000 so ca. nach 4
> Sekunden.

Nun, daß hat aber nichts mit der Interruptvektortabelle zu tun.

Da ist irgendwas faul in Deinem Programm und es rennt irgendwann hinten 
über auf 0x0000.
Und ohne den Bootloader hinten dran merkst Du das bloß nicht.


Peter

von Guile L. (sphinx)


Lesenswert?

hmm ich geb's ja ungern zu aber mir scheint, dass Du recht hast.
Die Interrupts werden jetzt schonmal richtig angesprungen! Immerhin!

Es scheint aber noch ein Problem bei der Initialisierung der Anwendung 
zu geben. Mal gucken!

von subitus (Gast)


Lesenswert?

Auch wenn der Beitrag schon ein wenig älter ist:
Für die Forumleser, die auf ein ähnliches Phänomen stoßen wie Guile 
Lampert (sphinx): Dieses Problem tritt immer dann auf, wenn Interrupts 
verwendet werden, deren Sprungadressen jedoch nicht definiert sind. 
Generell gilt: Stets alle ISR definieren - andernfalls springt der IRQ 
im Wald umher.

Gruß,
AnnoDazumaL

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.