# 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
