STM32F103_HAL库+寄存器学习笔记18 - CAN接收溢出中断
导言
在 STM32F103 上使用 CAN 外设时,接收 FIFO 溢出(FOVR)并不是可忽略的小概率事件,而是一种可能导致严重后果的硬件行为。你需要持续监控并在溢出发生时及时处理,主要基于以下几点理由:
- 避免丢失关键报文
CAN 总线常用于实时控制和安全相关场景(电机控制、车身网络、传感器数据采集等),一旦 FIFO 满后再进来的帧就会被硬件直接丢弃,不会再触发中断,也不会自动覆盖旧数据。丢失报文可能导致控制环路失稳或安全机制失效。 - 保持中断机制的正常运行
当 FIFO 溢出标志(RF0R.FOVR0 或 RF1R.FOVR1)被置位后,如果不在中断服务中清除它,后续的接收中断往往会被“卡死”,因为硬件认为你还没处理上一次的溢出。及时检测并清除溢出标志,才能保证新的接收中断正常触发。 - 系统可观测性与调优依据
将每次溢出事件记录下来(比如维护一个溢出计数器),可以让你了解系统在何种负荷或何种通信模式下容易发生瓶颈。基于这些数据,你可以:
- 调整 CAN 波特率(降低总线负载)
- 优化上层协议逻辑(限流、分帧、优先级)
- 扩大应用层的接收缓存(如环形缓冲区ringbuffer)
总之,CAN接收FIFO1的溢出监控非常重要。
如上所示,根据《STM32F1中文参考手册》的章节22.8,通过寄存器CAN_IER可以打开FIFO1溢出中断。然后,在全局中断里查看寄存器CAN_RF1R的FOVR1是否被置1。
`
如上所示,寄存器CAN_RF1R的FOVR1的说明。
项目地址:
- HAL库:https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_hal_library18_Can_Rec_Overflow_Error
- 寄存器方式:https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library18_Can_Rec_Overflow_Error
一、代码(HAL库)
1.1、HAL库对接收FIFO的溢出中断支持不足
如上所示,FIFO1溢出中断并没有像接收中断一样(HAL_CAN_RxFifo1MsgPendingCallback()
),另外,有一个对应的函数名给我们去使用。错误类型的中断,全部统一用HAL_CAN_ErrorCallback()
。
几番调试后发现HAL_CAN_ErrorCallback()也不行,FIFO1溢出中断产生的时候,不会进入
HAL_CAN_ErrorCallback()
。所以决定不用HAL库的函数HAL_CAN_ErrorCallback()!!!
1.2、myCanDrive.c
如上所示,在全局中断函数CAN1_RX1_IRQHandler()
里调用自己编写的FIFO1接收溢出中断处理函数CAN_FIFO1_Overflow_Handler()
。函数CAN_FIFO1_Overflow_Handler()
的目的很简单,只是将全局变量g_RxOverflowError
累加。通过全局变量g_RxOverflowError
等于10的话,证明有10个CAN消息被丢掉了,丢掉的原因是FIFO1满了,导致接收溢出。
实际项目上,我们就是通过全局变量g_RxOverflowError
来监控CAN接收的情况。如果有接收溢出的话,证明我们应调整CAN接收过滤器。让那些我们不关心的CAN报文禁止进入接收FIFO1。
1.3、myCanDrive.h
1.4、stm32f1xx_it.c
二、测试(HAL库)
2.1、编译代码
如上所示,代码编译成功。
2.2、debug测试
如上所示:
- 程序初始化后,使用CAN分析仪发送4个CAN报文到CAN总线上,立刻触发了STM32F103的接收FIFO1溢出中断。
- 在Keil的debug模式下,观察System Viewer->CAN->CAN_RF1R,看到FOVR1被置1(FIFO1溢出了)、看到FULL1被置1(FIFO1满了,没有空闲的接收邮箱)、看到FMP1 = 3(FIFO1的三个邮箱都有CAN报文)。
- 调试断点卡在函数
CAN_FIFO1_Overflow_Handler()
里。
如上所示,云释放函数CAN_FIFO1_Overflow_Handler()
里的断点后,全局变量g_RxOverflowError
从0变成1,表示FIFO1接收溢出了(丢失了)一条CAN报文。
总的效果如下:
三、代码(寄存器方式)
3.1、myCanDrive_reg.c
函数CAN_Config()
里,将代码CAN1->IER |= CAN_IER_FMPIE1;
注释掉,方便触发FIFO1溢出中断。接着,通过代码CAN1->IER |= CAN_IER_FOVIE1;
开启FIFO1溢出中断。
为了让代码更加模块化,将FIFO1溢出中断处理与之前的FIFO1挂号中断处理都各自编写一个函数来处理。
在全局中断里,根据标志位判断到底是什么中断,根据中断标志进行处理。
四、测试(寄存器方式)
4.1、编译代码
4.2、debug测试
总的来说,效果跟HAL库一样。但是,寄存器的代码精简很多,效率最高。
如上所示,效果跟HAL库一样。实验成功!!!