1. 模块定位
CanTp(CAN Transport Layer)是 AUTOSAR Classic 通信栈中的 CAN 传输层模块,实现 ISO 15765-2 定义的 DoCAN 传输协议。
它解决的核心问题是:一条完整的上层消息可能超过单个 CAN 帧的载荷上限,需要进行分段发送和接收重组。
#mermaid-svg-tYiODcHmVKkoRq0N{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tYiODcHmVKkoRq0N .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tYiODcHmVKkoRq0N .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tYiODcHmVKkoRq0N .error-icon{fill:#552222;}#mermaid-svg-tYiODcHmVKkoRq0N .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tYiODcHmVKkoRq0N .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tYiODcHmVKkoRq0N .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tYiODcHmVKkoRq0N .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tYiODcHmVKkoRq0N .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tYiODcHmVKkoRq0N .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tYiODcHmVKkoRq0N .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tYiODcHmVKkoRq0N .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tYiODcHmVKkoRq0N .marker.cross{stroke:#333333;}#mermaid-svg-tYiODcHmVKkoRq0N svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tYiODcHmVKkoRq0N p{margin:0;}#mermaid-svg-tYiODcHmVKkoRq0N .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tYiODcHmVKkoRq0N .cluster-label text{fill:#333;}#mermaid-svg-tYiODcHmVKkoRq0N .cluster-label span{color:#333;}#mermaid-svg-tYiODcHmVKkoRq0N .cluster-label span p{background-color:transparent;}#mermaid-svg-tYiODcHmVKkoRq0N .label text,#mermaid-svg-tYiODcHmVKkoRq0N span{fill:#333;color:#333;}#mermaid-svg-tYiODcHmVKkoRq0N .node rect,#mermaid-svg-tYiODcHmVKkoRq0N .node circle,#mermaid-svg-tYiODcHmVKkoRq0N .node ellipse,#mermaid-svg-tYiODcHmVKkoRq0N .node polygon,#mermaid-svg-tYiODcHmVKkoRq0N .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tYiODcHmVKkoRq0N .rough-node .label text,#mermaid-svg-tYiODcHmVKkoRq0N .node .label text,#mermaid-svg-tYiODcHmVKkoRq0N .image-shape .label,#mermaid-svg-tYiODcHmVKkoRq0N .icon-shape .label{text-anchor:middle;}#mermaid-svg-tYiODcHmVKkoRq0N .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tYiODcHmVKkoRq0N .rough-node .label,#mermaid-svg-tYiODcHmVKkoRq0N .node .label,#mermaid-svg-tYiODcHmVKkoRq0N .image-shape .label,#mermaid-svg-tYiODcHmVKkoRq0N .icon-shape .label{text-align:center;}#mermaid-svg-tYiODcHmVKkoRq0N .node.clickable{cursor:pointer;}#mermaid-svg-tYiODcHmVKkoRq0N .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tYiODcHmVKkoRq0N .arrowheadPath{fill:#333333;}#mermaid-svg-tYiODcHmVKkoRq0N .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tYiODcHmVKkoRq0N .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tYiODcHmVKkoRq0N .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tYiODcHmVKkoRq0N .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tYiODcHmVKkoRq0N .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tYiODcHmVKkoRq0N .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tYiODcHmVKkoRq0N .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tYiODcHmVKkoRq0N .cluster text{fill:#333;}#mermaid-svg-tYiODcHmVKkoRq0N .cluster span{color:#333;}#mermaid-svg-tYiODcHmVKkoRq0N div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tYiODcHmVKkoRq0N .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tYiODcHmVKkoRq0N rect.text{fill:none;stroke-width:0;}#mermaid-svg-tYiODcHmVKkoRq0N .icon-shape,#mermaid-svg-tYiODcHmVKkoRq0N .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tYiODcHmVKkoRq0N .icon-shape p,#mermaid-svg-tYiODcHmVKkoRq0N .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tYiODcHmVKkoRq0N .icon-shape .label rect,#mermaid-svg-tYiODcHmVKkoRq0N .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tYiODcHmVKkoRq0N .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tYiODcHmVKkoRq0N .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tYiODcHmVKkoRq0N :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Dcm
解析 UDS 服务
PduR
按 PDU ID 路由
CanTp
分段、重组、流控、超时
CanIf
CAN ID 与控制器映射
Can Driver
操作 CAN 控制器
CAN Controller / Bus
CanTp 不负责以下工作:
- 不解析 UDS 服务,例如
0x22、0x27、0x31; - 不决定 PDU 应路由到哪个上层模块;
- 不负责 CAN ID 与硬件控制器的最终映射;
- 不直接访问 CAN 控制器寄存器。
对应的职责边界如下:
| 工作内容 | 负责模块 |
|---|---|
| UDS 服务处理 | Dcm |
| PDU 路由 | PduR |
| 长报文分段与重组 | CanTp |
| CAN ID、控制器映射 | CanIf |
| CAN 硬件访问 | Can Driver |
2. 基本术语
2.1 N-SDU
N-SDU 是上层交给 CanTp 的完整消息。例如,一个包含 VIN 的 UDS 响应:
text
62 F1 90 + 17 字节 VIN
CanTp只处理这条消息的长度、地址信息和传输过程,不解释其中的诊断含义。
2.2 N-PDU
N-PDU 是 CanTp 根据传输协议生成或接收的分段,包括:
| 类型 | 名称 | 用途 |
|---|---|---|
| SF | Single Frame | 短消息一次发送完成 |
| FF | First Frame | 多帧传输的第一帧,携带总长度 |
| CF | Consecutive Frame | 携带后续分段数据 |
| FC | Flow Control | 接收方控制发送节奏 |
关系如下:
#mermaid-svg-wYqvWtmZrsYowtAj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wYqvWtmZrsYowtAj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wYqvWtmZrsYowtAj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wYqvWtmZrsYowtAj .error-icon{fill:#552222;}#mermaid-svg-wYqvWtmZrsYowtAj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wYqvWtmZrsYowtAj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wYqvWtmZrsYowtAj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wYqvWtmZrsYowtAj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wYqvWtmZrsYowtAj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wYqvWtmZrsYowtAj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wYqvWtmZrsYowtAj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wYqvWtmZrsYowtAj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wYqvWtmZrsYowtAj .marker.cross{stroke:#333333;}#mermaid-svg-wYqvWtmZrsYowtAj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wYqvWtmZrsYowtAj p{margin:0;}#mermaid-svg-wYqvWtmZrsYowtAj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wYqvWtmZrsYowtAj .cluster-label text{fill:#333;}#mermaid-svg-wYqvWtmZrsYowtAj .cluster-label span{color:#333;}#mermaid-svg-wYqvWtmZrsYowtAj .cluster-label span p{background-color:transparent;}#mermaid-svg-wYqvWtmZrsYowtAj .label text,#mermaid-svg-wYqvWtmZrsYowtAj span{fill:#333;color:#333;}#mermaid-svg-wYqvWtmZrsYowtAj .node rect,#mermaid-svg-wYqvWtmZrsYowtAj .node circle,#mermaid-svg-wYqvWtmZrsYowtAj .node ellipse,#mermaid-svg-wYqvWtmZrsYowtAj .node polygon,#mermaid-svg-wYqvWtmZrsYowtAj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wYqvWtmZrsYowtAj .rough-node .label text,#mermaid-svg-wYqvWtmZrsYowtAj .node .label text,#mermaid-svg-wYqvWtmZrsYowtAj .image-shape .label,#mermaid-svg-wYqvWtmZrsYowtAj .icon-shape .label{text-anchor:middle;}#mermaid-svg-wYqvWtmZrsYowtAj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wYqvWtmZrsYowtAj .rough-node .label,#mermaid-svg-wYqvWtmZrsYowtAj .node .label,#mermaid-svg-wYqvWtmZrsYowtAj .image-shape .label,#mermaid-svg-wYqvWtmZrsYowtAj .icon-shape .label{text-align:center;}#mermaid-svg-wYqvWtmZrsYowtAj .node.clickable{cursor:pointer;}#mermaid-svg-wYqvWtmZrsYowtAj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wYqvWtmZrsYowtAj .arrowheadPath{fill:#333333;}#mermaid-svg-wYqvWtmZrsYowtAj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wYqvWtmZrsYowtAj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wYqvWtmZrsYowtAj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wYqvWtmZrsYowtAj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wYqvWtmZrsYowtAj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wYqvWtmZrsYowtAj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wYqvWtmZrsYowtAj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wYqvWtmZrsYowtAj .cluster text{fill:#333;}#mermaid-svg-wYqvWtmZrsYowtAj .cluster span{color:#333;}#mermaid-svg-wYqvWtmZrsYowtAj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wYqvWtmZrsYowtAj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wYqvWtmZrsYowtAj rect.text{fill:none;stroke-width:0;}#mermaid-svg-wYqvWtmZrsYowtAj .icon-shape,#mermaid-svg-wYqvWtmZrsYowtAj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wYqvWtmZrsYowtAj .icon-shape p,#mermaid-svg-wYqvWtmZrsYowtAj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wYqvWtmZrsYowtAj .icon-shape .label rect,#mermaid-svg-wYqvWtmZrsYowtAj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wYqvWtmZrsYowtAj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wYqvWtmZrsYowtAj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wYqvWtmZrsYowtAj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 完整 N-SDU
例如 20 字节 UDS 响应
CanTp 分段
FF分段
第1个CF分段
第2个CF分段
多个 CAN 帧
2.3 N-PCI
N-PCI 是 CanTp 加入的协议控制信息,用于描述:
- 帧类型;
- 完整消息长度;
- 连续帧序号 SN;
- 流控状态 FS;
- 块大小 BS;
- 最小发送间隔 STmin。
例如,UDS请求为:
text
22 F1 90
使用经典 CAN 单帧发送时,可能形成:
text
03 22 F1 90 00 00 00 00
其中 03 是 CanTp 的单帧长度信息,22 F1 90 才是 UDS 数据。
3. 帧结构详解
3.1 阅读帧结构前的约定
以下结构先按最常见的组合说明:
text
Classic CAN + Normal Addressing + 8字节CAN载荷
在这种情况下,N-PCI从Byte 0开始。Byte 0的高4位表示帧类型:
| 高4位 | 帧类型 |
|---|---|
0 |
SF,单帧 |
1 |
FF,首帧 |
2 |
CF,连续帧 |
3 |
FC,流控帧 |
这里的Byte 0指CAN数据区的第一个字节,不包括CAN ID、DLC、CRC等数据链路层字段。
四种CanTp帧的字节结构汇总如下:
表中的
Data[n]表示完整N-SDU中的第n个数据字节,只是位置索引,不代表该字节的实际十六进制数值。例如,Data[6]表示"第7个上层数据字节",不是0xD6。
| 帧类型 | Byte07:4 | Byte03:0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|---|
| SF | 0 |
SF_DL |
Data[0] |
Data[1] |
Data[2] |
Data[3] |
Data[4] |
Data[5] |
Data[6] |
| FF | 1 |
LEN[11:8] |
LEN[7:0] |
Data[0] |
Data[1] |
Data[2] |
Data[3] |
Data[4] |
Data[5] |
| CF | 2 |
SN |
Data[n] |
Data[n+1] |
Data[n+2] |
Data[n+3] |
Data[n+4] |
Data[n+5] |
Data[n+6] |
| FC | 3 |
FS |
BS |
STmin |
Padding | Padding | Padding | Padding | Padding |
表中各字段的作用:
| 字段 | 含义 |
|---|---|
SF_DL |
单帧中有效N-SDU数据的长度 |
LEN[11:8]、LEN[7:0] |
首帧中的12位完整消息长度FF_DL |
SN |
连续帧序号,取值0~F并循环 |
FS |
流控状态:CTS、WAIT或OVFLW |
BS |
一个块中允许连续发送的CF数量 |
STmin |
相邻CF之间的最小发送间隔 |
Data n |
N-SDU的分段数据 |
Padding |
按配置补齐的填充字节,不属于上层有效数据 |
3.2 SF:单帧
当完整消息可以装入一个CAN帧时使用SF。
| 帧类型 | Byte07:4 | Byte03:0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|---|
| SF | 0 |
SF_DL |
Data[0] |
Data[1] |
Data[2] |
Data[3] |
Data[4] |
Data[5] |
Data[6] |
Byte0[7:4]固定为0000b,表示SF;Byte0[3:0]是SF_DL,表示本帧携带的有效N-SDU数据长度;- Normal Addressing下,经典CAN最多携带7字节N-SDU数据。
示例:发送3字节UDS数据22 F1 90。
text
CAN数据区(十六进制):03 22 F1 90 00 00 00 00
逐字节解释:
| 字节 | 数值 | 含义 |
|---|---|---|
| Byte 0 | 0x03 |
高4位0表示SF,低4位3表示有效数据3字节 |
| Byte 1 | 0x22 |
UDS服务SID |
| Byte 2 | 0xF1 |
UDS参数 |
| Byte 3 | 0x90 |
UDS参数 |
| Byte 4~7 | 0x00 |
示例中的填充字节,不属于UDS数据 |
3.3 FF:多帧传输的首帧
完整消息超过单帧容量时,发送方首先发送FF。
| 帧类型 | Byte07:4 | Byte03:0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|---|
| FF | 1 |
LEN[11:8] |
LEN[7:0] |
Data[0] |
Data[1] |
Data[2] |
Data[3] |
Data[4] |
Data[5] |
Byte0[7:4]固定为0001b,表示FF;Byte0[3:0]保存总长度的高4位;Byte1保存总长度的低8位;Byte2~7携带完整消息的前6字节数据。
前12位长度字段共同构成FF_DL:
text
FF_DL = ((Byte0 & 0x0F) << 8) | Byte1
示例:完整消息长度为20字节,即0x014。
text
Byte 0 = 0x10
Byte 1 = 0x14
因此FF的数据区结构为:
| 帧类型标签(不在线上传输) | Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|
| FF | 0x10 |
0x14 |
Data[0] |
Data[1] |
Data[2] |
Data[3] |
Data[4] |
Data[5] |
本例中,Byte0[3:0]=0,Byte1=0x14,因此完整消息长度为20字节。Normal Addressing下,经典CAN的FF使用2字节N-PCI,首帧能携带6字节N-SDU数据。
超过4095字节的长度
12位FF_DL最大表示4095。需要传输更长消息时,可使用扩展FF长度格式:
| 格式 | Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | 后续字节 |
|---|---|---|---|---|---|---|---|
| 扩展FF长度 | 0x10 |
0x00 |
LEN[31:24] |
LEN[23:16] |
LEN[15:8] |
LEN[7:0] |
N-SDU数据 |
Byte 0~1中的12位长度为0,表示真正的32位长度位于Byte 2~5。具体支持范围还受AUTOSAR版本、配置和上层缓冲区能力限制。
3.4 CF:连续帧
FF之后的剩余数据通过CF发送。
| 帧类型 | Byte07:4 | Byte03:0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|---|
| CF | 2 |
SN |
Data[n] |
Data[n+1] |
Data[n+2] |
Data[n+3] |
Data[n+4] |
Data[n+5] |
Data[n+6] |
Byte0[7:4]固定为0010b,表示CF;Byte0[3:0]是连续帧序号SN,范围为0x0~0xF;- Normal Addressing下,每帧最多携带7字节后续数据。
沿用前面"完整消息共20字节"的示例,FF已经携带Data[0]~Data[5],后续两个CF的数据区结构为:
| 帧类型标签(不在线上传输) | Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|
| 第1个CF,SN=1 | 0x21 |
Data[6] |
Data[7] |
Data[8] |
Data[9] |
Data[10] |
Data[11] |
Data[12] |
| 第2个CF,SN=2 | 0x22 |
Data[13] |
Data[14] |
Data[15] |
Data[16] |
Data[17] |
Data[18] |
Data[19] |
SN按照以下方式循环:
text
1 → 2 → 3 → ... → E → F → 0 → 1
接收方发现SN不连续时,应终止本次接收。
3.5 FC:流控帧
接收方收到FF后,用FC通知发送方是否可以继续,以及后续CF的发送节奏。
| 帧类型 | Byte07:4 | Byte03:0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|---|
| FC | 3 |
FS |
BS |
STmin |
Padding | Padding | Padding | Padding | Padding |
Byte0[7:4]固定为0011b,表示FC;Byte0[3:0]是流控状态FS;Byte1是块大小BS;Byte2是最小帧间隔STmin。
FS:Flow Status
| FS | 名称 | 含义 |
|---|---|---|
0 |
CTS | Continue To Send,允许继续发送 |
1 |
WAIT | 接收方暂时没有准备好,发送方继续等待 |
2 |
OVFLW | 缓冲区不足,终止本次传输 |
3~F |
Reserved | 保留值,不能当成CTS处理 |
BS:Block Size
BS规定发送方在下一次等待FC之前,最多可以连续发送多少个CF。
text
BS = 0:后续CF可一直发到消息结束,不再等待新的FC
BS = 8:每发送8个CF,暂停并等待下一帧FC
接收方每成功接收一个CF就增加块计数。达到BS且消息还未结束时,接收方发送新的FC,发送方收到后才能继续。
STmin:Separation Time Minimum
STmin规定相邻CF之间的最小发送间隔。
| STmin编码 | 对应时间 |
|---|---|
0x00~0x7F |
0~127 ms,数值本身就是毫秒数 |
0xF1~0xF9 |
100~900 μs,每增加1代表增加100 μs |
0x80~0xF0 |
保留 |
0xFA~0xFF |
保留 |
例如:
text
STmin = 0x05 → 相邻CF至少间隔5 ms
STmin = 0xF3 → 相邻CF至少间隔300 μs
一帧典型FC:
text
30 08 05 00 00 00 00 00
含义为:
text
0x30:FC.CTS
0x08:每发送8个CF后重新等待FC
0x05:相邻CF至少间隔5 ms
3.6 一次完整多帧传输
假设发送20字节数据,Normal Addressing、经典CAN、BS为8、STmin为5 ms:
接收方 发送方 接收方 发送方 #mermaid-svg-Gf57mveUWhjdCVOs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Gf57mveUWhjdCVOs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Gf57mveUWhjdCVOs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Gf57mveUWhjdCVOs .error-icon{fill:#552222;}#mermaid-svg-Gf57mveUWhjdCVOs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Gf57mveUWhjdCVOs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Gf57mveUWhjdCVOs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Gf57mveUWhjdCVOs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Gf57mveUWhjdCVOs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Gf57mveUWhjdCVOs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Gf57mveUWhjdCVOs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Gf57mveUWhjdCVOs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Gf57mveUWhjdCVOs .marker.cross{stroke:#333333;}#mermaid-svg-Gf57mveUWhjdCVOs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Gf57mveUWhjdCVOs p{margin:0;}#mermaid-svg-Gf57mveUWhjdCVOs .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Gf57mveUWhjdCVOs text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Gf57mveUWhjdCVOs .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Gf57mveUWhjdCVOs .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Gf57mveUWhjdCVOs .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Gf57mveUWhjdCVOs .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Gf57mveUWhjdCVOs #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Gf57mveUWhjdCVOs .sequenceNumber{fill:white;}#mermaid-svg-Gf57mveUWhjdCVOs #sequencenumber{fill:#333;}#mermaid-svg-Gf57mveUWhjdCVOs #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Gf57mveUWhjdCVOs .messageText{fill:#333;stroke:none;}#mermaid-svg-Gf57mveUWhjdCVOs .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Gf57mveUWhjdCVOs .labelText,#mermaid-svg-Gf57mveUWhjdCVOs .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Gf57mveUWhjdCVOs .loopText,#mermaid-svg-Gf57mveUWhjdCVOs .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Gf57mveUWhjdCVOs .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Gf57mveUWhjdCVOs .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Gf57mveUWhjdCVOs .noteText,#mermaid-svg-Gf57mveUWhjdCVOs .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Gf57mveUWhjdCVOs .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Gf57mveUWhjdCVOs .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Gf57mveUWhjdCVOs .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Gf57mveUWhjdCVOs .actorPopupMenu{position:absolute;}#mermaid-svg-Gf57mveUWhjdCVOs .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Gf57mveUWhjdCVOs .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Gf57mveUWhjdCVOs .actor-man circle,#mermaid-svg-Gf57mveUWhjdCVOs line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Gf57mveUWhjdCVOs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 得知总长度20字节 申请接收缓冲区 CTS,BS=8,STmin=5ms 至少间隔5ms 已接收20字节,重组完成 帧类型=FF;CAN数据区=0x10 0x14 Data0~Data5 帧类型=FC;CAN数据区=0x30 0x08 0x05及Padding 帧类型=CF,SN=1;CAN数据区=0x21 Data6~Data12 帧类型=CF,SN=2;CAN数据区=0x22 Data13~Data19
FF携带6字节,两个CF各携带7字节,总计6 + 7 + 7 = 20字节。由于总共只有两个CF,没有达到BS=8,因此不需要第二帧FC。
3.7 寻址方式对字节位置的影响
前面所有示例均为Normal Addressing。Extended Addressing或Mixed Addressing会在N-PCI之前增加一个地址字节,因此N-PCI整体向后移动一字节。
例如Extended Addressing下的SF:
| 寻址格式 | Byte0 | Byte17:4 | Byte13:0 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 |
|---|---|---|---|---|---|---|---|---|---|
| Extended/Mixed SF | 地址信息 | 0 |
SF_DL |
Data[0] |
Data[1] |
Data[2] |
Data[3] |
Data[4] |
Data[5] |
这里的"地址信息"是协议字段,不是示例十六进制值。具体内容取决于配置的寻址格式,例如N_TA或N_AE。
对应载荷能力:
| Classic CAN帧类型 | Normal Addressing | 增加1字节地址信息时 |
|---|---|---|
| SF有效数据 | 7字节 | 6字节 |
| FF首帧数据 | 6字节 | 5字节 |
| CF有效数据 | 7字节 | 6字节 |
因此抓取总线报文时,不能固定认为PCI一定在Byte 0,必须先确认CanTp的寻址格式。
3.8 CAN FD下的单帧长度
CAN FD一帧最多可携带64字节。Normal Addressing下,如果SF数据长度超过低4位能直接表达的范围,可使用SF转义长度格式:
| 格式 | Byte0 | Byte1 | Byte2及后续字节 |
|---|---|---|---|
| CAN FD SF转义长度 | 0x00 |
SF_DL |
Data[0]、Data[1]...... |
Byte 0仍表示SF,但低4位长度为0;Byte 1保存实际SF数据长度;- Normal Addressing下最多可留下62字节空间存放N-SDU数据。
CAN FD报文还要考虑合法DLC对应的载荷长度以及Padding配置。分析报文时,应使用SF_DL或FF_DL判断有效数据长度,不能直接把整个CAN FD载荷都当成上层数据。
4. CanTp的核心组成
从代码角度看,CanTp主要由以下四部分组成:
- 发送状态机;
- 接收状态机;
- 协议定时器;
- 上下层接口和数据搬运。
CanTp通常不会保存完整的诊断消息。完整缓冲区由Dcm等上层模块管理,CanTp通过PduR回调分段读取或写入数据,只保存当前连接的传输状态和少量帧数据。
5. 发送流程
5.1 单帧发送
当消息能够放入一个 CAN 帧时,发送过程为:
CanIf CanTp PduR Dcm CanIf CanTp PduR Dcm #mermaid-svg-F6PAahtjVAGQPdTC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-F6PAahtjVAGQPdTC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-F6PAahtjVAGQPdTC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-F6PAahtjVAGQPdTC .error-icon{fill:#552222;}#mermaid-svg-F6PAahtjVAGQPdTC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-F6PAahtjVAGQPdTC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-F6PAahtjVAGQPdTC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-F6PAahtjVAGQPdTC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-F6PAahtjVAGQPdTC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-F6PAahtjVAGQPdTC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-F6PAahtjVAGQPdTC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-F6PAahtjVAGQPdTC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-F6PAahtjVAGQPdTC .marker.cross{stroke:#333333;}#mermaid-svg-F6PAahtjVAGQPdTC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-F6PAahtjVAGQPdTC p{margin:0;}#mermaid-svg-F6PAahtjVAGQPdTC .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-F6PAahtjVAGQPdTC text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-F6PAahtjVAGQPdTC .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-F6PAahtjVAGQPdTC .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-F6PAahtjVAGQPdTC .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-F6PAahtjVAGQPdTC .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-F6PAahtjVAGQPdTC #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-F6PAahtjVAGQPdTC .sequenceNumber{fill:white;}#mermaid-svg-F6PAahtjVAGQPdTC #sequencenumber{fill:#333;}#mermaid-svg-F6PAahtjVAGQPdTC #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-F6PAahtjVAGQPdTC .messageText{fill:#333;stroke:none;}#mermaid-svg-F6PAahtjVAGQPdTC .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-F6PAahtjVAGQPdTC .labelText,#mermaid-svg-F6PAahtjVAGQPdTC .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-F6PAahtjVAGQPdTC .loopText,#mermaid-svg-F6PAahtjVAGQPdTC .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-F6PAahtjVAGQPdTC .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-F6PAahtjVAGQPdTC .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-F6PAahtjVAGQPdTC .noteText,#mermaid-svg-F6PAahtjVAGQPdTC .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-F6PAahtjVAGQPdTC .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-F6PAahtjVAGQPdTC .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-F6PAahtjVAGQPdTC .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-F6PAahtjVAGQPdTC .actorPopupMenu{position:absolute;}#mermaid-svg-F6PAahtjVAGQPdTC .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-F6PAahtjVAGQPdTC .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-F6PAahtjVAGQPdTC .actor-man circle,#mermaid-svg-F6PAahtjVAGQPdTC line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-F6PAahtjVAGQPdTC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 请求发送完整消息 CanTp_Transmit() PduR_CanTpCopyTxData() 返回待发送数据 CanIf_Transmit(SF) CanTp_TxConfirmation() PduR_CanTpTxConfirmation(E_OK) 整条消息发送结束
5.2 多帧发送
多帧发送的主状态如下:
#mermaid-svg-SuDSvqY9YsC8HqZO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SuDSvqY9YsC8HqZO .error-icon{fill:#552222;}#mermaid-svg-SuDSvqY9YsC8HqZO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SuDSvqY9YsC8HqZO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SuDSvqY9YsC8HqZO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SuDSvqY9YsC8HqZO .marker.cross{stroke:#333333;}#mermaid-svg-SuDSvqY9YsC8HqZO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SuDSvqY9YsC8HqZO p{margin:0;}#mermaid-svg-SuDSvqY9YsC8HqZO defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-SuDSvqY9YsC8HqZO g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-SuDSvqY9YsC8HqZO g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-SuDSvqY9YsC8HqZO g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-SuDSvqY9YsC8HqZO g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-SuDSvqY9YsC8HqZO g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-SuDSvqY9YsC8HqZO .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-SuDSvqY9YsC8HqZO .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-SuDSvqY9YsC8HqZO .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-SuDSvqY9YsC8HqZO .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-SuDSvqY9YsC8HqZO .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-SuDSvqY9YsC8HqZO .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-SuDSvqY9YsC8HqZO .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-SuDSvqY9YsC8HqZO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SuDSvqY9YsC8HqZO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SuDSvqY9YsC8HqZO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SuDSvqY9YsC8HqZO .edgeLabel .label text{fill:#333;}#mermaid-svg-SuDSvqY9YsC8HqZO .label div .edgeLabel{color:#333;}#mermaid-svg-SuDSvqY9YsC8HqZO .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-SuDSvqY9YsC8HqZO .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-SuDSvqY9YsC8HqZO .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-SuDSvqY9YsC8HqZO .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-SuDSvqY9YsC8HqZO .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-SuDSvqY9YsC8HqZO .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SuDSvqY9YsC8HqZO .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SuDSvqY9YsC8HqZO #statediagram-barbEnd{fill:#333333;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SuDSvqY9YsC8HqZO .cluster-label,#mermaid-svg-SuDSvqY9YsC8HqZO .nodeLabel{color:#131300;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-SuDSvqY9YsC8HqZO .note-edge{stroke-dasharray:5;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-note text{fill:black;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram-note .nodeLabel{color:black;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagram .edgeLabel{color:red;}#mermaid-svg-SuDSvqY9YsC8HqZO #dependencyStart,#mermaid-svg-SuDSvqY9YsC8HqZO #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-SuDSvqY9YsC8HqZO .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SuDSvqY9YsC8HqZO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CanTp_Transmit
CanIf接受FF
FF发送确认
收到FC.CTS
收到FC.WAIT
FC.OVFLW或超时
发送一个CF
未达到BS且数据未结束
达到BS
数据发送完毕
Idle
SendFF
WaitTxConfirm
WaitFC
SendCF
Abort
WaitCFConfirm
Finished
具体过程:
- PduR调用
CanTp_Transmit(); - CanTp记录总长度并选择SF或FF;
- 通过
PduR_CanTpCopyTxData()获取当前分段数据; - 调用
CanIf_Transmit()发送FF; - 收到FF的发送确认后进入等待FC状态;
- 解析FC中的FS、BS和STmin;
- 按照STmin发送CF,并维护SN和块计数;
- 达到BS后暂停,等待下一帧FC;
- 全部数据发送完成后,通过PduR通知上层。
需要注意:CanTp_TxConfirmation()只表示一个底层CAN帧完成发送,不表示远端ECU已经处理了UDS消息。
6. 接收流程
6.1 单帧接收
Dcm PduR CanTp CanIf Dcm PduR CanTp CanIf #mermaid-svg-Fkk2khocTTqikK9n{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Fkk2khocTTqikK9n .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fkk2khocTTqikK9n .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fkk2khocTTqikK9n .error-icon{fill:#552222;}#mermaid-svg-Fkk2khocTTqikK9n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fkk2khocTTqikK9n .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fkk2khocTTqikK9n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fkk2khocTTqikK9n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fkk2khocTTqikK9n .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fkk2khocTTqikK9n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fkk2khocTTqikK9n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fkk2khocTTqikK9n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fkk2khocTTqikK9n .marker.cross{stroke:#333333;}#mermaid-svg-Fkk2khocTTqikK9n svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fkk2khocTTqikK9n p{margin:0;}#mermaid-svg-Fkk2khocTTqikK9n .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fkk2khocTTqikK9n text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Fkk2khocTTqikK9n .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Fkk2khocTTqikK9n .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Fkk2khocTTqikK9n .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Fkk2khocTTqikK9n .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Fkk2khocTTqikK9n #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Fkk2khocTTqikK9n .sequenceNumber{fill:white;}#mermaid-svg-Fkk2khocTTqikK9n #sequencenumber{fill:#333;}#mermaid-svg-Fkk2khocTTqikK9n #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Fkk2khocTTqikK9n .messageText{fill:#333;stroke:none;}#mermaid-svg-Fkk2khocTTqikK9n .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fkk2khocTTqikK9n .labelText,#mermaid-svg-Fkk2khocTTqikK9n .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Fkk2khocTTqikK9n .loopText,#mermaid-svg-Fkk2khocTTqikK9n .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Fkk2khocTTqikK9n .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Fkk2khocTTqikK9n .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Fkk2khocTTqikK9n .noteText,#mermaid-svg-Fkk2khocTTqikK9n .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Fkk2khocTTqikK9n .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fkk2khocTTqikK9n .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fkk2khocTTqikK9n .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Fkk2khocTTqikK9n .actorPopupMenu{position:absolute;}#mermaid-svg-Fkk2khocTTqikK9n .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Fkk2khocTTqikK9n .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Fkk2khocTTqikK9n .actor-man circle,#mermaid-svg-Fkk2khocTTqikK9n line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Fkk2khocTTqikK9n :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CanTp_RxIndication(SF) PduR_CanTpStartOfReception() 返回缓冲区状态 PduR_CanTpCopyRxData() PduR_CanTpRxIndication(E_OK) 完整消息可用
6.2 多帧接收
#mermaid-svg-KdJD9H87ljvykIPM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KdJD9H87ljvykIPM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KdJD9H87ljvykIPM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KdJD9H87ljvykIPM .error-icon{fill:#552222;}#mermaid-svg-KdJD9H87ljvykIPM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KdJD9H87ljvykIPM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KdJD9H87ljvykIPM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KdJD9H87ljvykIPM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KdJD9H87ljvykIPM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KdJD9H87ljvykIPM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KdJD9H87ljvykIPM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KdJD9H87ljvykIPM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KdJD9H87ljvykIPM .marker.cross{stroke:#333333;}#mermaid-svg-KdJD9H87ljvykIPM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KdJD9H87ljvykIPM p{margin:0;}#mermaid-svg-KdJD9H87ljvykIPM defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-KdJD9H87ljvykIPM g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-KdJD9H87ljvykIPM g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-KdJD9H87ljvykIPM g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-KdJD9H87ljvykIPM g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-KdJD9H87ljvykIPM g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-KdJD9H87ljvykIPM .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-KdJD9H87ljvykIPM .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-KdJD9H87ljvykIPM .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-KdJD9H87ljvykIPM .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-KdJD9H87ljvykIPM .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-KdJD9H87ljvykIPM .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-KdJD9H87ljvykIPM .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-KdJD9H87ljvykIPM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KdJD9H87ljvykIPM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KdJD9H87ljvykIPM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KdJD9H87ljvykIPM .edgeLabel .label text{fill:#333;}#mermaid-svg-KdJD9H87ljvykIPM .label div .edgeLabel{color:#333;}#mermaid-svg-KdJD9H87ljvykIPM .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-KdJD9H87ljvykIPM .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-KdJD9H87ljvykIPM .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-KdJD9H87ljvykIPM .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-KdJD9H87ljvykIPM .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-KdJD9H87ljvykIPM .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KdJD9H87ljvykIPM .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KdJD9H87ljvykIPM #statediagram-barbEnd{fill:#333333;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KdJD9H87ljvykIPM .cluster-label,#mermaid-svg-KdJD9H87ljvykIPM .nodeLabel{color:#131300;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-KdJD9H87ljvykIPM .note-edge{stroke-dasharray:5;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-note text{fill:black;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram-note .nodeLabel{color:black;}#mermaid-svg-KdJD9H87ljvykIPM .statediagram .edgeLabel{color:red;}#mermaid-svg-KdJD9H87ljvykIPM #dependencyStart,#mermaid-svg-KdJD9H87ljvykIPM #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-KdJD9H87ljvykIPM .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KdJD9H87ljvykIPM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 收到FF
上层缓冲区可用
缓冲区溢出或格式错误
FC发送确认
收到正确SN的CF
N_Cr超时或SN错误
未达到BS且数据未结束
达到BS
完整消息接收完毕
Idle
ReceiveFF
SendFC
Abort
WaitCF
CopyCF
Finished
具体过程:
- CanIf通过
CanTp_RxIndication()交付收到的CAN帧; - CanTp解析PCI,判断是SF、FF、CF还是FC;
- 收到FF后解析完整消息长度;
- 调用
PduR_CanTpStartOfReception()申请上层缓冲区; - 根据缓冲区状态发送
FC.CTS、FC.WAIT或FC.OVFLW; - 接收CF,检查SN并通过
PduR_CanTpCopyRxData()写入上层缓冲区; - 达到BS后重新发送FC;
- 完整消息接收后调用
PduR_CanTpRxIndication(E_OK); - 发生超时、序号错误或长度错误时,以
E_NOT_OK结束本次接收。
7. 主要接口
7.1 CanTp提供给上层的接口
c
CanTp_Transmit();
CanTp_CancelTransmit();
CanTp_CancelReceive();
CanTp_ChangeParameter();
CanTp_ReadParameter();
其中 CanTp_Transmit() 用于启动一次传输。它通常只初始化运行时状态,实际分段发送由后续回调和周期函数完成。
7.2 CanTp调用PduR的接口
c
PduR_CanTpStartOfReception();
PduR_CanTpCopyRxData();
PduR_CanTpRxIndication();
PduR_CanTpCopyTxData();
PduR_CanTpTxConfirmation();
| 接口 | 作用 |
|---|---|
| StartOfReception | 确认上层是否能够接收完整消息 |
| CopyRxData | 将当前接收分段写入上层缓冲区 |
| RxIndication | 通知上层整条消息接收成功或失败 |
| CopyTxData | 从上层缓冲区取得下一段待发送数据 |
| TxConfirmation | 通知上层整条消息发送成功或失败 |
7.3 下层调用CanTp的接口
c
CanTp_RxIndication();
CanTp_TxConfirmation();
CanTp_RxIndication():收到SF、FF、CF或FC;CanTp_TxConfirmation():CanTp提交给下层的一个CAN帧已经发送完成或失败。
8. 时间参数与超时
CanTp的时间参数分成两类:
STmin:接收方通过FC告诉发送方的帧间最小间隔;N_*定时器:本地协议状态机用来检测等待事件是否超时。
二者不能混为一谈。STmin控制正常发送节奏,N系列定时器负责发现通信流程卡住。
8.1 六个N定时器
| 定时器 | 所在角色 | 开始计时的典型事件 | 等待的目标事件 |
|---|---|---|---|
| N_As | 消息发送方 | 请求下层发送SF、FF或CF | 对应N-PDU的发送确认 |
| N_Ar | 消息接收方 | 请求下层发送FC | FC的发送确认 |
| N_Bs | 消息发送方 | FF发送完成,或一个BS块发送完毕 | 接收下一帧FC |
| N_Br | 消息接收方 | 收到FF,或收到一个完整CF块 | 准备并提交下一帧FC |
| N_Cs | 消息发送方 | 收到FC.CTS,或上一CF进入后续处理 | 准备并提交下一帧CF |
| N_Cr | 消息接收方 | FC发送完成,或收到上一帧CF | 接收下一帧CF |
不同AUTOSAR栈的内部状态命名可能不同,但判断原则一致:确认发送是否完成、FC是否按时到达、CF是否按时到达。
8.2 N_As:发送方等待发送确认
CanIf/Can Driver CanTp发送方 CanIf/Can Driver CanTp发送方 #mermaid-svg-c4tfxKDKDIaXjzNJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-c4tfxKDKDIaXjzNJ .error-icon{fill:#552222;}#mermaid-svg-c4tfxKDKDIaXjzNJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-c4tfxKDKDIaXjzNJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-c4tfxKDKDIaXjzNJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-c4tfxKDKDIaXjzNJ .marker.cross{stroke:#333333;}#mermaid-svg-c4tfxKDKDIaXjzNJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-c4tfxKDKDIaXjzNJ p{margin:0;}#mermaid-svg-c4tfxKDKDIaXjzNJ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-c4tfxKDKDIaXjzNJ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-c4tfxKDKDIaXjzNJ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-c4tfxKDKDIaXjzNJ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-c4tfxKDKDIaXjzNJ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-c4tfxKDKDIaXjzNJ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-c4tfxKDKDIaXjzNJ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-c4tfxKDKDIaXjzNJ .sequenceNumber{fill:white;}#mermaid-svg-c4tfxKDKDIaXjzNJ #sequencenumber{fill:#333;}#mermaid-svg-c4tfxKDKDIaXjzNJ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-c4tfxKDKDIaXjzNJ .messageText{fill:#333;stroke:none;}#mermaid-svg-c4tfxKDKDIaXjzNJ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-c4tfxKDKDIaXjzNJ .labelText,#mermaid-svg-c4tfxKDKDIaXjzNJ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-c4tfxKDKDIaXjzNJ .loopText,#mermaid-svg-c4tfxKDKDIaXjzNJ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-c4tfxKDKDIaXjzNJ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-c4tfxKDKDIaXjzNJ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-c4tfxKDKDIaXjzNJ .noteText,#mermaid-svg-c4tfxKDKDIaXjzNJ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-c4tfxKDKDIaXjzNJ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-c4tfxKDKDIaXjzNJ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-c4tfxKDKDIaXjzNJ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-c4tfxKDKDIaXjzNJ .actorPopupMenu{position:absolute;}#mermaid-svg-c4tfxKDKDIaXjzNJ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-c4tfxKDKDIaXjzNJ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-c4tfxKDKDIaXjzNJ .actor-man circle,#mermaid-svg-c4tfxKDKDIaXjzNJ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-c4tfxKDKDIaXjzNJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动N_As 停止N_As CanIf_Transmit(SF/FF/CF) CanTp_TxConfirmation(E_OK)
如果在N_As内没有收到发送确认,说明帧可能一直未被下层接受、CAN控制器长时间繁忙或总线状态异常。CanTp终止当前发送并向上层报告失败。
8.3 N_Ar:接收方等待FC发送确认
N_Ar与N_As相似,但专门站在消息接收方观察FC发送:
text
请求发送FC → 启动N_Ar → 收到FC的TxConfirmation → 停止N_Ar
N_Ar超时后,接收方无法确认FC已经发出,本次接收终止。
8.4 N_Bs与N_Br:等待和产生FC
消息接收方 消息发送方 消息接收方 消息发送方 #mermaid-svg-wVWO5sspwbe5wvSY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wVWO5sspwbe5wvSY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wVWO5sspwbe5wvSY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wVWO5sspwbe5wvSY .error-icon{fill:#552222;}#mermaid-svg-wVWO5sspwbe5wvSY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wVWO5sspwbe5wvSY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wVWO5sspwbe5wvSY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wVWO5sspwbe5wvSY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wVWO5sspwbe5wvSY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wVWO5sspwbe5wvSY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wVWO5sspwbe5wvSY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wVWO5sspwbe5wvSY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wVWO5sspwbe5wvSY .marker.cross{stroke:#333333;}#mermaid-svg-wVWO5sspwbe5wvSY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wVWO5sspwbe5wvSY p{margin:0;}#mermaid-svg-wVWO5sspwbe5wvSY .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wVWO5sspwbe5wvSY text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-wVWO5sspwbe5wvSY .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wVWO5sspwbe5wvSY .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-wVWO5sspwbe5wvSY .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-wVWO5sspwbe5wvSY .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-wVWO5sspwbe5wvSY #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-wVWO5sspwbe5wvSY .sequenceNumber{fill:white;}#mermaid-svg-wVWO5sspwbe5wvSY #sequencenumber{fill:#333;}#mermaid-svg-wVWO5sspwbe5wvSY #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-wVWO5sspwbe5wvSY .messageText{fill:#333;stroke:none;}#mermaid-svg-wVWO5sspwbe5wvSY .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wVWO5sspwbe5wvSY .labelText,#mermaid-svg-wVWO5sspwbe5wvSY .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-wVWO5sspwbe5wvSY .loopText,#mermaid-svg-wVWO5sspwbe5wvSY .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-wVWO5sspwbe5wvSY .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wVWO5sspwbe5wvSY .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-wVWO5sspwbe5wvSY .noteText,#mermaid-svg-wVWO5sspwbe5wvSY .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-wVWO5sspwbe5wvSY .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wVWO5sspwbe5wvSY .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wVWO5sspwbe5wvSY .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wVWO5sspwbe5wvSY .actorPopupMenu{position:absolute;}#mermaid-svg-wVWO5sspwbe5wvSY .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-wVWO5sspwbe5wvSY .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wVWO5sspwbe5wvSY .actor-man circle,#mermaid-svg-wVWO5sspwbe5wvSY line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-wVWO5sspwbe5wvSY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动N_Bs,等待FC 启动N_Br,准备FC FC提交后转入N_Ar阶段 收到FC后停止N_Bs FF或一个完整CF块 FC.CTS / FC.WAIT / FC.OVFLW
N_Bs是发送方的等待上限。如果收不到FC,发送方不能继续发送CF;N_Br是接收方准备下一帧FC的时间预算,包括检查缓冲区等处理;- 接收方暂时没有缓冲区时可以发送FC.WAIT,但连续WAIT次数不得超过WFTmax;
- 收到FC.WAIT后,发送方继续等待,并按照协议规则重新监控N_Bs;
- 收到FC.OVFLW后立即终止传输。
8.5 N_Cs、N_Cr与STmin:连续帧阶段
消息接收方 消息发送方 消息接收方 消息发送方 #mermaid-svg-voqUkZMDRlUXtpt7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-voqUkZMDRlUXtpt7 .error-icon{fill:#552222;}#mermaid-svg-voqUkZMDRlUXtpt7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-voqUkZMDRlUXtpt7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-voqUkZMDRlUXtpt7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-voqUkZMDRlUXtpt7 .marker.cross{stroke:#333333;}#mermaid-svg-voqUkZMDRlUXtpt7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-voqUkZMDRlUXtpt7 p{margin:0;}#mermaid-svg-voqUkZMDRlUXtpt7 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-voqUkZMDRlUXtpt7 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-voqUkZMDRlUXtpt7 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-voqUkZMDRlUXtpt7 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-voqUkZMDRlUXtpt7 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-voqUkZMDRlUXtpt7 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-voqUkZMDRlUXtpt7 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-voqUkZMDRlUXtpt7 .sequenceNumber{fill:white;}#mermaid-svg-voqUkZMDRlUXtpt7 #sequencenumber{fill:#333;}#mermaid-svg-voqUkZMDRlUXtpt7 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-voqUkZMDRlUXtpt7 .messageText{fill:#333;stroke:none;}#mermaid-svg-voqUkZMDRlUXtpt7 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-voqUkZMDRlUXtpt7 .labelText,#mermaid-svg-voqUkZMDRlUXtpt7 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-voqUkZMDRlUXtpt7 .loopText,#mermaid-svg-voqUkZMDRlUXtpt7 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-voqUkZMDRlUXtpt7 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-voqUkZMDRlUXtpt7 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-voqUkZMDRlUXtpt7 .noteText,#mermaid-svg-voqUkZMDRlUXtpt7 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-voqUkZMDRlUXtpt7 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-voqUkZMDRlUXtpt7 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-voqUkZMDRlUXtpt7 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-voqUkZMDRlUXtpt7 .actorPopupMenu{position:absolute;}#mermaid-svg-voqUkZMDRlUXtpt7 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-voqUkZMDRlUXtpt7 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-voqUkZMDRlUXtpt7 .actor-man circle,#mermaid-svg-voqUkZMDRlUXtpt7 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-voqUkZMDRlUXtpt7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 准备CF,受N_Cs和STmin约束 FC发送完成后启动N_Cr 收到CF1,重启N_Cr 相邻CF至少满足STmin FC.CTS,携带BS和STmin CF1 CF2
N_Cs约束发送方准备下一帧CF不能拖得过久;STmin要求发送方不能发得过快;N_Cr约束接收方等待下一帧CF不能无限等待;- 如果SN错误或N_Cr超时,接收方终止重组并向上层报告失败。
可以把这一阶段理解为:发送方必须在"不早于STmin、又不晚于协议超时"的时间窗口内继续发送CF。
8.6 定时器与主函数周期
CanTp通常通过周期调用的CanTp_MainFunction()维护软件定时器。假设:
text
CanTp_MainFunction周期 = 1 ms
N_Bs配置值 = 1000 ms
实现中通常换算为约1000个主函数周期。若主函数被延迟调度,实际超时精度也会受到影响。STmin为微秒级时,还可能需要更高精度的计时机制,不能只依赖较慢的毫秒级主函数。
8.7 与UDS P2/P2*的区别
N_As/N_Bs/N_Cr等是CanTp传输层定时器;P2/P2*是UDS层等待诊断响应的时间参数。
text
CanTp定时器:一帧是否按时发送、FC/CF是否按时到达
UDS P2/P2*:诊断请求发出后,ECU是否按时给出诊断响应
CanTp传输正常不代表UDS业务一定按时完成,两层需要分别分析。
9. 代码实现框架
以下代码仅用于说明实现结构,不是完整的AUTOSAR产品代码。
9.1 运行时状态
c
typedef enum
{
CANTP_IDLE,
CANTP_TX_SEND_SF,
CANTP_TX_SEND_FF,
CANTP_TX_WAIT_CONFIRM,
CANTP_TX_WAIT_FC,
CANTP_TX_SEND_CF,
CANTP_RX_SEND_FC,
CANTP_RX_WAIT_CF
} CanTp_StateType;
typedef struct
{
CanTp_StateType state;
uint32 totalLength;
uint32 processedLength;
uint8 sequenceNumber;
uint8 blockSize;
uint8 blockCounter;
uint8 stMin;
uint32 timer;
PduIdType upperPduId;
PduIdType lowerPduId;
} CanTp_ChannelType;
实际实现通常为每个通道维护一份运行时上下文,并通过临界区保护回调函数与周期函数共享的数据。
9.2 发送入口
c
Std_ReturnType CanTp_Transmit(
PduIdType id,
const PduInfoType *info)
{
CanTp_ChannelType *channel = FindTxChannel(id);
if ((channel == NULL) || (channel->state != CANTP_IDLE))
{
return E_NOT_OK;
}
channel->totalLength = info->SduLength;
channel->processedLength = 0u;
channel->sequenceNumber = 1u;
channel->state = FitsInSingleFrame(info->SduLength)
? CANTP_TX_SEND_SF
: CANTP_TX_SEND_FF;
return E_OK;
}
9.3 接收回调
c
void CanTp_RxIndication(
PduIdType rxPduId,
const PduInfoType *pdu)
{
uint8 frameType = GetPciType(pdu->SduDataPtr);
switch (frameType)
{
case CANTP_PCI_SF:
HandleSingleFrame(rxPduId, pdu);
break;
case CANTP_PCI_FF:
HandleFirstFrame(rxPduId, pdu);
break;
case CANTP_PCI_CF:
HandleConsecutiveFrame(rxPduId, pdu);
break;
case CANTP_PCI_FC:
HandleFlowControl(rxPduId, pdu);
break;
default:
HandleInvalidFrame(rxPduId);
break;
}
}
9.4 周期函数
c
void CanTp_MainFunction(void)
{
for (each channel)
{
UpdateTimers(channel);
switch (channel->state)
{
case CANTP_TX_SEND_SF:
BuildAndTransmitSingleFrame(channel);
break;
case CANTP_TX_SEND_FF:
BuildAndTransmitFirstFrame(channel);
break;
case CANTP_TX_WAIT_FC:
CheckFlowControlTimeout(channel);
break;
case CANTP_TX_SEND_CF:
if (StMinExpired(channel))
{
BuildAndTransmitConsecutiveFrame(channel);
}
break;
case CANTP_RX_SEND_FC:
BuildAndTransmitFlowControl(channel);
break;
case CANTP_RX_WAIT_CF:
CheckConsecutiveFrameTimeout(channel);
break;
default:
break;
}
}
}
CanTp_MainFunction()由OS周期调用,主要负责更新定时器、检查超时以及推动状态机运行。
9.5 发送确认
c
void CanTp_TxConfirmation(
PduIdType txPduId,
Std_ReturnType result)
{
CanTp_ChannelType *channel = FindChannel(txPduId);
if (result != E_OK)
{
AbortTransmission(channel);
return;
}
switch (channel->lastTransmittedFrame)
{
case CANTP_PCI_SF:
FinishTransmission(channel);
break;
case CANTP_PCI_FF:
channel->state = CANTP_TX_WAIT_FC;
StartNbsTimer(channel);
break;
case CANTP_PCI_CF:
ContinueOrFinishCfTransmission(channel);
break;
case CANTP_PCI_FC:
channel->state = CANTP_RX_WAIT_CF;
StartNcrTimer(channel);
break;
}
}
10. 主要配置项
CanTp配置通常由DaVinci Configurator、EB tresos等工具根据ARXML生成,常见内容包括:
- Tx和Rx N-SDU;
- 上层PduId及CanIf PduId;
- 数据帧和FC帧的映射;
- Normal、Extended、Mixed等寻址格式;
- 物理寻址或功能寻址;
- BS、STmin和WFTmax;
- N_As、N_Bs、N_Cr等超时参数;
- Padding配置;
- Classic CAN或CAN FD;
- 通道及连接资源。
功能寻址通常限制为单帧传输。因为功能寻址属于广播式请求,如果允许多帧,多个ECU可能同时发送FC或响应,造成总线冲突。
11. 阅读和调试代码的方法
阅读一个具体CanTp实现时,建议先定位以下四个入口:
text
CanTp_Transmit()
CanTp_RxIndication()
CanTp_TxConfirmation()
CanTp_MainFunction()
然后沿四条典型链路分析:
11.1 单帧发送
text
CanTp_Transmit
→ PduR_CanTpCopyTxData
→ CanIf_Transmit(SF)
→ CanTp_TxConfirmation
→ PduR_CanTpTxConfirmation
11.2 多帧发送
text
CanTp_Transmit
→ 发送FF
→ 等待FC
→ 按BS和STmin发送CF
→ 整条消息发送完成通知
11.3 单帧接收
text
CanTp_RxIndication(SF)
→ PduR_CanTpStartOfReception
→ PduR_CanTpCopyRxData
→ PduR_CanTpRxIndication
11.4 多帧接收
text
CanTp_RxIndication(FF)
→ 申请上层缓冲区
→ 发送FC
→ 接收并检查CF
→ 分段复制数据
→ 整条消息接收完成通知
出现传输失败时,按以下顺序排查:
- PDU ID与CAN ID映射是否正确;
- CanTp寻址格式是否一致;
- FF之后是否收到FC;
- FC中的BS和STmin是否正确解析;
- CF的SN是否连续;
- 是否发生N_As、N_Bs或N_Cr超时;
- PduR是否提供了足够的收发缓冲区;
CanIf_Transmit()和发送确认是否正常;- Classic CAN、CAN FD及Padding配置是否一致。
12. 总结
CanTp的运行逻辑可以归纳为:
text
发送:取得完整消息 → 生成SF或FF/CF → 等待确认和FC → 完成通知
接收:识别SF或FF/CF → 申请缓冲区 → 发送FC → 重组 → 完成通知
代码实现的重点不是帧格式本身,而是以下四项之间的配合:
- 状态机维护当前传输阶段;
- 定时器约束各阶段的等待时间;
- PduR回调完成上层数据搬运;
- CanIf接口完成单个CAN分段的收发。
参考资料
- AUTOSAR Specification of CAN Transport Layer, R24-11
- AUTOSAR Specification of PDU Router, R24-11
- ISO 15765-2: Road vehicles --- Diagnostic communication over Controller Area Network (DoCAN) --- Transport protocol and network layer services