bootloader要做什么?错!应用代码正式运行前要准备什么?对

Bootloader是嵌入式系统在上电到main函数执行前的关键代码,主要负责代码的加载、存储位置与运行位置的处理、初始化全局变量(清bss段)、设置栈区、配置异常处理向量表、时钟、MPU和Cache等,确保系统的正常运行。此外,它可能包含汇编代码并可按需分级,例如在Linux加载场景中有多个级别的Bootloader。

一个定义:bootloader就是我们的嵌入式系统在上电到main函数开始执行中间所执行的代码。或者说,bootloader是正式的用户代码运行前的准备程序。
那bootloader到底要做什么?我觉得应该问:在运行正式代码时需要提前准备什么?

  1. 准备正式代码本身
    这个说法可能有点奇怪,奇怪的东西才能记住。代码的存储位置可能是在Flash, SD卡等媒介中,而运行位置可能是在RAM(内存)中,或者是Flah中就地运行;如果存储位置和运行位置不一致,自然就需要搬移。搬移就意味着要访问存储外设,需要DDR驱动或者Flash驱动等;
  2. 清bss段
    当把代码准备好了之后,理论上bootloader就可以直接跳到代码搬移后的地址开始运行了,现在提出第一个问题,我们知道一个被初始化为0或者没有初始化的全局变量是放在代码的bss段的,bss在上面1中所说的运行位置前后,还是初始的存储值,假如不对这块区域逐地址清除,我们使用的全局变量初始值就会出错。
  3. 配置栈区
    当代码开始运行,局部变量的定义、函数的调用都会涉及压栈、出栈的操作,堆栈的地址是存放在sp堆栈指针寄存器中的,如果我们不初始化sp直接使用,显然大错特错;
  4. 配置异常处理向量表
    这里的配置跟配置栈的原理是类似的,CPU运行发生异常后,如果不给指定去哪儿运行代码会导致程序崩溃;
  5. 配置时钟、MPU、Cache
    前面几条配置都是涉及代码的生死存亡的配置,不配置的话,代码要么运行错误,要么会崩溃。而如果时钟频率不配置,代码跑得倍儿慢,所以时钟要配置;MPU和Cache同理,都是影响代码运行性能的配置;
  6. 一些用户定制的功能
    用户可以添加shell,添加UART、Eth通信功能;可以加上升级功能、诊断功能等;

从前面的描述中我们可以总结出,bootloader的前一部分肯定是由汇编代码写成的,因为它本身有C语言的运行准备环境的功能。其次就是如果要完成的功能太多,bootloader可以分级,跳来跳去随心所欲,尤其是对于linux加载等场景,有多级的bootloader。

参考:

  1. STM32的完整启动流程分析 —— https://blog.youkuaiyun.com/Setul/article/details/121685929
  2. 【蛋饼嵌入式】我提着鞋带拎自己?嵌入式芯片启动过程全解析,彻底理解bootloader ——
    https://www.bilibili.com/video/BV1AN411R7Be/?spm_id_from=333.999.0.0&vd_source=d9ead405209236cb507b6c90c75777f9
### Bootloader 开发概念 Bootloader 是一种特殊的程序,负责初始化硬件并加载操作系统或其他应用程序。对于嵌入式系统而言,bootloader 主要功能是在设备启动时执行必要的初始化操作,并提供机制来更新固件。 在微控制器应用中,bootloader 可以通过多种接口接收新版本的应用程序代码,如 UART、USB 或 SPI 接口。一旦接收到新的二进制文件,bootloader 将其存储到闪存中的指定位置,并验证数据完整性[^1]。 对于 dsPIC 和 PIC24 系列单片机来说,bootloader 的设计通常涉及以下几个方面: - **引导过程**:当复位发生时,CPU 首先跳转至预定义地址运行 bootloader 代码。 - **通信协议**:用于与外部计算机交换数据,常见的是基于 UART 协议实现。 - **内存管理**:处理目标芯片内部 Flash 存储器的操作,包括擦除和编程特定扇区。 - **误检测与恢复**:确保传输过程中不会损坏现有应用程序,在失败情况下允许重新尝试上传。 ### C语言实现示例 下面是一个简单的 dsPIC/PIC24 UART bootloader 实现片段,展示了如何设置串行通信参数以及等待来自 PC 的命令帧: ```c #include <xc.h> #include "uart_bootloader.h" void setup_uart(void){ U1BRG = (FCY/BAUDRATE)/16 - 1; // 设置波特率 U1MODEbits.STSEL = 0; // 一位停止位 U1MODEbits.PDSEL = 0b00; // 无校验 U1STAbits.URXISEL = 0b00; // 中断级别选择 IEC0bits.U1RXIE = 1; // 启用接收中断 } int main(){ SYSTEM_Initialize(); // 初始化系统配置 while(1){ if(UART_DataReady()){ char cmd = UART_ReadChar(); switch(cmd){ case 'P': // 编程模式请求 enter_program_mode(); break; default: send_acknowledge('N'); // 发送否定确认 continue; } } // 处理其他状态... } } ``` 此代码段仅作为起点;实际项目还需要考虑更多细节,比如完整的 CRC 校验、更复杂的握手流程等。此外,为了支持不同型号的处理器,可能需要调整寄存器名称和其他硬件特性相关的部分。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值