国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
串口通信工程筆記

http://www.cnblogs.com/fyhui/archive/2012/05/02/2479595.html

2012

一、串口API

1.  打開串口

  使用CreateFile函數(shù)可以打開串口。通常有兩種方式可以打開,一種是同步方式(NonOverlapped),另外一種異步方式(Overlapped)。

        HANDLE hComm;

        hComm = CreateFile(   gszPort,                                                    //串口名

                                           GENERIC_READ|GENERIC_WRITE          //讀寫

                                           0,                        //注意:串口為不可共享設(shè)備,本參數(shù)須為0

                                           0,

                                           OPEN_EXISTING,

                                           FILE_FLAG_OVERLAPPED,                      //異步方式

                                           0);

        if(hComm == INVALID_HANDLE_VALUE)     //打開串口失敗處理

               ······

2.   配置串口

  DCB(Device Control Block)結(jié)構(gòu)定義了串口通信設(shè)備的控制設(shè)置,有3種方式可以初始化DCB。

  • 通過GetCommState函數(shù)得到DCB的初始值:

      DCB dcb;

               memset(&dcb, 0, sizeof(dcb));

               if(!GetCommState(hComm, &dcb))     ……        //錯誤處理

               else ……                                                         //已準(zhǔn)備就緒

  • 用BuildCommDCB函數(shù)初始化DCB結(jié)構(gòu):

      DCB dcb;

      memset(&dcb, 0, sizeof(dcb));

      dcb.DCBlength = sizeof(dcb);

      if(!BuildCommDCB(“9600,n,8,1”,  &dcb))       ……     //參數(shù)配置錯誤

      else ……                                                                //已準(zhǔn)備就緒

  • 用SetCommState函數(shù)手動設(shè)置DCB初值:

      DCB dcb;

      memset(&dcb, 0, sizeof(dcb));

      if(!GetCommState(hComm, &dcb))     return FALSE;

      dcb.BaudRate = CBR_9600;

3.  流控設(shè)備

  流控制有如下兩種設(shè)置:

  • 硬件流控制:硬件流控有兩種,DTE/DSR方式和RTS/CTS方式。這與DCB結(jié)構(gòu)的初始化有關(guān)系,建議采用標(biāo)準(zhǔn)流行的流控方式,采用硬件流控時,DTE、DSR、RTS、CTS的邏輯位直接影響到數(shù)據(jù)的讀寫及收發(fā)數(shù)據(jù)的緩沖區(qū)控制。
  • 軟件流控制:串口通信中采用特殊字符XON和XOFF作為控制串口數(shù)據(jù)的收發(fā)。

注意:在不設(shè)置流控制方式或軟件流控的情況下,基本上不會出現(xiàn)什么問題,但在硬件流控下,規(guī)范的RTS_CONTROL_HANDSHAKE流控方式的含義本來是當(dāng)緩沖區(qū)快滿的時候RTS會自動OFF通知對方暫停發(fā)送,當(dāng)緩沖區(qū)重新空出來的時候,RTS會自動ON,但很多時候當(dāng)RTS變OFF以后即使已經(jīng)清空了緩沖區(qū),RTS也不會自動的ON,造成對方停在那里不發(fā)送了。所以,如果要用硬件流控制的話,還要在接收后最好加上檢測緩沖區(qū)大小的判斷,具體做法是使用ClearCommError后返回COMSTAT.cbInQue,當(dāng)緩沖區(qū)已經(jīng)空出來的時候,要使用invoke(EscapeCommFunction,hComm,SETRTS)重新將RTS設(shè)置為ON。

4.  串口讀寫操作

  串口讀寫有兩種方式:同步方式(NonOverlapped)和異步方式(Overlapped)。同步方式指必須完成了讀寫操作,函數(shù)才返回,這可能會使程序無響應(yīng),因為如果在讀寫時發(fā)生了錯誤,永遠(yuǎn)不返回就會出錯,可能線程將停在原地。而異步方式則靈活的多,一旦讀寫不成功,就將讀寫掛起,函數(shù)直接返回,可以通過GetLastError函數(shù)得知讀寫未成功的原因,所以串口讀寫常常采用異步方式操作。

ReadFile()函數(shù)用于完成讀操作,異步方式的讀操作為:

  DWORD dwRead;

  BOOL fWaitingOnRead = FALSE;

  OVERLAPPED osReader;

  memset(&osReader, 0, sizeof(osReader));

  osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  if(osReader.hEvent == NULL)      ……       //錯誤處理

  if(!fWaitingOnRead)

  {

          if(!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader))              //讀串口

          {

                 if(GetLastError() != ERROR_IO_PENDING)     ……       //報告錯誤

                 else fWaitingOnRead = TRUE;

     }

  }

  else

  {

    //讀取完成,不必在調(diào)用GetOverlappedResults函數(shù)

    HandleASuccessfulRead(lpBuf, dwRead);

        }

 

  //如果讀操作被掛起,可以調(diào)用WaitForSingleObject()函數(shù)或

  //WaitForMuntilpleObjects()等待讀操作完成或者超時發(fā)生,

  //再調(diào)用GetOverlappedResult()得到想要的信息。

  if(fWaitingOnRead)

  {

    dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);

    switch(dwRes)

    {

    case WAIT_OBJECT_0:        //完成讀操作

           if(!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))   …… //錯誤

           else ……        //全部讀取成功

           HandleASuccessfulRead(lpBuf, dwRead);

             fWaitintOnRead = FALSE;

             break;

    case WAIT_TIMEOUT:         //操作尚未完成

           …….                           //處理其他任務(wù)

           break;

    default:

           ……              //出現(xiàn)錯誤

           break;

    }

  }

  注意上述代碼在處理多線程串口在windows系列下存在一些問題,修改完成后代碼參考1.4節(jié)。

5.  關(guān)閉串口

  程序結(jié)束或需要釋放串口資源時,必須正確關(guān)閉串口。調(diào)用CloseHandle函數(shù)關(guān)閉串口的句柄即可,

         CloseHandle(hComm);

  值得注意的是,在關(guān)閉串口前必須保證讀寫串口線程已經(jīng)退出,否則會引起誤操作,一般采用的辦法是使用事件驅(qū)動機制,啟動一事件,通知串口讀寫線程強制退出。

6. 其他問題

  串口通信中其他必須處理的問題主要有如下幾個:

  • 檢測通信事件:用SetCommMask()設(shè)置想要得到的通信事件的掩碼,再調(diào)用WaitCommEvent()檢測通信事件的發(fā)生。可設(shè)置事件標(biāo)志有EV_BREAK \ EV_VTS \ EV_DSR \ EV_ERR \ EV_RING \ EV_RLSD \ EV_RXCHAR \ EV_RXFLAG \ EV_TXEMPTY。
  • 處理通信超時:在通信中,超時是一個很重要的考慮因素,因為數(shù)據(jù)接收過程中由于某種原因突然中斷或停止,如果不采取超時控制機制,將會使得I/O線程被掛起或無限阻塞。超時設(shè)置分兩步,首先設(shè)置COMMTIMEOUTS結(jié)構(gòu)的5個變量,然后調(diào)用SetcommTimeouts()設(shè)置超時值,對于使用異步方式讀寫的操作,如果操作掛起后,異步成功完成了讀寫,WaitForSingleObject()或WaitForMultipleObjects()將返回WAIT_OBJECT_0,GetOverlappedResult()返回TRUE。其實還可以用GetCommTimeouts()得到系統(tǒng)初始值。
  • 錯誤處理和通信狀態(tài):在串口通信中,可以會產(chǎn)生很多的錯誤,使用ClearCommError()可以檢測錯誤并且清除錯誤條件。
  • WaitCommEvent()返回時,只是指出了如CTS等等狀態(tài)有變化。但要了解具體變化情況必須使用GetCommModemStatus()獲得串口線路狀態(tài)更詳細(xì)的信息。

 

二、串口操作方式

1.  同步方式

  同步(NonOverlapped)方式是比較簡單的一種方式,編寫代碼長度明顯少于異步(Overlapped)方式。同步方式中,讀串口的函數(shù)試圖在串口的接收緩沖區(qū)中讀取規(guī)定數(shù)據(jù)的數(shù)據(jù),直到規(guī)定數(shù)據(jù)的數(shù)據(jù)全部被讀出或設(shè)定超時時間已到時才返回。例如:

       COMMTIMEOUTS timeOver;

       memset(&timeOver, 0, sizeof(timeOver));

       DWORD timeMultiplier, timeConstant;

       ……

       timeOver.ReadTotalTimeoutMultiplier = timeMultiplier;

       timeOver.ReadTotalTimeoutConstant = timeConstant;

       SetCommTimeouts(hComm, &timeOver);

       ……

       ReadFile(hComm, inBuffer, nWantRead, &nRealRead, NULL); //NULL指采用同步文件讀寫

  如果所規(guī)定的待讀取數(shù)據(jù)的數(shù)目nWantRead較大且設(shè)定的超時時間較長,而接收緩沖區(qū)中數(shù)據(jù)較少,則可能引起線程阻塞。解決這一問題的方法是檢查COSTAT結(jié)構(gòu)的cbInQue成員,該成員的大小即為接收緩沖區(qū)中處于等待狀態(tài)的實際個數(shù)。如果令nWantRead的值等于COMSTAT.cbInQue,就能很好的防止線程阻塞。                       

2. 異步方式

  在異步方式中,利用Windows的多線程結(jié)構(gòu),可以讓串口的讀寫操作在后臺進行,而應(yīng)用程序的其他部分在前臺執(zhí)行。例如:

       OVERLAPPED wrOverlapped;

       COMMTIMEOUTS timeOVer;

       memset(&timeOver, 0, sizeof(timeOver));

       DWORD timeMultiplier, timeConstant;

       ……       //給timeMultiplier, timeConstant賦值

       timeOver.ReadTotalTimeoutMultiplier = timeMultiplier;

       timeOver.ReadTotalTimeoutConstant = timeConstant;

       SetCommTimeouts(hComm, &timeOver);

       wrOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

       ……

       ReadFile(hComm, inBuffer, nWantRead, &nRealRead,  &wrOverlapped);

       GetOverlappedResult(hComm, &wrOverlapped, &nRealRead,TRUE);

       ……

       ResetEvent(wrOverlapped.hEvent);

  上面代碼中的ReadFile由于采用了異步方式,所以只返回數(shù)據(jù)是否已經(jīng)開始讀入的狀態(tài),并不返回實際的讀入數(shù)據(jù),即ReadFile中的nRealRead無效。實際讀入的數(shù)據(jù)由GetOverlappedResult返回的,該函數(shù)的最后一個參數(shù)值為TRUE,表示它等待異步操作結(jié)束后才返回到應(yīng)用程序,此時,GetOverlappedResult與WaitForSingleObject函數(shù)無效。

3. 查詢方式

  即一個進程中的某一線程定時地查詢串口的接收緩沖區(qū),如果緩沖區(qū)中有數(shù)據(jù),就讀取數(shù)據(jù);若緩沖區(qū)沒有數(shù)據(jù),該線程將繼續(xù)執(zhí)行,因此會占用大量的CPU時間,它實際上是同步方式的一種派生。例如:

              COMMTIMEOUTS timeOver,

              Memset(&timeOver, 0, sizeof(timeOver));

              timeOver.ReadIntervalTimeout = MAXWORD;

              SetCommTimeouts(hComm, &timeOver);

              ……ReadFile(hComm, inBuffer, nWantRead, &nRealRead, NULL);

  除了COMMTIMEOUTS結(jié)構(gòu)的變量timeOver設(shè)置不同外,查詢方式與同步方式在程序代碼方面很類似,但二者的工作方式卻差別很大。盡管ReadFile采用的也是同步文件讀寫方式,但由于timeOver的區(qū)間超過時間設(shè)置為MAXWORD,所以ReadFile每次將讀出接收隊列中的所有處于等待狀態(tài)的數(shù)據(jù),一次最多可讀出nWantRead個字節(jié)的數(shù)據(jù)。

4.  事件驅(qū)動方式

  若對端口數(shù)據(jù)的響應(yīng)時間要求較嚴(yán)格,可采用事件驅(qū)動方式。事件驅(qū)動方式通過設(shè)置事件通知,當(dāng)所希望的事件發(fā)生時,Windows發(fā)出該事件已經(jīng)發(fā)生的通知。Windows定義了9中串口通信事件,常用的有以下3中:

  • EV_RXCHAR:接收到一個字節(jié),并放入輸入緩沖區(qū)。
  • EV_TXEMPTY:輸出緩沖區(qū)中的最后一個字符,發(fā)送出去。
  • EV_RXFLAG:接收到事件字符(DCB結(jié)構(gòu)中的EvtChar成員),放入輸入緩沖區(qū)。

  在用SetCommMask()制定了有用的事件后,應(yīng)用程序可調(diào)用WaitCommEvent()來等待事件的發(fā)生。SetCommMask可使WaitCommEvent()中止。例如:

              COMSTAT comStat;

              DWORD dwEvent;

              SetCommMask(hComm, EV_RXCHAR);

              ……

              if(WaitCommEvent(hComm, &dwEvent, NULL))

                     if((dwEvent & EV_RXCHAR) && comstat.cbInQue)

                            ReadFile(hComm, inBuffer, comstat.cbInQue, &nRealRead, NULL);

5. 總結(jié)

  一般要求情況下,查詢方式是一種最直接的讀串口的方式。但定時查詢存在一個致命的弱點,即查詢是定時發(fā)生的,可能發(fā)生的過早或過晚。在數(shù)據(jù)變化較快的情況下,特別是主控計算機的串口通過擴展板擴展多個時,需定時對所有串口輪流查詢,容易發(fā)生數(shù)據(jù)的丟失。雖然定時間隔越小,數(shù)據(jù)的實時性越高,但系統(tǒng)的資源也被占用越多。

  Windows中提出文件讀寫的異步方式,主要是針對文件IO相對較慢的速度而進行的改進,它利用了系統(tǒng)的多線程結(jié)構(gòu),雖然在Windows中沒有實現(xiàn)任何對文件IO的異步操作,但它卻能對串口進行異步操作。采用異步方式,可以提高系統(tǒng)整體性能,在對系統(tǒng)強壯性要求高的場合,建議采用這種方式。

  事件驅(qū)動方式是一種高效的串口讀方式。這種方式實時性較高,特別對擴展了多個串口的情況,并不要求像查詢方式那樣定時地對所有串口輪詢,而像中斷方式那樣,只有當(dāng)設(shè)定的事件發(fā)生時,應(yīng)用程序得到windows操作系統(tǒng)發(fā)出的消息后,才進行相應(yīng)處理,以免數(shù)據(jù)丟失。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C++串口編程實例
用VC 6.0實現(xiàn)串行通信的三種方法
Win32串口編程
API串口通信
Windows API串口編程參考
多線程異步串口通信編程
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服