基本概念
BootLoader是启动程序,主要作用就是做某些初始化动作和升级应用程序。根据升级方案的不同,可以分为带临时存储区域的和不带临时存储区域的。
带临时存储区域的升级流程里面,编程芯片将接收的到的升级文件以数据的形式放进临时区域,升级文件校验通过后,再将升级文件复制到应用程序区域。
不带临时存储区域的升级流程里面,编程芯片将接收的升级文件直接放到应用程序区域,升级文件校验。
两种方案的优缺点对比
带临时存储区域方案优点
1、升级失败的时候,应用程序功能不受影响。
2、升级流程绝大部分可以在应用程序中完成,BootLoader相对较小。
带临时存储区域方案缺点
需要额外的存储空间,耗时较长。
不带临时存储区域方案优点
节省存储空间,升级相对较快。
不带临时存储区域方案缺点
1、升级失败的时候,只能停留在BootLoader中,没有任何应用程序功能。
2、升级流程绝大部分在BootLoader当中完成,需要增加许多逻辑判断,BootLoader相对较大。
设计要点
一般有以下几个设计要点,我们这个项目采用不带临时存储区域方案,适用于存储区域小,要求实时性高的情况。
1、代码跳转:从应用程序跳转到BootLoader,从BootLoader跳转到应用程序。
2、保持上位机在线:该项目使用芯片为MCU,通过SOC芯片将获取到的数据传输下来。
3、升级失败和中途断电处理。
代码跳转
本质上就是在程序A中,将程序B存放的复位向量地址当做函数指针,通过函数指针运行程序B。
这里有个要点,跳转的时候目标地址要+4,这是芯片当中要求的。
举个例子
#define OBJ_ADDRESS 0x00000000//目标跳转到地址
uint32_t *Entry_ADDR = (uint32_t *)(OBJ_ADDRESS + 4);//目标地址要+4
typedef void(*JumpToPtr)(void);//类型定义
JumpToPtr pJumpToAddr;//定义个对象
pJumpToAddr = (void *)(*Entry_ADDR);//装载进函数
pJumpToAddr();
从应用程序跳转到BootLoader前一般需要关中断、清中断使能、清中断优先级、外围设备逆初始化、切换频率为正常工作频率。
保持上位机在线
SOC的电源管理在MCU,通过GPIO控制。这样我们进入BootLoader后,不初始化电源管理相关的GPIO。
升级失败和中途断电处理
这种情况本质上就是:该如何识别出升级未完成,避免跳转到应用程序当中?
我们可以先分析进入BootLoader里面的情况。
进入BootLoader有4种情况
1、从应用程序升级跳转到Boot:停留BootLoader等待升级
2、升级过程失败或升级过程超时:停留BootLoader等待升级
3、正常启动(应用程序正常):跳转应用程序
4、出厂启动(无应用程序):停留BootLoader等待升级
我们一般的方法就是,根据不同的情况,在Flash的特定位置写入特定信息,在需要的时刻读出判断,这个位置我们一般称为标志位。
升级标志位
1、升级状态标志位
2、应用程序校验通过标志位(一般应用程序区域的尾部)
操作标志位的时机
1、在应用程序中接收到升级命令,将升级状态标志位写为“升级开始/未完成”。
2、升级失败或升级过程超时,将升级状态标志位写为“升级失败””。
3、升级过程中擦除Flash,将应用程序校验通过标志位写为“不通过”。
4、升级成功,将升级状态标志位写为“升级完成”,应用程序校验通过标志位写为“通过”。
开发过程遇到问题
MCU异常复位
我们当时有两个项目同时进行,一个项目有复位问题,另一个项目没有复位问题,于是将两个项目的代码进行对比。经过排查,他们的区别在于BootLoader的Base代码里面底层配置了看门狗和定时器,在定时器里面喂狗。
定时器的问题是把也就是这个地方勾选上了。
通过芯片手册查到,这是使能了个外部触发的功能。
接口源代码如下
#define FTM_EXTTRIG_INITTRIGEN_MASK 0x40u
#define FTM_EXTTRIG_INITTRIGEN_SHIFT 6u
#define FTM_EXTTRIG_INITTRIGEN_WIDTH 1u
#define FTM_EXTTRIG_INITTRIGEN(x) (((uint32_t)(((uint32_t)(x))<<FTM_EXTTRIG_INITTRIGEN_SHIFT))&FTM_EXTTRIG_INITTRIGEN_MASK)
/*FTM external trigger */
/*!
* @brief Enables or disables the generation of the trigger when the FTM counter is equal
* to the CNTIN register.
*
* @param[in] ftmBase The FTM base address pointer
* @param[in] enable State of initialization trigger
* - true : To enable
* - false: To disable
*/
static inline void FTM_DRV_SetInitTriggerCmd(FTM_Type * const ftmBase,
bool enable)
{
ftmBase->EXTTRIG = (ftmBase->EXTTRIG & ~FTM_EXTTRIG_INITTRIGEN_MASK) | FTM_EXTTRIG_INITTRIGEN(enable);
}
其实就是这一位,使能的话,当定时器的数值等于CNTIN的数值就出发初始化定时器。
我们找到这个CNTIN寄存器,其实就是定时器的初始值,一到初始值就复位定时器,这样就经常复位定时器,拖长了喂狗的时间,偶发来不及喂狗。
操作升级标志位失败
本质上就是对Flash区域的读写。
发现在应用程序的时候操作失败,BootLoader里面操作成功。
在应用程序和BootLoader中对Flash都有初始化操作,在应用程序中对Flash的初始化失败了,尝试不初始化系统频率就成功了。
那我们在操作Flash的时候切换到正常频率,操作完再切换到高速频率就可以了。
在电源管理里面,HSRUN模式是高速频率,这时候操作Flash是失败的,要切换为RUN模式才能正常。