串口通訊編程

  在Win32下,可使用兩種編程方式實現串口通訊,其一是使用ActiveX控件,這種方法程序簡單,但欠靈活。其二是調用Windows的API函數,這種方法能夠清楚地掌握串口通訊的機制,而且自由靈活。本文只介紹API串口通訊部分。編程

  串口的操做能夠有兩種操做方式:同步操做方式和重疊操做方式(又稱爲異步操做方式)。同步操做時,API函數會阻塞直到操做完成之後才能返回(在多線程方式中,雖然不會阻塞主線程,可是仍然會阻塞監聽線程);而重疊操做方式,API函數會當即返回,操做在後臺進行,避免線程的阻塞。

       不管那種操做方式,通常都經過四個步驟來完成:安全

       (1) 打開串口
       (2) 配置串口
       (3) 讀寫串口
       (4) 關閉串口多線程

  

  本文只介紹同步方式。app

 

  一、打開串口

  Win32系統把文件的概念進行了擴展。不管是文件、通訊設備、命名管道、郵件槽、磁盤、仍是控制檯,都是用API函數CreateFile來打開或建立的。該函數的原型爲:  異步

HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDistribution, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); 

  

   pFileName:將要打開的串口邏輯名,如「COM1」; 
       dwDesiredAccess:指定串口訪問的類型,能夠是讀取、寫入或兩者並列; 
       dwShareMode:指定共享屬性,因爲串口不能共享,該參數必須置爲0; 
       lpSecurityAttributes:引用安全性屬性結構,缺省值爲NULL; 
       dwCreationDistribution:建立標誌,對串口操做該參數必須置爲OPEN_EXISTING; 
       dwFlagsAndAttributes:屬性描述,用於指定該串口是否進行異步操做,該值爲FILE_FLAG_OVERLAPPED,表示使用異步的I/O;該值爲0,表示同步I/O操做; 
       hTemplateFile:對串口而言該參數必須置爲NULL。函數

       同步I/O方式打開串口的示例代碼:  spa

  

HANDLE hCom; //全局變量,串口句柄   
hCom=CreateFile("COM1",//COM1口  
 GENERIC_READ|GENERIC_WRITE, //容許讀和寫  
 0, //獨佔方式  
 NULL,  
 OPEN_EXISTING, //打開而不是建立  
 0, //同步方式  
 NULL);   
if(hCom==(HANDLE)-1)   
{  
   AfxMessageBox("打開COM失敗!");  
   return FALSE;   
}  
return TRUE;   

 

  二、配置串口線程

   在打開通信設備句柄後,經常須要對串口進行一些初始化配置工做。這須要經過一個DCB結構來進行。DCB結構包含了諸如波特率、數據位數、奇偶校驗和中止位數等信息。在查詢或配置串口的屬性時,都要用DCB結構來做爲緩衝區。指針

       通常用CreateFile打開串口後,能夠調用GetCommState函數來獲取串口的初始配置。要修改串口的配置,應該先修改DCB結構,而後再調用SetCommState函數設置串口。
  DCB結構包含了串口的各項參數設置,下面僅介紹幾個該結構經常使用的變量:

  typedef struct _DCB{code

   DWORD BaudRate;//波特率,指定通訊設備的傳輸速率。這個成員能夠是實際波特率值或者下面的常量值之一:

  //CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200, CBR_38400, CBR_56000, CBR_57600,   //CBR_115200, CBR_128000, CBR_256000, CBR_14400

  DWORD fParity; // 指定奇偶校驗使能。若此成員爲1,容許奇偶校驗檢查

  BYTE ByteSize; // 通訊字節位數,4—8

  BYTE Parity; //指定奇偶校驗方法。此成員能夠有下列值: EVENPARITY 偶校驗 NOPARITY 無校驗 MARKPARITY 標記校驗 ODDPARITY 奇校驗

  BYTE StopBits; //指定中止位的位數。此成員能夠有下列值: ONESTOPBIT 1位中止位 TWOSTOPBITS 2位中止位 ON 5STOPBITS   1.5位中止位

  ……… 

  };

  GetCommState函數能夠得到COM口的設備控制塊,從而得到相關參數: 

  BOOL GetCommState( HANDLE hFile, LPDCB lpDCB); 

  SetCommState函數設置COM口的設備控制塊: 

      BOOL SetCommState( HANDLE hFile, LPDCB lpDCB ); 

 

  除了在BCD中的設置外,程序通常還須要設置I/O緩衝區的大小和超時。Windows用I/O緩衝區來暫存串口輸入和輸出的數據。若是通訊的速率較高,則應該設置較大的緩衝區。調用SetupComm函數能夠設置串行口的輸入和輸出緩衝區的大小。

  BOOL SetupComm( HANDLE hFile, // 通訊設備的句柄 

            DWORD dwInQueue, // 輸入緩衝區的大小(字節數) 

            DWORD dwOutQueue // 輸出緩衝區的大小(字節數)

   ); 

  在用ReadFile和WriteFile讀寫串行口時,須要考慮超時問題。超時的做用是在指定的時間內沒有讀入或發送指定數量的字符,ReadFile或WriteFile的操做仍然會結束。
要查詢當前的超時設置應調用GetCommTimeouts函數,該函數會填充一個COMMTIMEOUTS結構。調用SetCommTimeouts能夠用某一個COMMTIMEOUTS結構的內容來設置超時。
  讀寫串口的超時有兩種:間隔超時和總超時。間隔超時是指在接收時兩個字符之間的最大時延。總超時是指讀寫操做總共花費的最大時間。寫操做只支持總超時,而讀操做兩種超時均支持。用COMMTIMEOUTS結構能夠規定讀寫操做的超時。
  COMMTIMEOUTS結構的定義爲: 

typedef struct _COMMTIMEOUTS { 

         DWORD ReadIntervalTimeout; //讀間隔超時

         DWORD ReadTotalTimeoutMultiplier; //讀時間係數

         DWORD ReadTotalTimeoutConstant; //讀時間常量

         DWORD WriteTotalTimeoutMultiplier; // 寫時間係數

         DWORD WriteTotalTimeoutConstant; //寫時間常量

} COMMTIMEOUTS,*LPCOMMTIMEOUTS; 
COMMTIMEOUTS結構的成員都以毫秒爲單位。

  總超時的計算公式是:總超時=時間係數×要求讀/寫的字符數+時間常量
  例如,要讀入10個字符,那麼讀操做的總超時的計算公式爲:
  讀總超時=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant
  能夠看出:間隔超時和總超時的設置是不相關的,這能夠方便通訊程序靈活地設置各類超時。

  若是全部寫超時參數均爲0,那麼就不使用寫超時。若是ReadIntervalTimeout爲0,那麼就不使用讀間隔超時。若是ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 都爲0,則不使用讀總超時。若是讀間隔超時被設置成MAXDWORD而且讀時間係數和讀時間常量都爲0,那麼在讀一次輸入緩衝區的內容後讀操做就當即返回,而不論是否讀入了要求的字符。

  三、讀寫串口

    使用ReadFile和WriteFile讀寫串口,下面是兩個函數的聲明:

  

BOOL ReadFile( HANDLE hFile, //串口的句柄 
// 讀入的數據存儲的地址, 
//即讀入的數據將存儲在以該指針的值爲首地址的一片內存區
LPVOID lpBuffer, 
// 要讀入的數據的字節數 
DWORD nNumberOfBytesToRead,
 // 指向一個DWORD數值,該數值返回讀操做實際讀入的字節數
LPDWORD lpNumberOfBytesRead, 
// 重疊操做時,該參數指向一個OVERLAPPED結構,同步操做時,該參數爲NULL。
LPOVERLAPPED lpOverlapped );

BOOL WriteFile( HANDLE hFile, //串口的句柄 
// 寫入的數據存儲的地址, 即以該指針的值爲首地址的
LPCVOID lpBuffer,
//要寫入的數據的字節數
DWORD nNumberOfBytesToWrite,
// 指向指向一個DWORD數值,該數值返回實際寫入的字節數
LPDWORD lpNumberOfBytesWritten,  
// 重疊操做時,該參數指向一個OVERLAPPED結構,
// 同步操做時,該參數爲NULL。
LPOVERLAPPED lpOverlapped ); 

  在用ReadFile和WriteFile讀寫串口時,既能夠同步執行,也能夠重疊執行。在同步執行時,函數直到操做完成後才返回。這意味着同步執行時線程會被阻塞,從而致使效率降低。在重疊執行時,即便操做還未完成,這兩個函數也會當即返回,費時的I/O操做在後臺進行。
  ReadFile和WriteFile函數是同步仍是異步由CreateFile函數決定,若是在調用CreateFile建立句柄時指定了FILE_FLAG_OVERLAPPED標誌,那麼調用ReadFile和WriteFile對該句柄進行的操做就應該是重疊的;若是未指定重疊標誌,則讀寫操做應該是同步的。ReadFile和WriteFile函數的同步或者異步應該和CreateFile函數相一致。
  ReadFile函數只要在串口輸入緩衝區中讀入指定數量的字符,就算完成操做。而WriteFile函數不但要把指定數量的字符拷入到輸出緩衝區,並且要等這些字符從串行口送出去後纔算完成操做。
  若是操做成功,這兩個函數都返回TRUE。須要注意的是,當ReadFile和WriteFile返回FALSE時,不必定就是操做失敗,線程應該調用GetLastError函數分析返回的結果。例如,在重疊操做時若是操做還未完成函數就返回,那麼函數就返回FALSE,並且GetLastError函數返回ERROR_IO_PENDING。這說明重疊操做還未完成。

  

  在讀寫串口以前,還要用PurgeComm()函數清空緩衝區,該函數原型: 

    BOOL PurgeComm( HANDLE hFile, //串口句柄 

                DWORD dwFlags // 須要完成的操做 ); 
  參數dwFlags指定要完成的操做,能夠是下列值的組合: 
    PURGE_TXABORT 中斷全部寫操做並當即返回,即便寫操做尚未完成。 

    PURGE_RXABORT 中斷全部讀操做並當即返回,即便讀操做尚未完成。 

    PURGE_TXCLEAR 清除輸出緩衝區   

    PURGE_RXCLEAR 清除輸入緩衝區 

  在使用ReadFile 函數進行讀操做前,應先使用ClearCommError函數清除錯誤。

  ClearCommError函數的原型以下: 
  BOOL ClearCommError( HANDLE hFile, // 串口句柄 

  LPDWORD lpErrors, // 指向接收錯誤碼的變量 

  LPCOMSTAT lpStat // 指向通信狀態緩衝區 ); 
  該函數得到通訊錯誤並報告串口的當前狀態,同時,該函數清除串口的錯誤標誌以便繼續輸入、輸出操做。
  參數lpStat指向一個COMSTAT結構,該結構返回串口狀態信息。 

  

  四、關閉串口

       利用API函數關閉串口很是簡單,只需使用CreateFile函數返回的句柄做爲參數調用CloseHandle便可:  BOOL CloseHandle(      HANDLE hObject; //handle to object to close  );

相關文章
相關標籤/搜索