嵌入式软件运行流程
嵌入式系统启动流程解析:从硬件复位到多任务运行 本文系统阐述了嵌入式软件从硬件上电到多任务调度的完整启动过程。核心环节包括: 上电复位阶段:CPU通过向量表获取初始栈指针和复位处理函数,startup.s完成.data/.bss段初始化; 板级初始化:重点配置时钟树(HSI→HSE→PLL)、GPIO和中断控制器; Bootloader机制:实现向量表重定向和双Bank升级的安全跳转; RTOS初
目录
一、上电复位(Power-On Reset)
核心问题:CPU如何找到第一条指令? 关键技术:启动文件(startup.s) 和 向量表(Vector Table)
1.硬件自动行为:
-
上电瞬间,CPU的PC寄存器被强制设置为 复位向量地址(ARM Cortex-M是
0x00000000,实际映射到Flash起始地址0x08000000)。 -
该地址存放的是一个4字节的 栈指针初始值(SP) 和下一个4字节的 复位处理函数地址(Reset_Handler)。
2.startup.s的作用
; 向量表定义
__Vectors:
DCD __initial_sp ; 栈顶地址(由链接脚本定义)
DCD Reset_Handler ; 复位处理函数
DCD NMI_Handler ; NMI中断处理
... ; 其他中断向量
Reset_Handler:
; 1. 初始化.data段(从Flash拷贝到RAM)
LDR r0, =_sdata ; .data段起始地址(RAM)
LDR r1, =_edata
LDR r2, =_sidata ; .data段的Flash源地址
BL memcpy ; 拷贝初始化数据
; 2. 清零.bss段(未初始化全局变量)
LDR r0, =_sbss
LDR r1, =_ebss
BL zero_mem
; 3. 跳转到main()或SystemInit()
B SystemInit
关键操作:
-
初始化.data段:全局变量初始值存储在Flash中,需拷贝到RAM。
-
清零.bss段:未初始化的全局变量所在内存区域清零。
-
设置堆栈指针:从链接脚本获取
__initial_sp值。
3.底层原理
-
链接脚本(.ld文件) 定义了内存布局:
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
-
编译器根据链接脚本生成
_sdata、_edata等符号地址。
二、板级初始化(Board Initialization)
核心问题:如何让芯片外设进入可用状态? 关键技术:时钟树配置 和 寄存器级编程
1.时钟初始化(以STM32为例):
-
复位后时钟状态:默认使用内部HSI(8MHz),所有外设时钟关闭。
-
配置流程:
// 1. 使能外部晶振(HSE)
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)); // 等待HSE就绪
// 2. 配置PLL(锁相环)
RCC->PLLCFGR = (RCC_PLLCFGR_PLLSRC_HSE | // PLL输入源=HSE
(8 << RCC_PLLCFGR_PLLM_Pos) | // 分频系数M=8
(336 << RCC_PLLCFGR_PLLN_Pos)); // 倍频系数N=336
// 3. 切换系统时钟到PLL
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待切换完成
-
关键点:
-
需严格遵循芯片参考手册的时钟树时序。
-
错误配置会导致芯片“卡死”(如PLL未锁定就切换时钟)。
-
2.GPIO初始化底层:
-
寄存器操作示例(配置PA5为推挽输出):
// 1. 使能GPIOA时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// 2. 配置PA5模式
GPIOA->MODER &= ~(3 << (5 * 2)); // 清零模式位
GPIOA->MODER |= (1 << (5 * 2)); // 设置为输出模式(01)
// 3. 配置输出类型
GPIOA->OTYPER &= ~(1 << 5); // 推挽输出(0)
3.中断控制器(NVIC)初始化:
-
设置优先级分组:
NVIC_SetPriorityGrouping(3); // 4位抢占优先级,0位子优先级
-
使能中断:
NVIC_EnableIRQ(USART1_IRQn); // 使能USART1中断
三、引导/升级系统(Bootloader)
核心问题:如何安全跳转到应用程序? 关键技术:向量表重定向 和 Flash编程
1.应用程序跳转代码:
typedef void (*AppEntry)(void);
void JumpToApp(uint32_t appAddress) {
AppEntry startApp = (AppEntry)(*(volatile uint32_t*)(appAddress + 4));
__set_MSP(*(volatile uint32_t*)appAddress); // 设置主栈指针
startApp(); // 跳转到应用程序
}
-
关键点:
-
appAddress + 4是应用程序的Reset_Handler地址。 -
需先关闭所有中断再跳转。
-
2.双Bank升级流程(防止变砖):
Bank1(运行中) | Bank2(新固件)
-----------------|-----------------
1. 擦除Bank2 |
2. 写入新固件到Bank2 |
3. 设置标志位指向Bank2 |
4. 复位后Bootloader检查标志位跳转
四、 RTOS初始化(以FreeRTOS为例)
核心问题:如何创建第一个任务? 关键技术:任务栈初始化 和 上下文切换
1.任务栈结构:
-
ARM Cortex-M的任务栈需手动模拟异常返回时的栈帧:
StackType_t pxStack[1024];
pxStack[1023] = 0x01000000; // xPSR(Thumb模式)
pxStack[1022] = (StackType_t)TaskFunction; // PC
pxStack[1021] = (StackType_t)0xFFFFFFFE; // LR(异常返回特殊值)
-
vTaskStartScheduler()会触发 SVC异常,切换到第一个任务。
2.PendSV中断:
-
上下文切换通过 PendSV异常 实现(最低优先级中断):
PendSV_Handler:
MRS r0, PSP ; 获取当前任务栈指针
STMDB r0!, {r4-r11} ; 保存寄存器
BL vTaskSwitchContext ; 选择下一个任务
LDMIA r0!, {r4-r11} ; 恢复寄存器
MSR PSP, r0 ; 更新栈指针
BX lr ; 返回后自动恢复剩余寄存器
五、多任务调度实例
场景:传感器采集 + 无线传输
void SensorTask(void *pv) {
while (1) {
float temp = ReadTemperature(); // 阻塞式读取
xQueueSend(xDataQueue, &temp, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(100)); // 100ms周期
}
}
void CommsTask(void *pv) {
while (1) {
float temp;
xQueueReceive(xDataQueue, &temp, portMAX_DELAY);
SendToCloud(temp); // 通过Wi-Fi发送
}
}
调度器行为:
-
SensorTask调用vTaskDelay后进入阻塞状态,触发任务切换。 -
调度器选择
CommsTask运行(若队列中有数据)。 -
SysTick中断每1ms检查是否需要任务切换。
六、终极总结:嵌入式软件启动全景图
上电
│
├─ 硬件复位 → CPU读取0x00000000获取SP和Reset_Handler
│ │
│ └─ startup.s:初始化.data/.bss → 跳转到SystemInit()
│
├─ 板级初始化:
│ ├─ 时钟树配置(HSI→HSE→PLL)
│ ├─ GPIO/USART/DMA外设使能
│ └─ 中断向量表重定位(SCB->VTOR)
│
├─ Bootloader:
│ ├─ 检查升级标志 → 擦写Flash → 跳转App
│ └─ 应用程序起始地址需对齐到0x100(Cortex-M要求)
│
├─ RTOS初始化:
│ ├─ 创建空闲任务 → 初始化任务链表 → 启动SysTick
│ └─ 第一个任务通过SVC异常启动
│
└─ 多任务运行:
├─ 任务切换由PendSV处理(保存R4-R11)
└─ 同步机制:队列/信号量/事件组
关键原则:
-
启动阶段:严格遵循芯片手册的初始化顺序(如先时钟后外设)。
-
RTOS阶段:理解任务栈和上下文切换的汇编级实现。
-
调试技巧:若卡死,检查:
-
向量表地址是否正确(VTOR)。
-
栈是否溢出(填充
0xDEADBEEF检测)。 -
时钟配置是否成功(测量HSI/HSE频率)。
-
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)