Files
agv-control-slam/.claude/skills/can-protocol.md
CaiXiang af65c2425d initial
2025-11-14 16:09:58 +08:00

14 KiB
Raw Blame History

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


数据结构

设备类型

#define VCI_USBCAN2  4  // 用于 USBCAN-2A/2C/CANalyst-II 系列

VCI_BOARD_INFO

设备信息结构体:

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 帧结构体:

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 初始化配置:

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

智能滤波范围:

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 设备。每个设备只能打开一次。

DWORD VCI_OpenDevice(DWORD DevType, DWORD DevIndex, DWORD Reserved);

参数:

  • DevType设备类型VCI_USBCAN2 为 4
  • DevIndex:设备索引(第一个设备为 0第二个为 1依此类推
  • Reserved:保留参数(设为 0

返回值: 1=成功0=失败,-1=设备不存在或 USB 断开

示例:

int deviceType = 4;    // USBCAN-2A/2C/CANalyst-II
int deviceIndex = 0;   // 第一个设备
if (VCI_OpenDevice(deviceType, deviceIndex, 0) != 1) {
    // 处理错误
}

VCI_CloseDevice

关闭 USB-CAN 设备。

DWORD VCI_CloseDevice(DWORD DevType, DWORD DevIndex);

返回值: 1=成功0=失败,-1=设备不存在


VCI_InitCAN

初始化指定的 CAN 通道。每个通道调用一次。

DWORD VCI_InitCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex,
                  PVCI_INIT_CONFIG pInitConfig);

参数:

  • DevType:设备类型
  • DevIndex:设备索引
  • CANIndexCAN 通道0=CAN11=CAN2
  • pInitConfig:指向初始化配置的指针

返回值: 1=成功0=失败,-1=设备不存在

示例:

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);
    // 处理错误
}

验收滤波示例:

// 只接收 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 通道。初始化后调用。

DWORD VCI_StartCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex);

返回值: 1=成功0=失败,-1=设备不存在


VCI_ResetCAN

复位 CAN 通道(例如从总线关闭状态恢复)。无需重新初始化。

DWORD VCI_ResetCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex);

返回值: 1=成功0=失败,-1=设备不存在


VCI_Transmit

发送 CAN 帧。

DWORD VCI_Transmit(DWORD DevType, DWORD DevIndex, DWORD CANIndex,
                   PVCI_CAN_OBJ pSend, DWORD Length);

参数:

  • pSend:指向 CAN 帧数组的指针
  • Length:要发送的帧数(最大 1000建议1

返回值: 实际发送的帧数,-1=设备不存在

示例:

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 帧。

DWORD VCI_Receive(DWORD DevType, DWORD DevIndex, DWORD CANIndex,
                  PVCI_CAN_OBJ pReceive, ULONG Len, INT WaitTime);

参数:

  • pReceive:指向接收缓冲区数组的指针
  • Len最多接收的帧数缓冲区大小建议2500
  • WaitTime:保留参数(设为 0

返回值: 接收到的帧数,-1=设备不存在

示例:

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

获取接收缓冲区中未读取的帧数。

DWORD VCI_GetReceiveNum(DWORD DevType, DWORD DevIndex, DWORD CANIndex);

返回值: 未读取的帧数,-1=设备不存在

注意: 如果持续调用 VCI_Receive,通常不需要此函数。


VCI_ClearBuffer

清除接收和发送缓冲区。

DWORD VCI_ClearBuffer(DWORD DevType, DWORD DevIndex, DWORD CANIndex);

返回值: 1=成功0=失败,-1=设备不存在

注意: 如果持续调用 VCI_Receive,通常不需要此函数。


VCI_ReadBoardInfo

读取设备信息。

DWORD VCI_ReadBoardInfo(DWORD DevType, DWORD DevIndex, PVCI_BOARD_INFO pInfo);

返回值: 1=成功0=失败,-1=设备不存在

示例:

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 个设备。

DWORD VCI_FindUsbDevice2(PVCI_BOARD_INFO pInfo);

参数:

  • pInfo:包含 50 个 VCI_BOARD_INFO 结构的数组

返回值: 找到的设备数量

示例:

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 设备(相当于拔插一次)。

DWORD VCI_UsbDeviceReset(DWORD DevType, DWORD DevIndex, DWORD Reserved);

参数:

  • Reserved:保留参数(设为 0

返回值: 1=成功0=失败,-1=设备不存在

注意: 复位后必须重新调用 VCI_OpenDevice


VCI_SetReference

配置智能滤波。允许精确控制接收哪些 ID。

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 范围:

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 时:

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