当前位置: 首页 > web >正文

Bootloader(1):初步认识Bootloader概念(什么是Bootloader)

前言

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

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

只需要拷贝一份demo工程(可能只实现GPIO点亮几颗LED的功能)。

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

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

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

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

当时感觉好牛。

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

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

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

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

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

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

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

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

......

又好几年过去。

积累了各种MCU的知识后。

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

认识Bootloader需了解的知识

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

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

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

ROM(PFlash)、RAM概念

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

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

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

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

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

大家平时写的代码中,有函数、有局部变量、有全局变量、有常量。

其中

1、函数、常量

函数是用来实现各种逻辑的代码。

常量是不可变化的只读变量。

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

2、局部变量、全局变量

这2种变量都是代码运行过程中可以修改的、且下电会丢失的。

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

MCU地址概念

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

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

我们简单一点,把芯片理解成一个容器,因此,在这个容器里面一定有地方放我们上面说到的ROM和RAM。

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

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

PFlash1:0x8000 0000 ~ 0x801F FFFF

PFlash2:0x8020 0000 ~ 0x803F FFFF

RAM地址的区域就多了一些了(虽然分开的区域多,但空间大小是远小于ROM空间的)

大家不用管它叫什么PSPR、DSPR啥的,反正它就是RAM。

具体RAM地址举例如下:


好了, 现在稍微深入点了解了ROM和RAM之后。

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

比如我们这颗TC275芯片。

对于工程里面的函数、常量这些东西

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

画图举例如下:

对于工程里面的全局变量

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

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

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

画图举例如下:

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

初步认识Bootloader原理

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

接下来,我们再简单一点。

第一步:

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

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

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

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

第二步:

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

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

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

对于这个工程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在,你的烧录器就算被偷了,也能通过UART(或者CAN)直接把工程2的代码刷写进去:

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

2、接收工程2的hex文件

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

这个工程1的作用,就是执行这3个关键步骤去升级工程2。

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

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

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

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

比如通过以太网从云端远程接收数据包,然后进行升级。

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

结语

我们这里是简化了很多东西去讲Bootloader的大致的流程。

很多细节的东西我们都暂且忽略了,接下来就是一点点填坑,把这些细节一点点补上了。

比如:

main前面有什么东西,干啥用的?

工程的起始地址具体要怎么放到指定的ROM地址上?

Bootloader工程和App工程它们用的RAM是一起用的还是分开用的?

Bootloader跑起来的时候,怎么知道当前是上电流程还是升级流程?(如果是上电流程就要直接跳App工程,如果是升级流程就要停留在Bootloader里面跑升级功能)

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

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

App能不能倒反天罡,去升级Boot?

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

等等......

真正要搞起来,细节可太多了。

 

 

 

 

http://www.xdnf.cn/news/19603.html

相关文章:

  • 基于muduo库的图床云共享存储项目(三)
  • Ansible配置文件与主机清单
  • juicefs+ceph rgw 存储安装
  • MYSQL表的增删改查
  • 深入解析数据结构之单链表
  • ros2bag_py的api小结、丢帧问题对策
  • 【Linux基础】Linux系统启动:深入理解GRUB引导程序
  • 平面椭圆转化为三阶Bezier曲线的方法
  • 并发编程——10 CyclicBarrier的源码分析
  • 大模型参数到底是什么?
  • synchronized的锁对象 和 wait,notify的调用者之间的关系
  • EKS上部署gpu服务利用karpenter实现自动扩缩(s3作为共享存储)
  • 一、计算机系统知识
  • C++ 枚举算法详细利用与数字分解教学教案
  • Spring Security 6.x 功能概览与代码示例
  • 程序员独立开发直播卖产品 SOP 教程
  • arm容器启动spring-boot端口报错
  • 基于开源AI大模型、AI智能名片与S2B2C商城小程序的“教育用户”模式探究
  • 谈谈对BFC的理解
  • 当代科学(范畴大辩论) 的学科分科(论据)的要素论(论点)及方法论(论证):边缘处理
  • 浅谈 SQL 窗口函数:ROW_NUMBER() 与聚合函数的妙用
  • 机器视觉opencv教程(三):形态学变换(腐蚀与膨胀)
  • 利用爬虫获取淘宝商品信息,参数解析
  • 基于单片机停车场管理系统/车位管理/智慧停车系统
  • 小迪自用web笔记22
  • Java线程池使用入门
  • uvm验证环境中struct(结构体)和class的区别与联系
  • 基于单片机老人防丢失防摔倒系统/老人健康状态检测系统
  • CMake⼯程指南-3
  • [光学原理与应用-361]:ZEMAX - 分析 - 像差分析