理解Bootloader

1.单片机的组成

  • Flash 相当于电脑硬盘,用于存储代码区,例如编译生成的 hex 文件。
  • SRAM 相当于内存条,用于单片机在运行的时候需要的数据,资源,例如局部变量,数组什么的,断电丢失。

两个需要的知道的事情:

  • 嵌入式 SRAM 被映射在地址  0x20000000
  • 嵌入式 Flash 被映射在地址    0x80000000

2.STM32的启动流程

  • 上电复位
  • 读取 MSP(Main Stack Pointer)
    内核会先访问内存地址 0x00000000,取出 4 个字节的数据,将其赋值给主堆栈指针 MSP

    DCD __initial_sp
  • 读取 PC (Program Counter)
    接着,内核访问内存地址 0x00000004,取出 4 个字节的数据,将其赋值给程序计数器 PC。这个地址就是 Reset_Handler(复位中断服务程序)的入口地址。

    DCD Reset_Handler
  • 执行复位中断服务程序
    一旦 PC 被赋值,CPU 就跳转到 Reset_Handler 开始执行第一条指令。这是软件启动的起点。

    ; Reset handler
    Reset_Handler   PROC
                  EXPORT  Reset_Handler             [WEAK]
                  IMPORT  __main
                  IMPORT  SystemInit
                  LDR     R0, =SystemInit
                  BLX     R0               
                  LDR     R0, =__main
                  BX      R0
                  ENDP

    这个函数主要做两件事:
    1.调用 SystemInit

    • 指令:LDR R0, =SystemInit 然后 BLX R0
    • 作用:初始化微控制器的时钟系统(RCC)。它通常配置外部晶振(HSE)、锁相环(PLL)以及系统总线时钟。
    • 注意:在执行这一步之前,STM32 运行在内部低速时钟(HSI)上。执行完这一步后,STM32 通常就运行在配置好的高速时钟(如 72MHz)上了。它还会配置向量表偏移寄存器 (VTOR),这对于 Bootloader 和 App 跳转非常重要。

    2.跳转到 __main

    • 指令:LDR R0, =__main 然后 BX R0
    • 作用:跳转到 C 库的初始化入口。注意,这里不是用户的 main() 函数,而是编译器提供的 C 运行时库入口。
  • C 库初始化
    __main 是编译器(Keil MDK)提供的标准库函数,它负责为 C 语言代码的运行准备环境。它主要完成以下工作:

    • 数据拷贝 (Copy Data):将初始化数据段(.data 段,例如 int a = 10;)从 Flash 拷贝到 SRAM 中。
    • 清零 BSS (Zero BSS):将未初始化的全局变量段(.bss 段,例如 int b;)在 SRAM 中清零。
    • 初始化堆栈:设置堆(Heap)和栈(Stack)的初始环境。
    • 跳转到 main():环境准备好后,__main 函数最后会调用用户的 main() 函数。
  • 进入用户主函数
    此时,程序控制权正式交给用户,开始执行 main.c 中的逻辑(例如初始化 GPIO、外设,进入 while(1) 循环)。

添加新评论