STM32 CAN过滤器配置详解

1 前言

STM32F10X的bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B。
在CAN协议里,报文的标识符不代表节点的地址,而是和报文的内容相关的。因此,发送者以广播的形式把报文发送给所有的接收者。节点在接收报文时,根据标识符(CAN ID)的值决定软件是否需要该报文;如果需要,就拷贝到SRAM里;如果不需要,报文就被丢弃且无需软件的干预。
为满足这一需求,在互联型产品中, bxCAN控制器为应用程序提供了28个位宽可变的、可配置的过滤器组(270);在其它产品中,bxCAN为应用程序提供了14个位宽可变的、可配置的过滤器组(130),以便只接收那些软件需要的报文。硬件过滤的做法节省了CPU开销,否则就必须由软件过滤从而占用一定的CPU开销。每个过滤器组x由2个32位寄存器,CAN_FxR0和CAN_FxR1组成。
[互联型产品是指STM32F105xx和STM32F107xx微控制器。]

为了让大家了解STM32的bxCAN的接收过滤机制,首先大家需要了解几个概念。

2 几个重要的概念

2.1 为什么要过滤器?

bxCAN的过滤器,你只需要设置好你感兴趣的那些CAN报文ID,那么MCU就只能收到这些CAN报文,是从硬件上过滤掉,完全不需要软件参与进来,从而节省了大大节省了MCU的时间,可以更加专注于其他事务,这个就是bxCAN过滤器的意义所在。

2.2 两种过滤模式(列表模式与掩码模式)

假设我们是bxCAN这个IP的设计者,现在由我们来设计过滤器,那么我们该如何设计呢?

首先我们是不是很快就会想到只要准备好一张表,把我们需要关注的所有CAN报文ID写上去,开始过滤的时候只要对比这张表,如果接收到的报文ID与表上的相符,则通过,如果表上没有,则不通过,这个就是简单的过滤方案。恭喜你!bxCAN过滤器的列表模式采用的就是这种方案。

但是,这种列表方案有点缺陷,即如果我们只关注一个报文ID,则需要往列表中写入这个ID,如果需要关注两个,则需要写入两个报文ID,如果需要关注100个,则需要写入100个,如果需要1万个,那么需要写入1万个,可问题是,有这个大的列表供我们使用吗?大家都知道,MCU上的资源是有限的,不可能提供1万个或更多,甚至100个都嫌多。非常明显,这种列表的方式受到列表容量大小的限制,实际上,bxCAN的一个过滤器若工作在列表模式下,scale为32时,每个过滤器的列表只能写入两个报文ID,若scale为16时,每个过滤器的列表最多可写入4个CAN ID,由此可见,MCU的资源是非常非常有限的,并不能任我们随心所欲。因此,我们需要考虑另外一种替代方案,这种方案应该不受到数量限制。

2.3 验证码与屏蔽码

对于机器来说,我们要为它准备好两张纸片,一片写上屏蔽码,另一片纸片写上验证码屏蔽码上相应位为1时,表示此位需要与验证码对应位进行比较,反之,则表示不需要。机器在执行任务的时候先将获取的身份证号码与屏蔽码进行“与”操作,再将结果与验证码的进行比较,根据判断是否相同来决定是否通过。整个判别流程如下所示:
STM32 CAN过滤器配置详解_第1张图片
从上图可以很容易地理解屏蔽码与验证码的含义,这样一来,能通过的结果数量就完全取决于屏蔽码,设得宽,则可以通过的多(所有位为0,则不过任何过滤操作,则谁都可以通过),设得窄,则通过的少(所有位设为1,则只有一个能通过)。那么知道这个有什么用呢?因为bxCAN的过滤器的掩码模式就是采用这种方式,在bxCAN中,分别采用了两个寄存器(CAN_FiR1,CAN_FiR2)来存储屏蔽码与验证码,从而实现掩码模式的工作流程的。这样,我们就知道了bxCAN过滤器的掩码模式的大概工作原理。

但是,我们得注意到,采用掩码模式的方式并不能精确的对每一个ID进行过滤,打个比方,还是采用之前的守卫的例子,假如城主要求只有1150~1158年出生的人能通过,那么,若我们还是才用掩码模式,那么掩码就设为第7,9位为”1”,对应的,验证码的7,9位分别为”115”,这样就可以了。但是,仔细一想,出生于1159的人还是可以通过,是不是?但总体来说,虽然没有做到精确过滤,但我们还是能做到大体过滤的,而这个就是掩码模式的缺点了。在实际应用时,取决于需求,有时我们会同时使用到列表模式和掩码模式,这都是可能的。

2.4 列表模式与掩码模式的对比

综合之前所述,下面我们来对比一下列表模式与掩码模式这两种模式的优缺点。
STM32 CAN过滤器配置详解_第2张图片

2.5 标准CAN ID与扩展CAN ID

1986 年德国电气商BOSCH公司开发出面向汽车的CAN 通信协议,刚开始的时候,CAN ID定义为11位,我们称之为标准格式,ISO11898-1标准中CAN的基本格式如下图所示:
STM32 CAN过滤器配置详解_第3张图片
如上图所示,标准CAN ID存放在上图ID18~ID28中,共11位。随着工业发展,后来发现11位的CAN ID已经不够用,于是就增加了18位,扩展CAN ID到29位,如下图所示:
STM32 CAN过滤器配置详解_第4张图片
从上图对比扩展CAN报文与标准CAN报文,发现在仲裁域部分,扩展CAN报文的CAN ID包含了base Identifier与extension Identifier,即基本ID与扩展ID,而标准CAN报文的CAN ID部分只包含基本ID,扩展ID(ID0~ID17)被放在基本ID的右方,也就是说,属于低位。知道这些有什么用呢?至少我们可以得到这两条信息:

  • 标准ID一般小于或等于<=0x7FF(11位),只包含基本ID。
  • 对于扩展CAN的低18位为扩展ID,高11位为基本ID。

例如标准CAN ID 0x7E1,二进制展开为111 1110 0001 ,其全部是基本ID。

再例如扩展CAN ID 0x1835f107,二进制展开为[1 1000 0011 10][01 11110001 0000 0111],总共29位,左边红色中括号中的11位为基本ID,右边绿色中括号内的18位为扩展ID,请记住这个信息!知道这个之后,我们可以很方便地将一个CANID拆分成基本ID和扩展ID,这个也将在后续的内容中多次用到,再次留意一下,扩展ID是位于基本ID的右方,在扩展CAN ID的构成中,扩展ID位于低18位,而基本ID位于高11位,于是要获取一个扩展CANID的基本ID,就只需要将这个CANID右移18位。

3 设置过滤器模式

下图来自STM32F10X参考手册:
STM32 CAN过滤器配置详解_第5张图片
上图中可以看出,过滤器一共有4种工作模式:

32位宽的掩码模式,  配置过滤2个不同的ID号
32位宽的列表模式,  配置过滤4个不同的ID号
16位宽的列表模式,  配置过滤2个不同的ID号
16位宽的掩码模式    配置过滤1个不同的ID号


滤波模式有以下两种

屏蔽位模式
标识符列表模式

过滤器的位宽

16位过滤器
32位过滤器

屏蔽ID设置注意要点
在配置为标识符掩码模式时,若配置了多组标识符掩码,则只要接收到的标识符符合其中任意一组掩码的都会通过滤波器,所以千万不要有其中一组是通配的(即掩码为0x0000),否则你设置的其它掩码组都会无效,这个问题在配置为16位标识符掩码模式时很容易犯,因为这个时候你必须得配两组,很多人就会只配置了自己要的那一组(因为很多时候都是只需要一组掩码就够了),另外一组就不配了(0x0000,即通配),如下代码:

CAN_FilterInitStructure.CAN_FilterIdHigh=0x1200<<5;
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000<<5;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xff00;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;

代码本意是要让符合0x12xx的标识符通过滤波器,但是由于它第二组的掩码设置为0x0000,导致所有的标识符都通配了;如果要实现预期效果,则应该把第二组的ID和Mask也设置为一样,或者只把mask改为0xffff,这样就比预期多了一个0x0000可以通过滤波器。

下面将分别介绍过滤器的4种工作模式

3.1 32位宽的掩码模式

在这里插入图片描述
如上图所示,当寄存器FSC0 = 1; FBM0 = 0; 则0号过滤器处于32位宽的掩码模式,这时F0R1需配置ID即前面说的验证码,F0R2需配置掩码即前面说的屏蔽码;其余几个滤波器只是x从1变到13,对应不同的寄存器分别进行配置即可。最右边的num表示这两个32位寄存器都是配置同一个滤波器。

最下边显示的是与CAN ID各位对应的映射关系。上图最下边的映射关系恰好等于扩展CAN ID左移3位再补上IDE(扩展帧标识),RTR(远程帧标志)。注意看上图最底下的各位定义,可以看出,从右到左,首先,最低位是没有用的,然后是RTR,表示是否为远程帧,接着IDE,扩展帧标志,然后才是EXID[0:17]这18位扩展ID,最后才是STID[0:10]这11位标准ID,也就是前面所说的基本ID。在进行配置的时候,即将希望通过的CAN ID写入的时候,要注意各个位对号入座,即基本ID放到对应的STD[0:10],扩展ID对应放到EXID[0:17],若是扩展帧,则需要将IDE设为“1”,标准帧则为“0”,数据帧设RTR为“0”,远程帧设RTR为“1”。

在32位宽的掩码模式下,既可以过滤标准CAN ID,也可以过滤扩展CAN ID,甚至两者混合着来也是可以的,下面我们就这3种情况分别给出示例。

1、只针对标准CAN ID过滤

如下代码示例:
注:下面使用HAL库

static void CANFilterConfig_Scale32_IdMask_StandardIdOnly(void)
{
  CAN_FilterConfTypeDef  sFilterConfig;
  uint16_t StdIdArray[10] ={0x7e0,0x7e1,0x7e2,0x7e3,0x7e4,
                                0x7e5,0x7e6,0x7e7,0x7e8,0x7e9}; //定义一组标准CAN ID
  uint16_t      mask,num,tmp,i;
  
  sFilterConfig.FilterNumber = 2;				//使用过滤器2
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;		//配置为掩码模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;	//设置为32位宽
  sFilterConfig.FilterIdHigh =(StdIdArray[0]<<5);		//验证码可以设置为StdIdArray[]数组中任意一个,这里使用StdIdArray[0]作为验证码
  sFilterConfig.FilterIdLow =0;
  
  mask =0x7ff;						//下面开始计算屏蔽码
  num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);
  for(i =0; i<num; i++)		//屏蔽码位StdIdArray[]数组中所有成员的同或结果
  {
    tmp =StdIdArray[i] ^ (~StdIdArray[0]);	//所有数组成员与第0个成员进行同或操作
    mask &=tmp;
  }
  sFilterConfig.FilterMaskIdHigh =(mask<<5);
  sFilterConfig.FilterMaskIdLow =0|0x02; 		//只接收数据帧
  
  sFilterConfig.FilterFIFOAssignment = 0;		//设置通过的数据帧进入到FIFO0中
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;
  
  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

如上代码所示,对于验证码,任意一个期望通过的CAN ID都是可以设为验证码的,但屏蔽码,却是所有期望通过的CAN ID相互同或后的最终结果。
注:对于移位不明白的参考前面模式图中的的Mapping映射加以理解。

2、只针对扩展CAN ID过滤

如下代码示例:

static void CANFilterConfig_Scale32_IdMask_ExtendIdOnly(void)
{
  CAN_FilterConfTypeDef  sFilterConfig;
  //定义一组扩展CAN ID用来测试
uint32_t ExtIdArray[10] ={0x1839f101,0x1835f102,0x1835f113,0x1835f124,0x1835f105,
                            0x1835f106,0x1835f107,0x1835f108,0x1835f109,0x1835f10A};
  uint32_t      mask,num,tmp,i;
  
  sFilterConfig.FilterNumber = 3;					//使用过滤器3
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;			//配置为掩码模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;		//设为32位宽
  sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;//数组任意一个成员都可以作为验证码
  sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff) | CAN_ID_EXT;
  
  mask =0x1fffffff;
  num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);
  for(i =0; i<num; i++)				//屏蔽码位数组各成员相互同或的结果
  {
    tmp =ExtIdArray[i] ^ (~ExtIdArray[0]);	//都与第一个数据成员进行同或操作
    mask &=tmp;
  }
  mask <<=3;    								//对齐寄存器
  sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;
  sFilterConfig.FilterMaskIdLow = (mask&0xffff)|0x02; 		//只接收数据帧
  sFilterConfig.FilterFIFOAssignment = 0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;
  
  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

如上代码所示,与之前的标准CAN ID相比,扩展CAN ID的验证码与屏蔽码放入到相对应的寄存器时所移动的位数与标准CAN ID时有所差别,其他的都一样。
注:对于移位不明白的参考前面模式图中的的Mapping映射加以理解。

3、标准CAN ID与扩展CAN ID混合过滤

static void CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(void)
{
  CAN_FilterConfTypeDef  sFilterConfig;
  //定义一组标准CAN ID
uint32_t StdIdArray[10] ={0x711,0x712,0x713,0x714,0x715,
                          0x716,0x717,0x718,0x719,0x71a};
  //定义另外一组扩展CAN ID
uint32_t ExtIdArray[10] ={0x1900fAB1,0x1900fAB2,0x1900fAB3,0x1900fAB4,0x1900fAB5,
                            0x1900fAB6,0x1900fAB7,0x1900fAB8,0x1900fAB9,0x1900fABA};
  uint32_t      mask,num,tmp,i,standard_mask,extend_mask,mix_mask;
  
  sFilterConfig.FilterNumber = 4;				//使用过滤器4
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;		//配置为掩码模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;	//设为32位宽
  sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;	//使用第一个扩展CAN  ID作为验证码
  sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff);
  
  standard_mask =0x7ff;		//下面是计算屏蔽码
  num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);
  for(i =0; i<num; i++)			//首先计算出所有标准CAN ID的屏蔽码
  {
    tmp =StdIdArray[i] ^ (~StdIdArray[0]);
    standard_mask &=tmp;
  }
  
  extend_mask =0x1fffffff;
  num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);
  for(i =0; i<num; i++)			//接着计算出所有扩展CAN ID的屏蔽码
  {
    tmp =ExtIdArray[i] ^ (~ExtIdArray[0]);
    extend_mask &=tmp;
  }
  mix_mask =(StdIdArray[0]<<18)^ (~ExtIdArray[0]);	//再计算标准CAN ID与扩展CAN ID混合的屏蔽码
  mask =(standard_mask<<18)& extend_mask &mix_mask;	//最后计算最终的屏蔽码
  mask <<=3;    						//对齐寄存器
 
  sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;
  sFilterConfig.FilterMaskIdLow = (mask&0xffff);
  sFilterConfig.FilterFIFOAssignment = 0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;
  
  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

如上代码所示,在混合的情况下,只需稍微修改下屏蔽码的计算方式就可以了,其他的基本没有什么变化。
注:对于移位不明白的参考前面模式图中的的Mapping映射加以理解。

3.2 32位宽的列表模式

在这里插入图片描述

如上图所示,当寄存器FSC0 = 1; FBM0 = 1; 则0号过滤器处于32位宽的列表模式,这个过滤器一分为2可以保存两个CAN ID; 这时F0R1和F0R2都是配置ID即前面说的验证码;其余几个滤波器只是x从1变到13,一共14个,对应不同的寄存器分别进行配置即可。最右边的num表示这两个32位寄存器分别配置2个滤波器。
注意看上图最底下的Mapping各位定义,可以看出,从右到左,首先,最低位是没有用的,然后是RTR,表示是否为远程帧,接着IDE,扩展帧标志,然后才是EXID[0:17]这18位扩展ID,最后才是STID[0:10]这11位标准ID,也就是前面所说的基本ID。在进行配置的时候,即将希望通过的CAN ID写入的时候,要注意各个位对号入座,即基本ID放到对应的STD[0:10],扩展ID对应放到EXID[0:17],若是扩展帧,则需要将IDE设为“1”,标准帧则为“0”,数据帧设RTR为“0”,远程帧设RTR为“1”。示例代码如下:

使用标准库:

static void CANFilterConfig_Scale32_IdList(void)
{
	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); //设置过滤器
}

使用HAL库:

static void CANFilterConfig_Scale32_IdList(void)
{
  CAN_FilterConfTypeDef  sFilterConfig;
  uint32_t StdId =0x321;				//这里写入两个CAN ID,一个位标准CAN ID
  uint32_t ExtId =0x1800f001;			//一个位扩展CAN ID
  
  sFilterConfig.FilterNumber = 0;				//使用过滤器0
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;		//设为列表模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;	//配置为32位宽
  sFilterConfig.FilterIdHigh = StdId<<5;			//基本ID放入到STID中
  sFilterConfig.FilterIdLow = 0|CAN_ID_STD;			//设置IDE位为0
  sFilterConfig.FilterMaskIdHigh = ((ExtId<<3)>>16)&0xffff;
  sFilterConfig.FilterMaskIdLow = (ExtId<<3)&0xffff|CAN_ID_EXT;	//设置IDE位为1
  sFilterConfig.FilterFIFOAssignment = 0;			//接收到的报文放入到FIFO0中
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;
  
  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

上面对两种库的配置可以看到都差不多。
这里需要说明一下,由于我们使用的是HAL库,在HAL库中,CAN_FxR1与CAN_FxR2寄存器分别被拆成两段,CAN_FxR1寄存器的高16位对应着上面代码中的FilterIdHigh,低16位对应着FilterIdLow,而CAN_FxR2寄存器的高16位对应着FilterMaskIdHigh,低16位对应着FilterMaskIdLow,这个CAN_FilterConfTypeDef的的4个成员FilterIdHigh,FilterIdLow,FilterMaskIdHigh,FilterMaskIdLow,不应该单纯看其名字,被其名字误导,而应该就单纯地将这4个成员看成4个uint_16类型的变量x,y,m,n而已,后续其他示例也是同样理解,不再重复解释。这4个16位的变量其具体含义取决于当前过滤器工作与何种模式,比如当前32位宽的列表模式下,FilterIdHigh与FilterIdLow一起用来存放一个CAN ID,FilterMaskIdHigh与FilterMaskIdLow用来存放另一个CAN ID,不再表示其字面所示的mask含义,这点我们需要特别注意。

在上述代码示例中,我们分别将标准CAN ID和扩展CAN ID放入到CAN_FxR1与CAN_FxR2寄存器中。对于标准CAN ID,对比 图11,由于标准CAN ID只拥有标准ID,所以,只需要将标准ID放入到高16位的STID[0:10]中,高16位最右边被EXID[13:17]占着,因此,需要将StdId左移5位才能刚好放入到CAN_FxR1的高16位中,于是有了:

sFilterConfig.FilterIdHigh = StdId<<5;

另一个扩展CAN ID ExtId类型,将其基本ID放入到STID中,扩展ID放入到EXID中,最后设置IDE位为1。就这样配置好了。

3.3 16位宽的掩码模式

STM32 CAN过滤器配置详解_第6张图片

如上图所示,当寄存器FSC0 = 0; FBM0 = 0; 则0号过滤器处于16位宽的掩码模式,这个过滤器一分为2可以保存两个CAN ID和两个掩码;
CAN_FxR1的低16位是作为验证码,对应的16位屏蔽码为CAN_FxR1的高16位,同样的,CAN_FxR2的低16位是作为验证码,对应与CAN_FxR2的高16位为屏蔽码。
其示例代码如下:

static void CANFilterConfig_Scale16_IdMask(void)
{
  CAN_FilterConfTypeDef  sFilterConfig;
  uint16_t StdIdArray1[10] ={0x7D1,0x7D2,0x7D3,0x7D4,0x7D5,	//定义第一组标准CAN ID
                          0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};
  uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755,	//定义第二组标准CAN ID
                          0x756,0x757,0x758,0x759,0x75A};
  uint16_t      mask,tmp,i,num;
  
  sFilterConfig.FilterNumber = 5;					//使用过滤器5
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;			//配置为掩码模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;		//设为16位宽
  
  //配置第一个过滤对
  sFilterConfig.FilterIdLow =StdIdArray1[0]<<5;			//设置第一个验证码
  mask =0x7ff;
  num =sizeof(StdIdArray1)/sizeof(StdIdArray1[0]);
  for(i =0; i<num; i++)							//计算第一个屏蔽码
  {
    tmp =StdIdArray1[i] ^ (~StdIdArray1[0]);
    mask &=tmp;
  }
  sFilterConfig.FilterMaskIdLow =(mask<<5)|0x10;    //只接收数据帧
  
  //配置第二个过滤对
  sFilterConfig.FilterIdHigh = StdIdArray2[0]<<5;	//设置第二个验证码
  mask =0x7ff;
  num =sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);
  for(i =0; i<num; i++)					//计算第二个屏蔽码
  {
    tmp =StdIdArray2[i] ^ (~StdIdArray2[0]);
    mask &=tmp;
  }
  sFilterConfig.FilterMaskIdHigh = (mask<<5)|0x10;  //只接收数据帧
  
 
  sFilterConfig.FilterFIFOAssignment = 0;		//通过的CAN 消息放入到FIFO0中
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;
  
if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

如上代码所示,在这种模式下,其特殊之处就是可以配置两套验证码,屏蔽码组合,可以分别相对独立地对标准CAN ID进行过滤。

3.4 16位宽的列表模式

STM32 CAN过滤器配置详解_第7张图片

如上图所示,当寄存器FSC0 = 0; FBM0 = 1; 则0号过滤器处于16位宽的列表模式,这个过滤器一分为4可以保存4个CAN ID;
在16位宽的列表模式下,FilterIdHigh,FilterIdLow,FilterMaskIdHigh,FilterMaskIdLow这4个16位变量都是用来存储一个标准CAN ID,这样,就可以存放4个标准CAN ID了,需要注意地是,此种模式下,不能处理扩展CAN ID,凡是需要过滤扩展CAN ID的,都是需要用到32位宽的模式。代码示例:

static void CANFilterConfig_Scale16_IdList(void)
{
  CAN_FilterConfTypeDef  sFilterConfig;
  uint32_t StdId1 =0x123;						//这里采用4个标准CAN ID作为例子
  uint32_t StdId2 =0x124;
  uint32_t StdId3 =0x125;
  uint32_t StdId4 =0x126;
  
  sFilterConfig.FilterNumber = 1;				//使用过滤器1
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;		//设为列表模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;	//位宽设置为16位
  sFilterConfig.FilterIdHigh = StdId1<<5;	 //4个标准CAN ID分别放入到4个存储中
  sFilterConfig.FilterIdLow = StdId2<<5;
  sFilterConfig.FilterMaskIdHigh = StdId3<<5;
  sFilterConfig.FilterMaskIdLow = StdId4<<5;
  sFilterConfig.FilterFIFOAssignment = 0;			//接收到的报文放入到FIFO0中
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;
  
  if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

4 过滤器匹配序号

一旦收到的报文被存入FIFO,就可被应用程序访问。通常情况下,报文中的数据被拷贝到SRAM中;为了把数据拷贝到合适的位置,应用程序需要根据报文的标识符来辨别不同的数据。
bxCAN提供了过滤器匹配序号,以简化这一辨别过程。
根据过滤器优先级规则,过滤器匹配序号和报文一起,被存入邮箱中。因此每个收到的报文,都有与它相关联的过滤器匹配序号。过滤器匹配序号可以通过下面两种方式来使用:
● 把过滤器匹配序号跟一系列所期望的值进行比较
● 把过滤器匹配序号当作一个索引来访问目标地址

对于标识符列表模式下的过滤器(非屏蔽方式的过滤器),软件不需要直接跟标识符进行比较。
对于屏蔽位模式下的过滤器,软件只须对需要的那些屏蔽位(必须匹配的位)进行比较即可。
在给过滤器编号时,并不考虑过滤器组是否为激活状态。另外,每个FIFO各自对其关联的过滤 器进行编号。请参考下图的例子。
STM32 CAN过滤器配置详解_第8张图片
从上图可以看出,两个FIFO右边的滤波器序号是顺序累加的,与其关联的滤波器是哪个Bank无关。但由于是顺序累加的,所以与其关联的滤波器的工作模式有关系,因为决定了这个滤波器要分配几个序号。

小结:

当有一条报文通过过滤器匹配OK,过滤器匹配序号和报文一起,被存入邮箱中。应用程序可以读取该条报文的匹配序号快速确认对应的是哪个滤波器,比如是个列表模式滤波器则直接可以确定其ID值,通过匹配序号用得好可以简化对报文ID的比较过程
具体如何使用过滤器匹配序号:
● 把过滤器匹配序号跟一系列所期望的值进行比较
● 把过滤器匹配序号当作一个索引来访问目标地址

5 过滤器优先级规则

根据过滤器的不同配置,有可能一个报文标识符能通过多个过滤器的过滤;在这种情况下,存
放在接收邮箱中的过滤器匹配序号,根据下列优先级规则来确定:
● 位宽为32位的过滤器,优先级高于位宽为16位的过滤器
● 对于位宽相同的过滤器,标识符列表模式的优先级高于屏蔽位模式
● 位宽和模式都相同的过滤器,优先级由过滤器号决定,过滤器号小的优先级高
STM32 CAN过滤器配置详解_第9张图片
上面的例子说明了bxCAN的过滤器规则:在接收一个报文时,其标识符首先与配置在标识符列表模式下的过滤器相比较;如果匹配上,报文就被存放到相关联的FIFO中,并且所匹配的过滤器的序号被存入过滤器匹配序号中。如同例子中所显示,报文标识符跟#4标识符匹配,因此报文内容和FMI4被存入FIFO。
如果没有匹配,报文标识符接着与配置在屏蔽位模式下的过滤器进行比较。
如果报文标识符没有跟过滤器中的任何标识符相匹配,那么硬件就丢弃该报文,且不会对软件有任何打扰。

小结:

在接收一个报文时,其标识符首先与配置在**标识符列表模式**下的过滤器相比较,顺序是根据Filer Num从小到大且32位滤波器优先,如果都没有匹配,报文标识符接着与配置在** 屏蔽位模式 **下的过滤器进行比较

6 总结

在实际的应用中,我们需要根据需求的实际情况来决定使用何种过滤配置,STM32互联型的bxCAN提供了28个过滤器,在配置之前,我们需要先将那些需要通过的CAN ID进行整理,若数量少,则使用列表模式,精准,不会放进任何无关的报文;若只有标准CAN ID,则可以考虑使用16位宽模式,若需求中的CAN ID过多,则可以考虑使用多个过滤器,部分使用列表模式,部分使用掩码模式,CAN ID值相近的可以归纳成一组,使用掩码模式进行过滤。但使用掩码模式的同时,我们也需要意识到,也有可能部分不期望的CAN ID也会通过过滤器,掩码放得越宽,带进其他CAN ID的几率就越大,这点我们需要格外注意,视情况进行应用判断和处理。另外,对于相近的CAN ID,我们可以提前计算好屏蔽码,直接在代码中填入,而不是在代码中临时计算,这样可以提高软件效率。

你可能感兴趣的:(MCU相关专栏,stm32,stm32,单片机,arm)