打印
[应用相关]

裸机启动流程

[复制链接]
40|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、STM32 启动模式选择与地址映射
STM32 上电后,通过 BOOT 引脚配置不同的启动方式,决定 MCU 是从 Flash 启动、从 SRAM 启动,还是进入系统 Bootloader。

1.1 启动方式总览



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



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 文件):



四、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() 是用户写的程序入口,往往会初始化外设,然后进入主循环。

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



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

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

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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

36

主题

110

帖子

0

粉丝