AUTOSAR CanTp 模块学习笔记

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 服务,例如 0x220x270x31
  • 不决定 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]=0Byte1=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主要由以下四部分组成:

  1. 发送状态机;
  2. 接收状态机;
  3. 协议定时器;
  4. 上下层接口和数据搬运。

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

具体过程:

  1. PduR调用 CanTp_Transmit()
  2. CanTp记录总长度并选择SF或FF;
  3. 通过 PduR_CanTpCopyTxData() 获取当前分段数据;
  4. 调用 CanIf_Transmit() 发送FF;
  5. 收到FF的发送确认后进入等待FC状态;
  6. 解析FC中的FS、BS和STmin;
  7. 按照STmin发送CF,并维护SN和块计数;
  8. 达到BS后暂停,等待下一帧FC;
  9. 全部数据发送完成后,通过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

具体过程:

  1. CanIf通过 CanTp_RxIndication() 交付收到的CAN帧;
  2. CanTp解析PCI,判断是SF、FF、CF还是FC;
  3. 收到FF后解析完整消息长度;
  4. 调用 PduR_CanTpStartOfReception() 申请上层缓冲区;
  5. 根据缓冲区状态发送 FC.CTSFC.WAITFC.OVFLW
  6. 接收CF,检查SN并通过 PduR_CanTpCopyRxData() 写入上层缓冲区;
  7. 达到BS后重新发送FC;
  8. 完整消息接收后调用 PduR_CanTpRxIndication(E_OK)
  9. 发生超时、序号错误或长度错误时,以 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的时间参数分成两类:

  1. STmin:接收方通过FC告诉发送方的帧间最小间隔;
  2. 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
→ 分段复制数据
→ 整条消息接收完成通知

出现传输失败时,按以下顺序排查:

  1. PDU ID与CAN ID映射是否正确;
  2. CanTp寻址格式是否一致;
  3. FF之后是否收到FC;
  4. FC中的BS和STmin是否正确解析;
  5. CF的SN是否连续;
  6. 是否发生N_As、N_Bs或N_Cr超时;
  7. PduR是否提供了足够的收发缓冲区;
  8. CanIf_Transmit()和发送确认是否正常;
  9. Classic CAN、CAN FD及Padding配置是否一致。

12. 总结

CanTp的运行逻辑可以归纳为:

text 复制代码
发送:取得完整消息 → 生成SF或FF/CF → 等待确认和FC → 完成通知
接收:识别SF或FF/CF → 申请缓冲区 → 发送FC → 重组 → 完成通知

代码实现的重点不是帧格式本身,而是以下四项之间的配合:

  • 状态机维护当前传输阶段;
  • 定时器约束各阶段的等待时间;
  • PduR回调完成上层数据搬运;
  • CanIf接口完成单个CAN分段的收发。

参考资料

相关推荐
Hello_Damon_Nikola1 小时前
扰流筋从原理到应用
人工智能·单片机·嵌入式硬件
某林2121 小时前
从“仿真能跑”到“真机能走”:一套轮腿机械狗强化学习系统的工程化实践
人工智能·stm32·嵌入式硬件·架构·机器人·机械狗
luiyarch2 小时前
IP8到底是什么?
嵌入式硬件·车载系统
hongmai6668882 小时前
ESP32-WROVER-B-N8R8:8MB PSRAM加持的经典双核模组,现在还值得选吗?
单片机·嵌入式硬件·物联网·智能路由器·risc-v
VALENIAN瓦伦尼安教学设备3 小时前
ASHOOTER激光对中仪如何通过颜色确定调整是否合适
数据库·嵌入式硬件·算法
0x3F(小茶)3 小时前
复位异常(Cortex-M处理器)
单片机·mcu
SilicoCode4 小时前
STM32入门教程:BKP备份寄存器 & RTC实时时钟详解
stm32·单片机·嵌入式硬件
lhbweilai4 小时前
STM32 简单认识一下FreeRTOS(纯理论)
stm32·单片机·嵌入式硬件
0x3F(小茶)4 小时前
FreeRTOS 中断配置和临界段
单片机·嵌入式硬件