使用STM32 CAN滤波器接收指定ID的数据

CAN

文章目录

  • CAN
  • 一、配置
  • 1、对扩展数据帧进行过滤:(只接收扩展数据帧)
  • CAN_FilterIdHigh:
  • CAN_FilterIdLow:
  • 2、对扩展远程帧过滤:(只接收扩展远程帧)
  • 3、对标准远程帧过滤:(只接收标准远程帧)
  • 4、对标准数据帧过滤:(只接收标准数据帧)
  • 5、对扩展帧进行过滤:(只接收扩展帧)
  • 6、对标准帧进行过滤:(只接收标准帧)
  • 二、CAN过滤器详解
  • 2.1 过滤器
  • 2.2 过滤器的过滤模式
  • 2.2.1 屏蔽位模式
  • 2.2.2 标识符列表模式
  • 2.3 过滤器的位宽
  • 2.3 过滤器组的过滤模式和位宽设置
  • 2.4 过滤器匹配序号
  • 2.5 过滤器优先级规则
  • 三、CAN ID值的结构分析
  • 3.1位宽为32位的屏蔽模式
  • 3.2示例

  • 一、配置

    (slave_id=0x18010001为只接收的id号):

    1、对扩展数据帧进行过滤:(只接收扩展数据帧)

    CAN_FilterInitStructure.CAN_FilterIdHigh=(((u32)slave_id<<3)&0xFFFF0000)>>16;
    CAN_FilterInitStructure.CAN_FilterIdLow=(((u32)slave_id<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;
    

    CAN_FilterIdHigh:

    这一步是为了从32位的slave_id中提取出高16位作为CAN过滤器的ID高位部分。

    (u32)slave_id << 3:首先将slave_id强制转换为32位的无符号整数(u32),然后将其左移3位。这是为了将slave_id向左移动3位,为后续的位运算做准备。

    & 0xFFFF0000:然后将结果与0xFFFF0000进行按位与操作。0xFFFF0000是一个32位的十六进制数,它的二进制表示是11111111111111110000000000000000。这个操作会将结果的低16位全部变为0,只保留高16位的内容。>> 16:最后将上一步的结果右移16位。这个操作是为了将之前保留的高16位部分移到最低的16位中,这样就得到了最终的ID高位部分。

    因此,整个操作的目的是从32位的slave_id中提取出高16位,并放置到CAN_FilterInitStructure.CAN_FilterIdHigh中,以用作CAN过滤器的ID高位部分。

    CAN_FilterIdLow:

    这一步是为了从32位的slave_id中提取出低16位,并添加扩展帧和数据帧标志位,最后将结果进行掩码操作。

    (u32)slave_id << 3:首先将slave_id强制转换为32位的无符号整数(u32),然后将其左移3位。这是为了将slave_id向左移动3位,为后续的位运算做准备。

    | CAN_ID_EXT | CAN_RTR_DATA:然后将上一步的结果与CAN_ID_EXT和CAN_RTR_DATA进行按位或操作。CAN_ID_EXT代表扩展帧标志位,CAN_RTR_DATA代表数据帧标志位。通过按位或操作,可以将这两个标志位添加到ID的低16位部分。

    & 0xFFFF:最后将上一步的结果与0xFFFF进行按位与操作。0xFFFF是一个16位的十六进制数,它的二进制表示是1111111111111111。这个操作会将结果的高16位全部变为0,只保留低16位的内容。

    因此,整个操作的目的是从32位的slave_id中提取出低16位,并将扩展帧和数据帧标志位添加到其中,最后进行掩码操作,得到最终的ID低位部分,并放置到CAN_FilterInitStructure.CAN_FilterIdLo中,以用作CAN过滤器的ID低位部分。

    2、对扩展远程帧过滤:(只接收扩展远程帧)

    CAN_FilterInitStructure.CAN_FilterIdHigh = (((u32)slave_id<<3)&0xFFFF0000)>>16;
    CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)slave_id<<3)|CAN_ID_EXT|CAN_RTR_REMOTE)&0xFFFF;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;
    

    3、对标准远程帧过滤:(只接收标准远程帧)

    CAN_FilterInitStructure.CAN_FilterIdHigh   =(((u32)slave_id<<21)&0xffff0000)>>16;
    CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)slave_id<<21)|CAN_ID_STD|CAN_RTR_REMOTE)&0xffff;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;
    

    4、对标准数据帧过滤:(只接收标准数据帧)

    CAN_FilterInitStructure.CAN_FilterIdHigh   =(((u32)slave_id<<21)&0xffff0000)>>16;
    CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)slave_id<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xffff;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;
    

    5、对扩展帧进行过滤:(只接收扩展帧)

     CAN_FilterInitStructure.CAN_FilterIdHigh   =(((u32)slave_id<<3)&0xFFFF0000)>>16;
     CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)slave_id<<3)|CAN_ID_EXT)&0xFFF
     CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
     CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFC;
    

    6、对标准帧进行过滤:(只接收标准帧)

    CAN_FilterInitStructure.CAN_FilterIdHigh   =(((u32)slave_id<<21)&0xffff0000)>>16;
    CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)slave_id<<21)|CAN_ID_STD)&0xffff;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFC;
    

    二、CAN过滤器详解

    2.1 过滤器

    STM32总共提供14个过滤器组来处理CAN接收过滤问题,每个过滤器组包含两个32位寄存器CAN_FxR0和CAN_FxR1组成,在设置为屏蔽位模式下,其中一个作为标识符寄存器,另一个作为屏蔽码寄存器。过滤器组中的每个过滤器,编号(叫做过滤器号)从0开始,到某个最大数值(这时最大值并非13,而是取决于14个过滤器组的模式和位宽的设置,当全部配置为位宽为16,且为标识符列表模式时,最大编号为14*4-1=55)。

    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.3 过滤器组的过滤模式和位宽设置

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

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

    这些大家可以看一下。

    2.4 过滤器匹配序号

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

    在给过滤器编号时,并不考虑过滤器组是否为激活状态。另外,每个FIFO各自对其关联的过滤器进行编号。

    2.5 过滤器优先级规则

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

    三、CAN ID值的结构分析

    当给定一个CAN ID,如0x1800f001,当然这个是扩展ID,这里要问的是,这个CAN ID的值本身包含两部分,即基本ID与扩展ID,即么你知道这个扩展ID0x1800f001的哪些位是基本ID,哪些位又是扩展ID?(在基本CANID格式下不存在这个问题)
    我们先来看这两张图


    图上说的很清楚 ID0-17是扩展ID,18-28为标准ID,这个时候我们就可以下手了
    ID值0x1800f001用二进制表示为:0b 0001 1000 0000 0000 1111 0000 0000 0001,用括号分别区别为:0b 000[1 1000 0000 00][00 1111 0000 0000 0001],斜体部分为扩展ID,加粗部分为基本ID。
    每个寄存器都是32位的。最下边显示的是与CAN ID各位定位的映射关系。上图最下边的映射关系恰好等于扩展CAN值左移3位再补上IDE(扩展帧标识),RTR(远程帧标志)。

    3.1位宽为32位的屏蔽模式

    在此种模式下中过滤多个CAN ID,此时,过滤器包含两个寄存器,屏蔽码寄存器和标识符寄存器。此模式下最多只存在一个屏蔽过滤器。

    有些同学可能看不懂,我们可以从上帝视角看一下。上面的ID为标识符寄存器,中间部分的MASK为屏蔽码寄存器。可以看到他分成了4个块 比如你要发送ID为00 00 01 02 [7:0]=02 [15:8]=01依次类推。
    对号入座的方法多种多样1:在各种过滤器模式下,CAN ID与寄存器相应位置一定要匹配;2:在屏蔽方式下,屏蔽码寄存器某位为1表示接收到的CAN ID对应的位必须对验证码寄存器对应的位相同。

    3.2示例

    下面给出一个代码例子,假设我们要接收多个ID:0x7e9,0x1800f001,前面为标准ID,后面为扩展ID,要同时能接收这两个ID,那么该如何设置这个过滤器呢?

    CAN_FilterInitTypeDef  CAN_FilterInitStructure;
    U16 std_id =0x7e9;
    U32 ext_id =0x1800f001;
    U32 mask =0;
     
    CAN_FilterInit(&CAN_FilterInitStructure);     //初始化CAN_FilterInitStructrue结构体变量
    CAN_FilterInitStructure.CAN_FilterNumber=0;      //设置过滤器组0,范围为0~13
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;    //设置过滤器组0为屏蔽模式
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;   //设置过滤器组0位宽为32位
     
    //标识位寄存器的设置
    //ext_id<<3对齐,见上图9,再>>16取高16位
    CAN_FilterInitStructure.CAN_FilterIdHigh=((ext_id<<3) >>16) &0xffff;  //设置标识符寄存器高字节。先左移三位将ID对齐(方便对号入座),然后右移16位把高位数据拿出来(下面还有补充)
    CAN_FilterInitStructure.CAN_FilterIdLow=(U16)(ext_id<<3) | CAN_ID_EXT;   //设置标识符寄存器低字节(左移三位先对齐)这个有补充可以看下面的
    //这里也可以这样设置
    //CAN_FilterInitStructure.CAN_FilterIdHigh=std_id<<5;  //设置标识符寄存器高字节.这里为什么是左移5位呢?从上图可以看出,CAN_FilterIdHigh包含的是STD[0~10]和EXID[13~17],标准CAN ID本身是不包含扩展ID数据,因此为了要将标准CAN ID放入此寄存器,标准CAN ID首先应左移5位后才能对齐.
    //CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_ID_EXT;   //设置标识符寄存器低字节,这里也可以设置为CAN_ID_STD
     
    //屏蔽寄存器的设置
    //这里的思路是先将标准CAN ID和扩展CAN ID对应的ID值先异或后取反,为什么?异或是为了找出两个CAN ID有哪些位是相同的,是相同的位则说明需要关心,需要关心的位对应的屏蔽码位应该设置为1,因此需要取反一下。最后再整体左移3位。
    mask =(std_id<<18);//这里为什么左移18位?因为从ISO11898中可以看出,标准CAN ID占ID18~ID28,为了与CAN_FilterIdHigh对齐,应左移2位,接着为了与扩展CAN对应,还应该再左移16位,因此,总共应左移2+16=18位。也可以用另一个方式来理解:直接看Mapping的内容,发现STDID相对EXID[0]偏移了18位,因此左移18位.
    mask ^=ext_id;//将对齐后的标准CAN与扩展CAN异或后取反
    mask =~mask;
    mask <<=3;//再整体左移3位
    mask |=0x02; //只接收数据帧,不接收远程帧
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=(mask>>16)&0xffff; //设置屏蔽寄存器高字节
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=mask&0xffff;   //设置屏蔽寄存器低字节
     
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;  //此过滤器组关联到接收FIFO0
    CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活此过滤器组
    CAN_FilterInit(&CAN_FilterInitStructure); //设置过滤器
    

    由于右移16位操作可能会导致结果超出16位无符号整数的范围,因此需要使用& 0xFFFF进行按位与运算,以确保结果不超过该范围。

    CAN_ID_EXT是用于设置标识符寄存器低字节的掩码。通过将ext_id左移3位后与CAN_ID_EXT进行按位或运算,可以确保扩展ID的高位数据正确设置,并将低位对齐到标识符寄存器的低字节。

    在CAN通信中,标识符寄存器的低字节包含了扩展ID的低位数据以及一些控制位,如远程帧标志位(RTR)等。所以需要使用按位或运算来将扩展ID的低位数据与控制位进行合并,确保正确设置标识符寄存器的低字节。

    总结可知,当过滤器为屏蔽模式时,标识符寄存器对应的ID内容可为任意一需求接收的ID值,当同时要接收标准帧和扩展帧时,标识符寄存器对应IDE位也随意设置,屏蔽寄存器的IDE位设置为0,表示不关心标准帧还是扩展帧。而屏蔽寄存器对应的ID内容为各需求接收的ID值依次异或的结果再取反。

    最后感谢
    flydream0这位博主,我也只是在他的基础上加上自己的理解。

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32 CAN滤波器接收指定ID的数据

    发表评论