Forum: Mikrocontroller und Digitale Elektronik Bootloader ATmega1284p Rücksprung zum Anwendungsprogramm


von Christian A. (chrisa1)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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
von N. M. (mani)


Lesenswert?

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
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von fop (Gast)


Lesenswert?

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 ?

von fop (Gast)


Lesenswert?

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 ?

von Christian A. (chrisa1)


Angehängte Dateien:

Lesenswert?

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.

von Christian A. (chrisa1)


Lesenswert?

@ 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

von Frank K. (fchk)


Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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.

von Frank K. (fchk)


Lesenswert?

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

von Pandur S. (jetztnicht)


Lesenswert?

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.

von Christian A. (chrisa1)


Angehängte Dateien:

Lesenswert?

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
von Frank K. (fchk)


Lesenswert?

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

von Christian A. (chrisa1)


Angehängte Dateien:

Lesenswert?

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.

von Frank K. (fchk)


Lesenswert?

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
Noch kein Account? Hier anmelden.