Forum: Mikrocontroller und Digitale Elektronik AVR Bootloader: Sprung zur Applikation


von Jan K. (jeangonzales)


Lesenswert?

Hallo,

ich möchte von meinem Bootloader nach erfolgtem Flashen in die Anwendung 
springen. Das Problem ist aber, dass das nicht so recht funktioniert. 
Ich arbeite wie in der Application Note von Atmel gezeigt mit einem 
Funktionszeiger auf die Adresse 0x0000. Vorher werden noch die 
Interrupts wieder auf den Applikationsbereich umgebogen:
1
void (*jump_to_main_application)( void ) = 0x0000;
2
3
void main()
4
{
5
6
  ...
7
  uint8_t temp;
8
  cli();
9
  
10
  // Get MCUCR
11
  temp = MCUCR;
12
13
  // Enable change of Interrupt Vectors
14
  MCUCR = (temp | (1<<IVCE));
15
  
16
  // Move interrupts to App Flash section 
17
  MCUCR = (temp & ~(1<<IVSEL));
18
  
19
  jump_to_main_application();
20
21
}

Wenn der Code ausgeführt wird, springt der AVR aber immer wieder in den 
Bootloader, der dann in einer Endlosschleife immer wieder ausgeführt 
wird. Hier noch Ausschnitte aus den lss und lst Dateien (bin leider des 
Assemblers noch nicht so recht mächtig, um die Sprungadresse zu deuten):
1
                        jump_to_main_application();
2
   3e4e2:  e0 91 06 02   lds  r30, 0x0206
3
   3e4e6:  f0 91 07 02   lds  r31, 0x0207
4
   3e4ea:  19 95         eicall
5
   3e4ec:  a6 cf         rjmp  .-180      ; 0x3e43a <main+0x102>
6
7
8
1411 03d0 E091 0000     lds r30,jump_to_main_application
9
1412 03d4 F091 0000     lds r31,(jump_to_main_application)+1
10
1413 03d8 1995          eicall
11
1414 03da 00C0          rjmp .L144
12
13
1430                 .global  jump_to_main_application
14
1431                 .global  jump_to_main_application
15
1434                 jump_to_main_application:
16
1435 0006 0000          .skip 2,0

Irgend eine Idee, was verkehrt läuft?
Die Anwendung, die geflasht wurde, funktioniert. Wenn ich die Fuse 
BOOTRST lösche, wird die vom Bootloader geflashte Anwendung korrekt 
ausgeführt).

Grüße, Jan

von Klugscheisser (Gast)


Lesenswert?

Im wesentlichen gibt es zwei mögliche Ursachen.

1. Die Applikation wird zwar korrekt angesprungen, aber dort wird sofort 
wieder in den Bootloader gesprungen. Das ist auf nicht ganz 
durchprogrammierte Erkennung der Bedingung für das gewollte springen in 
den Bootloader zurückzuführen.

2. Ich bin mir nicht sicher, aber ich würde mal schauen wie der 
Assemblercode für den Teil:
1
  // Get MCUCR
2
  temp = MCUCR;
3
4
  // Enable change of Interrupt Vectors
5
  MCUCR = (temp | (1<<IVCE));
6
  
7
  // Move interrupts to App Flash section 
8
  MCUCR = (temp & ~(1<<IVSEL));

aussieht. Lt. Datenblatt dürfen max. 4 Zyklen zwischen dem setzen von 
IVCE und IVSEL liegen. Die Ausdrücke sehen wegen dem laden von temp 
etwas länglich aus, aber es könnte trotzdem OK sein. Dennoch würde ich 
mal nachschauen.

Falls Dir das noch ein wenig zu hoch ist, würde ich vorsichtshalber
1
  // Enable change of Interrupt Vectors
2
  MCUCR |= (1<<IVCE));
3
  
4
  // Move interrupts to App Flash section 
5
  MCUCR &= ~(1<<IVSEL);

stattdessen schreiben.

von tom (Gast)


Lesenswert?

Hallo Jan,

Mach mal so:

// pointer NUR deklarieren
static void (*pApplic) (void);


int main (void)
{

// dein code...

    // pointer HIER initialisieren
    pApplic = (void *) 0x0000; // app reset vector

    pApplic();
}


... dann sollte es tun.
wahrscheinlich hast Du einen mega128 o.ä. und benutzt gcc ?
Dann wird wohl die Initialisierung Deiner automatischen Variablen nicht 
funzen wg. der Lokierung oberhalb der 64k-Grenze.

Gutt Lack, tom.

von Jan K. (jeangonzales)


Lesenswert?

Hallo Tom,

Ja, ich verwende unter anderem einen Mega2561 und kompiliere mit 
avr-gcc.

verstehe zwar nicht, was du mit "Lokierung oberhalb 64k-Grenze" meinst", 
aber ich werde es heute abend mal ausprobieren!

Die Tipps aus dem Beitrag hier: 
Beitrag "Re: Sprung von Bootloader nach Hauptprogramm"

kann ich jedenfalls nicht mal kompilieren.

*Jan

von Jan K. (jeangonzales)


Lesenswert?

Klugscheisser wrote:
> Im wesentlichen gibt es zwei mögliche Ursachen.
>
> 1. Die Applikation wird zwar korrekt angesprungen, aber dort wird sofort
> wieder in den Bootloader gesprungen. Das ist auf nicht ganz
> durchprogrammierte Erkennung der Bedingung für das gewollte springen in
> den Bootloader zurückzuführen.

Wer soll denn was wo prüfen?
Wenn der Sprung funktionert, also der IP auf Adresse 0 gesetzt wird, 
sollte die Initialisierung wie nach einem normalen Reset stattfinden.



> aussieht. Lt. Datenblatt dürfen max. 4 Zyklen zwischen dem setzen von
> IVCE und IVSEL liegen. Die Ausdrücke sehen wegen dem laden von temp
> etwas länglich aus, aber es könnte trotzdem OK sein. Dennoch würde ich
> mal nachschauen.

Ist kein Problem. Derselbe Code funktioniert ja auch im Bootloader (nur 
dass die Vektoren dann auf den Bootloderbereich umgelegt werden) 
wunderbar. Ausserdem stammt der 1:1 aus der AN von Atmel.

Ich denke eher, der Tipp von Tom wird mich zum Erfolg führen :)

von Jan K. (jeangonzales)


Lesenswert?

So, jetzt bin ich wieder etwas schlauer. Der Hinweis mit der 64k-WORD 
Grenze hat mich letztlich auf den richtigen Weg gebracht:

Der GCC hat ja den Aufruf des NULL-Funktionszeigers mit einer 
EICALL-Instruktion übersetzt (An den Adressen 0x0237 und 0x0238 steht 
der NULL-Pointer:
1
                       jump_to_main_application();
2
   3e574:  e0 91 37 02   lds  r30, 0x0237
3
   3e578:  f0 91 38 02   lds  r31, 0x0238
4
   3e57c:  19 95         eicall
5
<main+0x184>

Aber er hat "vergessen", das EIND Register auf 0 zu setzen. Das 
erweitert ja die Adresse im Z-Register um ein weiteres Byte, wenn der 
Adressraum größer als 64k ist (wie bei meinem ATMega2561 der Fall).

Jetzt habe ich den Teil von Hand in Assembler kodiert:
1
asm volatile (
2
    "ldi r24, 0"  "\n\t"
3
    "out 0x3C, r24"  "\n\t"
4
    "ldi r30, 0x00"  "\n\t"
5
    "ldi r31, 0x00"  "\n\t"
6
    "eicall"
7
);

Und siehe da, es funktionert!
Dabei geholfen hat dieser Thread auf der avr-gcc mailinglist: 
http://www.mail-archive.com/avr-gcc-list@nongnu.org/msg05260.html

von tom (Gast)


Lesenswert?

Hallo Jan,

na prima, das es bei Dir jetzt funzt.
Du kannst aber auch einfach ein 'call 0x00000' benutzen für den Sprung 
auf den Reset-vector der applikation. Sollte auch tun.
Vice versa geht das so natürlich dann auch mit dem springen von 
applikation in den bootloader, da sollte dann natürlich die adresse des 
BTL-reset vectors drin stehen.

#define pApplic()     _asm__ __volatile_ ("call 0x00000")


void foo(void)
{

    pApplic();

}

Na denn, ciao + frohes werkeln.

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.