stm32_can错误中断 清除重点

本文探讨了STM32F407在CAN通信中遇到的出错中断循环问题,详细分析了CAN出错中断的机制,指出正确处理中断请求标志的重要性,避免程序死锁。

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

整理:MilerShao

 某日,有一客户反映他们在做STM32F407的CAN通信的出错测试时,发现出现类似死机的现象。后来跟踪调试发现是开启了出错中断,因其不停中断导致的貌似死机现象。纳闷的是,经过进一步测试,发现即使清除了“中断请求标志”后也无效。大致情形如下:

在CAN通讯时候让某节点做插拔、断电等测试,人为的产生一些CAN总线错误,看看能否让总线恢复正常。 结果测试发现,如果总线出现错误,并且打开了CAN错误中断处理的话,通过仿真器调试发现CPU一直在处理CAN错误中断,其他程序得不到处理而呈死机状态。 先是怀疑CAN控制器因为各类错误条件而进入错误中断后,虽在错误中断里清除了相关中断状态标志,但由于计数器值没有清零的原因,马上又产生新的错误中断,这样循环反复,导致其他程序得不到调度。

然后继续做实验,将CAN_IER寄存器中的LECIE和EWGIE这两种中断使能关闭掉,发现还是有这个问题。 客户觉得这样有点奇怪。

从客户粗略描述的现象来看,很像没有清除中断标志导致中断没完没了。经进一步的沟通了解,客户除了开启相关收发中断外,还使能了LECIE、EWGIE、EPVIE、BOFIE、ERRIE跟出错有关的中断,当然SLKIE和WKUIE没有使能。

结合CAN_IER和CAN_ESR寄存器的相关描述,可以看出即使关闭了LECIE、EWGIE两个出错条件,但还有EPVIE、BOFIE两种情形可能导致CAN出错中断。

  

  

 

跟CAN出错中断有关的寄存器主要涉及到3个,分别是CAN主控状态寄存器CAN_MSR,CAN中断使能寄存器CAN_IER,CAN错误状态寄存器CAN_ESR. 说实在的,这个跟CAN出错中断有关的东西还是有点复杂。看看下图,蓝色方框里的是各种错误中断的使能位,红色方框里的是各类出错条件或状态变化条件。请注意图中的与逻辑符号&和或逻辑符号+。

     上图中下方与CAN_MSR相关的两类中断触发源跟本话题无关,就不提了,重点看看上方那四个与CAN出错有关的中断源。

细心的人会发现,其实CAN_IER中的ERRIE位是一个CAN出错中断总开关,只有当它被使能的前提下,EWG、EPV、BOF、LEC四个中的一个或几个出错事件配合各自的中断使能位才能产生出错中断,并统一将CAN_MSR中的错误中断请求ERRI位置1,从而申请CAN错误中断。显然,CAN错误中断的请求标志位是ERRI@CAN_MSR,而不是CAN_ESR中的EWGF\EPVF\BOFF的任一位。 

聊到这里,我们基本对CAN出错中断相关的寄存器和相关标志有了大致的了解。要把握一点,CAN出错中断有个总使能位ERRIE,其它几个出错事件配合该总使能位最终产生错误中断请求,置位ERRI@CAN_MSR。

回到上面的话题,当查看客户的CAN错误中断处理程序时,发现他在中断程序里的确是做了些标志的清除【本意是想清除中断请求标志】。遗憾的是全是对CAN_ESR寄存器中的相关错误标志进行清零,即对LEC\EWGF\EPVF\BOFF这几个东西清零,偏偏没有对ERRI@CAN_MSR的清零。关键的东西成了漏网之鱼。

还有,他对EWGF\EPVF\BOFF这几个标志清零是无效的,因为这几个标志位是只读的,置1或清零是硬件根据TEC/REC的值动态处理的。
 
 

最后让客户把CAN出错中断代码做些调整,在进入CAN出错中断后将CAN_MSR中的ERRI位清0,确实可以解决错误中断循环往复的问题。

小结下,MCU中断行为一般涉及到使能标志、请求标志和触发源。应用中偶尔会出现有人把中断使能标志和中断请求标志弄混的情况。不过相比之下,这里谈到的CAN出错中断相比ST MCU其它外设的中断应用就要复杂或啰嗦些,尤其那个出错中断请求标志感觉有点隐晦。上面提到的案例,当事人可能没弄清CAN出错中断请求标志位到底是哪个,导致进入中断服务程序后清除动作无效,使得出错中断服务程序没完没了,而且还会影响到其它进程正常运行进而导致其它连锁异常。

### STM32 CAN 中断错误处理的解决方案 STM32CAN 模块提供了丰富的中断机制,用于检测和处理各种错误。以下是对 STM32 CAN 中断错误处理方法的详细说明[^1]。 #### 错误类型与中断STM32 CAN 支持多种错误类型,包括但不限于: - **总线错误(Bus Error)**:当 CAN 控制器检测到帧格式错误时触发。 - **CRC 错误**:接收数据时,如果 CRC 校验失败,则触发此错误。 - **ACK 错误**:发送方未收到接收方的确认信号。 - **形式错误(Format Error)**:CAN 帧格式不符合标准。 - **填充错误(Stuff Error)**:连续 5 个相同位后出现的第 6 个相同位。 这些错误可以通过配置 CAN 中断寄存器中的相关位来捕获,并在中断服务程序中进行处理[^1]。 #### 配置 CAN 中断 在启用 CAN 中断之前,需要正确配置 CAN 初始化参数和中断使能位。以下是配置 CAN 中断的基本步骤: ```c // 初始化 CAN 模块 void CAN_Init(void) { // 配置 CAN 初始化结构体 CAN_InitTypeDef CAN_InitStruct; CAN_FilterInitTypeDef CAN_FilterStruct; // 设置波特率等参数 CAN_InitStruct.CAN_TTCM = DISABLE; CAN_InitStruct.CAN_ABOM = ENABLE; CAN_InitStruct.CAN_NART = DISABLE; CAN_InitStruct.CAN_RFLM = DISABLE; CAN_InitStruct.CAN_TXFP = DISABLE; CAN_InitStruct.CAN_Mode = CAN_Mode_Normal; CAN_InitStruct.CAN_SJW = CAN_SJW_1tq; CAN_InitStruct.CAN_BS1 = CAN_BS1_6tq; CAN_InitStruct.CAN_BS2 = CAN_BS2_8tq; CAN_InitStruct.CAN_Prescaler = 2; // 初始化 CAN1 CAN_Init(CAN1, &CAN_InitStruct); // 配置过滤器 CAN_FilterStruct.CAN_FilterNumber = 0; CAN_FilterStruct.CAN_FilterMode = CAN_FilterMode_IdMask; CAN_FilterStruct.CAN_FilterScale = CAN_FilterScale_32bit; CAN_FilterStruct.CAN_FilterIdHigh = 0x0000; CAN_FilterStruct.CAN_FilterIdLow = 0x0000; CAN_FilterStruct.CAN_FilterMaskIdHigh = 0x0000; CAN_FilterStruct.CAN_FilterMaskIdLow = 0x0000; CAN_FilterStruct.CAN_FilterFIFOAssignment = CAN_FilterFIFO0; CAN_FilterStruct.CAN_FilterActivation = ENABLE; CAN_FilterInit(&CAN_FilterStruct); } // 启用 CAN 中断 void CAN_EnableInterrupt(void) { // 启用错误中断 CAN_ITConfig(CAN1, CAN_IT_ERR, ENABLE); // 启用 NVIC 中断 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); } ``` #### 中断服务程序实现 在中断服务程序中,需要检查错误标志并采取相应的措施。以下是个示例代码: ```c void USB_LP_CAN1_RX0_IRQHandler(void) { if (CAN_GetITStatus(CAN1, CAN_IT_ERR) != RESET) { uint32_t error_code = CAN_ErrorStatus(CAN1); if (error_code & CAN_ERROR_WDT) { // 处理看门狗超时错误 Handle_WatchdogTimeout(); } else if (error_code & CAN_ERROR_PASS) { // 处理被动错误状态 Handle_PassiveError(); } else if (error_code & CAN_ERROR_ACK) { // 处理 ACK 错误 Handle_AcknowledgeError(); } else if (error_code & CAN_ERROR_CRC) { // 处理 CRC 错误 Handle_CRCError(); } // 清除错误标志 CAN_ClearITPendingBit(CAN1, CAN_IT_ERR); } } // 示例错误处理函数 void Handle_WatchdogTimeout(void) { // 打印错误信息或执行其他操作 printf("Watchdog timeout error detected.\n"); } void Handle_PassiveError(void) { // 处理被动错误状态 printf("Passive error state detected.\n"); } void Handle_AcknowledgeError(void) { // 处理 ACK 错误 printf("Acknowledge error detected.\n"); } void Handle_CRCError(void) { // 处理 CRC 错误 printf("CRC error detected.\n"); } ``` #### 注意事项 1. 在实际应用中,可能需要根据具体场景调整错误处理逻辑,例如记录错误日志、重发数据或进入安全模式。 2. 确保及时清除中断标志,否则可能导致中断重复触发。 3. 使用 DMA 模式可以提高数据传输效率,减少 CPU 占用率[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值