前言
(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动/单片机/RTOS的实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)本章节主要是进行一些基础科普,对这部分了解的,可自行跳到后面章节。
Bootloader/IAP的前置知识
Bootloader/IAP是做什么的?
(1)首先,我们需要知道
Bootloader/IAP有什么作用,才更有动力学习Bootloader。
(2)在实际项目中,我们对一个产品往往是需要更新换代的。但是,在程序的升级过程中,我们总不可能把装配好的产品拆开,然后接线烧录程序。所以,为了解决这样的问题,我们就需要了解了解Bootloader/IAP这项技术了。
(3)拥有了Bootloader/IAP这项技术,我们嵌入式软件工程师也可以像纯软的工程师一样,实现远程调试,不需要实地进行调试开发板。这样不仅仅可以提高调试的效率,还可以降低调试的成本,减少频繁的无意义的出差。
程序的几种下载方式
(1)注意:此部分内容节选自B站:STM32的IAP技术,基于CAN总线的STM32F103 BootLoader设计视频讲义。
ICP
(1)在电路中编程。使用厂家配套的软件或仿真器进行程序烧录,目前主流的有
JTAG接口和SWD接口,常用的烧录工具为J-Link、ST-Link等。

(2)在程序开发阶段,通常在连接下载器的情况下直接使用编程软件进行程序下载调试。在
MDK软件中可以选择不同的下载器。

ISP
(1)在系统中编程。以
STM32为例,其内置了一段Bootloader程序,可以通过让boot0 = 1,boot1 = 0通过系统存储器启动来运行这段程序,再通过ISP编程工具将程序下载进去。下载完毕之后,让boot0 = 0,让MCU主闪存存储器启动,使得MCU运行所下载的程序。

IAP
(1)
IAP技术全称为In-Application Programming,翻译过来就是在应用中编程。IAP可以使用微控制器支持的任一种通信接口(如I/O端口、USB、CAN、UART、I2C、SPI等)下载程序或数据到FLASH中。IAP允许用户在程序运行时重新烧写FLASH中的内容。但需要注意,IAP要求至少有一部分程序(Bootloader)已经使用ICP或ISP烧到FLASH中。

(2)无论是
ICP技术还是ISP技术,都需要连接下载线,设置跳线帽等操作。一般来说,产品的电路板都会密封在外壳中,在这时若要使用ICP或ISP的方式对程序进行更新,则必然要拆装外壳,如果产品的数量比较多,将花费很多不必要的时间。
(3)采用IAP编程技术,可以在一定程度上避免上述的情况。一般情况下,产品的外壳都会留有通信接口,若能通过这种通信方式对程序进行升级,则可以省去拆装的麻烦。在此基础上,若引入远距离或无线数据传输方案,更可以实现远程编程或无线编程。
(4)例如我使用如下模块,一个接在开发板上,一个接在电脑上,即可实现无线编程。

程序分区
(1)需要学习
Bootloader/IAP技术之前,我们需要对程序的存储位置有一个详细的了解。因此,我建议各位先阅读C站:RAM明明断电会丢失数据,为什么初始化的全局变量存储在RAM?详细分析程序的存储这篇博客之后,再进行后续的学习。这样方便后续对Bootloader/IAP技术学习。
(2)通过上面这篇博客的学习,我们知道了有了如下知识基础:
- 代码段,初始化非0的可读可写段是需要占用Flash空间的。
- 在芯片启动阶段,初始化非0的可读可写段会从Flash复制粘贴到RAM空间。初始化非0的可读可写段会通过启动程序在RAM空间清零,并且进行占用。
Bootloader/IAP的工程是什么样子的
Bootloader/IAP与APP工程
(1)简单了解了
Bootloader之后,我们需要知道Bootloader的工程是长什么样子的。Bootloader是一个用户自定义的升级程序,因此Bootloader是一个独立的工程项目。在实际开发过程中,Bootloader是很少会需要进行调整的,因为Bootloader的调整就需要拆机重新烧录,会相当的麻烦。因此一家企业的Bootloader都需要反复的确认无误之后再进行应用程序开发。
(2)应用程序开发又是一个独立的工程文件,这个是实际的业务代码。即使代码出现错误,也可以通过Bootloader不进行拆机即可进行升级维护。因此这部分代码有较高的容错率。
(3)通过上述的科普,我们即可知道,如果是没有Bootloader,那么一个工程即可完成。但是有了Bootloader,那么一个工程就需要分为Bootloader工程和应用工程两部分。

如何知道Bootloader占用多少Flash和RAM
(1)如果需要自行开发
Bootloader,当然需要对芯片的布局要有一个详细的了解。例如我现在以STM32F103ZE为例,这款芯片的Flash为512KB,RAM为40KB。如下为有无Bootloader的Flash空间介绍,可以看出,如果有Bootloader之后,同一款芯片的应用程序空间会被挤压,这也是需要付出的代价。不过一般来说,Bootloader空间不大,而且如今的存储器已经不那么昂贵,为了方便开发,提高效率,这点牺牲是可接受的。

(2)上面我们说了,如果是没有
Bootloader的工程一个即可。但如果是拥有了Bootloader,那么就需要分成两个工程进行。如果分两个工程,那么我们就应该了解工程的FLash和RAM的空间是如何分配的。
(3)既然需要分配FLash和RAM的空间,那么就需要首先知道Bootloader的程序大小是多少。
(4)按照下图方式,即可知道当前的Bootloader所占Flash空间大小。
(5)至于RAM,因为Bootloader的生命周期仅仅为上电一瞬间,当Bootloader成功将芯片引导进入APP程序的时候,Bootloader就没有任何作用了。因此,我们只需要使用Keil自动帮我们分配好的RAM起始地址和大小即可。
注:这部分不了解的,可以先学习上面所说的,程序内存分区加深各位对RAM作用的理解。

如果分配Bootloader和APP的Flash和RAM
(1)我们已经知道了
Bootloader的的Flash大小,现在就可以分析如何分配Bootloader和APP的Flash和RAM了。
<1>首先Flash的起始地址为0x8000000,因此Bootloader的Flash起始地址设置为0x8000000,这个没有什么可以解释的。
<2>但是我们看看APP的Flash的起始地址却是0x8010000,这个是为什么呢?原因很简单,因为Bootloader是需要占用Flash空间的,而我们这里给Bootloader分配的空间是64KB,因此APP的Flash需要从0x8010000位置开始。
<3>因为STM32F103ZE的Flash为512KB,因此Bootloader的Flash大小设置为0x80000。个人不建议这么设置,因为我们给Bootloader分配的空间是64KB,所以我建议Bootloader的Flash大小这里设置为0x10000,因为这样,如果Bootloader如果程序大小超了64KB,编译器会进行提示。
<4>因为STM32F103ZE的Flash为512KB,而们给Bootloader分配的空间是64KB。因此APP的Flash大小就应该为0x70000(448KB)。
<5>现在各位有没有一个疑惑,怎么APP和Bootloader的RAM起始地址和大小都是一模一样的呢?如果了解程序的内存布局,就知道原因很简单,因为Bootloader的生命周期只有上电的一瞬间,当Bootloader将程序引导进入APP程序的时候,Bootloader就将不会再发挥任何作用。因此APP可以占有全部的RAM。

为什么Bootloader升级是需要bin文件
(1)如果我们使用
Bootloader升级是需要使用bin文件的。但是我们都知道,程序下载又有bin,hex,axf这三种文件,那么这三者有什么区别呢?
- bin:最直接的代码映像,它记录的内容就是要存储到
FLASH的二进制数据 (机器码本质上就是二进制数据),在FLASH中是什么形式它就是什么形式,没有任何辅助信息,包括大小端格式也没有,因此下载器需要有针对芯片FLASH平台的辅助文件才能正常下载(一般下载器程序会有匹配的这些信息);- hex:是一种使用十六进制符号表示的代码记录,记录了代码应该存储到
FLASH的哪个地址,下载器可以根据这些信息辅助下载。例如上面的ISP下载方式就是需要hex文件。- axf:不仅包含代码数据,还包含了工程的各种信息,因此它也是三
个文件中最大的。我们将axf文件进行反汇编,之后不仅可以看到代码数据,还有具体的标号、地址以及反汇编得到的代码。
keil的反汇编以及bin文件生成介绍
(1)之后进行
bootloader升级,是先让app程序生成bin文件,然后再通过上位机传输bin文件升级。而keil是不会自动生成bin文件的,需要我们输入命令行来进行。因此,我们需要在keil中配置如下指令。
(2)在bootloader的学习过程中,我们也会需要阅读反汇编来进行调试。而keil不会自动生成反汇编文本,因此也需要自行配置。

生成bin文件
(1)
fromelf是keil官方提供的一个工具,这个工具能够根据你输入的不同提示信息来做出相应的处理。如果有Linux基础的朋友对这个会比较熟悉,没有Linux基础的这部分可以不看,对后续学习不影响。
(2)
- –bin,生成一个bin文件
- –out,bin文件输出的路径,及其名字
- !L,等价
.\template\template.axf表示axf文件的路径。例如我现在axf文件路径是在工程的template文件夹中,axf文件名字叫做template.axf。因为大多数人采用的是第一种写法,怕各位无法理解产生疑惑,因此我都写出来。
# 常见写法
fromelf --bin --output .\template.bin .\template\template.axf
# 推荐写法
fromelf --bin !L --output template.bin
生成反汇编文件
(1)
- –text,表示将输出转换为文本格式。
- -a,打印数据的地址。
- -c,打印反汇编代码
# 常见写法
fromelf --text -ac --output=template.dis .\template\template.axf
# 推荐写法
fromelf !L --text -ac --output=template.dis

STM32F103的启动流程
无Bootloader的启动流程
(1)如果没有
Bootloader,芯片的启动流程如下。
这里需要注意,M3和M4内核上电启动,会从0x08000000取四字节数据作为堆栈指针,然后从0x08000004取四字节数据作为复位中断地址。

(2)如果是使用的
Keil,各位会发现,我根本就没有将数据段复制到RAM,也没有清除BSS段啊。这是因为,我们的这些操作,已经在Keil提供的__main函数中完成了,可以打开.s启动文件看一下,完成芯片的系统初始化之后,其实是跳转到__main函数,而不是main函数。

(3)如果想要了解芯片启动的全部流程,可以看看C站:如何使用Linux编写STM32程序并且烧录这篇博客。
(4)复制数据段和清除BSS段代码如下:
/* _data_loadaddr : Flash中数据段起始地址,由编译器提供
* _data : RAM中数据段起始地址,由编译器提供
* _edata : RAM中数据段结束地址,由编译器提供
*/
for (src = &_data_loadaddr, dest = &_data;
dest < &_edata;
src++, dest++) {
*dest = *src;
}
/* 因为bss段紧贴数据段,因此复制粘贴完数据段,马上就开始bss段的清零操作
* _ebss : RAM中bss段结束地址,由编译器提供
*/
while (dest < &_ebss) {
*dest++ = 0;
}
(5)上面的
_data_loadaddr,_data,_edata,_ebss这些数据是由编译器提供。如果是使用的Keil MDK,那么就应该使用如下的
| Keil中符号 | 含义 | 对应上述信息 |
|---|---|---|
| Load$$RW_IRAM1$$Base | Flash中数据段起始地址 | _data_loadaddr |
| Image$$RW_IRAM1$$Base | RAM中数据段起始地址 | _data |
| Image$$RW_IRAM1$$Length | 需要搬运的数据段长度,单位是字节 | _edata - _data |
| Image$$RW_IRAM1$$ZI$$Base | RAM中BSS段起始地址 | _edata + 1 |
| Image$$RW_IRAM1$$ZI$$Length | BSS段的长度,单位是字节 | _ebss - _edata - 1 |
(6)如果想要了解如何自己在MDK上写一个清除BSS和复制粘贴数据段的代码,可以看看ARM架构与编程 · 基于STM32F103课程。
(7)不过这部分课程的代码我个人认为并不严谨,因为Image\$\$RW_IRAM1\$\$Length和Image\$\$RW_IRAM1\$\$ZI\$\$Length单位是字节,而不是字,所以这些数据需要除以4。再者,如上的数据均为32位无符号数据,韦东山老师的课程里面是以int数据进行处理,明显有隐患。至于为什么能跑,原因很简单,数据段将本来要复制的数据从Flash复制到RAM之后,还会复制一些本不需要从Flash的数据复制到RAM,虽然这部分数据不需要复制,但是并不影响程序运行。BSS同理。

有Bootloader的启动流程
(1)如果有Bootloader之后,清除完BSS,就会进入Bootloader判断是否需要升级程序。
(2)是否进行固件升级的判断可以从硬件和软件两个方面进行考虑,以下只是提供一种思路,各位可以自行扩展自己的思维:
- 硬件:通过拨码开关、跳线帽等方式设定单片机某一引脚电平状态,程序通过读取引脚电平判断是否需要升级。此种方式需要接触板卡进行操作,当板卡被封闭在外壳中或安装于不便于操作位置时很难实现。
- 软件:单片机每次上电首先进入BootLoader程序,在BootLoader中等待一定时间,若上位机软件在该时间段内发起通讯,则停留在BootLoader程序中等待固件升级;若该时间段内无通讯,则跳转到正常的APP程序。该方式每次上电都要等待一定时间,需要考虑是否可以介绍。

上位机和下位机的传输协议适配
(1)上位机和下位机传输数据是需要遵循一定的通讯协议的,但我们也可以不使用任何通讯协议。
(2)不使用通讯协议的坏处是,如果bin文件传输过程中一旦出现了一点点差错,就会导致整个应用程序崩盘。而且我们还无法知道到底是应用程序有bug,还是传输过程中出现了错误。
(3)但是不使用通讯协议也有一定的好处,就是会让整个Bootloader更加简单明了,便于新手学习理解。
(4)我们这里打算不适用通讯协议传输bin文件,因此需要使用不加通讯协议传输bin文件的工具,下载链接:http://47.111.11.73/docs/tool/ruanjian/ATK-XCOM.html
我们的愿景和需要的实现
(1)本系列教程的需要实现的需求如下:
<1>带着各位基于STM32F103的芯片,实现一个简单的串口升级下载的Bootloader程序。
<2>能够兼容SPI,I2C,CAN等外设升级的需求。
<3>Bootloader的传输不具备传输协议,加密功能。不支持分页设计。
参考
(1)C站:如何使用Linux编写STM32程序并且烧录
(2)C站:RAM明明断电会丢失数据,为什么初始化的全局变量存储在RAM?详细分析程序的存储
(3)B站:STM32的IAP技术,基于CAN总线的STM32F103 BootLoader设计
(4)面包板社区:程序本身如何知道自身大小?这是鸡生蛋还是蛋生鸡的问题!
(5)C站:STM32启动过程详解
(6)C站:STM32三种BOOT启动模式详解(全网最全)
(7)百问网:ARM架构与编程 · 基于STM32F103
(8)[野火]STM32 库开发实战指南——基于野火霸道开发板:46.4.3.5 hex 文件及 bin 文件。
1120

被折叠的 条评论
为什么被折叠?



