周立功、致远电子usbcan2代码开发配置过程
梳理基于致远电子USBCAN2的项目创建、环境配置,以及模拟D520发送CAN报文的代码编写步骤。
ZLGCAN二次开发说明/函数库/例程下载 连接:
https://manual.zlg.cn/web/#/152?page_id=5332你现在需要按照「文件类型分类存放」的原则,将ZLG致远电子的开发文件放入对应目录,既要让Visual Studio编译器能找到头文件/库文件,又要让程序运行时能调用动态库文件,下面结合你的项目结构给出精准操作步骤:
创建 控制台应用C++ 项目
第一步:先整理ZLG开发文件(先分类,避免混乱)
先把你下载的ZLG开发资料中的文件按类型归类,明确核心文件:
| 文件类型 | 核心文件 | 用途 |
|---|---|---|
| 头文件(.h) | zlgcan.h、canframe.h等 | 让编译器识别ZCAN相关函数/结构体 |
| 静态库文件(.lib) | zlgcan.lib | 编译时链接CAN库接口 |
| 动态库文件(.dll) | zlgcan.dll | 程序运行时调用CAN设备功能 |
| 内核依赖文件夹 | kerneldlls(整个文件夹) | 支撑zlgcan.dll运行的底层依赖 |
| 其他可选文件 | 示例代码、帮助文档等 | 参考使用,无需放入项目目录 |
第二步:在项目根目录新建SDK文件夹(统一存放头文件+lib文件)
你的项目根目录是:D:\......\ZLG_CAN\ConsoleApplication1
- 进入该根目录,新建一个文件夹,命名为
ZLG_SDK(名称可自定义,方便识别即可); - 进入
ZLG_SDK文件夹,再新建两个子文件夹,分别命名为include(存放头文件.h)和lib(存放静态库.lib); - 将ZLG开发资料中的所有
.h文件复制到ZLG_SDK\include目录下; - 将ZLG开发资料中的
zlgcan.lib文件复制到ZLG_SDK\lib目录下。
第三步:存放动态库(.dll)和kerneldlls文件夹(关键:放可执行输出目录)
程序运行时需要加载zlgcan.dll和kerneldlls文件夹,必须放入项目编译后的可执行文件输出目录,你的项目有两个x64\Debug目录,优先放入「外层核心输出目录」,操作如下:
方案1:优先放入外层x64\Debug(推荐,VS默认输出目录)
直接将以下文件/文件夹复制到:D:\AppFile\铲运机代码资料\CAN\ZLG_CAN\ConsoleApplication1\x64\Debug
zlgcan.dll(动态库文件)kerneldlls(整个文件夹,包含其内部所有文件,不要拆分)
方案2:额外备份到内层x64\Debug(保险起见,避免路径不匹配)
再将上述zlgcan.dll和kerneldlls文件夹,复制到内层目录:D:\AppFile\铲运机代码资料\CAN\ZLG_CAN\ConsoleApplication1\ConsoleApplication1\x64\Debug
说明:VS项目的输出目录可能因配置不同,指向外层或内层x64\Debug,两个目录都放入可避免“缺少dll文件”的运行错误。
第四步:验证文件存放结构(确保无误)
存放完成后,你的项目目录结构应如下(关键节点标注):
D:. ├─ConsoleApplication1 (项目子目录) │ └─x64 │ └─Debug (内层可执行目录,已放zlgcan.dll + kerneldlls) ├─ZLG_SDK (新建的SDK文件夹,存放头文件和lib) │ ├─include (zlgcan.h、canframe.h等) │ └─lib (zlgcan.lib) └─x64 └─Debug (外层可执行目录,已放zlgcan.dll + kerneldlls,核心输出目录)第五步:衔接之前的项目属性配置(关键,让VS识别SDK文件)
文件存放完成后,回到Visual Studio中,右键你的项目(ConsoleApplication1)→「属性」,按以下配置(对应你新建的ZLG_SDK路径):
配置包含目录(让编译器找到.h文件)
路径:C/C++ → 常规 → 附加包含目录,添加$(ProjectDir)ZLG_SDK\include
($(ProjectDir)是VS内置变量,自动对应项目根目录,无需手动输入完整路径,更灵活)配置库目录(让链接器找到.lib文件)
路径:链接器 → 常规 → 附加库目录,添加$(ProjectDir)ZLG_SDK\lib配置附加依赖项(链接zlgcan.lib)
路径:链接器 → 输入 → 附加依赖项,添加zlgcan.lib
补充注意事项
- 不要随意修改ZLG文件的文件名(如zlgcan.dll改为zlgcan1.dll),否则程序会无法识别;
- 若后续切换Release模式(发布版本),需将
zlgcan.dll和kerneldlls同样复制到x64\Release目录下; - 若运行程序时提示“找不到zlgcan.dll”,说明动态库放错了目录,优先检查外层
x64\Debug目录是否存在该文件。
以下为我自己写的测试程序,根据键盘不同按键发送不同报文内容,可按需摘取改动。
#include<stdio.h>#include<windows.h>#include<process.h>#include<string.h>#include<stdarg.h>#defineZLG_CAN_API_ENABLE#defineUSBCAN2_SUPPORT#include"zlgcan.h"#include"canframe.h"#include"typedef.h"#include"config.h"#defineDEVICE_TYPEZCAN_USBCAN2#defineDEVICE_INDEX0#defineCAN_INDEX0CRITICAL_SECTION g_printLock;/* ================= 安全打印 ================= */voidsafe_printf(constchar*fmt,...){EnterCriticalSection(&g_printLock);va_list args;va_start(args,fmt);vprintf(fmt,args);va_end(args);LeaveCriticalSection(&g_printLock);}/* ================= 周期报文 ================= *//* 1BB / 2BB / 3BB / 4BB / 73B */ZCAN_Transmit_Data g_frames[]={/* 0: 1BB 行走/作业 */{{MAKE_CAN_ID(0x1BB,0,0,0),8,0,0,0,{0,0,0,0,0,0,0x01,0x20}},0},/* 1: 2BB 点火/驻车/喇叭 */{{MAKE_CAN_ID(0x2BB,0,0,0),8,0,0,0,{0,0,0,0,0,0,0x04,0x07}},0},/* 2: 3BB */{{MAKE_CAN_ID(0x3BB,0,0,0),2,0,0,0,{0x00,0x0F}},0},/* 3: 4BB */{{MAKE_CAN_ID(0x4BB,0,0,0),7,0,0,0,{0x30,0x8A,0x03,0x00,0xFF,0xFF,0x00}},0},/* 4: 73B 初始化完成 */{{MAKE_CAN_ID(0x73B,0,0,0),1,0,0,0,{0x05}},0}};#defineIDX_1BB0#defineIDX_2BB1constUINT FRAME_COUNT=sizeof(g_frames)/sizeof(g_frames[0]);/* ================= 上电一次 5BB ================= */ZCAN_Transmit_Data g_frame_5BB={{MAKE_CAN_ID(0x5BB,0,0,0),8,0,0,0,{0x60,0x00,0x28,0x01,0x00,0x35,0x00,0x08}},0};/* ================= 1BB 键盘 ================= */voidUpdate1BB(){BYTE*d=g_frames[IDX_1BB].frame.data;memset(d,0,8);d[6]=0x01;d[7]=0x20;/* 刹车 */if(GetAsyncKeyState(VK_SPACE)&0x8000){d[7]=0x21;return;}/* 作业 */if(GetAsyncKeyState('W')&0x8000)d[3]=0x64;if(GetAsyncKeyState('S')&0x8000)d[3]=0x9C;if(GetAsyncKeyState('A')&0x8000)d[4]=0x64;if(GetAsyncKeyState('D')&0x8000)d[4]=0x9C;/* 行走 / 转向 */if(GetAsyncKeyState(VK_LEFT)&0x8000){d[0]=0x9C;d[7]=0x22;}if(GetAsyncKeyState(VK_RIGHT)&0x8000){d[0]=0x64;d[7]=0x22;}if(GetAsyncKeyState(VK_DOWN)&0x8000){d[1]=0x64;d[7]=0x22;}if(GetAsyncKeyState(VK_UP)&0x8000){d[1]=0x9C;d[7]=0x22;}}/* ================= 2BB 键盘 ================= */voidUpdate2BB(){BYTE*d=g_frames[IDX_2BB].frame.data;memset(d,0,8);d[6]=0x04;d[7]=0x07;if(GetAsyncKeyState('F')&0x8000)// 点火{d[2]=0x01;d[3]=0x01;}if(GetAsyncKeyState('P')&0x8000)// 驻车{d[1]=0x08;d[2]=0x01;}if(GetAsyncKeyState('H')&0x8000)// 喇叭{d[2]=0x01;}}/* ================= 接收线程 ================= */unsigned__stdcallCanRxThread(void*param){CHANNEL_HANDLE hCan=(CHANNEL_HANDLE)param;ZCAN_Receive_Data rx[100];safe_printf("[CAN RX] 启动\n");while(1){UINT cnt=ZCAN_Receive(hCan,rx,100,100);for(UINT i=0;i<cnt;i++){safe_printf("[RX] %03X :",rx[i].frame.can_id&0x7FF);for(intj=0;j<rx[i].frame.can_dlc;j++)safe_printf(" %02X",rx[i].frame.data[j]);safe_printf("\n");}}}/* ================= 主函数 ================= */intmain(){DEVICE_HANDLE hDev;CHANNEL_HANDLE hCan;ZCAN_CHANNEL_INIT_CONFIG cfg={0};InitializeCriticalSection(&g_printLock);hDev=ZCAN_OpenDevice(DEVICE_TYPE,DEVICE_INDEX,0);if(hDev==INVALID_DEVICE_HANDLE){safe_printf("CAN 打开失败\n");return-1;}cfg.can_type=TYPE_CAN;cfg.can.acc_code=0;cfg.can.acc_mask=0xFFFFFFFF;cfg.can.filter=0;cfg.can.timing0=0x00;cfg.can.timing1=0x16;// 250Kcfg.can.mode=0;// ★ 正常模式hCan=ZCAN_InitCAN(hDev,CAN_INDEX,&cfg);ZCAN_StartCAN(hCan);safe_printf("CAN0 已启动(正常模式)\n");_beginthreadex(NULL,0,CanRxThread,hCan,0,NULL);ZCAN_Transmit(hCan,&g_frame_5BB,1);safe_printf("已发送 0x5BB\n");while(!(GetAsyncKeyState(VK_ESCAPE)&0x8000)){Update1BB();Update2BB();ZCAN_Transmit(hCan,g_frames,FRAME_COUNT);Sleep(50);}ZCAN_CloseDevice(hDev);DeleteCriticalSection(&g_printLock);return0;}