背景
在给STM32单片机做IAP(Flash分为两部分:BootLoader+APP)升级功能时,有如下需求- -
- 系统上电默认先进入BootLoader中,BootLoader会停留几百毫秒;
- 停留期间,系统会判断是否具有IAP请求,如果有则进行IAP升级,否则超时后跳转到APP中即可;
这样得逻辑很简单,但是有一个缺陷,就是不管上电还是复位后始终必须停留几百毫秒后才能跳转至APP,对于是实现要求高的、跑飞后需要立刻进行复位重启的系统,是不合理的。因此可以进行一个优化:
- 在需求2的基础上,先判断复位类型是上电店复位还是软件复位,如果是软件复位,直接跳转至APP,不停留。
关键代码实现
以stm32f4为例,修改system_stm32f4xx.c中的SystemInit函数
void SystemInit(void)
{
/* USER CODE BEGIN 1 */
// 判断 app是否存在标志,app存在时跳转的前提
uint8_t isAppExistFalg = *(__IO uint8_t*)(标记app是否存在的地址));
// 判断是否为软件复位,软件复位就直接跳转到app中
if( (1 == isAppExistFalg) && ((RCC->CSR) & 0x10000000) )
{
__HAL_RCC_CLEAR_RESET_FLAGS();
extern void jump2aplication(void);
jump2aplication();
}
/* USER CODE END 1 */
...
省略
...
// 注意:编译APP时,宏VECT_TAB_OFFSET要设定正确的偏移量,设定方法见文末
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
其中 函数jump2aplication()实现为:
typedef void (* pFunc)(void);
void jump2aplication(void)
{
// 确定应用程序入口地址
pFunc jump_to_application = (pFunc)(*(__IO uint32_t*)(APP的起始地址值 + 4));
// 检查应用程序堆栈地址是否合法
if(((*(__IO uint32_t *)APP的起始地址值) & 0x2FF80000) == 0x20000000)
{
// 初始化MSP
__set_MSP(*(__IO UINT32 *)APP的起始地址值);
// 跳转到应用程序
jump_to_application();
}
}
通过以上的修改,就可以实现BootLoader到APP的跳转。
更高级功能
现在,仔细思考后发现:如果想升级,就只能将设备断电,然后在上电的几百毫秒内握手升级(不然超时跳转到APP中)。能不能做到让APP主动跳转到BootLoader里面,这样不就可以随时进行升级?当然可以。 直接上代码:
typedef void (* pFunc)(void);
void jump2bootloader(void)
{
// 确定应用程序入口地址, FLASH_BASE=0x08000000,即stm32代码默认起始地址
pFunc jump_to_bootloader = (pFunc)(*(__IO uint32_t*)(FLASH_BASE + 4));
// 检查应用程序堆栈地址是否合法
if(((*(__IO uint32_t *)FLASH_BASE ) & 0x2FF80000) == 0x20000000)
{
// 是所有设备的设置恢复到初始状态,否则跳转后可能产生硬错误
__disable_irq();
HAL_RCC_DeInit();
// 初始化MSP
__set_MSP(*(__IO UINT32 *)FLASH_BASE );
// 跳转到应用程序
jump_to_bootloader();
}
}
注意:APP中一般会对外设、始终及中断做一系列配置,跳转至BootLoader后,并未经过复位操作,所以必须要调用HAL_RCC_DeInit()将所有外设和始终配置恢复初始化状态。我初次应用时就没有调用HAL_RCC_DeInit(),导致在main函数的 void SystemClock_Config(void)函数中进入错误死循环。至于APP,跳转至APP一般在复位或上电时刻,所有不执行HAL_RCC_DeInit()也无所谓。
参考
|