打印
[应用相关]

STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法

[复制链接]
楼主: 東南博士
手机看帖
扫描二维码
随时随地手机跟帖
21
東南博士|  楼主 | 2018-9-17 14:36 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
这里以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。

使用特权

评论回复
22
東南博士|  楼主 | 2018-9-17 14:36 | 只看该作者
于是,bxCAN处于这种考虑,也设置了这么一个寄存器CAN_FS1R来表示CAN ID的位宽,如下图所示:

使用特权

评论回复
23
東南博士|  楼主 | 2018-9-17 14:41 | 只看该作者
每一个位对应着bxCAN中28个过滤器的位宽,这个需要用户来设置。

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

使用特权

评论回复
24
東南博士|  楼主 | 2018-9-17 14:42 | 只看该作者
在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种工作模式。

使用特权

评论回复
25
東南博士|  楼主 | 2018-9-17 14:44 | 只看该作者
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

使用特权

评论回复
26
東南博士|  楼主 | 2018-9-17 14:47 | 只看该作者


使用特权

评论回复
27
東南博士|  楼主 | 2018-9-17 14:49 | 只看该作者
时钟树如下设置:

使用特权

评论回复
28
東南博士|  楼主 | 2018-9-17 14:50 | 只看该作者
在配置中的NVIC中,打开CAN1 RX0接收中断,如下图所示:

使用特权

评论回复
29
東南博士|  楼主 | 2018-9-17 14:50 | 只看该作者
其他的没有什么特殊设置,生成工程后的main函数如下:

int main(void)
{

  /* USER CODE BEGIN 1 */
  static CanTxMsgTypeDef        TxMessage;
  static CanRxMsgTypeDef        RxMessage;
  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN1_Init();

  /* USER CODE BEGIN 2 */
  hcan1.pTxMsg =&TxMessage;
  hcan1.pRxMsg =&RxMessage;
  CANFilterConfig_Scale32_IdList();                        //列表模式-32位宽
//CANFilterConfig_Scale16_IdList();                        //列表模式-16位宽
//CANFilterConfig_Scale32_IdMask_StandardIdOnly();        //掩码模式-32位宽(只有标准CAN ID)
//CANFilterConfig_Scale32_IdMask_ExtendIdOnly();        //掩码模式-32位宽(只用扩展CAN ID)
//CANFilterConfig_Scale32_IdMask_StandardId_ExtendId_Mix(); //掩码模式-32位宽(标准CANID与扩展CAN ID混合)
//CANFilterConfig_Scale16_IdMask();                        //掩码模式-16位宽
  HAL_CAN_Receive_IT(&hcan1,CAN_FIFO0);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

}

使用特权

评论回复
30
東南博士|  楼主 | 2018-9-17 14:50 | 只看该作者
如上代码所示,示例中将采用各种过滤器配置来演示,在测试时我们可以只保留一种配置,也可以全部打开,为了确保每种配置的准确性,这里建议只保留其中一种配置进行测试。

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

void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)
{
  if(hcan->pRxMsg->StdId ==0x321)
  {
    //handle the CAN message
    HandleCANMessage(hcan->pRxMsg);                //处理接收到的CAN报文
  }
  if(hcan->pRxMsg->ExtId ==0x1800f001)
  {
     HandleCANMessage(hcan->pRxMsg);                //处理接收到的CAN报文
  }
  HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);    //若收到消息则闪烁下LED4
  HAL_Delay(200);
  HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_RESET);  
  HAL_CAN_Receive_IT(&hcan1,CAN_FIFO0);
}

使用特权

评论回复
31
東南博士|  楼主 | 2018-9-17 14:51 | 只看该作者
4.2.   32位宽的列表模式
4.2.   32位宽的列表模式

使用特权

评论回复
32
東南博士|  楼主 | 2018-9-17 14:52 | 只看该作者
如上图所示,在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”。示例代码如下:

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();
  }
}

使用特权

评论回复
33
東南博士|  楼主 | 2018-9-17 14:52 | 只看该作者
这里需要说明一下,由于我们使用的是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位中,于是有了:


sFilterConfig.FilterIdHigh = StdId<<5;

使用特权

评论回复
34
東南博士|  楼主 | 2018-9-17 14:53 | 只看该作者
另一个扩展CAN ID ExtId类型,将其基本ID放入到STID中,扩展ID放入到EXID中,最后设置IDE位为1。就这样配置好了。

使用特权

评论回复
35
東南博士|  楼主 | 2018-9-17 14:54 | 只看该作者
4.3.   16位宽的列表模式

使用特权

评论回复
36
東南博士|  楼主 | 2018-9-17 14:54 | 只看该作者
在16位宽的列表模式下,FilterIdHigh,FilterIdLow,FilterMaskIdHigh,FilterMaskIdLow这4个16位变量都是用来存储一个标准CAN ID,这样,就可以存放4个标准CAN ID了,需要注意地是,此种模式下,是不能处理扩展CANID,凡是需要过滤扩展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();
  }
}

使用特权

评论回复
37
東南博士|  楼主 | 2018-9-17 14:54 | 只看该作者
4.4.   32位宽掩码模式

使用特权

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

4.4.1. 只针对标准CAN ID
如下代码示例:

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();
  }
}

使用特权

评论回复
39
東南博士|  楼主 | 2018-9-17 14:55 | 只看该作者
4.4.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();
  }
}

使用特权

评论回复
40
東南博士|  楼主 | 2018-9-17 14:55 | 只看该作者
4.4.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();
  }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则