Labwindows/CVI 编写CAN通讯的上位机

前言

   本人从事电机测试已久,会编写DSP的CAN通讯程序编写,但是一直对上位机的编译学不会。学习了很长一段时间的C#但是还是没有弄明白动态链接库和委托,最后我公司来了一位软件经理指导我一下,劝我放弃C#转学习Labwindows/CVI这样一来既可以学习C语言又可以学习上位机。通过对比我发现CVI确实比C#更容易一些,接下来我就将我写的过程分享一下。

一、Labwindows如何学习

    CVI学习起来很简单,如果有C语言的基础完全没有问题,个人建议学习的话在网上找一个视频,学习一下基本控件,然后学习一下动态链接库和多线程或者异步定时器就可以编写CAN通讯的上位机。CAN通讯的示例我是参照周立功的USBCAN_E_2E_U的【应用程序】labwindows_example(U系列)。

二、编译步骤

1.引入动态库

    找到了ControlCAN.dll、ControlCAN.h、ControlCAN.lib三个文件,放到了工程文件夹内,lib文件添加顺序是右击工程->Add Existing File->选中ControlCAN.lib文件。H文件添加顺序是右击工程->Add Existing File->选中ControlCAN.h文件。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201025165932154.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzQyNDMzOTcy,size_16,color_FFFFFF,t_70#pic_center)!

注意在生成C文件后,将#include “ControlCAN.h” 添加上

2.如何编译控件

    如何编写控件的最重要的一步就是要找到周立功的《接口函数库(二次开发库)》,仔细看看这几个函数VCI_OpenDevice、VCI_StartCAN、VCI_CloseDevice、 VCI_ResetCAN。首先界面上制作出来这几个控件:打开设备、启动CAN、复位CAN和关闭设备,并右击控件建立Callback函数。以打开设备为例,编写代码如下:

int CVICALLBACK OpenDevice_callback (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
VCI_INIT_CONFIG config;

DWORD AccCode;
DWORD AccMask;
UCHAR Filter;
UCHAR Mode;switch (event)
{case EVENT_COMMIT:if(connect==1)//如果连接则退出{return 0;}GetCtrlVal (panelHandle,PANEL_DEVICE_TYPE, &DeviceType);if(VCI_OpenDevice(DeviceType,0,0)==1)  //打开设备{SetCtrlVal (panelHandle, PANEL_LED, 1);}else{SetCtrlVal (panelHandle, PANEL_LED, 0);MessagePopup("提示","打开设备失败!");}//初始化CAN0通道参数SetBaud (panelHandle, 0, EVENT_COMMIT,0, 0, 0);   AccCode=0;     //验收码AccMask=-1;       //屏蔽码Filter=0;     //滤波方式Mode=0;         //模式config.AccCode=AccCode;config.AccMask=AccMask;config.Filter=Filter;config.Mode=Mode;config.Timing0=Timing0;config.Timing1=Timing1;  GetCtrlVal (panelHandle, PANEL_CAN_CAHENNEL, &CANChannel);if((VCI_InitCAN(DeviceType,0,CANChannel,&config))==0)                            //初始化{MessagePopup("错误","初始化失败");}connect=1;break;
}
return 0;

}
其中启动CAN、复位CAN和关闭设备的代码完全可以参照周立功的示例。

3.如何建立多线程并接受和发送

    在启动CAN的程序下建立多线程   CmtScheduleThreadPoolFunction (DEFAULT_THREAD_POOL_HANDLE, ThreadFunction,NULL, &threadID);  多线程的程序如下

int CVICALLBACK ThreadFunction(void *functionData)
{

int  i=0;
unsigned int recLen = 0;
int ReceiveNum=0;
VCI_ERR_INFO errinfo;//错误信信息
static int led_count=0; while(startflag)
{GetCtrlVal (panelHandle, PANEL_DEVICE_TYPE, &DeviceType);GetCtrlVal (panelHandle, PANEL_CAN_CAHENNEL, &CANChannel);ReceiveNum=VCI_GetReceiveNum(DeviceType,0,CANChannel);//接收到但尚未被读取的帧数量              if(0==ReceiveNum){//注意:如果没有读到数据则必须调用此函数来读取出当前的错误码,//千万不能省略这一步(即使你可能不想知道错误码是什么)CANChannel=0;VCI_ReadErrInfo(DeviceType,0,CANChannel,&errinfo);}else{SetCtrlVal (panelHandle, PANEL_RECEIVE_BUFFER, ReceiveNum);}recLen = VCI_Receive(DeviceType,0,CANChannel,rec,50,400); //接收函数。此函数从指定的设备CAN通道的接收缓冲区中读取数据。 //(设备类型,设备索引,can通道,用来接收的帧结构体VCI_CAN_OBJ数组的首指针用来接收的帧结构体数组的长度,保留参数)if((recLen>0) &&(recLen!=0xFFFFFFFF)) //4294967295{Delay(0.001);     led_count++;if(led_count>=7){SetCtrlVal (panelHandle, PANEL_LED_rec, 1);   led_count=0;}else if(led_count>=3){SetCtrlVal (panelHandle, PANEL_LED_rec, 0);   }for( int n = 0; n < recLen; n++ ){rec[i].TimeStamp=0;  //设备接收到某一帧的时间标识。 时间标示从CAN卡上电开始计时,计时单位为0.1ms。switch(rec[i].ID){case 0x00f07008:int counter=0;counter=rec[i].Data[1];SetCtrlVal (panelHandle, PANEL_Counter,counter);  break;case 0x00f07108:Udc_P28=(((rec[i].Data[0]&0x00FF)<<8)+((rec[i].Data[1]&0x00FF)))*0.1;Idc_P28=(((rec[i].Data[2]&0x00FF)<<8)+(rec[i].Data[3]&0x00FF))*0.1;Udc_N28=(((rec[i].Data[4]&0x00FF)<<8)+(rec[i].Data[5]&0x00FF))*0.1;Idc_N28=(((rec[i].Data[6]&0x00FF)<<8)+(rec[i].Data[7]&0x00FF))*0.1;P_kW=(Udc_P28*Idc_P28)*0.001;N_kW=(Udc_N28*Idc_N28)*0.001;kW=P_kW+N_kW;SetCtrlVal (panelHandle, PANEL_Udc_P,Udc_P28);  SetCtrlVal (panelHandle, PANEL_Idc_P,Idc_P28);SetCtrlVal (panelHandle, PANEL_P_kW,P_kW);SetCtrlVal (panelHandle, PANEL_Udc_N,Udc_N28);SetCtrlVal (panelHandle, PANEL_Idc_N,Idc_N28);SetCtrlVal (panelHandle, PANEL_N_kW,N_kW);SetCtrlVal (panelHandle, PANEL_KW,kW);break;case 0x00f07208:double Moterhour=0;double MoterWaterTemp=0;double Moteroilpress=0;double Moterspeed=0;Moterhour=(((rec[i].Data[0]&0x00FF)<<8)+((rec[i].Data[1]&0x00FF)))*0.1;MoterWaterTemp=rec[i].Data[2]-50;Moteroilpress=rec[i].Data[3]*4;Moterspeed=((rec[i].Data[4]&0x00FF)<<8)+((rec[i].Data[5]&0x00FF));SetCtrlVal (panelHandle, PANEL_Moterhour,Moterhour);SetCtrlVal (panelHandle, PANEL_MoterWaterTemp,MoterWaterTemp); SetCtrlVal (panelHandle, PANEL_Moteroilpress,Moteroilpress); SetCtrlVal (panelHandle, PANEL_Moterspeed,Moterspeed); break;case 0x00f07308:int  WorkState; //发电机工作状态int  Worklight; //发电指示int  GENOverTemp; //发电机过温int  INVOverTemp; //控制器过温int  oilPressAlarm; //油压报警int  OverCurrentAlarm; //过流报警int  OverVoltageAlarm; //过压报警 WorkState=(rec[i].Data[0]&0xc0)>>6;Worklight=(rec[i].Data[0]&0x30)>>4; GENOverTemp=(rec[i].Data[1]&0x80)>>7;INVOverTemp=(rec[i].Data[1]&0x40)>>6;oilPressAlarm=(rec[i].Data[1]&0x20)>>5;OverCurrentAlarm=(rec[i].Data[1]&0x10)>>4;OverVoltageAlarm=(rec[i].Data[1]&0x08)>>3;if(WorkState==2) {SetCtrlVal (panelHandle, PANEL_WorkState,1);}else if(WorkState==1){SetCtrlVal (panelHandle, PANEL_MArm,1);}else if(WorkState==0){SetCtrlVal (panelHandle, PANEL_WorkState,0);    }if(Worklight==2) {SetCtrlVal (panelHandle, PANEL_Worklight,1);}else if (Worklight==1){SetCtrlVal (panelHandle, PANEL_Arm,1);   }else if(Worklight==0){SetCtrlVal (panelHandle, PANEL_Worklight,0);   }if(GENOverTemp==1) {SetCtrlVal (panelHandle, PANEL_GENOverTemp,1);}else{SetCtrlVal (panelHandle, PANEL_GENOverTemp,0);   }if(INVOverTemp==1) {SetCtrlVal (panelHandle, PANEL_INVOverTemp,1);}else{SetCtrlVal (panelHandle, PANEL_INVOverTemp,0);   }if(oilPressAlarm==1) {SetCtrlVal (panelHandle, PANEL_oilPressAlarm,1);}else{SetCtrlVal (panelHandle, PANEL_oilPressAlarm,0);     }if(OverCurrentAlarm==1) {SetCtrlVal (panelHandle, PANEL_OverCurrentAlarm,1);}else{SetCtrlVal (panelHandle, PANEL_OverCurrentAlarm,0);    }if(OverVoltageAlarm==1) {SetCtrlVal (panelHandle, PANEL_OverVoltageAlarm,1);}else{SetCtrlVal (panelHandle, PANEL_OverVoltageAlarm,0);    }break;case 0x00f07408:int  a; //int  x; //int  y; //int  year; //年int  moth; //月int  day; //日int  sleftest; //自检信息 a=rec[i].Data[0];x=rec[i].Data[1];y=rec[i].Data[2];year=rec[i].Data[3]+2000;moth=rec[i].Data[4];day=rec[i].Data[5];sleftest=rec[i].Data[6];SetCtrlVal (panelHandle, PANEL_A,a);SetCtrlVal (panelHandle, PANEL_X,x); SetCtrlVal (panelHandle, PANEL_Y,y); SetCtrlVal (panelHandle, PANEL_Year,year); SetCtrlVal (panelHandle, PANEL_Moth,moth); SetCtrlVal (panelHandle, PANEL_Day,day); SetCtrlVal (panelHandle, PANEL_Sleftest,sleftest); break;   } }}
}return 0;

}
在PANL_callback 的函数下关闭多线程
int CVICALLBACK PANL_callback (int panel, int event, void *callbackData,
int eventData1, int eventData2)
{

switch (event)
{case EVENT_GOT_FOCUS:break;case EVENT_LOST_FOCUS:break;case EVENT_CLOSE:GetCtrlVal (panelHandle, PANEL_DEVICE_TYPE, &DeviceType);VCI_CloseDevice(DeviceType,0);QuitUserInterface (0);startflag = 0;CmtWaitForThreadPoolFunctionCompletion (DEFAULT_THREAD_POOL_HANDLE, threadID,0);  //立即结束CmtReleaseThreadPoolFunctionID (DEFAULT_THREAD_POOL_HANDLE, threadID); //释放线程函数break;
}
return 0;

}
CAN接受函数如下;
int CVICALLBACK CAN_Transmit (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int GetTransmit;
int statues;
switch (event)
{
case EVENT_COMMIT:
GetCtrlVal (panelHandle, PANEL_DEVICE_TYPE, &DeviceType);
GetCtrlVal (panelHandle, PANEL_CAN_CAHENNEL, &CANChannel);
GetCtrlVal (panelHandle, PANEL_CMD_TRANSMIT, &statues);
send[0].ID= 0x00f06407; //帧ID
send[0].SendType=0; //正常发送
send[0].RemoteFlag=0; //0时为为数据帧
send[0].ExternFlag= 1;//1时为扩展帧
send[0].DataLen=0x08; //数据长度

     if(statues==1){send[0].Data[1]=0x01;for(int i=0;i<100;i++){GetTransmit=VCI_Transmit(DeviceType,0,CANChannel,send,1); }}else{send[0].Data[1]=0x02;for(int i=0;i<100;i++){GetTransmit=VCI_Transmit(DeviceType,0,CANChannel,send,1);}}break;
}
return 0;

}

总结

    学会了Labwondows/CVI的程序编译后,在看看《接口函数库(二次开发库)》对照周立功的CVI示例很容易就能写出来一个上位机。不过我的程序存在BUG还需要慢慢修改,个人感觉可能异步定时器要比多线程要好一些吧。

Labwindows/CVI 编写CAN通讯的上位机相关推荐

  1. 通过串口通讯实现LabWindows/CVI对GDM-906X万用表的上位机控制

    前言:本篇介绍了如何通过串口通讯实现对GDM-906X万用表的上位机控制,由于小明还在对LabWindows/CVI的学习阶段,只是实现了对AC/DC电压电流和电阻的测量. 界面演示: 一.创建uir ...

  2. C#与三菱PLC以太网通讯程序上位机源码 通过3E帧SLMP /MC协议与三菱FX5U/Q系列PLC通讯

    C#与三菱PLC以太网通讯程序上位机源码 通过3E帧SLMP /MC协议与三菱FX5U/Q系列PLC通讯 1.该程序可以与FX5U/Q系列PLC以太网通讯,根据3E帧报文写了一个类库,可以读写各种类型 ...

  3. C#与三菱PLC以太网通讯程序上位机源码 通过3E帧SLMP MC协议与三菱FX5U Q系列PLC通讯

    C#与三菱PLC以太网通讯程序上位机源码 通过3E帧SLMP MC协议与三菱FX5U Q系列PLC通讯 1.该程序可以与FX5U Q系列PLC以太网通讯,根据3E帧报文写了一个类库,可以读写各种类型和 ...

  4. 雷丁CAN通讯信号上位机,比德文,宝路达,看通讯报文,查故障,灰常方便实用

    雷丁CAN通讯信号上位机,比德文,宝路达,看通讯报文,查故障,灰常方便实用. 本CAN上位机,适用雷丁通用can协议国标报文.轻松读取CAN报文,且界面清晰,数据流一目了然,修车好帮手. 专用数据插头 ...

  5. 雷丁CAN通讯信号上位机,比德文,宝路达,看通讯报文,查故障 ,非常方便实用。

    雷丁CAN通讯信号上位机,比德文,宝路达,看通讯报文,查故障 ,非常方便实用. 本CAN上位机,适用雷丁通用can协议国标报文.轻松读取CAN报文,且界面清晰,数据流一目了然,修车好帮手.确定仪表状态 ...

  6. C#编写一个串口助手上位机软件

    对于工程师来说,串口助手可以说是必不可少的一个工具,一个好的串口助手可以大大方便我们的研发调试.网上串口助手很多,如果能够根据自己需要做一个合适的串口助手,那么既能方便自己,也能掌握上位机的开发,对于 ...

  7. java 上位机 socket_通讯编程上位机软件实现(SOCKET)——第二回

    这篇废话不多说,直接上代码. 首先说明,通讯过程中的异常均不进行处理(连接异常除外),由超时重发控制. 一.获取SOCKET连接类TimeOutSocket public class TimeOutS ...

  8. Modbus通信从入门到精通_2_Modbus TCP通信详解及仿真(搭建ModbusTCP仿真环境:创建虚拟PLC并进行ModbusTCP通讯;寄存器与PLC中映射关系;适合理解如何编写上位机)

    本篇将会以西门子PLC软件搭建ModbusTCP仿真环境,并通过仿真环境,介绍基础知识及模拟实际应用中写一个简单的通信读取PLC数据方法,并简介了编写上位机的方法. 文章目录 1. 搭建ModbusT ...

  9. 实现一个CAN通讯上位机

    实现一个CAN通讯的上位机,需要满足以下步骤: 获取CAN接口硬件,并连接到计算机上.一般来说,CAN接口硬件会提供一个USB接口,可以直接连接到计算机上. 安装驱动程序.在大多数情况下,CAN接口硬 ...

  10. 写字机上位机c语言,易懂 | 手把手教你编写你的第一个上位机

    一.前言 大家好,我是ZhengN,本次来教大家编写一个基于QT的简单的上位机. 学习一个新的东西我们都从最基础地实例开始,比如学习C语言我们会从编写一个hello程序开始.学习嵌入式我们从点灯开始. ...

最新文章

  1. 人工智能AI Boosting HMC Memory Chip
  2. 【Arduino】按键按下执行不同模式程序
  3. 容器可以作为全局变量吗_四季青是风水树吗?哪些可以作为风水树?
  4. django mysql connector,MySQL Connector / python在Django中不起作用
  5. IQ测试(jzoj 5048)
  6. 以太网数据帧的报尾封装字段是什么_16、90秒快速“读懂”数据包的封装、解封装...
  7. 手机端网页开发的两个重要设置
  8. 微信公开课讲师李卿:小游戏开放 100 天
  9. 蓝桥杯 ALGO-21算法训练 装箱问题 java版
  10. 在线HTTP请求/响应头转JSON工具
  11. DotNetNuke 中文乱码问题的解决
  12. Linux篇---Grep和正则匹配
  13. 如何把PPT文件压缩到最小
  14. cocos creator 广告控制脚本
  15. 地图开发实战案例:高德地图弧线连接线标注
  16. 群晖外网映射网络盘符访问教程[转]
  17. Choosing Teams
  18. Glide的使用回收内存问题
  19. linux中nginx启动,重启,关闭命令
  20. N1盒子armbian蓝牙连接详细步骤

热门文章

  1. 说说我眼中的社交电商:深入浅出分析“每日一淘”
  2. 金士顿服务器内存条怎么看型号,区分内存条型号的方法
  3. Windows系统下桌面右键菜单新建项管理
  4. 用Excel利用RFM模型进行客户细分
  5. C2872 “detail”: 不明确的符号
  6. excel制作项目甘特图
  7. 芯唐语音识别_大联大品佳集团推出基于新唐科技的智能语音识别解决方案
  8. roundcube mysql_Webmail Roundcube安装配置
  9. 君正X1000开发板/方案开发介绍
  10. 车牌识别ocr为何物—科技普及大讲堂1