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

5.9 KiB
Raw Permalink Blame History

CAN 通信使用说明

文件说明

  1. ControlCAN.h - CAN 设备 API 头文件(位于 lib 目录)
  2. ControlCAN.dll - CAN 设备动态链接库(位于 lib 目录)
  3. ControlCAN.lib - 导入库(位于 lib 目录)
  4. CANController.h/cpp - CAN 控制器封装类
  5. can_complete_example.cpp - 完整使用示例

快速开始

1. 编译项目

使用 g++ 编译MinGW

# 编译封装类
g++ -c CANController.cpp -o CANController.o -std=c++11

# 编译完整示例
g++ can_complete_example.cpp CANController.o -o can_demo.exe -Llib -lControlCAN -std=c++11

# 或者使用提供的编译脚本
./build_can.sh

使用 MSVC 编译:

cl /EHsc /std:c++17 can_complete_example.cpp CANController.cpp /link /LIBPATH:lib ControlCAN.lib

2. 运行示例

确保 ControlCAN.dll 在可执行文件同目录或系统路径中:

# 复制 DLL
copy lib\ControlCAN.dll .

# 运行示例
./can_demo.exe

API 使用说明

初始化流程

#include "CANController.h"

// 1. 创建 CAN 控制器对象
CANController can(VCI_USBCAN2, 0, 0);  // 设备类型、设备索引、CAN通道

// 2. 初始化(波特率 500Kbps
if (!can.Initialize(0x00, 0x1C, 0)) {
    // 初始化失败处理
    return -1;
}

// 3. 发送数据
BYTE data[] = {0x11, 0x22, 0x33, 0x44};
can.SendStandardFrame(0x123, data, 4);

// 4. 接收数据
std::vector<VCI_CAN_OBJ> frames;
DWORD count = can.Receive(frames, 100);
for (const auto& frame : frames) {
    // 处理接收到的数据
}

// 5. 关闭(析构函数自动调用)
can.Close();

常用波特率配置

波特率 Timing0 Timing1 示例代码
1Mbps 0x00 0x14 can.Initialize(0x00, 0x14, 0)
800Kbps 0x00 0x16 can.Initialize(0x00, 0x16, 0)
500Kbps 0x00 0x1C can.Initialize(0x00, 0x1C, 0)
250Kbps 0x01 0x1C can.Initialize(0x01, 0x1C, 0)
125Kbps 0x03 0x1C can.Initialize(0x03, 0x1C, 0)
100Kbps 0x04 0x1C can.Initialize(0x04, 0x1C, 0)

工作模式

模式值 说明 应用场景
0 正常模式 正常的 CAN 通信,可收发
1 只听模式 总线监控,不影响总线
2 自发自收 设备测试,环回模式

AGV 控制示例

速度控制

// AGV 速度控制函数
void sendAGVVelocity(CANController& can, int16_t left_speed, int16_t right_speed) {
    BYTE data[8] = {0};
    data[0] = 0x10;  // 速度控制命令
    data[1] = 0x00;
    data[2] = (left_speed >> 8) & 0xFF;   // 左轮速度高字节
    data[3] = left_speed & 0xFF;          // 左轮速度低字节
    data[4] = (right_speed >> 8) & 0xFF;  // 右轮速度高字节
    data[5] = right_speed & 0xFF;         // 右轮速度低字节
    data[6] = 0x00;
    data[7] = 0x00;

    can.SendStandardFrame(0x200, data, 8);
}

// 使用示例
sendAGVVelocity(can, 100, 100);  // 直行,速度 100
sendAGVVelocity(can, 50, 100);   // 左转
sendAGVVelocity(can, 100, 50);   // 右转
sendAGVVelocity(can, 0, 0);      // 停止

周期性心跳

// 在独立线程中发送心跳
std::thread heartbeat_thread([&can]() {
    int counter = 0;
    while (running) {
        BYTE data[8] = {0xAA, 0x00, 0x00, 0x00};
        data[1] = (counter >> 8) & 0xFF;
        data[2] = counter & 0xFF;

        can.SendStandardFrame(0x100, data, 4);
        counter++;

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
});

接收处理(回调方式)

// 设置接收回调函数
can.SetReceiveCallback([](const VCI_CAN_OBJ& frame) {
    if (frame.ID == 0x201) {
        // 电机反馈
        int16_t speed = (frame.Data[0] << 8) | frame.Data[1];
        std::cout << "电机速度: " << speed << " RPM" << std::endl;
    }
    else if (frame.ID == 0x300) {
        // AGV 状态
        uint8_t status = frame.Data[0];
        std::cout << "AGV 状态: " << (int)status << std::endl;
    }
});

// 在主循环中接收数据
while (running) {
    std::vector<VCI_CAN_OBJ> frames;
    can.Receive(frames, 100);  // 会自动调用回调函数
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

故障排查

常见问题

  1. 打开设备失败

    • 检查 USB-CAN 设备是否正确连接
    • 检查设备驱动是否安装
    • 检查设备索引是否正确从0开始
  2. 初始化失败

    • 检查波特率参数是否正确
    • 确保设备未被其他程序占用
  3. 发送/接收失败

    • 检查 CAN 总线是否正常连接
    • 检查终端电阻是否正确
    • 使用示例3的监控模式检查总线通信
  4. DLL 加载失败

    • 确保 ControlCAN.dll 在可执行文件目录
    • 或将 dll 路径添加到系统 PATH

进阶功能

智能滤波

// 设置滤波器(仅接收特定 ID 范围)
VCI_FILTER_RECORD filter;
filter.ExtFrame = 0;      // 标准帧
filter.Start = 0x100;     // 起始 ID
filter.End = 0x1FF;       // 结束 ID

// 注意:需要直接调用 VCI_SetReference
VCI_SetReference(VCI_USBCAN2, 0, 0, 1, &filter);  // 添加滤波规则
VCI_SetReference(VCI_USBCAN2, 0, 0, 2, nullptr);  // 启用滤波

多设备支持

// 查找所有 USB-CAN 设备
VCI_BOARD_INFO devices[50];
DWORD count = VCI_FindUsbDevice2(devices);

std::cout << "找到 " << count << " 个设备:" << std::endl;
for (DWORD i = 0; i < count; i++) {
    std::cout << "设备 " << i << ": " << devices[i].str_Serial_Num << std::endl;
}

// 打开特定设备
CANController can1(VCI_USBCAN2, 0, 0);  // 第一个设备
CANController can2(VCI_USBCAN2, 1, 0);  // 第二个设备

参考资料

  • ControlCAN 接口函数库使用说明书docs/protocol/CAN_Protocol.pdf
  • can_complete_example.cpp - 完整示例代码
  • CANController.h/cpp - 封装类实现

技术支持

如有问题,请参考:

  • 官方文档docs/protocol/CAN_Protocol.pdf
  • 邮箱:zhcxgd@163.com