Win32串口編程-基本知識

來源:互聯網
上載者:User
一、基本知識    Win32下串口通訊與16位串口通訊有很大的區別。在Win32下,可以使用兩種編程方式實現串口通訊,其一是調用的Windows的API函數,其二是使用ActiveX控制項。使用API 呼叫,可以清楚地掌握串口通訊的機制,熟悉各種配置和自由靈活採用不同的流控進行串口通訊。下面介紹串口操作的基本知識。   開啟串口:使用CreateFile()函數,可以開啟串口。有兩種方法可以開啟串口,一種是同步方式(NonOverlapped),另外一種非同步方式(Overlapped)。使用Overlapped開啟時,適當的方法是: HANDLE hComm; hComm = CreateFile( gszPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (hComm == INVALID_HANDLE_VALUE) // error opening port; abort   配置串口:   1.DCB配置    DCB(Device Control Block)結構定義了串口通訊裝置的控制設定。許多重要設定都是在DCB結構中設定的,有三種方式可以初始化DCB。   (1)通過GetCommState()函數得DCB的初始值,其使用方式為: DCB dcb = {0}; if (!GetCommState(hComm, &dcb)) // Error getting current DCB settings else // DCB is ready for use.   (2)用BuildCommDCB()函數初始化DCB結構,該函數填充 DCB的傳輸速率、同位類型、資料位元、停止位。對於流控成員函數設定了預設值。其用法是: DCB dcb; FillMemory(&dcb, sizeof(dcb), 0); dcb.DCBlength = sizeof(dcb); if (!BuildCommDCB(“9600,n,8,1", &dcb)) { // Couldn't build the DCB. Usually a problem // with the communications specification string. return FALSE; } else // DCB is ready for use.   (3)用SetCommState()函數手動設定DCB初值。用法如下: DCB dcb; FillMemory(&dcb, sizeof(dcb), 0); if (!GetCommState(hComm, &dcb)) // get current DCB // Error in GetCommState return FALSE; // Update DCB rate. dcb.BaudRate = CBR_9600 ; // Set new state. if (!SetCommState(hComm, &dcb)) // Error in SetCommState. Possibly a problem with the communications // port handle or a problem with the DCB structure itself.   手動設定DCB值時,DCB的結構的各成員的含義,可以參看MSDN協助。    2.流控設定   硬體流控:串口通訊中的硬體流控有兩種,DTE/DSR方式和RTS/CTS方式,這與DCB結構的初始化有關係,DCB結構中的OutxCtsFlow、 fOutxDsrFlow、fDsrSensitivity、fRtsControl、fDtrControl幾個成員的初始值很關鍵,不同的值代表不同流控,也可以自己設定流控,但建議採用標準流行的流控方式。採用硬體流控時,DTE、DSR、RTS、CTS的邏輯位直接影響到資料的讀寫及收發資料的緩衝區控制。    軟體流控:串口通訊中採用特殊字元XON和XOFF作為控制串口資料的收發。與此相關的DCB成員是:fOut、fInX、XoffChar、XonChar、 XoffLim和XonLim。具體含義參見MSDN協助。    串口讀寫操作:串口讀寫有兩種方式:同步方式(NonOverlapped)和非同步方式(Overlapped)。同步方式是指必須完成了讀寫操作,函數才返回,這可能造成程式死掉,因為如果在讀寫時發生了錯誤,永遠不返回就會出錯,可能線程將永遠等待在那兒。而非同步方式則靈活得多,一旦讀寫不成功,就將讀寫掛起,函數直接返回,可以通過GetLastError函數得知讀寫未成功的原因,所以常常採用非同步方式操作。    讀操作:ReadFile()函數用於完成讀操作。非同步方式的讀操作為: DWORD dwRead; BOOL fWaitingOnRead = FALSE; OVERLAPPED osReader = {0}; // Create the overlapped event. Must be closed before exiting // to avoid a handle leak. osReader.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); if (osReader.hEvent == NULL) // Error creating overlapped event; abort. if (!fWaitingOnRead) { // Issue read operation. if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) { if (GetLastError() != ERROR_IO_PENDING) // read not delayed? // Error in communications; report it. else fWaitingOnRead = TRUE; } else { // read completed immediately HandleASuccessfulRead(lpBuf, dwRead); } }    如果讀操作被掛起,可以調用WaitForSingleObject()函數或WaitForMuntilpleObjects()函數等待讀操作完成或者逾時發生,再調用 GetOverlappedResult()得到想要的資訊。    寫操作:與讀操作相似,故不詳述,調用的API函數是: WriteFile函數。    串口狀態:   (1)通訊事件:用SetCommMask()函數設定想要得到的通訊事件的掩碼,再調用WaitCommEvent()函數檢測通訊事件的發生。可設定的通訊事件標誌(即SetCommMask()函數所設定的掩碼)可以有EV_BREAK、EV_CTS、EV_DSR、 EV_ERR、EV_RING、EV_RLSD、EV_RXCHAR、EV_RXFLAG、EV_TXEMPTY。    注意:1對於EV_RING標誌的設定,WIN95是不會返回EV_RING事件的,因為WIN95不檢測該事件。2設定EV_RXCHAR,可以檢測到字元到達,但是在綁定此事件和ReadFile()函數一起讀取串口接收資料時,可能會出現錯誤,造成少讀位元組數,具體原因查看MSDN協助。可以採用迴圈讀的辦法,另外一個比較好的解決辦法是調用ClearCommError()函數,確定在一次讀操作中在緩衝區中等待被讀的位元組數。   (2)錯誤處理和通訊狀態:在串口通訊中,可能會產生很多的錯誤,使用ClearCommError()函數可以檢測錯誤並且清除錯誤條件。    (3)Modem狀態:用SetcommMask()可以包含很多事件標誌,但是這些事件標誌只指示在串口線路上的電壓變化情況。而調用 GetCommModemStatus()函數可以獲得線路上真正的電壓狀態。    擴充函數:如果應用程式想用自己的流控,可以使用 EscapeCommFunction()函數設定DTR和RTS線路的電平。    通訊逾時:在通訊中,逾時是個很重要的考慮因素,因為如果在資料接收過程中由於某種原因突然中斷或停止,如果不採取逾時控制機制,將會使得I/O線程被掛起或無限阻塞。串口通訊中的逾時設定分為兩步,首先設定 COMMTIMEOUTS結構的五個變數,然後調用SetcommTimeouts()設定逾時值。對於使用非同步方式讀寫的操作,如果操作掛起後,非同步成功完成了讀寫,WaitForSingleObject()或 WaitForMultipleObjects()函數將返回WAIT_OBJECT_0,GetOverlappedResult()返回TRUE。其實還可以用GetCommTimeouts()得到系統初始值。    關閉串口:程式結束或需要釋放串口資源時,應該正確關閉串口,關閉串口比較簡單,使用API調用CloseHandle()關閉串口的控制代碼就可以了。   調用方法為:CloseHandle(hComm);    但是值得注意的是在關閉串口之前必須保證讀寫串口線程已經退出,否則會引起誤操作,一般採用的辦法是使用事件驅動機制,啟動一事件,通知串口讀寫線程強制退出,線上程退出之前,通知主線程可以關閉串口。二、實現  1.程式設計思路    對於不同的應用程式,雖然介面不同,但是如果採用串口與主機之間的通訊,對串口的處理方式大致相似,無非就是通過串口收發資料,對於通過串口接收到的資料,交給上層軟體處理顯示,對於上層要發給串口的資料,進行轉寄。但在實際編程中,由於採用的通訊方式和流控不同,串口設定也不同,這就涉及到 DCB的初始化問題和讀寫串口等細節問題。串口通訊應用程式設計的總體思路(即操作過程)是:首先,確定要開啟的串口名、傳輸速率、同位方式、資料位元、停止位,傳遞給CreateFile()函數開啟特定串口;其次,為了保護系統對串口的初始設定,調用 GetCommTimeouts()得到串口的原始逾時設定;然後,初始化DCB對象,調用SetCommState() 設定DCB,調用SetCommTimeouts()設定串口逾時控制;再次,調用SetupComm()設定串口接收發送資料的緩衝區大小,串口的設定就基本完成,之後就可以啟動讀寫線程了。   一般來說,串口的讀寫由串口讀寫線程完成,這樣可以避免讀寫阻塞時主程式死結。對於全雙工系統的串口讀寫,應該分別開啟讀線程和寫線程;對於半雙工和單工的,建議只需開啟一個線程即可。線上程中,按照預定好的通訊握手方式,正確檢測串口狀態,讀取發送串口資料。   2.實現細節   在半雙工的情況下,首先完成必要的串口配置,成功開啟串口、DCB設定、逾時設定;然後開啟線程,如: CwinThread hSerialThread = (CWinThread*) AfxBeginThread(SerialOperation,hWnd,THREAD_PRIORITY_NORMAL); 其中開啟之線程為SerialOperation,優先順序為普通。    全雙工系統情況下的串口編程,與單工差不多,區別僅僅在於啟動雙線程,分別為讀線程和寫線程,讀線程根據不同的事件或訊息,通過不斷查詢串口所收到的有效資料,完成讀操作;寫線程通過接收主線程的發送資料事件和要發送的資料,向串口發送。     
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.