STM32 CAN通信理解(是半双工还是全双工?)

STM32F429 CAN通信

CAN 是控制器局域网络 (Controller Area Network) 的简称,它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(ISO11519),是国际上应用最广泛的现场总线之一。

1、全双工允许数据在两个方向上同时传输;
2、半双工允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,实际上是切换的单工。

  • 有人认为CAN是“全双工”,也有人看是“半双工”。其实我认为都对吧,大家都知道,CAN节点包括CAN控制器和CAN收发器。只是站的角度不同,站在CAN控制器看问题,很明显是全双工,CAN控制器发送接收信号就有点类似串口发送TTL信号(这里不严谨,只是举个例子),全双工通信;换个角度,站在收发器看,虽然是用了两根信号线,但是它们是一对差分线发送差模信号,用两根线代表一个信号(显性信号和隐形信号),在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。

目录
一、STM32中CAN的初始化代码实现
二、CAN的筛选器使用及理解
三、CAN的物理层
四、CAN的协议层

一、STM32中CAN的初始化代码实现

1、GPIO的引脚初始化

void CAN_GPIO_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;   	
  
  RCC_AHB1PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE);
  GPIO_PinAFConfig(CAN_TX_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT);
  GPIO_PinAFConfig(CAN_RX_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT); 
	
  GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);
}

2、CAN控制器的初始化配置

static void CAN_Mode_Config(void)
{
	CAN_InitTypeDef        CAN_InitStructure;
    RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);

	/*CAN1寄存器的初始化*/
	CAN_DeInit(CAN1);
	CAN_StructInit(&CAN_InitStructure);

	/*CAN相关寄存器的配置*/
	CAN_InitStructure.CAN_TTCM=DISABLE;			   //MCR-TTCM  关闭时间触发通信模式使能
	CAN_InitStructure.CAN_ABOM=ENABLE;			   //MCR-ABOM  使能自动离线功能
	CAN_InitStructure.CAN_AWUM=ENABLE;			   //MCR-AWUM  使能自动唤醒模式
	CAN_InitStructure.CAN_NART=DISABLE;			   //MCR-NART  禁止报文自动重传,即使发送失败,不会再发
	CAN_InitStructure.CAN_RFLM=DISABLE;			   //MCR-RFLM  接受FIFO不锁定,接受数据溢出时,新报文会覆盖原报文
	CAN_InitStructure.CAN_TXFP=DISABLE;			   //MCR-TXFP  FIFO优先级由报文的标识符决定,不是时间顺序
	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;  //正常模式
    /*波特率配置,约定好双方收发一个数据位的速率*/    
    CAN_InitStructure.CAN_SJW=CAN_SJW_2tq;		   //BTR-SJW 重新同步跳跃宽度2个时间单元
	/* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) tq*(1+3+5)  */
	CAN_InitStructure.CAN_BS1=CAN_BS1_5tq;		   //BTR-TS1 时间段1 占用了5个时间单元 包括了PTS
	CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;		   //BTR-TS1 时间段 时间单元2 占用了5个时间单元	
	/* CAN Baudrate = 1 MBps (1MBps已是stm32中can的最高速速率) (CAN 的时钟总线 APB 1 = 45 MHz) */     
	CAN_InitStructure.CAN_Prescaler =5;		  //BTR-BRP 波特率分频器 波特率计算:45/(1+5+3)/5=1 Mbps
	CAN_Init(CANx, &CAN_InitStructure);
}

3、CAN的筛选器配置

void CAN_Filter_Config(void)
{
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
	/*CAN筛选器组0*/
	CAN_FilterInitStructure.CAN_FilterNumber=0;						
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;	//掩码模式
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;	//筛选器位宽 32位
	/* 验证码配置 */
	CAN_FilterInitStructure.CAN_FilterIdHigh= ((0x1314<<3)>>16)&0xffff//验证码32位中高16位 
	CAN_FilterInitStructure.CAN_FilterIdLow= ((0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //验证码32位中低16位  
	/*屏蔽码配置*/
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF;			//屏蔽码高16位,
	CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF;			//屏蔽码低16位,只允许一个通过
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;				//筛选器关联FIFO0
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;	//使能筛选器
	CAN_FilterInit(&CAN_FilterInitStructure);
	/*使能筛选器 接收中断*/
	CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}
/*通信发送报文的设置*/
void CAN_SetMsg(CanTxMsg *TxMessage)
{	  
  uint8_t ubCounter = 0;				 
  TxMessage->ExtId=0x1314;					 //扩展id
  TxMessage->IDE=CAN_ID_EXT;					 //扩展模式
  TxMessage->RTR=CAN_RTR_DATA;				 //发送 数据 类型
  TxMessage->DLC=8;							 //数据长度
	
	/*数据载入*/
	for (ubCounter = 0; ubCounter < 8; ubCounter++)
  {
    TxMessage->Data[ubCounter] = ubCounter;
  }

}

4、中断服务程序

void CAN_NVIC_Config(void)
{
   	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ;	   //CAN RX中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;			   
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
void CAN1_RX0_IRQHandler(void)
{
	/*从邮箱接收到报文*/
	CAN_Receive(CANx, CAN_FIFO0, &RxMessage);

	/* 验证id==0x1314 是否是扩展id 数据位是否8位*/ 
	if((RxMessage.ExtId==0x1314) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) )
	{
		flag = 1; 					      
	}
	else
	{
		flag = 0; 					   
	}
}

二、CAN的筛选器使用及理解

1、CAN筛选器的作用

CAN总线中,往往会有很多收发器进行发送多条数据,但是这些数据中只有一部分数据是你想要的,CPU却要处理很多无关紧要的数据,这无疑是给运行的效率带来的极大负担。所以筛选器起到了很大的作用,它可以为CPU过滤掉不要的数据,筛选出想要的数据,并把得到的数据存入到接收邮箱中。stm32通过识别过滤报文中的id,过滤方法有两种,列表模式和掩码模式。

在这里插入图片描述

每组筛选器包含 2 个 32 位的寄存器,分别为 CAN_FxR1 和 CAN_FxR2,它们用来存储要筛选的ID 或掩码。由可将一个32位寄存器分为两个16位寄存器,每组筛选器包含了4个16位寄存器。

//不管是32位还是16位宽的筛选器 
/*CAN_FxR1[31:16]*/
CAN_FilterInitStructure.CAN_FilterIdHigh 
/*CAN_FxR1[15:0]*/
CAN_FilterInitStructure.CAN_FilterIdLow
/*CAN_FxR2[31:16]*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh
/*CAN_FxR2[15:0]*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow

2、列表模式

筛选器会比较列表中固定的id,要求报文 ID 与列表中的某一个标识符完全相同才可以接收。这种筛选方式有个很大的缺陷,stm32资源很有限,如果选择32位宽的筛选器,只能筛选两个报文id,如果是16位,可以筛选4个报文id。
在这里插入图片描述

  • 32位宽的筛选器 列表模式
void CAN_Filter_Config(void)
{
	uint32_t StdId[2] = {0x1234,0x1314};
	uint32_t ExdId = 0x80908890;
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
	/*CAN筛选器组0*/
	CAN_FilterInitStructure.CAN_FilterNumber=0;						
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList;	//列表模式
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;	//筛选器位宽 32位
	/* 验证码配置 */
	/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
	CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位 
	CAN_FilterInitStructure.CAN_FilterIdLow= 0|CAN_ID_STD|CAN_RTR_DATA; //验证码32位中低16位  

    /*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh= (ExdId<<3)>>16&0xFFFF;//屏蔽码高16位,
	/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
	CAN_FilterInitStructure.CAN_FilterMaskIdLow= (ExdId<<3)&0xFFFF|CAN_ID_EXT|CAN_RTR_DATA;	//屏蔽码低16位,只允许一个通过
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;				//筛选器关联FIFO0
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;	//使能筛选器
	CAN_FilterInit(&CAN_FilterInitStructure);
}
  • 16位宽的筛选器 列表模式
    在这里插入图片描述

由参考手册可知,配置16宽的筛选器,CAN_FilterIdHigh、CAN_FilterIdLow、CAN_FilterMaskIdHigh、CAN_FilterMaskIdLow这四个变量可以存放4个标准id,不用存放扩展id,扩展只能用32位的筛选器。

void CAN_Filter_Config(void)
{
	uint32_t StdId[2] = {0x1234,0x1314,0x1212,0x1313};
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
	/*CAN筛选器组0*/
	CAN_FilterInitStructure.CAN_FilterNumber=0;						
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList;	//列表模式
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;	//筛选器位宽 32位
	/* 验证码配置 */
	/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
	CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位 
	CAN_FilterInitStructure.CAN_FilterIdLow= StdId[1]<<5; //验证码32位中低16位  

	/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh= StdId[2]<<5;//屏蔽码高16位,
	/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
	CAN_FilterInitStructure.CAN_FilterMaskIdLow= StdId[3]<<5;//屏蔽码低16位,只允许一个通过
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;	//筛选器关联FIFO0
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//使能筛选器
	CAN_FilterInit(&CAN_FilterInitStructure);
}

3、掩码模式

为了解决列表模式下的,列表的容量资源有限的问题。出现了第二种方案,可以替代列表模式。相比列表模式,掩码模式可以筛选出更多的报文id。筛选过程中,需要经过屏蔽和对比两个操作(屏蔽码和验证码,验证码不唯一,看需求)。

为了能更好的理解,举个简单的例子,比如要筛查id出生年份为2000年的青年人(44200078):

0x04 0x04 0x02 0x00 0x00 0x00 0x07 0x08 -->经过屏蔽码(只需匹配第2个~第5个字节,进行相与操作)

0x00 0x00 0xff 0xff 0xff 0xff 0x00 0x00 -->得到

0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00 -->对比验证码

0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00
如果是筛查00后的年轻人(只需匹配第2个~第4个字节):

将屏蔽码改为0x00 0x00 0xff 0xff 0xff 0x00 0x00 0x00

可以识别通过的数值2000~2009,可以看出只要通过数值的多少,关键看屏蔽码。设得宽,则可以通过的多(所有位为0,则不过任何过滤操作,则谁都可以通过),设得窄,则通过的少(所有位设为1,则只有一个能通过)。
在这里插入图片描述

  • 32位宽的筛选器 掩码模式
 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;
  
}
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;
  
}
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;
  
}
  • 16位宽的筛选器 掩码模式
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;
  
}

三、CAN的物理层

与SPI、I2C等具有时钟信号的同步通讯方式不同,CAN通信并不是以时钟信号进行同步,CAN是一种 异步 通讯。只具有CAN_High和CAN_Low两条信号线 ,共同构成 一对差分信号线 的形式进行通讯。

CAN的物理层主要有两种

闭环总线网络

闭环总线通讯网络 中的 CAN 通讯网络是一种遵循ISO11898 标准的高速、短距离“闭环网络”,它的总线最大长度为** 40m** ,通信速度最高为 1Mbps,总线的两端各要求有一个“120 欧”的电阻(并联) .

开环总线网络

开环总线通讯网络 中的是遵循 ISO11519-2 标准的低速、远距离“开环网络”,它的最大传输距离为** 1km** ,最高通讯速率为 125kbps,两根总线是独立的、不形成闭环,要求每根总线上各串联有一个“2.2 千欧”的电阻 。

通讯节点

CAN总线上可以挂载多个通讯节点,CAN通讯协议不对节点进行地址编码 ,而是对数据内容进行编码。CAN通讯节点由一个控制器及CAN收发器组成。当 CAN 节点需要发送数据时,控制器把要发送的二进制编码通过 CAN_Tx 线发送到收发器(类似TTL信号),然后由收发器把这个普通的逻辑电平信号转化成差分信号,通过差分线 CAN_High 和 CAN_Low 线输出到 CAN 总线网络。

差分信号

差分信号又称差模信号,这两个信号线的振幅相等,相位相反通过两根信号线的电压差值来表示逻辑0和逻辑1.

  • 相对单信号线传输方式,具有的优点:抗干扰能力,有效抑制它对外部的电磁干扰。时许定位准确。

  • 由于差分信号线具有这些优点,所以在 USB 协议、 485 协议、以太网协议及 CAN 协议的物理层中,都使用了差分信号传输。

CAN协议中的差分信号

  • 当逻辑"0"(显性电平),CAN_High和CAN_Low的压差2.5v;当逻辑"1"(隐性电平),CAN_High和CAN_Low的压差0v。

  • 在CAN总线中,在同一时间,只要由输出显性电平,总线处于显性电平状态。类似I2c总线的“线与”特性。

  • 由于 CAN 总线协议的物理层只有 1 对差分线,在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。在 CAN 的通讯网络中,因为共用总线,在整个网络中同一时刻只能有一个通讯节点发送信号,其余的节点在该时刻都只能接收。

四、CAN的协议层

CAN 的波特率及位同步

CAN 属于异步通讯,节点间使用 约定好的波特率 进行通讯,特别地, CAN 还会使用“位同步”的方式来抗 干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。

位时序分解

为了实现位同步,CAN 协议把每一个数据位的时序分解成SS 段、PTS 段、 PBS1 段、 PBS2 段,这四段的长度加起来即为一个 CAN 数据位的长度。分解后最小的时间单位是 Tq,而一个完整的位由 8~25 个 Tq 组成.
在这里插入图片描述

该图中表示的 CAN 通讯信号每一个数据位的长度为 19Tq,其中 SS 段占 1Tq, PTS 段占 6Tq, PBS1 段占 5Tq, PBS2 段占 7Tq。信号的采样点位于 PBS1 段与 PBS2 段之间,通过控制各段的长度 ,可 以对采样点的位置进行偏移,以便准确地采样。

• SS 段 (SYNC SEG)

若通讯节点检测到总线上信号的跳变沿被包含在 SS 段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。 SS 段的大小固定为 1Tq

• PTS 段 (PROP SEG)

PTS 译为传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS 段的大小可以为 1~8Tq。

• PBS1 段 (PHASE SEG1)

PBS1 译为 相位缓冲段 ,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的 时候可以加长。 PBS1 段的初始大小可以为1~8Tq。

• PBS2 段 (PHASE SEG2)

PBS2 这是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。 PBS2 段的初始大小可以为 2~8Tq。

通讯的波特率

总线上的各个通讯节点只要约定好 1 个 Tq 的时间长度以及每一个数据位占据多少个 Tq,就可以 确定 CAN 通讯的波特率。

例如,假设上图中的 1Tq=1us,而每个数据位由 19 个 Tq 组成,则传输一位数据需要时间 T1bit =19us,从而每秒可以传输的数据位个数为:

1x106/19 = 52631.6 (bps)  

这个每秒可传输的数据位的个数即为通讯中的波特率。

同步过程分析

波特率只是约定了每个数据位的长度 ,数据同步还涉及到相位的细节,这个时候就需要用到数据 位内的 SS、 PTS、 PBS1 及 PBS2 段了。

CAN的数据同步分为硬同步和重新同步 。其中硬同步只是当存在“帧起始信号”时起作用,无法确保后续一连串的位时序都是同步的,而重新同步方式可解决该问题。

硬同步

若某个 CAN 节点通过总线发送数据时,它会发送一个表示通讯起始的信号 ,该信号是一个由高变低的下降沿 。而挂载到 CAN 总线上的通讯节点在不发送数据 时,会时刻检测总线上的信号。

节点以硬同步的方式调整,把自己的位时序中的 SS 段平移至总线出现下降沿的部分,获得同步,同步后采样点就可以采集得正确数据了。
在这里插入图片描述

重新同步

如果在一帧很长的数据内,节点信号与总线信号相位有偏移时,这种同步方式就无能为力了。重新同步的方式分为超前和滞后两种情况。

第一种

相位超前的情况如图相位超前时的重新同步 ,节点从总线的边沿跳变中,检测到它内部的时序比总线的时序相对超前 2Tq,这时控制器在下一个位时序中的 PBS1 段增加 2Tq 的时间长度,使得节点与总线时序重新同步。
在这里插入图片描述

第二种相位滞后的情况如图相位滞后时的重新同步 ,节点从总线的边沿跳变中,检测到它的时序比总线的时序相对滞后 2Tq,这时控制器在前一个位时序中的 PBS2 段减少 2Tq 的时间长度,获得同步。
在这里插入图片描述

BS1 和 PBS2 中增加或减少的这段时间长度被定义为“重新同步补偿宽度 SJW (reSynchronization Jump Width)”。一般来说 CAN 控制器会限定 SJW 的最大值,如限定了最大 SJW=3Tq 时,单次同步调整的时候不能增加或减少超过 3Tq 的时间长度,若有需要,控制器会通过多次小幅度调整来实现同步。当控制器设置的 SJW 极限值较大 时,可以吸收的误差加大 ,但通讯的速度会下降

CAN 的报文种类及结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7ZCJk5v-1629516487177)(./image_15.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOlPOS6q-1629516487178)(./image_16.png)]

• 帧起始

SOF 段 (Start Of Frame),译为帧起始,帧起始信号只有一个数据位,是一个显性电平,它用于通 知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步

• 仲裁段

当同时有两个报文被发送时,总线会根据仲裁段的内容决定哪个数据包能被传输,这也是它名称的由来。 仲裁段的内容主要为本数据帧的 ID 信息 (标识符),数据帧具有标准格式和扩展格式两种,区别 就在于 ID 信息的长度,标准格式的 ID 为 11 位,扩展格式的 ID 为 29 位,它在标准 ID 的基础上多出 18 位。在 CAN 协议中, ID 起着重要的作用,它决定着数据帧发送的优先级 ,也决定着其它 节点是否会接收这个数据帧。 CAN 协议不对挂载在它之上的节点分配优先级和地址,对总线的占有权是由信息的重要性决定的,即对于重要的信息,我们会给它打包上一个优先级高的 ID,使 它能够及时地发送出去。也正因为它这样的优先级分配原则,使得 CAN 的扩展性大大加强,在总线上增加或减少节点并不影响其它设备。报文的优先级,是通过对 ID 的仲裁来确定的

RTR 位 (Remote Transmission Request Bit)

译作远程传输请求位,它是用于区分数据帧和遥控 帧的 ,当它为显性电平时表示数据帧,隐性电平时表示遥控帧。

IDE 位 (Identifier Extension Bit)

译作标识符扩展位,它是用于区分标准格式与扩展格式 ,当它 为显性电平时表示标准格式,隐性电平时表示扩展格式。

SRR 位 (Substitute Remote Request Bit)

只存在于扩展格式,它用于替代标准格式中的 RTR 位。由于扩展帧中的 SRR 位为隐性位, RTR 在数据帧为显性位,所以在两个 ID 相同的标准格式报文 与扩展格式报文中,标准格式的优先级较高。

• 控制段

在控制段中的 r1 和 r0 为保留位,默认设置为显性位。它最主要的是 DLC 段 (Data Length Code),译为数据长度码,它由 4 个数据位组成,用于表示本报文中的数据段含有多少个字节, DLC 段表示的数字为 0~8。

• 数据段

数据段为数据帧的核心内容,它是节点要发送的原始信息,由 0~8 个字节组成,MSB 先行。

• CRC 段

为了保证报文的正确传输, CAN 的报文包含了一段 15 位的 CRC 校验码,一旦接收节点算出的 CRC 码跟接收到的 CRC 码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC 部分的计算一般由 CAN 控制器硬件完成,出错时的处理则由软件控制最大重发数。在 CRC 校验码之后,有一个 CRC 界定符,它为隐性位,主要作用是把 CRC 校验码与后面的 ACK 段间隔起来。

• ACK 段

ACK 段包括一个 ACK 槽位,和 ACK 界定符位。类似 I2C 总线,在 ACK 槽位中,发送节点发送 的是隐性位,而接收节点则在这一位中发送显性位以示应答。在 ACK 槽和帧结束之间由 ACK 界 定符间隔开。

• 帧结束

EOF 段 (End Of Frame),译为帧结束,帧结束段由发送节点发送的 7 个隐性位表示结束。

以上是些学习笔记,如有错误,望批评指正!
起初筛选器部分,在下也是似懂非懂,通过参考以下博主的文章,很快就理解,好文,敬佩!
CAN筛选器 详解

版权声明:本文为CSDN博主「小昭dedug」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43746325/article/details/119837354

STM32F429 CAN通信

CAN 是控制器局域网络 (Controller Area Network) 的简称,它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(ISO11519),是国际上应用最广泛的现场总线之一。

1、全双工允许数据在两个方向上同时传输;
2、半双工允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,实际上是切换的单工。

  • 有人认为CAN是“全双工”,也有人看是“半双工”。其实我认为都对吧,大家都知道,CAN节点包括CAN控制器和CAN收发器。只是站的角度不同,站在CAN控制器看问题,很明显是全双工,CAN控制器发送接收信号就有点类似串口发送TTL信号(这里不严谨,只是举个例子),全双工通信;换个角度,站在收发器看,虽然是用了两根信号线,但是它们是一对差分线发送差模信号,用两根线代表一个信号(显性信号和隐形信号),在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。

目录
一、STM32中CAN的初始化代码实现
二、CAN的筛选器使用及理解
三、CAN的物理层
四、CAN的协议层

一、STM32中CAN的初始化代码实现

1、GPIO的引脚初始化

void CAN_GPIO_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;   	
  
  RCC_AHB1PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE);
  GPIO_PinAFConfig(CAN_TX_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT);
  GPIO_PinAFConfig(CAN_RX_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT); 
	
  GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);
}

2、CAN控制器的初始化配置

static void CAN_Mode_Config(void)
{
	CAN_InitTypeDef        CAN_InitStructure;
    RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);

	/*CAN1寄存器的初始化*/
	CAN_DeInit(CAN1);
	CAN_StructInit(&CAN_InitStructure);

	/*CAN相关寄存器的配置*/
	CAN_InitStructure.CAN_TTCM=DISABLE;			   //MCR-TTCM  关闭时间触发通信模式使能
	CAN_InitStructure.CAN_ABOM=ENABLE;			   //MCR-ABOM  使能自动离线功能
	CAN_InitStructure.CAN_AWUM=ENABLE;			   //MCR-AWUM  使能自动唤醒模式
	CAN_InitStructure.CAN_NART=DISABLE;			   //MCR-NART  禁止报文自动重传,即使发送失败,不会再发
	CAN_InitStructure.CAN_RFLM=DISABLE;			   //MCR-RFLM  接受FIFO不锁定,接受数据溢出时,新报文会覆盖原报文
	CAN_InitStructure.CAN_TXFP=DISABLE;			   //MCR-TXFP  FIFO优先级由报文的标识符决定,不是时间顺序
	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;  //正常模式
    /*波特率配置,约定好双方收发一个数据位的速率*/    
    CAN_InitStructure.CAN_SJW=CAN_SJW_2tq;		   //BTR-SJW 重新同步跳跃宽度2个时间单元
	/* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) tq*(1+3+5)  */
	CAN_InitStructure.CAN_BS1=CAN_BS1_5tq;		   //BTR-TS1 时间段1 占用了5个时间单元 包括了PTS
	CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;		   //BTR-TS1 时间段 时间单元2 占用了5个时间单元	
	/* CAN Baudrate = 1 MBps (1MBps已是stm32中can的最高速速率) (CAN 的时钟总线 APB 1 = 45 MHz) */     
	CAN_InitStructure.CAN_Prescaler =5;		  //BTR-BRP 波特率分频器 波特率计算:45/(1+5+3)/5=1 Mbps
	CAN_Init(CANx, &CAN_InitStructure);
}

3、CAN的筛选器配置

void CAN_Filter_Config(void)
{
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
	/*CAN筛选器组0*/
	CAN_FilterInitStructure.CAN_FilterNumber=0;						
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;	//掩码模式
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;	//筛选器位宽 32位
	/* 验证码配置 */
	CAN_FilterInitStructure.CAN_FilterIdHigh= ((0x1314<<3)>>16)&0xffff//验证码32位中高16位 
	CAN_FilterInitStructure.CAN_FilterIdLow= ((0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //验证码32位中低16位  
	/*屏蔽码配置*/
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF;			//屏蔽码高16位,
	CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF;			//屏蔽码低16位,只允许一个通过
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;				//筛选器关联FIFO0
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;	//使能筛选器
	CAN_FilterInit(&CAN_FilterInitStructure);
	/*使能筛选器 接收中断*/
	CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}
/*通信发送报文的设置*/
void CAN_SetMsg(CanTxMsg *TxMessage)
{	  
  uint8_t ubCounter = 0;				 
  TxMessage->ExtId=0x1314;					 //扩展id
  TxMessage->IDE=CAN_ID_EXT;					 //扩展模式
  TxMessage->RTR=CAN_RTR_DATA;				 //发送 数据 类型
  TxMessage->DLC=8;							 //数据长度
	
	/*数据载入*/
	for (ubCounter = 0; ubCounter < 8; ubCounter++)
  {
    TxMessage->Data[ubCounter] = ubCounter;
  }

}

4、中断服务程序

void CAN_NVIC_Config(void)
{
   	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ;	   //CAN RX中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;			   
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
void CAN1_RX0_IRQHandler(void)
{
	/*从邮箱接收到报文*/
	CAN_Receive(CANx, CAN_FIFO0, &RxMessage);

	/* 验证id==0x1314 是否是扩展id 数据位是否8位*/ 
	if((RxMessage.ExtId==0x1314) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) )
	{
		flag = 1; 					      
	}
	else
	{
		flag = 0; 					   
	}
}

二、CAN的筛选器使用及理解

1、CAN筛选器的作用

CAN总线中,往往会有很多收发器进行发送多条数据,但是这些数据中只有一部分数据是你想要的,CPU却要处理很多无关紧要的数据,这无疑是给运行的效率带来的极大负担。所以筛选器起到了很大的作用,它可以为CPU过滤掉不要的数据,筛选出想要的数据,并把得到的数据存入到接收邮箱中。stm32通过识别过滤报文中的id,过滤方法有两种,列表模式和掩码模式。

在这里插入图片描述

每组筛选器包含 2 个 32 位的寄存器,分别为 CAN_FxR1 和 CAN_FxR2,它们用来存储要筛选的ID 或掩码。由可将一个32位寄存器分为两个16位寄存器,每组筛选器包含了4个16位寄存器。

//不管是32位还是16位宽的筛选器 
/*CAN_FxR1[31:16]*/
CAN_FilterInitStructure.CAN_FilterIdHigh 
/*CAN_FxR1[15:0]*/
CAN_FilterInitStructure.CAN_FilterIdLow
/*CAN_FxR2[31:16]*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh
/*CAN_FxR2[15:0]*/
CAN_FilterInitStructure.CAN_FilterMaskIdLow

2、列表模式

筛选器会比较列表中固定的id,要求报文 ID 与列表中的某一个标识符完全相同才可以接收。这种筛选方式有个很大的缺陷,stm32资源很有限,如果选择32位宽的筛选器,只能筛选两个报文id,如果是16位,可以筛选4个报文id。
在这里插入图片描述

  • 32位宽的筛选器 列表模式
void CAN_Filter_Config(void)
{
	uint32_t StdId[2] = {0x1234,0x1314};
	uint32_t ExdId = 0x80908890;
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
	/*CAN筛选器组0*/
	CAN_FilterInitStructure.CAN_FilterNumber=0;						
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList;	//列表模式
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;	//筛选器位宽 32位
	/* 验证码配置 */
	/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
	CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位 
	CAN_FilterInitStructure.CAN_FilterIdLow= 0|CAN_ID_STD|CAN_RTR_DATA; //验证码32位中低16位  

    /*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh= (ExdId<<3)>>16&0xFFFF;//屏蔽码高16位,
	/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
	CAN_FilterInitStructure.CAN_FilterMaskIdLow= (ExdId<<3)&0xFFFF|CAN_ID_EXT|CAN_RTR_DATA;	//屏蔽码低16位,只允许一个通过
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;				//筛选器关联FIFO0
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;	//使能筛选器
	CAN_FilterInit(&CAN_FilterInitStructure);
}
  • 16位宽的筛选器 列表模式
    在这里插入图片描述

由参考手册可知,配置16宽的筛选器,CAN_FilterIdHigh、CAN_FilterIdLow、CAN_FilterMaskIdHigh、CAN_FilterMaskIdLow这四个变量可以存放4个标准id,不用存放扩展id,扩展只能用32位的筛选器。

void CAN_Filter_Config(void)
{
	uint32_t StdId[2] = {0x1234,0x1314,0x1212,0x1313};
	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
	/*CAN筛选器组0*/
	CAN_FilterInitStructure.CAN_FilterNumber=0;						
	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList;	//列表模式
	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;	//筛选器位宽 32位
	/* 验证码配置 */
	/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*报文的标准id是11位,需要向左移5位传入16位的寄存器 STID[10:0]*/
	CAN_FilterInitStructure.CAN_FilterIdHigh=StdId[0]<<5;//验证码32位中高16位 
	CAN_FilterInitStructure.CAN_FilterIdLow= StdId[1]<<5; //验证码32位中低16位  

	/*32筛选器寄存器 包括 11位标准id 18位扩展id 1位IDE 1位RTR 1位0*/
	/*扩展id左移3位,腾出IDE、RTR和0,再向右移得出高16位*/
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh= StdId[2]<<5;//屏蔽码高16位,
	/*扩展id左移3位,腾出IDE、RTR和0,标记扩展和数据类型*/
	CAN_FilterInitStructure.CAN_FilterMaskIdLow= StdId[3]<<5;//屏蔽码低16位,只允许一个通过
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;	//筛选器关联FIFO0
	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//使能筛选器
	CAN_FilterInit(&CAN_FilterInitStructure);
}

3、掩码模式

为了解决列表模式下的,列表的容量资源有限的问题。出现了第二种方案,可以替代列表模式。相比列表模式,掩码模式可以筛选出更多的报文id。筛选过程中,需要经过屏蔽和对比两个操作(屏蔽码和验证码,验证码不唯一,看需求)。

为了能更好的理解,举个简单的例子,比如要筛查id出生年份为2000年的青年人(44200078):

0x04 0x04 0x02 0x00 0x00 0x00 0x07 0x08 -->经过屏蔽码(只需匹配第2个~第5个字节,进行相与操作)

0x00 0x00 0xff 0xff 0xff 0xff 0x00 0x00 -->得到

0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00 -->对比验证码

0x00 0x00 0x02 0x00 0x00 0x00 0x00 0x00
如果是筛查00后的年轻人(只需匹配第2个~第4个字节):

将屏蔽码改为0x00 0x00 0xff 0xff 0xff 0x00 0x00 0x00

可以识别通过的数值2000~2009,可以看出只要通过数值的多少,关键看屏蔽码。设得宽,则可以通过的多(所有位为0,则不过任何过滤操作,则谁都可以通过),设得窄,则通过的少(所有位设为1,则只有一个能通过)。
在这里插入图片描述

  • 32位宽的筛选器 掩码模式
 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;
  
}
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;
  
}
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;
  
}
  • 16位宽的筛选器 掩码模式
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;
  
}

三、CAN的物理层

与SPI、I2C等具有时钟信号的同步通讯方式不同,CAN通信并不是以时钟信号进行同步,CAN是一种 异步 通讯。只具有CAN_High和CAN_Low两条信号线 ,共同构成 一对差分信号线 的形式进行通讯。

CAN的物理层主要有两种

闭环总线网络

闭环总线通讯网络 中的 CAN 通讯网络是一种遵循ISO11898 标准的高速、短距离“闭环网络”,它的总线最大长度为** 40m** ,通信速度最高为 1Mbps,总线的两端各要求有一个“120 欧”的电阻(并联) .

开环总线网络

开环总线通讯网络 中的是遵循 ISO11519-2 标准的低速、远距离“开环网络”,它的最大传输距离为** 1km** ,最高通讯速率为 125kbps,两根总线是独立的、不形成闭环,要求每根总线上各串联有一个“2.2 千欧”的电阻 。

通讯节点

CAN总线上可以挂载多个通讯节点,CAN通讯协议不对节点进行地址编码 ,而是对数据内容进行编码。CAN通讯节点由一个控制器及CAN收发器组成。当 CAN 节点需要发送数据时,控制器把要发送的二进制编码通过 CAN_Tx 线发送到收发器(类似TTL信号),然后由收发器把这个普通的逻辑电平信号转化成差分信号,通过差分线 CAN_High 和 CAN_Low 线输出到 CAN 总线网络。

差分信号

差分信号又称差模信号,这两个信号线的振幅相等,相位相反通过两根信号线的电压差值来表示逻辑0和逻辑1.

  • 相对单信号线传输方式,具有的优点:抗干扰能力,有效抑制它对外部的电磁干扰。时许定位准确。

  • 由于差分信号线具有这些优点,所以在 USB 协议、 485 协议、以太网协议及 CAN 协议的物理层中,都使用了差分信号传输。

CAN协议中的差分信号

  • 当逻辑"0"(显性电平),CAN_High和CAN_Low的压差2.5v;当逻辑"1"(隐性电平),CAN_High和CAN_Low的压差0v。

  • 在CAN总线中,在同一时间,只要由输出显性电平,总线处于显性电平状态。类似I2c总线的“线与”特性。

  • 由于 CAN 总线协议的物理层只有 1 对差分线,在一个时刻只能表示一个信号 ,所以对通讯节点来说, CAN 通讯是半双工 的,收发数据需要分时进行。在 CAN 的通讯网络中,因为共用总线,在整个网络中同一时刻只能有一个通讯节点发送信号,其余的节点在该时刻都只能接收。

四、CAN的协议层

CAN 的波特率及位同步

CAN 属于异步通讯,节点间使用 约定好的波特率 进行通讯,特别地, CAN 还会使用“位同步”的方式来抗 干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。

位时序分解

为了实现位同步,CAN 协议把每一个数据位的时序分解成SS 段、PTS 段、 PBS1 段、 PBS2 段,这四段的长度加起来即为一个 CAN 数据位的长度。分解后最小的时间单位是 Tq,而一个完整的位由 8~25 个 Tq 组成.
在这里插入图片描述

该图中表示的 CAN 通讯信号每一个数据位的长度为 19Tq,其中 SS 段占 1Tq, PTS 段占 6Tq, PBS1 段占 5Tq, PBS2 段占 7Tq。信号的采样点位于 PBS1 段与 PBS2 段之间,通过控制各段的长度 ,可 以对采样点的位置进行偏移,以便准确地采样。

• SS 段 (SYNC SEG)

若通讯节点检测到总线上信号的跳变沿被包含在 SS 段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。 SS 段的大小固定为 1Tq

• PTS 段 (PROP SEG)

PTS 译为传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS 段的大小可以为 1~8Tq。

• PBS1 段 (PHASE SEG1)

PBS1 译为 相位缓冲段 ,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的 时候可以加长。 PBS1 段的初始大小可以为1~8Tq。

• PBS2 段 (PHASE SEG2)

PBS2 这是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。 PBS2 段的初始大小可以为 2~8Tq。

通讯的波特率

总线上的各个通讯节点只要约定好 1 个 Tq 的时间长度以及每一个数据位占据多少个 Tq,就可以 确定 CAN 通讯的波特率。

例如,假设上图中的 1Tq=1us,而每个数据位由 19 个 Tq 组成,则传输一位数据需要时间 T1bit =19us,从而每秒可以传输的数据位个数为:

1x106/19 = 52631.6 (bps)  

这个每秒可传输的数据位的个数即为通讯中的波特率。

同步过程分析

波特率只是约定了每个数据位的长度 ,数据同步还涉及到相位的细节,这个时候就需要用到数据 位内的 SS、 PTS、 PBS1 及 PBS2 段了。

CAN的数据同步分为硬同步和重新同步 。其中硬同步只是当存在“帧起始信号”时起作用,无法确保后续一连串的位时序都是同步的,而重新同步方式可解决该问题。

硬同步

若某个 CAN 节点通过总线发送数据时,它会发送一个表示通讯起始的信号 ,该信号是一个由高变低的下降沿 。而挂载到 CAN 总线上的通讯节点在不发送数据 时,会时刻检测总线上的信号。

节点以硬同步的方式调整,把自己的位时序中的 SS 段平移至总线出现下降沿的部分,获得同步,同步后采样点就可以采集得正确数据了。
在这里插入图片描述

重新同步

如果在一帧很长的数据内,节点信号与总线信号相位有偏移时,这种同步方式就无能为力了。重新同步的方式分为超前和滞后两种情况。

第一种

相位超前的情况如图相位超前时的重新同步 ,节点从总线的边沿跳变中,检测到它内部的时序比总线的时序相对超前 2Tq,这时控制器在下一个位时序中的 PBS1 段增加 2Tq 的时间长度,使得节点与总线时序重新同步。
在这里插入图片描述

第二种相位滞后的情况如图相位滞后时的重新同步 ,节点从总线的边沿跳变中,检测到它的时序比总线的时序相对滞后 2Tq,这时控制器在前一个位时序中的 PBS2 段减少 2Tq 的时间长度,获得同步。
在这里插入图片描述

BS1 和 PBS2 中增加或减少的这段时间长度被定义为“重新同步补偿宽度 SJW (reSynchronization Jump Width)”。一般来说 CAN 控制器会限定 SJW 的最大值,如限定了最大 SJW=3Tq 时,单次同步调整的时候不能增加或减少超过 3Tq 的时间长度,若有需要,控制器会通过多次小幅度调整来实现同步。当控制器设置的 SJW 极限值较大 时,可以吸收的误差加大 ,但通讯的速度会下降

CAN 的报文种类及结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K7ZCJk5v-1629516487177)(./image_15.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOlPOS6q-1629516487178)(./image_16.png)]

• 帧起始

SOF 段 (Start Of Frame),译为帧起始,帧起始信号只有一个数据位,是一个显性电平,它用于通 知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步

• 仲裁段

当同时有两个报文被发送时,总线会根据仲裁段的内容决定哪个数据包能被传输,这也是它名称的由来。 仲裁段的内容主要为本数据帧的 ID 信息 (标识符),数据帧具有标准格式和扩展格式两种,区别 就在于 ID 信息的长度,标准格式的 ID 为 11 位,扩展格式的 ID 为 29 位,它在标准 ID 的基础上多出 18 位。在 CAN 协议中, ID 起着重要的作用,它决定着数据帧发送的优先级 ,也决定着其它 节点是否会接收这个数据帧。 CAN 协议不对挂载在它之上的节点分配优先级和地址,对总线的占有权是由信息的重要性决定的,即对于重要的信息,我们会给它打包上一个优先级高的 ID,使 它能够及时地发送出去。也正因为它这样的优先级分配原则,使得 CAN 的扩展性大大加强,在总线上增加或减少节点并不影响其它设备。报文的优先级,是通过对 ID 的仲裁来确定的

RTR 位 (Remote Transmission Request Bit)

译作远程传输请求位,它是用于区分数据帧和遥控 帧的 ,当它为显性电平时表示数据帧,隐性电平时表示遥控帧。

IDE 位 (Identifier Extension Bit)

译作标识符扩展位,它是用于区分标准格式与扩展格式 ,当它 为显性电平时表示标准格式,隐性电平时表示扩展格式。

SRR 位 (Substitute Remote Request Bit)

只存在于扩展格式,它用于替代标准格式中的 RTR 位。由于扩展帧中的 SRR 位为隐性位, RTR 在数据帧为显性位,所以在两个 ID 相同的标准格式报文 与扩展格式报文中,标准格式的优先级较高。

• 控制段

在控制段中的 r1 和 r0 为保留位,默认设置为显性位。它最主要的是 DLC 段 (Data Length Code),译为数据长度码,它由 4 个数据位组成,用于表示本报文中的数据段含有多少个字节, DLC 段表示的数字为 0~8。

• 数据段

数据段为数据帧的核心内容,它是节点要发送的原始信息,由 0~8 个字节组成,MSB 先行。

• CRC 段

为了保证报文的正确传输, CAN 的报文包含了一段 15 位的 CRC 校验码,一旦接收节点算出的 CRC 码跟接收到的 CRC 码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC 部分的计算一般由 CAN 控制器硬件完成,出错时的处理则由软件控制最大重发数。在 CRC 校验码之后,有一个 CRC 界定符,它为隐性位,主要作用是把 CRC 校验码与后面的 ACK 段间隔起来。

• ACK 段

ACK 段包括一个 ACK 槽位,和 ACK 界定符位。类似 I2C 总线,在 ACK 槽位中,发送节点发送 的是隐性位,而接收节点则在这一位中发送显性位以示应答。在 ACK 槽和帧结束之间由 ACK 界 定符间隔开。

• 帧结束

EOF 段 (End Of Frame),译为帧结束,帧结束段由发送节点发送的 7 个隐性位表示结束。

以上是些学习笔记,如有错误,望批评指正!
起初筛选器部分,在下也是似懂非懂,通过参考以下博主的文章,很快就理解,好文,敬佩!
CAN筛选器 详解

版权声明:本文为CSDN博主「小昭dedug」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43746325/article/details/119837354

生成海报
点赞 0

小昭dedug

我还没有学会写个人说明!

暂无评论

相关推荐