当前位置: 移动技术网 > 科技>操作系统>windows > Labwindows/CVI 编写CAN通讯的上位机

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

2020年10月25日  | 移动技术网科技  | 我要评论
Labwindows/CVI 编写CAN通讯的上位机前言 本人从事电机测试已久,会编写DSP的CAN通讯程序编写,但是一直对上位机的编译学不会。学习了很长一段时间的C#但是还是没有弄明白动态链接库和委托,最后我公司来了一位软件经理指导我一下,劝我放弃C#转学习Labwindows/CVI这样一来既可以学习C语言又可以学习上位机。通过对比我发现CVI确实比C#更容易一些,接下来我就将我写的过程分享一下。一、Labwindows如何学习 CVI学习起来很简单,如果有C语言的基础完全没有问题,

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还需要慢慢修改,个人感觉可能异步定时器要比多线程要好一些吧。

本文地址:https://blog.csdn.net/sinat_42433972/article/details/109275295

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网