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收發器發送出來,最後在上位機中顯示。發送數據的速率不宜過快,太快會導致數據丟失。

      上位機軟件界面如下所示:

 

提問方式:有啥不懂的可以隨時向我提問哈,掃描下方二維碼我會在第一時間給大家回覆的哈,謝謝。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章