Skip to content

ControlCAN API

ControlCAN API 是CANalyst-Ⅱ的 Windows 接口库。本篇将简单介绍ControlCAN,并给出应用示例,示例通过 ControlCAN.dll 配置打开 CANalyst-Ⅱ通道并收发 Classic CAN 报文。

开发包内容

资料包下载链接:https://www.cxcan.com/ZLXZ.html

常见的 C/C++ 开发文件包括:

文件用途
ControlCAN.h数据结构、常量和函数声明
ControlCAN.lib编译时链接使用的导入库
ControlCAN.dll程序运行时加载的动态库

官方资料还提供 C#、VB、Delphi、LabVIEW、LabWindows/CVI 和 MATLAB 等示例。不同语言最终调用的是同一套 ControlCAN 接口。

关键参数

设备与通道索引

参数CANalyst-Ⅱ 常用值说明
DevTypeVCI_USBCAN24设备类型
DevIndex0第一台设备;多设备时依次递增
CANIndex010 表示 CAN1,1 表示 CAN2

核心数据结构

  • VCI_INIT_CONFIG:保存验收码、屏蔽码、滤波方式、波特率和工作模式。
  • VCI_CAN_OBJ:表示一帧 CAN 报文,包括 ID、帧类型、DLC、数据和接收时间戳。
  • VCI_BOARD_INFO:保存设备序列号、通道数以及硬件、固件、驱动和接口库版本。

VCI_CAN_OBJ::Data 最多包含 8 字节数据,因此本接口用于 Classic CAN 报文,不用于 CAN FD 报文。

基本调用流程

text
VCI_OpenDevice

VCI_InitCAN

VCI_StartCAN

VCI_Transmit / VCI_Receive

VCI_CloseDevice

双通道设备只需打开一次。CAN1 和 CAN2 分别调用 VCI_InitCANVCI_StartCAN,收发时传入对应的 CANIndex

常用辅助函数:

函数用途
VCI_ReadBoardInfo读取设备和版本信息
VCI_GetReceiveNum查询接收缓冲区中的未读帧数
VCI_ClearBuffer清空指定通道的收发缓冲区
VCI_ResetCAN复位指定 CAN 通道
VCI_FindUsbDevice2枚举设备并读取序列号

常用波特率

波特率通过 VCI_INIT_CONFIGTiming0Timing1 设置。以下是官方手册给出的常用组合:

波特率Timing0Timing1
125 kbit/s0x030x1C
250 kbit/s0x010x1C
500 kbit/s0x000x1C
1 Mbit/s0x000x14

TIP

示例使用 500 kbit/s。实际开发时必须使用与目标 CAN 网络一致的波特率;非常规波特率应以配套资料中的计算结果为准。

C/C++ 最小示例

下面的示例打开第一台 CANalyst-Ⅱ 的 CAN1 通道,以 500 kbit/s 初始化,发送一帧标准数据帧,再尝试读取接收缓冲区。

cpp
#include <cstdint>
#include <iostream>
#include "ControlCAN.h"

namespace {
constexpr DWORD kDeviceType = VCI_USBCAN2;
constexpr DWORD kDeviceIndex = 0;
constexpr DWORD kChannelIndex = 0;
constexpr DWORD kOk = 1;
}

int main() {
  if (VCI_OpenDevice(kDeviceType, kDeviceIndex, 0) != kOk) {
    std::cerr << "Failed to open CANalyst-II\n";
    return 1;
  }

  VCI_INIT_CONFIG config{};
  config.AccCode = 0;
  config.AccMask = 0xFFFFFFFF;
  config.Filter = 1;       // 接收标准帧和扩展帧
  config.Timing0 = 0x00;  // 500 kbit/s
  config.Timing1 = 0x1C;
  config.Mode = 0;         // 正常模式

  if (VCI_InitCAN(kDeviceType, kDeviceIndex, kChannelIndex, &config) != kOk ||
      VCI_StartCAN(kDeviceType, kDeviceIndex, kChannelIndex) != kOk) {
    std::cerr << "Failed to initialize CAN1\n";
    VCI_CloseDevice(kDeviceType, kDeviceIndex);
    return 1;
  }

  VCI_CAN_OBJ tx{};
  tx.ID = 0x123;
  tx.SendType = 0;    // 正常发送,失败时由设备自动重发
  tx.RemoteFlag = 0;  // 数据帧
  tx.ExternFlag = 0;  // 标准帧
  tx.DataLen = 8;
  for (std::uint8_t i = 0; i < tx.DataLen; ++i) {
    tx.Data[i] = i;
  }

  const DWORD sent = VCI_Transmit(
    kDeviceType, kDeviceIndex, kChannelIndex, &tx, 1
  );
  std::cout << "Sent frames: " << sent << '\n';

  VCI_CAN_OBJ rx[100]{};
  const DWORD received = VCI_Receive(
    kDeviceType, kDeviceIndex, kChannelIndex, rx, 100, 0
  );
  if (received != static_cast<DWORD>(-1)) {
    std::cout << "Received frames: " << received << '\n';
  }

  VCI_CloseDevice(kDeviceType, kDeviceIndex);
  return 0;
}

该示例只展示最短调用链。正式程序通常需要在独立线程中周期性调用 VCI_Receive,并确保退出或异常分支最终执行 VCI_CloseDevice

返回值检查

  • VCI_OpenDeviceVCI_InitCANVCI_StartCANVCI_CloseDevice:返回 1 表示成功。
  • VCI_Transmit:返回实际发送的帧数。
  • VCI_Receive:返回实际读取的帧数,返回 0 表示当前没有数据。
  • 官方接口以无符号 DWORD 声明部分返回值,因此检查失败值时可与 static_cast<DWORD>(-1) 比较。

不要只检查打开设备是否成功。初始化、启动、发送和接收都应分别检查返回值,并在失败时记录设备索引、通道索引和调用阶段。

官方资料

接口、结构体和参数应以设备资料包中附带的 ControlCAN.h 与接口手册为最终依据。

雪球电子 · 专注通讯