介紹
在Windows的操作系統上,將串口(通信設備)作為文件來處理,所以串口的打開、關閉、讀寫所使用的API函數與文件操作一樣。所以打開串口使用CreateFile函數,讀寫串口使用ReadFile、WirteFile函數,關閉串口使用CloseHandle函數。
CreateFile
這個函數的功能是創建或者打開一個文件或者I/O設備,通常使用的I/O形式有文件、文件流、目錄、物理磁盤、卷、終端流等。如果執行成功,則返回文件句柄,INVALID_HANDLE_VALUE表示出錯,會設置GetLastError
函數聲明定義:
WINBASEAPI
HANDLE
WINAPI
CreateFileW(
_In_ LPCWSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
參數列表:
參數 | 類型 |
---|---|
lpFileName | string,要打開的文件的名稱 |
dwDesiredAccess | Long,如果為GENERIC_READ表示允許對設備進行讀訪問;如果未GENERIC_WRITE表示允許對設備進行寫訪問(可組合使用);如果為0,表示只允許獲取與一個設備有關的信息。 |
dwShareMode | Long,零表示不共享;FILE_SHARE_READ和FILE_SHARE_WRITE表示允許對文件進行共享訪問。 |
lpSecurityAttributes | SECURITY_ATTRIBUTES,指向一個SECURITY_ATTRIBUTES結構的指針,定義了文件的安全特性 |
dwCreationDisposition | Long,下述常數之一:CREATE_NEW創建文件;如文件存在則會出錯。CREATE_ALWAYS創建文件,會改寫前一個文件。OPEN_EXISTING文件必須已經存在。OPEN_ALWAYS如文件不存在則創建它。TRUNCATE_EXISTING將現有文件縮短為零長度。 |
dwFlagsAndAttributes | Long,一個或多個下述常數:FILE_ATTRIBUTE_ARCHIVE標記已歸檔屬性;FILE_ATTRIBUTE_COMPRESSED將文件標記為已壓縮,或者標記為文件在目錄中的默認壓縮方式;FILE_ATTRIBUTE_NORMAL默認屬性; FILE_ATTRIBUTE_HIDEN隱藏文件或目錄;FILE_ATTRIBUTE_READONLY文件只讀;FILE_ATTRIBUTE_SYSTEM文件為系統文件;FILE_FLAG_WRITE_THROUGH操作系統不得延遲對文件的寫操作;FILE_FLAG_OVERLAPPED允許對文件進行重疊操作;FILE_FLAG_NO_BUFFERING禁止對文件進行緩沖處理。文件只能寫入磁盤卷的扇區塊;FILE_FLAG_RANDOM_ACCESS針對隨機訪問對文件緩沖進行優化;FILE_FLAG_SEQUENTIAL_SCAN針對連續訪問對文件緩沖的優化;FILE_FLAG_DELETE_ON_CLOSE關閉了上一次打開的句柄后,將文件刪除。特別適合臨時文件。 |
hTemplateFile | Long,如果不為零,則指定一個文件句柄。新文件將從這個文件中復制擴展屬性 |
- lpFileName:指定要打開的串口邏輯名稱,用字符串表示。如COM1、COM2,分別表示串口1和串口2.
- dwDesiredAccess:端口屬性的訪問類型
- dwShareModel指定端口的共享屬性
該參數是由那些應用程序共享文件提供。對於串口來說,是不能共享的,因此,必須設置為0,這是通信設備與文件的特殊差別。
如果當前的應用程序調用CreateFIle打開一個串口,另外一個程序已經打開了串口,此時CreateFile會返回一個錯誤代碼
然而,同一個應用程序的多個線程是可以共享CreateFile返回的端口句柄。並且根據安全屬性設置,該句柄可以打開端口的應用程序的子程序來繼承。
- lpSecurityAttributes:安全屬性,一般該參數為NULL,即該端口被設置為缺省的安全屬性。缺省安全屬性下,端口的句柄是不能繼承的。
- dwCreateDisposition:指定此端口正在被其它程序占用采取的動作,因為串口總是存在的,因此必須設置為OPEN_EXISTING,該標志告訴Windows不要創建新的端口。而是打開一個已經存在的端口。
- dwFlagsAndAttributes:描述了端口的各種屬性,對於文件來說有很多屬性,但是對於串口來說,唯一的意義是FILE_FLAG_OVERLAPPED屬性,當設置該屬性時,端口IO可以在后台運行,稱為異步IO重疊結構。
- hTemplateFIle:指定模板的文件句柄,對於串口來說,此參數必須設置為0
用CreateFile函數打開串口COM1的示例如下:
HANDLE hCom;
DWORD dwError;
hCom = ::CreateFile(L"COM1", //這是串口號
GENERIC_READ | GENERIC_WRITE, // 允許讀和寫
0, // 獨占方式
NULL,
OPEN_EXISTING, // 打開而不是創建
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //異步I/O重疊結構
nullptr);
if (hCom == INVALID_HANDLE_VALUE) //判斷端口是否被正常打開
{
dwError = ::GetLastError(); //獲取錯誤代碼
}
關閉串口
關閉串口,使用CloseHandle,函數聲明為:
BOOL CloseHandle(
HANDLE hObject
)
函數比較簡單,參數是使用CreateFile打開的端口句柄。調用這個函數可以實現串口關閉。
示例如下:
if (::CloseHandle(hCom) == 0) //調用該函數關閉串口
{
// 關閉成功
}
else
{
// 關閉失敗
}
ReadFile
從文件指針指向的位置(設備文件,通信)開始將數據讀出到一個文件中,且支持同步和異步操作,如果文件打開方式沒有指明FILE_FLAG_OVERLAPPED的話,當程序調用成功時,它將實際讀出文件的字節數保存到lpNumberOfBytesRead指明的地址空間中。FILE_FLAG_OVERLAPPED允許對文件進行重疊操作。
BOOL
WINAPI
ReadFile(
_In_ HANDLE hFile, //文件屬性
LPVOID lpBuffer, // 接收數據用的buffer
_In_ DWORD nNumberOfBytesToRead, // 要讀取的字節數
_Out_opt_ LPDWORD lpNumberOfBytesRead, // 實際讀取的字節數
_Inout_opt_ LPOVERLAPPED lpOverlapped // OVERLAPPED結構,一般設定為NULL
);
示例:
char ch[1024] = { 0 };
DWORD readLen = 0;
do {
ZeroMemory(ch, 1024);
if (::ReadFile(hCom, ch, 1024, &readLen, nullptr))
{
if (readLen > 0)
{
cout << ch << endl;
}
}
} while (true);
WriteFile
將數據寫入一個文件(設備文件,通信)。該函數比fwrite函數靈活的多。也可以將這個函數應用於通信設備、管道、套接字以及郵槽的處理。返回時,TRUE(非零)表示成功,否則返回零。會設置GetLastError。
函數聲明定義:
BOOL
WINAPI
WriteFile(
_In_ HANDLE hFile, // 文件句柄
LPCVOID lpBuffer, //要寫入的數據buffer
_In_ DWORD nNumberOfBytesToWrite, // 要寫入的數據長度
_Out_opt_ LPDWORD lpNumberOfBytesWritten, // 實際寫入的字節數
_Inout_opt_ LPOVERLAPPED lpOverlapped // OVERLAPPED結構,一般設定為NULL
);
示例代碼:
char ch[1024] = "這是測試數據";
DWORD writeLen = 0;
do {
DWORD sendLen = strlen(ch);
if (::WriteFile(hCom, ch, sendLen, &writeLen, nullptr))
{
cout << "寫入成功,字節數:" << writeLen << endl;
}
Sleep(1000);
} while (true);
GetCommState
一旦串口打開,就可以對該串口的屬性進行讀取和設置。由於串口的屬性非常復雜,通常采用讀取該串口當前狀態值,然后在此基礎上進行修改的方法。
示例:
DCB dcb = { 0 }; // 設備控制塊DCB。幾乎所有的串口屬性和狀態都存儲在該結構的成員變量中
dcb.DCBlength = sizeof(DCB);
if (::GetCommState(hCom, &dcb))
{
}
SetCommState
設置串口屬性
示例:
dcb.BaudRate = CBR_115200; //設置波特率為115200
::SetCommState(hCom, &dcb);
分配接收和發送緩存區
::SetupComm(hCom, 1024, 1024);
清空接收和發送緩沖區
在進行串口發送和接收數據操作之前,最好使用PurgeComm函數將串口發送緩沖區和接收緩沖區中的數據清空。
::PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);
GetCommTimeouts
GetCommTimeouts和SetCommTimeouts用於設置串口接收發送數據的超時參數。