應用程序每次打開一個通訊端口時,必須使用COMMTIMEOUTS結構設置通訊超時。若是這個結構未被配置,端口使用由驅動程序提供的默認時間超時或是之前通訊鏈接的超時時間。若是實際使用的超時設置不一樣,而採用相同的超時處理,應用程序會出現讀寫操做永遠不能結束或是頻繁結束的現象。數組
當讀寫操做超時,操做完成,並且ReadFile和WriteFile函數沒有返回錯誤值。要肯定是否一個操做已超時,驗證明際傳輸的字節數是否少於請求的字節數。例如,若是ReadFile函數返回TRUE,但傳輸的字節數比請求的字節數要少,說明操做已超時。網絡
COMMTIMEOUTS結構是用來在SetCommTimeouts和GetCommTimeouts函數中對通訊設備的超時參數進行設置和查詢的功能。這些參數肯定了設備上ReadFile和WriteFile函數的行爲。app
COMMTIMEOUTS結構體的定義以下:ide
typedef struct _COMMTIMEOUTS { 函數
DWORD ReadIntervalTimeout; oop
DWORD ReadTotalTimeoutMultiplier; spa
DWORD ReadTotalTimeoutConstant; 線程
DWORD WriteTotalTimeoutMultiplier; 指針
DWORD WriteTotalTimeoutConstant; code
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
成員ReadIntervalTimeout指定了通訊線路上的兩個字符到達的可接受的最大時間,以毫秒爲單位。在執行ReadFile操做時,從收到的第一個字符時開始計時。若是任何兩個字符的到來時間間隔超過這一數額,ReadFile操做完成,並且任何緩衝的數據返回。零值表示超時間隔不使用。
成員ReadTotalTimeoutMultiplier指定了用於計算讀操做的總超時的乘數因子,以毫秒爲單位。對於每次讀操做,這個值乘以要讀取的字節數。
成員ReadTotalTimeoutConstant指定用於計算讀操做的總超時的常數因子,以毫秒爲單位。對於每次讀操做,該值被加上ReadTotalTimeoutMultiplier成員和所請求的字節數的乘積。若是ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant成員均爲零值,表示超時設置不用於讀操做。
成員WriteTotalTimeoutMultiplier和WriteTotalTimeoutConstant的意義分別與成員ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant相似。
設定串口的逾時參數的步驟以下:
1. 經過調用GetCommTimeouts函數或手動設置初始化COMMITEMEOUTS結構。
2. 設置ReadIntervalTimeout成員,指定傳輸的兩個字符之間在不超時的狀況下能間隔的最大毫秒數。
3. 指定讀超時乘數因子ReadTotalTimeoutMultiplier成員。對於每次讀操做,用這個數乘以預計將收到的字節數。
4. 指定讀超時常數因子ReadTotalTimeoutConstant成員。這個成員加上總字節數乘以ReadTotalTimeoutMultiplier毫秒數,結果是一個讀操做發生超時以前必須通過的毫秒數。
5. 指定寫超時乘數因子WriteTotalTimeoutMultiplier成員
6. 指定讀超時常數因子WriteTotalTimeoutConstant成員。
7. 調用SetCommTimeouts函數激活端口超時設置。
下面的例子給出瞭如何設定逾時參數:
// Retrieve the time-out parameters for all read and write operations
// on the port.
COMMTIMEOUTS CommTimeouts;
GetCommTimeouts (hPort, &CommTimeouts);
// Change the COMMTIMEOUTS structure settings.
CommTimeouts.ReadIntervalTimeout = MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier = 0;
CommTimeouts.ReadTotalTimeoutConstant = 0;
CommTimeouts.WriteTotalTimeoutMultiplier = 10;
CommTimeouts.WriteTotalTimeoutConstant = 1000;
// Set the time-out parameters for all read and write operations
// on the port.
if (!SetCommTimeouts (hPort, &CommTimeouts))
{
// Could not set the time-out parameters.
MessageBox (hMainWnd, TEXT("Unable to set the time-out parameters"),
TEXT("Error"), MB_OK);
dwError = GetLastError ();
return FALSE;
}
串口經過WriteFile函數鏈接到另外一臺設備進行數據傳輸。調用此函數以前,必須打開一個應用程序和配置串行端口。
函數WriteFile將數據寫入到一個文件。WriteFile從文件指針指示的位置向文件中寫入數據。寫操做已經完成後,文件指針根據實際寫入的字節數進行調整。
函數WriteFile的原型以下:
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);
參數hFile是要寫入的文件句柄。建立該文件句柄時,必須設置對文件的GENERIC_WRITE訪問。
參數lpBuffer指向要寫入文件數據的緩衝區。
參數nNumberOfBytesToWrite是寫入文件的字節數。零值指定一個空的寫操做。一個空寫操做不向文件寫入任何字節,但改變時間戳。 WriteFile函數不截斷也不擴展文件。若是須要截斷或擴展文件,使用SetEndOfFile函數。網絡上命名管道的寫操做長度的上限是65,535個字節。
參數lpNumberOfBytesWritten指向真正寫入到文件的字節數。在作任何工做或錯誤檢查以前,WriteFile函數將此值設置爲零。
參數lpOverlapped不被支持,值爲NULL。
函數WriteFile返回非零表示成功,零表示失敗。
由於Windows CE不支持重疊I / O時,主線程或任何線程不該該嘗試向串行端口寫入大量數據。應用程序能夠建立多個線程處理讀寫操做來模擬重疊I/O。爲了協調多個線程,應用程序須要調用WaitCommEvent函數來阻止線程,直到特定的通訊事件發生。
寫入通訊端口的步驟以下:
1. 將端口的句柄做爲hFile參數傳入到WriteFile函數。這個句柄是由CreateFile函數打開端口時返回的。
2. 向參數lpBuffer指定一個指針,指向要寫入的數據緩衝區。一般,這個數據是二進制數據或字符數組。
3. 向參數nNumberOfBytesToWrite中指定要寫入的字符數。基於Windows CE的設備,應用程序能夠將Unicode字符轉換爲ASCII字符,從而支持串口設備間的文本傳輸。整個緩衝區均可以傳遞到驅動程序。
4. 往參數lpNumberOfBytesWritten指針一個指針,用於返回實際寫入的字節數。 WriteFile函數將填充這個變量,這樣應用程序能夠判斷是否正確完成數據的傳輸。
5. 設置lpOverlapped爲NULL。
下面的例子給出瞭如何寫入串口:
DWORD dwError, dwNumBytesWritten;
WriteFile (hPort, // Port handle
&Byte, // Pointer to the data to write
1, // Number of bytes to write
&dwNumBytesWritten, // Pointer to the number of bytes written
NULL // Must be NULL for Windows CE
);
通信事件是在發生重大事故時由Windows CE發送給應用程序的通知。使用WaitCommEvent函數,應用程序能夠阻止一個線程直到一個特定的事件發生。調用SetCommMask函數能夠設置應用程序繼續處理前必需要等待的事件。當指定多個事件時,任何一個指定事件的發生都會形成WaitCommEvent函數返回。
例如,這種機制使應用程序可以監控數據到達串口的時間。經過等待一個表示數據是否到達的通訊事件,應用程序避免了一直循環調用ReadFile函數等待數據到達的串行端口。 ReadFile函數只有當有數據讀取時才被調用。
函數SetCommMask的功能是指定通信設備須要監視的事件。該函數的原型以下:
BOOL SetCommMask(
HANDLE hFile,
DWORD dwEvtMask);
參數hFile是要寫入的文件句柄。
函數dwEvtMask指定要啓用的事件。零值表示禁用全部事件。這個參數能夠是下列值的組合:如表9-7
值 |
描述 |
EV_BREAK |
檢測到輸入有中斷髮生 |
EV_CTS |
|
EV_DSR |
DSR信號發生狀態轉變 |
EV_ERR |
線狀態發生錯誤。線狀態錯誤有CE_FRAME,CE_OVERRUN,以及CE_RXPARITY。 |
EV_RING |
檢測到振鈴指示 |
EV_RLSD |
RLSD信號發生狀態轉變 |
EV_RXCHAR |
接收到字符,並存放在輸入緩衝區 |
EV_RXFLAG |
接收到事件字符,並存放在輸入緩衝區。事件字符在設備的DCB結構中指定,並使用SetCommState函數進行設置 |
EV_TXEMPTY |
輸出緩衝區中的最後一個字符被髮送 |
表9-7 事件返回值說明
函數SetCommMask返回非零表示成功,零表示失敗。
函數WaitCommEvent等待指定通訊設備上事件的發生。 WaitCommEvent監視的事件是包含在與設備句柄相關聯的事件掩碼中。函數的原型以下:
BOOL WaitCommEvent(
HANDLE hFile,
LPDWORD lpEvtMask,
LPOVERLAPPED lpOverlapped);
參數hFile是要寫入的文件句柄。
參數lpEvtMask是一個指向32位變量的長指針。這個變量接收表示發生的事件的掩碼。若是出現錯誤,該值是零;不然,它是一個或多個下列值:如表9-8所示
值 |
描述 |
EV_BREAK |
檢測到輸入有中斷髮生 |
EV_CTS |
CTS信號發生狀態轉變 |
EV_DSR |
DSR信號發生狀態轉變 |
EV_ERR |
線狀態發生錯誤。線狀態錯誤有CE_FRAME,CE_OVERRUN,以及CE_RXPARITY。 |
EV_POWER |
電源事件,這是在器件上電時產生的 |
EV_RING |
檢測到振鈴指示 |
EV_RLSD |
RLSD信號發生狀態轉變 |
EV_RXCHAR |
接收到字符,並存放在輸入緩衝區 |
EV_RXFLAG |
接收到事件字符,並存放在輸入緩衝區。事件字符在設備的DCB結構中指定,並使用SetCommState函數進行設置 |
EV_TXEMPTY |
輸出緩衝區中的最後一個字符被髮送 |
表9-8 程序狀態值說明
參數lpOverlapped被忽略,設置爲NULL。
函數WaitCommEvent返回非零表示成功,零表示失敗。
使用通訊事件的步驟以下:
1. 調用SetCommMask函數設置須要監控的事件。
2. 調用WaitCommEvent函數。當一個應用程序指定一個以上的事件,lpEvtMask參數指向一個變量。該變量用來標識致使WaitCommEvent函數返回的事件。
3. 若是WaitCommEvent函數返回有數據須要讀取的事件。使用一個循環調用ReadFile函數直到全部接收到的數據已所有被讀取。
4. 再次調用SetCommMask函數設置須要監控的事件。
下面的例子展現瞭如何使用通訊時間:
BYTE Byte;
DWORD dwBytesTransferred;
// Specify a set of events to be monitored for the port.
SetCommMask (hPort, EV_RXCHAR | EV_CTS | EV_DSR | EV_RLSD | EV_RING);
while (hPort != INVALID_HANDLE_VALUE)
{
// Wait for an event to occur for the port.
WaitCommEvent (hPort, &dwCommModemStatus, 0);
// Re-specify the set of events to be monitored for the port.
SetCommMask (hPort, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING);
if (dwCommModemStatus & EV_RXCHAR)
{
// Loop for waiting for the data.
do
{
// Read the data from the serial port.
ReadFile (hPort, &Byte, 1, &dwBytesTransferred, 0);
// Display the data read.
if (dwBytesTransferred == 1)
ProcessChar (Byte);
} while (dwBytesTransferred == 1);
}
調用CloseHandle函數來關閉串行端口,此時應用程序時完成對串口的交互。函數CloseHandle有一個參數,它是由CreateFile函數調用打開的端口返回的句柄。函數CloseHandle的原型以下:
BOOL CloseHandle(
HANDLE hObject);
函數CloseHandle返回非零表示成功,零表示失敗。
本章主要介紹了串口通訊的流程,包括了序列通訊的基礎,如何設置並監控序列通訊端口。