使用 STM32F042K6 从用户代码跳转到系统引导加载程序
Jump to System Bootloader from User Code with STM32F042K6
编辑:
为了开发者的解决方案,我设法使用 STM32F042K6 从用户代码中跳转系统引导加载程序。
boot.h
/* boot.h */
#include "stm32f0xx_hal.h"
#if defined(STM32F042x6) || defined(STM32F048xx)
#define SYSTEM_MEMORY_ADDRESS 0x1FFFC400
#elif defined(STM32F070xB)
#define SYSTEM_MEMORY_ADDRESS 0x1FFFC800
#elif defined(STM32F070x6)
#define SYSTEM_MEMORY_ADDRESS 0x1FFFC400
#endif
HAL_StatusTypeDef SystemBootloaderJump(void);
boot.c
/* boot.c */
#include "boot.h"
/* I just re-write this code -> https://electronics.stackexchange.com/questions/312303/stm32f091-jump-to-bootloader-from-application */
HAL_StatusTypeDef SystemBootloaderJump(void)
{
typedef void (*pFunction)(void);
pFunction JumpToApplication;
__HAL_RCC_USART1_FORCE_RESET();
HAL_Delay(5);
__HAL_RCC_USART1_RELEASE_RESET();
HAL_Delay(5);
HAL_RCC_DeInit();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/**
* Step: Disable all interrupts
*/
__disable_irq();
/* ARM Cortex-M Programming Guide to Memory Barrier Instructions.*/
__DSB();
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
/* Remap is bot visible at once. Execute some unrelated command! */
__DSB();
__ISB();
/** Get option bytes.
* More info at -> RM0091 Reference manual STM32F0x1/STM32F0x2/STM32F0x8 advanced ARM®-based 32-bit MCUs
* 4 Option byte
* There are up to 8 option bytes. They are configured by the end user depending on the
* application requirements.
* ...
* ...
* 4.1 Option byte description
* 4.1.1 User and read protection option byte
* Flash memory address: 0x1FFF F800
* ...
* Bits 23:16 USER: User option byte (stored in FLASH_OBR[15:8])
* Bit 23: BOOT_SEL
* 0: BOOT0 signal is defined by nBOOT0 option bit
* 1: BOOT0 signal is defined by BOOT0 pin value (legacy mode)
* Available on STM32F04x and STM32F09x devices only. Considered as “1” on other devices.
* ...
* ...
* Bit 19: nBOOT0
* When BOOT_SEL is cleared, nBOOT0 bit defines BOOT0 signal value used to select the
* device boot mode. Refer to Section 2.5: Boot configuration for more details.
* Available on STM32F04x and STM32F09x devices only.
*/
FLASH_OBProgramInitTypeDef pOBInit;
/* Get the Option byte configuration */
HAL_FLASHEx_OBGetConfig(&pOBInit);
/* BOOT_SEL = 0 */
pOBInit.USERConfig &= ~(OB_BOOT_SEL_SET);
/* nBOOT0=1 */
pOBInit.USERConfig |= OB_BOOT0_SET;
/** HAL_FLASHEx_OBProgram && HAL_FLASHEx_OBErase
* HAL_FLASH_OB_Unlock() should be called before to unlock the options bytes
*/
if(HAL_FLASH_OB_Unlock() != HAL_OK)
{
return HAL_ERROR;
}
/*We need to erase option bytes before write. */
if(HAL_FLASHEx_OBErase() != HAL_OK)
{
return HAL_ERROR;
}
/* Write changed option bytes */
if(HAL_FLASHEx_OBProgram(&pOBInit))
{
return HAL_ERROR;
}
JumpToApplication = (void (*)(void)) (*((uint32_t *) ((SYSTEM_MEMORY_ADDRESS + 4))));
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) SYSTEM_MEMORY_ADDRESS);
JumpToApplication();
return HAL_OK;
}
擦除闪存页面后如何重置STM32微控制器?
我的目标是使用 STM32F042K6 微控制器从用户闪存中跳转系统引导加载程序。 但是在 AN2606 应用笔记中 STM32 微控制器系统内存启动模式 它说;
Due to empty check mechanism present on this product, it is not possible to jump from user
code to system bootloader.
Such jump will result in a jump back to user flash space.
But if the first 4 bytes of User Flash (at 0x0800 0000) are empty at the moment of jump (ie.
erase first sector before jump or execute code from SRAM while Flash is empty), then
system bootloader will be executed when jumped to.
因此,我将除 main() 之外的函数放在 ROM 0x08007C00 - 0x08007FFF(闪存的最后一页)中。在我的主要功能中,我正在调用擦除功能 - 位于我的微控制器闪存的最后一页 - 并擦除第一页。它成功擦除第一页,但之后我无法在软件中重置我的微控制器或无法跳转到系统引导加载程序。微控制器卡在某个地方,但我找不到。但是当我用复位引脚复位微控制器时,它从系统引导程序启动。这意味着如果我能以某种方式重置微控制器,我就会实现我的目标。
我正在打开其他解决方案。我的代码就是这样;
/* main.c located at 0x0800 0000 - 0x0800 7BFF */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM16_Init();
StartBoot();
while(1){}
}
/* boot.c located at 0x0800 7C00 - 0x0800 7FFF */
#define ADDR_FLASH_PAGE_0 ((uint32_t)0x08000000) /* Base @ of Page 0, 1 Kbyte */
void StartBoot(void)
{
if(ErasePage(ADDR_FLASH_PAGE_0) == HAL_OK)
{
HAL_FLASH_Lock();
NVIC_SystemReset();
}
}
HAL_StatusTypeDef ErasePage(uint32_t address)
{
/* Unlock the Flash to enable the flash control register access */
HAL_FLASH_Unlock();
/* Erase the user Flash area */
/* Fill EraseInit structure*/
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = address;
EraseInitStruct.NbPages = 1; /* Erase 1 Page */
/* Note: If an erase operation in Flash memory also concerns data in the data or instruction cache,
you have to make sure that these data are rewritten before they are accessed during code
execution. If this cannot be done safely, it is recommended to flush the caches by setting the
DCRST and ICRST bits in the FLASH_CR register. */
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
{
/*
Error occurred while page erase.
User can add here some code to deal with this error.
PAGEError will contain the faulty page and then to know the code error on this page,
user can call function 'HAL_FLASH_GetError()'
*/
HAL_FLASH_Lock();
return HAL_ERROR;
}
return HAL_OK;
}
那个应用笔记指南有点奇怪。我使用过 STM32F042 系列,特别是 STM32F042K6。我的经验是,与其他 STM32 系列部件相比,唯一奇怪的行为是引导加载程序在 运行 时间强制执行 BOOT_SEL
位,而不仅仅是在芯片从复位启动时。
运行-时间检查 BOOT0
的状态通常会导致它跳回主闪存,即使您手动重新映射内存并像其他人一样跳转到引导加载程序也是如此STM32系列零件。
要解决这个问题,您可以在选项字节中设置 BOOT_SEL=0
和 nBOOT0=1
,这样在启动时,它总是启动到主闪存,如下面的 table 所示:
以这种方式设置选项字节后,您可以采用已经介绍过几次的正常 方法。
(注意:STM32F042 系列引导存储器位于 0x1FFFC400,与 STM32F072 示例中所示的 0x1FFFC800 相对)。
请注意,禁用 BOOT 引脚检查将阻止您使用 BOOT 引脚强制芯片进入引导加载程序。您可能希望添加自己的软件检查以在引导期间尽早读取 BOOT0
引脚并跳转到引导加载程序。
编辑: 为了开发者的解决方案,我设法使用 STM32F042K6 从用户代码中跳转系统引导加载程序。
boot.h
/* boot.h */
#include "stm32f0xx_hal.h"
#if defined(STM32F042x6) || defined(STM32F048xx)
#define SYSTEM_MEMORY_ADDRESS 0x1FFFC400
#elif defined(STM32F070xB)
#define SYSTEM_MEMORY_ADDRESS 0x1FFFC800
#elif defined(STM32F070x6)
#define SYSTEM_MEMORY_ADDRESS 0x1FFFC400
#endif
HAL_StatusTypeDef SystemBootloaderJump(void);
boot.c
/* boot.c */
#include "boot.h"
/* I just re-write this code -> https://electronics.stackexchange.com/questions/312303/stm32f091-jump-to-bootloader-from-application */
HAL_StatusTypeDef SystemBootloaderJump(void)
{
typedef void (*pFunction)(void);
pFunction JumpToApplication;
__HAL_RCC_USART1_FORCE_RESET();
HAL_Delay(5);
__HAL_RCC_USART1_RELEASE_RESET();
HAL_Delay(5);
HAL_RCC_DeInit();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/**
* Step: Disable all interrupts
*/
__disable_irq();
/* ARM Cortex-M Programming Guide to Memory Barrier Instructions.*/
__DSB();
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
/* Remap is bot visible at once. Execute some unrelated command! */
__DSB();
__ISB();
/** Get option bytes.
* More info at -> RM0091 Reference manual STM32F0x1/STM32F0x2/STM32F0x8 advanced ARM®-based 32-bit MCUs
* 4 Option byte
* There are up to 8 option bytes. They are configured by the end user depending on the
* application requirements.
* ...
* ...
* 4.1 Option byte description
* 4.1.1 User and read protection option byte
* Flash memory address: 0x1FFF F800
* ...
* Bits 23:16 USER: User option byte (stored in FLASH_OBR[15:8])
* Bit 23: BOOT_SEL
* 0: BOOT0 signal is defined by nBOOT0 option bit
* 1: BOOT0 signal is defined by BOOT0 pin value (legacy mode)
* Available on STM32F04x and STM32F09x devices only. Considered as “1” on other devices.
* ...
* ...
* Bit 19: nBOOT0
* When BOOT_SEL is cleared, nBOOT0 bit defines BOOT0 signal value used to select the
* device boot mode. Refer to Section 2.5: Boot configuration for more details.
* Available on STM32F04x and STM32F09x devices only.
*/
FLASH_OBProgramInitTypeDef pOBInit;
/* Get the Option byte configuration */
HAL_FLASHEx_OBGetConfig(&pOBInit);
/* BOOT_SEL = 0 */
pOBInit.USERConfig &= ~(OB_BOOT_SEL_SET);
/* nBOOT0=1 */
pOBInit.USERConfig |= OB_BOOT0_SET;
/** HAL_FLASHEx_OBProgram && HAL_FLASHEx_OBErase
* HAL_FLASH_OB_Unlock() should be called before to unlock the options bytes
*/
if(HAL_FLASH_OB_Unlock() != HAL_OK)
{
return HAL_ERROR;
}
/*We need to erase option bytes before write. */
if(HAL_FLASHEx_OBErase() != HAL_OK)
{
return HAL_ERROR;
}
/* Write changed option bytes */
if(HAL_FLASHEx_OBProgram(&pOBInit))
{
return HAL_ERROR;
}
JumpToApplication = (void (*)(void)) (*((uint32_t *) ((SYSTEM_MEMORY_ADDRESS + 4))));
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) SYSTEM_MEMORY_ADDRESS);
JumpToApplication();
return HAL_OK;
}
擦除闪存页面后如何重置STM32微控制器?
我的目标是使用 STM32F042K6 微控制器从用户闪存中跳转系统引导加载程序。 但是在 AN2606 应用笔记中 STM32 微控制器系统内存启动模式 它说;
Due to empty check mechanism present on this product, it is not possible to jump from user code to system bootloader. Such jump will result in a jump back to user flash space. But if the first 4 bytes of User Flash (at 0x0800 0000) are empty at the moment of jump (ie. erase first sector before jump or execute code from SRAM while Flash is empty), then system bootloader will be executed when jumped to.
因此,我将除 main() 之外的函数放在 ROM 0x08007C00 - 0x08007FFF(闪存的最后一页)中。在我的主要功能中,我正在调用擦除功能 - 位于我的微控制器闪存的最后一页 - 并擦除第一页。它成功擦除第一页,但之后我无法在软件中重置我的微控制器或无法跳转到系统引导加载程序。微控制器卡在某个地方,但我找不到。但是当我用复位引脚复位微控制器时,它从系统引导程序启动。这意味着如果我能以某种方式重置微控制器,我就会实现我的目标。
我正在打开其他解决方案。我的代码就是这样;
/* main.c located at 0x0800 0000 - 0x0800 7BFF */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM16_Init();
StartBoot();
while(1){}
}
/* boot.c located at 0x0800 7C00 - 0x0800 7FFF */
#define ADDR_FLASH_PAGE_0 ((uint32_t)0x08000000) /* Base @ of Page 0, 1 Kbyte */
void StartBoot(void)
{
if(ErasePage(ADDR_FLASH_PAGE_0) == HAL_OK)
{
HAL_FLASH_Lock();
NVIC_SystemReset();
}
}
HAL_StatusTypeDef ErasePage(uint32_t address)
{
/* Unlock the Flash to enable the flash control register access */
HAL_FLASH_Unlock();
/* Erase the user Flash area */
/* Fill EraseInit structure*/
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = address;
EraseInitStruct.NbPages = 1; /* Erase 1 Page */
/* Note: If an erase operation in Flash memory also concerns data in the data or instruction cache,
you have to make sure that these data are rewritten before they are accessed during code
execution. If this cannot be done safely, it is recommended to flush the caches by setting the
DCRST and ICRST bits in the FLASH_CR register. */
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
{
/*
Error occurred while page erase.
User can add here some code to deal with this error.
PAGEError will contain the faulty page and then to know the code error on this page,
user can call function 'HAL_FLASH_GetError()'
*/
HAL_FLASH_Lock();
return HAL_ERROR;
}
return HAL_OK;
}
那个应用笔记指南有点奇怪。我使用过 STM32F042 系列,特别是 STM32F042K6。我的经验是,与其他 STM32 系列部件相比,唯一奇怪的行为是引导加载程序在 运行 时间强制执行 BOOT_SEL
位,而不仅仅是在芯片从复位启动时。
运行-时间检查 BOOT0
的状态通常会导致它跳回主闪存,即使您手动重新映射内存并像其他人一样跳转到引导加载程序也是如此STM32系列零件。
要解决这个问题,您可以在选项字节中设置 BOOT_SEL=0
和 nBOOT0=1
,这样在启动时,它总是启动到主闪存,如下面的 table 所示:
以这种方式设置选项字节后,您可以采用已经介绍过几次的正常
(注意:STM32F042 系列引导存储器位于 0x1FFFC400,与 STM32F072 示例中所示的 0x1FFFC800 相对)。
请注意,禁用 BOOT 引脚检查将阻止您使用 BOOT 引脚强制芯片进入引导加载程序。您可能希望添加自己的软件检查以在引导期间尽早读取 BOOT0
引脚并跳转到引导加载程序。