• 详解CAN通信的标识符掩码和标识符列表两种过滤机制


    CAN 通信的应用非常广泛,本文不涉及CAN通信的基础配置,重点分析一下STM32和GD32的CAN通信两种ID过滤方式。

    首先,不管是STM32还是GD32,实现CAN通信ID过滤的机制和原理一定是一样的,只是用到的寄存器有差别。

    1. ID过滤原理:
    在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的内容相关的。因此,发送者以广播的形式把报文发送给所有的接收者。节点在接收报文时,根据标识符的值决定软件是否需要该报文。
    为满足这一需求,在互联型产品中,bxCAN控制器为应用程序提供了28个位宽可变的、可配置的过滤器组(270);在其它产品中,bxCAN控制器为应用程序提供了14个位宽可变的、可配置的过滤器组(130),以便只接收那些软件需要的报文。硬件过滤的做法节省了CPU开销,否则就必须由软件过滤从而占用一定的CPU开销。

    每个过滤器组x由2个32位寄存器,在STM32中是由CAN_FxR0和CAN_FxR1组成。在GD32中是由器CAN_FxDATA0和CAN_ FxDATA1组成。

    2. ID过滤方式:
    掩码模式、列表模式。
    不管是掩码模式还是列表模式,目的肯定都是为了让数据接收方接收特定ID的数据,但是既然分了两种模式,肯定有他们的不同的目的。

    3.ID过滤方式1——掩码模式:
    在掩码模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。
    举个例子:
    假如是标准帧模式,以16位宽度为例,预设的ID=0x0001,当期望接收所有ID尾号为1的数据,那么设置对应的掩码为MASK=0x0001即可。
    为什么呢?将ID的0x0001和MASK的0x0001解析成2进制:
    预设ID:0000 0000 0000 0001;
    MASK:0000 0000 0000 0001;
    MSAK值的1的位表示:接收的ID必须与预设ID的位一致;
    MASK值为0的位表示:接收的ID可以不与预设ID的位一致;
    因此示例的MASK仅限制了ID的最后1位与预设ID的最后1为一致即可,其余的位不做限制,只要满足这样形式的ID数据,都能通过示例的过滤机制,从而被接收方正确接收,此处可知,掩码的作用就是要以预设ID为模板,决定过滤掉哪些类型的ID;

    也就是说:
    为了过滤出一组标识符,应该设置过滤器组工作在掩码模式。

    注意,示例选用了标准帧模式,以16位宽度进行过滤,因此原本一个过滤器组的2个32位寄存器就分成了4个16位的空间,分别去保存预设的ID和相应的掩码:
    格式如下:
    预设ID1:【占用寄存器1的低16位】;
    ID1的掩码:【占用寄存器1的高16位】;
    同理:
    预设ID2:【占用寄存器2的低16位】;
    ID2的掩码:【占用寄存器2的高16位】;

    由此可知,此种方式下,最多可预设2种ID,可过滤出多个ID;
    STM32与GD32的过滤器机制一模一样:
    (1)STM32参考手册的16位掩码模式过滤器如下:
    在这里插入图片描述
    (2)GD32参考手册的16位掩码模式过滤器如下:
    在这里插入图片描述

    注意,此种模式下,在配置时需要将对应的ID和掩码数据分别写进寄存器的STID区域,如上图所示,在16位空间中,STID区域处于高11位,因此需要将预设ID和掩码数据左移5位后写进寄存器。代码如下:

    /**
     * @brief    can外设配置
     * @param    None
     * @retval   返回值
     */
    void can_config(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	CAN_InitTypeDef CAN_InitStructure;
    	CAN_FilterInitTypeDef CAN_FilterInitStructure;
    
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    
    	/*
        CAN1_RX -- PA11
        CAN1_TX -- PA12
        */
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    	CAN_DeInit(CAN1);
    	CAN_StructInit(&CAN_InitStructure);
    	CAN_InitStructure.CAN_TTCM = DISABLE; //不生成时间戳
    	CAN_InitStructure.CAN_ABOM = ENABLE;  //自动总线关闭管理
    	CAN_InitStructure.CAN_AWUM = DISABLE; //自动唤醒模式
    	CAN_InitStructure.CAN_NART = DISABLE; //仲裁丢失或出错后的自动重传功能
    	CAN_InitStructure.CAN_RFLM = DISABLE; //接收FIFO加锁模式
    	CAN_InitStructure.CAN_TXFP = ENABLE;  //传输FIFO优先级
    	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
    
    	CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
    	CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq;
    	CAN_InitStructure.CAN_BS2 = CAN_BS1_8tq;
    	CAN_InitStructure.CAN_Prescaler = 40; // 波特率36000000/18/40 = 50KHz
    	CAN_Init(CAN1, &CAN_InitStructure);
    
    	CAN_FilterInit(&CAN_FilterInitStructure);
    	CAN_FilterInitStructure.CAN_FilterNumber = 0;
    	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
    	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
    	if (can.device_mode == DEVICE_MASTER) //识别所有0x0xx类型的从设备地址
    	{
    		CAN_FilterInitStructure.CAN_FilterIdHigh = CAN_ID_BASE_SLAVE << 5; //ID1
    		CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x700 << 5;		   //ID1_MASK
    		CAN_FilterInitStructure.CAN_FilterIdLow = CAN_ID_DEFAULT << 5;	   //ID2
    		CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x7FF << 5;		   //ID2_MASK
    	}
    	else //仅识别与本设备地址匹配的地址,保证主设备对从设备实现一对一通讯,也识别0x0000广播
    	{
    		CAN_FilterInitStructure.CAN_FilterIdHigh = can.std_id << 5; //ID1
    		CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x7FF << 5;	//ID1_MASK
    		CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000 << 5;		//ID2
    		CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x7FF << 5;	//ID2_MASK
    	}
    
    	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
    	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
    	CAN_FilterInit(&CAN_FilterInitStructure);
    
    	CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //FIFO0消息挂号中断
    	nvic_set(USB_LP_CAN1_RX0_IRQn, IRQ_PRIO_CAN1);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    4. ID过滤方式2——列表模式:
    在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。通俗一点讲就是:接收的ID必须与预设的ID一模一样才能通过过滤,最终被接收到。

    举个例子:
    假如是标准帧模式,以16位宽度为例,预设的ID=0x0001,当期望仅接收与预设ID一致的数据,那么只要发送的ID与预设ID一样就可以被接收到,其余类型的ID数据都被过滤掉,接收方将接收不到此类数据。

    也就是说:
    为了过滤出一个标识符,应该设置过滤器组工作在标识符列表模式。

    注意,示例选用了标准帧模式,以16位宽度进行过滤,因此原本一个过滤器组的2个32位寄存器就分成了4个16位的空间,分别去保存预设的ID:
    格式如下:
    预设ID1:【占用寄存器1的低16位】;
    预设ID2:【占用寄存器1的高16位】;
    预设ID3:【占用寄存器2的低16位】;
    预设ID4:【占用寄存器2的高16位】;

    由此可知,此种方式下,最多可预设4种ID,可过滤出4个ID;
    STM32与GD32的过滤器机制一模一样:
    (1)STM32参考手册的16位列表模式过滤器如下:
    在这里插入图片描述
    (2)GD32参考手册的16位列表模式过滤器如下:
    在这里插入图片描述

    注意,此种模式下,在配置时需要将对应的4个ID分别写进寄存器的STID区域,如上图所示,在16位空间中,STID区域处于高11位,因此需要将预设ID数据左移5位后写进寄存器。示例代码如下:

    CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;     //??????  
      CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;    //?????16?  
      CAN_FilterInitStructure.CAN_FilterIdHigh = std_id<<5;  //4???CAN ID?????4????  
      CAN_FilterInitStructure.CAN_FilterIdLow = std_id1<<5;  
      CAN_FilterInitStructure.CAN_FilterMaskIdHigh = std_id<<5;  
      CAN_FilterInitStructure.CAN_FilterMaskIdLow = std_id1<<5;  
      CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;           //?????????FIFO0?  
    	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//激活过滤器0	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5. 总结一下:
    两种过滤方式各有千秋,需要过滤出一组ID,使用掩码模式;
    需要过滤出特定的几个ID,使用列表模式。
    实际使用中可巧妙地调整预设ID和掩码,甚至组合使用掩码模式和列表模式实现自己想要的过滤效果。

  • 相关阅读:
    string类的常用方法
    京东AB主图测试实验,优化主图提升转化!
    全新防关联技术出炉:亚马逊测评环境优化,下单成功率大提升
    查看 OceanBase 执行计划
    轻松添加背景图片并一键褪色的简单操作
    Arduino PID整定
    免费开源 | 基于SSM的校园订餐系统
    【python海洋专题二十二】在海图上text
    Db2连接的详细信息
    Oracle数据库更改账号密码,设置账号有效期
  • 原文地址:https://blog.csdn.net/weixin_44612435/article/details/133701872