如 与 CAN 交互 中所述,ECU 通常会通过 CAN 提供诊断功能,并遵循若干标准文档的要求:
诊断涉及客户端(诊断工具)与服务器(ECU)之间的大量数据传输,例如将新的固件文件发送到 ECU。 CAN 帧的有效载荷最多只能容纳 8 字节(对于 CAN FD 则为 64 字节),因此最好使用传输层来支持更大有效载荷的传输。 ISO-TP(ISO 15765-2)(有时也称为 CAN-TP)是一种传输层,旨在促进此类数据传输。
ISO-TP(ISO 15765-2)是 UDS 和 KWP2000 使用的传输层。它允许传输最大达 4095 字节的有效载荷。其工作原理是将每个 CAN 帧的第一个字节(或多个字节)用作报头,这一点与 IP 协议占用以太网帧前几个字节的方式类似。
ISO-TP 通常用于建立一对一的通信通道:一个客户端和一个服务器。客户端通过发送具有特定(静态)标识符的 CAN 帧来发出请求,而服务器则通过发送具有另一个特定(静态)标识符的 CAN 帧进行响应。例如,RAMN 的 ECU B 监听 ID 为 0x7e1 的 CAN 帧(由诊断工具发送),并将其解释为 ISO-TP 帧,然后通过发送 ID 为 0x7e9 的 CAN 帧作出应答。这称为正常寻址 ,但还有其他类型的寻址方式(参见寻址)。
ISO-TP 报头采用动态大小的报头(协议控制信息,PCI)。报头的类型由报头的第一个半字节( 前四位 )定义。共有四种类型的报头:
尽管 ISO-TP 具备流控机制,但它并未包含用于确认帧已正确重组的“确认”机制。表示 ISO-TP 数据的 CAN 帧可能会用任意值进行填充(常见的填充值包括 0xCC、0xAA 和 0x00),以确保其始终为 8 字节长度。
单帧(报头格式为“0X”)是简单的 ISO-TP 帧:其报头表明,整个 ISO-TP 有效载荷包含在 CAN 帧中,位于报头之后。这意味着该 ISO-TP 帧的长度为 7 字节或更短,否则有效载荷及其一字节的报头将无法容纳在一个单独的 CAN 帧中。
第二个半字节的值表示 ISO-TP 有效载荷的长度。例如,“01”表示一个字节的 ISO-TP 有效载荷,“05”则表示五字节的 ISO-TP 有效载荷。
对于单帧而言,无需流量控制,因此单帧即代表了整个 ISO-TP 传输过程。例如,如果诊断工具希望向 ECU B 发送 ISO-TP 有效载荷“00 11 22 33 44”(5 字节),它将发送以下 CAN 帧:
请注意,ISO-TP 有效载荷的大小为 5 字节 ,但相关的 CAN 帧却是 6 字节 ,因为 CAN 帧中有一字节用作报头。
首帧(报头格式为“1XXX”)与连续帧(报头格式为“2X”)配合使用,以传输大于 7 字节的有效载荷。首帧指定了 ISO-TP 有效载荷的长度,并且包含该有效载荷的前 6 个字节 。首字节的第二位半字节与报头的第二个字节拼接后,表示有效载荷的长度。例如,“10 09”的报头表示一个 9 字节的 ISO-TP 帧,“1F FF”则表示一个 0xFFF(4095)字节的 ISO-TP 帧。
连续帧包含该有效载荷的剩余部分。报头字节的第二位半字节用作索引,以确保有效载荷能够按顺序重新组装。
例如,如果诊断工具希望将 ISO-TP 有效载荷“00 11 22 33 44 55 66 77 88 99 AA BB CC DD”(共 14 字节)发送至 ECU B,则会发送以下 CAN 帧:
请注意,这代表了诊断工具所发送的全部内容,但对于大于7字节的帧,诊断工具实际上必须等待流控制帧,如下文所述。
流量控制帧(报头格式为“3XYYZZ”)由 ISO-TP 接收方发送,用于控制传输速度。由于此类帧由接收方发送,因此其使用的 CAN ID 与其他帧不同。作为对“首帧”的响应,至少总会发现一个流量控制帧。
流量控制帧的格式为“3X YY ZZ”,包含以下参数:
现代 ECU 通常会以“30 00 00”作为对“第一帧”的响应,这意味着“以最高速度发送所有数据”——在这种情况下,这将是整个通信过程中唯一的流控制帧。
如果 ECU 回复的是“30 00 F9”,客户端也会在不等待流控制帧的情况下发送所有帧,但每帧之间会间隔 900 毫秒。
如果 ECU 回复的是“30 01 00”,客户端将发送第一个“连续帧”,并在继续发送之前等待另一个流控制帧。
以下是一个完整的 ISO-TP 传输示例:
请注意,最后没有确认应答。如果 ECU 使用的块大小不为零,则相同的数据交换将如下所示。
can-utils 软件包提供了多种工具用于与 ISO-TP 交互。您可能会用到以下三个命令:isotpsend 用于发送 ISO-TP 帧,isotprecv 用于接收 ISO-TP 帧,以及 isotpdump 用于转储并解析 ISO-TP 流量。此外,您仍然可以使用前一指南中介绍的基本命令(参见与 CAN 交互)。
UDS 命令将在另一节中详细说明。本节主要使用基本的 UDS 命令 “3E 00”,该命令应由 ECU 回应 “7E 00”。我们将此命令发送至 ECU B,它使用 ID 0x7e1 接收命令,使用 ID 0x7e9 回应命令。任何其他随机命令,很可能是无效的,应以 7F <您发送的第一个字节> <错误码字节> 来回应,以指示您的命令存在错误。
在使用 ISO-TP 进行实验时,你可能希望显示原始的 CAN 流量。这可以通过使用 candump 并配合正确的过滤器来实现。例如,如果你只想显示 ECU B 的 ISO-TP 流量对应的 CAN 帧,则应仅显示 ID 为 0x7e1 和 0x7e9 的 CAN 帧:
如果您想显示现代车辆中通常出现的所有 ISO-TP 帧,可以使用 ID 介于 0x7e0 和 0x7f 之间的过滤器来筛选帧(请参阅 CAN 过滤器 ):
在试验 ISO-TP 时,建议您始终保留一个终端执行此命令,以便可以直接观察 CAN 帧。
尽管不推荐这样做,但并没有任何限制阻止您手动创建 ISO-TP 报头并发送 CAN 帧。要将 ISO-TP 有效载荷“3E 00”发送到 ECU B,您可以输入以下命令:
在您的 candump 终端上,您应该会看到两条消息:

请注意,CAN 帧的 DLC(有效载荷大小)为 3,但 ISO-TP 有效载荷仅为 2 字节——这是因为 CAN 有效载荷的第一个字节用于指示 ISO-TP 有效载荷的大小。此外,请注意,您的请求 CAN 报文中并未指定响应 CAN ID,该 ID 是静态设定的,不同于 TCP 等其他协议中的源端口和目的端口。
如果您忽略流控制帧,并仅寄希望于时间 timing 正确,也可以直接发送分段帧。例如,要发送有效载荷 00 11 22 33 44 55 66 77(8 字节),您可以输入:
并在 candump 中观察响应结果。

尽管在 ISO-TP 上层可能会出现错误信息(例如上述截图中的“7F 00 31”UDS 错误消息),但 在 ISO-TP 层却没有任何错误信息 。如果在格式化 ISO-TP 帧时出现错误,ECU 将直接不予响应。例如,对于以下格式错误的请求,ECU 不会作出任何回应,也不会报告相关错误。


ECU 通常对 ISO-TP 请求设置了非常快速的超时机制,如果帧发送不够迅速,传输将被关闭。建议您不要自行格式化 ISO-TP 帧,而是使用诸如 isotpsend 之类的工具。
命令 isotpsend 允许您向 ISO-TP 服务器发送任意有效载荷。作为参数,您需要提供 CAN 接口、源 CAN ID(客户端用于传输数据的 ID)以及目标 CAN ID(服务器用于传输流控帧的 ID)。有效载荷必须通过标准输入逐字节提供。在 Linux 中,这可以使用 echo 命令来完成。例如,使用以下命令将两字节的 ISO-TP 有效载荷“3E 00”发送到 ECU B:

这与之前使用 candump 捕获的 CAN 流量完全相同,只不过这次您无需自己添加报头。不过,您仍需提供目标 ID,因为 isotpsend 会等待流控帧的到来。
注意
请注意,“源”对应于目标 ECU 所监听的 ID——这一点对某些人来说可能有些反直觉。与 TCP 协议中的“源”和“目的”的类比存在局限性:对于 ISO-TP 而言,“源”和“目的”ID 是一组静态配对。如果你想与另一个 ECU 通信,你需要同时更改源和目的 。
你可以使用 isotpsend 发送最大 4095 字节的有效载荷,而无需担心 ISO-TP 的格式问题。例如:
应显示如下截图所示的流量。

如果你只想测试 ISO-TP 链路,而对有效载荷内容不关心,可以使用-D 选项让 isotpsend 为你生成特定大小的有效载荷。此外,你还可以使用-f 功能来强制设置客户端分离时间的值(单位为纳秒)。例如,使用以下命令生成一个 100 字节的有效载荷,并在每次传输之间设置 1 秒的延迟:
你应该能够在 candump 终端中观察到缓慢的传输过程。

命令 isotpsend 允许您发送 ISO-TP 帧,但它不会监听来自服务器(ECU)的响应。为此,您需要在另一个终端中使用 isotprecv,并指定相同的参数:
这将接收一个有效载荷后关闭,但您可以使用 -l 选项来监听多个 ISO-TP 有效载荷:
如果您在一个终端中打开其中一条命令,并在另一个终端中使用 isotpsend,那么您应该能够在 isotprecv 终端中看到 ECU 对您的 ISO-TP 有效载荷的响应。例如,在另一个终端中输入以下命令:
然后观察 isotprecv 终端,它应该会显示来自 ECU 的响应。

使用 isotprecv 和 isotpsend 时,请勿混淆源和目标——它们应使用相同的参数。如果您改为使用 isotprecv -s 7e9 -d 7e1 can0,您将不会监听 ECU B 的响应,而是会伪装成 ECU B,并监听与之相同的命令。通常情况下,真实 ECU 中的源 ID 低于目标 ID,因此:
请注意,请求不会显示在 isotprecv 中,仅显示响应。您可以使用 isotpdump 在同一终端中同时显示请求和响应。
命令 isotpdump 将在同一窗口中同时显示请求和响应。与 candump 类似,建议您保持一个终端窗口始终打开该命令。
使用以下命令转储您与 ECU B 之间的流量:
-c 选项为消息添加颜色,以便轻松区分请求(红色)和响应(蓝色)。
如果您在另一个终端中输入以下 isotpsend 命令,应该能够观察到 ISO-TP 流量。

您可以使用-a 选项以显示 ASCII 格式,并使用-u 选项将有效载荷解释为 UDS 流量:

与 candump 不同,isotpdump 会自动去除报头,方便您直接识别实际的 ISO-TP 有效载荷。不过,它仍然会逐个显示 CAN 帧。当有效载荷较大时,这种显示方式可能会显得有些繁杂,例如:

如果您只想查看重构的有效载荷,可以改用 isotpsniffer。您可以使用以下语法:
这应该只会显示重构后的有效载荷,例如在使用 isotpsend 传输大容量有效载荷时:

您可以使用 isotpperf 来测量 ISO-TP 连接的性能,例如针对 ECU B:
这将显示正在进行的传输,并测量其耗时。
您可以通过本地强制设置-STmin 值(使用-f 选项)来尝试各种传输。打开另一个窗口,输入以下命令以发送最大尺寸且具有不同时间间隔的有效载荷:

如果您希望通过 IP 网络访问 ECU 的 ISO-TP 服务器,可以使用 isotpserver。例如,如果您希望将来自 ECU B(ISO-TP 对 0x7e1/0x7e9)的 ISO-TP 流量转发到 IP 端口 7777,可以使用以下命令:
此命令允许任何能够访问您 IP 服务器的人员与 ECU B 进行通信。请仅在您清楚自己操作的情况下使用此功能。
您可以使用例如 netcat 连接到此端口:
您可以发送以 ASCII 格式嵌入在尖括号 <> 中的 ISO-TP 帧,例如,如果您输入 <3E00> 并按下回车键,您应在同一终端中收到 <7E00> 。

此命令可用于远程与多人共享一个 ECU。但请注意,只有 ISO-TP 响应(且仅限响应)会被广播给所有客户端。您还可以使用 isotptun 命令创建基于 ISO-TP 的 IP 隧道。
寻址方式有两种:
在正常寻址模式下,诊断工具一次仅与一个 ECU 进行通信。
在功能寻址中,诊断工具可以同时与一组 ECU 进行通信。它仅支持单帧,因为单帧无需流量控制,因此多个接收端可轻松同时接收。ECU 常用的函数地址为 0x7DF,该地址由 OBD-II PID 定义。
ISO-TP 中定义了多种寻址类型:
较新版本的 ISO-TP 利用了 CAN FD 提供的额外带宽(参见 CAN FD)。具体而言:
如果您遇到超时问题(例如出现“read socket t: Connection timed out”之类的错误信息),这表明连接不可靠,即以下情况之一:
如果问题出在 ECU A 上,其屏幕上应会显示错误信息。
如果您使用的是虚拟机,其 USB 设置可能不够可靠。在 VirtualBox 中,前往“设置”->“USB”,并勾选“USB 3.0 (xHCI) 控制器”(请确保您的虚拟机已关闭)。
如果仍然失败,请尝试使用 -f 选项与 isotpsend 配合,以强制其降低速度,例如使用“-f 100000”。