仓库地址:https://github.com/loogg/agile_upgrade_mcu_demos
一般 Bootloader
实现的逻辑如下:

这种方式适合于简单的裸机程序或可控的 OS 程序(即所有外设硬件都可把控),在准备环境的时候将其全部关闭。
但对于一些复杂的或者 OS 中轮子已造好的程序,有一些因素不花时间研究无法把控,在准备环境时很可能就会遗漏一些未关闭导致出各种各样的问题。
这里提供一种 万能 方法:
- 利用芯片中的不受软件复位影响的可供用户使用的寄存器 (如 STM32 中的备份寄存器)。
- 在需要跳入 APP 运行时将该寄存器赋值然后软件复位。
- 在 OS 还没初始化时判断该寄存器值,如果需要跳转只需要简单的准备环境即可跳转。

该方法可以使 Bootloader
就作为一个 OS 应用程序开发,需要跳转的时候就操作一下寄存器并软件复位即可。
该仓库下所有的 Bootloader
例子均使用此方法。
以正点原子探索者开发板的 STM32F4
为例,将 system_stm32f4xx.c
文件的 SystemInit
函数修改:
void boot_start_application(void);
void SystemInit(void)
{
boot_start_application();
...
}
boot_start_application
的实现为:
typedef void (*boot_app_func)(void);
void boot_start_application(void) {
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
RTC_HandleTypeDef RTC_Handler = {0};
RTC_Handler.Instance = RTC;
uint32_t bkp_data = HAL_RTCEx_BKUPRead(&RTC_Handler, BOOT_BKP);
HAL_RTCEx_BKUPWrite(&RTC_Handler, BOOT_BKP, 0);
if (bkp_data != 0xA5A5) return;
boot_app_func app_func = NULL;
uint32_t app_addr = BOOT_APP_ADDR;
if (((*(__IO uint32_t *)(app_addr + 4)) & 0xff000000) != 0x08000000) return;
/* 栈顶地址在 128K RAM 间 */
if (((*(__IO uint32_t *)app_addr) - 0x20000000) >= (STM32_SRAM_SIZE * 1024)) return;
app_func = (boot_app_func) * (__IO uint32_t *)(app_addr + 4);
/* Configure main stack */
__set_MSP(*(__IO uint32_t *)app_addr);
/* jump to application */
app_func();
}
设置寄存器并软件复位的实现为:
static void boot_app_enable(void) {
__disable_irq();
RTC_HandleTypeDef RTC_Handler = {0};
RTC_Handler.Instance = RTC;
HAL_RTCEx_BKUPWrite(&RTC_Handler, BOOT_BKP, 0xA5A5);
HAL_NVIC_SystemReset();
}
写的很好啊 谢谢讲解!
很好,新思路
跟在flash上写标志位有什么不同吗
新思路
备份区,断电应该是数据会丢失,但是复位不影响,确实是一个很好的思路,感谢分享
挺好的思路。
思路很好
👍
👍