VC++串口通訊編程詳解

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

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

   可是不管那種操做方式,通常都會經過如下四個步驟來完成:多線程

  (1) 打開串口;app

  (2) 配置串口;異步

  (3) 讀寫串口;ide

  (4) 關閉串口;函數

   一、打開串口

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

 1 HANDLE CreateFile( 
 2 
 3         LPCTSTR lpFileName, //須要打開的串口邏輯名,如"COM3"       
 4  
 5         DWORD dwDesiredAccess,//指定串口的訪問類型,讀、寫或者是二者共存,讀:GENERIC_READ、寫:GENERIC_WRITE
 6 
 7         DWORD dwShareMode, //指定共享屬性,可是串口不能共享,該參數必須爲0
 8 
 9         LPSECURITY_ATTRIBUTES lpSecurityAttributes, //引用安全性屬性結構,缺省值爲NULL
10 
11         DWORD dwCreationDistribution, //建立標誌,對串口操做時該參數必須爲OPEN_EXISTING
12 
13         DWORD dwFlagsAndAttributes, //屬性描述,指定該串口是同步仍是異步,該值爲FILE_FLAG_OVERLAPPED,表示使用異步的I/O;該值爲0,表示同步I/O操做;
14 
15         HANDLE hTemplateFile//對串口來講,該參數爲NULL
16 
17   );    

  二、配置串口線程

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

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

1 typedef struct _DCB{
2             DWORD BaudRate,//波特率:9600、115200等等
3             DWORD fParity,//指定奇偶校驗,爲1時,容許奇偶校驗
4             BYTE ByteSize,//通訊字節位,4-8位
5             BYTE Parity,//指定奇偶校驗的方法,EVENPARITY 偶校驗、 NOPARITY 無校驗、MARKPARITY 標記校驗、ODDPARITY 奇校驗
6             BYTE StopBits,//指定中止位的位數:ONESTOPBIT、ON 5STOPBITS、TWOSTOPBITS
7 };
DCB結構

  GetCommState函數能夠獲取COM口的設備控制塊,從而獲取相關參數,原型以下

1 BOOL GetCommState( 
2     HANDLE hFile, //標識通信端口的句柄 
3     LPDCB lpDCB //指向一個設備控制塊(DCB結構)的指針
4  ); 

  SetCommState函數設置COM口的設備控制塊,原型爲:

1 BOOL SetCommState( 
2     HANDLE hFile, //標識通信端口的句柄
3     LPDCB lpDCB//指向一個設備控制塊
4  );  

  咱們除了在DCB中的設置外,程序通常還須要設置I/O緩衝區的大小和超時。

  Windows中用I/O緩衝區來暫存串口輸入和輸出的數據。若是通訊的速率較高,則應該設置較大的緩衝區。調用SetupComm函數能夠設置串行口的輸入和輸出緩衝區的大小。 原型以下:

1 BOOL SetupComm( 
2     HANDLE hFile, // 通訊設備的句柄 
3     DWORD dwInQueue, // 輸入緩衝區的大小(字節數)
4     DWORD dwOutQueue // 輸出緩衝區的大小(字節數)
5  );  

  在用ReadFile和WriteFile讀寫串行口時,須要考慮超時問題。

  超時的做用是在指定的時間內沒有讀入或發送指定數量的字符,ReadFile或WriteFile的操做仍然會結束。要查詢當前的超時設置應調用GetCommTimeouts函數,該函數會填充一個COMMTIMEOUTS結構。調SetCommTimeouts能夠用某一個COMMTIMEOUTS結構的內容來設置超時。

  讀寫串口的超時有兩種:間隔超時和總超時。間隔超時是指在接收時兩個字符之間的最大時延。總超時是指讀寫操做總共花費的最大時間。寫操做只支持總超時,而讀操做兩種超時均支持。

  用COMMTIMEOUTS結構能夠規定讀寫操做的超時,原型以下:

1 typedef struct _COMMTIMEOUTS { 
2     DWORD ReadIntervalTimeout; //讀間隔超時 
3     DWORD ReadTotalTimeoutMultiplier; //讀時間係數 
4     DWORD ReadTotalTimeoutConstant; //讀時間常量 
5     DWORD WriteTotalTimeoutMultiplier; // 寫時間係數 
6     DWORD WriteTotalTimeoutConstant; //寫時間常量 
7 } COMMTIMEOUTS,*LPCOMMTIMEOUTS;  

  COMMTIMEOUTS結構的成員都以毫秒爲單位。總超時的計算公式是:

總超時=時間係數×要求讀/寫的字符數+時間常量例如,要讀入10個字符,那麼讀操做的總超時的計算公式爲:

讀總超時=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant

能夠看出:間隔超時和總超時的設置是不相關的,這能夠方便通訊程序靈活地設置各類超時。

  若是全部寫超時參數均爲0,那麼就不使用寫超時。若是ReadIntervalTimeout爲0,那麼就不使用讀間隔超時。若是ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 都爲0,則不使用讀總超時。若是讀間隔超時被設置成MAXDWORD而且讀時間係數和讀時間常量都爲0,那麼在讀一次輸入緩衝區的內容後讀操做就當即返回,而不論是否讀入了要求的字符。
  在用重疊方式讀寫串口時,雖然ReadFile和WriteFile在完成操做之前就可能返回,但超時仍然是起做用的。在這種狀況下,超時規定的是操做的完成時間,而不是ReadFile和WriteFile的返回時間。

  配置串口的示例代碼:

 1 SetupComm(hCom,1024,1024); //輸入緩衝區和輸出緩衝區的大小都是1024
 2 COMMTIMEOUTS TimeOuts; //設定讀超時
 3 TimeOuts.ReadIntervalTimeout=1000;
 4 TimeOuts.ReadTotalTimeoutMultiplier=500;
 5 TimeOuts.ReadTotalTimeoutConstant=5000; //設定寫超時
 6 TimeOuts.WriteTotalTimeoutMultiplier=500; 
 7 TimeOuts.WriteTotalTimeoutConstant=2000; 
 8 SetCommTimeouts(hCom,&TimeOuts); //設置超時 DCB dcb; 
 9 GetCommState(hCom,&dcb); 
10 dcb.BaudRate=9600; //波特率爲9600 
11 dcb.ByteSize=8; //每一個字節有8位 
12 dcb.Parity=NOPARITY; //無奇偶校驗位 
13 dcb.StopBits=TWOSTOPBITS; //兩個中止位
14 SetCommState(hCom,&dcb);
15 PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR); //清空緩衝區

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

1 BOOL PurgeComm( 
2     HANDLE hFile, //串口句柄 
3     DWORD dwFlags // 須要完成的操做,PURGE_TXABORT 中斷全部寫操做並當即返回,即便寫操做尚未完成; PURGE_RXABORT 中斷全部讀操做並當即返回,即便讀操做尚未完成;PURGE_TXCLEAR 清除輸出緩衝區 ;PURGE_RXCLEAR 清除輸入緩衝區 
4 );  

   三、讀寫串口

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

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

  寫函數的聲明原型:

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

  ReadFile和WriteFile函數是同步仍是異步由CreateFile函數決定,若是在調用CreateFile建立句柄時指定了FILE_FLAG_OVERLAPPED標誌,那麼調用ReadFile和WriteFile對該句柄進行的操做就應該是重疊的;若是未指定重疊標誌,則讀寫操做應該是同步的。ReadFile和WriteFile函數的同步或者異步應該和CreateFile函數相一致。

  在使用ReadFile 函數進行讀操做前,應先使用ClearCommError函數清除錯誤。ClearCommError函數的原型以下:

1 BOOL ClearCommError( 
2     HANDLE hFile, // 串口句柄 
3     LPDWORD lpErrors, // 指向接收錯誤碼的變量 
4     LPCOMSTAT lpStat // 指向通信狀態緩衝區 
5 );  

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

例舉一下同步方式讀寫串口的代碼:

 1 //同步讀串口 
 2 char str[100]; 
 3 DWORD wCount;//讀取的字節數 
 4 BOOL bReadStat; bReadStat=ReadFile(hCom,str,100,&wCount,NULL); 
 5 if(!bReadStat) 
 6 { 
 7     AfxMessageBox("讀串口失敗!"); 
 8     return FALSE; 
 9 } 
10 return TRUE; 
11 
12 //同步寫串口 
13 char lpOutBuffer[100]; 
14 DWORD dwBytesWrite=100; 
15 COMSTAT ComStat; 
16 DWORD dwErrorFlags; 
17 BOOL bWriteStat; ClearCommError(hCom,&dwErrorFlags,&ComStat);
18 bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL); 
19 if(!bWriteStat) 
20 { 
21     AfxMessageBox("寫串口失敗!"); 
22 } 
23 PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);  

  四、關閉串口

       利用API函數關閉串口很是簡單,只需使用CreateFile函數返回的句柄做爲參數調用CloseHandle便可:

1 BOOL CloseHandle(
2     HANDLE hObject; //handle to object to close
3 );
相關文章
相關標籤/搜索