Forum: Mikrocontroller und Digitale Elektronik Bootloader aufrufen ATMEGA 8 - Bootloader Grundlagen


von Ronald (Gast)


Lesenswert?

Für alle die es interessiert - da ich gerade mehrere Stunden damit 
verbracht habe, Beispiele beziehen sich auf AT MEGA 8 und 16 mit jeweils 
2kB (1024 words) Bootloader (MEGA8: 0x1800 | MEGA16:0x3800)...

Wenn man einen Bootloader aus dem Hauptporgramm aufruft, springt man 
einfach an die Adresse des Bootloaders. Dabei gibt es, nach meinen 
Recherchen, 2 Möglichkeiten:

1. #define Starte_Bootloader  asm volatile ( "jmp 0x3800" )
   _Aufruf mit:_ Starte_Bootloader;

2. void (*Starte_Bootloader)(void)=(void *)0x1C00;
   _Aufruf mit:_ Starte_Bootloader();

!!! Dabei gilt es zu beachten:
Variante 1 verlangt die Adresse des Bootloaders in Byte (0x3800)
Variante 2 verlangt die Adresse des Bootloaders Word (0x3800 / 2 = 
0x1C00)

Die Adresse wird im Datenblatt immer in WORD angegeben.
!!!

Kann jemand mal bitte bei Gelegenheit den Unterschied zwischen "asm" und 
"asm volatile" erläutern - ich weiß es nämlich nicht...


Sonderfall ATMEGA 8:
--------------------
Der Prozessor kennt den "JMP" nicht sondern nur den "RJMP" Befehl. Also 
würde man schreiben:

1. #define Starte_Bootloader  asm volatile ( "rjmp 0x1800" )
   _Aufruf mit:_ Starte_Bootloader;

das funktioniert aber nach meiner_ Erkenntnis _NICHT und der Prozessor 
springt irgendwohin. Kann jemand sagen warum?

also beim ATMEGA 8 Variante 2 verwenden!

Sonderfall Interrupts:
----------------------
Will man im Bootloader Interrupts verwenden so muss man die Interrupts 
auf den Bootbereich des Flashspeichers verbiegen. Das macht man im GICR 
Register mit den Bits IVCE und IVSEL.

IRQ auf Bootloaderbereich:
GICR=(1<<IVCE);
GICR=(1<<IVSEL);

IRQ auf Hauptprogrammbereich:
GICR=(1<<IVCE);
GICR=0;

Start des Hauptprogramms aus dem Bootloader:
--------------------------------------------
das geht wieder mit den oben erwähnten Varianten 1 und 2, nur das man 
diesmal zur Adresse 0x0000 springt:

1. #define Starte_Programm asm volatile ( "jmp 0x0000" )
   _Aufruf mit:_ Starte_Programm;

2. void (*Starte_Programm)(void)=(void *)0x0000;
   _Aufruf mit:_ Starte_Programm();

!!! Dabei gilt es wieder zu beachten:
Variante 1 verlangt die Adresse des Bootloaders in Byte (0x3800)
Variante 2 verlangt die Adresse des Bootloaders Word (0x3800 / 2 = 
0x1C00)

Die Adresse wird im Datenblatt immer in WORD angegeben.
!!!


Abschliessend sei noch gesagt:

Alle verwendeten Variablen werden beim Aufruf des Bootloaders oder des 
Hauptprogramms NICHT initialisiert !!!!!!!!!!!!! D.h. man sollte die 
verwendeten Variablen beim Start des jeweiligen Prgramms (Boot/Haupt) 
initialisieren, da insbesondere bei Variablen die im Bootloader genauso 
heißen wie im Hauptprogramm der Wert meistens erhalten bleibt, und so 
z.B. Puffer für den Datenaustausch nach dem Start des Bootloaders noch 
Werte enthalten die im Hauptprogramm vor dem Bootloaderstart empfangen 
wurden.

von Ronald (Gast)


Lesenswert?

Kleine Korrektur für den Teil:





Start des Hauptprogramms aus dem Bootloader:
--------------------------------------------
das geht wieder mit den oben erwähnten Varianten 1 und 2, nur das man
diesmal zur Adresse 0x0000 springt:

1. #define Starte_Programm asm volatile ( "jmp 0x0000" )
   _Aufruf mit:_ Starte_Programm;

2. void (*Starte_Programm)(void)=(void *)0x0000;
   _Aufruf mit:_ Starte_Programm();

!!! Dabei gilt es wieder zu beachten:
Variante 1 verlangt die Adresse des Hauptprogramms in Byte (0x0000)
Variante 2 verlangt die Adresse des Hauptprogramms Word (0x0000)

Die Adresse wird im Datenblatt immer in WORD angegeben.
!!!



Die Adresse 0x3800 bzw. 0x1c00 gehören ja zum Bootloader....

von Gast (Gast)


Lesenswert?

Hallo,

volatile bedeutet bei Variablen, dass sie bei jedem Zugriff neu aus dem 
Speicher geladen werden. Das ist nützlich, wenn durch eine ISR oder 
sonstwie die Variable im Speicher geändert wurde ohne sie im 
entsprechenden Register zu ändern.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Der ATMEGA8 kennt den jmp Befehl offiziell zwar nicht, führt ihn aber 
korrekt aus ;-).

von Stefan E. (sternst)


Lesenswert?

> Alle verwendeten Variablen werden beim Aufruf des Bootloaders oder des
> Hauptprogramms NICHT initialisiert !!!!!!!!!!!!!

Das ist Blödsinn, und wird durch die Verwendung von noch mehr 
Ausrufezeichen auch nicht richtiger.
Wenn das Hauptprogramm über einen Sprung an die Adresse 0 gestartet 
wird, wird exakt der gleiche Code ausgeführt, wie bei einem "normalen" 
Reset, also inklusive aller Initialisierungen. Für den Bootloader gilt 
das gleiche.
Probleme gibt es nur dann (dann aber auch nur beim Start von einem von 
beiden), wenn beide nicht als separate Programme erstellt wurden, 
sondern das Eine Bestandteil des Anderen ist. Also ein Programm, dass 
sowohl den Code vom Bootloader, als auch vom Hauptprogramm enthält. Das 
wäre dann aber (in meinen Augen) ein ziemlicher Designfehler.

> 1. #define Starte_Bootloader  asm volatile ( "rjmp 0x1800" )
>    _Aufruf mit:_ Starte_Bootloader;
>
> das funktioniert aber nach meiner_ Erkenntnis _NICHT und der Prozessor
> springt irgendwohin. Kann jemand sagen warum?

Weil rjmp ein relativer Sprung ist, und daher als Parameter die 
Sprungweite erwartet, und keine absolute Adresse.

von Peter D. (peda)


Lesenswert?

Ronald wrote:

> !!! Dabei gilt es wieder zu beachten:
> Variante 1 verlangt die Adresse des Hauptprogramms in Byte (0x0000)
> Variante 2 verlangt die Adresse des Hauptprogramms Word (0x0000)

Da gibts überhaupt nichts zu beachten !!!

0 bleibt 0


Peter

von Peter D. (peda)


Lesenswert?

Ronald wrote:

> !!! Dabei gilt es zu beachten:
> Variante 1 verlangt die Adresse des Bootloaders in Byte (0x3800)
> Variante 2 verlangt die Adresse des Bootloaders Word (0x3800 / 2 =
> 0x1C00)

Da gibts aber noch mehr zu beachten.

Es gibt für jeden AVR-Typ 4 unterschiedliche Positionen für den 
Bootloader!

An ne feste Adresse zu springen ist also garnicht gut, egal ob Word oder 
Byte.

Deshalb ist der übliche Weg, daß die Applikation den Watchdog startet 
und der findet dann ganz automatisch die richtige Adresse.


Peter

von Peter D. (peda)


Lesenswert?

Ronald wrote:
> Alle verwendeten Variablen werden beim Aufruf des Bootloaders oder des
> Hauptprogramms NICHT initialisiert !!!!!!!!!!!!!

Was hast Du denn fürn komischen C-Compiler?
Klar wird der SRAM vor dem Main mit 0 initialisiert!


Peter

von Ronald (Gast)


Lesenswert?

So, also ich wäre ja für solche Hinweise dankbar gewesen das so in einem 
Beispiel mal erklärt zu bekommen, andere scheinen da anderer Auffassung 
zu sein... Naja dann mal zu den Hinweisen:

>Der ATMEGA8 kennt den jmp Befehl offiziell zwar nicht, führt ihn aber
>korrekt aus ;-).

Ok, und wie bringt man den avr-gcc das bei das er nicht mit
"Error: illegal opcode jmp for mcu atmega8" antwortet?

>Weil rjmp ein relativer Sprung ist, und daher als Parameter die
>Sprungweite erwartet, und keine absolute Adresse.

Danke, das macht dann also wenig sinn diesen Befehl in C zu verwenden... 
Sei denn man steht auf *.lst files und rechnen...

>Das ist Blödsinn, und wird durch die Verwendung von noch mehr
>Ausrufezeichen auch nicht richtiger.

Kann man auch etwas freundlicher schreiben ! Ist aber richtig, mein 
Fehler.

>Probleme gibt es nur dann (dann aber auch nur beim Start von einem von
>beiden), wenn beide nicht als separate Programme erstellt wurden,
>sondern das Eine Bestandteil des Anderen ist. Also ein Programm, dass
>sowohl den Code vom Bootloader, als auch vom Hauptprogramm enthält. Das
>wäre dann aber (in meinen Augen) ein ziemlicher Designfehler.

Der Bootloader und das Main sind zwei eigenständige *.hex files die 
getrennt von einader kompiliert werden. Zuerst wird der Bootloader per 
Programmier Schnittstelle (SPI) eingespielt, dann wird per CAN und I²C 
Bus das hex File durch den Bootloader hochgeladen und gestartet. BOOTRST 
Fuse ist nicht programmiert, es startet immer das Hauptprogramm...

>Da gibts überhaupt nichts zu beachten !!!
>0 bleibt 0

...

>Da gibts aber noch mehr zu beachten.
>Es gibt für jeden AVR-Typ 4 unterschiedliche Positionen für den
>Bootloader!

Vielleicht mal den Post Zeile 1 und 2 lesen ?
"Beispiele beziehen sich auf AT MEGA 8 und 16 mit jeweils
2kB (1024 words) Bootloader (MEGA8: 0x1800 | MEGA16:0x3800)..."

Ich zitiere mal das Datenblatt:

"The size of the different sections is configured by
the BOOTSZ Fuses as shown in Table 82 on page 217 and Figure 102."

"Boot Loader Features: Flexible Boot Memory Size"

Ich denke mal das das jedem klar ist der einen Bootloader programmiert, 
das es da unterschiedliche Größen gibt...

>An ne feste Adresse zu springen ist also garnicht gut, egal ob Word oder
>Byte.

Und warum sollte das nicht gut sein?
Weil ich nicht weiß an welcher Adresse mein Bootloader sitzt?
Weil ich ständig immer die Größe meines Bootloaders ändere?

>Deshalb ist der übliche Weg, daß die Applikation den Watchdog startet
>und der findet dann ganz automatisch die richtige Adresse.

Achso, und woher weiß die Aplikation, die sonst so schön vor sich 
herrechnet das ich jetzt den Bootloader starten will? Oder das 
Hauptprogramm?

Und was ist wenn ich den WDT zu dem Zweck benutze wozu er eigentlich 
gedacht ist? Nämlich falls das Programm mal hängt einen Reset 
auszulösen?

Und, nicht jeder will sein Programm immer mit den Bootloader starten und 
falls nichts kommt das Main ausführen. Es soll auch Menschen geben die 
an einem PC sitzen, über einen Bus (CAN,VPN,LAN o.ä.) mit vielen 
"Modulen" (AVR's) verbunden sind und sagen, jetzt möchte ich AVR xy 
einen neuen Code verpassen.Und das aus dem Hauptprogramm heraus - ohne 
WDT oder Resetknopf drücken - mit einem Sprung in den Bootloader und 
dann wenn der fertig ist wieder ins Hauptprogramm springen ohne 
irgentwelche WD Timer zu missbrauchen...

>Was hast Du denn fürn komischen C-Compiler?

Vermutlich den selben komischen Compiler wie viele andere hier, aber Du 
kennst bestimmt einen viel besseren als den AVR-GCC Compiler

>Klar wird der SRAM vor dem Main mit 0 initialisiert!

Und was ist mit einem Sprung zum Bootloader AUS DEM LAUFENDEN PROGRAMM 
HERAUS? wird da der SRAM auch immer mit 0 initialisiert?

von Stefan E. (sternst)


Lesenswert?

Ronald wrote:

>>Das ist Blödsinn, und wird durch die Verwendung von noch mehr
>>Ausrufezeichen auch nicht richtiger.
>
> Kann man auch etwas freundlicher schreiben ! Ist aber richtig, mein
> Fehler.

Ich hätte es zurückhaltender formuliert, wenn du diese
Falschinformation nicht so RAUSGESCHRIEN hättest!!!!!!!!!!!!!

> Und was ist mit einem Sprung zum Bootloader AUS DEM LAUFENDEN PROGRAMM
> HERAUS? wird da der SRAM auch immer mit 0 initialisiert?

Wie ich schon sagte, es macht keinen Unterschied. Der ausgeführte Code 
ist der gleiche, inklusive der Initialisierungen.

von Peter D. (peda)


Lesenswert?

Ronald wrote:

> Der Bootloader und das Main sind zwei eigenständige *.hex files die
> getrennt von einader kompiliert werden. Zuerst wird der Bootloader per
> Programmier Schnittstelle (SPI) eingespielt, dann wird per CAN und I²C
> Bus das hex File durch den Bootloader hochgeladen und gestartet. BOOTRST
> Fuse ist nicht programmiert, es startet immer das Hauptprogramm...

Kann man machen, muß man dann aber immer hoffen, daß nie ein Fehler 
passiert. D.h. die Applikation muß immer in der Lage sein, den 
Bootloader zu starten.
Die Praxis zeigt aber oft das Gegenteil.

Die Atmel-Leute haben sich schon was gedacht bei der BOOTRST-Fuse.
Kostet fast nix, nur ein paar ms Wartezeit ehe die Applikation startet.
Bringt aber nen 2. Versuch, falls man mal ne falsche Applikation 
gebrannt hat, die Verbindung abbrach usw.


> Und warum sollte das nicht gut sein?
> Weil ich nicht weiß an welcher Adresse mein Bootloader sitzt?
> Weil ich ständig immer die Größe meines Bootloaders ändere?

Ganz genau.

Ich setze viele AVRs ein, nicht nur einen Typ.
Und der Bootloader kann sich auch in der Größe ändern, wenn man 
Funktionen oder Protokolle ändert.
Warum sich also zusätzliche Fehlerquellen einbauen, wenns ne einfache 
Lösung gibt?


> Achso, und woher weiß die Aplikation, die sonst so schön vor sich
> herrechnet das ich jetzt den Bootloader starten will? Oder das
> Hauptprogramm?

Das bleibt ganz Dir überlassen.
Ich vermute mal, Du schickst der Applikation ein bestimmtes Kommando, 
damit sie den Bootloader aufruft.
Und statt nun direkt zu irgendeiner Adresse zu springen, geht sie in ne 
Endlosschleife, bis der Watchdog zuschlägt.


> Und was ist wenn ich den WDT zu dem Zweck benutze wozu er eigentlich
> gedacht ist? Nämlich falls das Programm mal hängt einen Reset
> auszulösen?

Geht doch weiterhin.
Der Bootloader disabled den Watchdog nicht, sondern triggert ihn nur.


> Und, nicht jeder will sein Programm immer mit den Bootloader starten und
> falls nichts kommt das Main ausführen. Es soll auch Menschen geben die
> an einem PC sitzen, über einen Bus (CAN,VPN,LAN o.ä.) mit vielen
> "Modulen" (AVR's) verbunden sind und sagen, jetzt möchte ich AVR xy
> einen neuen Code verpassen.

Spricht doch überhaupt nichts dagegen.

Nur hast Du nen zusätzlichen Notnagel mit dem Poweron in den Bootloader.
Niemand hat gesagt, daß das der einzige Weg ist.
Es ist nur der Weg, der in jedem Fall geht.
Auch wenn man versehentlich ne Applikation gebrannt hat, die völlig 
buggy ist.


> Und was ist mit einem Sprung zum Bootloader AUS DEM LAUFENDEN PROGRAMM
> HERAUS? wird da der SRAM auch immer mit 0 initialisiert?

Ja natürlich, was denn sonst?
Du startest ein vollständig neues Programm und das initialisiert sich 
komplett neu.

Bootloader und Applikation sind zwei eigenständige Programme, die 
überhaupt nichts voneinander wissen.


Peter

von Manfred Flume (Gast)


Lesenswert?

Hallo!

jmp 0 für ATMEGA8
(etwas tricky..)

#if defined (_AVR_ATmega8_)
      asm volatile ("LDI R20,0");
      asm volatile ("PUSH R20");
      asm volatile ("PUSH R20");
      asm volatile ("RET");
#else
    asm volatile ("jmp 0");
#endif

Gruß Manfred

von Hc Z. (mizch)


Lesenswert?

Stefan Ernst schrieb:

>> das funktioniert aber nach meiner_ Erkenntnis _NICHT und der Prozessor
>> springt irgendwohin. Kann jemand sagen warum?
>
> Weil rjmp ein relativer Sprung ist, und daher als Parameter die
> Sprungweite erwartet, und keine absolute Adresse.

Aber genau dafür ist doch der Assembler da.  Schließlich schreibe ich in 
Assembler auch
1
   rjmp label

und nicht
1
   rjmp label - .

Die Offsetrechnung macht der Assembler, er erwartet also keine 
Sprungweite als Argument.  Warum soll das bei rjmp 0 nicht gehen?

von Hc Z. (mizch)


Lesenswert?

Ronald schrieb:

> 2. void (*Starte_Bootloader)(void)=(void *)0x1C00;
>    _Aufruf mit:_ Starte_Bootloader();

Oder, ohne dafür eine Variable verbraten zu müssen:
1
 ( (void(*)(void))0x3800 )();
Viele irritierende Klammern, aber jede hat ihren Zweck ...

von Stefan E. (sternst)


Lesenswert?

Hazeh Zimmerer schrieb:

> Aber genau dafür ist doch der Assembler da.  Schließlich schreibe ich in
> Assembler auch
>
1
>    rjmp label
2
>
>
> und nicht
>
>
1
>    rjmp label - .
2
>
>
> Die Offsetrechnung macht der Assembler, er erwartet also keine
> Sprungweite als Argument.  Warum soll das bei rjmp 0 nicht gehen?

Bei einem Label ist die Sache ja auch eindeutig. Bei einem relativen 
Sprung mit einer nackten Zahl ist es das nicht. Es hängt vom Assembler 
ab, ob er das als absolute Adresse oder relative Distanz interpretiert.

von Andreas (Gast)


Lesenswert?

Danke an den TE.
Ist zwar schon ne Weile her, aber hier wird durch die vielen Kommentare 
einiges verdeutlicht!

Hat mir sehr geholfen!!

von marixstorm (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Was hast Du denn fürn komischen C-Compiler?
> Klar wird der SRAM vor dem Main mit 0 initialisiert!

Ähm, Nein.
Wenn man das haben will:

*
* Under normal circumstances, RESET will not clear contents of RAM.
* As always, if you want it done - do it yourself...
*/
void _attribute_ ((naked)) _attribute_ ((section (".init3"))) 
__init3(void);
void __init3(void) {
  extern size_t __bss_end;
  asm volatile (
    "__clearram:\n\t"
    "ldi r29, %[ramendhi]\n\t"
    "ldi r28, %[ramendlo]\n\t"
    "__clearramloop%=:\n\t"
    "st -Y , __zero_reg__\n\t"
    "cp r28, %A[bssend]\n\t"
    "cpc r29, %B[bssend]\n\t"
    "brne __clearramloop%=\n\t"
    :
    : [ramendhi] "M" (((RAMEND+1)>>8) & 0xff),
      [ramendlo] "M" (((RAMEND+1)>>0) & 0xff),
      [bssend] "r" (&__bss_end)
    : "memory"
      );
}

von marixstorm (Gast)


Lesenswert?

Btw. für einen sprung in die BLS (Bootloadersektion) empfiehlt Atmel bei 
älteren MCUs (soweit ich mich richtig erinnere) den Watchdog zu benutzen 
um eine RESET condition zu erzeugen.

Ansonsten, ein Bootloadercode ist für soetwas sehr lehrreich:

https://github.com/baerwolf/USBaspLoader

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.