initial
This commit is contained in:
246
docs/can/CAN_API_Reference.cpp
Normal file
246
docs/can/CAN_API_Reference.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* CAN API 快速参考
|
||||
* 基于 ControlCAN 接口函数库
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// 1. 基本数据结构
|
||||
// ============================================
|
||||
|
||||
// CAN 帧结构
|
||||
typedef struct {
|
||||
UINT ID; // CAN ID
|
||||
UINT TimeStamp; // 时间戳(0.1ms)
|
||||
BYTE TimeFlag; // 时间标志
|
||||
BYTE SendType; // 0=正常发送, 1=单次发送
|
||||
BYTE RemoteFlag; // 0=数据帧, 1=远程帧
|
||||
BYTE ExternFlag; // 0=标准帧(11位), 1=扩展帧(29位)
|
||||
BYTE DataLen; // 数据长度(0-8)
|
||||
BYTE Data[8]; // 数据
|
||||
} VCI_CAN_OBJ;
|
||||
|
||||
// 初始化配置
|
||||
typedef struct {
|
||||
DWORD AccCode; // 验收码
|
||||
DWORD AccMask; // 屏蔽码(0xFFFFFFFF=接收所有)
|
||||
DWORD Reserved;
|
||||
BYTE Filter; // 滤波方式(1=所有类型)
|
||||
BYTE Timing0; // 波特率 T0
|
||||
BYTE Timing1; // 波特率 T1
|
||||
BYTE Mode; // 0=正常, 1=只听, 2=自发自收
|
||||
} VCI_INIT_CONFIG;
|
||||
|
||||
// 设备信息
|
||||
typedef struct {
|
||||
USHORT hw_Version; // 硬件版本
|
||||
USHORT fw_Version; // 固件版本
|
||||
BYTE can_Num; // CAN 通道数
|
||||
CHAR str_Serial_Num[20]; // 序列号
|
||||
} VCI_BOARD_INFO;
|
||||
|
||||
// ============================================
|
||||
// 2. 核心 API 函数
|
||||
// ============================================
|
||||
|
||||
// 打开设备
|
||||
DWORD VCI_OpenDevice(DWORD DevType, DWORD DevIndex, DWORD Reserved);
|
||||
// 参数:DevType=4(USBCAN2), DevIndex=设备索引(0,1,2...)
|
||||
// 返回:1=成功, 0=失败, -1=设备不存在
|
||||
|
||||
// 关闭设备
|
||||
DWORD VCI_CloseDevice(DWORD DevType, DWORD DevIndex);
|
||||
|
||||
// 初始化 CAN
|
||||
DWORD VCI_InitCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex,
|
||||
PVCI_INIT_CONFIG pInitConfig);
|
||||
// 参数:CANIndex=CAN通道(0=CAN0, 1=CAN1)
|
||||
|
||||
// 启动 CAN
|
||||
DWORD VCI_StartCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex);
|
||||
|
||||
// 复位 CAN
|
||||
DWORD VCI_ResetCAN(DWORD DevType, DWORD DevIndex, DWORD CANIndex);
|
||||
|
||||
// 发送数据
|
||||
DWORD VCI_Transmit(DWORD DevType, DWORD DevIndex, DWORD CANIndex,
|
||||
PVCI_CAN_OBJ pSend, DWORD Len);
|
||||
// 返回:实际发送的帧数
|
||||
|
||||
// 接收数据
|
||||
DWORD VCI_Receive(DWORD DevType, DWORD DevIndex, DWORD CANIndex,
|
||||
PVCI_CAN_OBJ pReceive, DWORD Len, INT WaitTime);
|
||||
// 返回:实际接收的帧数
|
||||
|
||||
// 获取接收数量
|
||||
DWORD VCI_GetReceiveNum(DWORD DevType, DWORD DevIndex, DWORD CANIndex);
|
||||
|
||||
// 清空缓冲区
|
||||
DWORD VCI_ClearBuffer(DWORD DevType, DWORD DevIndex, DWORD CANIndex);
|
||||
|
||||
// 读取设备信息
|
||||
DWORD VCI_ReadBoardInfo(DWORD DevType, DWORD DevIndex, PVCI_BOARD_INFO pInfo);
|
||||
|
||||
// ============================================
|
||||
// 3. 常用波特率配置
|
||||
// ============================================
|
||||
|
||||
// 波特率 Timing0 Timing1
|
||||
// 1000 Kbps 0x00 0x14
|
||||
// 800 Kbps 0x00 0x16
|
||||
// 500 Kbps 0x00 0x1C // 推荐
|
||||
// 250 Kbps 0x01 0x1C
|
||||
// 125 Kbps 0x03 0x1C
|
||||
// 100 Kbps 0x04 0x1C
|
||||
|
||||
// ============================================
|
||||
// 4. 完整使用流程
|
||||
// ============================================
|
||||
|
||||
void CAN_Example() {
|
||||
// 步骤1:打开设备
|
||||
if (VCI_OpenDevice(VCI_USBCAN2, 0, 0) != 1) {
|
||||
printf("打开设备失败\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤2:配置并初始化
|
||||
VCI_INIT_CONFIG config;
|
||||
config.AccCode = 0x00000000;
|
||||
config.AccMask = 0xFFFFFFFF; // 接收所有
|
||||
config.Filter = 1;
|
||||
config.Timing0 = 0x00; // 500Kbps
|
||||
config.Timing1 = 0x1C;
|
||||
config.Mode = 0; // 正常模式
|
||||
|
||||
if (VCI_InitCAN(VCI_USBCAN2, 0, 0, &config) != 1) {
|
||||
printf("初始化失败\n");
|
||||
VCI_CloseDevice(VCI_USBCAN2, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤3:启动 CAN
|
||||
if (VCI_StartCAN(VCI_USBCAN2, 0, 0) != 1) {
|
||||
printf("启动失败\n");
|
||||
VCI_CloseDevice(VCI_USBCAN2, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤4:清空缓冲区
|
||||
VCI_ClearBuffer(VCI_USBCAN2, 0, 0);
|
||||
|
||||
// 步骤5:发送数据
|
||||
VCI_CAN_OBJ send_frame;
|
||||
memset(&send_frame, 0, sizeof(VCI_CAN_OBJ));
|
||||
send_frame.ID = 0x123;
|
||||
send_frame.SendType = 0;
|
||||
send_frame.RemoteFlag = 0;
|
||||
send_frame.ExternFlag = 0; // 标准帧
|
||||
send_frame.DataLen = 8;
|
||||
send_frame.Data[0] = 0x11;
|
||||
send_frame.Data[1] = 0x22;
|
||||
// ...
|
||||
|
||||
DWORD ret = VCI_Transmit(VCI_USBCAN2, 0, 0, &send_frame, 1);
|
||||
printf("发送 %d 帧\n", ret);
|
||||
|
||||
// 步骤6:接收数据
|
||||
VCI_CAN_OBJ recv_frames[100];
|
||||
ret = VCI_Receive(VCI_USBCAN2, 0, 0, recv_frames, 100, 0);
|
||||
printf("接收 %d 帧\n", ret);
|
||||
|
||||
for (DWORD i = 0; i < ret; i++) {
|
||||
printf("ID=0x%03X, 数据=[", recv_frames[i].ID);
|
||||
for (int j = 0; j < recv_frames[i].DataLen; j++) {
|
||||
printf("%02X ", recv_frames[i].Data[j]);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
// 步骤7:关闭设备
|
||||
VCI_CloseDevice(VCI_USBCAN2, 0);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 5. 常见应用场景
|
||||
// ============================================
|
||||
|
||||
// 场景1:发送标准帧
|
||||
void Send_StandardFrame(UINT id, BYTE* data, BYTE len) {
|
||||
VCI_CAN_OBJ frame = {0};
|
||||
frame.ID = id;
|
||||
frame.SendType = 0;
|
||||
frame.RemoteFlag = 0;
|
||||
frame.ExternFlag = 0; // 标准帧
|
||||
frame.DataLen = len;
|
||||
memcpy(frame.Data, data, len);
|
||||
VCI_Transmit(VCI_USBCAN2, 0, 0, &frame, 1);
|
||||
}
|
||||
|
||||
// 场景2:发送扩展帧
|
||||
void Send_ExtendedFrame(UINT id, BYTE* data, BYTE len) {
|
||||
VCI_CAN_OBJ frame = {0};
|
||||
frame.ID = id;
|
||||
frame.SendType = 0;
|
||||
frame.RemoteFlag = 0;
|
||||
frame.ExternFlag = 1; // 扩展帧
|
||||
frame.DataLen = len;
|
||||
memcpy(frame.Data, data, len);
|
||||
VCI_Transmit(VCI_USBCAN2, 0, 0, &frame, 1);
|
||||
}
|
||||
|
||||
// 场景3:循环接收
|
||||
void Receive_Loop() {
|
||||
VCI_CAN_OBJ frames[2500];
|
||||
while (running) {
|
||||
DWORD count = VCI_Receive(VCI_USBCAN2, 0, 0, frames, 2500, 0);
|
||||
if (count > 0) {
|
||||
// 处理接收到的数据
|
||||
for (DWORD i = 0; i < count; i++) {
|
||||
Process_Frame(&frames[i]);
|
||||
}
|
||||
}
|
||||
Sleep(10); // 10ms 间隔
|
||||
}
|
||||
}
|
||||
|
||||
// 场景4:AGV 速度控制
|
||||
void AGV_SetVelocity(int16_t left_rpm, int16_t right_rpm) {
|
||||
BYTE data[8] = {0};
|
||||
data[0] = 0x10; // 速度命令
|
||||
data[1] = 0x00;
|
||||
data[2] = (left_rpm >> 8) & 0xFF; // 左轮高字节
|
||||
data[3] = left_rpm & 0xFF; // 左轮低字节
|
||||
data[4] = (right_rpm >> 8) & 0xFF; // 右轮高字节
|
||||
data[5] = right_rpm & 0xFF; // 右轮低字节
|
||||
|
||||
Send_StandardFrame(0x200, data, 8);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 6. 错误处理
|
||||
// ============================================
|
||||
|
||||
// 检查返回值
|
||||
DWORD ret = VCI_OpenDevice(VCI_USBCAN2, 0, 0);
|
||||
if (ret == 0) {
|
||||
printf("操作失败\n");
|
||||
} else if (ret == (DWORD)-1) {
|
||||
printf("设备不存在或 USB 掉线\n");
|
||||
} else {
|
||||
printf("操作成功\n");
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 7. 注意事项
|
||||
// ============================================
|
||||
|
||||
/*
|
||||
1. 一个设备只能打开一次
|
||||
2. 发送帧数建议每次 1 帧,提高效率
|
||||
3. 接收缓冲区建议 2500 帧
|
||||
4. 多节点通信时 SendType 必须为 0
|
||||
5. 扩展帧 ID 为 29 位,标准帧为 11 位
|
||||
6. 数据长度最大 8 字节
|
||||
7. 波特率配置要与总线其他设备一致
|
||||
8. 使用完毕后记得关闭设备
|
||||
*/
|
||||
232
docs/can/CAN_README.md
Normal file
232
docs/can/CAN_README.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# 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):
|
||||
|
||||
```bash
|
||||
# 编译封装类
|
||||
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 编译:
|
||||
|
||||
```bash
|
||||
cl /EHsc /std:c++17 can_complete_example.cpp CANController.cpp /link /LIBPATH:lib ControlCAN.lib
|
||||
```
|
||||
|
||||
### 2. 运行示例
|
||||
|
||||
确保 ControlCAN.dll 在可执行文件同目录或系统路径中:
|
||||
|
||||
```bash
|
||||
# 复制 DLL
|
||||
copy lib\ControlCAN.dll .
|
||||
|
||||
# 运行示例
|
||||
./can_demo.exe
|
||||
```
|
||||
|
||||
## API 使用说明
|
||||
|
||||
### 初始化流程
|
||||
|
||||
```cpp
|
||||
#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 控制示例
|
||||
|
||||
### 速度控制
|
||||
|
||||
```cpp
|
||||
// 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); // 停止
|
||||
```
|
||||
|
||||
### 周期性心跳
|
||||
|
||||
```cpp
|
||||
// 在独立线程中发送心跳
|
||||
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));
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 接收处理(回调方式)
|
||||
|
||||
```cpp
|
||||
// 设置接收回调函数
|
||||
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
|
||||
|
||||
## 进阶功能
|
||||
|
||||
### 智能滤波
|
||||
|
||||
```cpp
|
||||
// 设置滤波器(仅接收特定 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); // 启用滤波
|
||||
```
|
||||
|
||||
### 多设备支持
|
||||
|
||||
```cpp
|
||||
// 查找所有 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
|
||||
Reference in New Issue
Block a user