//Das erste Datenwort im System ROM (= Speicherbereich des Bootloaders) enthält die Adresse,
29
//mit welcher der Stackpointer geladen wird. Dieser Wert wird nun dem Stackpointer zugewiesen.
30
__set_MSP(*(uint32_t*)BOOTLOADER_ADR);
31
32
JumpToSystemROM();//Bootloader-Code über Aufruf des Funktionszeigers ausführen -> Sprung in System ROM
33
}
Dieser Code funktioniert auf einem STM32G030K6T6 problemlos. Sobald ich
den Code hochgeladen habe, startet der Mikrocontroller den Bootloader
und ich kann ihn mit dem Tool "STM32CubeProgrammer" über UART auslesen
und auch programmieren.
Jetzt habe ich einen STM32L031K6T6 und möchte ebenfalls mit der Funktion
Test_Jump() aus der Applikation in den Bootloader springen.
Hierfür habe ich die Zeilen
1
#define BOOTLOADER_ADR 0x1FFF0000
1
#include<stm32g0xx.h>
durch
1
#define BOOTLOADER_ADR 0x1FF00000
1
#include<stm32l0xx.h>
ersetzt, da der System Memory beim STM32L031K6T6 an einer anderen
Adresse beginnt. Der Rest des Programms ist exakt gleich.
Da der Code beim G0-Mikrocontroller funktioniert hat, bin ich davon
ausgegangen, dass dieser auch beim L0 funktioniert - leider Fehlanzeige.
Der CubeProgrammer meldet einen Timeout beim Warten auf das ACK.
Wenn ich mit dem Debugger unter Keil µVision den Code debugge, dann sehe
ich, dass der Code bis zum Aufruf von JumpToSystemROM (Z. 32)
funktioniert. Nach dieser Zeile landet der Mikrocontroller jedoch im
HardFault Handler.
Wenn ich den Code bis einschließlich Z. 30 laufen lasse und dann in Keil
den PC manuell auf 0x1FF00004 stelle, landet der Controller wie
gewünscht im Bootloader und ich kann über den CubeProgrammer auf ihn
zugreifen. Deshalb gehe ich davon aus, dass die Adresse richtig ist und
der restliche Code ebenfalls funktioniert.
Kann sich jemand einen Reim darauf machen? Wenn ich nach dem Debugger
gehe, scheint etwas am Funktionszeiger nicht zu stimmen, da der
Mikrocontroller bei dessen Aufruf im HardFault Handler landet. Aber wo
liegt der Fehler? Und wieso funktioniert der Code beim G0-Controller
ohne Probleme?
Wenn ich den BOOT0-Pin auf VCC ziehe, funktioniert der Bootloader
übrigens auch.
Vielleicht sieht jemand das Brett, welches ich gerade vor dem Kopf habe
und kann es durchsägen :-).
Gruß
Danke für die Antworten.
pegel schrieb:> Hat der L0 womöglich andere Anforderungen bezüglich SysTick Registern?>> Lass doch das Rücksetzen einfach mal weg.
Ich habe die Zeilen 21-23 auskommentiert, aber das hat leider nicht
geholfen, der µC landet immer noch im HardFault Handler statt im
Bootloader. Ein zusätzliches auskommentieren von Z. 26 hilft ebenfalls
nicht weiter.
pegel schrieb:> Meine BL Jump Funktion in verschiedene USB BL kümmert sich selbst um> alles Nötige und hat bei allen µC funktioniert:
Ich habe es gerade mal mit deiner Funktion versucht. Da ich ohne HAL
arbeite, habe ich die SYSCFG-Konfiguration direkt auf Registerebene
durchgeführt. Im Anhang ein Auszug aus dem Referenzhandbuch mit der
Beschreibung der Bits.
Leider funktioniert es hiermit auch nicht. Mit dem Debugger sehe ich,
dass der µC nach dem Aufruf von SysMemBootJump (Z. 14) wieder auf Z. 5
zurückspringt und die Funktion noch mal abarbeitet. Das ganze geht immer
so weiter, jedes mal folgt auf Z. 14 die Z. 5. Wie das zustande kommt,
ist mir nicht klar.
Ich habe gerade mal in der Bootloader Applikation Note von ST
nachgeschaut. Der L0 hat den Bootloader in der Version 12.0 und es sind
keine bekannten Probleme in der AN aufgelistet. Der G0 hat je nach
Revision die Bootloaderversion 5.0, 5.1 oder 5.2.
pegel schrieb:> Was hat es mit den anderen 3 Bit des SYSCFG_CFGR1 Registers auf sich?
Nichts, 0, zero. UFB gibt es bei diesem Chip garnicht und BOOT_MODE
funktioniert nicht
1
STM32L031xx/L041xx device errata 2.1.6
2
BOOT_MODE bits do not reflect the selected boot mode
3
Description
4
The BOOT_MODE[1:0] bits of the SYSCFG_CFGR1 register remain set to ‘0’
5
while they should reflect the boot mode selected by the boot pins.
6
Workaround
7
None.
Das macht aber nichts, die helfen uns nicht weiter, selbst wenn sie
funktionieren würden.
Was mich mehr irritiert: ihr postet hier immer nur kurze Ausschnitte aus
der Funktion JumpToBootloader(), oder? Ich meine, wenn man das System
Memory remapped muss man doch trotzdem SYSTICK und RCC zurücksetzen und
die Interrupts im NVIC abschalten und global wieder einschalten (cpsie)?
Die Daumenregel ist eigentlich ganz einfach: Alles, was man
eingeschaltet hat, muss man vor dem Jump wieder ausschalten. Alle
Register sollen im gleichen Zustand sein wie nach einem Reset.
Deshalb ist es einfacher, wenn man selbst einen Reset erzeugt. Das
eigene Programm muss gleich nach dem Start zwei verschiedene
Reset-Auslöser unterscheiden und entsprechend den Bootloader oder das
Hauptprogramm starten. Dafür gibt es das RCC->CSR.
Das würde gut zu pegels BootLoaderUniversalAnsprungFunktion passen.
Morgen,
danke für die Antworten.
Bauform B. schrieb:> Was mich mehr irritiert: ihr postet hier immer nur kurze Ausschnitte aus> der Funktion JumpToBootloader(), oder? Ich meine, wenn man das System> Memory remapped muss man doch trotzdem SYSTICK und RCC zurücksetzen und> die Interrupts im NVIC abschalten und global wieder einschalten (cpsie)?
Ich habe ein µVision Projekt, welches nur den Code enthält, den ich in
Beitrag 1 gepostet habe. Der Mikrocontroller springt also direkt nach
dem Reset in die Funktion TestJump(). Die Register sollten also alle
noch im Reset-State sein, da es keine anderen Programmteile gibt, in
denen ich Register modifiziere.
Den SysTick setze ich auf Registerebene zurück. Nach dem Hinweis von
pegel in Beitrag 2 habe ich diese Zeile testweise auch mal weggelassen,
was nicht geholfen hat.
Die Interrupts deaktiviere ich aktuell mittels __disable_irq(). Wenn ich
die Zeile auskommentiere oder darunter ein __enable_irq() einfüge, hilft
das ebenfalls nicht.
Bauform B. schrieb:> Die Daumenregel ist eigentlich ganz einfach: Alles, was man> eingeschaltet hat, muss man vor dem Jump wieder ausschalten. Alle> Register sollen im gleichen Zustand sein wie nach einem Reset.
Das müsste bei mir (eigentlich) gegeben sein, da ich direkt nach dem
Reset den Sprung in den Bootloader durchführe.
Bauform B. schrieb:> Das> eigene Programm muss gleich nach dem Start zwei verschiedene> Reset-Auslöser unterscheiden und entsprechend den Bootloader oder das> Hauptprogramm starten. Dafür gibt es das RCC->CSR.
Ich habe mir das Register gerade mal angeschaut. Damit könnte ich beim
Start unterscheiden, von welcher Quelle der Reset ausgelöst wurde
(NRST-Pin, Software-Reset, POR etc.). Wenn ich dich richtig verstehe,
würde ich dann zu Beginn von main() prüfen, ob z.B. ein Software-Reset
ausgelöst wurde. Falls ja, springe ich direkt an dieser Stelle in den
Bootloader.
Der einzige Unterschied zu meinem aktuellen Code wäre dann die
zusätzliche if-Abfrage. Ich kann mir nicht vorstellen, dass das einen
Unterschied macht, aber ich kann es ausprobieren.
pegel schrieb:> Moin,> habe mal etwas gesucht und auch gefunden:>> https://electronics.stackexchange.com/questions/312303/stm32f091-jump-to-bootloader-from-application>> https://electronics.stackexchange.com/questions/412157/stm32l011-jump-to-bootloader-from-user-code>> Besonders die Antwort im zweiten link beachten.
Ich habe mir die Links angesehen. Laut dem zweiten Link springt der
Bootloader also wieder zurück zum Flash, wenn dort ein Programm
vorhanden ist.
In meinem ersten Post hatte ich bereits erwähnt, dass der Sprung in den
Bootloader klappt, wenn ich den PC manuell auf die Bootloader-Adresse
setze:
mabe schrieb:> Wenn ich mit dem Debugger unter Keil µVision den Code debugge, dann sehe> ich, dass der Code bis zum Aufruf von JumpToSystemROM (Z. 32)> funktioniert. Nach dieser Zeile landet der Mikrocontroller jedoch im> HardFault Handler.> Wenn ich den Code bis einschließlich Z. 30 laufen lasse und dann in Keil> den PC manuell auf 0x1FF00004 stelle, landet der Controller wie> gewünscht im Bootloader und ich kann über den CubeProgrammer auf ihn> zugreifen. Deshalb gehe ich davon aus, dass die Adresse richtig ist und> der restliche Code ebenfalls funktioniert.
Ich gehe deshalb davon aus, dass das Programm im Flash nicht die
Problemursache ist.
Ich denke immer noch, dass etwas mit dem Funktionszeiger nicht stimmt.
Hier mal der Assembler-Code, den mir der Disassembler in Keil anzeigt:
32: JumpToSystemROM(); //Bootloader-Code über Aufruf des Funktionszeigers ausführen -> Sprung in System ROM
32
0x08000276 9800 LDR r0,[sp,#0x00]
33
0x08000278 4780 BLX r0
34
33: }
Wenn ich mit dem Debugger die Z. 32 ausführe, lande ich danach wie
bereits erwähnt im HardFault-Handler. Im Anhang ein Screenshot von Keil.
pegel schrieb:> Das mit dem USART1 aus link1 ist allerdings auch Möglich und lässt sich> schnell testen.
Ich verwende den USART2, das sollte aber keinen Unterschied machen. Ich
habe testweise mein Programm aus Post 1 mal wie folgt modifiziert:
1
#define BOOTLOADER_ADR 0x1FF00000
2
3
#include<stm32l0xx.h>
4
5
voidTestJump(void);
6
7
intmain(void)
8
{
9
TestJump();
10
11
while(1);
12
}
13
14
voidTestJump(void)
15
{
16
//Funktionszeiger auf Adresse des Bootloader deklarieren.
17
//Die Adresse muss um 4 erhöht werden, da das erste Datenwort der Wert ist, mit dem der Stack Pointer geladen wird.
//Das erste Datenwort im System ROM (= Speicherbereich des Bootloaders) enthält die Adresse,
33
//mit welcher der Stackpointer geladen wird. Dieser Wert wird nun dem Stackpointer zugewiesen.
34
__set_MSP(*(uint32_t*)BOOTLOADER_ADR);
35
36
JumpToSystemROM();//Bootloader-Code über Aufruf des Funktionszeigers ausführen -> Sprung in System ROM
37
}
Ich lande immer noch nach Aufruf von Z. 36 im HardFault-Handler. Aber da
ich ja wie gesagt direkt nach dem Reset die Funktion TestJump() aufrufe,
sollten eigentlich bzgl. der Register alles passen.
Eine harte Nuss, dieser Bootloader :-(
Gruß
mabe schrieb:> Wenn ich dich richtig verstehe, würde ich dann zu Beginn von> main() prüfen, ob z.B. ein Software-Reset ausgelöst wurde.> Falls ja, springe ich direkt an dieser Stelle in den> Bootloader.> Der einzige Unterschied zu meinem aktuellen Code wäre dann die> zusätzliche if-Abfrage. Ich kann mir nicht vorstellen, dass das einen> Unterschied macht
Ich auch nicht :( Allerdings: "zu Beginn von main()" könnte theoretisch
zu spät sein. Wissen wir, was vor main() schon alles initialisiert wird?
Ehrliche Frage, weil, HAL-Benutzer wissen das nicht.
Allerdings scheint das Problem wirklich der Call per Pointer zu sein.
Spricht hier jemand ARM-Assembler? Ich versuche mal, die
Maschinenbefehle auszuführen. Anscheinend verwirrt das MSR den Compiler.
Man zieht ihm ja quasi den Stack unter den Füßen weg.
1
0x0800026A 4809 LDR r0,[pc,#36] ; @0x08000290 // steht da
2
// wirklich 0x1ff00000?
3
0x0800026C 6800 LDR r0,[r0,#0x00] // der Bootloader-SP
4
0x0800026E 9001 STR r0,[sp,#0x04] // lokale Variable auf dem
5
0x08000270 9801 LDR r0,[sp,#0x04] // alten Stack?
6
0x08000272 F3808808 MSR MSP,r0 // der neue SP, zeigt ins
7
// Bootloader-RAM
8
0x08000276 9800 LDR r0,[sp,#0x00] // irgendwas aus dem RAM
9
0x08000278 4780 BLX r0 // ein Sprung ins Nirwana
-----------------------------------------------------
mabe schrieb:> 20: //SysTick zurücksetzen, wird u.U. im Bootloader benötigt
nicht nur u.U., die AN2606 behauptet das relativ überzeugend.
> 25: //Alle Interrupts deaktivieren> 26: __enable_irq(); //PRIMASK-Bit setzen
traue nie einem Kommentar, den du nicht selbst gefälscht hast :)
aber entscheidend und gut ist das:
> 0x08000268 B662 CPSIE I
-------------------------------------------------------
ihrseidzuschnellfürmich
pegel schrieb:> Besonders die Antwort im zweiten link beachten.
Das der empty check zuschlagen könnte, ist ein wertvoller Tipp. Aber
laut AN2606 und RM0377 hat der STM32L031 dieses Problem nicht.
Man kann den empty check auch ganz leicht überlisten. Der testet, ob
0x08000000 gelöscht ist. Man kann den MSP auch "zu Fuß" in der crt0.c
initialisieren, dann muss man diese Adresse niemals programmieren. Der
Bootloader glaubt dann immer, dass es kein User-Programm gibt.
Interessanter finde ich diese Hinweise aus dem anderen Link:
1
Solution was to reset USART1 via RCC_APB2RSTR.
Dazu muss man nur 1 Bit auf 1 und wieder auf 0 setzen und dieses USART
sieht wieder aus wie nach einem Reset.
1
insert "__enable_irq();" after your "__ISB();".
Das ist absolut überzeugend. Bei den Cortex-M sind die Interrupts nach
einem Hardware-Reset enabled.
Das system memory auf 0 zu mappen finde ich ähnlich überzeugend, weil
das bei einem Reset mit BOOT0=1 so passiert. Das sind aber einzelne von
vielen Maßnahmen und man weiß nicht, welche bei einem bestimmten Chip
wirklich nötig sind. Für eine BootLoaderUniversalAnsprungFunktion()
sollte man einen echten Reset erzeugen. Das wird auf stackexchange auch
gelegentlich empfohlen.
pegel schrieb:> mabe schrieb:>> Ich verwende den USART2>> Der BL fängt aber mit Test von 1 an?.
Das könnte stören, wenn z.B. auf dem USART1-RX-Pin eine
Wechselspannung gemessen werden soll. Normal kommt da nichts, also
testet er den nächsten Kandidaten, bis er irgendwo etwas empfängt.
Rätselhaft bleibt nur, wie er eine aktive USB-Verbindung erkennt
(betrifft nur andere Chips).
pegel schrieb:> Der BL fängt aber mit Test von 1 an?.
Laut der Bootloader Application Note von ST (AN2606, S. 251) können im
Bootloader beim STM32L031xx/041xx die Schnittstellen USART2 und SPI1
genutzt werden. Von USART1 ist dort keine Rede.
Auf S. 253 findet sich ein Diagramm, in welchem dargestellt ist, wie der
UART die Schnittstelle auswählt. Hier ist immer von USARTx und SPIx die
Rede.
Vielleicht ist das ein Fehler in der Application Note, aber da ich den
Bootloader ja ansprechen kann, wenn ich den PC manuell setze oder den
BOOT0-Pin auf VCC ziehe, würde ich sagen, dass hardwaretechnisch alles
i.O. sein sollte.
Bauform B. schrieb:> Ich auch nicht :( Allerdings: "zu Beginn von main()" könnte theoretisch> zu spät sein. Wissen wir, was vor main() schon alles initialisiert wird?> Ehrliche Frage, weil, HAL-Benutzer wissen das nicht.
Immerhin bin ich nicht der einzige, der sich den Kopf zerbricht und
nicht weiter weiß :D.
Ich verwende keine HAL, sondern nur CMSIS. Ich habe mal mit dem Debugger
nachgeschaut, tatsächlich wird nach einem Reset vor dem Aufruf von
main() die Funktion SystemInit() aufgerufen:
/*!< Reset HSION, HSIDIVEN, HSEON, CSSON and PLLON bits */
15
RCC->CR&=(uint32_t)0xFEF6FFF6U;
16
17
/*!< Reset HSI48ON bit */
18
RCC->CRRCR&=(uint32_t)0xFFFFFFFEU;
19
20
/*!< Reset HSEBYP bit */
21
RCC->CR&=(uint32_t)0xFFFBFFFFU;
22
23
/*!< Reset PLLSRC, PLLMUL[3:0] and PLLDIV[1:0] bits */
24
RCC->CFGR&=(uint32_t)0xFF02FFFFU;
25
26
/*!< Disable all interrupts */
27
RCC->CIER=0x00000000U;
28
29
/* Configure the Vector Table location add offset address ------------------*/
30
#ifdef VECT_TAB_SRAM
31
SCB->VTOR=SRAM_BASE|VECT_TAB_OFFSET;/* Vector Table Relocation in Internal SRAM */
32
#else
33
SCB->VTOR=FLASH_BASE|VECT_TAB_OFFSET;/* Vector Table Relocation in Internal FLASH */
34
#endif
35
}
So wie es aussieht, wird in dieser Funktion jedoch nur das Taktsystem
auf den Reset-Wert gebracht und der Offset für die Vektortabelle
eingestellt. Das scheint also nicht das Problem zu sein.
Bauform B. schrieb:> Allerdings scheint das Problem wirklich der Call per Pointer zu sein.> Spricht hier jemand ARM-Assembler? Ich versuche mal, die> Maschinenbefehle auszuführen. Anscheinend verwirrt das MSR den Compiler.> Man zieht ihm ja quasi den Stack unter den Füßen weg.
Ich kenne mich mit Assembler nicht aus, deshalb habe ich den Code hier
gepostet. Aber dasss das Problem vielleicht mit dem MSR zusammenhängen
könnte, ist ja schon mal eine Spur.
Bauform B. schrieb:>> 25: //Alle Interrupts deaktivieren>> 26: __enable_irq(); //PRIMASK-Bit setzen> traue nie einem Kommentar, den du nicht selbst gefälscht hast :)> aber entscheidend und gut ist das:
Mist, da ist wohl ein Fehler beim Kopieren des Codes passiert :D ich
hatte ja testweise das disable_irq() durch enable_irq() ersetzt und dann
anscheinend diesen Code hierher kopiert... Sorry für die Verwirrung ^^.
Ich habe es aber gerade wieder auf disable_irq() zurückgestellt und noch
mal getestet, geändert hat sich nichts.
Gruß
mabe schrieb:> Auf S. 253 findet sich ein Diagramm, in welchem dargestellt ist, wie der> UART die Schnittstelle auswählt.
Sorry, es soll natürlich ..."wie der Bootloader die Schnittstelle
auswählt." heißen.
mabe schrieb:> Ich habe es aber gerade wieder auf disable_irq() zurückgestellt> und noch mal getestet, geändert hat sich nichts.
Und ich sach noch: tu das nicht.
> pegel schrieb:>> Der BL fängt aber mit Test von 1 an?.> Laut der Bootloader Application Note von ST (AN2606, S. 251)...
Das ist hier tatsächlich nicht das Problem.
> Ich verwende keine HAL, sondern nur CMSIS. Ich habe mal mit dem Debugger> nachgeschaut, tatsächlich wird nach einem Reset vor dem Aufruf von> main() die Funktion SystemInit() aufgerufen:
Ja, sowas meinte ich. Ob das stört, nun ja, YMMV. Auf meinem STM32L031
initialisiere ich alle GPIOs, VTOR und Clock und springe dann -- es
funktioniert trotzdem. Aber das ist eben vom Chip und dem Bootloader und
anscheinend auch vom Compiler abhängig. In diesem Fall gilt ganz klar:
weniger ist mehr.
Man kann auf den Cortex-M ein hello_world zu 100% in C schreiben --
nett, aber manche Aufgaben funktionieren mit Assembler doch etwas
einfacher:
Jetzt wird es ganz irre. Kurz
-l053-disco herausgesucht
-kann den App endlos start bestätigen
-__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); in meiner Funktion 2 mal
hintereinander aufgerufen
-board macht keine endlosschleife mehr!
ES FUNKTIONIERT!!! :-)
Der Code von Bauform B. hat den Durchbruch gebracht. Folgende Funktion
habe ich jetzt implementiert und damit klappt der Sprung in den
Bootloader:
SYSCFG->CFGR1|=SYSCFG_CFGR1_MEM_MODE_0;//System Memory auf Adresse 0 remappen
19
20
__asm__volatile(
21
" movs r0, #0\n"// Auf 0x0 befindet sich jetzt system memory
22
" ldr r1, [r0, #4]\n"// Startadresse des Bootloaders
23
" ldr r0, [r0, #0]\n"// sein initial Stackpointer
24
" msr msp, r0\n"//
25
" mov pc, r1\n"// dies ist ein jump aka branch
26
);
27
28
while(1);
29
}
Zwei Dinge habe ich angepasst:
1. Ich habe die Zeilen für das Mapping des System Memory eingefügt. Dem
aufmerksamen Leser wird auffallen, dass ich anstatt
SYSCFG->CFGR1|=SYSCFG_CFGR1_MEM_MODE_0;//System Memory auf Adresse 0 remappen
Die Variante 1 sollte theoretisch auch funktionieren, in der Praxis
allerdings nicht. Wo hier jetzt der Fehler liegt, kann ich nicht sagen.
2. Ich habe am Ende der Funktion ein while(1) eingefügt. Der Grund ist,
dass Keil ansonsten folgende Warnung generiert:
1
warning: function declared 'noreturn' should not return [-Winvalid-noreturn]
Der Compiler bermerkt anscheinend nicht, dass durch den Sprung das
fehlende return hinfällig ist. Das while(1) stört nicht und dann
verschwindet auch die Warnung.
Jetzt muss die Funktion nur noch in meinem richtigen Programm
funktionieren. Sobald der µC per UART ein Codewort empfängt, soll er in
den Bootloader springen, damit er nahtlos progammiert werden kann.
Sobald ich das ausprobiert habe, melde ich mich noch mal und gebe
Bescheid, ob hier auch alles funktioniert.
pegel schrieb:> Jetzt wird es ganz irre. Kurz> -l053-disco herausgesucht> -kann den App endlos start bestätigen> -__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); in meiner Funktion 2 mal> hintereinander aufgerufen> -board macht keine endlosschleife mehr!
Das habe ich der Vollständigkeit halber auch noch ausprobiert. Egal, ob
ich den System Memory zwei- oder dreimal remappe - die Endlosschleife
bleibt :-(.
Anscheinend gibt es hier wirklich drastische Unterschiede zwischen den
Chips und den verschiedenen Bootloader-Versionen. Wie gesagt, der
ursprüngliche Code aus Post 1 funktioniert auf dem G030K6T6 problemlos.
Hier sollte ST mal eine Application Note erstellen und ein paar mehr
Infos bereitstellen - die ganzen Workarounds mit einem Kondensator am
BOOT0-Pin oder "Magic Numbers" im RAM sind doch alle nicht wirklich
optimal...
Auf jeden Fall danke für eure Hilfe und euch noch einen schönen Feiertag
:-). Wie gesagt, ich melde mich noch mal und gebe Bescheid, ob es im
Hauptprogramm auch funktioniert, sobald ich es getestet habe.
mabe schrieb:> #define BOOTLOADER_ADR 0x1FF00000
Dank pegels Idee brauchen wir das nicht mehr. Nach dem remapping stehen
Stackpointer und Startadresse immer auf 0 und 4, auf allen Chips, mit
allen Bootloader-Versionen, sogar beim H7 (da ist die Startadresse !=
dem Anfang des system memory).
>
SYSCFG->CFGR1|=SYSCFG_CFGR1_MEM_MODE_0;//System Memory auf Adresse 0 remappen
> Die Variante 1 sollte theoretisch auch funktionieren, in der Praxis> allerdings nicht. Wo hier jetzt der Fehler liegt, kann ich nicht sagen.
Sollte sie allerdings; wahrscheinlich eine unglückliche Verkettung mit
einem anderen Fehler. So kurz nach einem Reset sollte Bits löschen sogar
unnötig sein.
> Ich habe am Ende der Funktion ein while(1) eingefügt.
Ja, besser ist das; ich hab's vergessen - vor lauter Begeisterung, dass
es funktioniert. Mit einem aktuellen gcc kann man noch 2 Byte sparen,
statt while(1)
1
__builtin_unreachable();
> Der Compiler bermerkt anscheinend nicht, dass durch den Sprung das> fehlende return hinfällig ist.
Das soll er auch nicht. Aber es ist garnicht so sicher. Der gcc
optimiert u.U. auch solche asm Statements, man kann ihm genau erklären,
was da ablaufen soll. Nur sind mir die Regeln dafür viel zu kompliziert.
Zum Beispiel gehört das volatile da eigentlich nicht hin.
Ich habe die Funktion jetzt auch im Hauptprogramm getestet. Sobald über
den USART2 das Zeichen 'B' empfangen wird, wechselt der Mikrocontroller
in den Bootloader.
Ich musste zusätzlich noch zwei Codezeilen einfügen, die den USART2 in
den Reset-State versetzen. Ansonsten kann ich mich nach dem Wechsel in
den Bootloader nicht mit dem Controller verbinden.
Hier der finale Code:
SYSCFG->CFGR1|=SYSCFG_CFGR1_MEM_MODE_0;//System Memory auf Adresse 0 remappen
9
10
//Reset USART2
11
RCC->APB1RSTR|=RCC_APB1RSTR_USART2RST;
12
RCC->APB1RSTR&=~RCC_APB1RSTR_USART2RST;
13
14
//SysTick zurücksetzen, wird im Bootloader benötigt
15
SysTick->CTRL=0;
16
SysTick->LOAD=0;
17
SysTick->VAL=0;
18
19
__asm__volatile(
20
" movs r0, #0\n"// Auf 0x0 befindet sich jetzt system memory
21
" ldr r1, [r0, #4]\n"// Startadresse des Bootloaders
22
" ldr r0, [r0, #0]\n"// sein initial Stackpointer
23
" msr msp, r0\n"//
24
" mov pc, r1\n"// dies ist ein jump aka branch
25
);
26
27
__builtin_unreachable();
28
}
Bei meinen Tests hat es auch funktioniert, wenn ich das Zurücksetzen des
SysTicks und __disable_irq() weggelassen habe. Trotzdem habe ich die
Zeilen beibehalten, damit kein Interrupt bei der Ausführung der Funktion
dazwischenfunkt und der SysTick auch wirklich im Reset-State ist.
Den Hinweis von Bauform B...
Bauform B. schrieb:> Mit einem aktuellen gcc kann man noch 2 Byte sparen,> statt while(1) __builtin_unreachable ();
... habe ich ebenfalls implementiert, jetzt meldet Keil ebenfalls keine
Warnung mehr. Super!
Nochmals danke an euch und ein schönes Wochenende. :-)