您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center 汽车系统工程   模型库  
会员   
   
基于模型的数据治理与中台
11月11-12日 北京+线上
软件架构设计方法、案例与实践
11月13-14日 北京+线上
UML与面向对象分析设计
11月25-26日 北京+线上
     
   
 订阅
Bootloader科普:认识Bootloader作用与实现原理
 
作者:吃一嵌
  102   次浏览      8
 2025-9-26
 
编辑推荐:
本文主要介绍了从单片机新手到理解Bootloader的实现原理,揭秘如何通过双工程设计实现固件升级,以及Bootloader在OTA中的关键作用,希望对您的学习有所帮助。
本文来自于汽车电子与软件,由火龙果软件Alice编辑、推荐。

前言

回想起刚入门单片机,在学校使用单片机参加各种电子竞赛。

代码跑起来的流程可简单了。

只需要拷贝一份demo工程(比如Demo工程只实现GPIO点亮几颗LED)。

然后 在 工程上面加应用功能逻辑、编译,烧录,最后断开仿真器 ,代码就能直接跑起来了。

图片

简单来说,就是仅只使用一个工程,然后在上面实现我想要的应用功能就可以了。

这样的整套流程,可是搞了好几年。

后来毕业了,在一个公司实习,首次真正见识到了Bootloader。

当时感觉公司的代码太牛了!

它一个项目下面,有2个工程,一个工程叫App、一个工程叫Boot。

Boot的main函数,它跑了一些东西之后,会执行跳转,这个跳转代码很厉害,执行它就会跳到App工程里面(当时看这跳转代码,可是好久都没理解)。

在当时,脑袋瓜子对Boot的问题可是太多了。

Boot的大概作用,我百度一下倒是能懂,可以用于升级嘛。

但是,这两个工程它们是怎么衔接起来的?跟我以前接触的不一样啊?以前都是一个工程一烧录,就跑起来了。

好像还有一级Boot、二级Boot?这个听起来更牛了!搞那么多Boot干嘛?!

听说Bootloader会初始化必要的硬件外设,啥是必要的?为啥不能在App初始化?

当时还太菜了,怎么看都看不懂。

......

于是又好几年过去。

积累了各种MCU的知识后。

终于搞清楚了这Bootloader基本的来龙去脉。

1.认识Bootloader需了解的知识

在一个代码工程里面,东西多了去了:有函数,有局部变量,有全局变量,等等。

我们所谓的刷代码刷代码,到底刷的是啥?又是刷到MCU的哪里去?

如果这些都不知道,那还谈啥boot,谈啥升级呢。

1.ROM(PFlash)、RAM概念

首先,我们需要认识的是 ROM 和RAM( 这里我们主要是认识ROM )。

基础概念我们都知道:

ROM是断电数据不丢失的、只读的

RAM是断电后数据会丢失、可读可写的

但是,仅了解到这个层面还不够。

我们还需要深入去了解,在我们实际写的代码中,ROM对应哪些代码部分,RAM又对应哪些代码部分。

大家平时写的代码中,有函数、有局部变量、有全局变量、有常量等等,我们看看这些常见的代码都放到哪。

1.函数 、常量

函数是用来实现各种逻辑的代码,它是 只读 的。

常量则是不可修改的变量,也是 只读 的。

因此函数和常量都放在ROM里面 。

2.局部变量、全局变量

这两种类型变量的值都是代码运行过程中 可读可写 的,且MCU下电后数据会丢失的。

因此局部变量和全局变量放在RAM里面 。

2.MCU地址概念

在对ROM和RAM有了形象的认识后,我们就要看看它们在MCU芯片里面又是怎么放置的。

接下来我们就要去翻一翻MCU芯片手册了。

我们简单一点,把芯片理解成一个容器。

比如对于32位的MCU芯片,它的总大小就是:0x0000 0000~0xFFFF FFFF

ROM和RAM就放在这个容器(0x0000 0000 ~ 0xFFFF FFFF)里面。

于是我们翻到芯片手册关于地址的描述(以英飞凌TC275为例,Tc275它是3核芯片,但我们不用管它几个核)

然后我们找到ROM、RAM所在的位置。

ROM的地址共2个区域(大小共4M):

PFlash1:0x8000 0000 ~ 0x801F FFFF

PFlash2:0x8020 0000 ~ 0x803F FFFF

图片

RAM的地址区域就稍微分散一些 (这里简单截一张图):

图片

大家不用管它叫什么PSPR、DSPR啥的,反正它就是RAM (这款芯片的RAM区域具体如下图)

图片

好了, 现在我们稍微深入一些了解ROM和RAM之后。

我们再来看看刷代码是怎么个事。

比如我们这颗TC275芯片。

①对于代码工程里面的 函数、常量

所谓刷代码,就是把函数、常量这些东西刷进入了MCU的ROM所在的地址里面 。

画图举例如下:

图片

②对于工程里面的 全局 变量

所谓刷代码,就是把全局变量的地址,指定到MCU的某个RAM地址上面 。

比如定义一个全局变量 : uint16 u16TestVal ,它占用的空间是2个Byte。

假设我们把这个 u16TestVal 变量指定到0x5000 00 00 ~ 0x5000 0001这个地址上面,那么这个地址存的值就是 u16TestVal这个变量的值 。

画图举例如下:

图片

(我们这里只是简单举个ROM和RAM的例子,比如还有局部变量等等其它代码,就不展开讨论了)

2.认识Bootloader 作用与实现原理

到这里,你大概清楚了刷代码是刷了啥东西之后,你已经具备足够的知识去初步认识Bootloader的实现原理了。

第一步:

假设你手上有一个代码工程,并且只有一个main函数 ,先不用想这么复杂,mcu上电后,我们不管main之前还跑了别的什么东西,就假设main函数之前没有东西了。

你的代码只实现了一个功能,就是在main里面拉一下GPIO点个灯(LED1)。

图片

我们刚刚说了,函数放哪?放ROM嘛对吧。

因此,我们不妨直接把这个工程(即这个函数的起始位置)放到芯片手册里面ROM开始的地址:0x80000000(至于具体怎么放的,我们先不管)。

第二步:

你把刚刚那个工程拷贝一份, 然后改一下main函数的实现 内容(甚至你不改也行)。

图片

这个工程就改成点亮另一个LED灯吧(LED2)。

同样的,函数放ROM里面嘛。

对于这个工程2的起始地址(即工程2的main函数的起始地址),放到第1个工程的后面,我们给第1个工程一些预留位置(因为不可能一个工程就点个灯吧,后续要加功能的)

工程2起始地址就放到0x80200000 吧。

好了。经过了第一步、第二步的操作。

你现在MCU的ROM里面成功放了2个工程进去。

图片

同时,我们把MCU上电后跑起来的起始地址设为0x80000000,即工程1的main起始地址。

那么问题又来了,这两个工程怎么衔接起来?

这就需要靠下面这句跳转代码了:

((void (*)(void))(0x80200000))();

我们把这段代码放到工程1点亮LED1代码的后面。

图片

于是,MCU上电,代码跑起来后,工程1在点完LED1就会直接跳到工程2的main函数的起始地址,然后跑工程2的代码去点亮LED2了。

图片

说明:这里得再说一下, 实际工程的起始地址肯定不是main的,main的前面还会跑别的东西。我们这里只是简化理解。

但是,main前面的东西也一样是放在ROM里,所以我们现在不需要纠结main前面到底有啥。

到这里,很关键的一步已经完成了,你的MCU已经能跑2个工程了,已经有点Bootloader的雏形了。

接下来,我们再进一步。

比如我们简简单单,实现一个应用功能,比如两轮平衡小车的功能。

主要的逻辑功能放哪呢?

由于工程2的起始地址比工程1的靠后。

所以我们放到工程2里面,工程1我们后续有大用。

于是,在工程2里面就有了各种应用功能逻辑代码。

比如读取陀螺仪的方向转角代码啦,PID算法代码啦,等等。

图片

你好好地搞着平衡车的功能,突然有一天,天塌了。

烧录器被人偷了!没法烧录代码了!平衡小车功能还没优化完呢!

好在很久以前,你从网上ctrl-c、ctrl-v,移植了下面这样功能的代码到工程1里面,并且在烧录器没被偷之前就刷写进MCU了。

你移植到工程1的这代码干的事情也不多,主要是下面这些:

  1. 擦除指定ROM地址数据

    把 数据写入指定ROM地址之前,我们需要先把MCU这段ROM地址上的数据擦除 。

    比如我们现在需要刷写工程2到MCU里面,那么我们就需要从ROM地址0x80200000开始擦除,至于擦除到哪里结束,则取决于这个工程2使用的ROM大小了。

    又至于 它是怎么擦除的,实际代码就是调用一个函数接口,这个函数是MCU厂家的软件包就有这个接口 ,因此不需要担心。

  2. 接收升级包

    比如你编译工程2之后,会生成一个.hex文件。

    这个.hex文件就是升级包。

    然后,我们 电脑会有一个上位机(类似于串口工具那样的上位机),它可以载入这个.hex文件,然后通过UART(或者CAN)发送给MCU 。

    至于MCU如何接收整个数据包的过程,我们现在先不管。

  3. 把升级包写入指定ROM

    在擦除完成之后,就MCU就把接收到的升级包刷写至指定的ROM里面。

    至于它是怎么写入的,也是类似于擦除那样, MCU厂家的软件包就有这个函数调用接口,直接调用就行,只需要把数据本身、数据大小和写入地址传进函数里面,它就会去写入 了。

图片

到这里,我们整理一下:

工程2,实现各种想要的应用功能代码,用来搞功能逻辑的,比如你的平衡小车功能。

工程1,不搞应用逻辑功能,作用是通过UART(或CAN)直接把工程2的代码刷写进MCU。

好了,MCU里面有了工程1在,你的烧录器就算被偷了,在工程2需要升级的时候,工程1执行以下这3个关键步骤:

1.擦除工程2在MCU中所在的ROM地址

2.接收工程2的hex文件

3.把接收到的工程2的hex文件数据写入指定ROM地址

就能通过UART(或者CAN)直接把工程2的代码刷写进去了。

朋友们,这个工程1,不就是所以谓的Bootloader了嘛。

有了工程1在,我们的MCU不就具备升级功能了嘛。

我们这里接收升级包的方式是通过UART(或者CAN)。

但完全可以改成别的方式。

比如这个工程1可以通过云端远程接收数据包,然后对工程2进行升级。

这不就是所谓的OTA(Over The Air)了,你的手机、车、电脑不就是用这种方式升级的嘛!

3.结 语

朋友们,这里我们为了更清晰去理解Bootloader的作用和实现流程。

其中很多细节的东西我们都暂且忽略了(因此会有很多不严谨的地方,比如升级功能是Bootloader和App相互协作的,App也需要有跳转Bootloader的相关代码)。

后续就是一点点填坑,把这些细节一点点补上。

比如:

main前面还跑了什么东西,干啥用的?

具体要怎么指定代码工程的ROM到我们想要放到的MCU的ROM地址上(编译链接文件)?

Bootloader工程和App工程,他们使用的RAM又是怎么样去分配的?

Bootloader跑起来的时候,Bootloader它怎么知道当前是上电流程直接跑到App去还是执行升级流程升级App?

一级Boot、二级Boot又是怎么回事?

升级包接收的流程具体是怎么样的?

hex和bin有啥关系,好像两者都能用于升级?

等等......

真正要完整把Bootloader实现起来,细节可太多了,我们后续慢慢讲解。

   
102   次浏览       8 次
 
相关文章

CMM之后对CMMI的思考
对软件研发项目管理的深入探讨
软件过程改进
软件过程改进的实现
 
相关文档

软件过程改进框架
软件过程改进的CMM-TSP-PSP模型
过程塑造(小型软件团队过程改进)
软件过程改进:经验和教训
 
相关课程

以"我"为中心的过程改进(iProcess )
iProcess过程改进实践
CMMI体系与实践
基于CMMI标准的软件质量保证

最新活动计划
基于模型的数据治理与中台 11-11[北京]
软件架构设计方法、案例实践 11-13[北京]
OCSMP 认证培训课程 11-18[北京]
UML与面向对象分析设计 11-25[北京]
SysML和EA系统设计与建模 11-19[北京]
车载系统功能开发方法与实践 10-25[北京]
 
 
最新文章
iPerson的过程观:要 过程 or 结果
基于模型的需求管理方法与工具
敏捷产品管理之 Story
敏捷开发需求管理(产品backlog)
Kanban看板管理实践精要
最新课程
基于iProcess的敏捷过程
软件开发过程中的项目管理
持续集成与敏捷开发
敏捷过程实践
敏捷测试-简单而可行
更多...   
成功案例
英特尔 SCRUM-敏捷开发实战
某著名汽车 敏捷开发过程与管理实践
北京 敏捷开发过程与项目管理
东方证券 基于看板的敏捷方法实践
亚信 工作量估算
更多...