首页 > 其他分享 >Bootloader/IAP零基础入门(0) —— Bootloader/IAP的前置知识

Bootloader/IAP零基础入门(0) —— Bootloader/IAP的前置知识

时间:2024-03-22 18:31:32浏览次数:16  
标签:bin RAM Flash 程序 Bootloader IAP 前置

前言

(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-LinkST-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)已经使用ICPISP烧到FLASH中。

在这里插入图片描述

(2)无论是ICP技术还是ISP技术,都需要连接下载线,设置跳线帽等操作。一般来说,产品的电路板都会密封在外壳中,在这时若要使用ICPISP的方式对程序进行更新,则必然要拆装外壳,如果产品的数量比较多,将花费很多不必要的时间。
(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为例,这款芯片的Flash512KBRAM40KB。如下为有无Bootloader的Flash空间介绍,可以看出,如果有Bootloader之后,同一款芯片的应用程序空间会被挤压,这也是需要付出的代价。不过一般来说,Bootloader空间不大,而且如今的存储器已经不那么昂贵,为了方便开发,提高效率,这点牺牲是可接受的。

在这里插入图片描述

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

在这里插入图片描述

如果分配Bootloader和APP的Flash和RAM

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

在这里插入图片描述

为什么Bootloader升级是需要bin文件

(1)如果我们使用Bootloader升级是需要使用bin文件的。但是我们都知道,程序下载又有binhexaxf这三种文件,那么这三者有什么区别呢?

  • 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)fromelfkeil官方提供的一个工具,这个工具能够根据你输入的不同提示信息来做出相应的处理。如果有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$$BaseFlash中数据段起始地址_data_loadaddr
Image$$RW_IRAM1$$BaseRAM中数据段起始地址_data
Image$$RW_IRAM1$$Length需要搬运的数据段长度,单位是字节_edata - _data
Image$$RW_IRAM1$$ZI$$BaseRAM中BSS段起始地址_edata + 1
Image$$RW_IRAM1$$ZI$$LengthBSS段的长度,单位是字节_ebss - _edata - 1

(6)如果想要了解如何自己在MDK上写一个清除BSS和复制粘贴数据段的代码,可以看看ARM架构与编程 · 基于STM32F103课程。
(7)不过这部分课程的代码我个人认为并不严谨,因为Image\$\$RW_IRAM1\$\$LengthImage\$\$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>能够兼容SPII2CCAN等外设升级的需求。
<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 文件。

标签:bin,RAM,Flash,程序,Bootloader,IAP,前置
From: https://blog.csdn.net/qq_63922192/article/details/136713319

相关文章

  • uniapp开发ios,scroll-view横向滚动失效,动态获取scroll-view内部子容器总宽度,然后添加
    这是老bug了,官方一直没有解决掉。已经摸索到完美解决方案,遇到这个问题的可以看下。本文以三级导航页面中的二级横向滚动导航为例,说明如何做到不同宽度子元素的横向滚动。bug定位:本来横向滚动只要子元素宽度大于scroll-view固定宽度就可以滚动的,但是IOSApp开发中子元素高度必须......
  • uniapp根据链接生成二维码
    1.我们在根目录common中新建一个js文件2.然后再这个js文件当中添加以下这些代码//uqrcode.js//---------------------------------------------------------------------//githubhttps://github.com/Sansnn/uQRCode//----------------------------------------------......
  • 复试C++19真题_看程序写结果_前置++运算符重载 易错
    考察前置++运算符设置为友元函数,这题的坑在于,返回值是不是对象的引用,形参也不是对象的引用,导致自增离开了作用域以后就不在有任何效果。#include<iostream>usingnamespacestd;classC{private:intxx,yy;public:C(intx,inty):xx(x),yy(y){}friendC......
  • uniapp发布到h5调用Android原生拍照,相册,uniapp如何显示并上传
    先说一下我这边的需求:uniapp这边发布到h5,然后调用Android原生拍照,选择相册,最后将路径回调到uniapp,uniapp那边image要优先将图片回显出来,并将图片上传到服务器遇到的问题:1调用原生拍照,选择相册回调到uniapp,uniapp的image控件无法展示Android那边回调回来的图片路径,但是如......
  • uniapp编辑页修改数据后返回到列表页(上一页)或首页,列表页或首页数据不刷新问题
    uniapp编辑页(当前页)等修改数据后返回到列表页(上一页)或多次返回上一页到首页(非上一页),列表页或首页数据不刷新问题解决方法:一、返回到列表页(上一页)1、首先在编辑页的修改数据的成功回调中调用uni.$emit('refreshData');向上一页传递refreshData事件2、然后在列表页(上一......
  • uniapp 蓝牙连接斑马打印机发送zpl指令打印
    历程需求是想通过斑马的zpl语言打印小票等,需要用到蓝牙连接。一开始采用的是uniapp自带的蓝牙连接和打印,用的是uni.writeBLECharacteristicValue(OBJECT)方法,蓝牙能正常连接和发送数据。奇怪的是发送蓝牙数据始终都是ok,打印机确是一点动静都没有。基于以上的疑惑,我开始怀疑是u......
  • uniapp怎么获取元素高度,获取元素节点信息,获取界面剩余高度
    一、在uniapp项目开发的过程中,常常需要获取元素的高度信息,来更容易的实现元素的布局控制,使用场景:列入动态的计算容器的高度,设置组件的最小高度等等在获取元素节点信息中,通常有两种情况:①获取单个②获取v-for循环元素的节点信息,话不多说,直接上代码注意:需要在onReady()之后获取,否则......
  • uniapp获取定位导致APP闪退
    刚开始用setInterval定时1秒获取地图定位,因为uni.getLocation经常会返回失败,导致整个定时器错乱闪屏崩溃。本页面有一个webview,通过evalJS执行webview嵌套页面的方法。<web-view@message="getMessage"v-if="url":src="url"></web-view> 错误代码:this.time=setInterv......
  • uniapp 开发模板
    简介vue3-uniapp-template是基于vue3的uniapp快速开发模板,包含状态管理、网络请求、路由拦截、UI组件等常用功能。主要使用的技术栈:uniapp、vue3、pinia、vite、uv-ui下载地址PS:如果对你有帮助的话,点个Star支持下哈~GithubGitee项目启动#克隆代码gitclonehttps://gi......
  • 【uniapp】表单验证不生效的解决方案
    表单验证这个常见的功能,明明在elementui等框架已经用的很熟了,在uniapp开发时还是处处碰壁?这篇文章我会提示uni-forms表单验证的几个注意点,帮助大家排查。示例下面是一份包含普通验证和自定义验证的示例:<uni-formsref="baseForm":rules="rules":modelValue="form"> <un......