【嵌入式】CAN通信协议及使用方法介绍


前言

控制器局域网总线(CAN,Controller Area Network)是一种用于实时应用的串行通讯协议总线,由德国博世公司在20世纪80年代开发提出,由于其高性能、由于CAN总线具有很高的实时性能和应用范围,从位速率最高可达1Mbps的高速网络到低成本多线路的50Kbps网络都可以任意搭配。因此,CAN己经在汽车业、航空业、工业控制、安全防护等领域中得到了广泛应用。常见的电机通常也采用CAN总线通信方式,本文主要介绍CAN总线基础知识,以及如何使用STM32实现CAN通信。

硬件:
STM32F103C8T6
CAN收发器(SN65HVD230模块)


一、CAN介绍

1. 通信标准及物理层

高速CAN通信标准:ISO11898,针对通信速率为125Kbps~1Mbps的高速通信标准,闭环总线。
低速CAN通信标准:ISO11519-2,针对通信速率10Kbps~125Kbps的低速通信标准,开环总线。

CAN总线通过差分线CAN_H与CAN_L实现信号传输,通过差分线的电位差来判断总线电平,物理信号及显隐性关系如上图所示,其中CAN通信的显性电平对应逻辑0隐性电平对应逻辑1显性电平具有更高优先级(“显性”具有“优先”的意味,只要有一个单元输出显性电平,总线上即为显性电平。并且,“隐性”具有“包容”的意味,只有所有的单元都输出隐性电平,总线上才为隐性电平)。CAN为异步通信,无时钟线。
显性电平:CAN_H为3.5V、CAN_L为1.5V,此时电压差是2V(ISO 11898)
在这里插入图片描述
隐性电平:CAN_H和CAN_L都为2.5V,此时电压差就是0V(ISO 11898)
在这里插入图片描述
CAN总线需要终端电阻进行阻抗匹配,高速CAN总线终端电阻典型值为120欧
通信设备连接到CAN总线上需要CAN控制器CAN收发器,以STM32F103C8T6为例,该芯片内部自带CAN控制器,使用STM32F103C8T6进行CAN总线通信时需要额外连接CAN收发器,CAN控制器和CAN接收器之间通过CAN_TX、CAN_RX连接(类似串口通信),收发器负责将控制器发送的电平信号转换为CAN总线上的差分信号,并将总线上的差分通过CAN_RX发送给CAN控制器。

2. CAN帧

CAN通信是通过在CAN总线上传输CAN帧实现的,CAN帧包括数据帧、遥控帧、错误帧、过载帧、帧间隔。功能如下
在这里插入图片描述数据帧和遥控帧有标准格式扩展格式两种格式。标准格式使用11位的标识符(Identifier,以下称 ID),扩展格式使用29位的 ID。CAN通信中设备间没有主从关系,每个节点设备都可以向总线上所有设备发送CAN帧,ID用于标识CAN帧的内容,设备可以通过ID判断是否需要接收该CAN帧。
数据帧最常用也最复杂,本文主要对其介绍。

(1)数据帧

(D显性电平,R隐性电平)
数据帧
帧起始:标准格式和拓展格式的帧起始相同,为1个显性位。
仲裁段:表示数据优先级的段,也就是标识符(ID),包括标准ID(11bit)和拓展ID(29bit),均不允许高7位都是隐性位。ID值越小优先级更高
控制段:r0、r1为保留位,必须全部以显性电平发送。DLC为数据长度码,表示数据段的字节长度,0~8字节。
数据段:0~8字节,从MSB开始输出。
CRC校验段:CRC 段是检查帧传输错误的帧。由15个位的 CRC 顺序和1个位的 CRC 界定符(用于分隔的位)构成。(CRC 顺序是根据多项式生成的 CRC 值,CRC 的计算范围包括帧起始、仲裁段、控制段、数据段。接收方以同样的算法计算 CRC 值并进行比较,不一致时会通报错误。)
ACK 段:ACK 段用来确认是否正常接收。由 ACK 槽(ACK Slot)和 ACK 界定符 2 个位构成。发送者在ACK段会连续写入2个隐性位,如果发送者在回读过程中监控到ACK SLOT 位为“显性”位,则说明接收者已正确接收;如果发送者在回读过程中监控到ACK SLOT 位为“隐性”位,则说明没有节点正确接收该报文,则发送者会检测到这个隐性位而知道发送失败,此条报文需要重发,触发错误帧机制。

帧结束:帧结束是表示该该帧的结束的段。由 7 个位的隐性位构成。

(2)遥控帧

接收单元向发送单元请求发送数据所用的帧,遥控帧由6个段组成。(无数据段)
在这里插入图片描述
遥控帧的RTR位为隐性位,用以区分数据段长度为0的数据帧。
遥控帧无数据段。
遥控帧的DLC表示其请求的数据长度。

(3)错误帧

在这里插入图片描述用于在接收和发送消息时检测出错误通知错误的帧。错误帧由错误标志和错误界定符构成。
错误标志包括主动错误标志和被动错误标志两种:主动错误标志:6个位的显性位。被动错误标志:6个位的隐性位。
错误界定符:错误界定符由 8个位的隐性位构成。

(4)过载帧

过载帧是用于接收单元通知其尚未完成接收准备的帧。过载帧由过载标志和过载界定符构成。
在这里插入图片描述
过载标志:6个位的显性位
过载界定符:8个位的隐性位。

(5)帧间隔

帧间隔是用于分隔数据帧和遥控帧的帧。数据帧和遥控帧可通过插入帧间隔将本帧与前面的任何帧(数据帧、遥控帧、错误帧、过载帧)分开。过载帧和错误帧前不能插入帧间隔。
在这里插入图片描述
间隔:3个位的隐性位。
总线空闲:隐性电平,无长度限制。
延迟传送:8个位的隐性位。只在处于被动错误状态的单元刚发送一个消息后的帧间隔中包含的段。

3.优先级

  1. 在总线空闲态,最先开始发送消息的单元获得发送权。
  2. 多个单元同时开始发送时,从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送,其他设备转为接收状态。
  3. 具有相同 ID 的数据帧和遥控帧在总线上竞争时,仲裁段的最后一位(RTR)为显性位的数据帧具有优先权,可继续发送。数据帧具有更高优先级。
  4. 标准格式 ID 与具有相同 ID 的遥控帧或者扩展格式的数据帧在总线上竞争时,标准格式的 RTR 位为显性位的具有优先权,可继续发送。标准ID具有更高优先级

4.位填充

位填充是为防止突发错误而设定的功能。当同样的电平持续 5 位时则添加一个位的反型数据。
在发送数据帧和遥控帧时,SOF~CRC 段间的数据,相同电平如果持续5位,在下一个位(第6个位则要插入1位与前5位反型的电平。
在接收数据帧和遥控帧时,SOF~CRC 段间的数据,相同电平如果持续5位,需要删除下一位(第6个位)再接收。如果这个第6个位的电平与前5位相同,将被视为错误并发送错误帧。

5.错误帧

  1. 位错误
  2. 填充错误
  3. CRC 错误
  4. 格式错误
  5. ACK 错误

6.位时序

CAN通信为异步通信,需要总线上的设备具有相同的数据传输速率,每秒钟发送的位数称为位速率。传输速率也可用波特率(每秒钟传输的码元个数)表示,CAN传输中位速率与波特率相等
一个位的数据传输过程可分为4段。

  1. 同步段(SS)
  2. 传播时间段(PTS)
  3. 相位缓冲段1(PBS1)
  4. 相位缓冲段2(PBS2)

最小的单位时间位Tq,以上各段均可有Tq个数定义。
在这里插入图片描述
在这里插入图片描述
PBS1结束处为采样点,判断该位的电平状态。通过SJW补偿信号传输中的速度误差。

7.同步

CAN作为异步通信,需要总线上的设备提前约定好波特率,并在收发信息时与总线上的其他设备进行时序同步,保证通信数据的正确。

(1)硬同步

接收单元在总线空闲状态检测出帧起始时进行的同步调整。在检测出边沿的地方不考虑 SJW 的值而认为是 SS 段。硬同步仅在出现起始段时进行。
在这里插入图片描述

(2)再同步

在接收过程中检测出总线上的电平变化时进行的同步调整。每当检测出边沿时,根据 SJW 值通过加长 PBS1 段,或缩短 PBS2 段,以调整同步。但如果发生了超出 SJW值的误差时,最大调整量不能超过 SJW 值。
在这里插入图片描述
调整同步的规则
(1) 1 个位中只进行一次同步调整。
(2) 只有当上次采样点的总线值和边沿后的总线值不同时,该边沿才能用于调整同步。
(3) 在总线空闲且存在隐性电平到显性电平的边沿时,则一定要进行硬件同步。
(4) 在总线非空闲时检测到的隐性电平到显性电平的边沿如果满足条件(1)和(2),将进行再同步。但还要满足下面条件。
(5) 发送单元观测到自身输出的显性电平有延迟时不进行再同步。
(6) 发送单元在帧起始到仲裁段有多个单元同时发送的情况下,对延迟边沿不进行再同步。


二、固件库编程

使用STM32F103C8T6进行程序开发,使用CAN1。

1. GPIO配置

CAN_RX与CAN_TX的默认复用端口为PA11和PA12。
在这里插入图片描述
也可将PB8、PB9重映射为CAN_RX与CAN_TX。
在这里插入图片描述

static void CAN_GPIO_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

以上程序使用PA11和PA12,因此没有重映射,若使用PB8、PB9则需要添加重映射程序

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);

注意两个GPIO模式不同。

2. CAN配置

static void CAN_Config(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
	
	// 配置CAN
	CAN_InitTypeDef CAN_InitStructure;
	CAN_StructInit(&CAN_InitStructure);
	CAN_InitStructure.CAN_TTCM = DISABLE;
	CAN_InitStructure.CAN_ABOM = DISABLE;
	CAN_InitStructure.CAN_AWUM = DISABLE;
	CAN_InitStructure.CAN_NART = DISABLE;
	CAN_InitStructure.CAN_RFLM = DISABLE;
	CAN_InitStructure.CAN_TXFP = DISABLE;
	CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
	
	// 配置波特率
	// 36M/(1+3+5)/4=1M
	CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;		// 同步跳跃宽度(位时序延长或缩短的时间单元,不影响波特率)
	CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;		// 时间段1
	CAN_InitStructure.CAN_BS2 = CAN_BS2_5tq;		// 时间段2
	CAN_InitStructure.CAN_Prescaler = 4;

	uint8_t state;
	state = CAN_Init(CAN_x, &CAN_InitStructure);
	while(!state);
	
	// 配置CAN报文过滤器
	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	CAN_FilterInitStructure.CAN_FilterNumber = 0;					     	// 选择过滤器0-13
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;		 	// 掩码模式 or 列表模式
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;	 	// 过滤器位数 16bit or 32bit
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;				   	 	// 过滤器标识高16bit
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;					 	// 过滤器标识低16bit
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;				 	// 过滤器掩码高16bit 0不关心 1必须匹配
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;				 	// 过滤器掩码低16bit 0不关心 1必须匹配
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;	// 选择使用该过滤器的fifo
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;					// 使能过滤器
	
	CAN_FilterInit(&CAN_FilterInitStructure);
}

可使用回环模式进行单设备发送、接收调试
CAN通信的波特率计算和过滤器配置详见参考手册。

3.发送数据帧

CanTxMsg TxMessage;							// CAN报文结构体
TxMessage.StdId = 0x0f;						// 标准ID 11bit
TxMessage.ExtId = 0x01;						// 拓展ID 29bit
TxMessage.RTR = CAN_RTR_Data;				// 帧类型 数据帧 or 远程帧
TxMessage.IDE = CAN_Id_Standard;			// ID类型 标准ID or 拓展ID
TxMessage.DLC = 4;							// 数据段长度 0-8(字节)
TxMessage.Data[0] = 0x04;					// 数据段 8字节数组
TxMessage.Data[1] = 0x0f;
TxMessage.Data[2] = 0x01;
TxMessage.Data[3] = 0x00;

CAN_Transmit(CAN1, &TxMessage);

4.接收中断

中断配置

static void CAN_IT_Config(void)
{
	while(CAN_MessagePending(CAN_x, CAN_FIFO0))
	{
		CAN_FIFORelease(CAN_x, CAN_FIFO0);		// 清空FIFO
	}
	
	// 接收FIFO中报文个数非0时产生中断
	CAN_ITConfig(CAN_x, CAN_IT_FMP0, ENABLE);
}

中断服务函数

void USB_LP_CAN1_RX0_IRQHandler(void)
{
	if(CAN_GetFlagStatus(CAN1, CAN_FLAG_FMP0))
	{
		CanRxMsg RxMessage;
		CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
		for(int i=0;i<RxMessage.DLC;i++)
			Usart_SendByte(USART1, RxMessage.Data[i]);		// CAN数据通过串口发送至上位机
	}
}

三、实验

Normal模式,使用示波器显示CAN差分波形:
在这里插入图片描述
回环模式验证收发:
在这里插入图片描述在这里插入图片描述


四、总结

CAN通信为串行异步通信,传输距离远,需要信号线数量少,但通信协议复杂,复杂的通信协议保证了CAN通信的可靠性和有效性,在学习和使用中应注意细节,全面理解,上述内容仅对本人所了解内容进行介绍,持续补充。

参考:CAN入门书

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值