Quantex GmbH
您的地区:欧洲

J2534 开发者指南

最后修改:

什么是 J2534?

J2534(通常称为 Pass-Thru)是由 SAE(汽车工程师学会)制定的标准,它解决了一个重要问题:让同一款诊断软件能够与不同制造商的诊断适配器协同工作。

核心理念:

设想您要编写一款汽车诊断程序。如果没有 J2534 标准,您就必须为每一种希望支持的适配器(K-Line、CAN 适配器等)编写独立的代码。这既复杂又昂贵。

J2534 标准定义了统一的 API(程序接口),适配器制造商必须在其驱动程序中实现该接口。该标准仅将此 API 定义为 Windows 平台的 DLL 库

对开发者而言,这意味着:

因此,J2534 是您的应用程序与诊断适配器之间的“桥梁”,它隐藏了具体硬件的复杂性,让您能够专注于诊断逻辑。

许多开发者除 Windows 之外还希望使用其他平台。除了 Windows 平台的标准 DLL,我们还提供面向 Linux、macOS 以及移动平台 Android 和 iOS 的库。


J2534 负责什么(属于您的职责范围)

必须明确,J2534 是传输层标准。它负责通过所选物理接口(CAN、K-Line 等)完成所有数据字节的收发工作。

然而,J2534 标准并不定义

这些仍属于您应用程序的任务。作为开发者,您必须知道应当发送哪些字节才能请求例如故障码或当前参数,以及如何解析控制单元的响应。

为此,您需要掌握诊断应用层协议的知识,例如:

J2534 DLL 会把您的字节送达 ECU,但这些字节是什么、收到响应后如何处理,则由您的程序决定。


标准概览

J2534 标准是规范汽车诊断的庞大标准家族中的一部分。为深入理解,建议参阅官方文档。

协议与标准对应关系详表

ScanDoc 适配器实现的并非完整的 J2534-1 标准,以及 J2534-2 的部分扩展,同时还具有用于增强功能的专有函数。


支持的协议

J2534 标准定义了一组物理层和传输层协议,通过它们与汽车 ECU 进行数据交换。下面介绍 ScanDoc 适配器支持的每个协议。

ISO 15765(带传输层的 CAN)

用于现代汽车诊断的主要协议。ISO 15765(也称为 ISO-TP)在 CAN 总线之上实现了传输层:它会自动将长报文分段为 CAN 帧、进行流控制(Flow Control)并从多个帧重组响应。

正是这一协议被用于在 CAN 总线上进行 UDS(ISO 14229)和 OBD-II 诊断。如果您的任务是发送诊断请求并接收 ECU 的响应,请使用 ISO15765

// 以 500 kbit/s 通过 ISO 15765 连接
PassThruConnect(deviceID, ISO15765, 0, 500000, &channelID);

CAN(原始数据流)

CAN 协议提供对不带传输层的原始 CAN 帧流的访问。每条报文即一个 CAN 帧(最多 8 字节数据)。适配器不执行分段和重组——您按原样收发帧。

当您需要直接操作 CAN 总线时,请使用该协议:监控流量、发送单独的帧、处理基于 CAN 的非标准协议。

// 以 500 kbit/s、29 位 ID 连接 CAN
PassThruConnect(deviceID, CAN, CAN_29BIT_ID, 500000, &channelID);

ISO 14230 (KWP2000)

基于 K 线的诊断协议,广泛应用于 2000 年代的汽车(尤其是欧洲车型)。支持两种连接初始化方式:5 波特(慢速)和快速(fast init)。初始化完成后,按与 ECU 协商的速率进行通信。

用于 KWP2000 经销商级诊断以及基于 K 线的 OBD-II 诊断。

// 以 10400 波特通过 ISO 14230 连接,仅 K 线
PassThruConnect(deviceID, ISO14230, ISO9141_K_LINE, 10400, &channelID);

ISO 9141

较早的 K 线协议,由 ISO 9141-2 标准定义。用于老旧汽车的 OBD-II 诊断(视地区而定,约为 2004-2008 年之前)。仅支持 5 波特初始化。

// 以 10400 波特通过 ISO 9141 连接
PassThruConnect(deviceID, ISO9141, 0, 10400, &channelID);

SAE J1850 VPW

采用可变脉宽调制(Variable Pulse Width)的协议,曾用于 General Motors 汽车的 OBD-II 诊断。通过单根导线以 10.4 kbit/s 的速率工作。

// 通过 J1850 VPW 连接
PassThruConnect(deviceID, J1850VPW, 0, 10400, &channelID);

SAE J1850 PWM

采用脉宽调制(Pulse Width Modulation)的协议,曾用于 Ford 汽车的 OBD-II 诊断。通过两根导线以 41.6 kbit/s 的速率工作。

// 通过 J1850 PWM 连接
PassThruConnect(deviceID, J1850PWM, 0, 41600, &channelID);

如何开始(分步指南)

从您的应用程序操作 J2534 适配器通常遵循以下流程:

步骤 1:查找并加载 J2534 DLL

每家 J2534 适配器制造商都以 DLL 文件的形式提供其 API 实现。安装驱动时,关于该 DLL 的信息(文件路径)会写入 Windows 注册表。

您的应用程序应当:

  1. 在系统注册表中查找可用的 J2534 设备。
  2. 让用户选择要使用哪个适配器(如果有多个)。
  3. 使用 LoadLibrary 函数(在 Windows 中)将相应的 DLL 加载到内存。

步骤 2:与适配器交互的主循环

在加载 DLL 并获取函数指针之后,一次典型的诊断会话如下所示:

  1. 打开与适配器的连接:
    调用 PassThruOpen() 函数以初始化与物理设备的通信。作为响应,您将获得 DeviceID——此会话的唯一标识符。
    // 示例
    unsigned long deviceID;
    long result = PassThruOpen(NULL, &deviceID);
  2. 建立与 ECU 的通信通道:
    调用 PassThruConnect() 以按特定协议(例如用于 CAN 总线的 ISO15765)建立与车辆的逻辑通信通道。您需要指定协议、速率及其他参数。作为响应,您将获得 ChannelID
    // 示例
    unsigned long channelID;
    result = PassThruConnect(deviceID, ISO15765, 0, 500000, &channelID);
  3. 设置过滤器:
    设置过滤器是必需步骤。默认情况下,在未配置至少一个过滤器之前,适配器不会接收任何入站报文。过滤器可滤除总线上所有无用的流量,仅获取您关心的数据(例如来自特定 ECU 的响应)。创建和配置过滤器使用 PassThruStartMsgFilter() 函数。
  4. 数据交换:

    数据交换过程是异步的,并基于队列:

    • 发送数据:调用 PassThruWriteMsg() 函数并不会立即将报文发送到总线。相反,它会将一条或多条报文放入发送队列并立即返回控制权。适配器驱动会在条件允许时自行从队列中发送这些报文。
    • 读取数据:从总线接收到的报文(例如来自 ECU 的响应)会在内部接收队列中累积。PassThruReadMsg() 函数用于从该队列中取出报文。

    重要提示:J2534 库是单线程的。这意味着对同一通道的所有函数调用(PassThruWriteMsgPassThruReadMsg 等)都必须严格按顺序执行。在上一个函数执行完成之前,不能调用另一个函数。试图从不同线程对同一通道同时调用函数将导致不可预测的行为。

  5. 关闭通信通道:
    与 ECU 的工作完成后,使用 PassThruDisconnect() 关闭通道。
    // 示例
    result = PassThruDisconnect(channelID);
  6. 关闭与适配器的连接:
    最后,当与适配器的工作完全结束时,调用 PassThruClose() 以释放设备。
    // 示例
    result = PassThruClose(deviceID);

这一循环是任何 J2534 应用程序的基础。后续章节将详细介绍每个 API 函数及其参数。


错误处理

每个 J2534 API 函数都会返回一个状态码。执行成功时始终返回 STATUS_NOERROR(值为 0)。任何其他值都表示某个具体错误(例如 ERR_TIMEOUTERR_INVALID_CHANNEL_ID 等)。

在每次函数调用之后检查返回值至关重要。

有一个特殊的错误码——ERR_FAILED。这是一个通用的、非特定的错误。仅在此情况下才应调用 PassThruGetLastError() 函数,以从驱动获取对问题更详细的文本描述。

// 正确的错误处理示例
long result = PassThruConnect(deviceID, ISO15765, 0, 500000, &channelID);
if (result != STATUS_NOERROR)
{
    printf("PassThruConnect 出错。代码: %ld\n", result);

    // 如果是通用错误,则请求详细信息
    if (result == ERR_FAILED)
    {
        char errorDescription[80];
        PassThruGetLastError(&errorDescription[0], 80);
        printf("  附加信息: %s\n", errorDescription);
    }

    // 此处可以放置处理其他错误代码的逻辑
    // switch (result) { case ERR_TIMEOUT: ... }

    return; // 中断执行
}

J2534 标准函数参考

J2534 标准每个函数的详细说明已整理至函数参考