VC++ 的串列埠通訊 (轉)

worldblog發表於2007-12-07
VC++ 的串列埠通訊 (轉)[@more@]

  VC++ 的串列埠通訊
  代翔

  在VC++中有兩種方法可以進行串列埠通訊。一種是利用公司提供的

Microsoft Communications Control。另一種是直接用VC++訪問串列埠。下面將簡述

這兩種方法。

  一、Microsoft Communications Control

  Microsoft公司在中提供了一個串列埠通訊控制元件,用它,我們可以很簡單

的利用串列埠進行通訊。在使用它之前,應將控制元件加在應用的對話方塊上。然後再用

ClassWizard 生成相應的。現在我們可以使用它了。

  該控制元件有很多自己的屬性,你可以透過它的屬性視窗來設定,也可以用程式設定

。我推薦用程式設定,這樣更靈活。

   SetCommPort:指定使用的串列埠。

   GetCommPort:得到當前使用的串列埠。

   SetSettings:指定串列埠的引數。一般設為預設引數"9600,N,8,1"。這樣方便

與其他串列埠進行通訊。

   GetSettings:取得串列埠引數。

   SetPortOpen:開啟或關閉串列埠,當一個程式開啟串列埠時,另外的程式將無法使

用該串列埠。

   GetPortOpen:取得串列埠狀態。

   GetInBufferCount:輸入緩衝區中接受到的字元數。

   SetInPutLen:一次讀取輸入緩衝區的字元數。設定為0時,程式將讀取緩衝區的

全部字元。

   GetInPut:讀取輸入緩衝區。

   GetOutBufferCount:輸出緩衝區中待傳送的字元數。

   SetOutPut:寫入輸出緩衝區。

  一般而言,使用上述和屬性就可以進行串列埠通訊了。以下是一個範例。

#define MESSAGELENGTH 100

class CMyDialog : public CDialog
{
protected:
VARIANT InBuffer;
VARIANT OutBuffer;
CMmm m_Com;
public:
......
}

BOOL CMyDiaLog::OnInitDialog()
{
CDialog::OnInitDialog();
m_Com.SetCommPort(1);
if (!m_Com.GetPortOpen()) {
m_Com.SetSettings("57600,N,8,1");
m_Com.SetPortOpen(true);
m_Com.SetInBufferCount(0);
SetTimer(1,10,NULL);
InBuffer.bstrVal=new unsigned short[MESSAGELENGTH];
OutBuffer.bstrVal=new unsigned short[MESSAGELENGTH];
OutBuffer.vt=VT_BSTR;
}
return true;
}

void CMyDiaLog::OnTimer(UINT nvent)
{
if (m_Com.GetInBufferCount()>=MESSAGELENGTH) {
InBuffer=m_Com.GetInput();
// handle the InBuffer.
// Fill the OutBuffer.
m_Com.SetOutput(OutBuffer);
}
CDialog::OnTimer(nIDEvent);
}

 

  用該控制元件傳輸的資料是UNICODE格式。關於UNICODE和ANSI的關係和轉換請參

看MSDN。

  關於該控制元件的其他詳細資料請檢視MSDN關於COMM CONTROL部分。

 


  二、直接用VC++訪問串列埠。

  在VC++中,串列埠和可以統一的方式來簡單讀寫。這兩者幾乎沒有什麼不

同,只是在WINDOWS 9X下磁碟檔案只能做同步訪問,而串列埠只能做非同步訪問。

  CreateFile:用指定的方式開啟指定的串列埠。通常的方式為

  m_h= CreateFile( "COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );

  m_hCom為檔案控制程式碼。GENERIC_READ | GENERIC_WRITE指定可以對串列埠進行讀

寫操作。第三個引數0表示串列埠為獨佔開啟。OPEN_EXISTING表示當指定串列埠不存在

時,程式將返回失敗。 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED則表

示檔案屬性。當開啟串列埠時,必須指定 FILE_FLAG_OVERLAPPED,它表示檔案或設

備不會維護訪問指標,則在讀寫時,必須使用OVERLAPPED 結構指定訪問的檔案偏移

量。

   ReadFile:讀取串列埠資料。

   WriteFile:向串列埠寫資料。

   CloseHandle:關閉串列埠。

  COMMTIMEOUTS:COMMTIMEOUTS主要用於串列埠超時引數設定。

COMMTIMEOUTS結構如下:

typedef struct _COMMTIMEOUTS {
D ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
 

  ReadIntervalTimeout:兩字元之間最大的延時,當讀取串列埠資料時,一旦兩個字元

傳輸的時間差超過該時間,讀取函式將返回現有的資料。設定為0表示該引數不起作用

  ReadTotalTimeoutMultiplier:讀取每字元間的超時。

  ReadTotalTimeoutConstant:一次讀取串列埠資料的固定超時。所以在一次讀取串列埠

的操作中,其超時為ReadTotalTimeoutMultiplier乘以讀取的位元組數再加上

ReadTotalTimeoutConstant。將ReadIntervalTimeout設定為MAXDWORD,並將

ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant設定為0,表示讀取操作將立即

返回存放在輸入緩衝區的字元。

  WriteTotalTimeoutMultiplier:寫入每字元間的超時。

  WriteTotalTimeoutConstant:一次寫入串列埠資料的固定超時。所以在一次寫入串列埠

的操作中,其超時為WriteTotalTimeoutMultiplier乘以寫入的位元組數再加上

WriteTotalTimeoutConstant。

  SetCommTimeouts函式可以設定某裝置控制程式碼的超時引數,要得到某裝置控制程式碼的超

時引數可以用GetCommTimeouts函式。

  DCB:DCB結構主要用於串列埠引數設定。該結構太龐大,這裡就不一一講述了,

有興趣者可檢視MSDN關於DCB的描述。其中下面兩個是比較重要的屬性。

  BaudRate:串列埠的通訊速度。一般設定為9600。

  ByteSize:位元組位數。一般設定為8。

  DCB結構可以用SetCommState函式來設定,並可以用GetCommState來得到現有串

口的屬性。

  SetupComm:設定串列埠輸入、輸出緩衝區。

  OVERLAPPED:儲存串列埠非同步通訊的資訊。具體結構如下:

typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
 

  Internal,InternalHigh是保留給使用的,不需要設定。

  Offset,OffsetHigh是讀寫串列埠的偏移量,一般設定OffsetHigh為NULL,可以支援

2GB資料。

  hEvent讀寫事件,因為串列埠是非同步通訊,操作可能被其他程式堵塞,程式可以通

過檢查該時間來得知是否讀寫完畢。事件將在讀寫完成後,自動設定為有效。

  透過以上這些函式和結構,我們就可以透過串列埠進行通訊了,現在我們具體看下

面的例項:

BOOL CSerial::Open( int nPort, int nBaud )
{
if( m_bOpened ) return( TRUE );

char szPort[15];
DCB dcb;

wsprintf( szPort, "COM%d", nPort );
m_hComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );
if( m_hComDev == NULL ) return( FALSE );

memset( &m_OverlappedRead, 0, sizeof( OVERLAPPED ) );
memset( &m_OverlappedWrite, 0, sizeof( OVERLAPPED ) );

COMMTIMEOUTS CommTimeOuts;
CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 5000;
SetCommTimeouts( m_hComDev, &CommTimeOuts );

m_OverlappedRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
m_OverlappedWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

dcb.DCBlength = sizeof( DCB );
GetCommState( m_hComDev, &dcb );
dcb.BaudRate = nBaud;
dcb.ByteSize = 8;
if( !SetCommState( m_hComDev, &dcb ) ||
!SetupComm( m_hComDev, 10000, 10000 ) ||
m_OverlappedRead.hEvent == NULL ||
m_OverlappedWrite.hEvent == NULL ){
DWORD dwError = GetLastError();
if( m_OverlappedRead.hEvent != NULL ) CloseHandle( m_OverlappedRead.hEvent );
if( m_OverlappedWrite.hEvent != NULL ) CloseHandle( m_OverlappedWrite.hEvent );
CloseHandle( m_hComDev );
return FALSE;
}

m_bOpened = TRUE;

return m_bOpened;

}

int CSerial::InBufferCount( void )
{

if( !m_bOpened || m_hComDev == NULL ) return( 0 );

DWORD dwErrorFlags;
COMSTAT ComStat;

ClearCommError( m_hIDComDev, &dwErrorFlags, &ComStat );

return (int)ComStat.cbInQue;

}

DWORD CSerial::ReadData( void *buffer, DWORD dwBytesRead)
{

if( !m_bOpened || m_hComDev == NULL ) return 0;

BOOL bReadStatus;
DWORD dwErrorFlags;
COMSTAT ComStat;

ClearCommError( m_hComDev, &dwErrorFlags, &ComStat );
if( !ComStat.cbInQue ) return 0;

dwBytesRead = min(dwBytesRead,(DWORD) ComStat.cbInQue);

bReadStatus = ReadFile( m_hComDev, buffer, dwBytesRead, &dwBytesRead,

&m_OverlappedRead );
if( !bReadStatus ){
if( GetLastError() == ERROR_IO_PENDING ){
WaitForSingle( m_OverlappedRead.hEvent, 2000 );
return dwBytesRead;
}
return 0;
}

return dwBytesRead;

}

DWORD CSerial::SendData( const char *buffer, DWORD dwBytesWritten)
{

if( !m_bOpened || m_hComDev == NULL ) return( 0 );

BOOL bWriteStat;

bWriteStat = WriteFile( m_hComDev, buffer, dwBytesWritten, &dwBytesWritten,

&m_OverlappedWrite );
if( !bWriteStat){
if ( GetLastError() == ERROR_IO_PENDING ) {
WaitForSingleObject( m_OverlappedWrite.hEvent, 1000 );
return dwBytesWritten;
}
return 0;
}
return dwBytesWritten;

}

 

  上述函式基本實現串列埠的開啟,讀寫操作。本文章略去該串列埠類的說明和關閉函

數。讀者應該能將這些內容寫完。接下來,你就可以在你的程式中該串列埠類了。

關於本文有任何疑問,請與作者聯絡。

  本文參考文獻:MSDN 1999, Microsoft Corp
 

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-989451/,如需轉載,請註明出處,否則將追究法律責任。

相關文章