一个定义:bootloader就是我们的嵌入式系统在上电到main函数开始执行中间所执行的代码。或者说,bootloader是正式的用户代码运行前的准备程序。
那bootloader到底要做什么?我觉得应该问:在运行正式代码时需要提前准备什么?
- 准备正式代码本身
这个说法可能有点奇怪,奇怪的东西才能记住。代码的存储位置可能是在Flash, SD卡等媒介中,而运行位置可能是在RAM(内存)中,或者是Flah中就地运行;如果存储位置和运行位置不一致,自然就需要搬移。搬移就意味着要访问存储外设,需要DDR驱动或者Flash驱动等; - 清bss段
当把代码准备好了之后,理论上bootloader就可以直接跳到代码搬移后的地址开始运行了,现在提出第一个问题,我们知道一个被初始化为0或者没有初始化的全局变量是放在代码的bss段的,bss在上面1中所说的运行位置前后,还是初始的存储值,假如不对这块区域逐地址清除,我们使用的全局变量初始值就会出错。 - 配置栈区
当代码开始运行,局部变量的定义、函数的调用都会涉及压栈、出栈的操作,堆栈的地址是存放在sp堆栈指针寄存器中的,如果我们不初始化sp直接使用,显然大错特错; - 配置异常处理向量表
这里的配置跟配置栈的原理是类似的,CPU运行发生异常后,如果不给指定去哪儿运行代码会导致程序崩溃; - 配置时钟、MPU、Cache
前面几条配置都是涉及代码的生死存亡的配置,不配置的话,代码要么运行错误,要么会崩溃。而如果时钟频率不配置,代码跑得倍儿慢,所以时钟要配置;MPU和Cache同理,都是影响代码运行性能的配置; - 一些用户定制的功能
用户可以添加shell,添加UART、Eth通信功能;可以加上升级功能、诊断功能等;
从前面的描述中我们可以总结出,bootloader的前一部分肯定是由汇编代码写成的,因为它本身有C语言的运行准备环境的功能。其次就是如果要完成的功能太多,bootloader可以分级,跳来跳去随心所欲,尤其是对于linux加载等场景,有多级的bootloader。
参考:
- STM32的完整启动流程分析 —— https://blog.csdn.net/Setul/article/details/121685929
- 【蛋饼嵌入式】我提着鞋带拎自己?嵌入式芯片启动过程全解析,彻底理解bootloader ——
https://www.bilibili.com/video/BV1AN411R7Be/?spm_id_from=333.999.0.0&vd_source=d9ead405209236cb507b6c90c75777f9