Skip to content

Kvaser CANlib SDK ​

概述 ​

Kvaser CANlib SDK 是 Kvaser 官方提供的软件开发包,适用于 Kvaser CAN、CAN FD、LIN、远程 CAN 设备以及部分记录仪设备的二次开发。开发者可基于该 SDK 完成设备枚举、通道打开、总线参数配置、报文收发、状态读取、错误处理、日志转换、数据库解析以及远程设备管理等工作。

在实际项目中,CANlib 主要用于与连接到计算机和 CAN 总线的 Kvaser CAN 设备进行交互。其核心能力包括:

  • 识别和枚举 Kvaser CAN 设备;
  • 打开和关闭 CAN 通道;
  • 配置 Classic CAN 或 CAN FD 总线参数;
  • 控制 Bus On / Bus Off;
  • 发送和接收 CAN / CAN FD 报文;
  • 读取通道状态、错误状态和时间戳;
  • 配合 Kvaser 工具链完成测试、调试和自动化验证。

开发包获取 ​

开发前需要安装 Kvaser 官方 CANlib SDK。

Kvaser官方下载地址: <www.kvaser.cn/support/developer/canlib-sdk/>

SDK主要内容 ​

Kvaser CANlib SDK 并不只是一个 CAN 收发库,而是一套较完整的 Kvaser 二次开发工具包。常见组成包括:

组件用途
CANlib最核心的 CAN 通讯库,用于与 Kvaser CAN 设备进行交互。
LINlib用于 LIN 总线开发,功能与 CANlib 类似。
kvaDbLibKvaser 的 CAN 数据库操作库,用于创建、读取、写入和修改 CAN 数据库文件。
kvlclib日志文件转换库,主要用于转换 Kvaser Memorator 记录仪生成的日志文件。
kvmlib主要用于与 Kvaser Memorator 记录仪设备交互。
kvaMemoLibXML用于将 XML 格式的记录仪配置转换为 Kvaser Memorator 可使用的二进制配置文件,通常与 kvmlib 配合开发自定义记录仪配置工具。
kvrlib主要用于 Kvaser 远程设备的发现和配置,例如通过以太网或 WiFi 连接的 Kvaser 设备。

虚拟硬件与虚拟通道 ​

Kvaser CANlib SDK 提供虚拟硬件和虚拟 CAN 通道。虚拟通道可以在没有真实 Kvaser 硬件的情况下进行程序开发、功能演示和接口调试。

虚拟通道常用于以下场景:

  • 开发初期没有真实设备;
  • 上位机界面功能验证;
  • 自动化测试流程调试;
  • CAN 报文收发逻辑验证;
  • 给客户演示软件功能。

使用虚拟通道时,需要安装 Kvaser 虚拟驱动程序。代码中通常可以使用允许虚拟通道的打开参数,例如:

cpp
canOpenChannel(channel, canOPEN_ACCEPT_VIRTUAL);

需要注意的是,虚拟通道只能用于软件逻辑测试,不能代替真实 CAN 总线的电气层测试、负载测试、ACK 测试和错误帧测试。

支持的开发语言和编译环境 ​

Kvaser CANlib SDK 支持多种开发语言和编译环境,常见包括:

  • C / C++;
  • C#;
  • .NET;
  • Managed C++;
  • Visual Basic;
  • Delphi;
  • gcc;
  • MinGW;
  • Python 示例或第三方绑定;
  • 其他可调用动态库的语言环境。

CANlib 典型调用流程 ​

Classic CAN 调用流程 ​

Classic CAN 项目的典型调用流程如下:

  1. 初始化 CANlib;
  2. 获取系统中的 CAN 通道数量;
  3. 读取通道名称、设备型号、EAN、序列号等信息;
  4. 打开目标通道;
  5. 设置 CAN 波特率;
  6. 设置驱动模式,例如 Normal、Silent;
  7. 执行 Bus On;
  8. 发送 CAN 报文;
  9. 接收 CAN 报文;
  10. 读取总线状态和错误信息;
  11. 测试结束后执行 Bus Off;
  12. 关闭通道句柄。

C/C++ 通道枚举示例 ​

cpp
#include <canlib.h>
#include <iostream>

int main()
{
    canInitializeLibrary();

    int channelCount = 0;
    canStatus status = canGetNumberOfChannels(&channelCount);

    if (status != canOK)
    {
        std::cout << "canGetNumberOfChannels failed." << std::endl;
        return -1;
    }

    std::cout << "Detected Kvaser channels: "
              << channelCount
              << std::endl;

    for (int ch = 0; ch < channelCount; ch++)
    {
        char channelName[128] = {0};

        status = canGetChannelData(
            ch,
            canCHANNELDATA_CHANNEL_NAME,
            channelName,
            sizeof(channelName));

        if (status == canOK)
        {
            std::cout << "Channel "
                      << ch
                      << " : "
                      << channelName
                      << std::endl;
        }
    }

    return 0;
}

实际项目中还可以继续读取设备 EAN、序列号、硬件类型等信息,用于多设备绑定和通道自动匹配。

Classic CAN 发送示例 ​

以下示例演示 500 kbit/s Classic CAN 报文发送流程。

cpp
#include <canlib.h>
#include <iostream>

static void printError(canStatus status)
{
    char errorText[128] = {0};
    canGetErrorText(status, errorText, sizeof(errorText));
    std::cout << "CANlib error: " << errorText << std::endl;
}

int main()
{
    canInitializeLibrary();

    int channel = 0;

    canHandle handle = canOpenChannel(
        channel,
        canOPEN_ACCEPT_VIRTUAL);

    if (handle < 0)
    {
        printError((canStatus)handle);
        return -1;
    }

    canStatus status;

    status = canSetBusParams(
        handle,
        canBITRATE_500K,
        0,
        0,
        0,
        0,
        0);

    if (status != canOK)
    {
        printError(status);
        canClose(handle);
        return -1;
    }

    status = canSetBusOutputControl(
        handle,
        canDRIVER_NORMAL);

    if (status != canOK)
    {
        printError(status);
        canClose(handle);
        return -1;
    }

    status = canBusOn(handle);

    if (status != canOK)
    {
        printError(status);
        canClose(handle);
        return -1;
    }

    unsigned char data[8] = {
        0x01,
        0x02,
        0x03,
        0x04,
        0x05,
        0x06,
        0x07,
        0x08
    };

    status = canWrite(
        handle,
        0x123,
        data,
        8,
        0);

    if (status != canOK)
    {
        printError(status);
    }

    status = canWriteSync(
        handle,
        100);

    if (status != canOK)
    {
        printError(status);
    }

    canBusOff(handle);
    canClose(handle);

    return 0;
}

CAN FD 发送示例 ​

以下示例演示 CAN FD 报文发送流程。实际项目中,仲裁段和数据段波特率应根据网络中所有节点的配置保持一致。

cpp
#include <canlib.h>
#include <iostream>

static void printError(canStatus status)
{
    char errorText[128] = {0};
    canGetErrorText(status, errorText, sizeof(errorText));
    std::cout << "CANlib error: " << errorText << std::endl;
}

int main()
{
    canInitializeLibrary();

    int channel = 0;

    canHandle handle = canOpenChannel(
        channel,
        canOPEN_CAN_FD | canOPEN_ACCEPT_VIRTUAL);

    if (handle < 0)
    {
        printError((canStatus)handle);
        return -1;
    }

    canStatus status;

    /*
     * 仲裁段波特率。
     * 示例使用 1 Mbit/s,采样点 80%。
     * 具体宏定义以实际 SDK 头文件为准。
     */
    status = canSetBusParams(
        handle,
        canFD_BITRATE_1M_80P,
        0,
        0,
        0,
        0,
        0);

    if (status != canOK)
    {
        printError(status);
        canClose(handle);
        return -1;
    }

    /*
     * 数据段波特率。
     * 示例使用 4 Mbit/s,采样点 80%。
     */
    status = canSetBusParamsFd(
        handle,
        canFD_BITRATE_4M_80P,
        0,
        0,
        0);

    if (status != canOK)
    {
        printError(status);
        canClose(handle);
        return -1;
    }

    status = canSetBusOutputControl(
        handle,
        canDRIVER_NORMAL);

    if (status != canOK)
    {
        printError(status);
        canClose(handle);
        return -1;
    }

    status = canBusOn(handle);

    if (status != canOK)
    {
        printError(status);
        canClose(handle);
        return -1;
    }

    unsigned char data[64] = {0};

    for (int i = 0; i < 64; i++)
    {
        data[i] = (unsigned char)i;
    }

    /*
     * canFDMSG_FDF 表示 CAN FD 帧。
     * canFDMSG_BRS 表示使用数据段加速。
     * 如果不启用 BRS,可只使用 canFDMSG_FDF。
     */
    unsigned int flags = canFDMSG_FDF | canFDMSG_BRS;

    status = canWrite(
        handle,
        0x123,
        data,
        64,
        flags);

    if (status != canOK)
    {
        printError(status);
    }

    status = canWriteSync(
        handle,
        100);

    if (status != canOK)
    {
        printError(status);
    }

    canBusOff(handle);
    canClose(handle);

    return 0;
}

接收报文示例 ​

CANlib 可通过 canRead() 读取接收队列中的报文。接收时需要注意区分 Classic CAN 和 CAN FD 报文标志。

cpp
long id = 0;
unsigned char data[64] = {0};
unsigned int dlc = 0;
unsigned int flags = 0;
unsigned long timestamp = 0;

canStatus status = canRead(
    handle,
    &id,
    data,
    &dlc,
    &flags,
    &timestamp);

if (status == canOK)
{
    std::cout << "ID: 0x"
              << std::hex
              << id
              << " DLC: "
              << std::dec
              << dlc
              << " Timestamp: "
              << timestamp
              << std::endl;

    if (flags & canFDMSG_FDF)
    {
        std::cout << "CAN FD frame" << std::endl;
    }
    else
    {
        std::cout << "Classic CAN frame" << std::endl;
    }
}
else if (status == canERR_NOMSG)
{
    /*
     * 当前无报文,不属于严重错误。
     * 可以继续轮询,或使用事件机制等待接收。
     */
}
else
{
    printError(status);
}

对于高实时性或高负载项目,不建议用简单死循环一直轮询,可结合事件通知、接收线程或系统等待机制进行优化。

错误处理 ​

CANlib 的大多数接口都会返回 canStatus 状态码。开发中应检查所有关键 API 的返回值,并通过 canGetErrorText() 输出可读错误信息。

cpp
canStatus status = canBusOn(handle);

if (status != canOK)
{
    char errorText[128] = {0};

    canGetErrorText(
        status,
        errorText,
        sizeof(errorText));

    printf("canBusOn failed: %s\n", errorText);
}

建议重点关注以下错误或状态:

  • 通道打开失败;
  • 参数配置失败;
  • Bus On 失败;
  • 发送超时;
  • 接收队列为空;
  • TX 队列满;
  • Bus Heavy;
  • Bus Off;
  • ACK Error;
  • Error Frame;
  • 硬件断开;
  • 驱动未安装或版本不匹配。

Python 开发方式 ​

Python 项目通常适合自动化测试、脚本化验证和快速调试。常见方式有两种。

1. 使用 python-can ​

python-can 可以通过 interface="kvaser" 调用 Kvaser 后端。

Classic CAN 示例:

python
import can

bus = can.Bus(
    interface="kvaser",
    channel=0,
    bitrate=500000
)

msg = can.Message(
    arbitration_id=0x123,
    data=[1, 2, 3, 4],
    is_extended_id=False
)

bus.send(msg)
bus.shutdown()

CAN FD 示例:

python
import can

bus = can.Bus(
    interface="kvaser",
    channel=0,
    bitrate=1000000,
    data_bitrate=4000000,
    fd=True
)

msg = can.Message(
    arbitration_id=0x123,
    data=bytes(range(64)),
    is_extended_id=False,
    is_fd=True,
    bitrate_switch=True
)

bus.send(msg)
bus.shutdown()

使用 python-can 时,仍然需要先安装 Kvaser 驱动和 CANlib 运行环境,否则 Kvaser 后端无法正常工作。

2. 使用 Kvaser 官方 Python CANlib 包 ​

也可以使用 Kvaser 官方 Python CANlib 包进行开发。

安装方式通常为:

bash
pip install canlib

示例代码需要根据当前 Python CANlib 包版本编写。不同版本的类名和接口封装可能略有变化,因此正式项目中建议以安装包自带示例和官方文档为准。

雪球电子 · 专注通讯