ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于STM32F103的OTA升级项目设计(二)

基于STM32F103的OTA升级项目设计(二) 基于STM32F103的OTA升级项目设计(二)项目设计功能规划前言前一篇我们主要讨论了什么是OTA以及为什么要设计OTA这一篇分析OTA设计的需求以及需要实现的功能最后实现分区跳转的功能设计。根据上一篇的内容我们已经知道了OTA的更新流程简单来说就是开机检测→触发升级→收包校验→擦写固件→重启运行这篇我我们先来关注项目的功能规划。双区架构设计双区IAP架构是目前最广泛采用的OTA升级方案之一尤其适用于资源有限的嵌入式平台。其核心思想是将Flash划分为两个独立的应用区域A/B区或一个固定Bootloader区加一个可更新App区。由于采用的视频中使用的方法因其结构简单、易于实现且兼容性强故本项目采用的是后者。什么是IAP在单片机和嵌入式系统开发领域IAP 是一种允许设备在正常运行过程中自主更新其固件软件的技术。核心优势与传统的需要外部编程器介入的 ISP在系统编程不同IAP 允许设备通过自身的程序逻辑对内部 Flash 存储器进行擦写。这使得设备支持远程升级OTA极大提升了产品的可维护性。工作原理通常将单片机的存储空间划分为 Bootloader引导程序区和应用程序区。系统上电后先运行 Bootloader检查是否有新固件需要更新如果没有则跳转执行主应用程序。STM32F103的Flash划分典型的双区IAP架构会将Flash划分为两个主要区域-Bootloader区位于起始地址如0x08000000大小一般为64KB~128KB存放引导程序。-App区紧随其后如0x08005000用于存放用户应用程序。本项目使用的是STM32F103C8T6价格便宜且性能够用。其flash大小为64KB。主存储器分为64页每页扇区大小为1KB共64KB。故项目初步的AB分区划分如下B区bootloader暂时规划为20K20页0~19。A区application根据64-2044K20~63。为什么Boot区要在App区之前如果将A区放置在B区之前当OTA升级到一半时如何发生意外比如突然掉电这时内存中的程序被烧写了一半。再次重启后代码先来到app区代码势必不能运行。而如果是B区在A区之前单片机一上电程序先跳转到B区会先检查OTA标志位有没有被置位。如果是清零状态直接跳转到A区。如果置位先检查A区版本号如果发现版本号不一致进行重新刷写。这将很好的避免了升级到一半掉电的问题。STM32F103启动模式选择与程序执行流程STM32的启动过程由 BOOT0 和 BOOT1 引脚共同决定。常见的三种模式如下BOOT0BOOT1启动地址来源0x0x08000000主Flash正常模式100x1FFF0000系统存储器ISP110x20000000内部SRAM在IAP场景下我们始终希望芯片上电后首先进入Bootloader程序即保持 BOOT00 。Bootloader运行后根据标志位判断是否需要升级。若无需升级则跳转至用户App区继续执行若有新固件待写入则接收数据、校验并烧录至App区完成后清除升级标志并重启跳转。从OTA的角度来说设计Bootloader程序最根本的任务就是两个​在没有OTA时跳转到A分区。​在有OTA时更新A区的flash程序数据。AB分区的功能划分明确功能职责是系统设计的前提。在双区IAP架构中Bootloader与App之间存在严格的分工Bootloader需要实现的功能 没有OTA事件发生时调转到应用程序区A区发生OTA事件时更新应用程序A区的程序串口IAP功能通过串口更新程序区A区程序设置物联网平台要求的OTA初始版本号利用外部Flash存放多个程序文件需要哪个就把哪个更新到应用程序区A区App程序职责 实现业务逻辑传感器采集、控制算法等定期检查云端是否有新版本下载固件包并通知Bootloader准备升级设置升级标志位AB分区两者之间的交互主要通过共享变量、特殊内存区域或外设寄存器完成。程序跳转机制函数指针与向量表重映射实现从Bootloader跳转至App的关键在于两点 设置堆栈指针SP 和 跳转至复位处理函数 。STM32的复位流程规定CPU上电后从 0x08000000 读取主堆栈指针MSP然后从 0x08000004 读取复位向量地址并跳转。因此若要跳转至App区必须模拟这一过程参考项目代码如下load_a load_A;//函数指针/*-------------------------------------------------*//*函数名BootLoader分支判断 *//*参 数无 *//*返回值无 *//*-------------------------------------------------*/voidBootLoader_Brance(void){if(BootLoader_Enter(20)0){//判断OTA_flag是不是OTA_SET_FLAG定义的值是的话进入ifif(OTA_Info.OTA_flagOTA_SET_FLAG){USART_printf(OTA更新\r\n);//串口1输出信息BootStaFlag|UPDATA_A_FLAG;//置位标志位表明需要更新A区UpDataA.W25Q64_BlockNB0;//W25Q64_BlockNB等于0表明是OTA要更新A区}else{USART_printf(跳转A分区\r\n);//串口1输出信息LOAD_A(STM32_A_SADDR);//跳转到A区}}USART_printf(进入BootLoader命令行\r\n);BootLoader_Info();}/*-------------------------------------------------*//*函数名设置SP *//*参 数addr栈顶指针初始值 *//*返回值无 *//*-------------------------------------------------*/__asmvoidMSR_SP(uint32_taddr){//addr的值加载到了r0通用寄存器然后通过MSR指令将通用寄存器r0的值写入到MSP主堆栈指针MSR MSP,r0//返回调用MSR_SP函数的主函数BX r14}/*-------------------------------------------------*//*函数名跳转到A区 *//*参 数addrA区的起始地址 *//*返回值无 *//*-------------------------------------------------*/voidLOAD_A(uint32_taddr){//判断sp栈顶指针的范围是否合法在对应型号的RAM控件范围内if((*(uint32_t*)addr0x20000000)(*(uint32_t*)addr0x20004FFF)){MSR_SP(*(uint32_t*)addr);//设置SPload_A(load_a)*(uint32_t*)(addr4);//将函数指针load_A指向A区的复位向量BootLoader_Clear();//清除B区使用的外设load_A();//调用函数指针load_A改变PC指针从而转向A区的复位向量位置完成跳转}else{USART_printf(跳转A分区失败\r\n);USART_printf(*(uint32_t *)addr %x\r\n,*(uint32_t*)addr);}}
返回列表