can 过滤器_stm32can过滤器不过滤 - CSDN
精华内容
参与话题
  • STM32--CAN ID过滤器分析

    千次阅读 2019-08-08 13:30:27
    1 前言 在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的内容相关的。因此,发送者以广播的形式把报文...为满足这一需求,bxCAN为应用程序提供了14个位宽可变的、可配置的过滤器组(13~0),以便只接收那...

    1 前言

    在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的内容相关的。因此,发送者以广播的形式把报文发送给所有的接收者。节点在接收报文时,根据标识符(CAN ID)的值决定软件是否需要该报文;如果需要,就拷贝到SRAM里;如果不需要,报文就被丢弃且无需软件的干预。
             为满足这一需求,bxCAN为应用程序提供了14个位宽可变的、可配置的过滤器组(13~0),以便只接收那些软件需要的报文。硬件过滤的做法节省了CPU开销,否则就必须由软件过滤从而占用一定的CPU开销。每个过滤器组x由2个32位寄存器,CAN_FxR0和CAN_FxR1组成。

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

    2 几个重要的概念

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

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

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

    应用程序不用的过滤器组,应该保持在禁用状态。

    关于过滤器配置,可参见下图:

                                                                                        图1

    2.4 过滤器匹配序号

    一旦收到的报文被存入FIFO,就可被应用程序访问。通常情况下,报文中的数据被拷贝到SRAM中;为了把数据拷贝到合适的位置,应用程序需要根据报文的标识符来辨别不同的数据。bxCAN提供了过滤器匹配序号,以简化这一辨别过程。根据过滤器优先级规则,过滤器匹配序号和报文一起,被存入邮箱中。因此每个收到的报文,都有与它相关联的过滤器匹配序号。

    过滤器匹配序号可以通过下面两种方式来使用:

    • 把过滤器匹配序号跟一系列所期望的值进行比较
    • 把过滤器匹配序号当作一个索引来访问目标地址

    对于标识符列表模式下的过滤器(非屏蔽方式的过滤器),软件不需要直接跟标识符进行比较。
    对于屏蔽位模式下的过滤器,软件只须对需要的那些屏蔽位(必须匹配的位)进行比较即可。

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

                                                                                         图2

    2.5 过滤器优先级规则

    根据过滤器的不同配置,有可能一个报文标识符能通过多个过滤器的过滤;在这种情况下,存放在接收邮箱中的过滤器匹配序号,根据下列优先级规则来确定:

    • 位宽为32位的过滤器,优先级高于位宽为16位的过滤器
    • 对于位宽相同的过滤器,标识符列表模式的优先级高于屏蔽位模式
    • 位宽和模式都相同的过滤器,优先级由过滤器号决定,过滤器号小的优先级高

    如下图:

                                                                                    图3

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

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

     

    3 与过滤器相关的寄存器

    3.1 CAN 过滤器主控寄存器 (CAN_FMR)

    地址偏移量: 0x200

    复位值: 0x2A1C 0E01

    注: 该寄存器的非保留位完全由软件控制。

                                                                                                                        图4

    位31:1 保留位,强制为复位值
    位0 FINIT : 过滤器初始化模式
    针对所有过滤器组的初始化模式设置。
    0: 过滤器组工作在正常模式;
    1: 过滤器组工作在初始化模式。

    3.2 CAN 过滤器模式寄存器 (CAN_FM1R)

    地址偏移量: 0x204
    复位值: 0x0000 0000
    注: 只有在设置CAN_FMR(FINIT=1),使过滤器处于初始化模式下,才能对该寄存器写入。

                                                                                         图5

    位31:14     保留位,硬件强制为0
    位13:0     FBMx : 过滤器模式
    过滤器组x的工作模式。
    0: 过滤器组x的2个32位寄存器工作在标识符屏蔽位模式;
    1: 过滤器组x的2个32位寄存器工作在标识符列表模式。

     

     

     

     

    3.3 CAN 过滤器位宽寄存器 (CAN_FS1R)

    地址偏移量: 0x20C
    复位值: 0x0000 0000
    注: 只有在设置CAN_FMR(FINIT=1),使过滤器处于初始化模式下,才能对该寄存器写入。

                                                                                                                  图6

    位31:14 保留位,硬件强制为0
    位13:0 FSCx : 过滤器位宽设置
    过滤器组x(13~0)的位宽。
    0:过滤器位宽为2个16位;
    1:过滤器位宽为单个32位。

     

     

     

     

    3.4 CAN 过滤器FIFO关联寄存器 (CAN_FFA1R)

    地址偏移量: 0x214
    复位值: 0x0000 0000
    注: 只有在设置CAN_FMR(FINIT=1),使过滤器处于初始化模式下,才能对该寄存器写入。

                                                                                                             图7

    位31:14 保留位,硬件强制为0。
    位13:0 FFAx : 过滤器位宽设置
    报文在通过了某过滤器的过滤后,将被存放到其关联的FIFO中。
    0:过滤器被关联到FIFO0;
    1:过滤器被关联到FIFO1。

     

     

     

     

    3.5 CAN 过滤器激活寄存器 (CAN_FA1R)

    地址偏移量: 0x21C
    复位值: 0x0000 0000

                                                                                            图8

    位31:14 保留位,硬件强制为0。
    位13:0 FACTx : 过滤器激活
    软件对某位设置1来激活相应的过滤器。只有对FACTx位清0,或对CAN_FMR寄存器的FINIT位设置1后,才能修改相应的过滤器寄存器x(CAN_FxR[0:1])。
    0:过滤器被禁用;
    1:过滤器被激活。

     

     

     

     

    3.6 CAN 过滤器组x寄存器 (CAN_FiRx) (i=0..13,x=1..2)

    地址偏移量:0x240h..0x2AC
    复位值:未定义位

    注: 共有14组过滤器:i=0..13。每组过滤器由2个32位的寄存器,CAN_FiR[2:1]组成。只有在CAN_FaxR寄存器(CAN过滤器激活寄存器)相应的FACTx位清’0’,或CAN_FMR寄存器(CAN过滤器主控寄存器)的FINIT位为’1’时,才能修改相应的过滤器寄存器。

                                                                                     图9

    位31:0 FB[31:0] : 过滤器位
    当为标识符模式时:
                 寄存器的每位对应于所期望的标识符的相应位的电平。
                 0: 期望相应位为显性位;
                 1: 期望相应位为隐性位。
    当为屏蔽位模式时:
                 寄存器的每位指示是否对应的标识符寄存器位一定要与期望的标识符的相应位一致。
                 0: 不关心,该位不用于比较;
                 1: 必须匹配,到来的标识符位必须与滤波器对应的标识符寄存器位相一致。

     

     

     

     

     

    注: 根据过滤器位宽和模式的不同设置,过滤器组中的两个寄存器的功能也不尽相同。关于过滤器的映射,功能描述和屏蔽寄存器的关联,请参见2节标识符过滤。

    屏蔽位模式下的屏蔽/标识符寄存器,跟标识符列表模式下的寄存器位定义相同。

    4 代码实例

    4.1 CAN ID值的结构分析

    在讲到代码实例之前,首先大家都弄懂一件事,当给定一个CAN ID,如0x1800f001,当然这个是扩展ID,这里要问的是,这个CAN ID的值本身包含两部分,即基本ID与扩展ID,即么你知道这个扩展ID0x1800f001的哪些位是基本ID,哪些位又是扩展ID?(在基本CANID格式下不存在这个问题)

    在回答这个问题之前我们来看看ISO11898的定义,如下图:

                                                                                         图10

    如上图,基本格式不存在扩展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。那么知道这些有什么用呢?接下来的代码示例中你就会有什么用了。
     

    4.2 位宽为32位的屏蔽模式

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

    如下图所示:

                                                                                     图11

    如上图,上面的ID为标识符寄存器,中间部分的MASK为屏蔽码寄存器。每个寄存器都是32位的。最下边显示的是与CAN ID各位定位的映射关系。由4.1的知识很快可以发现,上图最下边的映射关系恰好等于扩展CAN值左移3位再补上IDE(扩展帧标识),RTR(远程帧标志)。

    因此,我们初步得出这样的推论:对于一个扩展CAN ID,不能单纯地将它看到的一个数,而应该将它看成两部分,基本ID和扩展ID(当然标准CAN ID只包含基本ID部分),过滤器屏蔽码寄存器和标识符寄存器也应该看成多个部分,然后问题就变成了如何将CAN ID所表示的各部分如何针对过滤器寄存器各部分对号入座的问题了。

    对号入座的方法多种多样,但万变不离其心,主要是掌握其核心思想即可:

    1:在各种过滤器模式下,CAN ID与寄存器相应位置一定要匹配;

    2:在屏蔽方式下,屏蔽码寄存器某位为1表示接收到的CAN ID对应的位必须对验证码寄存器对应的位相同。

    下面给出一个代码例子,假设我们要接收多个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;  //设置标识符寄存器高字节。
    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); //设置过滤器

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

    4.3 位宽为32位的标识符列表模式

    在此种模式下,过滤器组包含的两个寄存器含义一样,此模式下只多存在两个标识符列表过滤器如下图:

                                                                                                       图12

    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); //设置过滤器
    
     

    4.4 位宽为16位的屏蔽码模式

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

                                                                              图13

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

     

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

    在此模式下,由于标识符寄存器的高16位和低16位,屏蔽寄存器的高16位和低16位都用来做标识符寄存器,因此,最多可存在4个标识符过滤器。同样,只能实现对标准帧的过滤。具体代码就不介绍了,参见上图的映射即可。

     

     

     

     

    展开全文
  • CAN总线之CAN ID过滤器分析

    万次阅读 2018-02-28 12:20:48
    (参考博客:http://blog.csdn.net/flydream0/article/details/8148791)1.CAN报文标识符:并不代表节点的地址 而是和报文的内容有关 发送者以广播的形式发送消息 节点在...过滤器组 Filterx x为过滤器号 STM32...

    (参考博客:http://blog.csdn.net/flydream0/article/details/8148791)

    1.CAN报文标识符:并不代表节点的地址 而是和报文的内容有关 发送者以广播的形式发送消息 节点在接收报文时根据标识符(CAN ID)决定是否需要该报文

     如果需要则拷贝到SRAM中 不需要则丢弃
    实现硬件过滤 节省CPU开销

    2.过滤器组 Filterx  x为过滤器号
     STM32总共提供14个过滤器组来处理CAN接收过滤问题,每个过滤器组包含两个32位寄存器CAN_FxR0和CAN_FxR1组成

    3.过滤器的过滤模式 通过CAN_FilterMode设置
    a.屏蔽位模式IdMask:为了过滤出一组标识符,应该设置过滤器组工作在屏蔽位模式。 在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。如需要出生年月需要找出1995的则出生年月中年份的四位必须匹配 月和日则不用关心
    b.标识符列表模式IdList:为了过滤出一个标识符,应该设置过滤器组工作在标识符列表模式。在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。

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

    ●2个16位过滤器CAN_FilterScale_16bit,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位


    5.过滤器组的过滤模式和位宽设置 通过相应的CAN_FMR寄存器(CAN过滤器主控寄存器)配置


    FSCx=1时为1个32位寄存器位宽为32
    FSCx=0时为2个16位寄存器位宽为32

    6.过滤器匹配序号
    对于标识符列表模式下的过滤器(非屏蔽方式的过滤器),软件不需要直接跟标识符进行比较。
    对于屏蔽位模式下的过滤器,软件只须对需要的那些屏蔽位(必须匹配的位)进行比较即可。

    7.CAN 过滤器组x寄存器 (CAN_FiRx) (i=0..13,x=1..2)
    FB[31:0] : 过滤器位
    当为标识符模式时:
                 寄存器的每位对应于所期望的标识符的相应位的电平。
                 0: 期望相应位为显性位;
                 1: 期望相应位为隐性位。
    当为屏蔽位模式时:
                 寄存器的每位指示是否对应的标识符寄存器位一定要与期望的标识符的相应位一致。
                 0: 不关心,该位不用于比较;
                 1: 必须匹配,到来的标识符位必须与滤波器对应的标识符寄存器位相一致。


    CAN ID值的结构分析


    如上图,基本格式不存在扩展ID,而扩展格式中ID0~ID17为Extension ID(扩展ID),而ID18~ID28为Base ID(基本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。


    8.位宽为32位的屏蔽模式


    STID为标准ID EXID为扩展ID 如果STID为0x06 扩展ID为0x00则 CAN ID值0x00060000用二进制表示为0b000[0 0000 0001 10](标准ID) [00 0000 0000 0000 0000](扩展ID)
    在屏蔽方式下,屏蔽码寄存器某位为1表示接收到的CAN ID对应的位必须对验证码寄存器对应的位相同
    则CAN_FilterIdHigh(标识符寄存器高字节)为上图中的ID16到31位应该为0b0000 0000 0001 1000
    0x0018 
    屏蔽寄存器的设置:
    在只接收标准ID时 将STDID对应寄存器的值填入ID32位寄存器即CAN_FilterIdHigh高16位 其中EXID及其他各位都设置为0因为在这个时候IDE(帧格式 标准帧(CAN_ID_STD 0)或者是扩展帧( CAN_ID_EXT0x1))和RTR(数据帧0或者为遥控帧1) 再将对应的需要保准ID32位中需要一样的为1放入mask寄存器中 
    接收其他类型的ID时类似
     位宽为32位的标识符列表模式
    在列表模式下如果位宽为32为则可以设置两个ID列表

    16位模式可以设置4个ID列表


    16位宽的列表模式
    在16位宽的列表模式下,FilterIdHigh,FilterIdLow,FilterMaskIdHigh,FilterMaskIdLow这4个16位变量都是用来存储一个标准CAN ID,这样,就可以存放4个标准CAN ID了,需要注意地是,此种模式下,是不能处理扩展CANID,凡是需要过滤扩展CAN ID的,都是需要用到32位宽的模式。

    在32位宽的列表模式下,CAN_FxR1与CAN_FxR2都用来存储希望通过的CAN ID,由于是32位宽的,因此既可以存储标准CAN ID,也可以存储扩展CAN ID。注意看上图最底下的各位定义,可以看出,从右到左,首先,最低位是没有用的,然后是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位宽模式下,FilterIdHigh与FilterIdLow合在一起表示CAN_FxR1寄存器,用来存放验证码,而FilterMaskIdHigh与FilterMaskIdLow合在一起表示CAN_FxR2寄存器,用来存放屏蔽码,关于验证码与屏蔽码的概念在之前的2.3节已经明确说明了,不清楚的可以回过去看看2.3节的内容。在32位宽的掩码模式下,既可以过滤标准CAN ID,也可以过滤扩展CAN ID,甚至两者混合这来也是可以的
    对所有的ID先进行异或操作 再将异或后的值中为1的写入mask中

    展开全文
  • STM32的CAN过滤器详解

    千次阅读 2019-03-14 13:03:27
    STM32普通芯片的CAN有14组过滤器组,互联型有28组过滤器组,每组过滤器有2个可配置的32位寄存器:CAN_FxR0和CAN_FxR1。用以对收到的帧进行过滤,以此判断是否接收该帧。 每组过滤器组有两种工作模式:标识符列表...

    STM32普通芯片的CAN有14组过滤器组,互联型有28组过滤器组,每组过滤器有2个可配置的32位寄存器:CAN_FxR0和CAN_FxR1。用以对收到的帧进行过滤,以此判断是否接收该帧。

     

    每组过滤器组有两种工作模式:标识符列表模式,标识符屏蔽位模式。

            标识符列表模式:收到的标识符必须与过滤器的值完全相等,才能通过。

            标识符屏蔽位模式:只要标识符的某些位符合,就算通过。

     

    每组过滤器的过滤宽度是可变的,有两种类型:32位,16位。

     

    可以通过配置工作模式和宽度,将一个过滤器组配置为如下4种形式:

            32位的屏蔽位模式:1个过滤器。

                    CAN_FxR1用于指定需要关心哪些位

                    CAN_FxR0用于指定这些位的值

            32位的列表模式:2个过滤器

                    CAN_FxR0用来指定过滤器0的标准值

                    CAN_FxR0用于指定过滤器1的标准值

            16位的 屏蔽位模式:2个过滤器

                    CAN_FxR0用来配置过滤器0,[31-16]位指定需要关心的位,[15-0]指定这些位的值

                    CAN_FxR1用来配置过滤器1,[31-16]位指定需要关心的位,[15-0]指定这些位的值

            16位的列表模式:4个过滤器

                    CAN_FxR0的[15-0]位用来配置过滤器0,[31-16]位用来配置过滤器1

                    CAN_FxR1的[15-0]位用来配置过滤器2,[31-16]位用来配置过滤器3

     

    STM32有两个接收FIFO,分别是FIFO0,FIFO1。每组过滤器组必须关联且只能关联一个FIFO,复位都关联到FIFO0。所谓关联就是一旦报文通过过滤器,就会存到事先关联的FIFO中。

     

    下面具体看一下,CAN_FxR0和CAN_FxR1各中模式下各位的定义:

            32位的屏蔽位模式:

                   

            32位的列表模式:

                   

            16位的屏蔽位模式:

                   

            16位的列表模式:

                   

    

    展开全文
  • STM32 CAN过滤器详解

    千次阅读 2020-05-11 15:23:58
    1. 前言 bxCAN是STM32系列最稳定的IP核之一,无论有哪个新型号出来,这个IP核基本未变,可见这个IP核的设计是相当成熟的。本文所讲述的内容属于这个IP核的一部分,掌握了本文所讲内容,就可以很方便...

    1. 前言

    bxCAN是STM32系列最稳定的IP核之一,无论有哪个新型号出来,这个IP核基本未变,可见这个IP核的设计是相当成熟的。本文所讲述的内容属于这个IP核的一部分,掌握了本文所讲内容,就可以很方便地适用于所有STM32系列中包含bxCAN外设的型号。有关bxCAN的过滤器部分的内容在参考手册中往往看得“不甚明白“,本文就过滤器的4种工作模式进行详细讲解并使用具体的代码进行演示,这些代码都进行过实测验证通过的,希望能给读者对于bxCAN过滤器有一个清晰的理解。

     

    2. 准备工作

    2.1.   为什么要过滤器?

    在这里,我们可以将CAN总线看成一个广播消息通道,上面传输着各种类型的消息,好比报纸,有体育新闻,财经新闻,政治新闻,还有军事新闻,每个人都有自己的喜好,不一定对所有新闻都感兴趣,因此,在看报纸的时候,一般人都是只看自己感兴趣的那类新闻,而过滤掉其他不感兴趣的内容。那么我们一般是怎么过滤掉那些不感兴趣的内容的呢?下面有两种方法来实现这个目的:

    第一种方法

             每次看报纸时,你都看下每篇文章的标题,如果感兴趣则继续看下去,如果不感兴趣,则忽略掉。

    第二种方法

             你告诉邮递员,你只对财经新闻感兴趣,请只将财经类报纸送过来,其他的就不要送过来了,就这样,你看到的内容必定是你感兴趣的财经类新闻。

    上面那种方法好呢?很明显,第二种方法是最好的,因为你不用自己每次判断哪些新闻内容是你感兴趣的,可以免受“垃圾”新闻干扰,从而可以节省时间忙其他事。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的资源是非常非常有限的,并不能任我们随心所欲。因此,我们需要考虑另外一种替代方案,这种方案应该不受到数量限制。

     

    下面假设我们是古时候一座城镇的守卫,城主要求只有1156年出生的人才可以进城,我们又该如何执行呢?假设古时候的人也有类似今天的身份证(...->_<-…),大家都知道,身份份证号码中有4位是表示出生年月,如下图:


    图 1 18位身份证号码的各位定义

    如上图,身份证中第7~10这4位数表示的是出生年份,那么,我们可以这么执行:

    检查想要进城的所有人的身份证号码的第7~10位数字,如果这个数字依次为1156则可以进入,否则则不可以,至于身份证号码的其他位则完全不关心。假如过几天城主放宽进城条件为只要是1150年~1160前的人都可以进城,那么,我们就可以只关注身份证号码的第7~9这3位数是否为115就可以了,对不对?这样一来,我们就可以非常完美地执行城主的要求了。

     

    再变下,假设现在使用机器来当守卫,不再是人来执行这个“筛选”工作。机器是死的,没有人那么灵活,那么机器又该如何执行呢?

    对于机器来说,每一步都得细化到机器可以理解的程度,于是我们可以作如下细化:

    第一步:获取想进城的人的身份证号码

    第二步:只看获取到身份证的第7~9位,其他位忽略

    第三步:将忽略后的结果与1156进行比较

    第四步:比较结果相同则通过,不同则不能通过

    这种方式,我们称之为掩码模式

    2.3.   验证码与屏蔽码

    仔细查看上面4个步骤,这不就是C代码中的if语句吗?如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. if( x & y ==z) //x表示待检查身份证号码,y表示只关注第7~9位的屏蔽码,Z则为1156,这里叫做验证码  
    2. {  
    3.     //可以通过  
    4. }  
    5. else  
    6. {  
    7.     //不可以通过  
    8. }  

    对于机器来说,我们要为它准备好两张纸片,一片写上屏蔽码,另一片纸片写上验证码,屏蔽码上相应位为1时,表示此位需要与验证码对应位进行比较,反之,则表示不需要。机器在执行任务的时候先将获取的身份证号码与屏蔽码进行“与”操作,再将结果与验证码的进行比较,根据判断是否相同来决定是否通过。整个判别流程如下所示:

    图 2 掩码模式的计算过程

    从上图可以很容易地理解屏蔽码与验证码的含义,这样一来,能通过的结果数量就完全取决于屏蔽码,设得宽,则可以通过的多(所有位为0,则不过任何过滤操作,则谁都可以通过),设得窄,则通过的少(所有位设为1,则只有一个能通过)。那么知道这个有什么用呢?因为bxCAN的过滤器的掩码模式就是采用这种方式,在bxCAN中,分别采用了两个寄存器(CAN_FiR1,CAN_FiR2)来存储屏蔽码与验证码,从而实现掩码模式的工作流程的。这样,我们就知道了bxCAN过滤器的掩码模式的大概工作原理。

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


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

    综合之前所述,下面我们来对比一下列表模式与掩码模式这两种模式的优缺点。

    模式 优点 缺点
    列表模式 能精确地过滤每个指定的CAN ID 有数量限制
    掩码模式 取决于屏蔽码,有时无法完全精确到每一个CAN ID,部分不期望的CAN ID有时也会收到 数量取决于屏蔽码,最多无上限

    2.5.   标准CAN ID与扩展CAN ID

    1986 年德国电气商BOSCH公司开发出面向汽车的CAN 通信协议,刚开始的时候,CAN ID定义为11位,我们称之为标准格式,ISO11898-1标准中CAN的基本格式如下图所示:

    图 3 标准CAN报文格式定义


    如上图所示,标准CAN ID存放在上图ID18~ID28中,共11位。随着工业发展,后来发现11位的CAN ID已经不够用,于是就增加了18位,扩展CAN ID到29位,如下图所示:

    图 4 扩展CAN报文格式定义

    从上图对比扩展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,二进制展开为0b 0[111 1110 0001] ,只有中括号内的11位才有效,其全部是基本ID。

    再例如扩展CAN ID 0x1835f107,二进制展开为0b 000[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. bxCAN的过滤器的解决方案

    终于进入到正题了!前面已经介绍了过滤器的列表模式与掩码模式,以及掩码模式下的屏蔽码与验证码的含义,还介绍了标准CAN ID与扩展CAN ID的组成部分。现在我们终于要站在bxCAN的角度来分析其过滤方案。

    首先过滤模式分列表模式和掩码模式,因此,对于没有过滤器,我们需要这么一个位来标记,用户可以通过设置这个位来标记他到底是想要这个过滤器工作在列表模式下还是掩码模式,于是,这个表示过滤模式的位就定义在CAN_FM1R寄存器中的FBMx位上,如下图:

    图5 CAN过滤器模式寄存器CAN_FM1R定义


    这里以STM32F407为例,bxCAN共有28个过滤器,于是上图的每一个位对应地表示这28个过滤器的工作模式,供用户设置。”0”表示掩码模式,”1”表示列表模式。

     

    另外,我们知道了标准CAN ID位11位,而扩展CAN ID有29位,对于标准的CAN ID来说,我们有一个16位的寄存器来处理他足够了,相应地,扩展CAN ID,我们就必须使用32位的寄存器来处理它,而在实际应用中,根据需求,我们可能自始至终都只需要处理11位的CAN ID。对于资源严重紧张的MCU环境来说,本着不浪费的原则,这里最好能有另外一个标志用告诉过滤器是否需要处理32位的CAN ID。于是,bxCAN处于这种考虑,也设置了这么一个寄存器CAN_FS1R来表示CAN ID的位宽,如下图所示:

    图6 CAN过滤器位宽寄存器CAN_FS1R定义


    如上图,每一个位对应着bxCAN中28个过滤器的位宽,这个需要用户来设置。

    于是根据模式与位宽的设置,我们共可以得出4中不同的组合:32位宽的列表模式,16位宽的列表模式,32位宽掩码模式,16位宽的掩码模式。如下图所示:

    图 7 CAN过滤器的4中工作模式


    在bxCAN中,每个过滤器都存在这么两个寄存器CAN_FxR1和CAN_FxR2,这两个寄存器都是32位的,他的定义并不是固定的,针对不同的工作模式组合他的定义是不一样的,如列表模式-32位宽模式下,这两个寄存器的各位定义都是一样的,都用来存储某个具体的期望通过的CAN ID,这样就可以存入2个期望通过的CAN ID(标准CAN ID和扩展CAN ID均可);若在掩码模式-32位宽模式下时,则CAN_FxR1用做32位宽的验证码,而CAN_FxR2则用作32位宽的屏蔽码。在16位宽时,CAN_FxR1和CAN_FxR2都要各自拆分成两个16位宽的寄存器来使用,在列表模式-16位宽模式下,CAN_FxR1和CAN_FxR2定义一样,且各自拆成两个,则总共可以写入4个标准CAN ID,若在16位宽的掩码模式下,则可以当做2对验证码+屏蔽码组合来用,但它只能对标准CAN ID进行过滤。这个就是bxCAN过滤器的解决方案,它采用了这4种工作模式。

     

    本着从易到难得目的,下面我们将依次介绍如何使用bxCAN的这4种工作模式并给出对应的代码示例.

    4. 应用实例

    4.1.   工程建立及主体代码

    本文硬件采用STM3240G-EVAL评估板和ZLG的USBCAN-2E-U及其配套的软件工具CANTest来实现对MCU进行CAN报文的发送。工程使用STM32CubeMx自动生成:

     

    引脚如下:

    PD0: CAN1_Rx

    PD1: CAN1_Tx

    PG6: LED1

    PG8: LED2

    PI9:  LED3

    PC7: LED4

    图 8 引脚定义


    时钟树如下设置:



    图 9时钟树设置

    在配置中的NVIC中,打开CAN1 RX0接收中断,如下图所示:



    图 10 打开CAN1的RX0接收中断


    其他的没有什么特殊设置,生成工程后的main函数如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. int main(void)  
    2. {  
    3.   
    4.   /* USER CODE BEGIN 1 */  
    5.   static CanTxMsgTypeDef        TxMessage;  
    6.   static CanRxMsgTypeDef        RxMessage;  
    7.   /* USER CODE END 1 */  
    8.   
    9.   /* MCU Configuration----------------------------------------------------------*/  
    10.   
    11.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */  
    12.   HAL_Init();  
    13.   
    14.   /* Configure the system clock */  
    15.   SystemClock_Config();  
    16.   
    17.   /* Initialize all configured peripherals */  
    18.   MX_GPIO_Init();  
    19.   MX_CAN1_Init();  
    20.   
    21.   /* USER CODE BEGIN 2 */  
    22.   hcan1.pTxMsg =&TxMessage;  
    23.   hcan1.pRxMsg =&RxMessage;  
    24.   CANFilterConfig_Scale32_IdList();         //列表模式-32位宽  
    25. //CANFilterConfig_Scale16_IdList();         //列表模式-16位宽  
    26. //CANFilterConfig_Scale32_IdMask_StandardIdOnly();  //掩码模式-32位宽(只有标准CAN ID)  
    27. //CANFilterConfig_Scale32_IdMask_ExtendIdOnly();    //掩码模式-32位宽(只用扩展CAN ID)  
    28. //CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(); //掩码模式-32位宽(标准CANID与扩展CAN ID混合)  
    29. //CANFilterConfig_Scale16_IdMask();         //掩码模式-16位宽  
    30.   HAL_CAN_Receive_IT(&hcan1,CAN_FIFO0);  
    31.   /* USER CODE END 2 */  
    32.   
    33.   /* Infinite loop */  
    34.   /* USER CODE BEGIN WHILE */  
    35.   while (1)  
    36.   {  
    37.   /* USER CODE END WHILE */  
    38.   
    39.   /* USER CODE BEGIN 3 */  
    40.   }  
    41.   /* USER CODE END 3 */  
    42.   
    43. }  

    如上代码所示,示例中将采用各种过滤器配置来演示,在测试时我们可以只保留一种配置,也可以全部打开,为了确保每种配置的准确性,这里建议只保留其中一种配置进行测试。

    另外,接收中断回调函数如下所示:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)  
    2. {  
    3.   if(hcan->pRxMsg->StdId ==0x321)  
    4.   {  
    5.     //handle the CAN message  
    6.     HandleCANMessage(hcan->pRxMsg);      //处理接收到的CAN报文  
    7.   }  
    8.   if(hcan->pRxMsg->ExtId ==0x1800f001)  
    9.   {  
    10.      HandleCANMessage(hcan->pRxMsg);     //处理接收到的CAN报文  
    11.   }  
    12.   HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);    //若收到消息则闪烁下LED4  
    13.   HAL_Delay(200);  
    14.   HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_RESET);    
    15.   HAL_CAN_Receive_IT(&hcan1,CAN_FIFO0);  
    16. }  

    接下来将分别介绍过滤器的4中工作模式以及所对应的代码示例。

    4.2.   32位宽的列表模式


    11 32位宽下的CAN_FxR1与CAN_FxR2各位定义


    如上图所示,在32位宽的列表模式下,CAN_FxR1与CAN_FxR2都用来存储希望通过的CAN ID,由于是32位宽的,因此既可以存储标准CAN ID,也可以存储扩展CAN ID。注意看上图最底下的各位定义,可以看出,从右到左,首先,最低位是没有用的,然后是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”。示例代码如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. static void CANFilterConfig_Scale32_IdList(void)  
    2. {  
    3.   CAN_FilterConfTypeDef  sFilterConfig;  
    4.   uint32_t StdId =0x321;                //这里写入两个CAN ID,一个位标准CAN ID  
    5.   uint32_t ExtId =0x1800f001;           //一个位扩展CAN ID  
    6.     
    7.   sFilterConfig.FilterNumber = 0;               //使用过滤器0  
    8.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;     //设为列表模式  
    9.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;    //配置为32位宽  
    10.   sFilterConfig.FilterIdHigh = StdId<<5;          //基本ID放入到STID中  
    11.   sFilterConfig.FilterIdLow = 0|CAN_ID_STD;         //设置IDE位为0  
    12.   sFilterConfig.FilterMaskIdHigh = ((ExtId<<3)>>16)&0xffff;  
    13.   sFilterConfig.FilterMaskIdLow = (ExtId<<3)&0xffff|CAN_ID_EXT;   //设置IDE位为1  
    14.   sFilterConfig.FilterFIFOAssignment = 0;           //接收到的报文放入到FIFO0中  
    15.   sFilterConfig.FilterActivation = ENABLE;  
    16.   sFilterConfig.BankNumber = 14;  
    17.     
    18.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)  
    19.   {  
    20.     Error_Handler();  
    21.   }  
    22. }  

    这里需要说明一下,由于我们使用的是cube库,在cube库中,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位中,于是有了:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. sFilterConfig.FilterIdHigh = StdId<<5;  

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

    4.3.   16位宽的列表模式

    图12 16位宽的列表模式


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

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. static void CANFilterConfig_Scale16_IdList(void)  
    2. {  
    3.   CAN_FilterConfTypeDef  sFilterConfig;  
    4.   uint32_t StdId1 =0x123;                       //这里采用4个标准CAN ID作为例子  
    5.   uint32_t StdId2 =0x124;  
    6.   uint32_t StdId3 =0x125;  
    7.   uint32_t StdId4 =0x126;  
    8.     
    9.   sFilterConfig.FilterNumber = 1;               //使用过滤器1  
    10.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST;     //设为列表模式  
    11.   sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;    //位宽设置为16位  
    12.   sFilterConfig.FilterIdHigh = StdId1<<5;  //4个标准CAN ID分别放入到4个存储中  
    13.   sFilterConfig.FilterIdLow = StdId2<<5;  
    14.   sFilterConfig.FilterMaskIdHigh = StdId3<<5;  
    15.   sFilterConfig.FilterMaskIdLow = StdId4<<5;  
    16.   sFilterConfig.FilterFIFOAssignment = 0;           //接收到的报文放入到FIFO0中  
    17.   sFilterConfig.FilterActivation = ENABLE;  
    18.   sFilterConfig.BankNumber = 14;  
    19.     
    20.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)  
    21.   {  
    22.     Error_Handler();  
    23.   }  
    24. }  

    可见,列表模式还是非常好理解的。

    4.4.   32位宽掩码模式


    图13 32位宽掩码模式


    如上图所示,32位宽模式下,FilterIdHigh与FilterIdLow合在一起表示CAN_FxR1寄存器,用来存放验证码,而FilterMaskIdHigh与FilterMaskIdLow合在一起表示CAN_FxR2寄存器,用来存放屏蔽码,关于验证码与屏蔽码的概念在之前的2.3节已经明确说明了,不清楚的可以回过去看看2.3节的内容。在32位宽的掩码模式下,既可以过滤标准CAN ID,也可以过滤扩展CAN ID,甚至两者混合这来也是可以的,下面我们就这3中情况分别给出示例。

    4.4.1. 只针对标准CAN ID

    如下代码示例:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. static void CANFilterConfig_Scale32_IdMask_StandardIdOnly(void)  
    2. {  
    3.   CAN_FilterConfTypeDef  sFilterConfig;  
    4.   uint16_t StdIdArray[10] ={0x7e0,0x7e1,0x7e2,0x7e3,0x7e4,  
    5.                                 0x7e5,0x7e6,0x7e7,0x7e8,0x7e9}; //定义一组标准CAN ID  
    6.   uint16_t      mask,num,tmp,i;  
    7.     
    8.   sFilterConfig.FilterNumber = 2;               //使用过滤器2  
    9.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;     //配置为掩码模式  
    10.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;    //设置为32位宽  
    11.   sFilterConfig.FilterIdHigh =(StdIdArray[0]<<5);     //验证码可以设置为StdIdArray[]数组中任意一个,这里使用StdIdArray[0]作为验证码  
    12.   sFilterConfig.FilterIdLow =0;  
    13.     
    14.   mask =0x7ff;                      //下面开始计算屏蔽码  
    15.   num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);  
    16.   for(i =0; i<num; i++)      //屏蔽码位StdIdArray[]数组中所有成员的同或结果  
    17.   {  
    18.     tmp =StdIdArray[i] ^ (~StdIdArray[0]);  //所有数组成员与第0个成员进行同或操作  
    19.     mask &=tmp;  
    20.   }  
    21.   sFilterConfig.FilterMaskIdHigh =(mask<<5);  
    22.   sFilterConfig.FilterMaskIdLow =0|0x02;        //只接收数据帧  
    23.     
    24.   sFilterConfig.FilterFIFOAssignment = 0;       //设置通过的数据帧进入到FIFO0中  
    25.   sFilterConfig.FilterActivation = ENABLE;  
    26.   sFilterConfig.BankNumber = 14;  
    27.     
    28.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)  
    29.   {  
    30.     Error_Handler();  
    31.   }  
    32. }  

    如上代码所示,对于验证码,任意一个期望通过的CAN ID都是可以设为验证码的,但屏蔽码,却是所有期望通过的CAN ID相互同或后的最终结果,这个即是屏蔽码。

    4.4.2. 只针对扩展CAN ID

    如下代码示例:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. static void CANFilterConfig_Scale32_IdMask_ExtendIdOnly(void)  
    2. {  
    3.   CAN_FilterConfTypeDef  sFilterConfig;  
    4.   //定义一组扩展CAN ID用来测试  
    5. uint32_t ExtIdArray[10] ={0x1839f101,0x1835f102,0x1835f113,0x1835f124,0x1835f105,  
    6.                             0x1835f106,0x1835f107,0x1835f108,0x1835f109,0x1835f10A};  
    7.   uint32_t      mask,num,tmp,i;  
    8.     
    9.   sFilterConfig.FilterNumber = 3;                   //使用过滤器3  
    10.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;         //配置为掩码模式  
    11.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;        //设为32位宽  
    12.   sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;//数组任意一个成员都可以作为验证码  
    13.   sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff) | CAN_ID_EXT;  
    14.     
    15.   mask =0x1fffffff;  
    16.   num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);  
    17.   for(i =0; i<num; i++)              //屏蔽码位数组各成员相互同或的结果  
    18.   {  
    19.     tmp =ExtIdArray[i] ^ (~ExtIdArray[0]);  //都与第一个数据成员进行同或操作  
    20.     mask &=tmp;  
    21.   }  
    22.   mask <<=3;                                  //对齐寄存器  
    23.   sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;  
    24.   sFilterConfig.FilterMaskIdLow = (mask&0xffff)|0x02;       //只接收数据帧  
    25.   sFilterConfig.FilterFIFOAssignment = 0;  
    26.   sFilterConfig.FilterActivation = ENABLE;  
    27.   sFilterConfig.BankNumber = 14;  
    28.     
    29.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)  
    30.   {  
    31.     Error_Handler();  
    32.   }  
    33. }  

    如上代码所示,与之前的标准CAN ID相比,扩展CAN ID的验证码与屏蔽码放入到相对应的寄存器时所移动的位数与标准CAN ID时有所差别,其他的都一样。

    接下来是标准CAN ID与扩展CAN ID混合着来。

    4.4.3. 标准CAN ID与扩展CAN ID混合过滤

    如下代码所示:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. static void CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(void)  
    2. {  
    3.   CAN_FilterConfTypeDef  sFilterConfig;  
    4.   //定义一组标准CAN ID  
    5. uint32_t StdIdArray[10] ={0x711,0x712,0x713,0x714,0x715,  
    6.                           0x716,0x717,0x718,0x719,0x71a};  
    7.   //定义另外一组扩展CAN ID  
    8. uint32_t ExtIdArray[10] ={0x1900fAB1,0x1900fAB2,0x1900fAB3,0x1900fAB4,0x1900fAB5,  
    9.                             0x1900fAB6,0x1900fAB7,0x1900fAB8,0x1900fAB9,0x1900fABA};  
    10.   uint32_t      mask,num,tmp,i,standard_mask,extend_mask,mix_mask;  
    11.     
    12.   sFilterConfig.FilterNumber = 4;               //使用过滤器4  
    13.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;     //配置为掩码模式  
    14.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;    //设为32位宽  
    15.   sFilterConfig.FilterIdHigh =((ExtIdArray[0]<<3) >>16) &0xffff;    //使用第一个扩展CAN  ID作为验证码  
    16.   sFilterConfig.FilterIdLow =((ExtIdArray[0]<<3)&0xffff);  
    17.     
    18.   standard_mask =0x7ff;     //下面是计算屏蔽码  
    19.   num =sizeof(StdIdArray)/sizeof(StdIdArray[0]);  
    20.   for(i =0; i<num; i++)          //首先计算出所有标准CAN ID的屏蔽码  
    21.   {  
    22.     tmp =StdIdArray[i] ^ (~StdIdArray[0]);  
    23.     standard_mask &=tmp;  
    24.   }  
    25.     
    26.   extend_mask =0x1fffffff;  
    27.   num =sizeof(ExtIdArray)/sizeof(ExtIdArray[0]);  
    28.   for(i =0; i<num; i++)          //接着计算出所有扩展CAN ID的屏蔽码  
    29.   {  
    30.     tmp =ExtIdArray[i] ^ (~ExtIdArray[0]);  
    31.     extend_mask &=tmp;  
    32.   }  
    33.   mix_mask =(StdIdArray[0]<<18)^ (~ExtIdArray[0]);    //再计算标准CAN ID与扩展CAN ID混合的屏蔽码  
    34.   mask =(standard_mask<<18)& extend_mask &mix_mask;   //最后计算最终的屏蔽码  
    35.   mask <<=3;                          //对齐寄存器  
    36.   
    37.   sFilterConfig.FilterMaskIdHigh = (mask>>16)&0xffff;  
    38.   sFilterConfig.FilterMaskIdLow = (mask&0xffff);  
    39.   sFilterConfig.FilterFIFOAssignment = 0;  
    40.   sFilterConfig.FilterActivation = ENABLE;  
    41.   sFilterConfig.BankNumber = 14;  
    42.     
    43.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)  
    44.   {  
    45.     Error_Handler();  
    46.   }  
    47. }  

    如上代码所示,在混合的情况下,只需稍微修改下屏蔽码的计算方式就可以了,其他的基本没有什么变化。

    4.5.   16位宽掩码模式

    如下图所示:


    图14 16位宽的掩码模式


    如上图所示,在16位宽的掩码模式下,CAN_FxR1的低16位是作为验证码,对应的16位屏蔽码为CAN_FxR1的高16位,同样的,CAN_FxR2的低16位是作为验证码,对应与CAN_FxR2的高16位为屏蔽码。于是,其示例代码如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. static void CANFilterConfig_Scale16_IdMask(void)  
    2. {  
    3.   CAN_FilterConfTypeDef  sFilterConfig;  
    4.   uint16_t StdIdArray1[10] ={0x7D1,0x7D2,0x7D3,0x7D4,0x7D5, //定义第一组标准CAN ID  
    5.                           0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};  
    6.   uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755, //定义第二组标准CAN ID  
    7.                           0x756,0x757,0x758,0x759,0x75A};  
    8.   uint16_t      mask,tmp,i,num;  
    9.     
    10.   sFilterConfig.FilterNumber = 5;                   //使用过滤器5  
    11.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;         //配置为掩码模式  
    12.   sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;        //设为16位宽  
    13.     
    14.   //配置第一个过滤对  
    15.   sFilterConfig.FilterIdLow =StdIdArray1[0]<<5;           //设置第一个验证码  
    16.   mask =0x7ff;  
    17.   num =sizeof(StdIdArray1)/sizeof(StdIdArray1[0]);  
    18.   for(i =0; i<num; i++)                          //计算第一个屏蔽码  
    19.   {  
    20.     tmp =StdIdArray1[i] ^ (~StdIdArray1[0]);  
    21.     mask &=tmp;  
    22.   }  
    23.   sFilterConfig.FilterMaskIdLow =(mask<<5)|0x10;    //只接收数据帧  
    24.     
    25.   //配置第二个过滤对  
    26.   sFilterConfig.FilterIdHigh = StdIdArray2[0]<<5; //设置第二个验证码  
    27.   mask =0x7ff;  
    28.   num =sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);  
    29.   for(i =0; i<num; i++)                  //计算第二个屏蔽码  
    30.   {  
    31.     tmp =StdIdArray2[i] ^ (~StdIdArray2[0]);  
    32.     mask &=tmp;  
    33.   }  
    34.   sFilterConfig.FilterMaskIdHigh = (mask<<5)|0x10;  //只接收数据帧  
    35.     
    36.    
    37.   sFilterConfig.FilterFIFOAssignment = 0;       //通过的CAN 消息放入到FIFO0中  
    38.   sFilterConfig.FilterActivation = ENABLE;  
    39.   sFilterConfig.BankNumber = 14;  
    40.     
    41. if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)  
    42.   {  
    43.     Error_Handler();  
    44.   }  
    45. }  

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

    4.6.   测试验证

    上述代码运行的STM3240G-EVAL评估板上,使用ZLG的USBCAN-2E-U盒子配合PC上的软件CANTest进行验证,整个系统连接后的效果如下图所示:

    图 15 测试环境


    测试时,逐个测试各个配置,并使用PC端软件CANTest发送各个测试的CAN ID均能通过,而使用其他的CAN ID则不能通过,测试结果正常.

    5. 总结

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


    示例代码下载地址:http://download.csdn.net/detail/flydream0/9613116

    几年前我有写过一篇关于CAN ID过滤器的另外一篇文章,是基于标准库的,感兴趣的朋友可以参考下:http://blog.csdn.net/flydream0/article/details/8148791
    展开全文
  • STM32之CAN ---CAN ID过滤器分析

    万次阅读 多人点赞 2017-01-03 10:00:31
    1 前言 在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的内容相关的。因此,发送者以广播的形式把报文发送给所有的... 为满足这一需求,bxCAN为应用程序提供了14个位宽可变的、可配置的过滤器组(13~0),以
  • STM32 CAN 过滤器总结

    2013-05-17 23:52:51
    STM32 CAN 过滤器总结 在STM32互联型产品中,CAN1和CAN2分享28个过滤器组,其它STM32F103xx系列产品中有14个过滤器组,用以对接收到的帧进行过滤。 每组过滤器包括了2个可配置的32位寄存器:CAN_FxR0和CAN_...
  • STM32的CAN过滤器-bxCAN的4种工作模式

    千次阅读 2018-10-14 22:24:33
    1、CAN过滤器的作用 在互联性产品中, bxCAN提供28个位宽可变/可配置的标识符过滤器组,软件通过对它们编程,从而在引脚收到的报文中选择它需要的报文,而把其它报文丢弃掉。在其它STM32F103xx系列产品中有14个位宽...
  • 1. 前言bxCAN是STM32系列最稳定的IP核之一,无论有哪个新型号出来,...有关bxCAN的过滤器部分的内容在参考手册中往往看得“不甚明白“,本文就过滤器的4种工作模式进行详细讲解并使用具体的代码进行演示,这些代码都进
  • CAN过滤器的配置(f103 hal1.8 系列) can的过滤器的配置是对can接收到的报文进行过滤的配置,在STM32芯片中,可以对can的报文进行过滤,从而省略cpu的处理过程。 can的过滤模式 f103中为应用程序提供了14个位宽可变...
  • GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_... //过滤器5 只要过滤器组大于4,次滤波器组就没有作用了 ????????????? CAN...
  • stm32 CAN过滤器

    千次阅读 2017-11-11 14:32:21
    在互联型产品中, CAN1和CAN2分享28个过滤器组 其它STM32F103xx系列产品中有14个过滤器组 位宽设置 四种配置方式: 1个32位的屏蔽位模式 2个32位的标识符列表模式,可以过滤2个标识符id 2个16位的屏蔽位模式...
  • 一、在STM32互联型产品中,CAN1和CAN2分享28个过滤器组,其它STM32F103xx系列产品中有14个过滤器组,用以对接收到的帧进行过滤。 1、过滤器组  每组过滤器包括了2个可配置的32位寄存器:CAN_FxR0和CAN_FxR1。这些...
  • CAN标识符、过滤器、屏蔽器之间的关系 CAN节点通过标识符来识别CAN帧是不是自己想要的,识别方法就是通过滤波屏蔽寄存器的设置来完 成,接收滤波寄存器设置了标识符每位的值,接收屏蔽寄存器一般有相同的数量匹配接收...
  • STM32F105 CAN通信过滤器配置问题

    千次阅读 2019-01-16 21:39:28
    当设置了CAN过滤器,表示只能接收指定ID的数据,其他ID发送过来的数据,不再触发CAN中断,可以进一步提高系统的稳定性。当CAN总线上数据比较多的时候,是很有必要的。 具体配置如下: CAN_SlaveSta...
  • STM32 CAN 过滤器总结(先收着)

    千次阅读 2012-12-28 19:54:49
    在STM32互联型产品中,CAN1和CAN2分享28个过滤器组,其它STM32F103xx系列产品中有14个过滤器组,用以对接收到的帧进行过滤。 每组过滤器包括了2个可配置的32位寄存器:CAN_FxR0和CAN_FxR1。这些过滤器相当于关卡,...
  • 最近在调试11个节点的CAN网络,出于安全性考虑增加过滤器功能(原子标配代码没设置相关功能),上午花费1个小时时间查阅资料与实验,最终完成,总结如下: 1、首先了解CAN协议的扩展帧格式,如下,定义的扩展标识符...
  • STM8/32 CAN过滤器设置详解

    千次阅读 2018-03-28 18:07:26
    CAN总线的标准帧和扩展帧主要决定帧ID的长度,标准帧的帧ID长度是11位,也就是帧ID的范围是000-7FF。扩展帧的帧ID长度是29位,也就是帧ID的范围是0000 0000-1FFF FFFF。CANopen帧ID最多是11位的,因此是标准帧;而...
1 2 3 4 5 ... 20
收藏数 51,298
精华内容 20,519
关键字:

can 过滤器