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.
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....
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.
Der ATMEGA8 kennt den jmp Befehl offiziell zwar nicht, führt ihn aber korrekt aus ;-).
> 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.
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
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
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
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?
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.
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
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
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?
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 ...
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.
Danke an den TE. Ist zwar schon ne Weile her, aber hier wird durch die vielen Kommentare einiges verdeutlicht! Hat mir sehr geholfen!!
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" ); }
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.