文章目录
0. 前言
博客内容来自B站上CAN总线入门教程视频讲解,博客中的插图和内容均为视频中的内容。视频链接 CAN总线入门教程
CAN 总线帧格式其实是一项人为规定,由是当我发送一串 1010… 的数据流后,你总得规定每一位都是什么意思吧,要不然接收方只能收到这个数据流,而无法对齐进行解析。所以,为了明确每一位都什么意思,CAN 总线协议规定了以下五种类型的帧:
- 数据帧的用途是发送设备主动发送数据(广播式)。
- 遥控帧的用途是接收设备主动请求数据(请求式)。
- 错误帧的用途是某个设备检测出错误时,向其他设备通知错误。
- 过载帧的用途是接收设备通知其尚未做好接收准备。
- 帧间隔的用途是用于将数据帧及遥控帧与前面的帧分离开。
这类型虽然比较多,但是我们主要学习数据帧即可,因为数据帧最复杂,而且用得最多,发数据主要靠的就是数据帧。遥控帧和数据帧很像,学会数据帧,遥控帧很容易理解。错误帧、过载帧主要是用于异常处理的,设计比较绕,初学可以不必过多关心,了解即可。帧间隔就是等待几位的时间,也是了解即可。
1 数据帧
数据帧的用途是发送设备主动发送数据,属于广模式的通信方式。那看下这个图:
它定义了数据帧的格式,数据帧有标准格式和扩展格式两种,CAN总线标准刚制定时只有标准格式,扩展格式是后来新增的。扩展格式增加了 ID 位数,能承载更多种类的 ID。
我们先分析下标准格式:
1.1 认识下图例
-
图上灰色部分表示的是 D,D 是 Dominant 的缩写,意思是显性电平0,也就是灰色部分必须发显性电平0。
-
图上紫色部分表示的是 D/R,R是Recessive缩写,意思是隐性电平1。D/R 的意思就是这段时序根据发送数据的不同,可以选择发 D,也可以选择发R。
-
最后白色部分表示的是R,也就是这个位置必须是隐性电平1。
-
最后这个白色和灰色各占一半,这个是应答位特有的,在这一位,发送方必须发隐性1,接收方必须发显性0,也就是发送方释放总线,接收方拉开总线,表示接收方对发送方的应答。
在上图中间这里,写的数字,表示的是此段时序所占的位数,比如11位、18位、1位4位等。
图例明白了,来分析一下数据帧:
1.2 数据帧标准格式
-
在发送数据帧之前,总线必须处于空闲状态。空闲状态时,总线是隐性电平。这里画的是逻辑表示,所以是高电平1状态。颜色是白色,隐性。
-
随后数据帧开始,数据帧第一位,这里线画的是低电平,颜色是灰色,表示此位必须是显性电平0,它的意思是 SOF ,帧起始。这个和串口波形的起始位一样,帧起始的作用是“打破宁静”,因为空闲是隐性1,所有设备都不去碰总线。那你要想开始发数据帧,第一位就必须得张开总线,发送显性0。如果第一位发隐性1,那这个1就和前面的空闲状态融为一体了,没人知道你开始发数据,所以,数据端必须以显性0开头,作用是打破总线空闲,开始一帧数据,同时也告诉接收方,接下来的一段时间里,我要是再释放总线,总线处于隐性1,那就不是空闲,而是我想要发送的就是1。
-
一位帧起始之后,首先发送的是报文 ID,标准格式是11位。如果你想发报文 ID 是10101010101,那这一段时序就是隐显隐 …,这一段是紫色,表示根据数据的不同,它可以是显性0,也可以是隐性1。报文 ID 的用途,之前说过,它可以表示后面数据的功能,因为总线上各种报文消息都有,如果没有 ID 加以区分,那肯定就搞混了。同时,报文 ID 还用于区分优先级。当多个设备同时发送时,根据仲裁规则,ID 小的报文优先发送,ID 大的报文等待下一次总线空闲再重试发送,不同功能的数据帧,其 ID 都不同,否则两个设备同时发相同 ID 的数据帧,那仲裁规则就没法判断谁先谁后了。仲裁规则第四张再详细介绍。
-
报文 ID 后,紧跟着的是 RTR, RTR占据一位,在数据帧里必须为显性0。RTR 的意思是远程请求标志位,用于区分数据帧还是遥控帧的,数据帧必须为显性0,遥控帧必须为隐性1,这里是数据帧,所以必须是0。
报名 ID 加上 RTR 位可以称为仲裁段。仲裁主要靠 ID 来实现,RTR 也加入进来的目的是,相同 ID 的数据帧和遥控帧,数据帧的优先级大于遥控帧,数据帧和遥控帧的 ID 是可以相同的,其实这一段在I2C里是不是也似曾相识?没错,它和 I2C 里的7位从机地址+1位读写位的设计可以说是异曲同工。另外,I2C 也有多主机模式,其仲裁段也是7位从机地址+1位读写位。I2C起始后,首先发7位从机地址,指定从机,+1位读写位,表示写入还是读取。这里也一样,11位报名 ID 表示报文功能,+1位RTR,数据帧就是写入,遥控帧其实就可以理解为读取了,只是 CAN 总线的设计,取消了从机地址的概念,它不对从机地址进行分配,而是对报文消息 ID 进行分配,这样做的好处就是,同一条报文可以被多个设备同时接收,如果规定每个设备只能接收一个固定 ID 的消息,那这里的报文 ID,就和从机地址是一摸一样了。
-
之后进入控制段,首先是 IDE,意思是 ID 扩展标志位,用以区分标准格式还是扩展格式。标准格式固定为显性0,看下面扩展格式这一位就固定为隐性1,作为一个区分,如果不用这位加以区分,那后面的数据我到底是按标准格式解析还是按扩展格式解析,这就无法区分了。所以加上 ID 这一位,就是为了区分标准格式还是扩展格式。
-
之后下一位是 r0,必须为显性0。r0意思是保留位,目前还没有用到,如果说帧格式的设计体现了设计者智慧,那么保留位的设计就体现了设计者的远瞻力。保留位现在还不用,但是以后进行升级的时候就会有大用,前面的 IDE 就是例子。在最开始的时候,IDE 其实也是保留位,叫 r1,后来 ID 不够用了,加上了扩展格式,那这个保留位就赋予了实际意义。IDE 用于区分原有版本和升级版本,如果没有保留位,那么扩展格式就得设计得更复杂。
I2C 也是个例子,I2C 在进行7位地址到10位地址的升级过程中,就是因为事先没有设计保留位,后来不得已,只能把某些未分配的地址作为扩展的标志位,所以说保留位的意义重大。
-
后面4位是DLC,意思是表示数据段的长度,之前我们说了,CAN总线一帧数据可以有1~8字节有效载荷,并且可以灵活指定,怎么指定?就靠这个 DLC,如果想发一个字节, DLC就可以给0001,如果想发8个字节, DLC就给1000,当然 DLC 要配合后面的数据段来使用,数据段就是有效载荷的数据了。
根据 DLC 的指定,数据段可以有多个字节,DLC 指定几个字节,数据段就发几个字节,这个要对应起来。那数据段的长度是0~64位,最大64位,即8个字节,这里位数要是8的倍数。
这里看到,数据段长度可以给0,所以数据帧其实也可以发0个有效字节,不过既然要传输数据,那长度一般都会大于0。
-
数据段之后,是 CRC 段,有15位。CRC 是一种高效的校验算法,它会对前面所有的数据位进行 CRC 算法计算,从 SOF 到 Data, 这些数据位计算得到一个校验码,附在后面,接收方收到数据和校验码之后,也会调用 CRC 算法进计算,看看计算的校验码是否一致,以判断传输是否有误。
校验码应用很广,比如身份证号的最后一位,如果身份证号输错了,不符合校验算法,那么软件就会提示你输入有误。
在串口里也有类似的设计,就是奇偶校验,不过奇偶校验的漏检率很高,实际基本不用。而 CRC 是一种漏检率非常低的校验算法,基本上只要错了,必然就能发现。
-
下一个是 CRC 界定符,1位,必须是隐形电平。
-
之后跟着就是 ACK 段,里面有 ACK 槽和 ACK 鉴定符,都是1位, ACK 槽的作用就是应答,发送方发送一帧数据,到底有没有设备收到呢,就靠 ACK 位来实现。
这里的应答设计,和 I2C 的应答,又是异曲同工。它的基本思想就是,当发送方发完一帧数据的主要内容后,在应答这一位时,发送方释放总线,总线回归默认状态,隐性1。如果接收方收到数据了,它就会在 ACK 槽这一位,主动出击,把总线再拉开,使总线呈现显性0的状态,发送方释放总线后,在 ACK 槽会读取总线状态。
如果发送方读取为显性0,那发送方就会想,我释放了总线,但是现在读回来却是显性0,这说冥冥之中,必有接收方。发送方就安心了,因为它知道数据被正确接收了。如果发送方读取为隐性1,那发送方就会想,我释放了总线,根据规定,谁收到,谁就得在 ACK 槽这一位拉开总线,有设备拉开,总线必然是显性0,但现在我读取总线却收到隐性1,竟然没有回应我,我不安心,这样就说明发送失败,发送方可以配置自动重发,也可以不管,总之发送方知道发送状态就行。
所以说在图例中, ACK 槽这一位,发送方要发隐形1,如果接收方存在,则接收方要发显性0,如果接收方不存在,那就没有这个显性0了。可以看出,在 ACK 槽这一位前后,操作总线的权利是有个短暂的交接的。前面所有波形都只有发送方有权操作总线, 在ACK 槽这一位时,变为接收方操作总线,为了给权力交接留出时间,ACK 槽前后,就要留两个鉴定符,在 CRC 鉴定符时,发送方必须发隐形1,除了做一个分隔,另一个作用就是,在 ACK 槽之前,发送方必须释放总线。发送隐形1,就是释放总线。之后在 ACK 槽的时间点,接收方会拉开总线,ACK 槽接收后,接收方不能一直拉着不放,所以在 ACK 界定符时,接收方必须及时释放总线,交出控制权,这就是 ACK 槽的设计,和一前一后两个鉴定符的用途。
这里有两个注意事项,第一是, ACK 槽时,可以允许多个接收方共同拉开总线,因为一个报文消息可以被多个设备同时接收,所以多个设备可以在这里同时拉开,多个设备同时操作总线,输出显性0是没问题的。
第二个是大家要知道,并不是发送方把一段波形完整发出去,然后再接收应答的,而是,发送方和接收方共同完成一整个波形,发送方每发出一位,接收方就立刻收到这一位了。所以在这条时许的最后,整个数据帧还没结束,接收方其实就已经收完了,所以这里短暂交接权力,让接收方操作一下总线,产生应答,应答之后,发送方的整个数据帧才结束。也就是说应答是夹在发送过程之中的,这个注意一下。
-
最后应答结束后,发送方再发7个隐性1,作为 EOF, 帧结束。这一段跟串口波形的停止位是一个意思。
好,到这里,标准格式的所有内容就讲完了,整理一下设计者的思路是,我想要设计一个波形承载数据,那必然要有数据段,我又想要数据段几个字节是可调的,所以加了 DLC, 用于指定数据段的长度,为了区分这个数据的功能,在前面加上 ID 号,为了实现广播式,类似于只写,或者是请求式,类似于读取,要用RTR位进行区分,为了判断数据是否正确传输,在后面加上了 CRC 校验。为了判断是否有接收方,又加了应答设计,之后r0和r1是留的保留位,为了后续升级,当然现在已经用到了 r1,r1变成了 IDE,用于区分标准格式和扩展格式,最后整个波形前面套上起始,后面套上结束,这个伟大的设计就大功造成了。
1.3 数据帧扩展格式
那看完了标准格式,再来看一下扩展格式,扩展格式出现的原因就是,标准格式的11位 ID 不够用了,需要加一些,并且,扩展格式必须也要考虑对标准格式的兼容,看它是怎么设计的。
-
首先,前面的11位 ID 仍然保持不变,后面,另外再额外加上18位 ID。
-
那么中间还夹着两位,一个是 SRR,意思是替代 RTR 的位,标准格式这一位是 RTR,后面学仲裁的时候会知道,仲裁是先比较 ID,后比较 RTR,所以 RTR 必须在所有 ID 位的后面。这里可以看到,在扩展格式中 RTR 已经挪到这最后面,那原来属于 RTR 的位置,现在就空出来了。
但是这一位虽然空出来了,你也不能用作别的用途,因为,同样是为了保证仲裁规则中,标准格式的优先级高于扩展格式的设计,这一位 SRR 虽然没用,但必须要给隐性电平1。至于仲裁流程,第四章再细说,现在知道 SRR 出现的原因就行。
-
然后后面 IDE, 这一位就是扩展格式标志位了,标准格式中 IDE 为显性0,扩展格式中 IDE 为隐性1,当接的方收到了一段波形,它就会从前往后开始判断,收到 IDE 后,如果为显性0,则后续会按照标准格式来解析。如果 IDE 为隐性1,则后续会按照扩展格式来解析。在收到 IDE 之前,接收方并不知道是哪种格式,但是这也没事,因为 IDE 之前,两种格式也没有差异,所以不需要区分。
-
最后, ID 结束后,是 RTR, 这样扩展格式的设计就大功造成了。
标准格式的 ID + RTR 是这一块,扩展格式的 ID + RTR 是这一块。中间夹了2位,目的是为了兼容标准格式,那 ID 升级完成,后续的时许不需要变,就把标准格式照搬下来就行。所以之后是 r1、r0、DLC ,剩下部分与标准格式相同,IDE 这一位本来就是 r1,在扩展格式里已经有 IDE 了,所以后面这个照搬下来就恢复它 r1的身份,同时扩展格式的 r1和 r0也为后续进一步升级留下了空间。
到这里,数据帧这个图就分析完了,之后下一页是对每部分的用途简介。
1.4 数据帧各部分用途简介
了解下英文全称有利于记住这个缩写的意思。
1.5 数据帧的发展历史
CAN 1.2时期,仅存在标准格式, IDE 位当时仍为保留位 r1,右边可以看到,CAN1.2的版本,11位 ID 还是够用的,所以数据帧就只有一种,没有标准格式和扩展格式的说法,这样的时序就叫数据帧,而且这里,数据在设计还是挺规整的。
ID + RTR 的仲裁段, r1 r0保留位 + DLC 的控制段,这里是 r1保留位,还没有出现 IDE,0~64位的数据端,15位 CRC 段,之后是 ACK 段,这是CAN 1.2时期,数据帧的设计。
之后,CAN 2.0时期, ID 不够用,出现了扩展格式,增加了 ID 的位数。为了区分标准格式与扩展格式,协议将标准格式中的 r1赋予了新功能——IDE, 也就是右图,我们刚才讲的那个设计,目前学的就是CAN 2.0协议。
可以看出,为下兼容老版本,扩展数据帧设计的其实并不规整,也不优雅。ID 被拆成了两部分,还出现了一个无意义的 SRR 位,这就像是软件升级时所出现的“屎山代码”现象吧。为了兼容老版本,软件越升级屎山代码堆得就越多,软件就越臃肿,但是你升级又不能不兼容老版本,因为不兼容带来的问题是致命的,这样发展下去,软件最终的归宿就是,出现一个全新的软件,去替代积重难返的老软件。硬件也是一样,不过硬件升级的速度比较慢,推倒重来的成本也更高。所以硬件的生命周期还是比软件长很多的。好,到这里,数据帧的内容就全部讲完了,之后下一个是遥控帧。
2 遥控帧
遥控帧用途是接收设备主动请求数据,遥控帧其实和数据帧非常类似,只是它没有数据段有效载荷这部分。
2.1 使用场景
目前了解到,CAN 总线数据主要靠发送方自觉广播出来,一般发送方会定一个周期,定时广播自己的数据,但如果发送方没有及时发出数据,或者这个数据的使用频率太低了,广播太频繁了,大家都用不到,浪费总线资源,广播太慢了,偶尔有用的话,又及时拿不到。
这样我们就可以规定发送方就不要主动广播这个数据了,而是如果有设备需要的话,首先接收方发出一个遥控帧,遥控帧包含报文 ID,其实遥控帧也是广播出来的,每个设备都能收到遥控帧,然后如果其中某个设备有这个 ID 的数据,它就会再通过数据帧广播出来,这样接收方就能及时获取这个数据。
所以,可以看出,遥控帧实现请求式数据传输,也需要数据帧的配合,请求式传输,每传输一次数据,都需要一来一回两个过程,适合那种使用频率低,但偶尔又需要集中用几次的数据,如果数据使用频率高,再用遥控帧请求式就不合适了,因为请求产生的遥控帧本身也在占用总线资源,所以使用频率高的数据,可以直接用广播式,发送方无脑发就行了,反正接收方也要经常使用,不是很浪费,这是遥控帧和它的使用场景。
2.2时序
看下遥控帧的时许定义。一句话,遥控帧无数据段,RTR 为隐性电平1,其他部分与数据帧相同。 所以学会了数据帧、遥控帧理解起来就非常简单。
看一下图。首先遥控帧数据帧通过 RTR 进区分,数据帧 RTR 是显性0,遥控帧 RTR 是隐性1,当接收方收到 RTR 是 1 后,后续就按照遥控帧的格式解析,遥控帧的 DLC之后,没有数据帧,直接跟的就是 CRC 校验码,当某个设备发出遥控帧时,代表它想请求这个数据,请求自然不需要数据段。
刚才说了,一次完整的请求,需要遥控帧和数据帧配合,请求方发出遥控帧,遥控帧的 ID 表示要请求的数据,响应请求的一方,通过相同 ID 的数据帧反馈数据。
当请求和反馈数据同时发生时,数据帧拥有更高的优先级,这是遥控帧的作用和请求流程。
3 错误帧 & 过载帧 & 帧间隔
错误帧、过载帧和帧间隔,这些设计就跟错误和异常处理有关了。设计比较绕,初学的时候可以不必关心,了解即可。
3.1 错误帧
其中,错误帧的用途是,某个设备检测出错误时向其他设备通知错误,当某个设备发现总线上的波形不对,产生错误了,它就会发出错误帧。
错误帧可以叠加在数据帧上,并且可以破坏数据帧的数据。意思就是我发现这一帧数据有错,现在我破坏了这个数据,大家都不要用了,这是一种错误处理机制,了解下即可。
总线上所有设备都会监督总线的数据,一旦发现 “位错误” 或“填充错误”或“ CRC 错误”或“格式错误”或“应答错误”,这些设备便会发出错误帧来破坏数据,同时终止当前的发送设备。
下面是错误帧的定义,错误分为主动错误和被动错误,详细解释第五章再讲,这里知道一下,设备默认处于主动错误状态,处于主动错误状态的设备检测出错误时会连续发6个显性位,发显性位就是拉开总线,总线只要有一个设备拉开了,就必然处于显性状态,即 0 和 1 相遇时,总线总是处于0状态,这是“线与”特性,所以主动错误标志的6个显性位必然会破坏正常传输的数据。其他设备检测到错误标志就会抛弃这个数据。
那主动错误产生太频繁了,说明这个设备不太可靠,设备就会进入被动错误状态,处于被动错误状态的设备检测出错误时,会连续发6个隐性位,发送隐性位就是不去碰总线,不碰总线就不会破坏总线别人发的数据,但是会破坏自己发的数据,自己的数据有问题,自己破坏掉,不会影响到别的设备发的数据。
所以这里画的图例是,主动错误标志为显性,被动错误标志为隐性,上图中错误标志为6位,发完错误标志后,再跟8个隐性1,作为错误界定符,这是错误帧的定义。
那这里为什么要画0 - 6位的延长时间呢?这是因为,一个设备发出的错误标志可能会引发其他设备连带产生错误标志,多个设备的错误标志叠加起来,这个标志位的长度就可能不止 6 位了,所以后面这些画的是,错误标志可能会延长0~6位的时间,下面也标了,这是错误标志的重叠部分,这个错误帧先知道一下就行,暂时不必深究。
3.2 过载帧
过载帧用途是,接收设备通知其尚未做好接收准备。其帧格式和错误帧类似,只是过载帧不是在错误的时候产生,而是发生方发太快,接收方处理不了的时候,由接收方产生,因为数据是发生方主动发出的,接收方无法直接调整发送方的发送频率。
那怎么办?只能产生一个和错误帧类似的过载帧了,那将数据破坏掉,发送方发不出去,就会重试,在这个破坏和重试的过程中发生数据就被延误了,同时也间接告诉发送方,接收方收不了,如果发送方有相应的处理逻辑,它就会降低一点发送频率。
当接收方收到大量数据而无法处理时,其可以发出过载帧,延缓发送方的数据发送,以平衡总线负载,避免数据丢失。
过载帧的定义其实和过载帧是一样的,只是它们的产能条件和意义不同,这个也了解即可。
3.3 帧间隔
帧间隔用途是,用于将数据帧及遥控帧与前面的帧分离开,这个你知道连续发生数据帧时,其中间会有一小段帧间隔就行,帧间隔也分为主动错误状态和被动错误状态。主动错误状态的针间隔是3位,被动错误状态的帧间隔是3位,另加 8 位延迟传输。
被动状态表示设备不太可靠。不可靠就别那么着急发数据了,延迟传送会将设备置于仲裁不利的处境,尽量减少此设备干扰总线。
错误帧、过载帧、帧间隔以及各种意外状态的处理, 其实设计得非常复杂。建议是刚开始不必掌握,先把基本的数据传输学会,之后有需要了再研究。
好,五种帧类型就讲到这里。接下来看一下位填充。
4. 位填充
刚才讲的时序波形,数据帧和遥控帧这两个在最终发送到总线之前还要经过位填充的处理。 先介绍位填充规则,然后再解释为什么要进行位填充。
首先看一下位填充规则:发送发每发送5个相同电平后,会自动追加一个相反电平的填充位,接头方检测到检通位时,会自动移出填充位,恢复原始数据。
看一下示例
示例1
比如发送方即将要发送数据流 100000110,那么实际发送的其实是1000001110,在这里会多发一个 1,这个
1就是填充位规则加上去的,为什么多发一个1呢?
~
因为填充规则规定,每发送5个相同电平后,自动追加一个相反电平的填充位,在这里数据流中出现了连续发5个0的情况,所以在5个0后面紧跟着就会插入一个1,那位填充规则随便改我的数据不会导致数据出错吗?答案是不会。
~
继续看,既然你发的是1000001110,那接收方实际接收肯定也是1000001110了。根据规则,接收方检测的填充位时,会自动移除填充位,恢复原始数据,所以接收方发现,我这里连续收到5个0,后面一个数据必定是填充位,于是,接收方收到数据的第一步就把这个填充位给移除,恢复原始数据流,所以接收方移除填充位后,收到的数据是10000110,与发送方想要发送的数据流一致,接收没问题。
~
示例2
继续看下一个例子,发生方即将发送10000011110,这里出现了5个连续的0和4个连续的1,根据位填充规则,实际发送时5个0后需要追加一个相反电平1,但是注意,原来后面这里本来是只有4个1的,但是追加一个填充位1后,4个1就变成5个1了,这种情况根据规定,这5个1后还需要再执行位填充,也要是在后面这里再插入一个1的相反电平0。
~
后面接收方实际接收,就是由两个填充位的数据流,接收方根据位填充规则再把这两个填充位移除,恢复原始数据。
概括一下就是,位填充插入的数据位,要和后续的数据位合并,进新的位填充判定。
示例3
接下看最后一个例子,发送方即将发送0,然后一大长串1,这样执行位填充,实际发送是这样的,出现5个1了,后面补一个0,然后继续又出现5个1了,然后再补一个0。
~
所以实际发送是 011111011111010 ,接收方实际接收后,移除填充位,接收的数据与发送一致。
这就是位填充规则。简单地说,位填充是发送方的最后一步处理,接收方的第一步处理,真正收发的有效数据,发送方和接收方都是一致的,数据传输不会有错,只是最终呈现在CAN 总线上的波形时,会遵守位填充的规则。
那为什么要费这么大劲?发的时候加点位,接收了又把它移除。好像对数据传输也没什么用。那继续看位填充的作用。
位填充的作用:用一句话来说,就是防止数据波形长时间不变化,比如一下子发了一大长串1,一点变化也没有,那对应到 CAN 走线上,就是一长串的隐性电平,长时间持续不变的波形,对接收方的准确接收是有不利影响的,所以,当连续发生5个一样的电平时,它就强行让你发一个相反电平变化一下,当然位填充还有其他作用。
-
增加波形的定时信息,利于接收方执行“再同步”,防止波形长时间无变化,导致接收方不能精确掌握数据采样时机。
举个例子,比如说发送一个数据帧,数据段有8个字节,64位,这8个字节的内容都是 FF,如果直接发出这一段波形,那么在数据段时,总线就会呈现出连续64位的1,这个时间是非常长的。如果接收方时钟稍有偏差,接收方可能收到的就是65 个1,或者63个1,因为你波形一点也不变化,位与位之间就没有分界点了,接收方没法利用数据的跳变沿确定采样点是不是在一位的中间,也无法得知采样点的位置是不是准确。更没法依据跳变沿,执行再同步,进行误差补偿了。
所以长时间不变的波形容易导致接收出错。为了避免这个问题,就让发动方发数据时强行变化一下,加入位填充后,发送方不可能出现连续5个以上的相同位,数据多跳变跳变,数据位的边界才会更加清晰,才利于接收方确定采样点的准确位置,这是位填充的第一个作用。
-
将正常数据流与错误帧和过载帧区分开。标志错误帧和过载帧的特异性。
刚才看了数据帧和过载帧的规定,就包含了连续6个相同的电平,而正常的数据流经过位填充后不可能出现连续5个以上的相同电平。所以,当有错误帧或过载帧叠加在数据帧之上时,立刻就能发现它。
因为错误帧和过载帧正好是超过5个,即6个相同电平。如果没有位填充规则,当错误帧出现时,你咋知道这是错误帧还是正常的6个数据位。就没法区分了。
-
保持 CAN 总线在发送正常数据流时的活跃状态,防止被误认为总线空闲。
CAN 总线规定,当总线出现连续11个隐形1后,即认为总线空闲。所以,如果没有位填充,数据段最多就能出现连续64个隐性1,这波形不带动的,别的设备就可能认为当前是空闲的,如果别的设备也发数据,那就产生冲突了。
而有了位填充规则后,总现在正常发数据帧或遥控帧时,不可能出现连续超过5个的1或0,自然也不会被别的设备认为总线是空闲的。好,以上就是位填充的规则。
5. 波形实例
5.1 标准数据帧
来看第一个图,这是发送标准数据帧时,CAN总线产生的波形。这个数据帧报文 ID 是0X555,数据长度1字节,数据内容为0X AA。看一下波形:
在发送数据帧之前,CAN 总线一直处于两线收紧的空闲状态,开始发数据帧时,首先要把两线张开,发送显性0,表示 SOF, 打破空闲,一个帧开始总线也进入占用状态。
SOF 之后,根据标准数据帧的定义,首先发送报文 ID,因为 ID 是0X555,所以这里就呈现101 0101 0101这样的波形。注意 ID 只有11位,所以转换16进制时需要对 ID 进行3/4/4的划分,那这一块就表示11位报文 ID 。
下一位是 RTR。RTR 的波形是张开状态,显性0。表示当前是数据帧,然后 IDE 是显性0,表示标准数据帧 ,r0保留位必须是0,保留位为什么必须是0呢?这是因为在仲裁时,0的优先级更高,之后如果用到了这个保留位,可以保证,当前定义的优先级高于以后扩展定义的优先级。
就像 IDE 一样,因为之前保留位 r1 默认是0,所以现在就有标准帧优先级高于扩展帧优先级的这个特性,所以保留位的默认值也不是随便给的。
之后是4位 DLC,同时我们也发现在这个位置产生一个填充位,就是标黄的这一位,在这一位之前可以看到已经连续出现5个0了,所以产生填充位,强行插入一个1,这个填充位会在实际的波形中体现,当然在我们解析数据帧时,要移除填充位。所以这里线虽然画了5位,但实际上只需要分析4位,这四位是0001,表示 DLC 的指示0x1。
接着 DLC 之后就是数据段了,因为 DLC 是1,所以数据段只有一个字节,依次判定数据位是1010 1010,表示数据内容是0xAA,同时也可以发现,CAN总线是高位先行的。
之后数据段结束,是15位的 CRC 段,发送时 CRC 可以由CAN控制器自动生成,接收时,也可以由CAN控制器自动校验,所以不需要我们操心。
之后是 CRC 界定符。CRC 界定符的作用是,在 ACK 槽之前,发生方释放总线。如果没有 CRC 界定符,我们看到 CRC 的最后一位就是显性0,如果直接跟 ACK 槽,那你发送方还拽着总线,让接收方怎么发送应答位呢?所以 CRC 与 ACK 之间加一个 CRC 界定符,为隐性1。这样发送方在 ACK 槽之前,肯定会释放动线,之后,总线控制权短暂交接给接收方,发送方在 ACK 槽期间不会碰总线,如果有接收方,接收方会在 ACK 槽拉开动线,只要有一个设备拉开了总线,总线必定呈现显性0状态,这里,看到 ACK 槽是显性0,说明有接收方给应答了,当然注意,这一位不是发送方拉开的,而是接收方拉开的,表示接收方对发送方的应答。
之后应答结束,是 ACK 鉴定符,接收方会在这一位期间释放总线,交出总线控制权。当然发送发也是释放状态。
最后7位 EOF, 都是隐性1,正好 EOF 结束后,总线一直都可以是默认的隐性1状态。
5.2 扩展数据帧
SOF 之后,先是正常的11位 ID,SRR 无意义位,必须给1, IDE, 扩展标识,也给了1,表示这是一个扩展帧,那么从这里开始,后面的波形就得按照扩展格式来解析了。所以紧跟着的是扩展的18位 ID,前面11位,后面18位,拼在一起组成完整的29位 ID。
那这29位 ID 换成16进制就是0X0789ABCD, 本来想给 ID 为0X6789ABCD,但是有个易错点大家要清楚,因为 ID 是29位的,并不是4的整数倍。所以扩展 ID 号的范围是0X00000000~0X 1FFFF FFFF, 这个范围表示不了0X6789ABCD 这个数。
标准数据帧也是一样,标准 ID 号是11位的,范围是0X000到0X7FF ,这个注意一下。
继续看扩展数据帧,除了 ID 号字段变化以外,后面的波形就都是一样的了。RTR 是0,表示的是数据帧, r1 r0保留位,默认给0,之后的内容和标准帧一样。
5.3 遥控帧
首先 SOF 跟 11位 ID,11位ID本来想给0x888,但是刚才说了11位 ID 最大只有0x7FF,表示不了0x888,所以实际 ID 是00010001000,0x088,然后 RTR 遥控帧标志位给了1,标志着这是一个遥控帧,然后按照遥控帧的定义来解析波形 ,IDE 是0,说明这是一个标准遥控帧。
4位 DLC 之后没有数据段了,直接跟的是15位 CRC,最后是应答和 EOF。当然这里看到,这个位填充就是我们刚才讲的一个特例,本来正常数据是5个0和4个1,因为5个0后又填充了一个1,所以原来只有4个1的情况,现在变成5个1了。所以在新的5个1 后还得再补一个0,这就是位填充的一个特例,在实际波形中就是这样体现的。
然后遥控帧因为没有数据段,那这个 DLC 的意义我觉得就不大了,因为 DLC 表示的是后面数据段的长度。那现在数据段都没有了,DLC 是不是得给0呢?但这里看到,虽然没有数据段,DLC 还是可以给非0值的,有的人解释是,遥控帧的 DLC 可以表示我需要请求多个数据,但是我觉得每个数据帧有几个数据应该都是规定好来的,不需要在请求的时候指定。
总之,遥控帧最主要的部分还是ID号,表示我要请求这个 ID 的数据帧,那这就是遥控帧的波形实例。
6. 展望
那么发送方发送一段波形后,接送方该如何准确地采样,得到每一个数据位的值呢?
就是像我们这样,在每一位数据位中间画一条线,来读取总线电平。我们人类用眼睛观察,想当然地觉得数据位应该这样划分,在这里采样,但是对应到实际的接收方,我们必须明确规定进行采样的具体步骤,并且设计出一个可以实现的硬件电路。
另外针对一些实践问题,我们也得有相应的解决办法。就是第三章位时许和位同步所要涉及的内容。