multiple definition of `NMI_Handler' stm32CubeMX 生成gcc编译出错

本文介绍了一个关于STM32项目的Makefile配置问题,其中出现了多个定义错误,特别是针对`NMI_Handler`的问题。文章详细展示了重复的C源文件列表,并探讨了如何避免在编译过程中出现重复定义的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

multiple definition of `NMI_Handler'

还有其它提示重复的,请检查下Makefile文件里面 是不是重复了c源文件 我的不知为何生成Src/stm32f1xx_it.c \这一行和main.c重复

C_SOURCES =  \
Src/main.c \
/Src/system_stm32f1xx.c \
Src/stm32f1xx_it.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c \
Src/stm32f1xx_hal_msp.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash_ex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc_ex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_pwr.c

<think>我们分析两个典型的异常处理程序:NMI_Handler和HardFault_Handler。在ARM Cortex-M系列处理器中,这些异常处理程序通常以汇编形式出现在启动文件(如startup_stm32f10x_hd.s)中。下面逐行分析。 ### 1. NMI_Handler 分析 ```assembly NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDP ``` - **`PROC`**:表示一个过程的开始,这里定义了一个名为`NMI_Handler`的过程(即函数)[^2]。 - **`EXPORT NMI_Handler [WEAK]`**: - `EXPORT`使得该符号(`NMI_Handler`)对其他模块可见(全局符号)。 - `[WEAK]`属性表示这是一个弱符号。如果其他地方(如用户代码)定义了同名的强符号(例如用户自己实现的`NMI_Handler`函数),链接时将使用强符号而忽略此处的定义[^3]。 - **`B .`**: - `B`是分支指令(跳转)。 - `.`表示当前地址。因此`B .`表示无限循环(跳转到自身)[^1]。 - 作用:当发生NMI(不可屏蔽中断)且用户未自定义处理程序时,处理器将在此处无限循环,通常用于调试或捕获错误。 - **`ENDP`**:表示过程结束。 ### 2. HardFault_Handler 分析 ```assembly HardFault_Handler PROC EXPORT HardFault_Handler [WEAK] B . ENDP ``` - 结构同`NMI_Handler`,区别在于处理的是硬件错误异常(HardFault)。 - **`EXPORT HardFault_Handler [WEAK]`**:同样为弱符号,允许用户自定义强符号覆盖。 - **`B .`**:同样表示无限循环,用于未自定义处理程序时挂起系统。 ### 关键机制详解 #### 1. **弱符号(WEAK)的作用** - **覆盖默认实现**:嵌入式开发中,用户可能需要自定义异常处理(如记录错误信息、复位系统)。通过`[WEAK]`属性,启动文件提供默认实现(死循环),同时允许用户在其他文件(如C文件)中重新定义同名函数覆盖它[^3]。 - **示例**:用户可在C文件中定义: ```c void HardFault_Handler(void) { printf("Hard Fault Occurred!\n"); while(1); // 自定义处理 } ``` 链接时,用户定义的强符号将覆盖启动文件中的弱符号。 #### 2. **`B .` 指令的二进制编码** - **指令格式**:`B`指令为相对跳转,编码中包含24位有符号偏移量。计算方式为: $$ \text{偏移量} = \frac{\text{目标地址} - (\text{当前PC} + 4)}{4} $$ 其中`+4`是因为ARM流水线效应(PC指向下两条指令地址)[^1]。 - **`B .` 的偏移量**:目标地址为当前指令地址,因此: $$ \text{偏移量} = \frac{(\text{当前PC}) - (\text{当前PC} + 4)}{4} = -1 $$ 对应的二进制编码为`0xE7FE`(Thumb状态)或`0xEAFFFFFE`(ARM状态)。在Cortex-M(始终Thumb状态)中,实际机器码为`0xE7FE`。 #### 3. **异常处理流程** 1. **异常触发**:发生NMI或HardFault异常。 2. **硬件自动行为**: - 压栈(保存寄存器:PSR, PC, LR, R12, R3-R0)。 - 更新PC:从向量表(如`0x00000008`对应NMI,`0x0000000C`对应HardFault)加载处理函数地址。 3. **执行处理程序**: - 若用户未覆盖,执行默认的`B .`(死循环)。 - 若用户覆盖,执行自定义代码。 ### 地址分配示例(向量表) 假设向量表起始于`0x00000000`: | 地址 | 异常类型 | 处理函数地址(示例) | |---------------|----------------|----------------------| | 0x00000000 | 初始SP值 | 0x20001000 | | 0x00000004 | Reset_Handler | 0x08000001 (Thumb) | | **0x00000008**| **NMI_Handler**| **0x080000C1** | | **0x0000000C**| **HardFault_Handler** | **0x080000C5** | - 处理函数地址最低位为1表示Thumb状态(Cortex-M必须)[^1]。 ### 相关问题 §§相关问题§§ 1. 在Cortex-M中,为什么异常处理函数的地址在向量表中必须将最低位置1? 2. 如何通过调试器(如J-Link)在发生HardFault时自动捕获并定位错误原因? 3. 用户自定义的HardFault处理函数中,如何获取触发异常的指令地址及相关寄存器状态?</think>### 逐行分析 ARM 汇编异常处理程序 #### 1. **NMI_Handler 分析** ```assembly NMI_Handler PROC ; ① 定义不可屏蔽中断处理程序 EXPORT NMI_Handler [WEAK] ; ② 声明全局符号(弱引用) B . ; ③ 无限循环指令 ENDP ; ④ 过程结束 ``` ① **`PROC`** 表示过程(函数)开始,定义名为 `NMI_Handler` 的异常处理程序。在 ARM 异常向量表中,NMI 通常位于偏移量 `0x00000008` 处[^1]。 ② **`EXPORT NMI_Handler [WEAK]`** - `EXPORT`:使符号全局可见,允许链接器从其他模块访问 - `[WEAK]`:关键属性,表示: - 允许用户在其他文件中定义同名强符号进行覆盖 - 若用户未自定义,则使用此默认实现 - 避免链接错误(为可选功能提供默认实现)[^3] ③ **`B .`** - 分支指令到当前地址(`.` 表示当前程序计数器 PC) - 实际机器码为 `0xE7FE`(Thumb 状态) - 效果:**无限循环**,用于: - 调试时捕获未处理的异常 - 防止程序跑飞 - 等待调试器介入[^1] ④ **`ENDP`** 标记过程结束,与 `PROC` 配对使用。 --- #### 2. **HardFault_Handler 分析** ```assembly HardFault_Handler PROC ; ① 硬件错误处理程序 EXPORT HardFault_Handler [WEAK] ; ② 弱引用声明 B . ; ③ 死循环 ENDP ; ④ 过程结束 ``` ① **`HardFault_Handler PROC`** 处理硬件错误异常(如非法内存访问、除零错误等),在向量表中固定位于 `0x0000000C`[^1]。 ② **`[WEAK]` 的实际应用** 在操作系统移植中(如 uC/OS-II),常需自定义此处理程序: ```c // 用户自定义实现(覆盖弱符号) void HardFault_Handler(void) { log_error("Hard Fault!"); // 记录错误 while(1); // 安全挂起 } ``` 链接时优先使用此强符号[^3]。 ③ **`B .` 的二进制细节** - 在 Thumb-2 指令集中编码为 16 位:`1110 0111 1111 1110`(`0xE7FE`) - 相对跳转偏移量计算: $$ \text{偏移量} = \frac{\text{目标地址} - (\text{PC} + 4)}{4} = \frac{\text{当前地址} - (\text{当前地址} + 4)}{4} = -1 $$ 实际扩展为 32 位值 `0xFFFFFFFE`[^1] --- ### 关键机制图解 ```plaintext +---------------------+ | 异常向量表 | | 0x00: 初始SP | | 0x04: Reset_Handler | | 0x08: NMI_Handler | ---> [默认实现] B . (死循环) | 0x0C: HardFault | ---> [用户可选覆盖] +---------------------+ | v +---------------------+ | 用户代码 | | void HardFault_Handler() | | { /* 自定义处理 */ } | <--- 强符号覆盖弱符号 +---------------------+ ``` ### 核心设计思想 1. **弱引用灵活性** 允许开发者按需覆盖默认异常处理,特别适用于: - 实时操作系统移植[^3] - 自定义错误恢复机制 - 安全关键系统的错误诊断 2. **死循环的必要性** - 防止未处理异常导致不可控行为 - 保留现场供调试器分析(通过 SP 回溯调用栈) - 符合功能安全标准(如 IEC 61508)的故障冻结要求 3. **异常处理流程** 当异常发生时: ```mermaid graph LR A[异常触发] --> B{用户是否定义处理程序?} B -->|是| C[执行自定义处理] B -->|否| D[执行默认 B . 死循环] ``` --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值