[研电赛技术支持] 我对 OTA 的理解随记,附GD32/STM32例程

[复制链接]
1296|0
晓伍 发表于 2025-9-9 08:37 | 显示全部楼层 |阅读模式
前置知识解析:
        首先,我们需要知道单片机的程序是如何运行的,先不说底层原理,我们单从实际现象上可以看得出来,单片机按下复位按键后,就开始执行main程序了

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

        先看cortex m3/4权威指南:

3896468bf76c22095b.png

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

6548568bf76be4445e.png

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

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

9165268bf76b8b5447.png

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

9739268bf76b30b044.png

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

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

1548368bf76adcbf31.png

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

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

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

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

2564868bf76a7bcde6.png

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

复位RCC时钟,同时配置HSI

1419768bf76a233a5f.png

中断向量表重定向:

6911568bf769cbb191.png

其中:

2924368bf7696b3803.png

4230268bf76914a885.png

        紧接着,执行__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参考手册,如下:

9278168bf768794d8d.png

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

3886768bf7682dbcfb.png

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

7525268bf767d4adee.png

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

        系统存储器里面是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程序:

首先需要关闭所有中断,因为跳转过程中不能被中断打断
规定新的APP程序的起始地址为ApplicationAddress

372468bf767413abf.png

52668bf766e3cb66.png

3. 接着通过位置偏移+4字节,定位到复位向量地址,因为ApplicationAddress定义为新程序的起始地址,由之前可得,为MSP初始值

5488168bf76673da3c.png

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

5348568bf7661c4f69.png

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

3883168bf765b26b34.png

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

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

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

9193368bf765346f81.png

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

8289268bf764212cec.png

总结为一张图片如下:


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

6527568bf763b243bb.png

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

3686068bf76316bccb.png

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

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

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

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

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

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

8652768bf762a53e41.png

————————————————
版权声明:本文为CSDN博主「云伴枫轻舞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Mr_ybfqw/article/details/151332334

3031468bf75f677d3c.png
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

4362

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部