ARM学习总结1->寄存器初始值问题

今天调了一上午ADC实验都没出效果,调试发现一直停留在while(rADCCON & ADC_START);总是出不来。开始以为是ADC_Convert转换函数不对,仔细检查没发现问题,再考虑可能是初始化函数不对, 所以AD转换不能真正开始,但是自己核对和别人可行的代码,查书,也没有发现有什么错误,无奈之下,只能把别人可行的代码一段一段替代我的代码,再运行看效果,把JustDo的初始化函数 替代我的初始化函数Init_ADC,发现效果出来了,太Happy了,锁定问题就一定出在Init_ADC函数啦。  但是还有一个问题就是我的几乎和他的一模一样啊,怎么他的能出来,我的出不来了?再仔细分析  代码我的是rADCCON = (PRESCALE_EN | PRSCVL49 | CHANNEL0) ; 他的是rADCCON = (channel<<3) | (preScaler<<6) | (1<<14); 看了又看只有他的值直接赋给ADCCON寄存器,而我是或给它的。此刻!我完全清晰啦,肯定是ADCCON  上电初始value不是0。如果是或的话保留了其他值的其他位,这样肯定和你想要的设置不同啦。 查手册发现初始值真的不是0,而是0x3fc4。犯这个错误根本原因就是自己想当然了,没有仔细看手册,   并不是所有的寄存器初始value就是0。

这个问题把我纠结了一上午,最后出来了,也要总结一些经验教训啦,出了问题,在查手册,检查实在发现不了误的情况下,可以把别人可行的代码按模块一步一步替代自己代码,在一步一步仿真看效果,到那里出效果啦,也就知道自己代码问题出那里了,也许看起来就是一模一样,但它也只是看起来一样而已,肯定有些许不同,那些许不同就是自己最容易犯的错误,也是自己平时最容易忽略的。当然这是最后的杀手锏啦,之前还得自己反复调试才行啦。

<think>我们被要求通过SCB->CFSR寄存器分析HardFault的具体原因。SCB(System Control Block)中的CFSR(Configurable Fault Status Register)寄存器ARM Cortex-M系列中用于诊断异常原因的关键寄存器。它包含了三种类型错误的详细信息:用法错误(Usage Fault)、总线错误(Bus Fault)和存储器管理错误(MemManage Fault)。每个错误类型都有对应的状态位,当发生HardFault时,我们可以通过读取CFSR的值来确定具体的错误原因。 ### 一、CFSR寄存器结构 CFSR寄存器是一个32位寄存器,分为三个部分: 1. **存储器管理错误状态寄存器(MMFSR)**:位[0:7],8位 2. **总线错误状态寄存器(BFSR)**:位[8:15],8位 3. **用法错误状态寄存器(UFSR)**:位[16:31],16位 我们可以通过读取`SCB->CFSR`来获取整个寄存器的值,然后分别解析这三个部分。 ### 二、如何读取CFSR寄存器 在HardFault处理函数中,我们可以直接读取CFSR寄存器: ```c void HardFault_Handler(void) { // 读取CFSR寄存器 uint32_t cfsr = SCB->CFSR; // 其他诊断代码... } ``` ### 三、CFSR各状态位含义及诊断方法 下面我们分别介绍三个部分的状态位。注意:每个位都是“粘性”位,一旦置位,只有通过软件写1才能清除(写0无效)。 #### 1. 存储器管理错误(MMFSR, bits 0-7) - **MMARVALID(bit 7)**:1表示`SCB->MMFAR`(存储器管理错误地址寄存器)中保存了有效的错误地址 - **MLSPERR(bit 5)**:在浮点惰性压栈时发生存储器管理错误 - **MSTKERR(bit 4)**:在异常入栈时发生存储器管理错误(堆栈访问失败) - **MUNSTKERR(bit 3)**:在异常出栈时发生存储器管理错误(堆栈访问失败) - **DACCVIOL(bit 1)**:数据访问违规(如访问了禁止访问的区域) - **IACCVIOL(bit 0)**:取指违规(如从禁止执行的区域取指令) **诊断建议**: - 若`DACCVIOL`或`IACCVIOL`置位,检查MMFAR寄存器中的地址(如果MMARVALID置位),确定非法访问的地址。 - 若`MSTKERR`或`MUNSTKERR`置位,通常表示栈溢出或栈指针错误。 #### 2. 总线错误(BFSR, bits 8-15) - **BFARVALID(bit 15)**:1表示`SCB->BFAR`(总线错误地址寄存器)中保存了有效的错误地址 - **LSPERR(bit 13)**:在浮点惰性压栈时发生总线错误 - **STKERR(bit 12)**:在异常入栈时发生总线错误(堆栈访问失败) - **UNSTKERR(bit 11)**:在异常出栈时发生总线错误(堆栈访问失败) - **IMPRECISERR(bit 10)**:非精确的总线错误(错误地址无法确定) - **PRECISERR(bit 9)**:精确的总线错误(错误地址保存在BFAR中) - **IBUSERR(bit 8)**:取指时的总线错误(指令预取阶段) **诊断建议**: - 若`PRECISERR`置位,检查BFAR寄存器中的地址(如果BFARVALID置位),确定导致错误的访问地址。 - 若`STKERR`或`UNSTKERR`置位,同样检查栈指针和栈大小。 - `IMPRECISERR`通常由写操作引起,且难以定位具体地址,建议检查内存时序或DMA配置。 #### 3. 用法错误(UFSR, bits 16-31- **DIVBYZERO(bit 9)**:除零错误(SDIV或UDIV指令除数为零) - **UNALIGNED(bit 8)**:非对齐访问(如非对齐的LDR/STR指令) - **NOCP(bit 3)**:尝试访问协处理器(但协处理器不存在或禁用) - **INVPC(bit 2)**:非法的EXC_RETURN值(异常返回时PC加载了无效值) - **INVSTATE(bit 1)**:无效的状态切换(如尝试进入ARM状态) - **UNDEFINSTR(bit 0)**:未定义指令(CPU无法识别指令) **诊断建议**: - `DIVBYZERO`:检查除法运算前的除数是否为0。 - `UNALIGNED`:检查内存访问指令(尤其是结构体指针强制转换)。 - `INVSTATE`:通常由函数指针错误引起,检查函数指针是否指向有效代码。 ### 四、诊断流程 在HardFault处理函数中,我们可以按以下步骤分析: ```c void HardFault_Handler(void) { // 1. 读取CFSR uint32_t cfsr = SCB->CFSR; // 2. 打印或记录CFSR值 printf("HardFault: CFSR = 0x%08X\n", cfsr); // 3. 检查存储器管理错误 if (cfsr & 0xFF) { printf("MemManage Fault: "); if (cfsr & (1 << 0)) printf("IACCVIOL "); if (cfsr & (1 << 1)) printf("DACCVIOL "); // ... 其他位 } // 4. 检查总线错误 if (cfsr & 0xFF00) { printf("Bus Fault: "); if (cfsr & (1 << 8)) printf("IBUSERR "); if (cfsr & (1 << 9)) printf("PRECISERR "); // ... 其他位 if (cfsr & (1 << 15)) { // BFARVALID printf("Fault Address: 0x%08X", SCB->BFAR); } } // 5. 检查用法错误 if (cfsr & 0xFFFF0000) { uint16_t ufsr = cfsr >> 16; printf("Usage Fault: 0x%04X ", ufsr); if (ufsr & (1 << 0)) printf("UNDEFINSTR "); if (ufsr & (1 << 1)) printf("INVSTATE "); // ... 其他位 } // 6. 死循环(或复位) while(1); } ``` ### 五、注意事项 1. **清除状态位**:在调试时,读取CFSR后需要手动清除状态位(通过向对应位写1): ```c SCB->CFSR = SCB->CFSR; // 写1清除所有粘性位 ``` 但注意,在正式产品中,清除前务必记录错误信息。 2. **关联寄存器**: - `SCB->HFSR`(HardFault状态寄存器):指示HardFault是否由升级的异常引起。 - `SCB->MMFAR`和`SCB->BFAR`:分别保存存储器管理错误和总线错误的精确地址。 3. **调试技巧**: - 结合`__get_MSP()`和`__get_PSP()`获取栈指针,分析栈内容。 - 使用`CmBacktrace`等工具自动解析错误[^3]。 ### 六、常见错误与解决方案 | CFSR位 | 可能原因 | 解决方案 | |----------------|----------------------------|----------------------------| | STKERR | 栈溢出 | 增大栈空间 | | UNALIGNED | 非对齐访问 | 检查结构体对齐或使用`__packed` | | UNDEFINSTR | 函数指针指向非法地址 | 检查函数指针初始化 | | INVSTATE | 中断处理函数未使用`__attribute__((naked))` | 确保中断函数使用正确的属性 | ### 七、总结 通过分析SCB->CFSR寄存器,我们可以快速定位HardFault的根本原因。重点检查: 1. **栈溢出**(STKERR/MSTKERR) 2. **非法地址访问**(DACCVIOL/PRECISERR) 3. **指令错误**(UNDEFINSTR/INVSTATE) 结合故障地址寄存器(MMFAR/BFAR)和调用栈回溯,能高效解决大部分HardFault问题。 §§相关问题§§ 1. 如何通过栈回溯定位HardFault发生的位置? 2. 在FreeRTOS中如何检测任务栈溢出? 3. 如何配置ARM Cortex-M的MPU来防止非法内存访问?</think>## 通过 SCB->CFSR 寄存器诊断 HardFault 异常原因详解 在 ARM Cortex-M 系列 MCU 中,`SCB->CFSR` (Configurable Fault Status Register) 是诊断 HardFault 原因的核心寄存器。它包含三个子状态寄存器,共同指示故障的具体类型和原因。以下是详细分析流程: ### 一、CFSR 寄存器结构解析 CFSR 寄存器由三部分组成(32位结构): ```c | 31 - 16 | 15 - 8 | 7 - 0 | |---------|--------|-------| | UFSR | BFSR | MMFSR | ``` 1. **MMFSR** (MemManage Fault Status, bits 0-7):内存管理错误 2. **BFSR** (Bus Fault Status, bits 8-15):总线错误 3. **UFSR** (Usage Fault Status, bits 16-31):用法错误 ### 二、诊断步骤(代码实现) #### 1. 读取并解析 CFSR 寄存器 ```c void HardFault_Handler(void) { uint32_t cfsr = SCB->CFSR; // 读取故障状态寄存器 // 解析内存管理错误 (MMFSR) if(cfsr & 0xFF) { if(cfsr & (1 << 0)) printf("IACCVIOL: 指令访问违规\n"); if(cfsr & (1 << 1)) printf("DACCVIOL: 数据访问违规\n"); if(cfsr & (1 << 3)) printf("MUNSTKERR: 异常返回时出栈错误\n"); if(cfsr & (1 << 4)) printf("MSTKERR: 异常入栈错误\n"); if(cfsr & (1 << 7)) { printf("MMARVALID: 非法地址=0x%08X\n", SCB->MMFAR); } } // 解析总线错误 (BFSR) if(cfsr & 0xFF00) { if(cfsr & (1 << 8)) printf("IBUSERR: 指令预取错误\n"); if(cfsr & (1 << 9)) printf("PRECISERR: 精确数据总线错误\n"); if(cfsr & (1 << 10)) printf("IMPRECISERR: 非精确数据总线错误\n"); if(cfsr & (1 << 13)) printf("LSPERR: 浮点惰性保存错误\n"); if(cfsr & (1 << 15)) { printf("BFARVALID: 总线错误地址=0x%08X\n", SCB->BFAR); } } // 解析用法错误 (UFSR) uint16_t ufsr = cfsr >> 16; if(ufsr) { if(ufsr & (1 << 0)) printf("UNDEFINSTR: 未定义指令\n"); if(ufsr & (1 << 1)) printf("INVSTATE: 无效状态(如切换ARM模式)\n"); if(ufsr & (1 << 2)) printf("INVPC: 非法的EXC_RETURN值\n"); if(ufsr & (1 << 3)) printf("NOCP: 协处理器不存在\n"); if(ufsr & (1 << 8)) printf("UNALIGNED: 非对齐内存访问\n"); if(ufsr & (1 << 9)) printf("DIVBYZERO: 除零错误\n"); } while(1); // 死循环防止继续执行 } ``` ### 三、关键错误类型及解决方案 #### 1. 内存管理错误 (MMFSR) | 标志位 | 含义 | 解决方案 | |------------|----------------------|----------------------------| | IACCVIOL | 非法指令取指 | 检查函数指针是否越界 | | DACCVIOL | 非法数据访问 | 检查指针有效性 | | MSTKERR | 入栈错误 | **增大栈空间** [^3] | | MUNSTKERR | 出栈错误 | 检查中断嵌套深度 | | MMARVALID | 附带非法地址(SCB->MMFAR) | 分析该地址是否在有效内存区 | #### 2. 总线错误 (BFSR) | 标志位 | 含义 | 解决方案 | |--------------|----------------------|----------------------------| | PRECISERR | 精确总线错误 | 检查DMA配置或外设寄存器访问 | | IMPRECISERR | 非精确总线错误 | 检查内存时序配置 | | IBUSERR | 指令总线错误 | 验证代码是否在Flash有效区 | | BFARVALID | 附带错误地址(SCB->BFAR) | 定位非法访问位置 | #### 3. 用法错误 (UFSR) | 标志位 | 含义 | 解决方案 | |-------------|----------------------|----------------------------| | UNDEFINSTR | 未定义指令 | 检查编译器生成的二进制 | | INVSTATE | 无效状态 | 确认未尝试切换到ARM模式 | | UNALIGNED | **非对齐访问** | 检查结构体强制类型转换 | | DIVBYZERO | 除零错误 | 添加除数零值检查 | ### 四、高级诊断技巧 #### 1. 结合栈帧分析 获取故障时的PC值定位具体代码行: ```c __attribute__((naked)) void HardFault_Handler(void) { __asm volatile( "TST LR, #4 \n" "ITE EQ \n" "MRSEQ R0, MSP \n" "MRSNE R0, PSP \n" "B HardFault_C \n" ); } void HardFault_C(uint32_t* stack_frame) { uint32_t pc = stack_frame[6]; // PC在栈帧偏移24字节处 printf("Fault PC: 0x%08X\n", pc); // 通过.map文件查找对应函数 } ``` #### 2. 错误地址有效性验证 ```c // 检查地址是否在有效Flash范围 #define FLASH_START 0x08000000 #define FLASH_END 0x08100000 if((SCB->MMFAR >= FLASH_START) && (SCB->MMFAR <= FLASH_END)) { printf("Flash访问错误!\n"); } // 检查是否在RAM范围 else if((SCB->BFAR >= 0x20000000) && (SCB->BFAR < 0x20010000)) { printf("RAM访问越界!\n"); } ``` ### 五、诊断流程图 ```mermaid graph TD A[HardFault] --> B[读取SCB->CFSR] B --> C{分析MMFSR} C -->|DACCVIOL| D[检查指针访问] C -->|MSTKERR| E[增大栈空间] B --> F{分析BFSR} F -->|PRECISERR| G[检查外设寄存器] F -->|IMPRECISERR| H[检查DMA配置] B --> I{分析UFSR} I -->|UNALIGNED| J[检查结构体对齐] I -->|DIVBYZERO| K[添加零除保护] D --> L[修复成功] E --> L G --> L ``` > **关键提示**:当检测到`UNALIGNED`错误时,可考虑: > 1. 使用`__packed`定义结构体 > 2. 启用内核的未对齐访问支持:`SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;` > 3. 检查强制类型转换:`uint32_t val = *(uint32_t*)&byte_array[1];` // 可能导致非对齐访问 通过系统化分析CFSR寄存器,90%以上的HardFault可精确定位。对于复杂场景,建议结合[CmBacktrace](https://github.com/armink/CmBacktrace)等错误追踪库[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值