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 过滤器组的过滤形式和位宽设置
过滤器组能够经过相应的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
图7
位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’时,才干修正相应的过滤器存放器。
图8
位31:0 | FB[31:0] : 过滤器位
存放器的每位对应于所希望的标识符的相应位的电平。
存放器的每位指示是否对应的标识符存放器位必定要与希望的标识符的相应位共同。 |
注: 依据过滤器位宽和形式的不同设置,过滤器组中的两个存放器的功用也不尽相同。。关于过滤器的映射,功用描绘和屏蔽存放器的相关,请拜见2节标识符过滤。
屏蔽位形式下的屏蔽/标识符存放器,跟标识符列表形式下的存放器位界说相同。
4 代码实例
4.1 CAN ID值的结构剖析
在讲到代码实例之前,首要咱们都弄懂一件事,当给定一个CAN ID,如0x1800f001,当然这个是扩展ID,这儿要问的是,这个CAN ID的值自身包括两部分,即根本ID与扩展ID,即么你知道这个扩展ID0x1800f001的哪些位是根本ID,哪些位又是扩展ID?(在根本CANID格局下不存在这个问题)
在答复这个问题之前咱们来看看ISO11898的界说,如下图:
图9
如上图,根本格局不存在扩展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,此刻,过滤器包括两个存放器,屏蔽码存放器和标识符存放器。此形式下最多只存在一个屏蔽过滤器。
如下图所示:
图10
如上图,上面的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_FilterInitTypeDefCAN_FilterInitStructure;
- U16std_id=0x7e9;
- U32ext_id=0x1800f001;
- U32mask=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],规范CANID自身是不包括扩展ID数据,因而为了要将规范CANID放入此存放器,规范CANID首要应左移5位后才干对齐.
- //CAN_FilterInitStructure.CAN_FilterIdLow=0|CAN_ID_EXT;//设置标识符存放器低字节,这儿也能够设置为CAN_ID_STD
- //屏蔽存放器的设置
- //这儿的思路是先将规范CANID和扩展CANID对应的ID值先异或后取反,为什么?异或是为了找出两个CANID有哪些位是相同的,是相同的位则阐明需求关怀,需求关怀的位对应的屏蔽码位应该设置为1,因而需求取反一下。最终再全体左移3位。
- mask=(std_id<<18);//这儿为什么左移18位?因为从ISO11898中能够看出,规范CANID占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位的标识符列表形式
在此种形式下,过滤器组包括的两个存放器意义相同,此形式下只多存在两个标识符列表过滤器如下图:
图11
- CAN_FilterInitTypeDefCAN_FilterInitStructure;
- U16std_id=0x7e9;
- U32ext_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位的屏蔽码形式
在此形式下,最多存在两个屏蔽码过滤器,如下图:
图12
由上图映射可知,最下面的映射只包括STDID0~ID10,因而,此形式下的两个屏蔽过滤器只能完成对规范ID的过滤。详细代码就不介绍了,拜见上图的映射即可。
4.5 位宽为16位的标识符列表形式
图13
在此形式下,因为标识符存放器的高16位和低16位,屏蔽存放器的高16位和低16位都用来做标识符存放器,因而,最多可存在4个标识符过滤器。相同,只能完成对规范帧的过滤。详细代码就不介绍了,拜见上图的映射即可。