This commit is contained in:
CaiXiang
2025-11-14 16:09:58 +08:00
commit af65c2425d
74 changed files with 14650 additions and 0 deletions

View File

@@ -0,0 +1,551 @@
# 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; // 波特率定时器 0BTR0
UCHAR Timing1; // 波特率定时器 1BTR1
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=CAN11=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` 进行简单滤波
---
## 参考文件
另请参阅:
- `附件1ID对齐方式.pdf` - ID 对齐详情
- `附件2CAN参数设置.pdf` - CAN 参数设置
- `波特率侦测工具使用说明书.pdf` - 波特率检测工具
**技术支持:** zhcxgd@163.com