车载Android开发的秘密--搞懂CAN通信

1,431 阅读18分钟

全文五千字,码字不易求个赞赞

我以前写了一篇搞懂串口通信,一经发出,就获得好多人观看收藏和点赞。最近工作用到了CAN通信,我就把CAN通信总结一下。 学习CAN通信之前,我在搜索学习资料的时候,大部分都介绍CAN的历史,等等,什么车载应用估计是培训机构的文章,读完感觉没啥用。写代码和硬件沟通还是无从下手。我先讲通信原理,再讲协议。

1、CAN简介

CAN总线(Controller Area Network Bus)控制器局域网总线 CAN总线是构建的一种局域网网络。每个挂载在CAN总线的设备,都可以利用这个局域网去发送自己的消息,也可以接收局域网的各种消息。每个设备都是平等的,都在共享这个局域网的通信资源。这个就是CAN总线的设计理念。

CAN总线是由BOSCH公司开发的一种简介易用,传输速度快,易扩展,可靠性高的串行通信总线,广泛应用于汽车,嵌入式,工业控制等领域。CAN开始之初是为了汽车领域而研究的,对其可靠性和稳定性要求都是非常高的。 CAN总线特征

  • 两根通信线(CAN_Hight,CAN_Low)线路少无需共地只需两根线
  • 差分信号通信,差分信号的特点。抗干扰能力强。线路如果产生干扰,一般两根线都会受到干扰。但是两根线的电压差值是不变的。所以差分信号会极大的避免干扰
  • 高速CAN(ISO11898):125K-1Mbps <40m
  • 低速CAN(ISO11519):10k-125kbps <1km
  • 异步,无需时钟线,通信速率由设备各自约定
  • 半双工,可挂载多设备,多设备同时发送数据时,通过仲裁决定发送顺序
  • 11位(标准格式)/29位报文ID(扩展格式),用于区分消息功能,同时决定优先级
  • 可配置1-8字节的有效载荷
  • 可实现广播式和请求式两种传输方式
  • 应答、CRC校验、位填充,位同步,错误处理等特性。体现了严谨和安全

image.png

废话说完,进入正题

2、CAN通信原理

在计算机领域中,我们任何数据通信其实传输的是0和1的信号,无论是串口,还是网线TCP,其底层都是传输的0和1的信号。 那在CAN中是怎么传递这些信号的呢。

2.1、CAN物理接线

image.png

image.png

  • 每个设备通过CAN收发器挂载在CAN总线网络上
  • CAN控制器引出TX和RX与CAN收发器相连,CAN收发器引出CAN_H和CANL分别与总线CAN_H和CAN_LOW相连
  • 高速CAN使用闭环网络,CAN_H和CAN_L两端添加120Ω的终端电阻
  • 低速CAN使用开环网络,CAN_H和CAN_L其中一端添加2.2kΩ的终端电阻

CAN收发器:他是个什么东西呢,是个芯片,主要实现电平转换,输出驱动和输入采样几个功能。也就是用来采集和输出电平信号的。 CAN控制器:这个就是我们Android程序员需要操作的东西了,因为信号要转成可视化的数据才行,转成byte数组,数组转成字符串啊,数字我们来显示。然后我们要发送的消息,也可能是数字,字符串之类的。我们需要把我们的指令传给收发器,收发器再转成电平信号,传给其他设备。

image.png

高速CAN总线,没有设备进行通讯的时候,终端电阻会收紧,终端电阻就像一根弹簧一样,收紧状态会使,CAN_H和CAN_L的电压相同,其差值是0,代表1信号,如果CAN设备想发送信号1,终端电阻就会张开,使其两边的电压差增大,表示其0状态。如果CAN设备想发送1时,无需对总线进行任何操作,CAN总线默认就是收紧状态就是1。

低速CAN的原理,有兴趣的同学可以去自行搜索资料学习一下。我们这里就不做介绍了。只要知道有这玩意就可以了。

2.2、 CAN电平标准

CAN总线采用差分信号,即两线电压差(Vcan_h -Vcan_l)传输数据位

高速CAN规定

  • 电压差为0V时,表示逻辑1(隐形电平)
  • 电压差为2V时,表示逻辑0(显性电平)

image.png

显示电平,隐形电平表示的线路实际状态,因为总线默认状态时收紧状态,不需要设备干预,所以收紧状态为隐形电平,而张开状态需要设备干预,所以定义为显性电平。 在与逻辑电路的对应上,电路约定俗成的习惯,默认状态为高电平1 ,所以默认的隐形电平就和逻辑1绑定了,显性电平就和0绑定了,显性电平和隐形电平同时出现时,总线会表现出显性电平状态,这样能对应电路中 0 强于1 的规定。

我们分析帧时序时,用逻辑电平。

2.3、收发器原理

收发器的工作原理,有点复杂,而且还需要对电路有一定了解,感觉有点复杂。我们就不介绍了,有兴趣的同学可以自行学习。 CAN收发器 TJA1050(高速CAN)

image.png

2.4、CAN物理层特性

image.png

2.5、CAN通讯思路总结

其通讯思路,CAN总线好比一个大灯, CAN设备分别是小明,小红,和小华, 这三个人时刻关注灯的状态。小明想发送1101,他就会在四个时序分别 灭灯,灭灯,量灯,灭灯。小红和小华会根据灯的状态解析出来1101。我觉得这样比较容易理解。至于他们都想发消息怎么办,谁先发谁后发,这就到了我们通讯协议环节

3、CAN总线帧格式

帧格式规定了通讯协议,就是规定传输的0和1代表什么意思。

帧类型用途
数据帧发送设备主动发送数据 (广播式)
遥控帧接收设备主动请求数据 (请求式)
错误帧某个设备检测出错误时向其他设备通知错误
过载帧接收设备通知其尚未做好接收准备
帧间隔用于将数据帧及遥控帧与前面的帧分离开

3.1 数据帧

image.png

我们先看一下图例 D Dominat 显性电平, R Recessive 隐性电平 灰色部分D只能发送显性电平0,紫色部分D/R 可以发送显示电平或者隐性电平,白色部分代表R只能发送隐性电平。

ACK位槽 这个时应答位特有的,发送方必须发隐形电平,接收方发显示电平

图里边的数字,代表此段时序所占的位数,比如1位,11位,18位。

然后我们分析一下标准数据帧。

3.1.1 SOF(帧起始)

我们发送数据帧之前,总线必须处在空闲状态,空闲状态总线时隐性电平1,随后数据帧开始,SOF(帧起始)灰色部分,显示电平0,帧起始的作用是打破宁静。因为空闲时隐性1,所有设备都不去碰总线,你想要发送数据,第一位必须张开总线,发送显性0,如果你发送隐性1,那就会与前边的隐性状态融为一体。没人知道你开始发数据了。还有一个作用是告诉接收方,如果后边我再释放总线,总线不是空闲状态,而是我发送的就是1

3.1.2 Identifier(ID)报文ID

帧起始后边就是报文ID,标准格式是11位,

报文ID的功能,可以表示后边数据段的功能,因为总线上各种报文信息都有,如果不以ID加以区分,消息就会混乱,不知道哪个是那个了。

报文ID的第二个功能,就是用来区分优先级,当多个设备同时发送时,根据仲裁规则,ID小的报文优先发送。ID大的报文等待下一次总线空闲再重试发送。

不同功能的数据帧,其ID都不同,否则两个设备同时发相同ID的数据帧,仲裁规则就无法谁先谁后发送了。

3.1.3 RTR (远程请求标志位)

用来区分遥控帧和数据帧的标志位,数据帧必须为显性0,遥控帧必须为隐性1,我们分析的数据帧,所以这一位必须是0

Identifier 和 RTR,这两段加起来叫做仲裁段,我们主要是靠ID仲裁,为啥把RTR加进来呢?是因为遥控帧和数据帧的ID是可以相同的,然后相同ID的数据帧和遥控帧,数据帧优先发送。

3.1.4 IDE (ID扩展标志位)

这一位是ID扩展标志位,作用用来区分这个数据帧是标准帧,还是扩展帧。标准格式,位固定显性电平0,扩展格式为隐性电平1,

3.1.5 r0(保留位)目前没有用到

3.1.6 DLC 数据段的长度,数据段的字节数

3.1.7 Data 数据段,数据段长度占的位数,要是8的倍数,也可以是0

3.1.8 CRC Sequence CRC校验校验符 占15位

它会对前边所有的数据位进行CRC算法计算,从SOF到Data 这些所有数据位计算得到一个校验码,放到里面,接收方接收到校验码之后,也会调用CRC算法计算,看校验码是否一致。以判断传输是否有误

3.1.9 CRC界定符 必须为隐性电平1

3.1.10 ACK槽

发送方可以根据ACK槽,知道数据是否被接收,可以用来做重发机制。 发送方会在这一位释放总线,然后会读这一位,如果这一位被拉高,置为显性0,说明数据被接收了,发送方就可以安心了。 如果发送方回读还是隐性1,那么就可以安排重发,或者不用管。

3.1.11 ACK界定符

他的作用是接收方接到消息后ACK拉高之后,要交出控制权。所以要用一个界定符,让接收方发送隐性1.

3.1.12EOF 帧结束,七个隐性1 代表帧结束

这个数据段波形,是接收方和发送方一起完成的,就是帧起始开始,接收方已经开始接收了,并不是,发送方发完这一帧,接收方才开始接收的。 理解这一句话,上边的才好理解。

3.1.13 扩展帧

扩展帧出现的原因,就是标准格式的ID不够用了,需要加一些,而且扩展格式,也要考虑必须对标准格式的兼容。 我们分析完标准帧,扩展帧就相对于来说,更容易了。 扩展帧的RTR挪到了扩展ID后边,原来的RTR 变为了SRR,现在也没有作用, 必须搞成隐性1,然后后边就是IDE,扩展帧标志位,如果是显性0,则后续按照标准帧格式进行解析,如果是隐性1,按扩展帧解析,再往后就是18位扩展id。扩展格式rtr 后边的 r1,和r0 是保留位必须显性0,后面的格式就是和标准数据帧一样了。

3.2 遥控帧

image.png 遥控帧无数据段,RTR位隐性电平1,其他部分与数据帧相同。 用于数据不是频繁更新的场景,和数据帧搭配使用。

3.3 错误帧

总线上所有设备多会监督总线的数据,一旦发现位错误或者填充错误或CRC错误或格式错误,或者应答错误,这些设备便会发出错误帧来破坏数据,同时终止当前的发送设备

image.png

3.4 过载帧

当接收方收到大量数据无法处理时,其可以发出过载帧,延缓发送方的数据发送,以平衡总线负载,避免数据丢失

image.png

3.5 帧间隔

将数据帧与远程帧与前面的帧分离开

image.png

错误帧,过载帧 和帧间隔。在设计的时候是非常复杂的,建议初学者了解就可以。我们学会收发数据即可。

4、位填充

位填充规则:发送方每发送五个相同电平后,自动追加一个相反电平的填充位, 接收方检测到填充位时,会自动移除填充位,恢复原始数据

image.png 如果位填充之后,和后边的四位相同,则会再填充一位。填充位与后边的数据位合并,之后再用填充规则进行位填充。

位填充作用:

  • 增加波形的定时信息,利于接收方执行再同步,防止波形长时间无变化,导致接收方不能精确掌握数据采样时机。如果长时间相同的电平,时钟稍有偏差,就会接收出错。
  • 将正常数据流与错误帧和过载帧区分开,标志错误帧和过载帧的特异性。(都有连续六位相同的电平)
  • 保持CAN总线在发送正常数据流时的活跃状态,防止被误认为总线空闲(如果你要发送的数据是一大串1)

5、接收方数据采样

  • CAN总线没有时钟线,总线上的所有设备通过约定波特率的方式确定每一个数据位的时长
  • 发送方以约定的位时长,每 隔固定时间输出一个数据位
  • 接收方以约定的位时长每隔固定时间采样总线的电平,输入一个数据位
  • 理想状态下,接收方能依次采样到发送方发出的每个数据位,且采样点位于数据位中心附近

image.png 上面是理想状态啊,实际操作肯定会遇到问题的。

接收方数据采样遇到的问题

接收方以约定的位时长进行采样,但是采样点没有对齐数据位中心附近

image.png 这里就涉及到数据同步问题,如何让采样点对齐数据位中心呢? 如果在跳变沿采样,这个数据是1还是0,有点说不清了,所以上图采的数据是有问题的,如果没对齐,我们参考第一个跳变沿,采样时间往后延半个数据位的时间,然后后边的再用数据位时间间隔进行采样,这样就对齐了。这就涉及到硬同步了。

接收方刚开始采样正确,但是时钟有误差,随着误差累积,采样点逐渐偏离。

image.png

这个问题,如果采样时间过慢,我们可以在偏差不是很大的时候,减少一次采样间隔时间,这样对于后边所有的采样时间,就会往前提一点。如果过快,相反,我们增加一次采样间隔时间,后边所有采样的时间都会往后移一点。这就是用到了再同步的概念 通过这两个问题,我们也知道了位填充的重要性,如果波形长时间,不变,我们就无法进行同步,采集的数据就会有问题。 我们了解个大概就可以了,如果你想做硬件,可以继续再研究一下,硬同步和再同步。

6、 仲裁规则

CAN总线只有一对差分信号线,同一时间只能有一个设备操作总线发送数据,若多个设备有同时发送数据的需求,该如何分配总线资源?

思路: 指定资源分配规则,一次满足多个设备的发送需求,确保同一时间只有一个设备操作总线。

规则一 先占先得

先占先得,如果设备一已经开始发送了,发送的途中,第二个设备想发送数据,禁止发送。

  • 若当前已经有设备正在操作总线发送数据帧/遥控帧,则其他任何设备不能再同时发送数据帧/遥控帧(可以发送错误帧/过载帧,破坏当前数据)
  • 任何设备检测到连续11个隐性电平,即认为总线空闲,只有在总线空闲时,设备才能发送数据帧
  • 一旦有设备正在发送数据帧/遥控帧,进行了位填充总线就会变为活跃状态,必然不会出现连续11个隐性电平,其他设备自然也不会破坏当前发送。
  • 若总线活跃状态其他设备有发送需求,则需要等待总线变为空闲,才能执行发送需求。

但是如果,在开始的时候,两个设备都想发送数据呢。都没开始呢?

规则二 非破坏性仲裁

  • 若多个设备的发送需求同时到来或因等待而同时到来,则CAN总线协议会根据ID号(仲裁段 )进行非破坏性仲裁,ID号小的(优先级高)取到总线控制权,ID号大的(优先级低)仲裁失利后将转入接收状态,等待下一次总线空闲时再尝试发送。
  • 实现非破坏性仲裁需要两个要求
    1. 线与特性,总线上任何设备发送显性电平0时,总线就会呈现显性电平0状态,只有当前所有设备都发送隐性电平1时,总线才呈现隐性电平1状态。即: 0&X&X =0,1&1&1=1(X代表可以是0可以是1)
    2. 回读机制:每个设备发出一个数据位后,都会读回总线当前的电平状态,已确认自己发出的电平是否被真实的发送出去了,根据线与特性,发出0读回必然是0,发出1,读回不一定是1(ACK槽)

仲裁过程

image.png

数据位从前到后依次比较,出现差异且数据位为1的设备仲裁失利。

单元1和单元2是两个设备,他们都可以回读总线电平。前边的数据位是想通的,所以回读的数据也是相同的,所以会继续发送,当走到红色部分时,单元一发送隐性1,但是单元二发送的事显性0,总线电平这时时显性0,单元二回读和自己发送一样,单元一回读和自己发送有差别,感知到总线有其他设备抢占资源,仲裁失利,下一位起变为接收状态。

id号越小,其二进制,出现1就会越晚,也就越晚退出仲裁。完美解释了id号小优先级高的问题。

位填充不会影响仲裁优先级。你找不到两个ID A和B,没有填充A比B优先级高,填充了B比A高,找不到,根本找不到。

从仲裁的过程,我们可以看出,仲裁的最后的胜利者,它所有的回读,和自己发送的数据是一样的。原有的数据都没发生改变所以它叫非破坏性仲裁。

数据帧和遥控帧的优先级,先按id号仲裁,如果id号一致,再走RTR位仲裁。

image.png 标准帧的id号,不允许出现一样的,遥控帧的id号也不能出现一样的。如果一样的话,他们的仲裁段完全相同。到后边数据会被破坏的。

扩展帧和数据帧的优先级 标准格式11位ID号和扩展格式的29位ID号的高11位一样时,标准格式的优先级,高于扩展帧(SRR必须始终为1,以保证此要求)

image.png

还有一种极端情况,就是标准遥控帧的id号和扩展帧的高11位相同时,怎么仲裁的呢。 到这里标准遥控帧的仲裁端已经结束了,扩展帧的SRR 是0,标准遥控帧的RTR 也是0,但是,扩展帧的仲裁段还没有结束,SRR 后边是IDE 因为是扩展帧所有它的idE 是1,标准帧的ide是0,扩展帧就会出现发1读0的情况,仲裁失利,退出竞争。

7、错误处理

1730447477206.png

  • 主动错误状态的设备正常参与通信并在检测到错误是发出主动错误帧
  • 被动错误状态的设备正常参与通信,但检测的错误时,只能发出被动错误帧,不会破坏别人发送的数据。
  • 总线关闭状态的设备不能参与通信
  • 每个设备内部管理一个TEC和REC,更具TEC和REC的值确定自己的状态 TEC和REC是计数器,TEC发送错误计数一次,正确发送减少一次,REC接收错误计数一次,正确接收减少一次。 image.png

image.png

8、总结

我们从CAN的物理接线,开始介绍,介绍了协议的主要内容,也介绍了协议对特殊情况的处理。消息仲裁,和错误处理。相信大家可以正常的跟硬件工程师交流了。至于Android代码实现,这篇有点太长了,会再写个文章发出。多多见谅。