一,CANFD 的两条中断线
STM32H7 的 CANFD 外设提供了两条线:
fdcan_intr0_it : 中断线 0
fdcan_intr1_it :中断线 1
使用以下代码实现中断的开启
本文会用到的 CANFD 中断种类:
可以分为以下几类:
接收新消息中断。(每次接收到新的消息就会触发一次中断)
消息 FIFO 满中断。(Message RAM 设置的 FIFO 深度已满,没有及时的将消息读取走,触发的中断)
触发水位(watermark)中断。(有的地方翻译成了水印,感觉不是很贴切,这里应该理解为警戒线的意思,如果 FIFO 深度为 10,那么可以把水位设置为 8,这样在接收到新的报文的时候,会触发一次水印中断,通过标志位的检测,可以知道 FIFO 即将满了,必须及时的将数据取走,避免触发 FIFO 满中断,导致的数据丢失)
1 新消息接收中断
CANFD 的消息通过中断模式接收,在不进行任何中断线配置的情况下,默认使用中断线 0 ,通过以下的代码实现接收数据由中断线 1 响应
2 消息 FIFO 满中断
在 CANFD 初始化的时候需要设置 FIFO0/1 的深度,根据设置的深度会决定 FIFO0/1 满中断触发的时机
3 接收水印中断
接收水印中断来通知用户 FIFO 将满,请及时处理
4 中断回调函数
不同的中断线使用的是一样的回调函数:
RXFIFO 接收回调中的 RxFifo0ITs RxFifo1ITs 是接收数据类型的标志位,通过对标志位的检查,可知接收的是哪种标志位的数据,可用标志位是以下几种类型
5 注意事项
接收水印只有 RXFIFO 有,而 RXBUFF 没有。
RXFIFO0, RXFIFO1, RXBUFF 可以同时使用。
中断线0, 中断线1 可以同时使用。
默认使用中断线0,对于没有特殊需求的情况下,默认使用中断线 0 ,能满足大部分的需求场景。
RXFIFO 模式下对于接收 FIFO FULL 状态下有两种处理模式:阻止模式和覆盖模式。阻止模式,就是新的消息不在接收;覆盖模式,就是直接覆盖 Message RAM RXFIFO 里面最旧的一条消息。默认为阻止模式,可以通过以下的代码切换到覆盖模式。
二,RXFIFO 与 RXBUFF
在 CANFD 中有 RXFIFO0 ,RXFIFO1,RXBUFF。RXFIFO 最大支持深度为 64,RXBUFF 的最大支持个数为 64.。
关于 RXFIFO 和 RXBUFF 的区别见上图,在做几点补充:
RXFIFO 和 RXBUFF 模式模式情况下,在接收数据满了以后会阻止新的数据接收,但是 RXFIFO 可以修改为覆盖模式来进行继续接收,而 RXBUFF 必须手动清除 RXBUFF 对应的 BUFF index ,也就是 FDCAN_NDAT 的 bit 位,NDAT1(0 - 31),NDAT2(32 - 63)。
RXFIFO 按照接收数据的顺序依次放入到 RXFIFO 当中,RXBUFF 需要用户自己设置滤波器规则,来放入自定序列号的 BUFF。
1 RXFIFO 获取新的消息
当进入 RXFIFO 的回调函数之后,可以检测当前的接收数据的标志位,来进行数据的接收
2 RXBUFF 获取新的消息
RXBUFF 接收到的数据,必须放到指定的 BUFF 索引,因此必须在进入 RXBUFF 接收回调之后进行检查,是哪个索引触发了接收中断
注意 RXBUFF_INDEX_MAX 的最大值,必须与 Message RAM 中设置的 hfdcan1.Init.RxBuffersNbr 保持一致。
3 注意事项
中断回调函数的本身是在中断环境中,RXBUFF 的查询是一个开销比较大的操作,可以在裸机环境中可以使用全局变量的作为标志位的去通知,在 RTOS 环境中可以使用信号量的方式。
前面提到 RXBUFF 需要手动清楚 RXBUFF 接收数据的 index , 实际上在使用 HAL_FDCAN_GetRxMessage 的时候已经帮用户清除了 FDCAN_NDAT 标志位。
三,滤波器
滤波器的作用就是对 CANFD 的帧 ID 进行匹配,匹配成功之后按照用户的设置的规则进行响应,在 STM32H7 的 CANFD 中,相对于 BxCAN 滤波器进行了升级,本文只做 CAFDN 的滤波器说明,不做对比。在认识滤波器之前,一定要明白滤波器只在接收报文时生效,发送模式与滤波器无关。
通过对 Message RAM 的配置可知,CANFD 的滤波器配置可以分为两套,分别是:
标准帧标识符。(ID范围:0x000 - 0x7FF)
扩展帧标识符。(ID范围:0x0000 0000 - 0x1FFF FFFF)
滤波器可以设置的滤波模式:
FDCAN_FILTER_RANGE (范围过滤)
FDCAN_FILTER_DUAL (精确过滤)
FDCAN_FILTER_MASK (掩码过滤)
FDCAN_FILTER_RANGE_NO_EIDM (范围过滤,但是忽略 EIDM,该模式仅在扩展帧标识符模式下生效)
滤波器匹配之后的行为:
FDCAN_FILTER_TO_RXFIFO0 (保存在 RXFIFO0)
FDCAN_FILTER_TO_RXFIFO1 (保存在 RXFIFO1)
FDCAN_FILTER_REJECT (拒绝该报文)
FDCAN_FILTER_HP (设置为高优先级报文,仅通知)
FDCAN_FILTER_TO_RXFIFO0_HP (设置为高优先级报文,并保存在 RXFIFO0)
FDCAN_FILTER_TO_RXFIFO1_HP (设置为高优先级报文,并保存在 RXFIFO0)
FDCAN_FILTER_TO_RXBUFFER (保存在 RXBUFF)
了解以上这些特性之后,分别对 标准帧标识符和扩展帧标识符分别进行说明。
1 标准帧标识符的滤波器设置
标准帧标识符的滤波器的个数根据 Message RAM 配置来决定,最大支持 128 个过滤器组。
1.1 标准帧标识符符的范围过滤
以上设置为:标准帧标识符过滤器的第 0 组,过滤条件但是范围过滤,过滤匹配的数据会被保存在 RXFIFO0,过滤的范围是 0x100 - 0x110
1.2 标准帧标识符的精确过滤
以上设置为:标准帧标识符过滤器的第 1 组,过滤条件但是精确过滤,过滤匹配的数据会被保存在 RXFIFO1,只会接受帧ID为 0x200 和 0x210 的报文,接收后会被保存在 RXFIFO1
1.3 标准帧标识符的掩码过滤
以上设置为:标准帧标识符过滤器的第 2 组,过滤条件但是掩码过滤,过滤匹配的数据会被保存在 RXBUFF。
过滤帧 ID 是 0x300 , 转化成二进制就是:0000 0011 0000 0001, 帧 ID 的掩码是 0x3FF,转换成二进制就是:0000 0011 1111 1111, 对他们进行求与运算的结果就是 0000 0011 0000 00001 , 对 bit 位是 1 的位关心,对 bit 位是 0 的位不关心,因此这个滤波器设置的的可以选范围是 0x301 0x311 0x321 0x331 等等,当然还包括 0x701 0x711 0x721 0x731 等。
2 扩展帧标识符过滤器
扩展帧标识符的滤波器的个数根据 Message RAM 配置来决定,最大支持 64 个过滤器组。
2.1 扩展帧标识符的范围过滤
以上设置为:扩展帧标识符过滤器的第 0 组,过滤条件但是范围过滤,过滤匹配的数据会被保存在 RXFIFO0,接收帧 ID 的范围是 0x10000100 - 0x10000110,但是因为设置了 EIDM 的掩码为 0x1000 0001 因此接收的帧 ID 的应该是 0x10000100 - 0x10000110 范围内且 ID 最低位是 1 的帧 ID,如: 0x10000101 0x10000103 。
2.2 扩展帧标识符的精确过滤
以上设置为:扩展帧标识符过滤器的第 1 组,过滤条件但是精确过滤,过滤匹配的数据会被保存在 RXFIFO1,只会接受帧 ID 为 0x10000200 和 0x10000210 的报文,接收后会被保存在 RXFIFO1
2.3 扩展帧标识符的掩码过滤
以上设置为:扩展帧标识符过滤器的第 2 组,过滤条件但是掩码过滤,过滤匹配的数据会被保存在 RXBUFF。
过滤帧 ID 是 0x10000000, 转化成二进制就是:0001 0000 0000 0000 0000 0000 0000 00000帧 ID 的掩码是 0x1FFFFFFF,转换成二进制就是:0001 1111 1111 1111 1111 1111 1111 1111, 对他们进行求与运算的结果就是 0001 0000 0000 0000 0000 0000 0000 00000 , 对 bit 位是 1 的位关心,对 bit 位是 0 的位不关心,因此这个滤波器设置的的可以选范围是 0x1000 0000 - 0x1FFF FFFF。
2.4 扩展帧标识符的范围过滤忽略 EIDM
EIDM是 Extended ID and Mask ,扩展帧掩码的过滤器。
以上设置为:扩展帧标识符过滤器的第 3 组,过滤条件但是范围过滤忽略 EIDM,过滤匹配的数据会被保存在 RXFIFO0,接收帧 ID 的范围是 0x10000100 - 0x10000110。
3 使能滤波器配置
在完成滤波器的配置之后,还需要对滤波器进行使能才能使滤波器生效。
HAL_FDCAN_ConfigGlobalFilter 用来设置在符合滤波器的匹配条件之后的动作。
这个函数有五个参数,第一个就忽略不讲,主要讲一下后面四个参数:
NonMatchingStd :不匹配的标准帧 ID 的处理行为
NonMatchingExt :不匹配的扩展帧 ID 的处理行为
RejectRemoteStd :拒绝远程标准帧
RejectRemoteExt : 拒绝远程扩展帧
对不匹配的标准/扩展帧 ID 的处理行为,有以下三种方式:
可以选择保存在 RXFIFO0 ,RXFIFO1,或者拒绝接收,这里设置为拒绝接收。
拒绝远程标准/扩展帧
FDCAN_FILTER_REMOTE 允许(标准/扩展帧)的远程帧经过滤波器
FDCAN_REJECT_REMOTE 拒绝(标准/扩展帧)的远程帧经过滤波器
这里设置为允许远程帧经过滤波器。
四,高优先级报文
在 CANFD 中支持高优先级报文的功能,高优先级的报文顾名思义就是等级比较高的报文,他可以第一时间被通知到用户。需要注意的是高优先级的报文只能在 FIFO 模式下使用。
CANFD 的高优先报文是在经过滤波器之后将该报文设置为高优先级,所以高优先级的报文配置是在需要通过滤波器来配置。高优先级的报文支持两种模式:
设置高优先级的消息但是保存该消息
设置高优先级的消息并将该消息保存到 RXFIFO0/1 中
高优先级的消息使用了接收回调的方式来通知到用户
1 接收高优先级的消息但不保存
通过对滤波器的设置,可以设置对应帧 ID 的消息为高优先级消息
以上的设置为:标准帧 ID 为 0x402 和 0x403 的报文会通过滤波器并在高优先级的回调函数中通知到用户,但是通过以下的代码是 无法 读取到高优先的消息
2 接收高优先级的消息保存在 RXFIFO0/1 中
通过对滤波器的设置,可以设置对应帧 ID 的消息为高优先级消息
以上的设置为:标准帧 ID 为 0x500 和 0x510 的报文会通过滤波器并在高优先级的回调函数中通知到用户,但是通过以下高优先级消息的回调函数是 无法 直接读取到高优先的消息
高优先级的消息是保存在 RXFIFO 当中,所以在接收到高优先级的消息之后,应该应该尽快的将缓存在 RXFIFO 中的消息都取出,来获取到高优先级帧 ID 的消息。
3 注意事项
高优先级的消息在 RXFIFO 中不会插队,因此收到高优先级的消息后要尽快的取出 RXFIFO 中的数据;
如果滤波器 0 与 滤波器 4 中的帧 ID 设置一致,那么该消息不会被设置为高优先级的消息,因为滤波器是采用遍历的滤波器索引的方式去检查该报文是否可以通过,一旦符合滤波器的规则,那么将不会继续向下索引。
五,总结
CANFD 的配置非常灵活,这些配置与 Message RAM 的配置息息相关,熟练掌握 CANFD 的接收模式,有利于设计出更加健壮的程序,针对以上未提到的几个知识点在做以下总结:
CANFD 的滤波设计会从滤波器的索引 0 开始遍历,直到找到一个符合滤波器规则的通道之后,再接收这个数据,所以一个优秀的滤波器索引设计会提高程序的执行效率;
CANFD 的接收模式虽然很多且复杂,但实际上,直接用默认的 中断线 0,RXFIFO0,应付一般的场景也是游刃有余;
同时使用多种接收方式的时候需要是用一下方式来进行激活通知
在回调函数里面接收完数据之后,只需要根据具体的类型的再次执行激活函数即可。