[应用相关] 裸机启动流程

[复制链接]
684|10
 楼主| 小海师 发表于 2025-5-10 18:58 | 显示全部楼层 |阅读模式
一、STM32 启动模式选择与地址映射
STM32 上电后,通过 BOOT 引脚配置不同的启动方式,决定 MCU 是从 Flash 启动、从 SRAM 启动,还是进入系统 Bootloader。

1.1 启动方式总览

25686681d4c02749e2.png

1.2 启动地址别名机制
STM32 将 0x0000 0000 视为启动地址,它是对真实存储区域的映射(alias):

15005681d4bf63d3c4.png

1.3 BOOT 引脚配置方式
开发板:使用拨码开关或跳线帽配置 BOOT0/BOOT1
正式产品:可通过 Option Bytes 写死启动模式,避免引脚漂移
1.4 进入串口烧录模式步骤(以 STM32F1 为例)
设置 BOOT0 = 1, BOOT1 = 0
上电或复位,MCU 进入 System Memory
连接串口,使用 STM32CubeProgrammer 烧录程序
1.5 启动源检测(调试时使用)
if ((SCB->VTOR & 0x2FFE0000) == 0x20000000) {
  // SRAM 启动
} else if ((SCB->VTOR & 0x2FFE0000) == 0x1FFF0000) {
  // Bootloader 启动
} else {
  // Flash 启动
}


二、STM32 裸机启动流程概述
STM32 裸机开发启动流程是指在没有操作系统参与的情况下,从上电复位到执行 main() 函数的全过程。这个过程涉及启动汇编文件、系统初始化、C运行时初始化以及用户主函数的调用。

2.1 流程图
上电/复位
   ↓
读取向量表地址 (栈顶地址, Reset_Handler)
   ↓
Reset_Handler
   ├─> 初始化 .data/.bss 段
   ├─> 调用 SystemInit()
   ├─> 调用 __libc_init_array()
   └─> main()


三、启动文件 startup_stm32xxx.s
启动文件是一个汇编文件,包含两部分:中断向量表定义和 Reset_Handler 实现。

3.1 中断向量表定义
.section .isr_vector,"a",%progbits
.word _estack              // 栈顶地址(链接脚本定义)
.word Reset_Handler        // 复位中断向量
.word NMI_Handler
.word HardFault_Handler
...


3.2 Reset_Handler 函数实现
Reset_Handler:
  LDR R0, =_sidata      // flash 中 .data 初始化数据
  LDR R1, =_sdata       // RAM 中 .data 起始地址
  LDR R2, =_edata       // RAM 中 .data 结束地址
Loop_Copy_Data:
  CMP R1, R2
  ITTT LT
  LDRLT R3, [R0], #4
  STRLT R3, [R1], #4
  BLT Loop_Copy_Data

  LDR R1, =_sbss
  LDR R2, =_ebss
  MOVS R3, #0
Loop_Zero_BSS:
  CMP R1, R2
  IT LT
  STRLT R3, [R1], #4
  BLT Loop_Zero_BSS

  BL SystemInit          // 初始化系统时钟
  BL __libc_init_array   // C库初始化
  BL main                // 进入主函数



这些地址符号来自链接脚本(.ld 文件):

51182681d4bd6ee475.png

四、SystemInit() — 系统时钟初始化
定义在 system_stm32xxx.c,由 ST 官方提供,常见初始化内容:

打开 HSE/HSI、配置 PLL
设置 Flash 等待周期
切换系统时钟为 PLL
设置 AHB/APB 分频器
示例(STM32F1):
void SystemInit(void)
{
  RCC->CR |= RCC_CR_HSION;  // 开启内部高速时钟
  RCC->CFGR = 0x00000000;   // 清除分频设置
  RCC->CR &= ~(RCC_CR_HSEON | RCC_CR_PLLON);
  RCC->PLLCFGR = ...        // 配置 PLL 参数

  FLASH->ACR |= FLASH_ACR_LATENCY_5WS; // 设置 Flash 延时

  RCC->CR |= RCC_CR_PLLON;             // 开启 PLL
  while ((RCC->CR & RCC_CR_PLLRDY) == 0); // 等待锁定

  RCC->CFGR |= RCC_CFGR_SW_PLL;        // 切换系统时钟为 PLL
}


五、__libc_init_array() — 初始化 C/C++ 运行环境
这个函数位于标准 C 库中(如 newlib),主要功能:

调用 .preinit_array 函数(用于 C++)
调用 .init_array 中的构造函数
初始化静态变量
void __libc_init_array(void)
{
  for (i = 0; i < __preinit_array_end - __preinit_array_start; i++)
    __preinit_array_start();

  for (i = 0; i < __init_array_end - __init_array_start; i++)
    __init_array_start();
}


如果是纯 C 项目,可以选择不使用它,而直接在 Reset_Handler 中清理 .bss、复制 .data。

六、main() 函数 — 用户应用程序入口
int main(void)
{
  init_gpio();
  init_uart();

  while (1) {
    toggle_led();
    delay_ms(500);
  }
}


main() 是用户写的程序入口,往往会初始化外设,然后进入主循环。

七、小结:完整启动流程表

69245681d4bb4a1599.png

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/hallo_zz/article/details/147712387

磨砂 发表于 2025-6-6 16:20 | 显示全部楼层
裸机启动流程主要指STM32系列微控制器从上电复位到执行用户主函数的过程
晓伍 发表于 2025-6-6 18:32 | 显示全部楼层
STM32通过[size=0.875]BOOT0[size=0.875]BOOT1引脚配置启动模式,决定程序从哪个存储区域启动
八层楼 发表于 2025-6-6 20:50 | 显示全部楼层
正式产品可通过Option Bytes固化[size=0.875]BOOT配置,避免引脚漂移
观海 发表于 2025-6-6 23:01 | 显示全部楼层
CPU从固定地址[size=0.875]0x00000000开始执行代码,该地址是中断向量表的起始位置,实际映射到具体存储区域由[size=0.875]BOOT引脚决定
guanjiaer 发表于 2025-6-7 09:22 | 显示全部楼层
启动文件是汇编文件,负责硬件初始化和跳转到用户代码
heimaojingzhang 发表于 2025-6-7 11:50 | 显示全部楼层
    .section .isr_vector,"a",%progbits
    .word   _estack          // 初始堆栈指针
    .word   Reset_Handler    // 复位向量
    .word   NMI_Handler      // 非掩蔽中断向量
    .word   HardFault_Handler// 硬故障向量

Reset_Handler:
    LDR R0, =_sidata      // Flash中.data段初始值
    LDR R1, =_sdata       // SRAM中.data段起始地址
    LDR R2, =_edata       // SRAM中.data段结束地址
Copy_Data:
    CMP R1, R2            // 比较地址
    LDRLT R3, [R0], #4   // 从Flash读取数据
    STRLT R3, [R1], #4   // 写入SRAM
    BLT Copy_Data         // 循环直到.data段复制完成

    LDR R1, =_sbss        // .bss段起始地址
    LDR R2, =_ebss        // .bss段结束地址
    MOVS R3, #0
Zero_BSS:
    CMP R1, R2
    STRLT R3, [R1], #4   // 清零.bss段
    BLT Zero_BSS

    BL SystemInit         // 初始化系统时钟
    BL __libc_init_array // C库初始化
    BL main               // 跳转至main函数


keaibukelian 发表于 2025-6-7 14:12 | 显示全部楼层
初始化堆栈的时候会从向量表读取MSP值,设置堆栈指针
paotangsan 发表于 2025-6-7 16:33 | 显示全部楼层
开发阶段可灵活切换[size=0.875]BOOT引脚,正式产品建议通过Option Bytes固化配置
renzheshengui 发表于 2025-6-7 19:03 | 显示全部楼层
若需修改中断向量表地址,可通过[size=0.875]SCB->VTOR寄存器设置,但需确保对齐要求
wowu 发表于 2025-6-7 21:19 | 显示全部楼层
[size=0.875]SystemInit()中添加断点,验证时钟配置是否正确
您需要登录后才可以回帖 登录 | 注册

本版积分规则

79

主题

242

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部