Hallo, ich benötige Hilfe bei Thema Bootloader für AVR Atmega. Konkretes Problem: Rücksprung vom Bootloader zum Anwendungsprogramm. Ich habe einen ATmega1284p und programmiere in C mit dem aktuellen AVR Studio. Mein Board hat drei LEDs (grün, orange, rot). Ich habe einen Bootloader erstellt anhand der Beschreibung AVR Bootloader in C - eine einfache Anleitung. Da ich das ganze Thema mit dem Bootloader zunächst einfach halten möchte, habe ich das Anwendungsprogramm in einen Array in den Bootloader kodiert. Das flashen des Anwendungsprogramms funktioniert. Beim anlegen der Versorgungsspannung startet der Bootloader. Der Bootloader schaltet zunächst die rote LED ein und wartet eine Sekunde, danach wird die rote LED wieder abgeschaltet und es wird die grüne LED angeschaltet. Es wird nun gewartet bis der Taster betätigt wird. Wird nun die Taste gedrückt, wird das flashen durchgeführt. Ich habe den Flash-Inhalt vor dem flashen und nach dem flashen (Taster drücken) ausgelesen. Vor dem flashen: Das Anwendungsprogramm ist nicht im Flash. Nach dem flashen (Taste drücken): Das Anwendungsprogramm ist ab der Adresse 0 im Flash-Speicher. Das Anwendungsprogramm ist sehr simpel. Das Anwendungsprogramm schaltet nur die drei LED gleichzeitig an und danach ist eine leere While-1-Schleife mit einem Wartezyklus. Wird nur das Anwendungsprogramm in den AVR geladen funktioniert es so wie soll. Ich habe nun die Taste gedrückt, nun wird der eigentliche Teil des Bootloader ausgeführt und das Array, in welchem das Anwendungsprogramm steht, wird in den AVR Flash geschrieben. Während des Schreibens in den Flash leuchtet die orange LED. Danach geht die orange LED wieder aus, also der Flash-Vorgang ist beendet. Nun wird soll der Sprung zum Anwendungsprogramm erfolgen. Dies erfolgt mit der Funktion: void (*start)( void ) = 0x0000; welche aufgerufen wird. /* Rücksprung zur Adresse 0x0000 */ start(); Anstatt nun die drei LED (grün, orange, rot) aufleuchten. Leuchtet wie bei dem Start des Bootloader zunächst eine Sekunde die rote LED und danach die gründe LED und mit dem bestätigen des Taster kann wird wieder der Flash-Vorgang durchgeführt. Das PROBLEM: Der Sprung vom Bootloader zum Anwendungsprogramm funktioniert scheinbar nicht. Ich habe auch den Sprung mit weiteren Möglichkeiten ausprobiert wie z.B.: goto *0; asm volatile ("jmp 0"); Leider mit dem gleichen Fehlschlag. Was mache ich falsch? Wie kann der Sprung zum Anwendungsprogramm aussehen? Ich freue mich über konstruktive Vorschläge.
Lass den Watchdog einen reset ausführen. Beispiel:
1 | #include <avr/wdt.h> |
2 | ...
|
3 | sei (); |
4 | |
5 | wdt_enable(WDTO_500MS); |
6 | |
7 | for (;;) |
8 | {
|
9 | ;
|
10 | }
|
Siehe auch http://www.netzmafia.de/skripten/hardware/Arduino/Watchdog/index.html für andere Timing-Werte. Ob sei() wirklich notwendig ist, weiß ich nicht aus dem Kopf, habe nur eben mal in einem meiner Bootloader nachgeschaut.
:
Bearbeitet durch Moderator
Christian A. schrieb: > Ich habe einen Bootloader erstellt anhand der Beschreibung AVR > Bootloader in C - eine einfache Anleitung. Fuses stehen alle richtig? Evtl wäre es sinnvoll ein Ruckgelesenes Bin/Hex und die Fuse Einstellungen zu posten. Frank M. schrieb: > Lass den Watchdog einen reset ausführen. Sehe ich auch so. Dann kannst du auch in jedem FW Teil sicher sein dass alles initialisiert ist und die Peripherie so initialisiert ist wie du es erwartest.
:
Bearbeitet durch User
N. M. schrieb: > Frank M. schrieb: > >> Lass den Watchdog einen reset ausführen. > > Sehe ich auch so. Ist leider nur die halbe Miete, habe gerade nochmal meinen Bootloader studiert. Das Problem ist, dass bei aktiviertem Bootloader auch der Watchdog-Reset wieder in den Bootloader springt. Hier kommt man dann aber nach einem Timeout mit
1 | void (*funcptr) (void) = 0x0000; |
2 | ...
|
3 | funcptr(); |
wieder in die Anwendung.
So ganz ist mir noch nicht klar, wie Dein Bootloader unterscheidet, was laufen soll Bootloader oder Applikation. Wenn der Bootloader losläuft, bedeutet das doch, ein Reset springt in den Bootloader. Den Bootloader mit der Applikation zu überschreiben ist ja taktisch eher unklug, so wegen Updates, Abbruch des Flashens und überhaupt. Also kann ein Reset nicht Deine Applikation starten. Oder fummelst Du an der BOOTRST Fuse rum, nachdem Du die Applikation geflasht hast ? Welchen Speicherbereich hat Dein Bootloader, welchen die Applikation ?
Microchip hat sich das ja so gedacht, dass der Bootloader hinten im Flash liegt und die Applikation davor. Die Interruptvektoren verlegt man par VSEL Bit im MCUCR SFR. Den Einsprung beim Reset über die BOOTRST Fuse. Wobei ich die Fuse so stehen lassen würde, dass immer der Bootloader gestartet wird. Der entscheidet, ob die Applikation gestartet wird. Ein Sprung auf Adresse 0 sollte da echt das Richtigste sein. Vorher solltest Du so wenig wie möglich verdrehen, weil die Applikation nicht darauf gefasst sein könnte. Eventuell kannst Du ja nach dem Flashen einen echten Reset auslösen und wenn dann der Bootloader neu startet, entscheiden, dass jetzt die Applikation dran ist und auf Adresse 0 springen. Für den echten Reset brauchst Du wohl wirklich den Watchdog. Ich würde allerdings die kürzeste Ablaufzeit so um die 16ms wählen, wozu soll sich der µC 500ms die Pins in den Bauch stehen ? Aber nochmal die Frage : liegt Dein Bootloader "hinten" im Flash ? So ab Adresse 0xF?00 je nach BOOTSZ Fuses ?
Zunächst schon einmal vielen Dank für eure Posts. Ich habe Screenshots von den Fuse-Bits gemacht. Ich habe ebenso Screenshots von den Hex-Files gemacht. ex_vortaste.png (flash_2021-05-17_004.hex)-> Bevor der Bootloader den Code in den Flash schreibt hex_nachtaste.png (flash_2021-05-17_005.hex)-> Nachdem der Bootloader den Code geschrieben hat. Die Möglichkeit mit dem Watchdog, wie Frank M. beschrieben hat, habe ich auch gerade ausprobiert. Leider auch kein Erfolg.
@ fop Das BOOTRST ist ständig an/gesetzt. Der Bootloader soll später entscheiden, ob nach einem Reset geflasht werden soll oder nicht und direkt zur Anwendung springen. Bzgl. Speicherbereich: Bootloader -> 0xE000 Anwendungsprogramm -> 0x0 => siehe Screnshots
Da stimmt was nicht. Du sagst "Bootloader -> 0xE000". In deinem fuse.jpg steht aber: "High.BOOTSZ: size=4096 words Boot address = $F000." Das passt nicht zusammen. Dein Bootloader muss bei $F000 beginnen und darf maximal 4096 Bytes groß sein. Mehr geht bei dieser Architektur nicht. Siehe Datenblatt Seite 288, "Table 24-7.Boot Size Configuration" fchk
Christian A. schrieb: > Das BOOTRST ist ständig an/gesetzt. Der Bootloader soll später > entscheiden, ob nach einem Reset geflasht werden soll oder nicht und > direkt zur Anwendung springen. > > Bzgl. Speicherbereich: > > Bootloader -> 0xE000 > Anwendungsprogramm -> 0x0 Wenn das alles so ist, dann sollte es das
1 | void (*funcptr) (void) = 0x0000; |
2 | ...
|
3 | funcptr(); |
Doch auch problemlos tun. Natürlich sollte das (und damit auch die vorherige Entscheidung) passieren, bevor der Bootloader irgendwas großartig an Hardware für seinen eigentlichen Job benutzt. Und das wenige, was er benutzt, um die Entscheidung fällen zu können, sollte er einfach wieder aufräumen, bevor er springt. Kann ja nicht so schwer sein. Das einfachste Entscheidungskriterium wäre übrigens die Reset-Ursache. D.h.: Du führst ein Flashupdate durch, dann löst du einen Watchdog-Reset aus und landest mit sauber initialisierter Hardware wieder in deinem Bootloader. Hier stellst du fest, weswegen der Reset kam und entscheidest anhand der Reset-Ursache. Vermutlich wäre es hier am sinnvollsten, bei allen Resets außer Reset-Pin oder PowerOn direkt in die Anwendung zu springen. Und für diese beiden verbleibenden muss man halt je nach konkreter Schaltung die Sache festlegen. Also letztlich: wodurch genau soll eigentlich die Bootloader-Funktionalität ausgelöst/ermöglicht werden.
Frank K. schrieb: > Das passt nicht zusammen. Dein Bootloader muss bei $F000 beginnen und > darf maximal 4096 Bytes groß sein. Mehr geht bei dieser Architektur > nicht. Ich meinte 4096 Words und nicht Bytes. Sorry. fchk
Ich hab auch mal einen Bootloader geschrieben. Der prueft erst mal, ob ein gueltiges Programm geladen ist. Falls nicht, wir das so angezeigt und auf kommunikation gewartet. Falls ein gueltiges Programm geladen ist, wird zB eine Sekunde auf kommunikation gewartet und dann geht's weiter zur Applikation. Ohne spezielle Vorkehrungen muss man waehrend der Kommunikation grad programmieren, weil die Menge an zu programmierendem Code ein Stueck groesser wie der verfuegbare speicher ist.
Frank K. schrieb: > Frank K. schrieb: > >> Das passt nicht zusammen. Dein Bootloader muss bei $F000 beginnen und >> darf maximal 4096 Bytes groß sein. Mehr geht bei dieser Architektur >> nicht. > > Ich meinte 4096 Words und nicht Bytes. > > Sorry. > > fchk Ja da muss man aufpassen. Size = 4096 words sind eben 8kBytes. Also passt die Größe des Bootloaders. Im AVR Studio habe entsprechende Linker-Optionen eingegeben, dass der Bootloader an der Adresse OxF000 stehen soll. Hier wird wieder mit words (2Bytes) hantiert. Ich denke, dass sollte passen. Lasse mich aber gerne vom Gegenteil überzeugen. Was mich etwas stutzig macht ist folgendes. Ich habe das HEX File genauer angeschaut und genau in der Mitte habe (siehe Screenshot) den Eintrag > :020000021000EC < gefunden. Danach beginnt die Adresse im HEX File von FFF0 auf 0000 von vorn. Ist das normal? Ich habe auch schon versucht das Register EIND auf null zu setzten. Jedoch ist dies ja kein AVR mit größer als 128kB Flash und folge dessen hat der Atmega1284p auch nicht das Register. Dementsprechend erfolgt auch eine Fehlermeldung. Gibt es eventuell jedoch bei dem 1284p ein ähnliches Register welches für große Sprünge gesetzt werden muss?
:
Bearbeitet durch User
Christian A. schrieb: > Was mich etwas stutzig macht ist folgendes. Ich habe das HEX File > genauer angeschaut und genau in der Mitte habe (siehe Screenshot) den > Eintrag > :020000021000EC < gefunden. Danach beginnt die Adresse im HEX > File von FFF0 auf 0000 von vorn. Ist das normal? ok, damit addieren sie einen Offset von 64k auf alles, was folgt. (Type 2: Extended Segment Address Record) Dein Bootloader beginnt also an 0x1e000 (Byte-Adresse), was 0xf000 (Word-Adresse) entspricht. fchk
Das Problem liegt immer noch vor... Ich habe nun folgendes gemacht: Ich habe einen ATmega16 mit einem Olimex Board genommen und ebenfalls so einfach wie möglich gemacht. Das Setup... Anwendungsprogramm: Ich habe ein total einfaches Anwendungsprogramm erstellt. Das Anwendungsprogramm schaltete die LED ein und warte dann in einer while1-Schleife.
1 | // Anwendungsprogramm
|
2 | int main(void) |
3 | {
|
4 | DDRB |= 0x01; |
5 | PORTB &= ~ (0x01); |
6 | while (1) |
7 | {
|
8 | asm volatile("nop"); |
9 | }
|
10 | }
|
Bootloader: Der Bootloader gibt einmal („warten“) aus und warte bis die Taste gedrückt wird. Danach wird geflasht und zuvor wird über UART(„flashen“) ausgegeben. Ist das flashen vorbei, wird über UART („fertig“) ausgegeben und wieder gewartet bis die Taste gedrückt wird. Wird nun ein weiteres Mal die Taste gedrückt, sollte nun zum Anwendungsprogramm gesprungen werden.
1 | void (*start)( void ) = 0x0000; |
2 | |
3 | // Bootloader
|
4 | int main(void) |
5 | {
|
6 | ....
|
7 | ....
|
8 | send_uart('w'); |
9 | send_uart('a'); |
10 | send_uart('r'); |
11 | send_uart('t'); |
12 | send_uart('e'); |
13 | send_uart('n'); |
14 | while( taster1() == 1 ) |
15 | {
|
16 | asm volatile("nop"); |
17 | }
|
18 | |
19 | PORTB |= 0x01; // LED ausschalten |
20 | |
21 | send_uart('f'); |
22 | send_uart('l'); |
23 | send_uart('a'); |
24 | send_uart('s'); |
25 | send_uart('h'); |
26 | send_uart('e'); |
27 | send_uart('n'); |
28 | flash_program(0, bootarray); |
29 | |
30 | send_uart('f'); |
31 | send_uart('e'); |
32 | send_uart('r'); |
33 | send_uart('t'); |
34 | send_uart('i'); |
35 | send_uart('g'); |
36 | while( taster1() == 1 ) |
37 | {
|
38 | asm volatile("nop"); |
39 | }
|
40 | |
41 | start(); |
42 | |
43 | while (1) |
44 | {
|
45 | asm volatile("nop"); |
46 | }
|
47 | }
|
Nun passiert das gleich wie bei dem ATmega1284p. Das Anwendungsprogramm wird wieder in den Flash geschrieben. Jedoch der eigentliche Sprung erfolgt nicht zum Anwendungsprogramm, sondern zum Bootloader wieder. Wenn ich die Taste gedrückt halte. Wird über UART ständig wartenflashenfertig ausgegeben. Siehe Screenshot Term2. Ich habe statt Funktion zur Adresse 0x0000;
1 | void (*start)( void ) = 0x0000; |
Ein goto mit Zeiger zur Adresse 0 verwendet.
1 | goto *0; |
Leider der gleiche Fehlschlag. Nach meiner Analyse liegt das Problem, dass der Sprung zwar ausgeführt wird aber nicht zum Anwendungsprogramm, sondern zum Anfang des Bootloaders. Was mache ich falsch? Ich bin ratlos. Würde mich freue auf eure Unterstützung.
Christian A. schrieb: > Nach meiner Analyse liegt das Problem, dass der Sprung zwar ausgeführt > wird aber nicht zum Anwendungsprogramm, sondern zum Anfang des > Bootloaders. > > Was mache ich falsch? Ich bin ratlos. Würde mich freue auf eure > Unterstützung. Besorge Dir einen JTAG-Debugger und lass das ganze im Einzelschrittmodus im Debugger laufen. Wofür gibts den denn? fchk
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.