2012年4月26日 星期四

WIN32 API 通訊操作 - (2) 設定串口

(2)設定串口

在打開通訊設備控制碼後,常常需要對串口進行一些初始化配置工作。這需要通過一個DCB結構來進行。DCB結構包含了諸如串列傳輸速率、資料位元數、同位和停止位元數等資訊。在查詢或配置串口的屬性時,都要用DCB結構來作為緩衝區。
  一般用CreateFile打開串口後,可以調用GetCommState函數來獲取串口的初始配置。要修改串口的配置,應該先修改DCB結構,然後再調用SetCommState函數設置串口。
  

DCB結構包含了串口的各項參數設置,下面僅介紹幾個該結構常用的變數:
typedef struct _DCB {
………
//串列傳輸速率,指定通信設備的傳輸速率。
//這個成員可以是實際串列傳輸速率值或者下面的常量值之一:
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位停止位
                    //ONE5STOPBITS   1.5位停止位
………
} DCB;
winbase.h檔中定義了以上用到的常量。如下:
#define NOPARITY            0
#define ODDPARITY           1
#define EVENPARITY          2
#define ONESTOPBIT          0
#define ONE5STOPBITS        1
#define TWOSTOPBITS         2
#define CBR_110             110
#define CBR_300             300
#define CBR_600             600
#define CBR_1200            1200
#define CBR_2400            2400
#define CBR_4800            4800
#define CBR_9600            9600
#define CBR_14400           14400
#define CBR_19200           19200
#define CBR_38400           38400
#define CBR_56000           56000
#define CBR_57600           57600
#define CBR_115200          115200
#define CBR_128000          128000
#define CBR_256000          256000
GetCommState函數可以獲得COM口的設備控制塊,從而獲得相關參數:
BOOL GetCommState (
    HANDLE hFile, //標識通訊埠的控制碼
    LPDCB lpDCB //指向一個設備控制塊(DCB結構)的指標
);
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在完成操作以前就可能返回,但超時仍然是起作用的。在這種情況下,超時規定的是操作的完成時間,而不是ReadFile和WriteFile的返回時間。

配置串口的示例代碼:
SetupComm(hCom,1024,1024);     //輸入緩衝區和輸出緩衝區的大小都是1024

COMMTIMEOUTS TimeOuts;
//設定讀超時
TimeOuts.ReadIntervalTimeout=1000;
TimeOuts.ReadTotalTimeoutMultiplier=500;
TimeOuts.ReadTotalTimeoutConstant=5000;
//設定寫超時
TimeOuts.WriteTotalTimeoutMultiplier=500;
TimeOuts.WriteTotalTimeoutConstant=2000;
SetCommTimeouts(hCom,&TimeOuts);      //設置超時
    
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=9600;           //串列傳輸速率為9600
dcb.ByteSize=8;              //每個位元組有8位元
dcb.Parity=NOPARITY;         //無同位檢查位元
dcb.StopBits=TWOSTOPBITS;    //兩個停止位
SetCommState(hCom,&dcb);
    
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
在讀寫串口之前,還要用PurgeComm()函數清空緩衝區,該函數原型:
BOOL PurgeComm( 
    HANDLE hFile, //串口控制碼
    DWORD dwFlags ); // 需要完成的操作
參數dwFlags指定要完成的操作,可以是下列值的組合: 
PURGE_TXABORT 中斷所有寫操作並立即返回,即使寫操作還沒有完成。
PURGE_RXABORT 中斷所有讀操作並立即返回,即使讀操作還沒有完成。
PURGE_TXCLEAR 清除輸出緩衝區 PURGE_RXCLEAR 清除輸入緩衝區

1 則留言: