Can通信接口学习笔记[通俗易懂]

2022-08-31 16:25:55 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

第一步:了解Can通信接口协议,这里推荐大家 <<Can入门教程>>(必读),里面详细说明的can相关知识点;另外推荐大家看有关Can协议标准书籍(选读),相关的pdf书籍下载地址:链接:https://pan.baidu.com/s/1KDtoqkm541xZhoTUpXVJaw 提取码:9dvs

第二步: 特别需要关注点,1、通信速度与传输距离关系,2、通讯接口的硬件连接方式(终端电阻)3、协议帧的种类和格式

4、位时序定义和采样点位置设置 注意:请大家务必了解该知识点,该知识点与支持can协议控制芯片MUC息息相关

第三步:学习如何使用can控制芯片(STM32系列IC)传输数据

特别说明:stm32芯片的CAN接口是Bxcan接口,芯片仅提供can控制,不支持can传输物理层,所以硬件设计上需要使用CAN收发器连接mcu与CAN总线才可以正常工作,同时需要注意终端电阻。

有关stm32的can知识点,请查看中文数据手册:下载链接如下:链接:https://pan.baidu.com/s/1hlcSPKhZI6FyUinlgQbDnw 提取码:pgo3

stm32的CAN相关知识点:

一、stm32对Can波特率与位时序定义:

● 同步段(SYNC_SEG):通常期望位的变化发生在该时间段内。其值固定为1 个时间单元(1 x tCAN)。 ● 时间段1(BS1):定义采样点的位置。它包含CAN 标准里的 PROP_SEG 和 PHASE_SEG1。其值可以编程为1 到16 个时间单元,但也可以被自动延 长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。 ● 时间段2(BS2) : 定义发送点的位置。它代表CAN 标准里的 PHASE_SEG2。其值可以编程为1 到8 个时间单元,但也可以被自动缩短 以补偿相位的负向漂移

BRP[9:0]: 波特率分频器

PCLK = Can在stm32外设总线上时钟大小(请查看stm23时钟树看can对应的时钟)

bps = PCLK/(BRP[9:0]*(BS1 BS2 SYNC_SEG))

其中SYNC_SEG固定为1个时间单元所以 bps = PCLK/(BRP[9:0]*(BS1 BS2 1)),此时注意跟同步跳转宽度SJW没关系,尽管他也一直设置为1.

二、stm32的CAN采样点设置

采样点位于时间段1和时间段2之间。根据CIA推荐采样点,最好设置在85%~90%。

计算公式: (BS1 1)/(1 BS1 BS2)

以下是经验值,仅供参考:

75% when 波特率 > 800K

80% when 波特率 > 500K

87.5% when 波特率 <= 500K

75% when 波特率 > 800K

80% when 波特率 > 500K

87.5% when 波特率<= 500K

三:STM32 CAN 过滤器

1)两种过滤模式定义:列表模式 掩码模式

列表模式:把我们需要关注的所有CAN报文ID写上去,开始过滤的时候只要对比这张表,如果接收到的报文ID与表上的相符,则通过,如果表上没有,则不通过。但是,这种列表方案有点缺陷,即如果我们只关注一个报文ID,则需要往列表中写入这个ID,如果需要关注两个,则需要写入两个报文ID,如果需要1万个,那么需要写入1万个,可问题是,MCU上的资源是有限的,不可能提供1万个或更多。非常明显,这种列表的方式受到列表容量大小的限制。

掩码模式:包括验证码和掩码,其中验证码即为我们需要关注的报文大致ID,掩码则是过滤验证码,验证码与掩码进行与操作得到结果值就是验证通过的报文ID。比如验证码设置为0x12345678;掩码0xff00ff00;进行与后结果为 0x12xx56xx;也就是说只要收到报文ID[31:24] = 0x12,ID[15:8] = 0x56(其他位不考虑,可为0或1)都是正确的报文ID,也就是可以通过过滤器;假如设置掩码为0x00000000,则表示任意报文ID都可以通过过滤器。

2) 列表模式与掩码模式的对比

3)can 工作模式:32位列表模式,32位掩码模式,16位列表模式,16位掩码模式

在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) 运用实例:

注意:stm32官方提供的demo中,官方提供4个int16_t变量数据表示CAN_FxR1和CAN_FxR1寄存器;它们分别为FilterIdHigh,FilterIdLow,FilterMaskIdHigh, FilterMaskIdLow;在不同的位宽工作模式下组合不一样;

16位带宽模式下(寄存器在不同过滤模式下表示值请参考上图):

CAN_FxR1 = (FilterMaskIdLow << 16 | FilterIdLow)

CAN_FxR2 = (FilterMaskIdHigh<< 16 | FilterIdHigh)

可能有人会有这样疑问,为什么Mask在高16位?其实我们看会上图给出的4中带宽工作模式就可以知道,带mask都是占用寄存器高位的

32位带宽模式下(寄存器在不同过滤模式下表示值请参考上图):

CAN_FxR1 = (FilterIdHigh<< 16 | FilterIdLow)

CAN_FxR2 = (FilterMaskIdHigh<< 16 | FilterMaskIdLow )

运用实例1: 16位带宽掩码模式:

代码语言:javascript复制
static void CANFilterConfig_Scale16_IdMask(void)
{
  CAN_FilterConfTypeDef  sFilterConfig;
  uint16_t StdIdArray1[10] ={0x7D1,0x7D2,0x7D3,0x7D4,0x7D5,    //定义第一组标准CAN ID
                          0x7D6,0x7D7,0x7D8,0x7D9,0x7DA};
  uint16_t StdIdArray2[10] ={0x751,0x752,0x753,0x754,0x755,    //定义第二组标准CAN ID
                          0x756,0x757,0x758,0x759,0x75A};
  uint16_t      mask,tmp,i,num;
  
  sFilterConfig.FilterNumber = 5;                    //使用过滤器5
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;            //配置为掩码模式
  sFilterConfig.FilterScale = CAN_FILTERSCALE_16BIT;        //设为16位宽
  
  //配置第一个过滤对
  sFilterConfig.FilterIdLow =StdIdArray1[0]<<5;            //设置第一个验证码
  mask =0x7ff;
  num =sizeof(StdIdArray1)/sizeof(StdIdArray1[0]);
  for(i =0; i<num; i  )                            //计算第一个屏蔽码
  {
    tmp =StdIdArray1[i] ^ (~StdIdArray1[0]);
    mask &=tmp;
  }
  sFilterConfig.FilterMaskIdLow =(mask<<5)|0x10;    //只接收数据帧
  
  //配置第二个过滤对
  sFilterConfig.FilterIdHigh = StdIdArray2[0]<<5;    //设置第二个验证码
  mask =0x7ff;
  num =sizeof(StdIdArray2)/sizeof(StdIdArray2[0]);
  for(i =0; i<num; i  )                    //计算第二个屏蔽码
  {
    tmp =StdIdArray2[i] ^ (~StdIdArray2[0]);
    mask &=tmp;
  }
  sFilterConfig.FilterMaskIdHigh = (mask<<5)|0x10;  //只接收数据帧
  
 
  sFilterConfig.FilterFIFOAssignment = 0;        //通过的CAN 消息放入到FIFO0中
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.BankNumber = 14;
  
if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

运用实例2:16位带宽列表模式:

代码语言:javascript复制
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();
  }
}

运用实例3: 32位带宽掩码模式

代码语言:javascript复制
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();
  }
}

运用实例4: 32带宽列表模式

代码语言:javascript复制
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();
  }
}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151739.html原文链接:https://javaforall.cn

0 人点赞