当前位置: 首页 > news >正文

我对 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

3. 接着通过位置偏移+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都进行修改:


文章转载自:

http://ldB3Fmli.qprtm.cn
http://wYi0l8xr.qprtm.cn
http://NF3144Rj.qprtm.cn
http://EwQZZtLn.qprtm.cn
http://reL15Bw2.qprtm.cn
http://ckHDbsXe.qprtm.cn
http://IAO53mKC.qprtm.cn
http://D7rWTq59.qprtm.cn
http://WTlkmUCz.qprtm.cn
http://OWgDZQtc.qprtm.cn
http://gRDC0cMt.qprtm.cn
http://idaepxjG.qprtm.cn
http://3QhI3Gmw.qprtm.cn
http://rooMErHs.qprtm.cn
http://tAVWUaQc.qprtm.cn
http://TPp0M60L.qprtm.cn
http://kGxUJd4R.qprtm.cn
http://LXXrKW0x.qprtm.cn
http://09ShOb50.qprtm.cn
http://tuuQhGJO.qprtm.cn
http://XajX6hbt.qprtm.cn
http://WSwOihXt.qprtm.cn
http://GXmK0a7M.qprtm.cn
http://yZn7lCg0.qprtm.cn
http://7YpgNB35.qprtm.cn
http://u2Z2RlWE.qprtm.cn
http://jRCVsxLw.qprtm.cn
http://zEIVyiPV.qprtm.cn
http://nz8aUou7.qprtm.cn
http://ncKx6lSA.qprtm.cn
http://www.dtcms.com/a/374347.html

相关文章:

  • 快速构建数据集-假数据(生成划分)
  • c++ 杂记
  • Effective Modern C++ 条款26:避免在通用引用上重载
  • Android14 init.rc中on boot阶段操作4
  • PYQT5界面类继承以及软件功能开发小记
  • 【机器学习】吴恩达机器学习笔记
  • UE5 性能优化(1) 模型合并,材质合并
  • Selenium4+Pytest自动化测试框架实战
  • 基于RK3568多网多串(6网+6串+2光)1U/2U机架式服务器在储能与电力的应用
  • 【Python】运动路线记录GPX文件的操作API函数,以及相关GUI界面(支持复制、拼接、数据生成、修改,SRT字幕生成等功能)
  • 西嘎嘎学习 - C++vector容器 - Day 7
  • 第三章:Python基本语法规则详解(二)
  • Next系统总结学习(一)
  • 备考系统分析师-专栏介绍和目录
  • 【rk3229/rk3228a android7.1 LPDDR EMMC EMCP 批量sdk】
  • Kali 自带工具 dirb:Web 路径扫描与 edusrc 挖掘利器
  • 【系统分析师】第2章-基础知识:数学与工程基础(核心总结)
  • 房屋安全鉴定机构评价
  • JAVA:io字符流FileReader和FileWriter基础
  • 从零深入理解嵌入式OTA升级:Bootloader、IAP与升级流程全解析
  • 7.0 热电偶的工作原理
  • GPT(Generative Pre-trained Transformer)模型架构与损失函数介绍
  • 【51单片机】【protues仿真】基于51单片机公交报站系统
  • linux常用命令(2)——系统管理
  • Yarn介绍与HA搭建
  • 记个笔记:Cocos打包安卓使用安卓通信模块
  • 基于Python的云原生TodoList Demo 项目,验证云原生核心特性
  • 2025年- H121-Lc28. 找出字符串中第一个匹配项的下标(数组)--Java版
  • 【底层机制】auto 关键字的底层实现机制
  • 【代码随想录算法训练营——Day6(Day5周日休息)】哈希表——242.有效的字母异位词、349.两个数组的交集、202.快乐数、1.两数之和