以STM32F103为例,通过分析libopencm3中特定芯片的启动代码,来了解整个启动过程——从CPU上电到运行main()。
1. 复位向量获取
- 从向量表的
0000 0000偏移处加载初始堆栈指针(SP) - 从向量表的
0000 0004偏移获取复位处理函数地址(reset_handler) - 内存映射:
- 定义于链接脚本
stm32f103cbtx_flash.ld(连接脚本在编译阶段就划分好了内存布局) - Flash: 0x08000000-0x08020000 (128KB)
- RAM: 0x20000000-0x20005000 (20KB)
- 定义于链接脚本
关键符号定义(链接脚本中):
_stack= 0x20005000(栈顶指针)_data= RAM中.data段起始_edata= RAM中.data段结束_ebss= .bss段结束_data_loadaddr= FLASH中.data段的原始位置
补充:内存布局详解
嘉立创STM32F103CBT6官方手册连接
2. 执行复位处理函数
文件位置:lib/libopencm3/lib/cm3/vector.c、/lib/libopencm3/lib/stm32/f1/vector_nvic.c
中断向量表是嵌入式系统中的关键数据结构,位于Flash起始位置(0x08000000),包含以下核心信息:
- 物理结构:
- 32位地址数组,每个条目对应一个异常/中断
- 首条目:初始堆栈指针(SP)
- 第二条目:复位处理函数地址(reset_handler)
- 后续条目:其他异常和中断处理函数地址
- 所以中断向量表本质上是提供各种处理函数入口地址的容器。根据中断向量表提供的对应函数指针
- 工作原理:
中断向量表中的中断函数指针调用过程完全由硬件自动完成。这里的NVIC是指嵌套向量中断控制器(Nested Vectored Interrupt Controller),位于 ARM Cortex-M 处理器内部,是 CPU 核的一部分。作用:接收并仲裁所有中断请求(IRQ),并根据优先级决定是否响应中断。NVIC的关键操作:自动计算中断向量地址并触发 CPU 跳转
- 各处理函数采用弱符号机制,用户可以自定义函数覆盖掉它们。在编译阶段实现这种覆盖。
- 对于复位处理函数,则比较特殊。如前面所说,CPU上电后会直接:
- CPU上电从0x08000000读取SP值初始化栈指针
- 从0x08000004读取reset_handler地址并跳转执行(也就意味着,rest_handler是是CPU复位后执行的第一段机器代码,负责系统的启动工作)
- 设计意义:
- 提供硬件与软件的异常处理接口
- 允许灵活重定向中断处理函数
- 通过弱定义(#pragma weak)支持用户自定义处理
2.1 中断向量表结构
在cm3(Cortex - M3 架构)目录下,定义了基础的向量表结构
/** Type of an interrupt function. Only used to avoid hard-to-read function
* pointers in the efm32_vector_table_t struct. */
typedef void (*vector_table_entry_t)(void); //
typedef struct {
unsigned int *initial_sp_value; /** 初始栈顶位置 */
vector_table_entry_t reset;
vector_table_entry_t nmi;
vector_table_entry_t hard_fault;
vector_table_entry_t memory_manage_fault; /* not in CM0 */
vector_table_entry_t bus_fault; /* not in CM0 */
vector_table_entry_t usage_fault; /* not in CM0 */
vector_table_entry_t reserved_x001c[4];
vector_table_entry_t sv_call;
vector_table_entry_t debug_monitor; /* not in CM0 */
vector_table_entry_t reserved_x0034;
vector_table_entry_t pend_sv;
vector_table_entry_t systick;
vector_table_entry_t irq[NVIC_IRQ_COUNT];
} vector_table_t;
创建向量表实例,链接到.vectors段
__attribute__ ((section(".vectors"))) //__attribute__ 是GCC的扩展语法,用于指定变量、函数或类型的特殊属性
vector_table_t vector_table = {
.initial_sp_value = &_stack, // 初始栈指针
.reset = reset_handler, // 复位处理函数
.nmi = nmi_handler, // NMI中断
.hard_fault = hard_fault_handler, // 硬错误
// Cortex-M3/M4特有异常
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
.memory_manage_fault = mem_manage_handler,
.bus_fault = bus_fault_handler,
.usage_fault = usage_fault_handler,
.debug_monitor = debug_monitor_handler,
#endif
.sv_call = sv_call_handler, // 系统调用
.pend_sv = pend_sv_handler, // PendSV中断
.systick = sys_tick_handler, // SysTick中断
.irq = {
IRQ_HANDLERS} // 外设中断向量
};
关键特性:
- 使用
.vectors段确保位于Flash起始(0x08000000)IRQ_HANDLERS由平台特定代码填充(如STM32F1的nvic.c)
2.2 复位处理函数详解(reset_handler)
void __attribute__ ((weak)) reset_handler(void) //__attribute__((weak)): 声明为弱符号。若其他文件中定义了同名函数,链接器将优先使用强符号版本(允许自定义复位函数覆盖默认实现)。
{
volatile unsigned *src, *dest;
funcp_t *fp;
// 1. 初始化.data段(复制Flash→RAM)
for (src = &_data_loadaddr, dest = &_data;
dest < &_edata;
src++, dest++) {
*dest = *src;
}
// 2. 清零.bss段
while (dest < &_ebss) {
*dest++ = 0;
}
// 3. 配置堆栈对齐(8字节)
SCB_CCR |= SCB_CCR_STKALIGN;
// 4. 平台预初始化
pre_main();
// 5. 执行C++全局构造函数
for (fp = &__preinit_array_start; fp <



最低0.47元/天 解锁文章
534

被折叠的 条评论
为什么被折叠?



