1、CAN总线的介绍
CAN总线简称 — 控制器局域网络,由德国BOSCH公司开发,是一种串行的差分总线,并且这种差分总线只传递数据信息。
CAN总线已经是国际上的一种通信标准(ISO11519),它具有高可靠性、良好的错误检测能力、总线仲裁等优良特点,被广泛应用在汽车控制系统、环境恶劣场所、电磁辐射强、干扰性大等场所中。
CAN是一种异步的通信方式,它的通讯不需要时钟进行同步,线路上只有CAN_H、CAN_L两根通信线,数据以差分信号的方式进行传输。
2、CAN的通信网络
2.1、闭环总线网络
CAN的闭环通信网络如下:
闭环通信网络的特点如下:
1)遵照标准为 ISO11898;
2)最高速度可达1Mbps,总线最大长度为40m;
3)总线的两端要求各有一个120欧姆的电阻。
2.2、开环总线网络
CAN的开环通信网络如下:
开环通信网络的特点如下:
1)遵循ISO11519-2标准;
2)最高的通信速度为125Kbps,是一种低速通信的连接方式;
3)最大的通信距离可达1Km;
4)两根总线独立,且要求每根总线上要串联一个2.2K欧姆的电阻。
3、CAN的通信速率
按照 ISO11898 标准中所规定的,CAN总线的通讯波特率最高为 1Mbps。大多数项目常用的CAN总线波特率有125Kbits/s,250Kbits/s,500Kbits/s,1MKbits/s等。
CAN总线的传输距离与速率的对照表:
4、CAN的差分信号
CAN总线的数据传输使用的是差分信号的方式进行的。这两根线中的信号振幅相等,相位相反,逻辑1 和 逻辑0 通过两根信号线的电压差值表示出来。
在CAN的应用中,逻辑1也称为隐性电平,逻辑0也称为显性电平。
比如,以高速CAN为例:
逻辑1:CAN_H、CAN_L的电压均为2.5V,电压差为VH - VL = 0V。
逻辑0:CAN_H电压为3.5V,CAN_L电压为1.5V,电压差为 VH - VL = 2V。
示意图如下:
两根信号线的电压值根据标准的不同是有所差异的,如下表所示:
注意:CAN总线上,在同一个时刻只能处于隐性电平(逻辑1)或者显性电平(逻辑0)中的一个状态,且显性电平相比隐性电平具有优先权。比如:CAN总线上有两个节点,在同一个时刻,一个输出隐性电平,一个输出显性电平,这个时候总线上是显性电平状态。
5、CAN的位同步 & 数据同步
5.1、CAN 的位同步
由于CAN总线是异步通信的,它不像IIC、SPI这种同步通行方式一样具有同步时钟,所以总线网络中的节点就需要约定好通信的波特率。
在CAN中使用了 “位同步” 的方式来抵抗干扰、吸收误差,实现对总线电平信号的正常采样,确保通讯正常进行。
同时,这个位同步也是用于调节通信过程中的采样点的。
CAN实现位同步的过程中,会把每一个数据位的时序进行分解,会分解成SS段、PTS段、PBS1段、PBS2段,这四个段加起来就是一个CAN的数据位长度。分解后的最小时间单位为Tq。它们分别如下:
同步段(SS段):该段的大小固定为1Tq。
当总线上的节点在SS段的范围内检测到总线上信号发生了跳变,就认为该节点与总线的时序是同步的。当节点与总线同步时,在采样点采集到的总线电平即可以确认为该位的电平。
传播时间段(PTS段):该段的大小为 1Tq ~ 8Tq。
这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。
相位缓冲段1(PBS1):该段的大小为 1Tq ~ 8Tq。
主要用来补偿边沿阶段的误差,它的时间长度在重新同步的时候可以加长。
相位缓冲段2(PBS2):该段的大小为 1Tq ~ 8Tq。
是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。
为了方便说明,我就直接用高低电平表示CAN的逻辑1 和 逻辑0 信号,示意图如下:
图:CAN数据位分解图
上图是对CAN通信中的逻辑0进行分解的说明示意图。图中就表示每个数据位的长度为19Tq(SS 段占 1Tq,PTS 段占 6Tq,PBS1 段占 5Tq,PBS2 段占 7Tq),信号的采样点位于 PBS1 段与 PBS2 段之间,通过控制各段的长度,可以改变采样点的位置。
5.2、CAN 的数据同步
CAN的数据同步使用到SS段、PTS段、PBS1段、PBS2段,并且数据的同步分为硬同步、重新同步。
(1)硬同步
CAN节点希望通过总线发送数据时,会发送一个由高变低的下降沿作为通讯的起始信号;而节点不发送数据时会时刻检测总线上的信号。
一个硬同步的过程如下图示例:
从上图中可以看到当总线上出现了帧起始信号时,某一个节点检测到总线的帧起始信号不在该节点内部时序的 SS 段范围,所以就可以判断它自己的内部时序与总线不同步,因而这种状态下的采样点采集得的数据是不正确的。所以节点以硬同步的方式调整,把自己的位时序中的 SS 段平移至总线出现下降沿的部分,获得同步,同步后采样点就可以采集得正确数据了。
(2)重新同步
硬同步是存在帧起始信号时才起作用,但是假如是一帧很长的数据,这一长串数据内不再出现帧起始信号,那么在节点信号与总线信号出现相位偏移时,用硬同步方式就行不通了。
所以就有了重新同步的方式,这种方式利用普通数据位的电平的高至低的跳变沿来同步同样都是在 SS 段中进行检测,从而实现同步的目的。
注意:同步的目的都是为了能在节点内的 SS 段把跳变沿包含起来。
重新同步可以细分为两种同步方式:相位超前重新同步和相位滞后重新同步。这两种方式以总线跳变沿与 SS 段的相对位置进行区分的。
1)相位超前重新同步
如下图所示:
上图中可以看到,节点的SS段相对于总线的边沿跳变往前了两个Tq的时间,即节点的时序比它检测的总线时序相对超前了2Tq。
解决这个问题可以在下一个位时序中的PBS1段增加2Tq的时间长度,使得节点与总线时序重新同步,如下图:
2)相位滞后重新同步
如下图所示:
上图中可以看到,节点的SS段相对于总线的边沿跳变延迟了两个Tq的时间,即节点的时序比它检测的总线时序相对落后了2Tq。
解决这个问题可以在下一个位时序中的PBS2段减少2Tq的时间长度,使得节点与总线时序重新同步,如下图:
6、CAN的波特率
在CAN总线的通讯中,各个通讯的节点只要确定了1个Tq的时间长度以及每个数据位占多少个Tq,由此就可以算出CAN通讯的波特率。
在CAN中,波特率也就只是约定了每个数据位的长度而已。
比如,上图(图:CAN数据位分解图)中,假设1Tq = 1us,每个数据位由19个Tq组成,则每传输1Bit数据需要的时间为:19Tq = 19us。
则一秒可以传输的数据位(bit)为:
1000000 / 19 = 52.6316K(bps)
注意:波特率指的是每秒可以传输的数据位(bit)的个数。
7、CAN的帧种类和用途
CAN的帧种类有如下几种:
特别地,数据帧和遥控帧有标准格式和扩展格式两种格式。
标准格式 — 有 11 个位的标识符 ID
扩展格式 — 有 29 个位的 标识符 ID
7.1、CAN总线的数据帧
数据帧一般由 7 个段构成,即:
这几个段的作用如下:
1) 帧起始。表示数据帧开始的段。
2) 仲裁段。表示该帧优先级的段,即ID信息。
3) 控制段。表示数据的字节数及保留位的段。
4) 数据段。数据的内容,一帧可发送 0~8 个字节的数据。
5) CRC 段。检查帧的传输错误的段。
6) ACK 段。表示确认正常接收的段。
7) 帧结束。表示数据帧结束的段。
CAN的数据帧有标准格式和扩展格式两种,这两者不同的地方如下图如下:
数据帧的标准格式和扩展格式两者的不同之处主要在于仲裁段中,控制段中有细微的不同。
1)标准格式的ID是11位。
从ID28到ID18一次发送,禁止高7位都为隐形(禁止ID=1111111XXXX)。
2)扩展格式的ID有29个位。
基本ID从ID28到ID18,扩展ID由ID17到ID0表示。基本ID和标准格式的ID相同。
RTR位:帧选择位
0:数据帧 1:远程帧
IDE位:
0:标准标识符 1:扩展标识符
SRR位:
远程请求位,为隐形位,代替了标准帧中的RTR位。
r0、r1位:
保留位,发送时必须全部是显性电平,但是接收时可以是显性、隐形、任意组合的电平。
7.1.1、起始帧
CAN的数据帧和远程帧开始发送时,是需要发送一个开始的标志的,称之为帧起始。它是一个显性位(逻辑0)。
一个CAN节点只有在总线处于空闲状态时才可以发送起始帧。
7.1.2、仲裁段
起始帧发送完成之后,紧接着发送出的就是仲裁段的数据。
CAN2.0A的仲裁场一共有12位,如下图所示。
在这12位中,前11位表示的是11位标识符,第12位是远程发送请求RTR。在数据帧中RTR为显性,在远程帧中RTR为隐性。如果相同标识符的数据帧与远程帧发生冲突,数据帧优先。
CAN2.0B的仲裁场一共有32位,它在CAN2.0A的基础上,将CAN ID扩展到了29位,并且向CAN2.0A兼容,如下图所示。
7.1.3、控制段
仲裁之段后便是控制场。控制场的头两位为保留位(R1、R0),为隐性。
后面是数据长度代码DLC(Data Length Code)。数据长度代码指示了数据场中有效的字节个数。DLC的长度为4位二进制数,虽然可以最大范围可以表示到0~15,但是由于CAN总线的数据域长度最大为8个字节,所以DLC的大小可以认为是最大为8。
控制场示意图如下:
详细示意图如下:
7.1.4、数据段
控制场传输完成之后,紧接着传输的就是数据场了,数据场里面传输的就是我们想要传输的数据,长度为8个字节,如下图所示。
详细示意图如下:
7.1.5、CRC段
CAN数据每次发送的时候,都会将CAN数据帧里面的有效数据进行CRC校验,并且将最终生成的CRC信息一起包含在数据帧中的CRC域内进行发送。接收端接收到一帧CAN总线数据后,会将接收到的数据进行CRC计算,并且同发出端的CRC校验码进行比较,如果相同,则代表当前接收到的信息是正确的。
CRC段的示意如下:
CRC校验域的格式如下图所示。
在经典CAN中,使用15位CRC。在硬件可使用移位和异或运算完成CRC的计算。CAN总线中使用的几种CRC生成多项式公式如下图所示。
7.1.6、应答段
CAN总线的应答场长度为2个位,包含应答间隙(ACK SLOT)和应答界定符(ACK DELIMITER)。
在应答场里,发送站发送两个“隐性”位。当接收器正确地接收到有效的报文,接收器就会在应答间隙(ACK SLOT)期间(发送ACK信号)向发送器发送一“显性”的位以示应答。
应答间隙:所有接收到匹配CRC序列(CRC SEQUENCE)的站会在应答间隙(ACK SLOT)期间用以“显性”的位写入发送器的“隐性”位来作出回答。
ACK界定符:ACK界定符是ACK场的第二个位,并且是一个必须为“隐性”的位。因此,应答间隙(ACK SLOT)被两个“隐性”的位所包围,也就是CRC界定符(CRC DELIMITER)和ACK界定符(ACK DELIMITER)。
CAN总线的应答场如下图所示:
详细如下图所示:
7.1.7、帧结束
应答段之后的7位数据是CAN总线的结束帧,是为7位连续的1(隐性电平),如下图所示。
8、CAN通信的数据段示例
下图可以看到一个CAN数据帧排列的所有数据域。下图展示了一个完整的数据段的通信过程,如下图:
9、STM32的CAN的配置示例
这里以STM32F407的CAN外设的配置为例。代码如下:
配置CAN1,并配置使用CAN1中断接收CAN消息:
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); //使能CAN1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA11,PA12
GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_CAN1); //GPIOA11复用为CAN1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_CAN1); //GPIOA12复用为CAN1
CAN_InitStructure.CAN_TTCM = DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM = DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART = ENABLE; //启动禁止自动重传,即不自动重传
CAN_InitStructure.CAN_RFLM = DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP = DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //模式设置
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
CAN_InitStructure.CAN_BS1 = CAN_BS1_7tq; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2 = CAN_BS2_6tq; //Tbs2范围CAN_BS2_1tq ~CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler = 6; //分频系数(Fdiv)为brp+1 波特率=Fpclk1/((BS1+1+BS2+1+1)*Prescaler),即42M/((6+7+1)*6)=500Kbps
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
CAN_FilterInitStructure.CAN_FilterNumber = 0; //过滤器0
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //32位
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; ////32位ID
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; //32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);