有开发者在STM32H743新品开发项目中用到了片内FDCAN外设。前期针对FDCAN作基础性验证测试时,发现即使在回环模式下也会发生丢帧现象。
具体情况是这样的:将FDCAN配置在传统经典模式,配置了3个过滤器。其中2个过滤器只接收特定ID的消息,过滤选择后的消息存入指定的Buffer;另外1个过滤器针对一定范围的ID的消息进行接收,接收到的消息将存入FIFO0。
CAN的滤波器的配置是这样的:
第一个过滤器,允许通过的消息ID范围是0x200~0x2FF,消息存入Rx FIFO0
第二个过滤器,允许通过的消息ID固定是0x111,消息存入Rx Buffer0
第三个过滤器,允许通过的消息ID固定是0x222,消息存入Rx Buffer1
三个过滤器的配置代码如下:
static void FDCAN1_Filter0_ToFifo0_Config(void){FDCAN_FilterTypeDef sFilterConfig = {0};sFilterConfig.IdType = FDCAN_STANDARD_ID; /* 标准ID */sFilterConfig.FilterIndex = 0; /* Index 0 */sFilterConfig.FilterType = FDCAN_FILTER_RANGE; /* 范围模式 */sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; /* 命中后送入FIFO0 */sFilterConfig.FilterID1 = 0x200; /* 范围起始 */sFilterConfig.FilterID2 = 0x2FF; /* 范围结束 */if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK){Error_Handler();}}/** FDCAN1_Filter1_ToRxBuffer0_Config — 配置 FDCAN1 滤波器 Index 1:精确匹配 0x111 → Rx Buffer0**/static void FDCAN1_Filter1_ToRxBuffer0_Config(void){FDCAN_FilterTypeDef sFilterConfig = {0};sFilterConfig.IdType = FDCAN_STANDARD_ID;sFilterConfig.FilterIndex = 1; /* Index 1 */sFilterConfig.FilterType = FDCAN_FILTER_MASK;sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXBUFFER;sFilterConfig.FilterID1 = 0x111;sFilterConfig.FilterID2 = 0x7FF; // 精确匹配sFilterConfig.RxBufferIndex = 0; /* 命中后送入BUFFER 0 */if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK){Error_Handler();}}/** FDCAN1_Filter2_ToRxBuffer1_Config — 配置 FDCAN1 滤波器 Index 2:精确匹配 0x222 → Rx Buffer1**/static void FDCAN1_Filter2_ToRxBuffer1_Config(void){FDCAN_FilterTypeDef sFilterConfig = {0};sFilterConfig.IdType = FDCAN_STANDARD_ID;sFilterConfig.FilterIndex = 2; /* Index 2 */sFilterConfig.FilterType = FDCAN_FILTER_MASK;sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXBUFFER;sFilterConfig.FilterID1 = 0x222;sFilterConfig.FilterID2 = 0x7FF; // 精确匹配sFilterConfig.RxBufferIndex = 1; /* 命中后送入BUFFER 1 */if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK){Error_Handler();}}
启动基于FIFO和BUFFER接收的新消息CAN中断【注:这里没启用FIFO满和丢消息中断】。
if (HAL_FDCAN_ActivateNotification(&hfdcan1,FDCAN_IT_RX_FIFO0_NEW_MESSAGE |FDCAN_IT_RX_BUFFER_NEW_MESSAGE,0) != HAL_OK){Error_Handler();}
现在让FDCAN工作在回环模式,程序里每隔几秒钟一次性发送三帧消息。每次发的第一帧消息的ID在0x200~0x2FF间变化,另外两帧的ID分别是0x111和0x222,并依次先后发送。测试过程中,他发现了个奇怪的现象。
虽然每次发送三帧消息,可总是只收到2帧消息。具体收到的就是ID为x0111和0x222的消息。下面截图是实际测试结果。
从串口打印的结果来看,0x111的消息被正确接收到了buffer0,0x222的消息虽然收到了,但它并不是进的buffer1,而是进到rx FIFO。
怎么会这样呢?从目前的CAN Filter配置来看,3个Filter的配置索引是这样的:
其实,消息是依次从Filter0~Filter2进行过滤匹配的,若在上级发生匹配后就进入相应消息缓存【FIFO或buffer】,不再往下做匹配。
现在Filter 0允许通过的ID是0x200~0x2FF,而ID 0x222又落在这个区间,导致ID 0x222的消息总是进入到FIFO而没有机会经Filter 2进入Buffer1.这就不难理解ID 0x222的消息虽能收到,但进入的是FIFO而不是Buffer。
既然这样,我们将目前filter 0和filter 3的索引号换一下,像下面这样:
其它不动,再行测试。下面是输出结果:
图中信息显示,ID 0x111的消息进入buffer0, ID 0x222的消息进入了buffer1,ID位于0x200~0x2ff的消息进入了FIFO。
不难看出,调整filter索引顺序后的运行结果是正确的,各ID消息进到各自的接收缓冲,没有丢帧现象,一切都符合预期。到此,似乎一切都圆满解决了。
其实还不能这么说,针对filter索引调整之前的输出结果还有待探究的地方。因为即使基于调整前的配置,顶多发生ID 0x222的消息没有按预期进到buffer而进到了FIFO,但ID位于0x200~0x2ff的消息完全接收不到,从原理上还是解释不通!
换句话说,在初始配置下,每次发3帧过来后,RXFIFO里应该有2帧数据,其中除了ID 0x222那帧外,另外一帧应该就是来自0x200~0x2ff的某ID帧。
从目前运行结果来看,围绕FDCAN的硬件配置应该是没啥问题的,难道是CAN接收处理代码有问题?于是,重点检查FDCAN FIFO接收中断的相关代码,代码里有针对FIFO填充深度的检查,并逐一提取。
提取的信息放在缓冲数组,并设置消息收到标志,在主循环里基于消息标志实施打印输出。经过代码的仔细检查,结果发现在CAN消息接收中断处理代码里有个bug,具体就是所有接收消息共用了同一个缓冲数组,导致数组里每次只放最后从FIFO里提取出来的消息。
如果说每次FIFO里最多就一个消息倒没事,不会出错。若FIFO里每次提取时存入了2个甚至更多帧消息时,这时就会出现问题,即每次最终只打印输出最后进FIFO的那帧消息。
鉴于此,为了满足每次接收可能出现多条FIFO消息的打印输出,便根据预设的FIFO深度【比方8】准备了多个将用于打印输出的数组缓冲,避免消息覆盖问题【当然,你采用别的方式也是可以的】。调整了基于FIFO接收中断的回调处理函数后,先退回最初的CAN Filter配置,再进行验证测试。一起来看看输出结果:
显然这次没有发生丢帧的现象,只不过每次FIFO里收到了两帧消息,一帧是ID 0x222的,另外一帧是0x200~0x2ff内某ID的。聊到这里,我们应该明白了,刚开始以为的丢帧,在硬件上是不存在的,只是用户软件处理代码bug导致的误会,属于软件层面。
当然,之前的ID 0x222的消息进不到buffer是不符合设计预期的,后来的filter索引顺序的调整就正好解决了这个问题,最终实现每帧消息均进入预设的目标缓冲区(精确ID进Buffer,范围型ID进FIFO)。OK,今天的话题就分享到这里。
159