应用跳转:1级boot,2级boot, app之间
2026/7/6 12:58:42 网站建设 项目流程

1.__initial_sp 变量

2.Reset_Handler

🧬 第一步:硬件规定的“黄金八字节”

Cortex-M 处理器上电或复位后,其硬件逻辑会执行一个固定的操作:

  1. 从地址0x00000000读取第一个 32 位数据,将其自动加载到主堆栈指针 (MSP)寄存器中。

  2. 从地址0x00000004读取第二个 32 位数据,将其自动加载到程序计数器 (PC)寄存器中。

因此,0x000000000x00000004这两个地址的内容必须分别是__initial_spReset_Handler的地址。

🏗️ 第二步:启动文件中的“向量表”定义

启动文件(通常是以.s为后缀的汇编文件,如startup_stm32f4xx.s)是确保这一切的第一步。它会定义一个名为__Vectors的数据段,并按照处理器规定的顺序,依次列出所有异常和中断处理函数的入口地址。

其核心片段如下:

assembly

__Vectors DCD __initial_sp ; 向量表第1项: 栈顶地址[reference:12][reference:13] DCD Reset_Handler ; 向量表第2项: 复位向量[reference:14][reference:15] DCD NMI_Handler ; 第3项: NMI DCD HardFault_Handler ; 第4项: 硬错误 ; ... 后续其他中断向量

DCD指令用于分配一个 32 位的存储器单元,并将后面符号的地址放入其中。这样,__initial_spReset_Handler的地址就被按顺序放在了向量表的开头。

第三步:链接器脚本中的“地址”决议

启动文件只是定义了符号,而链接器脚本(Linker Script)则负责最终把这些符号和代码段安排到正确的内存地址。

  • 确定__initial_sp的值:这个符号通常在链接器脚本中定义,代表栈的起始地址(栈顶)

    c

    _estack = ORIGIN(RAM) + LENGTH(RAM); /* 栈顶设在 RAM 的最高地址 */ /*[reference:21]*/

    然后,在启动文件中通过IMPORT __initial_spEXTERN声明,将其引入并使用。链接器会在链接阶段把这个符号替换为_estack的实际数值。

  • 确定Reset_Handler的地址Reset_Handler作为函数,其地址由链接器根据代码在 Flash 中的位置自动分配。链接器脚本会通过ENTRY(Reset_Handler)命令,将其指定为程序的入口点。

👑 第四步:FreeRTOS 的介入与“接管”

当芯片复位后,会按照上述顺序执行:

  1. 硬件自动加载__initial_sp到 MSP。

  2. 硬件自动跳转到Reset_Handler执行。

  3. Reset_Handler完成 C 运行时环境初始化后,调用main()函数。

  4. main()中,我们调用vTaskStartScheduler()来启动 FreeRTOS 调度器。

  5. FreeRTOS 的vPortStartFirstTask()函数会再次从向量表(NVIC 表)中读取__initial_sp的值,并将其重新写入 MSP 寄存器。这一步是为了确保任务调度开始时,栈指针被正确地重置到向量表定义的位置。

这个过程可以总结为下表:

阶段执行主体关键动作涉及文件/组件
定义开发人员在启动文件中按顺序列出向量表启动文件 (.s)
决议链接器计算__initial_spReset_Handler的最终地址链接器脚本 (.ld)
加载CPU 硬件自动从0x000x04加载 MSP 和 PCCortex-M 处理器
执行复位处理函数初始化系统,最终跳转到main()启动文件、C 运行时库
接管FreeRTOS启动调度器时,再次从向量表加载 MSPtasks.c,port.c

所以,FreeRTOS 能确保向量表内容正确,关键在于它依赖并遵循由启动文件和链接器脚本共同建立的、符合硬件规范的工程结构。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询