Forum: Mikrocontroller und Digitale Elektronik STM32L031K6 Sprung in Bootloader aus Applikation funktioniert nicht


von mabe (Gast)


Lesenswert?

Hallo zusammen,

ich habe folgendendes Programm:
1
#define BOOTLOADER_ADR 0x1FFF0000
2
3
#include <stm32g0xx.h>
4
5
void TestJump(void);
6
7
int main(void)
8
{
9
  TestJump();
10
  
11
  while(1);
12
}
13
14
void TestJump(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.
18
  void (*JumpToSystemROM)(void) = (void (*)(void))(*((uint32_t *)(BOOTLOADER_ADR + 4)));
19
20
  //SysTick zurücksetzen, wird u.U. im Bootloader benötigt
21
  SysTick->CTRL = 0;
22
  SysTick->LOAD = 0;
23
  SysTick->VAL = 0;
24
25
  //Alle Interrupts deaktivieren
26
  __disable_irq(); //PRIMASK-Bit setzen (nur NMI/HardFault Interrupt möglich)
27
28
  //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ß

von pegel (Gast)


Lesenswert?

Scheint soweit zu passen.
Hat der L0 womöglich andere Anforderungen bezüglich SysTick Registern?

Lass doch das Rücksetzen einfach mal weg.

von pegel (Gast)


Lesenswert?

Meine BL Jump Funktion in verschiedene USB BL kümmert sich selbst um 
alles Nötige und hat bei allen µC funktioniert:
1
void JumpToBootloader(void) {
2
    void (*SysMemBootJump)(void);
3
4
    __HAL_RCC_SYSCFG_CLK_ENABLE();           //make sure syscfg clocked
5
    __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();  //remap system memory to address 0x0000000
6
    SCB->VTOR = 0;                           //set vector table offset to 0
7
8
    SysMemBootJump = (void (*)(void))(*((uint32_t*)(4)));
9
    __set_MSP(*(uint32_t*)(0));
10
11
    SysMemBootJump();
12
}

Ist das möglicherweise die BootLoaderUniversalAnsprungFunktion? ;)

von mabe (Gast)


Angehängte Dateien:

Lesenswert?

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.
1
void JumpToBootloader(void)
2
{
3
  void (*SysMemBootJump)(void);
4
5
  RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Takt aktivieren
6
  SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_MEM_MODE; //Bits löschen
7
  SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE_0; //System Memory auf Adresse 0 remappen
8
  SCB->VTOR = 0; //set vector table offset to 0
9
10
  SysMemBootJump = (void (*)(void))(*((uint32_t*)(4)));
11
12
  __set_MSP(*(uint32_t*)(0));
13
14
  SysMemBootJump();
15
}

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.

von pegel (Gast)


Lesenswert?

Leider fallen mir gleich die Augen zu.

Was hat es mit den anderen 3 Bit des SYSCFG_CFGR1 Registers auf sich?

von Bauform B. (bauformb)


Lesenswert?

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.

von pegel (Gast)


Lesenswert?


von pegel (Gast)


Lesenswert?

Das mit dem USART1 aus link1 ist allerdings auch Möglich und lässt sich 
schnell testen.

von mabe (Gast)



Lesenswert?

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:
1
    18:   void (*JumpToSystemROM)(void) = (void (*)(void))(*((uint32_t *)(BOOTLOADER_ADR + 4))); 
2
    19:  
3
    20:   //SysTick zurücksetzen, wird u.U. im Bootloader benötigt 
4
0x08000256 6800      LDR      r0,[r0,#0x00]
5
0x08000258 9000      STR      r0,[sp,#0x00]
6
0x0800025A 480A      LDR      r0,[pc,#40]  ; @0x08000284
7
0x0800025C 2100      MOVS     r1,#0x00
8
    21:   SysTick->CTRL = 0; 
9
0x0800025E 6001      STR      r1,[r0,#0x00]
10
0x08000260 4809      LDR      r0,[pc,#36]  ; @0x08000288
11
    22:   SysTick->LOAD = 0; 
12
0x08000262 6001      STR      r1,[r0,#0x00]
13
0x08000264 4809      LDR      r0,[pc,#36]  ; @0x0800028C
14
    23:   SysTick->VAL = 0; 
15
    24:  
16
    25:   //Alle Interrupts deaktivieren 
17
0x08000266 6001      STR      r1,[r0,#0x00]
18
    26:   __enable_irq(); //PRIMASK-Bit setzen (nur NMI/HardFault Interrupt möglich) 
19
    27:  
20
    28:   //Das erste Datenwort im System ROM (= Speicherbereich des Bootloaders) enthält die Adresse, 
21
    29:   //mit welcher der Stackpointer geladen wird. Dieser Wert wird nun dem Stackpointer zugewiesen. 
22
0x08000268 B662      CPSIE    I
23
0x0800026A 4809      LDR      r0,[pc,#36]  ; @0x08000290
24
    30:   __set_MSP(*(uint32_t *)BOOTLOADER_ADR); 
25
    31:  
26
0x0800026C 6800      LDR      r0,[r0,#0x00]
27
0x0800026E 9001      STR      r0,[sp,#0x04]
28
   348:   __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); 
29
0x08000270 9801      LDR      r0,[sp,#0x04]
30
0x08000272 F3808808  MSR      MSP,r0
31
    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
void TestJump(void);
6
7
int main(void)
8
{
9
  TestJump();
10
11
  while(1);
12
}
13
14
void TestJump(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.
18
  void (*JumpToSystemROM)(void) = (void (*)(void))(*((uint32_t *)(BOOTLOADER_ADR + 4)));
19
20
  //USART2 auf Reset-State zurücksetzen
21
  RCC->APB1RSTR |= RCC_APB1RSTR_USART2RST;
22
  RCC->APB1RSTR &= ~RCC_APB1RSTR_USART2RST;
23
  
24
  //SysTick zurücksetzen, wird u.U. im Bootloader benötigt
25
  SysTick->CTRL = 0;
26
  SysTick->LOAD = 0;
27
  SysTick->VAL = 0;
28
29
  //Alle Interrupts deaktivieren
30
  __disable_irq(); //PRIMASK-Bit setzen (nur NMI/HardFault Interrupt möglich)
31
32
  //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ß

von pegel (Gast)


Lesenswert?

mabe schrieb:
> Ich verwende den USART2

Der BL fängt aber mit Test von 1 an?.

von pegel (Gast)


Lesenswert?

Auch wenn nix dran hängt.

von Bauform B. (bauformb)


Lesenswert?

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.

von Bauform B. (bauformb)


Lesenswert?

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).

von mabe (Gast)


Lesenswert?

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:
1
/**
2
  * @brief  Setup the microcontroller system.
3
  * @param  None
4
  * @retval None
5
  */
6
void SystemInit (void)
7
{
8
/*!< Set MSION bit */
9
  RCC->CR |= (uint32_t)0x00000100U;
10
11
  /*!< Reset SW[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], MCOSEL[2:0] and MCOPRE[2:0] bits */
12
  RCC->CFGR &= (uint32_t) 0x88FF400CU;
13
14
  /*!< 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ß

von mabe (Gast)


Lesenswert?

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.

von Bauform B. (bauformb)


Lesenswert?

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:
1
void __attribute__ ((noreturn))
2
BootLoaderUniversalAnsprungFunktion (void) // ™pegel
3
{
4
   SYSCFG->MEM_MODE = 1;        // Dies (und das #include) muss je nach
5
                                // Entwicklungsumgebung passend formuliert
6
                                // werden. Ausserdem ist das SYSCFG-Register
7
                                // Chip-abhängig, deshalb machen wir das
8
                                // nicht per Assembler.
9
   __asm__ volatile (
10
      " movs  r0, #0\n"         // Auf 0x0 befindet sich jetzt system memory
11
      " ldr   r1, [r0, #4]\n"   // Startadresse des Bootloaders
12
      " ldr   r0, [r0, #0]\n"   // sein initial Stackpointer
13
      " msr   msp, r0\n"        //
14
      " mov   pc, r1\n"         // dies ist ein jump aka branch
15
   );
16
}

: Bearbeitet durch User
von pegel (Gast)


Lesenswert?

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!

von mabe (Gast)


Lesenswert?

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:
1
#define BOOTLOADER_ADR 0x1FF00000
2
3
#include <stm32l0xx.h>
4
5
void __attribute__ ((noreturn))
6
BootLoaderUniversalAnsprungFunktion(void);
7
8
int main(void)
9
{
10
  BootLoaderUniversalAnsprungFunktion();
11
}
12
13
void __attribute__ ((noreturn))
14
BootLoaderUniversalAnsprungFunktion(void) // ™pegel
15
{
16
  RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Takt aktivieren
17
  SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_MEM_MODE_1; //Bit löschen
18
  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
1
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Takt aktivieren
2
SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_MEM_MODE; //Bits löschen
3
SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE_0; //System Memory auf Adresse 0 remappen

,wie in den vorherigen Posts, jetzt diese Zeilen verwende:
1
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Takt aktivieren
2
SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_MEM_MODE_1; //Bit löschen
3
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.

von Bauform B. (bauformb)


Lesenswert?

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).

>
1
SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_MEM_MODE; //Bits löschen
2
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.

: Bearbeitet durch User
von pegel (Gast)


Lesenswert?

Schön das es so funktioniert.

BootLoaderUniversalAnsprungFunktion(void) darf gern frei verwendet 
werden, wenn sie besser als die Originale ist. ;)

von mabe (Gast)


Lesenswert?

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:
1
void __attribute__ ((noreturn))
2
BootLoaderUniversalAnsprungFunktion(void) // ™pegel
3
{
4
  __disable_irq(); //Interrupts deaktivieren, PRIMASK-Bit setzen
5
  
6
  RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Takt aktivieren
7
  SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_MEM_MODE_1; //Bit löschen
8
  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. :-)

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.