前言
(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动/单片机/RTOS的实习岗位,可C站直接私聊,或者邮件:[email protected],此消息至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
的传输不具备传输协议,加密功能。不支持分页设计。
参考
标签:bin,RAM,Flash,程序,Bootloader,IAP,前置 From: https://blog.csdn.net/qq_63922192/article/details/136713319(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 文件。