【STM32】裸机启动流程

STM32 裸机启动流程详解

一、STM32 启动模式选择与地址映射

STM32 上电后,通过 BOOT 引脚配置不同的启动方式,决定 MCU 是从 Flash 启动、从 SRAM 启动,还是进入系统 Bootloader。

1.1 启动方式总览

启动模式BOOT 引脚配置启动地址用途
Main FlashBOOT0 = 00x0800 0000默认从用户程序启动
System MemoryBOOT0 = 1, BOOT1 = 00x1FFF xxxx启动内置 Bootloader,支持串口/I2C
SRAMBOOT0 = 1, BOOT1 = 10x2000 0000用于调试程序,通常用于开发阶段

1.2 启动地址别名机制

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

BOOT模式0x0000 0000 映射地址实际跳转
Flash 启动0x0800 0000用户程序
System Memory 启动0x1FFF F000ST Bootloader
SRAM 启动0x2000 0000RAM 中程序

1.3 BOOT 引脚配置方式

  • 开发板:使用拨码开关或跳线帽配置 BOOT0/BOOT1
  • 正式产品:可通过 Option Bytes 写死启动模式,避免引脚漂移

1.4 进入串口烧录模式步骤(以 STM32F1 为例)

  1. 设置 BOOT0 = 1, BOOT1 = 0
  2. 上电或复位,MCU 进入 System Memory
  3. 连接串口,使用 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 文件):

符号含义
_sidata.data 初始化数据(Flash)
_sdata.data 段起始地址(RAM)
_edata.data 段结束地址(RAM)
_sbss.bss 段起始地址
_ebss.bss 段结束地址
_estack栈顶地址

四、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[i]();

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

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


六、main() 函数 — 用户应用程序入口

int main(void)
{
  init_gpio();
  init_uart();

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

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


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

阶段执行内容
启动模式通过 BOOT 引脚或 option bytes 选择启动源
启动汇编设置栈,拷贝 .data,清 .bss
SystemInit配置系统时钟、PLL、Flash
libc initC/C++构造函数,全局变量初始化
main用户应用程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值