【微小的经验】ZLGCAN二次开发接口函数库的使用与数据解析

设备:ZCAN_USBCANFD_200U

引入ZLGCAN库

打开官方资源界面:https://www.zlg.cn/can/down/down/id/22.html

打开 应用软件 -【应用软件】ZLGCAN上位机例程

下载其中的 zlgcan二次开发接口函数库(2024.4.16).zip

将文件中的x64/x86的lib根据需求添加到环境变量或者直接在项目中引用。

此处举例两种引用方式(Visual Studio & CMake)

  • VS需在属性-链接器-输入-添加依赖项中添加 zlgcan_x64(x86)/zlgcan.lib
  • CMake需在CMakeLists中添加如下内容:
target_link_libraries(${PROJECT_NAME} PRIVATE
    debug $ENV{ZLGCAN_PATH}/zlgcan_x64/zlgcan.lib
    optimized $ENV{ZLGCAN_PATH}/zlgcan_x64/zlgcan.lib
)

此处ZLGCAN_PATH已经在环境变量中添加,路径为 D:\xx\CAN_lib\zlgcanlib(路径根据使用者的实际使用路径变化。)

代码生成时会根据选择的Debug/Release生成对应的文件夹,将ZLGCAN开发接口函数库中的kerneldlls文件夹及zlgcan.dll文件拷贝到Debug/Release文件夹中。以确保ZLGCAN的接口函数可以正常使用。

此时环境正确配置完成。

开启ZLGCAN设备

创建设备句柄

DEVICE_HANDLE mDeviceHandle;

调用ZCAN_OpenDevice函数。ZCAN_OpenDevice的三个参数分别是,设备类型,设备索引,和一个保留参数位。

我使用的设备是USB 200U 因此在设备类型中填写ZCAN_USBCANFD_200U,其他两个位置填入0。

mDeviceHandle = ZCAN_OpenDevice(ZCAN_USBCANFD_200U, 0, 0);

启动ZLGCAN设备通道

创建通道句柄

CHANNEL_HANDLE mChannelHandle[2];

初始化通道配置,设置CAN协议,设置终端电阻,设置波特率。

ZCAN_CHANNEL_INIT_CONFIG config;

// 设置CAN协议
char path[50] = { 0 };
char value[100] = { 0 };
sprintf_s(path, "%d/canfd_standard", channel);
sprintf_s(value, "%d", 0);
unsigned int ret = ZCAN_SetValue(deviceHandle, path, value);
if (!ret)
{
    return false;
}

// 设置终端电阻
char path3[50] = { 0 };
sprintf_s(path3, "%d/initenal_resistance", channel);
char value3[10] = { 0 };
sprintf_s(value3, "%d", 1);
ret = ZCAN_SetValue(deviceHandle, path3, value3);
if (!ret)
{
    return false;
}

// 设置波特率
char path2[50] = { 0 };
sprintf_s(path2, "%d/canfd_abit_baud_rate", 1);
char value2[10] = { 0 };
sprintf_s(value2, "%d", 500000);
int ret1 = ZCAN_SetValue(deviceHandle, path2, value2);

sprintf_s(path2, "%d/canfd_dbit_baud_rate", 1);
sprintf_s(value2, "%d", 2000000);
int ret2 = ZCAN_SetValue(deviceHandle, path2, value2);
if ((ret1 && ret2))
{
    config.can_type = TYPE_CANFD;
    config.canfd.mode = 0;
    config.canfd.filter = 1;
    config.canfd.acc_code = 0x00000000;
    config.canfd.acc_mask = 0xFFFFFFFF;

    channelHandle = ZCAN_InitCAN(deviceHandle, channel, &config);

    if (INVALID_CHANNEL_HANDLE == channelHandle)
    {
        printf("初始化CAN失败");
        return false;
    }
}
else
{
    printf("设置波特率失败");
    return false;
}

if (ZCAN_StartCAN(channelHandle) != STATUS_OK)
{
    printf("启动通道失败");
    return false;
}

此流程如果可以正常完成,观察ZLGCAN的指示灯状态,如果由暗转亮,证明已经正常开启了通道,可以继续之后的操作。如果没有,回去看下kerneldlls文件是否正确置入Debug/Release文件夹,或者设备的连接是否正常。

唤醒设备及解析设备信息

此处需根据项目需求进行处理。如果项目中作为数据源的设备需要唤醒,可以先创建线程,并持续发送唤醒命令。唤醒命令的发送如下。线程的处理根据个人需求来做,在此不做举例。

unsigned char data[8] = { 0x00 ,0x01 ,0x02 ,0x03 ,0x04 ,0x05 ,0x06 ,0x07 };

//向两个通道持续发送命令,保持通信
for (int i = 0; i < 2; i++)
{
    if (mChannelHandle[i] != INVALID_CHANNEL_HANDLE)
    {
        mZlgCan.sendCANFDMsg(0x600, data, 8, mChannelHandle[i]);
    }
}

//此处使用的sendCANFDMsg如下

bool ZlgCan::sendCANFDMsg(unsigned int canId, unsigned char* data, unsigned char dataLen, CHANNEL_HANDLE channelHandle)
{
    UINT result = 0;
    ZCAN_TransmitFD_Data canfd_data;

    bool bDelay = 0;
    UINT nDelayTime = 1000;

    memset(&canfd_data, 0, sizeof(canfd_data));
    canfd_data.frame.can_id = canId;
    canfd_data.frame.len = dataLen;
    memcpy(canfd_data.frame.data, data, dataLen);
    canfd_data.transmit_type = 0;
    canfd_data.frame.flags |= 1 ? CANFD_BRS : 0;
    if (bDelay)
    {
        canfd_data.frame.flags |= TX_DELAY_SEND_FLAG;
        canfd_data.frame.__res0 = 0x00FF & nDelayTime;
        canfd_data.frame.__res1 = 0xFF00 & nDelayTime > 8;
    }

    //由于有时会遇到channel只连接了一路的情况,外围的循环获取数据会导致数据堵塞,
    //为避免此问题,解决办法就是直接在初始化时,如果无法正常调用,就直接关闭没有设备连接的channel
    //此处的超时处理就是为了解决这个问题

    time_t stime, curtime;

    time(&stime);

    result = ZCAN_TransmitFD(channelHandle, &canfd_data, 1);

    time(&curtime);

    int passtime = (curtime - stime) * 1000;

    if (passtime > 1000)
    {
        ZCAN_ResetCAN(channelHandle);
    }

    if (result != 1)
    {
        return false;
    }
    else
    {
        return true;
    }
}

假设目前设备已经在持续发送数据,那么在连接通道后,我们需要做的就是解析接收到的设备信息。解析过程中的实际解析内容要根据实际项目去适配,在此只举例解析的流程,不关心解析内容。

开启一个接收数据并解析数据的线程,先接收,后解析。

接收过程如下:

ZCAN_ReceiveFD_Data canfd_data[100];

if (len = ZCAN_GetReceiveNum(channelHandle, TYPE_CANFD))
{
    len = ZCAN_ReceiveFD(channelHandle, canfd_data, 100, 66);

    for (auto i = 0; i < len; ++i)
    {
        memcpy(&canfdArr[i], &canfd_data[i].frame, sizeof(canfd_frame));
    }
}

解析则根据canfd_frame中的can_id来解析对应的数据。

can_id的值为多少到多少是哪段数据,需要根据协议进行实际的判断。

canfd_frame canfdframe[2][100];

//举例,例如id为0x400

if (canfdframe[chn][i].can_id == 0x400)
{
    //根据其中的数据位解析对应的数据
}

此处除了手动去解析数据位,还有种方法是直接根据dbc去解析,此时可以使用ZLGCAN官方的zdbc_lib库来做解析。

ZDBC_LIB的使用

打开文章上方的ZLG官网,找到并下载 应用软件 - 【应用软件】DBC解析模块库和示例代码_240612

在下载的文件中找到zdbc_lib,使用跟上面相同的方式引入lib。

首先需要加载dbc文件:

DBCHandle mDBC = ZDBC_Init();

if (INVALID_DBC_HANDLE == mDBC)
{
    qDebug() << "[DBC] Init dbc handle failed!";
    return false;
}

FileInfo fileinfo;
strcpy_s(fileinfo.strFilePath, _MAX_FILE_PATH_, path.toStdString().c_str());
fileinfo.merge = false;

if (!ZDBC_LoadFile(mDBC, &fileinfo))
{
    qDebug() << "[DBC] Load dbc file failed!";
    return false;
}

if (0 == ZDBC_GetMessageCount(mDBC))
{
    qDebug() << "[DBC] File no message!";
    return false;
}

成功加载dbc文件后,便可在接收数据的处理后进行解析:

if (!ZDBC_Analyse(mDBC, &canfdFrame, FT_CANFD, pMsg))
{
    return false;
}

double mData[MAX_MESSAGE_SIGNAL_NUM];
for (auto i = 0; i < pMsg->nSignalCount; ++i)
{
    double ret = ZDBC_CalcActualValue(&pMsg->vSignals[i], &pMsg->vSignals[i].nRawValue);
    mData[i] = ret;
}
parseData(mData, pMsg->nSignalCount, data);

此处使用ZDBC库解析后只需要读取信号中的值,而不需要在代码中再去做协议解析。

以上只是个人在开发中的一些收获和认识,肯定会有很多不足的地方,欢迎各位大佬指点问题,也欢迎有相关开发经验或者跟我有类似问题的朋友多多交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值