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 包版本编写。不同版本的类名和接口封装可能略有变化,因此正式项目中建议以安装包自带示例和官方文档为准。

雪球电子 · 专注通讯