CANTP(CAN Transport Layer)传输协议是建立在CAN总线之上的高层协议,用于在控制器局域网络上可靠地传输大数据量的信息。其遵循ISO15765 - 2和AUTOSAR标准规范,处于PduR与CANIf模块之间,负责分割、重组和组装CAN消息,以确保大数据包的可靠传输。主要作用是对CAN I-PDU(信息参数数据单元)进行分段和重新组装,确保接收的数据长度不超过8个字节(CAN总线直接通信,CAN I-PDU不大于8字节)或64个字节(CAN FD,全双工通信,CAN I-PDU不大于64字节)。 一般来说大数据功能主要在诊断中使用,用来传输诊断数据,CanTp在AUTOSAR中的位置如下图所示:
CAN 传输协议(CanTp)的上层接口为协议数据单元路由器(PduR)模块提供了全局访问权限,以便进行数据的发送和接收操作。这种访问是通过 CAN 网络层服务数据单元标识符(CAN NSduId)来实现的。CAN NSduId 指向一个固定的数据结构,该数据结构由描述 CAN 网络层服务数据单元(CAN N-SDU)的属性组成。每个 CAN 网络层服务数据单元(CAN N-SDU)特定的数据结构可能包含诸如以下的属性:N-SDU 的类型(发送或接收)、其寻址格式、该消息的链路层服务数据单元(L-SDU)标识符,或者其他对实现有帮助的属性。下图展示了 CAN 传输协议(CanTp)、协议数据单元路由器(PduR)和 CAN 接口(CanIf)模块之间的交互情况。
在学习CanTp之前,大家在往期内容中看下PDU、PCI的概念,以更好理解后续内容。
在对上述内容有一定了解后,我们对N-Pdu进一步解读。在ISO15765 - 2中,根据PCI的不同,将N-Pdu分为不同类型的帧类型,如下图所示:
对于初学者,对于对于流控帧中的FS、BS、STmin参数可能不是很了解,解释如下,在后边的例子中我们会进一步讲解:
BS(Block Size):发送的数据量
STmin(Separation Time Min):多帧间的最小间隔时间
FS(Flow Status) 有三种状态:
请求继续发送FC.CTS;
请求继续等待FC.WAIT;
过载溢出FC.OVFLW
当数据长度若不超过7字节,单帧一帧就可以搞定。(为什么是七个字节的数据,这里是因为第一个byte被占用了,后面只有七个byte的空位了)
例如收到数据:04 11 22 33 44 FF FF FF
此时第一个字节04 ,0代表这是一个单帧,4表示数据长度,这一帧后面带着四字节数据。
11 22 33 44 是这一单帧携带的数据,后面的FF FF FF 是填充位。
例如收到了 10 22 33 33 33 33 33 33 第一个字节的1 代表这是一帧首帧 0 22代表数据长度,代表传输的数据一共有34个字节,首帧先传6字节,后面的28个字节由后续的连续帧补上。后面的6个33 33 33 33 33 33是首帧携带的数据。
流控帧用于记录当前的流量控制情况,包括发送方可以发送的数据量、接收方的处理能力等信息。一共包含三种信息FS、BS、STmin。
FS流控帧状态
FS=0 请继续发送(接收能力完全没问题)
FS=1 请等待(有点接受不过来了,先不要发,等通知)
FS=2 过载了(太多了,接受不了了)
BS (block size) 块大小: 接收方能够接收的最大数据块数量。
BS=0 告诉发送方不需要再等流控帧了,可以一直发送(CF发送)数据。
BS=01~FF 告诉发送方在没有接收到下流控帧期间能发送的最大数目的连续帧
比方说A发送了一个首帧,B回复了一个流控帧,其中BS为0,则A在收到流控帧时候可以一直发送连续帧知道数据结束。如果B回复的流控帧中的BS值设置为04,表示A在发送完4帧连续帧之后,必须等待B再次回复流控帧之后才可以继续发送连续帧数据。
STmin (ms): 发送方发送的两个连续帧之间,最小需要的时间间隔。
00~7F单位 ms;F1~F9表示0.1~0.9ms;0表示按照发送方最快的速度发送。假如STmin设置为1ms, 那么发送方可以以1ms发送也可以1.2ms间隔发送。
Tip:总的来说流控帧控制发送方以多少大小,多少速度的方式发来数据,太大了或者太快 了受不了。
假设收到报文 30 00 14 AA AA AA AA AA,其中 3 表示是一个流控帧,0表示允许我方继续发送数据,00表示可以无限制的发连续帧,14 表示连续帧与连续帧直接的最小时间间隔是20ms。
连续帧第一包SN编号是1,之后编号一直累加直到F,之后再从0开始
完整报文数据:
以下是诊断过程的完整报文,分别是单帧和多帧收发,可以结合上边的内容自己分析一下这段报文:
分段:CanTp接收从CanIf上传的数据后,将较大的数据流切分成多个传输单元,以适应CAN总线的有限帧长度,同时提高数据传输的可靠性。每个传输单元被赋予一个唯一的序列号,以便在接收端正确的重组数据。分段过程如下:
数据包大小:CanTp将发送首帧后,接收方将返回流控帧,CanTp根据流控帧信息组织数据包大小
添加头信息:每个数据包会包含头信息,通常包括序列号、总包数量等,以便接收端能够识别和重组数据
数据完整性:在分段过程中,可能会对每个数据包进行校验以确保数据的完整性
重组:CanTp在分段完成后,将接收到的数据向上层模块传输前需要将数据包进行重组。CanTp将接收到的多个数据包按照其序列号正确的重组成完整的原始数据流的过程,如果在重组过程中有错误或数据丢失,CanTp会请求发送方重新传输相应单元数据。重组过程如下:
接收包:CanTp按顺序接收数据包,这些包可能是乱序到达的
缓存管理:将接收到的各个数据包存储在缓存中,等待缺失的数据包被接收
序列号利用:根据前述的序列号SN及包总数信息,确定包的顺序和完整性,从而将其重组为完整的数据流
错误处理:如果发现数据丢失或错误,可以请求重新传输特定的数据包
传输请求:传输操作CanTp_Transmit()将允许上层(一般指诊断)请求使用CAN传输协议设施(分段、扩展寻址格式等)进行数据传输。功能函数CanTp_Transmit()应该是异步的,多段传输不阻塞。传输请求被接受后,如果N-Sdu传输完成(成功或不成功),CanIf模块调用传输确认函数,通知CAN传输层CanTp请求的CAN帧传输是否成功执行。L-Pdu标识符与调用相关联以便识别相应的传输。当超过最大时间(等于N_As)没有收到发送确认时,CanTp模块将中止相应的会话。在收到TxConfirmation之前,N-Pdu对于其他并发会话仍然不可用,不管成功与否。CanTp模块将通知其上层。PduR的发送请求流程如下:
上层通过调用CanTp_Transmit()请求传输N-Sdu。CanTp_Transmit()的参数描述了CAN NSduld和要发送的完整Tx N-Sdu长度
CanTp_Transmit只使用完整的SduLength信息,而不使用可用的N-Sdu数据缓冲区来准备SF或FF的PCI。当在通用连接上调用CanTp_Transmit时,CanTp模块将根据元数据中包含的地址信息发送帧
上层发送请求后,CanTp模块在调用PduR_CanTpCopyTxData之前启动超时N_Cs。如果在计时器过去之前没有可用的数据,CanTp模块将终止通信。CanTp调用PduR_CanTpCopyTxData接口发送的每个帧(SF, FF和CF)。假设函数返回BUFREQEBUSY, CanTp模块将稍后重试复制数据
缓存策略:因为CanTp没有缓冲能力,所以要发送的N-Sdu负载不会在内部复制,收到的N-Pdu也不会在内部重新组装。CAN传输层直接在PduR的存储区域上工作。要访问这些内存区域,CAN传输层使用PduR_CanTpCopyTxData()或PduR_CanTpCopyRxData()接口。因此,为了保证数据一致性,PduR锁定该内存区域,直到出现指示。当发送缓冲区被锁定时,PduR不能在缓冲区内写入数据。当接收缓冲区被锁定时,CAN传输层不能保证缓冲区的数据一致性。PduR既不能读也不能写缓冲区中的数据,如下图所示:
传输取消:传输是可以取消的,一个实例场景:当更高优先级的诊断协议来临的时候。此功能通过CanTp_CancelTransmit()触发。注意,如果传输正在进行中,将在接收端产生一个超时错误。
当CanTp模块收到SF或FF的N-Pdu时,使用PduR_CanTpStartOfReception接口通知上层(PduR)。若CanTp接收到FF的内容提供给PduR,PduR根据其中信息为N-Sdu预留一段缓冲区。若CanTp接收到SF的内容将直接被PduR接收并通过路由路径分发到上层模块
接收到的数据链路层数据长度(RXDL)由CAN帧/Pdu的第一个接收到的有效载荷长度(CANDL)导出(CAN_DL小于等于8应该按照8字节计算)。
如果函数PduR_CanTpStartOfReception()返回BUFREQENOT_OK,表示PduR上层预留可用缓冲区小于已经接收到的数据,CanTp模块将中止接收N-Sdu,并调用PduR_CanTpRxIndication()返回ENOTOK。
如果函数PduR_CanTpCopyRxData()在接收到块的最后一个连续帧后返回BUFREQ_OK,但是剩余的缓冲区不足以接收下一个块,CanTp模块将启动定时器NBr。当定时器NBr处于活动状态时,CanTp模块将在每次处理MainFunction期间调用PduR_CanTpCopyRxData()接口时,如果 NBr定时器过期,而可用缓冲区的大小仍然不够大,CanTp模块将发送一个新的FC(WAIT)来暂停N-Sdu的接收,并重新加载NBr定时器。
如果函数PduR_CanTpStartOfReception()返回BUFREQEOVFL给CanTp模块,CanTp模块将发送一个流控N-Pdu,状态为溢出(FS(OVFLW)),并终止接收N-Sdu。
Tip:如果PduR没有缓冲区可用,因为一个错误(例如,在网关的情况下,它可能表明到目标网络的传输会话已经中断)或资源限制(例如N-SDU长度超过上层的最大缓冲区大小),PduR_CanTpStartofReception()接口返回BUFREQENOT_OK或者BUFREQEOVFL停止接收。
这里举两个大数据收发的案例,来帮助大家更好的理解前文的一些概念
使用CAN2.0帧发送长度为50字节的帧的过程如下所示:
PduR请求传输50个数据字节,调用CanTpTransmit()下发任务
CanTp调用PduRCanTpCopyTxData向PduR请求负载数据,并发送FF(首帧): 10 32 XX XX XX XX XX XX(其中第一个字节为0x10,其MSB 4 位为 1,定义它是第一帧;LSB 4 位加上下一个 1 字节共有 12 位,定义CAN TP要发送的数据长度。0x32转换为十进制为50字节,XX表示传输数据内容)
接收FC(流控帧): 30 07 14 XX XX XX XX XX(其中第一个字节为0x30,前 4 位为 3,定义了它是流控帧;后四位 4 位是流的状态,0 表示清除发送接收端空闲,可以周期接收数据;第二个字节BS0x07 是块大小,表示可以发送方可以连续发送的连续帧数量,第三个字节 0x14 最小间隔时间,即两个连续帧之间的时间延迟,以便给接收端一些时间准备好周期无缝接收下一个连续帧)
CanTp在收到流控帧后,将剩余的有效载荷数据作为连续帧序列发送;上层在每个连续帧(CF)上复制 6 或 7 字节的有效载荷数据
注:流控帧中的BS为7,表示可以发送7个连续帧,加上首帧,最大可以发送49+6=55个字节,足够本次发送的50个字节,假设需要发送的自述书超过55个字节,则发送方在发送完第七个连续帧之后,需要等待接收方,再次返回流控帧FC之后才可以继续发送数据。
CanIf将接收的FF(首帧)通过CanTp_RxIndication()通知到CanTp。CanTp将接收到首帧中数据通知转发给PduR,并询问PduR可用缓冲区大小。CanTp接收FF(首帧) 10 31 XX XX XX XX XX XX(其中第一个字节为0x10,其前四位4 位为 1,表示它是首帧;后4位加上下一个字节)0X031共有 12 位,表示数据总长度为49字节,XX表示首帧传输数据内容)
PduR返回可用缓冲区大小为25字节,CanTp根据上层可用缓冲区回复FC(流控帧)。发送30 02 14 XX XX XX XX XX(其中第一个字节为0x30,高4 位为 3,表示是流控帧;低4 位是流的状态,0 表示清除发送接收端空闲,可以周期接收数据;第二个字节0x02 是块大小,表示发送方可以连续发送3帧连续帧而不需要等待流控帧,第三个字节 0x14 最小间隔时间,即两个连续帧之间的时间延迟,以便给接收端一些时间准备好周期无缝接收下一个连续帧)
CanTp每接收到一帧报文后,通过调用PduR_CanTpCopyRxData()将数据拷贝到上层模块。
在接收到两个连续帧之后(6+7*2=20),剩余的缓冲区大小不够下一个块(一个块为两个连续帧大小)CanTp通过调用PduR_CanTpCopyRxData(),请求PduR剩余的缓冲区大小,并回复一个FC(流控帧)31 00 14 XX XX XX XX XX(其中第一个字节为0x30,高4 位为 3,表示是流控帧;低4 位是流的状态,1 表示当前接收端暂无可用缓冲区,数据接收暂停等待;第二个字节0x00 是块大小,表示可以发送的连续帧数量第三个字节 0x14 最小间隔时间)直到下一个块的足够缓冲区可用
当缓冲区大小最终足够下一个块时,CanTp将再次向发起者发送一个FC(流控帧) CTS 30 02 14 XX XX XX XX XX,并继续接收下一个连续帧块。
在复制完块的最后一个连续帧后,剩余的缓冲区对于下一个块来说太低了,所以CanTp再次发送FC(流控帧)并监控剩余的缓冲区大小。CanTp发送31 00 14 XX XX XX XX XX,并暂停接收数据,当最后一个块的缓冲区可用时,CanTp将继续接收。
当接收完成时,CanTp通过调用PduR_CanTpRxIndication()通知PduR接收结束。
接收49字节N-Sdu至PduR,PduR提供25字节大小的Rx缓冲区的接收过程:
在我们实际接触CanTp需求的过程中,我们经常会听到一些关于时间参数的配置定义,NAs、NBs、N_Cs这些,刚接触CanTp模块的人肯定是一脸懵,那么这些定时器到底是干什么的呢?
结合下边这张图带你们梳理一下这些定时器的作用
首先,定时器分为接收定时器和发送定时器,定时器名称中的s表示send,r表示receive,它们分别各有A、B、C三种定时器,这样我们记起来就清晰了很多。(这段一定要反复仔细品味一下)
发送方发送了一帧报文后,它会启动N_As定时器,用于检测报文是否发送超时,收到发送确认后会重置该定时器
发送方发送完成首帧后,启动N_Bs定时器,用于检测是否在规定时间内收到接收方回复的流控帧FC
• 接收方收到首帧后,启动NBr定时器并开始组织流控帧(用于监测流控帧是否回复超时),流控帧组织完成后,重置NBr定时器,同时启动N_Ar,用于监控流控帧是否发送超时
• 等待接收方收到流控帧的发送确认后,重置NAr定时器,同时启动NCr定时器,用来监控是否在规定时间内收到连续帧
• 之后发送的N_Cs就是用来控制连续帧的发送间隔,它的值等于流控帧中设置的STmin的值
大家对着这段文字和图自己梳理一遍这个过程,理清楚一遍之后,会发现定时器不过如此。