内心OS:emmm,画图画得好丑,详细讲述方案三
硬件环境:STM32F103RC为例
代码工具:STM32CubeMX
编译环境:Keil5
目录
两份代码(Boot loader和App)同一种STM32CubeMX配置:
有需要的话可评论,私发方案几的Boot loader代码以及App代码,均已经过测试
方案一:运行的App位置固定,使用内部Flash
个人不推荐使用这种方案(至于为什么,emm~,感觉上)
注:只要Flash足够大,可以放置多个AppBuff,但是得提前规划好各个App的首地址和大小
流程:Boot loader检测Flag标志是否更新App,若需要更新App,将指定的AppBuff覆盖AppRun,然后跳转运行AppRun
优缺点(个人觉得):
优点:App修改小,只修改VECT_TAB_OFFSET参数即可,工作都在Boot loader上操作
缺点:1.如果只有一个AppBuff,则说明无后路可言,不能进行版本回滚
2.更新情况下,需要在Boot loader内进行数据搬运,需要一定的时间(虽然时间很短)
3.内部Flash读写次数有限,操作次数过多
4.App大小受限,App.bin文件大小不能超过规定的App大小
方案二:运行App位置固定,借助外部Flash
流程:Boot loader读取外部/内部Flag标志是否更新App,若需要更新App,将指定存储在外部Flash的AppBuff读取覆盖App,然后跳转运行App
优缺点(还是个人觉得):
优点:1.内部Flash擦写次数少
2.除去Boot loader外,其余空间可以都是App的
缺点:1.App和Boot loader的工程量都不小,应为涉及到外部Flash(例如SPI的W25Qx)
2.更新情况下,在Boot loader操作的时间包括擦除内部Flash、读取外部Flash以及写内部Flash三部分操作
方案三:运行App可选,使用内部Flash
注:只要Flash足够大,你想放几个App都行,但是得提前规划好各个App的首地址和大小
流程:Boot loader检测Flag标志选择性跳转App,更新时可以选择性擦除更换App
优缺点(依旧是个人觉得):
优点:1.Boot loader工程小
2.内部Flash擦写次数少
3.无需搬运擦写,只需实现跳转
缺点:1.App上需要改动,且各个App大小及地址等必要信息需要与Boot loader一一对应
2.App大小受限,App.bin文件大小不能超过规定的App大小,且App的个数与大小成反比
这里就只使用App1和App2为例子,且只实现跳转,擦除和更换只是对Flash的读写操作而已
两份代码(Boot loader和App)同一种STM32CubeMX配置:
RCC配置:只修改High Speed Clock (HSE)为Crystal/Ceramic Resonator,其他保持默认
SYS配置:配置Debug为Serial Wire
串口配置(UART1):Mode为Asynchronous
Boot loader代码:
// Bootloader.h
#define FLASH_ONEPAGE_SIZE (0x800) // 2k bytes
#define USER_BASE_ADDR (0x08000000)
#define USER_BOOT_SIZE (0x00005000) // bootloader program size
#define USER_APP_PAGE (0x32) // 50 * 2k = 100k
#define USER_APP_SIZE (USER_APP_PAGE * FLASH_ONEPAGE_SIZE) // 50 * 2k = 100k
#define USER_APP1_ADDR (USER_BASE_ADDR + USER_BOOT_SIZE) // The first address of the user program
#define USER_APP2_ADDR (USER_APP1_ADDR + USER_APP_SIZE) // The second address of the user program
#define USER_FLASH_SIZE (0x00040000)
#define USER_INFO_ADDR (USER_BASE_ADDR + USER_FLASH_SIZE - FLASH_ONEPAGE_SIZE)
#define CLOSE_ALL_INT() __set_PRIMASK(0x01) // Turn off all interrupts
/* Exported types ------------------------------------------------------------*/
typedef void (*pFunc)(); // Defines a function pointer
void user_LoadApp(void); // jump functions
// Bootloader.c
/**
* @brief Redirect pointer
* @param uint32_t Map addresses
* @retval void
*/
__asm void user_MSRMSP(uint32_t addr)
{
MSR MSP, r0
BX r14;
}
/**
* @brief Launch the app
* @param void
* @retval void
*/
void user_LoadApp(void)
{
pFunc jumptoapp;
uint32_t info = *(uint32_t *)USER_INFO_ADDR;
uint32_t addr = ((info == 0x00000000) || (info == 0xFFFFFFFF)) ? USER_APP1_ADDR : USER_APP2_ADDR;
if(((*(uint32_t *)addr) & 0x2FFF8000) == 0x20000000)
{
user_MSRMSP(addr);
jumptoapp = (pFunc)*(volatile uint32_t *)(addr + 0x04);
__set_MSP(*(volatile uint32_t *)addr);
log_info("Ready to jump to the user program whose Flash is 0x%08X", addr);
CLOSE_ALL_INT();
SCB->VTOR = addr | (0x00 & (uint32_t)0xFFFFFE00);
jumptoapp();
}
else
{
uint32_t data = (*(uint32_t *)addr);
log_err("The user program with address 0x%08X could not be found", data);
}
}
// main.c里面int main(void)函数
// while(1)前添加
user_LoadApp(); // 只添加此函数即可
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
App的Keil测试设置:
App代码段:
// main.h(此处需要添加与Bootloader.h等同的宏定义)
// 唯一的差别就是最后一行的开关中断
#define FLASH_ONEPAGE_SIZE (0x800) // 2k bytes
#define USER_BASE_ADDR (0x08000000)
#define USER_BOOT_SIZE (0x00005000) // bootloader program size
#define USER_APP_PAGE (0x32) // 50 * 2k = 100k
#define USER_APP_SIZE (USER_APP_PAGE * FLASH_ONEPAGE_SIZE) // 50 * 2k = 100k
#define USER_APP1_ADDR (USER_BASE_ADDR + USER_BOOT_SIZE) // The first address of the user program
#define USER_APP2_ADDR (USER_APP1_ADDR + USER_APP_SIZE) // The second address of the user program
#define USER_FLASH_SIZE (0x00040000)
#define USER_INFO_ADDR (USER_BASE_ADDR + USER_FLASH_SIZE - FLASH_ONEPAGE_SIZE)
#define OPEN_ALL_INT() __set_PRIMASK(0x00) // Turn off all interrupts
// main.c->int main(void)内HAL_Init();前
uint32_t info = *(uint32_t *)USER_INFO_ADDR;
uint32_t addr = ((info == 0x00000000) || (info == 0xFFFFFFFF)) ? USER_APP1_ADDR : USER_APP2_ADDR;
SCB->VTOR = addr | (0x00 & (uint32_t)0xFFFFFE00);
OPEN_ALL_INT();