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-Ⅱ 常用值 | 说明 |
|---|---|---|
DevType | VCI_USBCAN2(4) | 设备类型 |
DevIndex | 0 | 第一台设备;多设备时依次递增 |
CANIndex | 0 或 1 | 0 表示 CAN1,1 表示 CAN2 |
核心数据结构
VCI_INIT_CONFIG:保存验收码、屏蔽码、滤波方式、波特率和工作模式。VCI_CAN_OBJ:表示一帧 CAN 报文,包括 ID、帧类型、DLC、数据和接收时间戳。VCI_BOARD_INFO:保存设备序列号、通道数以及硬件、固件、驱动和接口库版本。
VCI_CAN_OBJ::Data 最多包含 8 字节数据,因此本接口用于 Classic CAN 报文,不用于 CAN FD 报文。
基本调用流程
VCI_OpenDevice
↓
VCI_InitCAN
↓
VCI_StartCAN
↓
VCI_Transmit / VCI_Receive
↓
VCI_CloseDevice双通道设备只需打开一次。CAN1 和 CAN2 分别调用 VCI_InitCAN、VCI_StartCAN,收发时传入对应的 CANIndex。
常用辅助函数:
| 函数 | 用途 |
|---|---|
VCI_ReadBoardInfo | 读取设备和版本信息 |
VCI_GetReceiveNum | 查询接收缓冲区中的未读帧数 |
VCI_ClearBuffer | 清空指定通道的收发缓冲区 |
VCI_ResetCAN | 复位指定 CAN 通道 |
VCI_FindUsbDevice2 | 枚举设备并读取序列号 |
常用波特率
波特率通过 VCI_INIT_CONFIG 的 Timing0 和 Timing1 设置。以下是官方手册给出的常用组合:
| 波特率 | Timing0 | Timing1 |
|---|---|---|
| 125 kbit/s | 0x03 | 0x1C |
| 250 kbit/s | 0x01 | 0x1C |
| 500 kbit/s | 0x00 | 0x1C |
| 1 Mbit/s | 0x00 | 0x14 |
TIP
示例使用 500 kbit/s。实际开发时必须使用与目标 CAN 网络一致的波特率;非常规波特率应以配套资料中的计算结果为准。
C/C++ 最小示例
下面的示例打开第一台 CANalyst-Ⅱ 的 CAN1 通道,以 500 kbit/s 初始化,发送一帧标准数据帧,再尝试读取接收缓冲区。
#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_OpenDevice、VCI_InitCAN、VCI_StartCAN、VCI_CloseDevice:返回1表示成功。VCI_Transmit:返回实际发送的帧数。VCI_Receive:返回实际读取的帧数,返回0表示当前没有数据。- 官方接口以无符号
DWORD声明部分返回值,因此检查失败值时可与static_cast<DWORD>(-1)比较。
不要只检查打开设备是否成功。初始化、启动、发送和接收都应分别检查返回值,并在失败时记录设备索引、通道索引和调用阶段。
官方资料
接口、结构体和参数应以设备资料包中附带的 ControlCAN.h 与接口手册为最终依据。
