我对 OTA 的理解随记,附GD32/STM32例程

前置知识解析:

首先,我们需要知道单片机的程序是如何运行的,先不说底层原理,我们单从实际现象上可以看得出来,单片机按下复位按键后,就开始执行main程序了

实际上,单片机的程序不是简简单单的从main开始执行的,实际情况需要我们阅读两个资料:cortex m3/4权威指南,以及一个简单的启动文件

先看cortex m3/4权威指南:

我们首先要知道一个事实:单片机上电后是从0x0000 0000 处开始运行的,权威指南也说得很清楚了:

从向量表也可以看得出来,地址0x0000 0000 处是MSP的初始值,将这个值赋给MSP,这是第一步

第二步,单片机通过内核的硬件机制,自动将PC指向复位中断向量,紧接着就是指向复位中断,这点在权威指南里面也说得很清楚:

对于第二步,可以通过启动文件进一步分析:

Vector的中文意思就是向量,图片中的就是中断向量表

中断向量表有什么用?是干什么的?再次回到权威指南寻找答案如下:

说白了就是存储不同中断的入口地址,比如外部中断啊,定时器中断啊,DMA中断啊......就是存储这些中断函数的入口地址

同时我们也可以得出,向量表是默认存储在物理地址0处的,刚刚好对应上面的地址0x0000 0000 处是MSP的初始值这句话

同时,权威指南里面的向量表结构也与启动文件的向量表的结构一模一样

回到刚才说的PC指向复位中断向量,我们可以在启动文件找到复位中断函数:

我们看到,在复位函数里面,首先要进行SystemInit,通过跳转该函数,可以得出,SystemInit做的工作如下:

复位RCC时钟,同时配置HSI

中断向量表重定向:

其中:

紧接着,执行__main,这跟我们主程序的main是两个完全不同的函数,根据MDK文档:

It is automatically created by the linker when it sees a definition of main()。简单点来说,当编译器发现定义了main函数,那么就会自动创建__main,这个函数在IDE中找不到,得在反汇编文件.map里面找,执行完后,才会跳转到main主函数执行主程序

之前如果你完全没有接触甚至听说过OTA,只是说在keil里面写好程序,烧录就行,那么,你的代码执行流程就是这样

额外扩展:在系统存储器里面启动程序

一般情况下,我们的程序都是从flash开始运行的,flash的起始地址是0x8000 0000,但是之前说的起始地址是0x0000 0000,产生了地址不一致的问题,这就引出了存储器映射:

STM32 通过存储器映射机制,将 FLASH 的内容映射到地址 0x0 处。当 CPU 访问 0x0 地址时,实际上访问的是 0x08000000 处的 FLASH 内容。这种映射是硬件自动完成的,对软件透明,当然这是当BOOT0与BOOT1都处于GND的情况下

通过STM32F10参考手册,如下:

这三种启动方式对应的物理地址如下:

同时参考手册有这么一句话需要特别注意:

先看第二种:系统存储器启动:

系统存储器里面是ST官方写的一个名为BootLoader的程序,也叫自举程序,如果说用户想要使用串口烧录程序,就需要实验这个模式,这种方法可以通过ST官方的BOOTLoader的程序,将从串口下载的程序内置到flash中

也就是,之前通过ST-LINK啊,DAP LINK啊, JLINK啊,是直接将程序下载到flash里面的,这次是通过BootLoader将程序放到flash的,也就是偶尔听过的串口下载程序,两者相同之处都是在flash启动程序

通过这里,其实已经可以引出OTA了,既然bootloader有这种功能,那么我们是不是也可以自己写一个bootloader?获取串口接收的代码文件,将其放到指定区域,而且我们也不一定要用串口,是不是也可以通过无线模块,只要单片机可以无线获取bin程序文件,再通过我们以及向单片机内部写入的bootloader来加载程序文件,岂不是就是类似于无线烧录的方法了?比如使用HC-06蓝牙模块,或者ESP8266模块

将接收到应用程序全部正确写入Flash的App指定位置后,Bootloader 需要完成跳转到应用程序App的开始位置的操作,这也是Bootloader 的核心作用

通过执行下面代码,即可实现跳转至APP程序:

  1. 首先需要关闭所有中断,因为跳转过程中不能被中断打断
  2. 规定新的APP程序的起始地址为ApplicationAddress
  1. 接着通过位置偏移+4字节,定位到复位向量地址,因为ApplicationAddress定义为新程序的起始地址,由之前可得,为MSP初始值

将复位中断向量作为APP的入口函数,语法上就是将APP的复位中断向量的地址作为函数指针,

紧接着需要设置APP程序的MSP的起始地址

接下来只需要通过Jump_To_Application();即可进行调用

紧接着,需要进行中断向量表偏移,因为原来的中断向量表是bootloader程序,新的APP程序存储在0x0800 5000位置,相对偏移了0x5000,中断向量表也需要对应进行偏移0x5000,否则在新的APP程序发生中断的时候,无法找到对应的中断向量表,从而造成中断混乱,

在之前的分析SystemInit的时候,这个语句其实就是实现的VTOR的地址设置

其中VECT_TAB_OFFSET设置的就是中断向量表的偏移量,设置的时候需要注意注释中的内容,数值要为0x100的倍数(应该是考虑到地址对齐的因素,不同的芯片,这里的注释也不愿意,有的是0x200倍数),可见其默认值是0,再结合上面的语句,也可以得出一个结论:中断向量表默认从flash的起始地址开始存储

总结为一张图片如下:

没有引入BootLoader之前,系统程序的启动流程:

加入BootLoader后,系统程序的启动流程:

编写BootLoader:

STM32:

可以参考官方的例程:STSW-STM32008 | Product - 意法半导体STMicroelectronics

需要注意,官方的代码有缺陷,就是没有进行中断向量表的偏移,如果测试的APP带有中断,将会导致运行错误

GD32:

我近段时间仿造STM32的官方代码,以及GD32的库函数,以及CSDN上其他大佬的代码,我自己针对GD32F407VET6写了一份,带有BootLoader与测试APP,欢迎来我仓库下载:

具体是怎么修改的以及注意事项我都写在了BootLoader程序的注释里面,看注释就行,不懂的评论区说出来我有空看看

GD32_OTA: GD32F407VET6实现OTA升级例程代码

测试的时候,需要注意根据实际的APP地址,对两个程序的FLASH的start与size都进行修改:

相关推荐
Aczone2811 小时前
硬件(五) 存储、ARM 架构与指令系统
arm开发·嵌入式硬件·架构
LS·Cui11 小时前
单片机按键示例功能
单片机
【ql君】qlexcel11 小时前
MCU上电到运行的全过程
单片机·嵌入式硬件·mcu·启动过程
搞一搞汽车电子12 小时前
S32K3平台eMIOS 应用说明
开发语言·驱动开发·笔记·单片机·嵌入式硬件·汽车
pQAQqa12 小时前
FreeRTOS项目(2)摇杆按键检测
stm32·单片机·嵌入式硬件·freertos
小莞尔13 小时前
【51单片机】【protues仿真】基于51单片机停车场的车位管理系统
c语言·开发语言·单片机·嵌入式硬件·51单片机
一川月白70914 小时前
51单片机---硬件学习(跑马灯、数码管、外部中断、按键、蜂鸣器)
单片机·学习·51单片机·外部中断·蜂鸣器·数码管·跑马灯
月阳羊15 小时前
【硬件-笔试面试题-69】硬件/电子工程师,笔试面试题(知识点:电机驱动电路的反馈电路)
java·经验分享·嵌入式硬件·面试
weixin_4684668517 小时前
树莓派32位与64位系统安装teamviewer远程软件
linux·单片机·自动化·树莓派·远程控制·vnc·teamviewer