# USB-CAN 接口函数库参考手册 ## 概述 本技能文档提供 USB-CAN 接口函数库(ControlCAN.dll)版本 2.10 的参考信息,兼容周立功(ZLG)函数库。 **库文件:** - `ControlCAN.dll` - 主库文件 - `ControlCAN.lib` - 链接库 - `ControlCAN.h` - C/C++ 头文件 - `ControlCAN.bas` - VB 声明文件 - `ControlCAN.pas` - Delphi 声明文件 - `ControlCAN.llb` - LabVIEW 模块 **支持设备:** USBCAN-2A, USBCAN-2C, CANalyst-II, MiniPCIe-CAN --- ## 数据结构 ### 设备类型 ```c #define VCI_USBCAN2 4 // 用于 USBCAN-2A/2C/CANalyst-II 系列 ``` ### VCI_BOARD_INFO 设备信息结构体: ```c typedef struct _VCI_BOARD_INFO { USHORT hw_Version; // 硬件版本(如 0x0100 = V1.00) USHORT fw_Version; // 固件版本 USHORT dr_Version; // 驱动版本 USHORT in_Version; // 接口库版本 USHORT irq_Num; // 保留 BYTE can_Num; // CAN 通道数量 CHAR str_Serial_Num[20]; // 序列号 CHAR str_hw_Type[40]; // 硬件类型字符串 USHORT Reserved[4]; } VCI_BOARD_INFO; ``` ### VCI_CAN_OBJ CAN 帧结构体: ```c typedef struct _VCI_CAN_OBJ { UINT ID; // CAN ID(右对齐) UINT TimeStamp; // 时间戳(上电后 0.1ms 单位) BYTE TimeFlag; // 1=时间戳有效(仅接收时) BYTE SendType; // 0=正常发送,1=单次发送 BYTE RemoteFlag; // 0=数据帧,1=远程帧 BYTE ExternFlag; // 0=标准帧(11位),1=扩展帧(29位) BYTE DataLen; // 数据长度(0-8) BYTE Data[8]; // 数据字节 BYTE Reserved[3]; } VCI_CAN_OBJ; ``` **SendType 取值:** - `0` = 正常发送(失败自动重发,超时 1 秒) - `1` = 单次发送(只发送一次,不重发) - **⚠️ 重要:** 多节点通信时务必使用 `SendType=0`,否则会丢帧 ### VCI_INIT_CONFIG CAN 初始化配置: ```c typedef struct _INIT_CONFIG { DWORD AccCode; // 验收码 DWORD AccMask; // 屏蔽码(0xFFFFFFFF = 接收全部) DWORD Reserved; UCHAR Filter; // 滤波方式(1/2/3) UCHAR Timing0; // 波特率定时器 0(BTR0) UCHAR Timing1; // 波特率定时器 1(BTR1) UCHAR Mode; // 0=正常,1=只听,2=自测 } VCI_INIT_CONFIG; ``` **滤波模式:** - `1` = 接收所有类型帧(标准帧 + 扩展帧) - `2` = 只接收标准帧 - `3` = 只接收扩展帧 **工作模式:** - `0` = 正常模式 - `1` = 只听模式(只接收,不影响总线) - `2` = 自测模式(环回模式) ### VCI_FILTER_RECORD 智能滤波范围: ```c typedef struct _VCI_FILTER_RECORD { DWORD ExtFrame; // 0=标准帧,1=扩展帧 DWORD Start; // 起始 ID DWORD End; // 结束 ID } VCI_FILTER_RECORD; ``` --- ## 波特率配置 常用波特率设置(采样点 87.5%,SJW=0): | 波特率 | Timing0 (BTR0) | Timing1 (BTR1) | |--------|----------------|----------------| | 20 Kbps | 0x18 | 0x1C | | 40 Kbps | 0x87 | 0xFF | | 50 Kbps | 0x09 | 0x1C | | 80 Kbps | 0x83 | 0xFF | | 100 Kbps | 0x04 | 0x1C | | 125 Kbps | 0x03 | 0x1C | | 200 Kbps | 0x81 | 0xFA | | 250 Kbps | 0x01 | 0x1C | | 400 Kbps | 0x80 | 0xFA | | 500 Kbps | 0x00 | 0x1C | | 666 Kbps | 0x80 | 0xB6 | | 800 Kbps | 0x00 | 0x16 | | 1000 Kbps | 0x00 | 0x14 | | 33.33 Kbps| 0x09 | 0x6F | | 66.66 Kbps| 0x04 | 0x6F | | 83.33 Kbps| 0x03 | 0x6F | **注意事项:** - 基于 SJA1000 控制器(16MHz) - 最低支持波特率:10 Kbps(高速收发器) - 非常规波特率请使用波特率计算工具 --- ## 核心函数 ### VCI_OpenDevice 打开 USB-CAN 设备。每个设备只能打开一次。 ```c DWORD VCI_OpenDevice(DWORD DevType, DWORD DevIndex, DWORD Reserved); ``` **参数:** - `DevType`:设备类型(VCI_USBCAN2 为 4) - `DevIndex`:设备索引(第一个设备为 0,第二个为 1,依此类推) - `Reserved`:保留参数(设为 0) **返回值:** 1=成功,0=失败,-1=设备不存在或 USB 断开 **示例:** ```c int deviceType = 4; // USBCAN-2A/2C/CANalyst-II int deviceIndex = 0; // 第一个设备 if (VCI_OpenDevice(deviceType, deviceIndex, 0) != 1) { // 处理错误 } ``` --- ### VCI_CloseDevice 关闭 USB-CAN 设备。 ```c DWORD VCI_CloseDevice(DWORD DevType, DWORD DevIndex); ``` **返回值:** 1=成功,0=失败,-1=设备不存在 --- ### VCI_InitCAN 初始化指定的 CAN 通道。每个通道调用一次。 ```c DWORD VCI_InitCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_INIT_CONFIG pInitConfig); ``` **参数:** - `DevType`:设备类型 - `DevIndex`:设备索引 - `CANIndex`:CAN 通道(0=CAN1,1=CAN2) - `pInitConfig`:指向初始化配置的指针 **返回值:** 1=成功,0=失败,-1=设备不存在 **示例:** ```c VCI_INIT_CONFIG config; config.AccCode = 0x80000008; config.AccMask = 0xFFFFFFFF; // 接收全部 config.Filter = 1; // 接收所有类型 config.Timing0 = 0x00; // 1000 Kbps config.Timing1 = 0x14; config.Mode = 0; // 正常模式 if (VCI_InitCAN(deviceType, deviceIndex, 0, &config) != 1) { VCI_CloseDevice(deviceType, deviceIndex); // 处理错误 } ``` **验收滤波示例:** ```c // 只接收 ID 0x123(标准帧) config.AccCode = 0x24600000; // 0x123 << 21 config.AccMask = 0x00000000; // 所有位都相关 // 接收 ID 范围 0x120-0x123 config.AccCode = 0x24600000; // 0x123 << 21 config.AccMask = 0x00600000; // 0x03 << 21,位 0-1 无关 ``` --- ### VCI_StartCAN 启动 CAN 通道。初始化后调用。 ```c DWORD VCI_StartCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex); ``` **返回值:** 1=成功,0=失败,-1=设备不存在 --- ### VCI_ResetCAN 复位 CAN 通道(例如从总线关闭状态恢复)。无需重新初始化。 ```c DWORD VCI_ResetCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex); ``` **返回值:** 1=成功,0=失败,-1=设备不存在 --- ### VCI_Transmit 发送 CAN 帧。 ```c DWORD VCI_Transmit(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_CAN_OBJ pSend, DWORD Length); ``` **参数:** - `pSend`:指向 CAN 帧数组的指针 - `Length`:要发送的帧数(最大 1000,建议:1) **返回值:** 实际发送的帧数,-1=设备不存在 **示例:** ```c VCI_CAN_OBJ frame; frame.ID = 0x123; frame.SendType = 0; // 正常发送(自动重试) frame.RemoteFlag = 0; // 数据帧 frame.ExternFlag = 0; // 标准帧 frame.DataLen = 8; for (int i = 0; i < 8; i++) { frame.Data[i] = i; } DWORD sent = VCI_Transmit(deviceType, deviceIndex, 0, &frame, 1); if (sent != 1) { // 处理错误 } ``` --- ### VCI_Receive 从缓冲区接收 CAN 帧。 ```c DWORD VCI_Receive(DWORD DevType, DWORD DevIndex, DWORD CANIndex, PVCI_CAN_OBJ pReceive, ULONG Len, INT WaitTime); ``` **参数:** - `pReceive`:指向接收缓冲区数组的指针 - `Len`:最多接收的帧数(缓冲区大小,建议:2500) - `WaitTime`:保留参数(设为 0) **返回值:** 接收到的帧数,-1=设备不存在 **示例:** ```c VCI_CAN_OBJ rxFrames[2500]; DWORD count = VCI_Receive(deviceType, deviceIndex, 0, rxFrames, 2500, 0); if (count > 0) { for (DWORD i = 0; i < count; i++) { // 处理 rxFrames[i] printf("ID: 0x%X, Data: ", rxFrames[i].ID); for (int j = 0; j < rxFrames[i].DataLen; j++) { printf("%02X ", rxFrames[i].Data[j]); } printf("\n"); } } else if (count == -1) { // USB 断开,尝试重新连接 VCI_CloseDevice(deviceType, deviceIndex); VCI_OpenDevice(deviceType, deviceIndex, 0); } ``` **最佳实践:** 在循环中持续调用 `VCI_Receive`。比使用 `VCI_GetReceiveNum` 更高效。 --- ### VCI_GetReceiveNum 获取接收缓冲区中未读取的帧数。 ```c DWORD VCI_GetReceiveNum(DWORD DevType, DWORD DevIndex, DWORD CANIndex); ``` **返回值:** 未读取的帧数,-1=设备不存在 **注意:** 如果持续调用 `VCI_Receive`,通常不需要此函数。 --- ### VCI_ClearBuffer 清除接收和发送缓冲区。 ```c DWORD VCI_ClearBuffer(DWORD DevType, DWORD DevIndex, DWORD CANIndex); ``` **返回值:** 1=成功,0=失败,-1=设备不存在 **注意:** 如果持续调用 `VCI_Receive`,通常不需要此函数。 --- ### VCI_ReadBoardInfo 读取设备信息。 ```c DWORD VCI_ReadBoardInfo(DWORD DevType, DWORD DevIndex, PVCI_BOARD_INFO pInfo); ``` **返回值:** 1=成功,0=失败,-1=设备不存在 **示例:** ```c VCI_BOARD_INFO info; if (VCI_ReadBoardInfo(deviceType, deviceIndex, &info) == 1) { printf("硬件: %s\n", info.str_hw_Type); printf("序列号: %s\n", info.str_Serial_Num); printf("通道数: %d\n", info.can_Num); } ``` --- ## 扩展函数 ### VCI_FindUsbDevice2 查找所有已连接的 USB-CAN 设备并获取序列号。最多支持 50 个设备。 ```c DWORD VCI_FindUsbDevice2(PVCI_BOARD_INFO pInfo); ``` **参数:** - `pInfo`:包含 50 个 VCI_BOARD_INFO 结构的数组 **返回值:** 找到的设备数量 **示例:** ```c VCI_BOARD_INFO devices[50]; int count = VCI_FindUsbDevice2(devices); for (int i = 0; i < count; i++) { printf("设备 %d: %s\n", i, devices[i].str_Serial_Num); } ``` **使用场景:** 1. **软硬件绑定:** 将软件绑定到特定设备序列号 2. **多设备管理:** 将设备索引与序列号匹配 3. **设备识别:** 识别物理设备对应的索引 --- ### VCI_UsbDeviceReset 复位 USB 设备(相当于拔插一次)。 ```c DWORD VCI_UsbDeviceReset(DWORD DevType, DWORD DevIndex, DWORD Reserved); ``` **参数:** - `Reserved`:保留参数(设为 0) **返回值:** 1=成功,0=失败,-1=设备不存在 **注意:** 复位后必须重新调用 `VCI_OpenDevice`。 --- ### VCI_SetReference 配置智能滤波。允许精确控制接收哪些 ID。 ```c DWORD VCI_SetReference(DWORD DevType, DWORD DevIndex, DWORD CANIndex, DWORD RefType, PVOID pData); ``` **RefType 取值:** | RefType | pData | 说明 | |---------|-------|------| | 1 | `PVCI_FILTER_RECORD` | 添加滤波范围(每个范围调用一次) | | 2 | `NULL` | 启用滤波表 | | 3 | `NULL` | 清除滤波表 | **示例 - 接收特定 ID 范围:** ```c VCI_FILTER_RECORD filters[4]; // 接收标准帧 ID 0x0001 filters[0].ExtFrame = 0; filters[0].Start = 0x0001; filters[0].End = 0x0001; // 接收标准帧 ID 范围 0x0003-0x0005 filters[1].ExtFrame = 0; filters[1].Start = 0x0003; filters[1].End = 0x0005; // 接收扩展帧 ID 0x00000002 filters[2].ExtFrame = 1; filters[2].Start = 0x00000002; filters[2].End = 0x00000002; // 接收扩展帧 ID 范围 0x00000004-0x00000007 filters[3].ExtFrame = 1; filters[3].Start = 0x00000004; filters[3].End = 0x00000007; // 清除现有滤波器(可选) VCI_SetReference(21, deviceIndex, 0, 3, NULL); // 添加滤波范围 for (int i = 0; i < 4; i++) { VCI_SetReference(21, deviceIndex, 0, 1, &filters[i]); } // 启用滤波器 VCI_SetReference(21, deviceIndex, 0, 2, NULL); ``` **重要提示:** - 滤波表只写(无法读回) - 在启用前添加所有滤波器 - 要修改滤波器,需清除后重新添加 - 掉电后滤波设置丢失 - 在 `VCI_InitCAN` 之后、操作期间调用 --- ## 典型使用流程 ``` 1. [可选] VCI_FindUsbDevice2() // 查找设备和序列号 2. VCI_OpenDevice() // 打开设备 3. VCI_InitCAN() // 初始化 CAN 通道 4. [可选] VCI_SetReference() // 配置智能滤波器 - RefType=3: 清除滤波器 - RefType=1: 添加滤波范围(多次调用) - RefType=2: 启用滤波器 5. VCI_StartCAN() // 启动 CAN 6. 主循环: - VCI_Transmit() // 发送帧 - VCI_Receive() // 接收帧(持续循环) - [可选] VCI_ResetCAN() // 总线关闭时复位 7. VCI_CloseDevice() // 完成时关闭设备 ``` --- ## 错误处理 所有函数返回: - `1` = 成功 - `0` = 操作失败 - `-1` = USB 设备未找到或断开 **USB 热插拔支持:** 当 `VCI_Receive` 返回 `-1` 时: ```c if (VCI_Receive(...) == -1) { VCI_CloseDevice(deviceType, deviceIndex); // 等待或重试 if (VCI_OpenDevice(deviceType, deviceIndex, 0) == 1) { VCI_InitCAN(...); VCI_StartCAN(...); // 恢复操作 } } ``` --- ## 最佳实践 1. **缓冲区大小:** 接收缓冲区使用 2500 帧(匹配内部缓冲区) 2. **接收循环:** 持续调用 `VCI_Receive`;不要依赖 `VCI_GetReceiveNum` 3. **发送类型:** 多节点通信始终使用 `SendType=0` 4. **滤波设置:** 高负载总线使用智能滤波(`VCI_SetReference`) 5. **错误恢复:** 监控返回值并妥善处理 USB 断开 6. **多通道:** 分别初始化和启动每个通道 7. **序列号:** 多设备设置中使用 `VCI_FindUsbDevice2` 进行设备识别 --- ## 常见问题 **问:无法接收帧?** - 检查 `AccMask` 是否为 0xFFFFFFFF(接收全部) - 验证 `Filter` 模式是否匹配帧类型 - 检查波特率是否匹配总线 - 确保已调用 `VCI_StartCAN` **问:多节点设置中发送失败?** - 始终使用 `SendType=0`(不是 1)进行正常通信 - 检查总线终端和线路 **问:总线关闭状态?** - 调用 `VCI_ResetCAN` 恢复 - 检查电气问题(终端、电缆质量) **问:如何过滤特定 ID?** - 使用带智能滤波表的 `VCI_SetReference` - 或使用 `AccCode`/`AccMask` 进行简单滤波 --- ## 参考文件 另请参阅: - `附件1:ID对齐方式.pdf` - ID 对齐详情 - `附件2:CAN参数设置.pdf` - CAN 参数设置 - `波特率侦测工具使用说明书.pdf` - 波特率检测工具 **技术支持:** zhcxgd@163.com