STM32 CAN通讯过滤器使用总结及代码分析

一、简介

在 CAN 协议中,消息的标识符与节点地址无关,但与消息内容有关。因此,发送器将消息广播给所有接收器。在接收到消息时,接收器节点会根据标识符的值来确定软件是否需要该消息。如果需要,该消息将复制到 SRAM 中。如果不需要,则必须在无软件干预的情况下丢弃该消息。

二、详细各概念

2.1 筛选器组

bxCAN 控制器为应用程序提供了 28 个可配置且可调整的筛选器组 (CAN1和CAN2分享28个过滤器组27-0)。在互联型产品中,bxCAN 控制器为应用程序提供了 14 个可配置且可调整的筛选器组 (13-0),以便仅接收软件需要的消息。此硬件筛选功能可以节省软件筛选所需的 CPU 资源。每个筛选器组 x 均包含两个 32 位寄存器,分别是 CAN_FxR0 和 CAN_FxR1。

sFilterConfig.BankNumber = n;

此句代码针对双CAN的STM32产品,配置CAN2可使用的过滤器组的起始编号,n取值范围为0~28。从编号为n的过滤器组到编号为27的过滤器组分配给CAN2使用。如果此句配置不在代码中明确写出来,默认值就是14. 即硬件默认将编号为14起往后的过滤器组分配给CAN2使用。

(配置为32位模式则一个屏蔽要占用两个过滤器组,如下图231)

2.2 过滤器的过滤模式

        STM32提供两种过滤模式供用户设置:屏蔽位模式和标识符列表模式。

2.2.1 屏蔽位模式

为了过滤出一组标识符,应该设置过滤器组工作在屏蔽位模式。
在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。

2.2.2 标识符列表模式

为了过滤出一个标识符,应该设置过滤器组工作在标识符列表模式。
在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。

2.3 过滤器的位宽

        每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:
        ●1个32位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
        ●2个16位过滤器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位

2.4 过滤器组的过滤模式和位宽设置

        过滤器组可以通过相应的CAN_FMR寄存器(CAN过滤器主控寄存器)配置。但是不是什么时候都可以直接配置,在配置一个过滤器组前,必须通过清除CAN_FAR寄存器(CAN过滤器激活寄存器)的FACT位,把它设置为禁用状态。然后才能设置或设置过滤器组的配置。

通过设置CAN_FS1R(CAN过滤器位宽寄存器)的相应FSCx位,可以配置一个过滤器组的位宽。
通过CAN_FM1R(CAN过滤器模式寄存器)的FBMx位,可以配置对应的屏蔽/标识符寄存器的标识符列表模式或屏蔽位模式。

 上图对应四种模式设置寄存器各位的内容。

2.5 过滤器匹配序号

 根据过滤器优先级规则,过滤器匹配序号和报文一起,被存入邮箱中。因此每个收到的报文,都有与它相关联的过滤器匹配序号。

              过滤器匹配序号可以通过下面两种方式来使用:
             ● 把过滤器匹配序号跟一系列所期望的值进行比较
             ● 把过滤器匹配序号当作一个索引来访问目标地址
            对于标识符列表模式下的过滤器(非屏蔽方式的过滤器),软件不需要直接跟标识符进行比较。
            对于屏蔽位模式下的过滤器,软件只须对需要的那些屏蔽位(必须匹配的位)进行比较即可。
在给过滤器编号时,并不考虑过滤器组是否为激活状态。另外,每个FIFO各自对其关联的过滤器进行编号,如下图(此图暂时未能理解):
 

2.6 过滤器优先级规则

根据过滤器的不同配置,有可能一个报文标识符能通过多个过滤器的过滤;在这种情况下,存放在接收邮箱中的过滤器匹配序号,根据下列优先级规则来确定:
● 位宽为32位的过滤器,优先级高于位宽为16位的过滤器
● 对于位宽相同的过滤器,标识符列表模式的优先级高于屏蔽位模式
● 位宽和模式都相同的过滤器,优先级由过滤器号决定,过滤器号小的优先级高

如下图:

 如上图,在接收一个报文时,其标识符首先与配置在标识符列表模式下的过滤器相比较;如果匹配上,报文就被存放到相关联的FIFO中,并且所匹配的过滤器的序号(这时为4)被存入过滤器匹配序号中。如同例子中所显示,报文标识符跟#4标识符匹配,因此报文内容和FMI4被存入FIFO

              如果没有匹配,报文标识符接着与配置在屏蔽位模式下的过滤器进行比较。
              如果报文标识符没有跟过滤器中的任何标识符相匹配,那么硬件就丢弃该报文,且不会对软件有任何打扰。
 

 三、实测代码

CAN ID值的结构分析(非常重要)

ISO11898的定义,如下图:

如上图,基本格式不存在扩展ID,而扩展格式中ID0~ID17为Extension ID,而ID18~ID28为Base ID.

因此CAN ID值0x1800f001用二进制表示为:0b 0001 1000 0000 0000 1111 0000 0000 0001,用括号分别区别为:0b 000[1 1000 0000 00][00 1111 0000 0000 0001],红色部分为扩展ID,蓝色部分为基本ID。

举例:0x35e    二进制:0000 0011 0101 1110     需要左移两位与上图基本ID位对齐,还需左移三位与下图寄存器位对齐。

扩展ID0x0187  相对简单,本身与上图扩展ID位对齐,只需要左移三位即可与下面寄存器位对齐。(自己理解了好久,唉)
 

 位宽为32位的屏蔽模式

 两个过滤器,分别过滤两类ID。(实际项目使用)

u16 std_id   =0x0350;//后四位不比较,即0x350~0x35f
u16 std_id_f =0x035F;
u32 ext_id   =0x00001870;//同上
u32 ext_id_f =0x0000187F;
u32 mask =0;
//配置过滤器0
CAN_FilterInitStructure.CAN_FilterNumber=0;	  //过滤器0  0-13
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;  //屏蔽模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32ID

//扩展ID屏蔽
//ext_id<<3对齐,见上图9,再>>16取高16位
CAN_FilterInitStructure.CAN_FilterIdHigh=((ext_id<<3) >>16) &0xffff;
CAN_FilterInitStructure.CAN_FilterIdLow=(u16)(ext_id<<3) | CAN_ID_EXT;  
mask  = ext_id;
mask ^= ext_id_f;
mask  = ~mask;
mask <<=3;
mask |=0x02; //只接受数据帧
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=(mask>>16)&0xffff;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=mask&0xffff;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //使能
CAN_FilterInit(&CAN_FilterInitStructure);//配置
		

CAN_FilterInitStructure.CAN_FilterNumber=1;	  //过滤器1
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; 
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32ID
//基本ID屏蔽
//设置标识符寄存器高字节.这里为什么是左移5位呢?从上图可以看出,CAN_FilterIdHigh包含的是STD[0~10]和EXID[13~17],标准CAN ID本身是不包含扩展ID数据,因此为了要将标准CAN ID放入此寄存器,标准CAN ID首先应左移5位后才能对齐.
CAN_FilterInitStructure.CAN_FilterIdHigh=std_id<<5;
CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_Id_Standard;
mask  = std_id<<5;
mask ^= std_id_f<<5;
mask  = ~mask;
mask |=0x02; //只接受数据帧
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = mask ;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;////关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //使能
CAN_FilterInit(&CAN_FilterInitStructure);//配置

一个过滤器筛选出一个标准ID,一个扩展ID。(网上)

CAN_FilterInitTypeDef  CAN_FilterInitStructure;
U16 std_id =0x7e9;
U32 ext_id =0x1800f001;
CAN_FilterInit(&CAN_FilterInitStructure); //初始化CAN_FilterInitStructrue结构体变量
CAN_FilterInitStructure.CAN_FilterNumber=0;     //设置过滤器组0,范围为0~13
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList;    //设置过滤器组0为标识符列表模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;   //设置过滤器组0位宽为32位
 
//设置屏蔽寄存器,这里当标识符寄存器用
CAN_FilterInitStructure.CAN_FilterIdHigh=std_id<<5) ;  //为什么左移5位?与上面相同道理,这里不再重复解释
CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_ID_STD; //设置标识符寄存器低字节,CAN_FilterIdLow的ID位可以随意设置,在此模式下不会有效。
 
//设置标识符寄存器
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=((ext_id<<3)>>16) & 0xffff; //设置屏蔽寄存器高字节
CAN_FilterInitStructure.CAN_FilterMaskIdLow=((ext_id<<3)& 0xffff) | CAN_ID_EXT;   //设置屏蔽寄存器低字节
 
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;  //此过滤器组关联到接收FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此过滤器组
CAN_FilterInit(&CAN_FilterInitStructure); //设置过滤器

 位宽为16位的屏蔽码模式

在此模式下,最多存在两个屏蔽码过滤器,如下图:

                                                       

                                           

由上图映射可知,最下面的映射只包含STDID0~ID10,因此,此模式下的两个屏蔽过滤器只能实现对标准ID的过滤。具体代码就不介绍了,参见上图的映射即可。

 位宽为16位的标识符列表模式

                                                   

                                    

在此模式下,由于标识符寄存器的高16位和低16位,屏蔽寄存器的高16位和低16位都用来做标识符寄存器,因此,最多可存在4个标识符过滤器。同样,只能实现对标准帧的过滤。具体代码就不介绍了,参见上图的映射即可。
(部分参考https://blog.csdn.net/flydream0/article/details/8148791) 

猜你喜欢

转载自blog.csdn.net/qq_27747359/article/details/89682407