MCU启动过程简介
第一阶段:芯片内置硬件自动处理 (上电复位后)
这是完全由硬件自动完成的步骤,程序员无法干预,但必须了解。
上电复位 (Power-On Reset)
当给 MCU 施加电源后,内部的复位电路会保持 MCU 处于复位状态,直到电源电压稳定达到正常工作范围(例如 3.3V 或 5.0V)。这确保了芯片不会在电压不稳时发生误操作。
时钟初始化
复位释放后,MCU 首先会使用其内部RC振荡器 (Internal RC Oscillator) 作为初始时钟源。这是因为内部RC振荡器起振快,但精度较低。它为后续更复杂的初始化过程提供基本的时钟驱动。
获取初始堆栈指针 (SP) 和程序计数器 (PC)
这是最关键的一步。CPU 会从中断向量表 (Interrupt Vector Table) 的起始地址(通常是 Flash 存储器的第一个地址,如
0x0000_0000
)读取前两个字(Word):第一个字:加载到主堆栈指针 (MSP) 寄存器。这是系统启动后使用的第一个堆栈,为后续的C语言函数调用提供内存空间。
第二个字:加载到程序计数器 (PC) 寄存器。这个值通常是复位异常处理函数 (Reset_Handler) 的地址。CPU 会跳转到这个函数开始执行代码。
第二阶段:启动文件执行 (Reset_Handler)
从这一步开始,软件开始接管。芯片厂商会提供一个默认的启动文件(Startup File,通常是汇编和C混合编写,如 startup_stm32fxxx.s
)。这个文件中的 Reset_Handler
函数负责为运行C语言代码搭建一个完整的环境。
初始化堆栈指针 (SP)
虽然硬件已经设置了MSP,但一些复杂的系统(如带有RTOS或双堆栈的ARM Cortex-M芯片)可能还会在这里重新设置或初始化堆栈。
初始化数据段 (.data)
问题:已初始化的全局变量和静态变量(如
int a = 100;
)的初始值存储在Flash中,但它们的运行时值需要在可读写的RAM中。操作:启动代码会将这部分变量的初始值从Flash拷贝到RAM中对应的
.data
区域。
清零BSS段 (.bss)
问题:未初始化的全局变量和静态变量(如
int b;
)默认值应为0,它们被归类在.bss
段。操作:启动代码会将整个
.bss
段所在的RAM区域全部清零。这确保了这些变量从0开始。
初始化标准库 (可选)
如果用户程序使用了标准库函数(如
printf
,malloc
),这里会调用SystemInit
等函数来初始化系统时钟(切换到外部晶振、配置PLL提高频率)、初始化内存管理单元等。
跳转到 main 函数
在所有准备工作完成后,
Reset_Handler
会最终跳转到用户的main()
函数。从此,控制权完全交给了用户的应用程序。
第三阶段:用户主程序 (main())
用户程序开始执行,通常包含以下步骤:
硬件外设初始化
在
main()
函数中,首先要初始化所需的各种外设,如GPIO(设置输入/输出模式)、UART(配置波特率)、SPI、I2C、ADC等。配置系统时钟(如果启动文件中没有完全配置的话)。
设置中断优先级和使能中断。
RTOS初始化(如果使用)
对于运行实时操作系统(RTOS)的系统,会在这里创建任务(Task)、队列(Queue)、信号量(Semaphore)等,然后启动调度器(Scheduler)。
进入主循环 (Main Loop)
最后,程序通常会进入一个无限的
while(1)
循环,在这里不断地执行主要的应用程序逻辑,如读取传感器数据、处理用户输入、控制执行器等。
总结与要点
阶段 | 执行者 | 核心任务 | 是否可编程 |
---|---|---|---|
第一阶段 | 硬件 | 稳定电压、提供基础时钟、读取SP和PC | 否 |
第二阶段 | 启动文件 (Reset_Handler ) | 搭建C语言环境:初始化堆栈、.data段、.bss段 | 是(需修改启动文件) |
第三阶段 | 用户程序 (main() ) | 初始化外设、创建任务、执行应用逻辑 | 是 |
关键概念:
中断向量表 (IVT):一个存放着所有异常和中断处理函数地址的表格,是硬件和软件连接的桥梁。
启动文件:芯片厂商提供,是项目的重要组成部分,它完成了硬件到软件的关键过渡。理解它对于深入理解嵌入式系统至关重要。
链接脚本 (Linker Script):它定义了内存布局(Flash和RAM的地址范围),并指定了各个段(如
.text
,.data
,.bss
)在内存中的存放位置。启动文件的操作依赖于链接脚本的定义。
希望这份详细的分解能帮助你彻底理解MCU的启动过程!