安徽博微長安電子有限公司 龍 灝 丁旭玲
隨著監控系統的發展,監控系統與各個外圍設備數據傳輸量巨大,通訊方式越來越多樣,CPU處理任務越來越繁重。串口通訊的數據收發處理是監控程序的重要一部分,串口通信中如何結合異步通信、多線程等技術,編寫出高質量、高效率的串口通信程序,具有一定的現實意義。
在Windows平臺下,串口被視為一種特殊的文件,串口的操作可以被視為一種文件操作。在實際的編程中,可以使用文件相關的API函數,例如CreateFile()、ReadFile()以及WriteFile()等對串口進行關聯或者操作。
串口的操作模式分為同步模式和異步模式,同步模式是指程序中的代碼按順序執行,只有前面的程序執行完畢并返回,才能繼續執行后面的程序,該模式容易造成程序的假死。異步模式下,調用的函數會立即返回,不會造成線程的阻塞,線程可以在不同的句柄上同時執行I/O操作,甚至可以在同一句柄上同時進行讀寫操作,效率更高。
程序將串口操作函數及相應的變量封裝為串口類,方便主程序調用。主程序可定義串口類的對象調用各種函數,同時對多串口進行監聽及讀寫操作。串口類中的重要函數如表1所示:

表1 串口類包含的函數及功能實現
為了提高程序的效率,串口類中由事件驅動數據發送、接收和線程的退出。類中定義了3個事件:
m_hShutDownEvent(串口關閉事件)、m_hWriteEvent(數據發送事件)、Overlapped結構的m_ov(數據接收事件)。配合線程中的WaitForMultipleObjects()函數,實現線程的調度。為了實現對不同串口初始化,需要使用臨界區對象,以保證在某一時刻只進行一個串口的初始化。部分初始化代碼如下:
If(m_ov.hEvent!=NULL)//事件的建立
ResetEvent(m_ov.hEvent);
m_ov.hEvent=CreatEvent(NULL,TRUE,FALSE,NULL);//事件被定義為手動復位
……
m_h Event Array[0]=m_h Shut Dow n Ev ent;//m_hEventArray為事件數組
m_hEventArray[1]=m_ov.hEvent;
m_hEventArray[2]=m_hWriteEvent;
InitializeCriticalSection(&m_csCommunication);//初始化臨界區對象
……
串口的初始化首先需要使用API函數CreateFile()打開串口并創建與該串口相關聯的文件。注意將文件相關屬性設置為FILE_FLAG_OVERLAPPED,才可以對串口進行異步操作。打開串口代碼如下:
HANDLE hCom; //定義串口句柄
hCom=CreateFile(szPort,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,NULL);//創建異步模式文件并關聯串口,返回其句柄

表2 串口配置函數
在獲得通訊設備句柄后,可使用句柄進行串口參數設置。在串口API函數編程時,波特率、校驗方式等參數均被封裝到一個結構體DCB中。在查詢或配置串口的參數時,都要使用該結構體。配置串口使用的函數如表2所示。
串口初始化完成后,可在線程中等待通信事件的發生。線程函數中使用WaitForMultipleObjects()函數對m_hEventArray數組中的3個事件同時進行監測處理。當判斷某個事件為有信號時,執行相應的操作。線程函數部分代碼如下:
for (;;) //循環
{
……
Event = Wait For Multip leObjects(3, por t->m_hEventArray, FALSE, INFINITE);
//無限等待事件的發生
switch (Event)
{
case 0://線程關閉事件為有信號狀態
{
port->m_bThreadAlive = FALSE;
AfxEndThread(0);//退出線程
}
case 1: // 讀事件為有信號狀態
{
GetCommMask(port->m_hComm, &CommEvent);//得到串口事件
if (CommEvent & EV_RXCHAR)//判斷為接收事件
if(!port->m_bBlockRead)
ReceiveChar(port, comstat);//調用ReadFile()函數讀取數據,每接收一個字節數據向主程序送WM_COMM_RXCHAR消息和接收到的數據
break;
}
case 2: // 寫事件為有信號狀態
{
WriteChar(port);// 調用WriteFile()發送數據
break;
}
}
將串口類的源程序及頭文件導入到主程序工程中,即可在主程序中定義該串口類的對象,通過對象引用串口初始化InitPort()、串口監視函數StartMonitoring()等函數。當需要向串口發送數據時,可調用WritePort()函數。當串口類的線程收到一個字節的數據,會向主程序發送WM_COM_RXchar消息和接收到的數據,因此在主程序中需自定義WM_COMM_RxCHAR消息,并關聯消息處理函數以處理接收的數據。主程序消息處理函數部分代碼如下所示:
OnRecvByte(UINT wParam,LONG lParam)
{
Unsigned char JustRecv=(unsigned char)wParam;
Int PortNum=(int)lParam;
If(PortNum==1)
{
RecvData[Cnt]=JustRecv;//將接收的字節保存
Cnt++;
}
…
}
上面的代碼為自定義消息WM_COMM_RxCHAR的處理函數OnRecvByte(),在消息處理函數中將不同串口的數據存儲到指定的數組中,供主程序使用。
本文介紹了基于API函數的串口類的實現,綜合使用了異步模式、多線程、事件等方法,并給出VC開發環境下實現串口通信類的關鍵代碼。在實際監控項目中,該串口類運行穩定可靠,實現多串口數據的高效率讀寫,具備較強的實踐價值和參考意義。
[1]闞能琪.VC++串口通信中多線程技術的應用研究[J].西華大學學報,2005,24(4):84-85.
[2]梁偉,等.Visual C++網絡編程案例實戰[M].北京:清華大學出版社,2014,10:35-54.
[3]李琳娜,等.Visual C++編程實戰寶典[M].北京:清華大學出版社,2014,9:379-388.
[4]趙永發,由大偉,楊麗,等.Visual C++開發寶典[M].北京:機械工業出版社,2012,4:510-519.