VS2012 MFC编程之USB-CAN II通讯上位机(1)

       最近做实验需要用到上位机显示,所以编写了这个USB-CAN上位机通讯程序。本程序是采用MFC编写,通讯程序比较简单,主要是调用API函数(API的操作请参考相关说明文档)对下位机进行操作,下位机采集的数据再返回回上位机显示,显示部分使用了TeeChart控件,后面我会再次写博客介绍该控件的使用,这篇文章主要讲解如何编写CAN通讯的上位机程序。

(1)建立工程

       先建立基于对话框的MFC工程,将ControlCAN.lib,ControlCAN.DLL,ControlCAN.h这三个文件拷贝到工程目录下,并在工程中添加头文件

#include "ControlCAN.h"

接下来在项目属性页里的配置属性→连接器→输入→附加依赖项中添加ControlCAN.lib这个文件,或者直接在工程中添加如下代码:

#pragma comment(lib,"controlcan.lib")

再添加DevType(设备类型),DevIndex(设备索引)这两个变量作为类的成员变量,变量的初始化值如下所示:

CPV_Test01Dlg::CPV_Test01Dlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CPV_Test01Dlg::IDD, pParent)
//	, m_chart(0)
	, nDeviceType(4)                //设备类型,对于USB-CAN2 配置为4
	, nDeviceInd(0)                 //设备索引,只有一个USB-CAN适配器时,索引号为0
	, nConnectFlag(0)
	, nReceiveFlag(FALSE)
//	, nFilterFlag(FALSE)
	, szRXID(_T(""))
	, szTXID(_T(""))
	, szTXData(_T(""))
	, pThread(NULL)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON);
}

至此,和CAN有关的配置基本完成。

对话框如下所示:

(2)连接CAN

    连接CAN的消息响应函数如下所示:

void CPV_Test01Dlg::OnBnClickedStart()
{
	int nCANInd = 0; /* 第1个通道 */

	DWORD dwRel;
	VCI_INIT_CONFIG vic;

	if(nConnectFlag == 0)
	{
		dwRel = VCI_OpenDevice(nDeviceType, nDeviceInd, 0);

		if(!dwRel)
		{
			AfxMessageBox(_T("打开设备失败!"));
			return;
		}
		nConnectFlag = 1;
		GetDlgItem(IDC_START) ->SetWindowTextW(_T("断开CAN"));

		vic.AccCode=0x80000008;
		vic.AccMask=0xFFFFFFFF;
		vic.Filter=1;           //接收所有类型(扩展帧+标准帧)
		vic.Timing0=0x00;
		vic.Timing1=0x1C;		//波特率500kbps
		vic.Mode=0;				//正常模式
		dwRel = VCI_InitCAN(nDeviceType, nDeviceInd, nCANInd, &vic);
	
		if(!dwRel)
		{
			AfxMessageBox(_T("初始化设备失败!"));
			return;
		}
		dwRel = VCI_StartCAN(nDeviceType, nDeviceInd, nCANInd);
		if(!dwRel)
		{
			VCI_CloseDevice(nDeviceType, nDeviceInd);
			AfxMessageBox(_T("启动设备失败!"));
			return;
		}
		AfxMessageBox(_T("设备启动成功!"));	
	}
	else
	{
		dwRel = VCI_CloseDevice(nDeviceType, nDeviceInd);
		if(dwRel != 1)
		{
			AfxMessageBox(_T("关闭设备失败!"));
			return;
		}
		nConnectFlag = 0;
		GetDlgItem(IDC_START) ->SetWindowTextW(_T("连接CAN"));

		AfxMessageBox(_T("设备关闭成功!"));
	}

}

        当点击“连接CAN”按钮时进入消息响应函数,根据标志位nConnectFlag判断设备是否已经连接。设备未连接时,调用API函数dwRel = VCI_OpenDevice(nDeviceType, nDeviceInd, 0)打开CAN设备,再将标志位置一,同时将按钮文字设置为“断开CAN”。

        打开设备后再对设备进行初始化操作,先填充CAN初始化结构体VCI_INIT_CONFIG,主要是对波特率、收发模式、接收数据类型进行配置。配置好结构体之后调用CAN初始化函数dwRel = VCI_InitCAN(nDeviceType, nDeviceInd, nCANInd, &vic)初始化设备。

        当设备连接成功后,标志位nConnectFlag=1,再次点击“断开CAN”按钮时调用关闭设备API函数dwRel = VCI_CloseDevice(nDeviceType, nDeviceInd)关闭CAN设备,再将标志位置零,同时将按钮文字设置为“连接CAN”。

(3)发送数据

        点击发送按钮,调用的消息响应函数如下所示:

void CPV_Test01Dlg::OnBnClickedSend()
{
	UpdateData(TRUE);

	DWORD dwRel;
	CString str;
	VCI_CAN_OBJ frameinfo;
        int nCANInd = 0;

	frameinfo.DataLen = 1;
	frameinfo.RemoteFlag =0;
	frameinfo.ExternFlag =1;
	frameinfo.SendType =0;
	frameinfo.ID =_wtoi(szTXID);
//	frameinfo.ID =0xFF;
	frameinfo.Data[0] = _wtoi(szTXData);
//	frameinfo.Data[0] =123;

	dwRel = VCI_Transmit(nDeviceType,nDeviceInd,nCANInd,&frameinfo,1);	//发送帧数不宜过大

	if(dwRel==-1)
	{
		AfxMessageBox(_T("数据发送失败!"));
		return;
	}
//	AfxMessageBox(_T("数据发送成功!"));
}

      在发送数据函数中,先调用UpdateData(TRUE)将对话框中配置的数据(主要配置发送ID、数据类型、发送的数据等)调回到内存变量中,再配置CAN帧结构体VCI_CAN_OBJ,最后调用API发送数据函数dwRel = VCI_Transmit(nDeviceType,nDeviceInd,nCANInd,&frameinfo,1),该函数最后的一个变量为发送的帧数量,通过测试发现,该值不能设置得过大,设置得过大会导致发送数据时接收数据出错,一般以实际要发送的帧数量为准。

(4)接收数据

   下位机发送数据给上位机,通过配置接收线程来接收数据。点击接收数据复选框时调用以下函数开启线程,函数如下所示:

void CPV_Test01Dlg::OnBnClickedReceive()
{
	UpdateData(TRUE);

	if(nReceiveFlag)
	{
		pThread = AfxBeginThread(ReceiveThread,0);			//开启线程
		nStopFlag=0;
	}
	else
	{
		nStopFlag=1;
	}
}

     在接收线程函数中接收数据,接收线程函数必须定义为类的静态成员函数或者全局函数,不然会报错。接收线程函数如下所示:

unsigned int CPV_Test01Dlg::ReceiveThread(void* param)
{
	CPV_Test01Dlg *dlg=(CPV_Test01Dlg*) AfxGetApp()->GetMainWnd();					//获取主窗口指针

	VCI_CAN_OBJ frameinfo[2500];
	int len;
	CString str,szID;

	while(1)
	{
		len = VCI_Receive(dlg ->nDeviceType,dlg ->nDeviceInd,0,frameinfo,2500,0);    //2500为接收缓存区,尽量设大,避免数据溢出

		if(len>=1)
		for(int num=0;num<len;num++)
		{
			dlg ->GetDlgItemTextW(IDC_RXID,szID);
			str.Format(_T("%d"),frameinfo[num].ID);

			if(BST_UNCHECKED == dlg ->m_checkFilter.GetCheck()||
				(BST_CHECKED == dlg ->m_checkFilter.GetCheck()&&str==szID))
			{
				dlg ->m_chart.Series(0).AddXY(nCount,frameinfo[num].Data[0],NULL,NULL);
				str.Format(_T("%d"),frameinfo[num].Data[0]);
				dlg ->SetDlgItemTextW(IDC_RXDATA,str);
				nCount++;
				str.Format(_T("%d"),nCount);
				dlg ->SetDlgItemTextW(IDC_RXDATANUMBER,str);
			}
		}
		
		Sleep(1);			//进程延时1ms

		if(nStopFlag)
			break;			//退出while循环

	}
	return TRUE;
}

      接收线程函数为类内的静态成员函数,不能访问类内的成员变量,通过*dlg=(CPV_Test01Dlg*) AfxGetApp()->GetMainWnd()获取类的指针,进而访问类内的成员变量。通过调用API函数len = VCI_Receive(dlg ->nDeviceType,dlg ->nDeviceInd,0,frameinfo,2500,0)来获取接收数据的信息,接收帧信息存放在结构体变量frameinfo中。接收数据的缓存区尽可能的设大以防止数据的丢失。

(5)总结

      下位机通过STM32单片机配置好后,下位机采集到的数据通过CAN收发器发送出来,最后在上位机中显示。发送数据的速率不宜过快,太快会导致数据丢失。

      上位机软件界面如下所示:

 

提问方式:有啥不懂的可以随时向我提问哈,扫描下方二维码我会在第一时间给大家回复的哈,谢谢。 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页