STM32G0系OTA开发相关问题的解决方案
实习过程中对STM32G0系开发板OTA功能开发时遇到的一些问题和解决办法,主要涉及Bootloader程序的跳转和APP分区思路问题。 + Bootloader 跳转到APP区后发生卡死的情况。 + 双APP分区时只能跳转到其中某一个分区,无法跳转到另一分区的情况。
Bootloader程序跳转后程序卡死问题
先简单说明程序跳转的操作,就是设置PC指针位置+堆栈指针位置,PC指针指向地址应该是 \([程序烧录地址] + 4\)。CBT6后缀的开发板HAL库代码可以参考下面几句:
1 |
|
解决方法
Cortex M系列芯片中,除了M0以外,其它芯片都保留有VTOR寄存器用于重定位中断向量指针。G0系列开发板使用的是M0+芯片,可以使用该寄存器。因此在程序跳转前,给VTOR寄存器赋值相应地址就行。向量表地址根据上面的示意图,就是程序开始位置。完整跳转函数代码如下:
1 |
|
双APP分区下Bootloader只能跳转到其中某一个分区问题
双APP分区是笔者为了在没有eeprom的情况下实现OTA首先构想的方法,也就是分Bootloader + flag + AB区。OTA更新思路如下: 1. 程序启动默认进入A区; 2. 发生OTA请求,将新程序写入B区; 3. 写入成功则将flag区对应地址标志位置为有效; 4. 让程序进入死循环,由看门狗重启系统; 5. Bootloader验证程序通过后跳入B区执行,此时A区作为新的下载区。
上述方法的优点就是始终保证有一个分区是有可执行程序的,这样即使OTA失败甚至写入被强制中断,也有一个有效区可以执行。除此之外有效利用程序flash空间,减少单个区域擦写次数,延长flash寿命。
不过上述方法忽略了程序本身的地址指针问题。在使用各类工具编译完成后,我们得到的二进制执行文件中,其实保留了程序的起始地址等信息。这段地址信息一般是在bin文件的开头部分(其实也就是前一个问题中提到的中断向量表地址)。当硬件执行bin程序时,会先读取这些地址信息,跳转到对应位置去执行。实际上我们给相同程序设置不同的烧写位置,其编译生成的bin文件是不同的。看下面的对比可以更直观一些: 从划分区间的数据段区别能明显看出头部数据段不同。如果我们单纯将同一个程序(编译时设定烧入同一flash区域)烧入到不同的区域,即使Bootloader将程序跳转到正确位置,在程序读取开头字段时,中断就又会跳转回原先的区域。这意味着在编写新程序时,我们需要提前知晓烧录目标地址进行编译,否则就会出现区域跳转错误的现象。这对于后续OTA升级非常不方便,因此无法直接使用该方法。
解决方法
1.针对头字段在程序中进行修改[不具备可复用性]
这种方法就是在程序内,根据flag区域所保存的OTA目标地址,修改待OTA程序的头字段。上图中对比数据也可以知道,头字段会在特定区间重复一个字段值。该字段值大致是\(程序烧写目标地址+某偏移量\)。但具体这个偏移量是多少,笔者暂时没有找到规律,不同的程序似乎不一样。考虑到项目尽可能要剔除这种不稳定因素,所以该方案暂时没有采用。
2.修改方案: 程序区+下载区
简单来说就是要避开向不同区域的跳转,尽量每次启动固定向一个区域跳转即可。设定A区为APP,运行OTA时将程序下载入B区,然后向flag区写入OTA标志信息和CRC校验码信息,通过看门狗重启。在Bootloader程序中去验证B区程序的正确性,然后再复制到A区中执行,随后重置flag区信息。
当然,上面的方案会有运行稳定性方面的问题:需要进行OTA时Bootloader的启动效率会下降,同时也很难保证复制程序的正确性。所以最好能够使用外置eeprom或者flash来解决OTA程序暂存的问题。