第 10 章:MCU 上电、启动、中断向量与 main() 的全流程(BootROM → OS)
MCU上电启动流程详解:从硬件初始化到操作系统接管 摘要:MCU上电后首先执行芯片固化的BootROM代码,完成电源检测、时钟初始化和安全检查。随后读取复位向量跳转到启动文件,进行栈指针设置、中断向量表搬运等操作。C运行时环境初始化(crt0)负责.data段初始化和.bss段清零,为main()函数创建可预期的运行环境。对于RTOS系统,main()将启动任务调度器,而AUTOSAR OS则调用
(本系列从硬件 → 架构 → 中断 → 上下文 → OS → 车规 OS → 编译器 → 上电流程 → 全栈图 升级推进)
目录
10.8 第六步:操作系统开始接管(RTOS 或 AUTOSAR OS)
10.1 MCU 上电到底发生了什么?
当 MCU 上电的那一刻,用户的 C 代码、main()、RTOS、AUTOSAR OS 都还没有出现。
最先运行的永远是芯片内部固化的 BootROM。
BootROM 是 MCU 厂商写死在芯片里的“最底层初始化逻辑”,主要负责:
电源稳定检测
时钟初始化
安全检查(例如 ECC / Boot 校验)
读取复位向量
跳转到用户程序入口
上电流程高度架构化,跟操作系统完全无关。
10.2 MCU 的启动顺序(抽象结构)
无论 Cortex‑M、RH850、TriCore,整体流程都类似:
上电
↓
BootROM(固化代码)
↓
设置栈指针(SP)
↓
读取复位向量(Reset Vector)
↓
跳转到 Startup(启动文件)
↓
C 运行时初始化(crt0)
↓
初始化 .data / .bss
↓
调用 main()
↓
如果是 OS 系统 → 启动 OS
↓
进入任务调度器
下面逐步拆开。
10.3 第一步:BootROM(MCU 厂商固化代码)
BootROM 是芯片内部只读区域,由 MCU 厂商实现,用于:
- 早期硬件上电检查
- 时钟源设置
- 寄存器初始值配置
- 安全机制(ECC、Flash 校验)
- 选择 Boot 模式(Normal Boot / Flash Boot / Serial Boot)
- 跳转到用户程序的 Reset Handler
用户看不到 BootROM,也无法修改它。
10.4 第二步:读取复位向量(Reset Vector)
当芯片完成 BootROM 的基本初始化后,会读取一个固定地址作为初始入口点。
举例(假设地址,不对应任何实际 MCU):
0x0000_0000 → 初始栈指针(Initial SP)
0x0000_0004 → Reset_Handler 地址
CPU 做两件事:
SP ← *(0x0000_0000)
PC ← *(0x0000_0004)
程序计数器(PC)加载 Reset_Handler 后开始执行你的“启动文件”。
Cortex‑M、RH850、TriCore 均类似,只是向量表布局不同。
10.5 第三步:执行 Startup(启动文件)
Startup 通常是 .s(汇编)文件,是整个系统的“真正入口”。
Startup 做的事情包括:
设置堆栈(SP)
初始化 FPU(若有)
中断向量表搬运(若需要)
调用 C 运行时初始化函数(__init / __crt0)
典型启动文件结构(抽象示例):
Reset_Handler:
ldr sp, =_stack_top
bl SystemInit
bl __crt0_init
b main
Startup 是整个系统承上启下的关键位置。
10.6 第四步:C 运行时初始化(crt0)
crt0 是由编译器工具链生成的初始化代码,包括:
- 初始化 .data 段
- 把 Flash 中的初值复制到 RAM
- 清零 .bss 段
- 全部未初始化的全局变量归零
- 设置 RAM 中的运行环境
- 调用构造函数(C++)
- 准备 main() 所需的语言环境
如果没有 crt0:
- 全局变量不会有默认值
- 静态变量不会清零
- main() 会在混乱的内存状态下运行
crt0 保证 main() 执行时的世界是可预期的。
10.7 第五步:调用 main()
上述步骤完成后,系统终于运行到用户的 C 代码:
int main(void)
{
// 用户逻辑
}
如果是裸机:
- 在 main() 内部通常是死循环或驱动初始化
如果是 RTOS:
- 通常在 main() 中创建任务、启动调度:
main()
{
xTaskCreate(...);
vTaskStartScheduler(); // RTOS 接管 CPU
}
如果是 AUTOSAR OS:
- main() 通常调用
StartOS(AppMode),由 OS 接管控制
main()
{
StartOS(OSDEFAULTAPPMODE);
}
10.8 第六步:操作系统开始接管(RTOS 或 AUTOSAR OS)
在 OS 接管后:
- SysTick 或 OSTM 启动节拍
- 中断向量表配置完毕
- 上下文切换机制启动
- 第一个任务进入 Running 状态
这时,系统已经从“引导流程”进入“操作系统运行期”。
FreeRTOS:
vTaskStartScheduler()
↓
创建 Idle Task / Timer Task
↓
启动 SysTick
↓
调度第一个任务
AUTOSAR OS:
StartOS()
↓
OS 初始化 + Counter 初始化
↓
ScheduleTable / Alarm 启动
↓
Ready 最高优先级任务运行
10.9 第七步:中断向量与异常表的实际工作方式
中断向量表的核心作用:
- 提供所有异常/中断入口地址
- 供 CPU 在异常发生时跳转
- OS 在启动时必须重定向/初始化向量表
- 每个中断最终进入 ISR(ISR1 或 ISR2)
向量表示例(假设地址):
0x0000_0000: 初始 SP
0x0000_0004: Reset_Handler
0x0000_0008: NMI_Handler
0x0000_000C: HardFault_Handler
...
0x0000_0050: UART0_RX_Handler
0x0000_0054: TIMER0_Handler
OS(尤其是 AUTOSAR OS)会在中断入口加入:
保存上下文(或由硬件保存)
调用 OS 中断服务子系统
触发事件或任务激活
调度器判断是否切换任务
10.10 从上电到 OS:完整流程图(抽象)
上电
↓
BootROM(内部固化代码)
↓
加载向量表(SP & Reset_Handler)
↓
进入 Startup.s(启动文件)
↓
C 运行时初始化(crt0)
- 初始化 .data
- 清零 .bss
↓
main()
↓
StartOS() 或 vTaskStartScheduler()
↓
启动调度器(SysTick/TAUJ/OSTM)
↓
进入任务调度 → 运行应用
这就是 MCU 世界完整的引导流程。
10.11 为什么理解启动流程至关重要?
因为它关系到你之后的所有内容:
- 为什么 main() 之前必须跑 crt0?
- interrupt vector table 为什么要放在固定地址?
- 操作系统为什么要接管中断?
- RTOS 如何开始第一次任务切换?
- AUTOSAR OS 为什么要在 StartOS() 内做初始化?
- 链接脚本为什么必须精确分布 MEMORY?
- 为什么 .data 要搬到 RAM?
启动流程串起:
硬件 → BootROM → 启动文件 → C init → OS → 应用
是整个系统的生命线。
更多推荐


所有评论(0)