天天看點

使用Win32建立序列槽通訊程式

使用Win32建立序列槽通訊程式

作者:konchat

翻譯:PowerCPP

使用Win32建立序列槽通訊程式

介紹:

本文的目的是介紹如何在Win32中處理序列槽。序列槽通訊可以通過多種技術實作,例如:ActiveX,I/O存取和檔案操作。本文介紹Win32平台下通過檔案操作技術使用序列槽。程式員可以使用 Microsoft Visual C++ Version 6.0所提供的kernel32.lib。在Microsoft Windows(2000,Me,XP and 95/98)中,序列槽作為檔案處理。是以可以通過Windows檔案建立函數打開序列槽。

文章不僅介紹了序列槽通訊,也介紹了在序列槽通訊應用程式中實作多任務,多任務可以使序列槽通訊應用程式在同一時間處理更多的任務,例如:讀資料任務,發送資料任務,GUI任務等。

以下主題描述了Win32中基本的序列槽操作:

初始化/打開序列槽通訊

  • 建立端口句柄
  • 擷取配置(DCB)
  • 修改配置
  • 儲存配置
  • 設定通訊逾時

接收/發送資料

  • 發送資料
  • 接收資料
  • 關閉序列槽

設計步驟:

初始化/打開序列槽

打開序列槽的第一步是初始化或設定序列槽配置,目的是建立序列槽代理,整篇文章我們都将用檔案句柄作為序列槽代理。

建立端口句柄

序列槽句柄是可以被用來存取的序列槽對象句柄,建立序列槽句柄的函數是CreateFile,如下代碼所示:

handlePort_ = CreateFile(portName,  // 端口裝置: 預設 "COM1"
GENERIC_READ | GENERIC_WRITE,       // 裝置打開模式: 允許讀寫
0,                                  // 不共享
NULL,                               // 預設安全設定
OPEN_EXISTING,                      // 打開方式:打開已經存在的端口
0,                                  // 預設
NULL);                              // 預設      

如圖2所示,portName = "COM1": portName 示一個const char*變量,它指定想建立序列槽句柄的端口名稱。

使用Win32建立序列槽通訊程式

圖2:CreateFile函數

擷取配置

在控制裝置中擷取目前配置,配置中包含了用于設定序列槽通訊裝置的參數。

可以用 GetCommState函數得到目前裝置配置并用指定通訊裝置的目前配置填充裝置控制塊(DCB結構),如下代碼所示:

// 擷取序列槽目前配置
if (GetCommState(handlePort_,&config_) == 0)
{
      AfxMessageBox("Get configuration port has problem.");
      return FALSE;
}      

修改配置

當你已經在DCB結構中擷取序列槽配置,你應該修改其中的參數,如下代碼所示:

// 指定使用者參數
config_.BaudRate = dcb.BaudRate;  // 波特率
config_.StopBits = dcb.StopBits;  // 停止位
config_.Parity = dcb.Parity;      // 奇偶校驗
config_.ByteSize = dcb.ByteSize;  // 資料位      
  • DWORD BaudRate :

    波特率 (預設 = 9600)

  • BYTE StopBits :

    0,1,2 = 1, 1.5, 2 (預設 = 0)

  • BYTE Parity :

    0-4= 無, 奇, 偶, 标志, 空格 (預設 = 0)

  • BYTE ByteSize :

    資料位, 4-8 (預設 = 8)

對于典型的通訊,建議程式員使用預設值。圖3所示,Watch對話框顯示了典型通訊使用的預設值。

使用Win32建立序列槽通訊程式

圖3:序列槽配置

儲存配置

下一步是将已經修改的配置儲存到裝置控制中。調用SetCommState API函數儲存配置。SetCommState函數裝置控制塊(DCB結構)配置通訊裝置。該函數重新初始化所有的硬體控制設定,但不清空輸入輸出隊列。代碼如下所示:

if (SetCommState(handlePort_,&config_) == 0)
{
  AfxMessageBox("Set configuration port has problem.");
  return FALSE;
}      

設定通訊逾時

開啟端口的最後一步是通過使用COMMTIMEOUTS資料結構和調用SetCommTimeouts函數進行通訊逾時設定。如下代碼所示:

// COMMTIMEOUTS對象
COMMTIMEOUTS comTimeOut;
   
// 接收時,兩字元間最大的時延
comTimeOut.ReadIntervalTimeout = 3;

// 讀取每位元組的逾時
comTimeOut.ReadTotalTimeoutMultiplier = 3;

// 讀序列槽資料的固定逾時
// 總逾時 = ReadTotalTimeoutMultiplier * 位元組數 + ReadTotalTimeoutConstant
comTimeOut.ReadTotalTimeoutConstant = 2;

// 寫每位元組的逾時
comTimeOut.WriteTotalTimeoutMultiplier = 3;

// 寫序列槽資料的固定逾時
comTimeOut.WriteTotalTimeoutConstant = 2;

// 将逾時參數寫入裝置控制
SetCommTimeouts(handlePort_,&comTimeOut);      

ReadIntervalTimeout

指定通訊線上兩個字元到達的最大時延,以毫秒為機關。在ReadFile操作期間,時間周期從第一個字元接收到算起。如果收到的兩個字元之間的間隔超過該值,ReadFile操作完畢并傳回所有緩沖資料。如果ReadIntervalTimeout為0,則該值不起作用。

如果值為MAXDWORD, 并且ReadTotalTimeoutConstant和ReadTotalTimeoutMultiplier兩個值都為0, 則指定讀操作攜帶已經收到的字元立即傳回,即使沒有收到任何字元。

ReadTotalTimeoutMultiplier

指定以毫秒為機關的累積值。用于計算讀操作時的逾時總數。對于每次讀操作,該值與所要讀的位元組數相乘。

ReadTotalTimeoutConstant

指定以毫秒為機關的常數。用于計算讀操作時的逾時總數。對于每次讀操作,ReadTotalTimeoutMultiplier與所要讀的位元組數相乘後與該值相加。

如果ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都為0,則在讀操作時忽略總逾時數。

WriteTotalTimeoutMultiplier

指定以毫秒為機關的累積值。用于計算寫操作時的逾時總數。對于每次寫操作,該值與所要寫的位元組數相乘。

WriteTotalTimeoutConstant

指定以毫秒為機關的常數。用于計算寫操作時的逾時總數。對于每次寫操作, WriteTotalTimeoutMultiplier與所要寫的位元組數相乘後與該值相加。

如果 WriteTotalTimeoutMultiplier 和 WriteTotalTimeoutConstant都為0,則在寫操作時忽略總逾時數。

提示:使用者設定通訊逾時後,如沒有出錯,序列槽已經被打開。

發送資料

序列槽資料發送多作為寫檔案處理的,程式員可以應用檔案操作函數發送資料到序列槽。采用WriteFile函數發送資料到序列槽。

if (WriteFile(handlePort_,     // 檔案句柄
      outputData,              // 資料緩沖區指針
      sizeBuffer,              // 位元組數
&length,NULL) == 0)          // 接收成功發送資料長度的指針
{
      AfxMessageBox("Reading of serial communication has problem.");
      return FALSE;
}      

提示:如果函數成功,傳回非0值

接收資料

序列槽資料接收多作為讀檔案處理。程式員可以應用檔案操作函數從序列槽接收資料。用ReadFile函數接收序列槽的資料。

if (ReadFile(handlePort_,    // 句柄
    inputData,               // 資料緩沖區指針
    sizeBuffer,              // 位元組數
    &length,                 // 指向已經讀入的位元組數
    NULL) == 0)              // 重疊I/O結構體
{
    AfxMessageBox("Reading of serial communication has problem.");
    return FALSE;
}      

提示:如果函數成功,傳回非0值

關閉序列槽

可以調用CloseHandle API函數關閉序列槽

if(CloseHandle(handlePort_) == 0)    // 調用該函數關閉序列槽
{
      AfxMessageBox("Port Closeing isn''t successed.");
      return FALSE;
}      

提示:如果函數成功,傳回非0值