STM32F103启动过程分析:从CPU上电到main()

以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 < 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值