前言:
想必大家对单片机烧录一词都不陌生,就是将程序下载到我们的板子(MCU)里面。常见的烧录方法有用Keil下载,或者是编译出Hex文件通过烧录软件(上位机例如:muisp、flymcu)、烧录器软件(例如:J-LINK、ST-LINK)烧录,从程序的角度来看通过烧录,它被“更新”了。
但是实际应用中单片机被封装在产品的内部,甚至有些涉及到加密(常见的是对汽车芯片的调试),这时没有烧录口,我们从一个设计者的角度来看,怎么才能对程序“更新”呢?
–>本文以以STM32F103C8T6为例,介绍使用串口通过Bootload方式实现程序的更新,即IAP技术
ps:小白想看懂IAP原理从第一章开始;使用IAP更新方法移步第三章;上位机使用专用IAP助手;
一、什么是IAP?
IAP-百度百科
IAP是(In Application Programming)即应用编程,IAP是指用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
通俗的来说,IAP是将Flash划分为两部分,两部分区域各存放一个程序,一个叫Bootloader(引导加载程序),另一个叫user application(用户应用程序),下文简称APP。IAP技术的关键在于Bootloader程序,因为我们要依靠它来在单片机内部帮我们更新的APP程序。
所以在我们自己不方便烧录时,通过在Flash中内置一个Bootloader程序帮我们更新APP程序,这也是我们经常听到的更新固件。
在程序执行初始进入bootloader,在bootloader里面检测条件是否被触发(可通过按键是否被按下、串口是否接收到特定的数据、U盘是否插入等等),如果有则进行对user application进行擦除和重新写入操作,如果没有则直接跳转到user application执行应用;如果有则进行擦除用户代码
(Old APP)并重新写入新的用户代码
(New APP)。
摘自:静静地思考_STM32+IAP方案的实现,IAP实现原理(详细解决说明)_一、什么是IAP,为什么要IAP
二、IAP实现原理(以STM32F103C8T6为例)
2.1 Bootloader运行流程
既然说IAP技术关键在于Bootloader,那它又是如何帮我们更新APP的呢?
当芯片上电后,首先是第一个项目代码(Bootloader)开始运行,它作如下操作: (摘自百科)
1)检查是否需要对第二部分代码进行更新
2)如果不需要更新则转到4)
3)执行更新操作
4)跳转到第二部分代码执行
也就是说我们先将Bootloader程序烧录(正常烧录方式)到MCU中,Bootloader包含完整的程序(Main函数等等),不更新时他就在我们设定的Flash内存中等待更新指令,需要更新时才帮助我们更新。
2.2 Flash分区
既然IAP技术要依靠Bootloader,且Bootloader和APP程序在Flash不同区,那么先搞清楚Flash怎么分区则是重中之重;
STM32F103C8T6为中容量产品,所以Flash闪存容量为128K,查手册可知,地址为0x0800 0000-0x0801 FFFF;
PS:这里关于STM32F103C8T6内置Flash是64K还是128K个人感觉手册写的比较含糊,储存器映像里是128K,我一直都是按照128K在是使用,但是配置又写分为64K与128K。
这里有一篇博文我觉得可以参考参考
所以容量确实有128K,但是只有64K经过测试。尽量还是设置为64K,以免出现一些奇奇怪怪的问题;
所以Bootloader程序例如下图放在Flash中 0x 0800 0000 - 0x 0800 1000地址占用4K大小空间(这样设置只是举个例子,大小位置都可以自己设置),当然设置Bootloader程序位置和APP程序不能重叠;
看到这里可能有小伙伴会有疑惑:这里不是ROM区吗,怎么起始地址是Flash起始地址??
Flash可以说是兼容ROM和RAM功能,属于广义上的ROM。
2.3 那程序是如何在两个区之间运行的呢??
因为一旦涉及到Flash分区,两个区程序中相应的中断向量,中断函数就需要偏移(各用各的中断)【挖个坑有读者感兴趣后面可以写一篇中断文章一起交流】,那么在Flash中不同区域中断向量表是如何联系以及执行;
下图中,flash中存储了两块程序,同时存在了两个中断向量表。
上电后,程序执行顺序如下:
STM32复位后,还是从0X08000004地址(第一个中断向量,所以也说明复位中断的优先级最高)
<1> 取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAP的main函数,如图标号①所示;
在执行完IAP以后(即将新的APP代码写入STM32的FLASH,灰底部分。新程序的复位中断向量起始地址为0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,如图标号②和③所示,同样main函数为一个死循环,并且注意到此时STM32的FLASH,在不同位置上,共有两个中断向量表
。
在main函数 (APP程序) 执行过程中,如果CPU得到一个中断请求, PC指针(指示程序运行位置的指针)仍强制跳转到 地址0X08000004中断向量表处,而不是新程序的中断向量表 (APP程序) ,如图标号④所示;程序再根据我们设置的中断向量表偏移量,
跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回main函数继续运行,如图标号⑥所示。
通过以上两个过程的分析,我们知道IAP程序必须满足两个要求:
1) 新程序必须在IAP程序之后的某个偏移量为x的地址开始
;
2) 必须将新程序的中断向量表相应的移动,移动的偏移量为x;返回起始地址,而是返回了最新的中断向量表地址。
摘自正点原子-IAP测试实验
<1>:不知道大家有没有疑问就是关于这里为什么第一个中断向量地址是0x0800 0004 而不是0x0800 0001或者0x0800 0002等等?
—>这里推荐大家去看看这篇文章,通俗的来说就是,32位单片机CPU一次处理32位,这样寄存器等等一次收发32位性能最优。
2.4 IAP过程的跳转(IAP核心在于进程的转换)
通过上图的相关分析,我们就能够知道,IAP的跳转过程无非是将PC指针跳转至用户APP中断向量表的开头,但在这个过程中,必须要先关闭所有中断,否则会让程序直接跑飞。
具体代码如下(示例):
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4);
//用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr);
//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
该文件总共只有2个函数,其中,iap_write_appbin函数用于将存放在串口接收buf(寄存器) 里面的APP程序写入到FLASH。iap_load_app函数,则用于跳转到APP程序运行;
其参数appxaddr为APP程序的起始地址,程序先判断栈顶地址是否合法,在得到合法的栈顶地址后,通过MSR_MSP函数(该函数在sys.c文件)设置栈顶地址,
最后通过一个虚拟的函数(jump2app)跳转到APP程序执行代码,实现IAP-APP的跳转。
第一句:
我们判断中断向量表的首字节,也就是系统栈堆顶部的值是否在0x20000000-0x20002000之间。因为APP的启动文件最开始就是初始化栈堆空间,如果这个里的栈顶值正确,那么就说明应用程序已经下载,并且已经初始化完成了。
第二句:
由于在整个跳转过程中不允许中断发送,因此这里先屏蔽所有中断。
第三句:
在common.c文件的第34行定义了pFunction Jump_To_Application;
(ApplicationAddress + 4)即为0x0800 3004也就是中断向量表中的复位句柄。
第四句:
__disable_irq()在官方文件中是一个空函数,作用是关闭用到的中断,需要用户自己编写函数,用到什么中断就关闭什么。
第五句:
查看pFunction的定义,我们可以在common.h文件中找到typedef void (pFunction)(void);
该函数声明了一个函数指针,加上typedef之后,pFunciton也只是类型void ()(void)的一个别名。
所以Jump_To_Application = (pFunction) JumpAddress; 此时Jump_To_Application指向了复位函数所在的地址;
第六句:
设置了主函数的栈指针
第七、八句:
清除所有挂起的中断,同时取消屏蔽总中断
第九句:
跳转至用户程序,也就是将PC指针跳转至了APP中断向量表中的复位句柄处。
摘自正点原子-IAP测试实验
2.5 IAP过程的总结
根据上文的相关分析,其实IAP过程除 设置好分区
之外,最重要的是跳转进程,其中要关闭所有中断,通过函数指针的形式将PC指针跳转至APP的复位句柄处,那么就能跳转进程到我们的APP程序。
对于APP程序来说,一方面其存储地址不能和Bootloader程序重复,另一方面在程序开始时必须重定位中断向量表位置。
—>下文将介绍Bootloader程序如何接收我们想更新的APP程序
三、上位机软件(以IAP专用串口助手为例)
3.1上位机又是什么???
1.1-做了这么久,才知道什么是上位机
这里所说的上位机软件也就是实现IAP中,给Bootloader发送更新指令的一种方式(类似的可以根据串口方式、无线方式等发送),因为考虑到实际应用中IAP的需要:
所以采用上位机的方式发送更新指令。更新指令中包含帧头和CRC校验以及更新程序大小、分包等等。(可以使用Qt编写上位机程序自定义帧头);
本例中上位机协议如下图:
3.2 上位机发送程序基本流程
四、教程(以STM32F10C8T6为例)
在实际操作之前应该明白原理,熟读本例源码,再上手操作这一点很重要!!!
4.1 Bootloader的写入
第一步:keil设置MCU内存大小
由于本例使用的是STM32F103C8T6,中容量芯片,需要修改对应Flash宏。
点击魔术棒,切换到C/C++选项卡,将Preprocessor Symbols下的Define栏中改成STM32F10X_MD。
这里大家根据自己MCU用户手册修改
第二步:设置Bootloader程序的位置和大小
设置Keil在烧写Bootloader时的Flash大小,这里限制为0x1FFF,8kB,所以APP层的代码应该在0x0800 2000的位置。
实际上Bootloader程序用不到8K的储存空间,一般2K都足够了,本例实际使用不到6K。
如何从keil工程知道使用了多少RAM和ROM空间
如上,我们便完成了对Bootloader的设置,通过烧录口正常将程序烧录到MCU中,接下来就是通过Bootloader帮助我们烧录APP程序;
4.2 APP程序的烧写
第一步:keil设置APP的烧录位置
设置Keil在烧写APP时的起始位置,一定不能与Bootloader程序地址重合。故写入位置应该从0x0800 2000开始。
第二步:APP程序中设置地址偏移量
本程序相对向量基地址偏移8K字节 ,对应Bootloader程序设置的8K空间
第三步:生成bin文件,再使用IAP上位机向MCU发送更新指令
大家平时烧录对Hex文件应该都不陌生,可是这里为什么要选择使用BIN文件呢?
在KEIL中生成bin文件的方法
BIN文件为纯二进制码流,不用解析直接放在指定的Flash位置。
成功生成BIN文件Output如下:
4.3 通过上位机实现IAP
第一步:打开串口
第二步:选择需要更新的BIN文件
第三步:下载APP程序
上图未烧录Bootloader程序上位机发送更新请求
烧录Bootloader程序后,成功更新APP程序
更新成功后LED(PC13)闪烁
STM32F103C8T6实现IAP更新APP程序
APP程序部分中断偏移有待改进,同时也正在实现用外挂Eeprom储存的方式完善IAP,欢迎有兴趣的小伙伴一起交流学习。
2023/12/2
外挂Eeprom方式已经实现;详情请看这篇文章
现在实现BIN文件拆包后乱序方式储存到Eeprom中;文章来源:https://www.toymoban.com/news/detail-773099.html
网盘链接_有效期至2024-11
提取码:LUCK文章来源地址https://www.toymoban.com/news/detail-773099.html
到了这里,关于【STM32 IAP技术实现】适合小白“食用”(以STM32F103C8T6为例)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!