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.
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.
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
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?
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
intmain(void)
3
{
4
DDRB|=0x01;
5
PORTB&=~(0x01);
6
while(1)
7
{
8
asmvolatile("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
intmain(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
asmvolatile("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
asmvolatile("nop");
39
}
40
41
start();
42
43
while(1)
44
{
45
asmvolatile("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