2012年4月26日 星期四

WIN32 API 通訊操作 - (3) 同步讀寫串口

(3) 同步讀寫串口

我們使用ReadFile和WriteFile讀寫串口,下面是兩個函數的聲明:
BOOL ReadFile(
    HANDLE hFile,               //串口的控制碼
    LPVOID lpBuffer,            //讀入的資料存儲的位址
    DWORD nNumberOfBytesToRead, // 要讀入的資料的位元組數
    
    // 指向一個DWORD數值,該數值返回讀操作實際讀入的位元組數
    LPDWORD lpNumberOfBytesRead, 
    
    // 重疊操作時,該參數指向一個OVERLAPPED結構,同步操作時,該參數為NULL
    LPOVERLAPPED lpOverlapped
    );
 
BOOL WriteFile(
    HANDLE hFile,                //串口的控制碼
    LPCVOID lpBuffer,            // 寫入的資料存儲的位址
    DWORD nNumberOfBytesToWrite, //要寫入的資料的位元組數
    
    // 指向指向一個DWORD數值,該數值返回實際寫入的位元組數
    LPDWORD lpNumberOfBytesWritten, 
    
    // 重疊操作時,該參數指向一個OVERLAPPED結構,
    // 同步操作時,該參數為NULL。
    LPOVERLAPPED lpOverlapped
    );
在用ReadFile和WriteFile讀寫串口時,既可以同步執行,也可以重疊執行。
在同步執行時,函數直到操作完成後才返回。這意味著同步執行時執行緒會被阻塞,從而導致效率下降。

在重疊執行時,即使操作還未完成,這兩個函數也會立即返回,費時的I/O操作在後台進行。
ReadFile和WriteFile函數是同步還是非同步由CreateFile函數決定,如果在調用CreateFile創建控制碼時指定了 FILE_FLAG_OVERLAPPED標誌,那麼調用ReadFile和WriteFile對該控制碼進行的操作就應該是重疊的;如果未指定重疊標誌, 則讀寫操作應該是同步的。ReadFile和WriteFile函數的同步或者非同步應該和CreateFile函數相一致。

ReadFile函數只要在串口輸入緩衝區中讀入指定數量的字元,就算完成操作。而WriteFile函數不但要把指定數量的字元複製到輸出緩衝區,而且要等這些字元從串列口送出去後才算完成操作。

如果操作成功,這兩個函數都返回TRUE。需要注意的是,當ReadFile和WriteFile返回FALSE時,不一定就是操作失敗,執行緒應該調用GetLastError函數分析返回的結果。
例如,在重疊操作時如果操作還未完成函數就返回,那麼函數就返回FALSE,而且 GetLastError函數返回ERROR_IO_PENDING。這說明重疊操作還未完成。

同步方式讀寫串口比較簡單,下面先例舉同步方式讀寫串口的代碼:
//同步讀串口
char str[100];
DWORD wCount;           //讀取的位元組數
BOOL bReadStat;
bReadStat = ReadFile(hCom, str, 100, &wCount, NULL);
if(!bReadStat)
{
    AfxMessageBox("讀串口失敗!");
    return FALSE;
}
return TRUE;

//同步寫串口
char lpOutBuffer[100];
DWORD dwBytesWrite = 100;
COMSTAT ComStat;
DWORD dwErrorFlags;
BOOL bWriteStat;
ClearCommError(hCom, &dwErrorFlags, &ComStat);
bWriteStat = WriteFile(hCom, lpOutBuffer, dwBytesWrite,
                       &dwBytesWrite, NULL);
if(!bWriteStat)
{
    AfxMessageBox("寫串口失敗!");
}
PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | 
                PURGE_TXCLEAR | PURGE_RXCLEAR );
在重疊操作時,操作還未完成函數就返回。
重疊I/O非常靈活,它也可以實現阻塞(例如我們可以設置一定要讀取到一個資料才能進行到下一步操作)。

有兩種方法可以等待操作完成:
一種方法是用象 WaitForSingleObject這樣的等待函數來等待OVERLAPPED結構的hEvent成員;另一種方法是調用 GetOverlappedResult函數等待,後面將演示說明。

下面我們先簡單說一下OVERLAPPED結構和GetOverlappedResult函數: OVERLAPPED結構 OVERLAPPED結構包含了重疊I/O的一些資訊,定義如下:
typedef struct _OVERLAPPED { 
    DWORD  Internal; 
    DWORD  InternalHigh; 
    DWORD  Offset; 
    DWORD  OffsetHigh; 
    HANDLE hEvent; 
} OVERLAPPED;
在使用ReadFile和WriteFile重疊操作時,執行緒需要創建OVERLAPPED結構以供這兩個函數使用。執行緒通過OVERLAPPED結構獲得當前的操作狀態,該結構最重要的成員是hEvent。hEvent是讀寫事件。當串口使用非同步通訊時,函數返回時操作可能還沒有完成,程式可以通過檢查 該事件得知是否讀寫完畢。

當調用ReadFile, WriteFile 函數的時候,該成員會自動被置為無信號狀態;當重疊操作完成後,該成員變數會自動被置為有信號狀態。
以下是GetOverlappedResult函數
BOOL GetOverlappedResult(
    HANDLE hFile, // 串口的控制碼  
 
    // 指向重疊操作開始時指定的OVERLAPPED結構
    LPOVERLAPPED lpOverlapped, 
    
    // 指向一個32位元變數,該變數的值返回實際讀寫操作傳輸的位元組數。
    LPDWORD lpNumberOfBytesTransferred, 
    
    // 該參數用於指定函數是否一直等到重疊操作結束。
    // 如果該參數為TRUE,函數直到操作結束才返回。
    // 如果該參數為FALSE,函數直接返回,這時如果操作沒有完成,
    // 通過調用GetLastError()函數會返回ERROR_IO_INCOMPLETE。
    BOOL bWait  
    );
該函數返回重疊操作的結果,用來判斷非同步作業是否完成,它是通過判斷OVERLAPPED結構中的hEvent是否被置位來實現的。

1 則留言:

  1. 請教一個問題,
    我做了一個 USB HID DEVICE, HID report as follows:

    Usage Page (Vendor-Defined 1) 06 00 FF
    Usage (Vendor-Defined 2) 09 02
    Collection (Application) A1 01
    Logical Minimum (0) 15 00
    Logical Maximum (255) 26 FF 00
    Report Size (8) 75 08
    Report ID (1) 85 01
    Report Count (7) 95 07
    Usage (Vendor-Defined 2) 09 02
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
    Usage (Vendor-Defined 2) 09 02
    Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
    Report ID (2) 85 02
    Report Count (63) 95 3F
    Usage (Vendor-Defined 2) 09 02
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
    Usage (Vendor-Defined 2) 09 02
    Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
    End Collection C0
    USB HID device success send 8 bytes data to pc. (01 35 00 00 00 00 00 00)

    HidP_GetCaps(pPreData, &cap) get cap.InputReportByteLength = 64
    Part of the C++ source code:

    {
    memset(ReadReportBuffer, 0, sizeof(ReadReportBuffer));
    ReadFile(m_hReadHandle,
    ReadReportBuffer,
    64,
    NULL,
    &ReadOverlapped);
    }
    dObject = WaitForMultipleObjects(2, hArray, FALSE, INFINITE);
    if (dObject == WAIT_OBJECT_0)
    {
    if (!MyDevFound)//Device extract also set the event
    {
    continue;
    }
    GetOverlappedResult(m_hReadHandle,
    &ReadOverlapped,
    &nBytesRead,
    TRUE);//you can also set the last parameter False
    if (nBytesRead != 0)
    {
    m_strTemp = ReadReportBuffer;
    m_strLog.Format(_T("Read the Report Data Length is %d.(%02d:%02d:%02d)"),
    nBytesRead, sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
    m_strLog += m_strTemp;
    Display_Info::SendEvent(m_strLog);
    //Display Receive Data
    pDlg->DisplayDataHex(ReadReportBuffer, nBytesRead);
    }

    why nBytesRead value = 64?
    讀進來的DATA有包含USB送的8byte DATA.

    回覆刪除