工程项目上有时候需要为设备更新应用程序,如果不预留接口升级的话就需要拆除外壳,太麻烦了。一般会留下升级程序的接口,在需要升级设备的时候,给一个信号使程序进入BOOT区运行,然后进行在线升级。
我们一般把单片机flash空间划分为3块。
1、BOOT: 储存进行在线升级的驱动程序
2、APP : 储存应用程序
3、配置信息 : 储存需要保存的应用数据
以下是BOOT区以及APP区的配置方法。
1、设置BOOT程序的代码占用空间
根据BOOT功能来划分,这里我举个例子分配0x2000的空间给BOOT
2、BOOT区启动后要对APP区的代码进行校验
根据校验的结果来决定是停留在BOOT区等待,还是跳转APP程序运行。
可以把APP程序的校验和存在APP程序区的末尾,在烧录程序的时候烧录进去。在BOOT刚启动的时候,计算APP区代码的校验和进行对比。如果相同,就跳转到APP区,如果不同,就执行BOOT区接下来的程序进行远程升级。
3、APP区如何进入BOOT区
1、更改APP区校验信息
上面我们说到BOOT区启动的时候会校验APP区的代码来决定是否运行APP,所以APP区进入BOOT区之前,要破坏APP区的校验信息。在APP区进行操作,把校验信息改写,取反或者加1都是不错的选择。在BOOT区无法进行远程升级的时候,可以更改校验位回到APP区运行。
2、重启进入BOOT区
在更改APP区校验信息之后,需要重启单片机运行。
STM32的重启代码是:
NVIC_SystemReset();
4、BOOT区在线升级
在确定了需要在线升级之后,就可以用预留的接口,来接收数据进行在线升级了。
我们将接收到的数据,写到APP的flash区内(不要覆盖了BOOT区和配置信息区),数据接收完后,还需要计算APP区内的校验信息并写入。然后重启BOOT区,这时校验信息正确,进入APP区。
5、BOOT区跳转到APP区流程
1、关闭一些可能会干扰到跳转的功能
STM32可以用这个来实现
EXTI_DeInit();
SYSCFG_DeInit();
RCC_DeInit();
2、检查栈顶地址是否合法.
APP_ROM_ADDR 为APP区的起始位置,比如0x08002000
if (((*(uint32_t*)APP_ROM_ADDR) & 0x2FFE0000 ) == 0x20000000)
计算如果不为0x20000000,那就不进行跳转
3、初始化用户程序的堆栈指针
__set_MSP(*(__IO uint32_t*) APP_ROM_BASE_ADDR);
4、执行跳转
首先要获取跳转地址。
APP区的地址存在 APP_ROM_ADDR+4 (例如0x08002004)的位置,且有4个字节,
我们可以这样获取跳转地址以及执行跳转。
typedef void (*pFunction)(void);//定义了一个新类型,该类型是一个函数指针,
pFunction Jump_To_Application; //定义程序地址指针
JumpAddress = *(__IO uint32_t*) (APP_ROM_ADDR + 4);
Jump_To_Application = (pFunction)JumpAddress;
Jump_To_Application();
6、写APP程序所需注意的几个地方
程序并不是进入APP区就可以直接运行了,APP程序需要做一些处理才可以运行。
1、要划分好APP区的存放地址
比如我们设定BOOT区程序大小为0x2000,那么APP区程序就要从0X2000
2、需要重映射中断向量
__IO uint32_t VectorTable[48] __attribute__((at(0x20000000)));
void iRemapIrqVector(void)
{
uint8_t i = 0;
for(i = 0; i < 48; i++)
VectorTable[i] = *(__IO uint32_t*)(APP_ROM_ADDR + (i<<2)); //中断向量是一个指针,每个占4个字节
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //这个一定要有
SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); //设置为RAM启动模式
}
要分配RAM