嵌入式技术之IAP,自从有了它老板再也不担心我的代码了!(中)

上篇文章我们一起学习了IAP的工作原理和IAP包含的3个重要功能:数据交互、数据存储和程序跳转

这3个重要功能称为“IAP的三板斧”,接下来我们看这三板斧具体完成哪些细节工作,如何实现这三板斧。请添加图片描述

1.数据交互

数据交互的功能是IAP核心功能之一,这个功能作用是使得设备和外部完成数据交互,数据交互的主要目的是获取新版本程序固件
在这里插入图片描述
IAP数据交互通常有两种方式:读取文件方式收发数据方式
在这里插入图片描述
读取文件方式指的是设备通过接口读取存储介质,获取程序固件实现更新。在这种方式下设备可以使用USB接口读取U盘,使用读卡器接口读取SD卡。

读取文件方式需要设备拥有文件系统功能(如FatFs文件系统),在这种方式下新版本程序固件以特定名称存入存储介质,存储介质插入设备的存储接口,设备检开始查询存储介质中的文件,只有当检测到特定名称的文件后,设备才会根据这个特定名称文件中的内容更新程序固件。这种方式只需要一个存储器即可(不需要外接其他设备),非常适合小批量脱机更新固件。
在这里插入图片描述
收发数据方式指的是设备通过通讯接口与外部通讯,获取程序固件实现更新。在这种方式下设备可以使用485接口或者以太网口与外部进行数据收发。

例如设备使用网口接收云端服务器的数据,服务器将新版本固件程序文件分为n个小的数据包发送给设备,设备接收到数据后,将数据一包一包保存起来,获取完整的程序固件后执行更新操作。这种方式适合无人化升级,也非常适合大批量在线更新固件。在这里插入图片描述
IAP数据交互使用收发数据方式的较为常见,接下来将详细的讲解一下这种方式。数据发送的本质是通讯,通讯功能由底层硬件和通讯协议共同实现。底层硬件可以是485接口,以太网口,蓝牙接口等。通讯中的数据必须按照一定的格式进行打包发送和数据解析,因此需要制定通讯协议,收发数据方式的重点就是制定通讯协议和实现通讯协议功能。
制定通讯协议有两种实现方式:使用通用协议和使用自定义协议

适合IAP通用通讯协议有 XModem ,YModem等协议,这些协议比较简单,非常适合传输小体积文件。使用通用协议的最大好处是不用特定去设计上位机软件,可以直接使用现成的软件工具实现文件传输,例如软件工具SecureCRTPortable可以支持XModem ,YModem等协议。
在这里插入图片描述
自定义的通讯协议指的是公司根据需求定制一套专用的通讯协议。这种方式定制的协议会非常适合设备,对设备的固件程序软件开发比较友好,但是这种方式需要上位机工程师来实现上位机的文件发送功能。

不管是使用通用的通讯协议,还是使用自定义通讯协议,作为通讯协议它们至少要包含以下通讯指令:
1、升级请求指令。在升级前,通过发送升级请求,让双方的设备进入准备状态。
2、数据包请求指令。指的是在升级过程中一包一包的收发固件数据。
3、结束请求指令。指的是完成固件数据发送后,通知设备数据已经发生完毕,设备收到通知后自行进行固件更新。
在这里插入图片描述

2.数据存储

数据存储是IAP核心功能之一,该功能主要作用是访问(读/写)设备内部的存储器,数据存储的主要目的是将新版本的app程序固件以数据的方式写入设备内部存储器的指定位置
在这里插入图片描述
数据存储的基本操作是读/写存储器,目前嵌入式设备内部的存储器通常为NOR-FLASH(片内FLASH),设备运行的固件存储在NOR-FLASH中,嵌入式设备可以直接运行NOR-FLASH中的指令(XIP)。片内FLAHS不仅可以存储固件还可以存储其他数据。因此数据存储的基本操作就是FLASH读写操作,FLASH读写有如下特点:
1、最小擦除单位为扇区。
2、必须是先擦除一个扇区,才能写入数据。扇区擦除后数据变为全1。
3、数据写操作只能将数据从1写为0,不能将数据从0写为1,如果该扇区数据已经被写入过了,则需要先擦除再写入。
下图是STM32系列MCU内部FLASH数据表:在这里插入图片描述
对FLASH区间进行规划是数据存储的一个重要部分,通常情况会将FLASH划分为以下几个区间
1、bootloader区间,存放bootloader程序固件。
2、app区间,存放需要运行app程序固件。
3、app备份区间,存放接收到的最新版本app程序固件。
4、更新标志位区间,存放更新标志位。在这里插入图片描述
其中bootloader区间,app区间和更新标志位区间是必须的,app备份区间可以根据实际情况来选择。

数据存储重要功能是将最新的app程序固件以数据的方式写入app区,写入数据到app区有两种方式
1、单包写入。设备接收到一个数据包后,按照一包数据写入app区存储器,就是每接收到一包数据就向app区写一包数据。通常情况下每一包数据长度是固定的(末尾包可以使用0XFF填充),此时需要计算好一个扇区包含多少个包,保证写入数据前扇区已经执行了擦除操作。
2、整包写入。设备能接收到一个数据包后,先将一包数据写入app备份区存储器,所有包接收完成后,再将app备份区数据按照扇区为单位写入app区间。
在这里插入图片描述
App数据完成写入后,需要在FLASH更新标志位区间写入一个标志位,bootloader可以读取标志位,判断是否需要执行app数据更新,因此需要完成升级完成标志位和检查升级标志位功能。为了提高稳定性标志为采用一串幻数作为标志为,例如当需要更新时标志位区间的前5个数据为0x13,0x14,0x17,0x55,0x20 ,不需要更新时标志位区间的前5个数据为0xFF,0xFF, 0xFF,0xFF ,0xFF。

3.跳转

IAP功能通常需要计两个项目代码:第一个项目代码不执行产品功能操作部分代码称为bootloader,第二个项目代码执行真正的产品功能,这部分代码称为app。bootloader和app这两个代码都同时烧录在Flash中,当芯片上电后,bootloader代码首先开始运行,随后跳转并执行app代码。

程序跳转是IAP的核心功能,程序跳转主要目的就是实现从bootloader程序跳转到APP程序,让APP程序运行。程序跳转也是IAP中最难理解的部分,需要将一步一步地揭开它神秘的面纱。
在这里插入图片描述

3.1 工程启动

每次复位后MCU都可以重头开始运行写入的固件(写入MCU内部FLASH)。实现这种现象的底层的运行逻辑主要有以下两方面原因:
1、MCU复位后,PC寄存器被强制赋予了一个初始值(通常情况下为0X00000000),因此MCU从地址0X00000000获取第一条指令。
2、在编译工程的时候,通常会工程配置代码下载的起始位置(通常是0X00000000),编译好的固件会携带起始位置信息。使用FLASH烧写工具下载固件的时候,烧写软件会根据起始位置信息,将固件从指定起始位置开始烧写到FLASH。
由于代码被下载到MCU内部FLASH的起始位置是0X00000000,而MCU复位后PC值也为0X00000000,因此每次复位后都可以重头开始运行写入MCU内部FLASH固件。MCU复位后启动流程如下图:
请添加图片描述
以STM32系列MCU为例,复位后先从地址 0x00000000 处取出值强制写入 MSP (MSP初始化),然后从地址 0x00000004 处取出值强制写入 PC ,然后从地址 0x00000004中数据所对应的地址处取指运行。
在这里插入图片描述
STM32系列MCU主闪存存储器是STM32内置的Flash(芯片Flash),正常的工作模式是将主内置的Flash地址0x08000000映射到0x00000000,这样代码启动之后就相当于从0x08000000开始。下图是一个由STM32系列工程的编译出的HEX文件,由图可知此HEX文件会从FLASH地址0x08000000处(程序的起始位置)开始烧写。
在这里插入图片描述

3.2 跳转原理

复位后PC值赋值为0X00000000,FLASH中固件的起始位置是0X00000000,这样就能保证每次复位后都可以重头开始运行固件。那么我们将固件代码B下载到0X0000Y000处,然后将PC值设置为0X0000Y000,是不是就跳转到固件代码B了
在这里插入图片描述
这种想法是完全正确的,事实上这也就是IAP跳转的工作原理:bootloader固件代码被下载到了0X00000000处,app固件代码被下载到0X0000Y000处,复位后bootloader先运行,随后bootloader代码中执行将PC至设置为0X0000Y000,跳转到app固件代码。

IAP跳转工作原理有两关键点:
1、修改工程代码的下载起始位置。
2、模拟复位流程,强制修改PC值完成跳转动作。

修改工程代码起始位置
通常情况下我们可以通过配置工程参数,实现修改工程代码的下载起始位置。以STM32系列MCU,使用KEIL开发环境为例。点开工程设置,将IROM1起始值改为0x8020000,此时工程就将代码下载的起始位置修改为0x8020000(MCU会将0x08020000映射到0x00020000)。
在这里插入图片描述
配置好工程IROM1起始值后编译工程,打开HEX文件就可以发现下载的起始位置为0x0802000。
在这里插入图片描述
修改PC值完成跳转

那么应该如何模拟复位流程完成跳转动作呢?STM32系列MCU复位后先从地址 0x00000000 处取出值强制写入 MSP (MSP初始化),然后从地址 0x00000004 处取出值强制写入 PC ,然后从地址 0x00000004中数据所对应的地址处取指运行。
因此模拟复位流程首先完成MSP初始化,然后强制写入 PC 值,模拟复位流程跳转代码如下:
在这里插入图片描述
由代码可知,程序首先将将0x00020004这个地址中的值强制转换成一个函数jump_to_application,然后0x00020000这个地址中的值赋值为MSP,最后执行jump_to_application函数,执行jump_to_application函数等价于跳转到地址0x00020004中数值对应的代码处执行。
在这里插入图片描述
对工程进行反汇编,分析运行指令执行流程,代码反汇编后如下图
在这里插入图片描述
核心汇编代码分析如下:

MOV      r0,#0x20000    ; 将立即数0x20000存入r0
LDM      r0,{
    
    r0,r4}		; 将地址0x20000中的数据存入r0,地址0x20004中的数据存入r4
BLX      r4			; 跳转至r4指向的地址,即地址0x20004中的数据指向的地址

汇编中的跳转指令(分支指令)可用来改变程序的执行流程或者调用子程序。在ARM架构中有两种方法可以实现程序流程跳转:
1、使用分支指令
2、向程序计数器PC写入目标地址值。
通过向程序计数器PC写入跳转地址可以实现4G(32位系统)的地址空间任意跳转,而使用分支指令是跳转空间受到限制,ARM架构中的跳转指令如下表:
在这里插入图片描述

3.3 构建实例

结合修改代码的下载起始位置和模拟复位流程修改PC值完成跳转这两个知识点,以STM32系列MCU为例构件一个IAP项目,这个项目包含两个工程bootloader工程和app工程。

工程代码的下载起始位置设置:bootloader工程使用默认配置0x08000000 ,app工程配置0x08020000 ,两个工程的配置如下图:

在这里插入图片描述
在bootloader工程中完成必需的功能后调用iap_jump_to_app函数,实现跳转到地址0x08020000中数值指向的位置执行代码,iap_jump_to_app函数如下:

在这里插入图片描述
完成bootloader工程和app工程后,编译这两个工程,并将这两个工程下载到MCU中,我们会发现MCU先运行bootloader固件,然后再运行app固件,至此完成IAP项目构建。下载固件的时候一定要把Erase Full Chip这个选项去掉,不然的话下载第二个固件的时候会将整个FLASH擦除,因此第一个下载的固件(之前的固件)会被擦除掉

在这里插入图片描述

3.4 注意事项

上节构建了一个简单的IAP项目,在bootloader中设置了MSP和PC,MCU跳转到app固件,这个跳转过程实际上是模拟了一个复位流程。使得MCU开始执行app固件,在app固件前面通常会执行一段启动代码,启动代码会对RAM等外设执行初始化。

但是真正的复位,是由硬件发起的,硬件会将所有外设进行默认设置,通常情况是默认关闭所有外设,将GPIO设置为输入状态。app固件虽然执行了启动代码,但是所有外设的设备都是保存了bootloader固件中设置的状态,这些外设(如USART,ADC,TIMER等等)如果在bootloader中被配置和开启,会对app运行产生异常影响,因此在bootloader执行跳转前,需要将bootloader中用到的外设全部关闭。
在这里插入图片描述
在bootloader执行跳转前除了关闭外设之外,还需要对MCU的中断向量进行重映射,中断向量是什么?

中断向量是指计算机系统中由硬件产生的中断入口地址。中断是指计算机在执行程序的过程中,出现特殊请求时,计算机停止当前运行的程序,跳转到这些特殊请求的处理程序,处理结束后再返回到程序的中断处继续执行。在这里插入图片描述
在ARM架构处理器中,中断向量的大小是4个字节,其中存放的不是中断程服务程序的入口地址,而是是一条跳转指令。当中断发生时,硬件自动执行相应中断向量处的跳转代码,跳转到响应的中断服务程序的入口地址。
中断向量规定了中断服务的入口地址,这个入口地址由硬件决定,bootloader工程使用了默认配置,中断向量与bootloader工程代码是匹配的。跳转到app工程后中断向量并没有改变,这样在app工程中发生中断,此时程序会跳转到bootloader工程代码中!因此我们在app工程启动后需要重新设置中断向量,否则程序将会运行异常!

如果不对外设和中断向量进行相应的操作,会这么样?接下来使用一个反例来看看结果如何。
bootloader工程核心代码如下

在这里插入图片描述
由bootloader代码可知:系统开启了滴答定时器,滴答定时器中断函数中输出了打印信息,配置完成后执行了跳转函数,跳转到了app固件中。

app工程核心代码如下

在这里插入图片描述
由app代码可知:系统开启了滴答定时器,滴答定时器中断函数中输出了打印信息,配置完成后进入whlie(1)流程定时输出打印信息。
运行工程的结果如下图

在这里插入图片描述
根据串口信息可知app代码运行的时候,滴答定时器产生中断后跳转到了bootloader代码中,执行了bootloader代码中的滴答定时器产生中断函数

4.总结

本文讲解了IAP的3个重要功能:数据交互、数据存储和程序跳转。并构建了一个简单的IAP项目,同时说明了在设计IAP项目中的注意事项。下篇文章将详细的介绍如何完成IAP的功能代码,并构建一个完成的IAP项目。
在这里插入图片描述
创作不易希望朋友们点赞,转发,评论,关注!
您的点赞,转发,评论,关注将是我持续更新的动力!
CSDN:https://blog.csdn.net/li_man_man_man
今日头条:https://www.toutiao.com/article/7149576260891443724

猜你喜欢

转载自blog.csdn.net/li_man_man_man/article/details/128344319