Windows CE下串口通信

c

作者:付林林


  網上已經有CE下串口通信的文章了。我之所以發表同樣內容的文章是因爲我的文章是一系列的,不會因爲別人寫過我就不寫了。另外我對串口通信有着自己的觀點。
  現在大多數的筆記本電腦都沒有外置串口,這不奇怪,因爲有更快更穩定的接口代替了串口。不過基於 Windows CE 的設備仍然保留着串口,而且目前看來串口的地位暫時不會動搖。目前流行的基於CE的設備很多都具有像導航、打電話等功能,而GPS、GSM/GPRS模塊都是外置串口的終端設備,你想不用串口都不行。
  上面我說了我有着自己的觀點,我的觀點就是不要把串口通信封裝成類。我不明白爲什麼有些人總要把串口封裝成類呢。把一個事物封裝成類,那這個事物就一定是不易改變的,如果每次編寫都要修改,那封裝成類就一點意義都沒有了。設想如果MFC類總要改變的話,那我們用MFC編的程序也要修改同樣次數了。如果編寫超級終端一類的程序倒是可以將串口封裝成類,因爲超級終端只管輸入命令和顯示輸出數據,不對輸出數據進行處理,那讀串口的函數就可以一直使用而不必更改。但事實上串口通信大多數用來與終端設備進行通信,需要對終端設備返回的數據進行處理。而返回的數據在什麼時間返回、數據量的大小不是確定的,非要封裝成類難度很大。
  正如CE的幫助文檔所說,串口通信是最簡單的通信之一。稍麻煩的是在讀數據方面。

一、打開串口

       hSerial = CreateFile(L"COM1:", 
               GENERIC_READ | GENERIC_WRITE,
               0, 
               NULL, 
               OPEN_EXISTING, 
               0, 
               NULL);

       if(m_hSerial == NULL)
       {
              ///L"串口打開失敗";
              return;
       }
       ///配置串口
       DCB  PortDCB;    
       PortDCB.DCBlength = sizeof(DCB); 
       // 默認串口參數
       GetCommState(hSerial, &PortDCB);
       PortDCB.BaudRate = 115200; // baud
       PortDCB.ByteSize = 8;     // Number of bits/byte, 4-8 
       PortDCB.Parity = NOPARITY; 
       PortDCB.StopBits = ONESTOPBIT;  
       if (! SetCommState(hSerial, &PortDCB))
       {
              ///L"配置串口失敗";
              return;
       }
       ////配置超時值
       COMMTIMEOUTS  CommTimeouts;
       GetCommTimeouts(m_hSerial, &CommTimeouts);
       CommTimeouts.ReadIntervalTimeout = MAXDWORD;  
       CommTimeouts.ReadTotalTimeoutMultiplier = 10;  
       CommTimeouts.ReadTotalTimeoutConstant = 10;    
       CommTimeouts.WriteTotalTimeoutMultiplier = 50;  
       CommTimeouts.WriteTotalTimeoutConstant = 100;    
       if (!SetCommTimeouts(hSerial, &CommTimeouts))
       {
              ///L"不能設置超時參數";
              return;
       }                  

  CE的串口驅動不支持重疊,這個大家都知道的。這樣的話收和發就要分開。要接收串口數據就必須創建一個線程專門用於接收數據。串口的配置不需要設置很多參數,默認的配置大部分是不需要修改的。一般改動就是波特率、位數、奇偶校檢等幾項。超時值是需要改動的。ReadIntervalTimeout是指兩個字符傳送之間的超時時間。一次寫操作的超時時間等於WriteTotalTimeoutMultiplier 乘以 要發送的字符數 加上WriteTotalTimeoutConstant。 單位是毫秒。讀操作的超時和寫類似。所以設置超時是一個關鍵。設置太小可能丟失數據。

二、關閉串口

關閉串口用關閉句柄函數。

if(hSerial != NULL)  
{
    CloseHandle(hSerial);
    hSerial = NULL;
}

三、向串口發送數據

WriteFile (hSerial,        // 句柄
           &Byte,             // 數據緩衝區地址
           nByte,             // 數據大小
           &dwNumBytes,       // 返回發送出去的字節數
           NULL               // 不支持重疊
);

  向串口發送數據一般都會成功。需要注意的是如果終端設備需要一定處理時間或者稱反應時間的話,那麼兩個寫操作之間一定要注意時間間隔不能太小。具體的時間由終端設備的反應時間和緩衝區大小有關。

四、讀取串口數據

  串口麻煩就麻煩在讀取數據上。除了考慮及時的讀取數據外,還要解決接收到的數據的處理工作。如果在讀取串口數據的線程中安置數據處理工作,那麼可能會丟失數據(終端設備發送數據但是沒收到),也有可能不會丟失(終端設備發送的數據的時間、大小都是確定的)。如果肯定接收的數據在處理工作結束後終端設備才發送數據,那麼完全可以將數據處理工作放在讀取串口的線程中。對於及時的讀取數據,下面提供了一種解決辦法:

*** 假設接收的都是字符 ***
UINT  ReadThread(LPVOID pParam) ?////接收串口數據線程
{     
       HANDLE   hPort = *(HANDLE*)pParam;
       BYTE   Byte;
       int    iCounter = 0;
       DWORD   dwBytes;
       char    ReceiveBuf[1000];  ///緩衝區的大小     
              
       SetCommMask (hPort, EV_RXCHAR);   ///只接收字符
       while (hPort != INVALID_HANDLE_VALUE) 
       {
              DWORD  dwCommStatus;
              WaitCommEvent(hPort, &dwCommStatus, 0);
              SetCommMask (hPort, EV_RXCHAR); ///重新設置要等待的信號
              //// 接收數據
              do 
              {
                     ReadFile(hPort, &Byte, 1, &dwBytes, 0);                         
                     if(dwBytes == 1)
                     {
                            ReceiveBuf[iCounter++] = Byte;
                            if(iCounter == 1000)
                            {
                                   ///L"接收緩衝區已滿";
                                   return -1;
                            }
                     }                          
              } while (dwBytes == 1);
              if(iCounter == 0)  ?////沒接到數據
              {
                     continue;
              }
              //////保存數據
              char* pTmp = new char[iCounter + 1];
              if(pTmp == NULL)
              {
                     ///L"內存不足,接收串口數據線程關閉";
                     return -1;
              }
              memcpy(pTmp, ReceiveBuf, iCounter);
              pTmp[iCounter] = NULL;   ////字符串結尾
              ////////創建新線程處理數據          
              
              ////在ProcessData函數中處理數據。別忘了delete[] pTmp;
              AfxBeginThread(ProcessData, pTmp);   
              iCounter = 0;         ////清空計數器
       } ///////end while
       return 0;
}

寫作時間:2004-08-22

  未經本文作者同意,不準擅自轉載本篇文章。聯繫作者請郵至 [email protected] 或[email protected]

MSN Messenger:[email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章