一個簡單得異步串口類

/*
一個簡單異步串口類(WIN32),這個是我自己一直使用得串口類

這個異步類只實現了一些簡單功能。在使用時候需要注意下面幾點:
1。串口得相關參數需要在open之前設置好,打開之後,就不能設置了
2。讀取串口數據採取查詢方式
3。向串口發送數據函數得執行需要一定得時間,知道成功它才返回。
4。默認是沒有硬件握手得,如果要握手,需要調用函數setflowctrl
5。串口波特率直接傳遞數值就可以,比如9600。
如果有問題,請與我聯繫。Email:[email protected]
CSDN上帳號:stevecrisewu(月亮騎士)

說明:
1。發送數據用write函數,直到成功才返回(如果沒有其他錯誤的話)
2。讀取數據用read函數,能夠立刻返回,需要你定時去調用。當然,你也可以用消息通知的辦法,你可以創建一個線程來給你的應用程序發送消息。
3。獲取線控狀態函數用getlstatus函數,如下示就可以返回DSR,CTS,RI和CD的線的狀態。
      value=getlstatus();
     DSR=value&S_DSR;
     CTS=value&S_CTS;
     RI=value&S_RI;
     CD=value&S_CD;
*/
#include <windows.h>

/* MODEM CONTROL setting */
#define C_DTR  0x01
#define C_RTS  0x02

/* MODEM LINE STATUS */
#define S_CTS  0x01
#define S_DSR  0x02
#define S_RI  0x04
#define S_CD  0x08

#define SIO_OK  0
#define SIO_ERROR -1 

class CAsynComm 
{
public:
 //構造函數
 CAsynComm();
 //析構函數
 virtual ~CAsynComm();
protected:
 //串口號
 volatile int nPort; 
 //串口句柄
 volatile HANDLE ComHandle;
 //DCB結構
 DCB MyDcb;  
 //輸入輸出緩衝區大小
 int InbufSize, OutbufSize;
 //超時參數
 COMMTIMEOUTS coTimeOut;
 //OVERLAPPED結構
 OVERLAPPED ro,wo;
 //初始化函數
 void Init();
 //判斷串口是否打開函數
 bool IsOpen(); 
public:
 //從串口讀取一個字符,返回SIO_ERROR表示失敗,否則爲0-255之間的一個數
 int getch();
 //向串口寫一個字符,返回SIO_ERROR表示失敗,SIO_OK表示成功寫入
 int putch(char ch);
 //從串口讀取len個字符並放到緩衝區buf中,返回SIO_ERROR表示失敗,否則返回實際讀取的字符數
 int read(char *buf, int len);
 //向串口寫入len個字符,返回SIO_ERROR表示失敗,否則返回實際寫入串口的字符數
 int write(char *buf, int len);    
 //關閉串口
 int close();
 //打開串口,port爲串口號,默認打開9600,n,8,1,無硬件握手
 virtual int open(int port);
 //設置串口通信參數
 int setsetting(int BaudRate, int ByteSize = 8, int Parity = NOPARITY, int StopBits = ONESTOPBIT);
 //設置硬件握手
 int setflowctrl();
 //讀取控制線狀態
 int getlstatus();
 //設置DTR狀態
 int setdtr(bool enable);
 //設置RTS狀態
 int setrts(bool enable);
 //設置超時時間
 int settimeout(unsigned long ulTimeOut);
 //設置輸入輸出緩衝區大小函數
 int setiosize(int isize,int osize);
};

cpp文件如下:

#include "AsynComm.h"
//初始化串口
CAsynComm::CAsynComm()
{
 Init(); 
}

//關閉串口並釋放資源
CAsynComm::~CAsynComm()
{
 close();
 if(ro.hEvent != INVALID_HANDLE_VALUE)
  CloseHandle(ro.hEvent);

 if(wo.hEvent != INVALID_HANDLE_VALUE)
  CloseHandle(wo.hEvent); 
}

//初始化函數
void CAsynComm::Init()
{
 memset(&MyDcb, 0, sizeof(MyDcb));
 MyDcb.DCBlength = sizeof(MyDcb);
 
 MyDcb.BaudRate = 9600;  //通信速率
 MyDcb.ByteSize = 8;  //比特位 
 MyDcb.Parity   = NOPARITY; //奇偶校驗位
 MyDcb.StopBits = ONESTOPBIT; //停止位
 
 ComHandle = INVALID_HANDLE_VALUE; //初始化爲無效句柄
 
 InbufSize = 8192;  //默認輸入緩衝爲8k
 OutbufSize = 8192;  //默認輸出緩衝爲8k
 memset(&coTimeOut, 0, sizeof(coTimeOut)); //定義超時,默認爲5秒
 coTimeOut.ReadIntervalTimeout = 0xFFFFFFFF;
 coTimeOut.ReadTotalTimeoutMultiplier = 0;
 coTimeOut.ReadTotalTimeoutConstant = 0;
 coTimeOut.WriteTotalTimeoutMultiplier = 0;
 coTimeOut.WriteTotalTimeoutConstant = 5000;
 
 memset(&ro, 0, sizeof(ro));  //讀入時候得重疊結構
 memset(&wo, 0, sizeof(wo));  //寫入時候得重疊結構
 ro.hEvent = CreateEvent(NULL, true, false, NULL); 
 wo.hEvent = CreateEvent(NULL, true, false, NULL);  
}

//返回TRUE表示串口處於open狀態
bool CAsynComm::IsOpen()
{
 return (ComHandle!=INVALID_HANDLE_VALUE); 
}

//關閉串口
int CAsynComm::close()
{
 if(IsOpen()) CloseHandle(ComHandle);   
 ComHandle = INVALID_HANDLE_VALUE;
 return SIO_OK; 
}

//異步讀函數
//buf爲用戶提供得暫存緩衝區,len爲緩衝區大小
//函數返回SIO_ERROR表示讀取失敗,原因可能是串口沒有打開等
//函數返回非負數表示成功讀取得字節數
int CAsynComm::read(char *buf, int len)
{
 if(!IsOpen()) return SIO_ERROR; //串口沒有打開
 memset(buf,0,len);

 COMSTAT  stat;
 DWORD error;

 if(ClearCommError(ComHandle, &error, &stat) && error > 0)  //清除錯誤
 {
  PurgeComm(ComHandle, PURGE_RXABORT | PURGE_RXCLEAR);
  return SIO_ERROR;
 }
 if(!stat.cbInQue) return SIO_ERROR;  //如果緩衝區沒有數據,直接返回

 unsigned long count = 0;
 len = min((int)(len - 1), (int)stat.cbInQue);  //取緩存中得實際數據和len得最小值

 if(!ReadFile(ComHandle, buf, len, &count, &ro))  //如果調用失敗,則看錯誤類型,count包含了讀取得實際字節
 {
  if(GetLastError() == ERROR_IO_PENDING)   
  {
   if(!GetOverlappedResult(ComHandle, &ro, &count, false))
   {
    if(GetLastError() != ERROR_IO_INCOMPLETE) count = 0;
   }
  }
  else
   count = 0;
 }   
 
 if(count>0) 
 {
  buf[count] = '/0'; 
  return count; 
 }
 return SIO_ERROR;
}

//從緩衝區中讀取一個字節函數
int CAsynComm::getch()
{
 if(!IsOpen()) return SIO_ERROR; //串口沒有打開
 COMSTAT  stat;
 DWORD error;
 char buf[2];

 if(ClearCommError(ComHandle, &error, &stat) && error > 0)  //清除錯誤
 {
  PurgeComm(ComHandle, PURGE_RXABORT | PURGE_RXCLEAR);
  return SIO_ERROR;
 }
 if(!stat.cbInQue) return SIO_ERROR;  //如果緩衝區沒有數據,直接返回
 unsigned long count = 0; 

 if(!ReadFile(ComHandle, buf, 1, &count, &ro))  //如果調用失敗,則看錯誤類型,count包含了讀取得實際字節
 {
  if(GetLastError() == ERROR_IO_PENDING)
  {
   if(!GetOverlappedResult(ComHandle, &ro, &count, false))
   {
    if(GetLastError() != ERROR_IO_INCOMPLETE) count = 0;
   }
  }
  else
   count = 0;
 }   
 
 if(count>0) 
 {  
  return ((int)(buf[0])); 
 }
 
 return SIO_ERROR;
}

//異步寫函數
//buf爲需要寫入得數據緩衝區,len爲其長度
//返回SIO_ERROR表示寫入失敗,否則返回實際寫入得字節數
//在這裏,我們採取了超時機制,知道數據成功寫入才返回,超時時間可以用函數settimeout設置
int CAsynComm::write(char *buf, int len)
{
 if(!IsOpen()) return SIO_ERROR;  //串口沒有打開

 if(buf==NULL) return SIO_ERROR;
 DWORD    error;
 
 if(ClearCommError(ComHandle, &error, NULL) && error > 0) //清除錯誤
 {
  PurgeComm(ComHandle, PURGE_TXABORT | PURGE_TXCLEAR); 
 }
 
 unsigned long count = 0;
 unsigned long value=0;
 if(!WriteFile(ComHandle, buf, len, &count, &wo))  //如果調用失敗,則看錯誤類型
 {
  value=GetLastError();
  if(value != ERROR_IO_PENDING) count = 0;  //如果非ERROR_IP_PENDING,則返回錯誤
  else
  {
   if(!GetOverlappedResult(ComHandle, &wo, &count, TRUE))
   {
    if(GetLastError() != ERROR_IO_INCOMPLETE) count = 0;
   }
  }
 }
 if(count>0) return count;
 return SIO_ERROR;
}

//向緩衝區寫一個字節函數
int CAsynComm::putch(char ch)
{
 if(!IsOpen()) return SIO_ERROR; //串口沒有打開
 
 DWORD    error;
 char buf[2];
 
 if(ClearCommError(ComHandle, &error, NULL) && error > 0)  //清除錯誤
 {
  PurgeComm(ComHandle, PURGE_TXABORT | PURGE_TXCLEAR); 
 }
 
 buf[0]=ch;
 buf[1]=0;
 
 unsigned long count = 0;
 
 if(!WriteFile(ComHandle, buf, 1, &count, &wo))  //如果調用失敗,則看錯誤類型
 {
  if(GetLastError() != ERROR_IO_PENDING) count = 0;
  else
  {
   if(!GetOverlappedResult(ComHandle, &wo, &count, TRUE))
   {
    if(GetLastError() != ERROR_IO_INCOMPLETE) count = 0;
   }
  }
 }
 if(count>0) return SIO_OK;
 return SIO_ERROR;
}

//串口打開函數,只需要指定端口號就可以
//對於串口得基本參數,系統默認採取9600,n,8,1和無硬件握手
//如果希望調整參數,請打開串口之前調用setsetting,settimeout,setflowctrl,setiosize等函數
int CAsynComm::open(int port)
{
 if(port>1024 || port<1) return SIO_ERROR;  //非法端口號
 
 if(IsOpen()) close();    //如果已經打開,則關閉
 nPort = port;
 char str[10];
 
 if(ro.hEvent==INVALID_HANDLE_VALUE || wo.hEvent==INVALID_HANDLE_VALUE) return SIO_ERROR; 
 strcpy(str, "COM");
 ltoa(port, str + 3, 10);
 ComHandle = CreateFile(    //打開串口
   str,
   GENERIC_READ | GENERIC_WRITE,
   0,
   NULL,
   OPEN_EXISTING,
   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,  //重疊標誌
   NULL
   );
 if(!IsOpen()) return SIO_ERROR;  //打開失敗,直接返回
  
 SetupComm(ComHandle, InbufSize, OutbufSize); //設置緩衝區大小
 SetCommState(ComHandle, &MyDcb);  //設置通信參數 
 SetCommTimeouts(ComHandle, &coTimeOut);  //設置超時
 PurgeComm(ComHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ); //清除錯誤
 
 return SIO_OK; 
}

//設置通信參數
//不允許在串口打開之後還可設置
//BaudRate爲通信速率,如9600,19200,38400等,傳遞時候可直接傳遞數值
int CAsynComm::setsetting(int BaudRate, int ByteSize, int Parity, int StopBits)
{
 if(IsOpen()) return SIO_ERROR;
 
 MyDcb.BaudRate = BaudRate;
 MyDcb.ByteSize = ByteSize;
 MyDcb.Parity   = Parity;
 MyDcb.StopBits = StopBits;
 
 return SIO_OK;
}

//設置超時時間
//不允許在串口打開之後還可設置
int CAsynComm::settimeout(unsigned long ulTimeOut)
{
 if(IsOpen()) return SIO_ERROR; 
 coTimeOut.WriteTotalTimeoutConstant = ulTimeOut;
 return SIO_OK;
}

//設置硬件握手,流量控制
//不允許在串口打開之後還可設置
int CAsynComm::setflowctrl()
{
 if(IsOpen()) return SIO_ERROR; 
 MyDcb.fOutxCtsFlow=TRUE;
 MyDcb.fRtsControl=TRUE;
 return SIO_OK;
}

//獲取線路狀態
int CAsynComm::getlstatus()
{
 DWORD  dwEvtmask; 
 int ret=0;
 if(!IsOpen()) return SIO_ERROR; 

 if(GetCommModemStatus(ComHandle,&dwEvtmask)==0)
 {
  return SIO_ERROR;
 }
 ret=dwEvtmask; 
 return ret;
}

//設置DTR線狀態
int CAsynComm::setdtr(bool enable)
{
 if(!IsOpen()) return SIO_ERROR;
 
 DWORD data=CLRDTR; 
 if(enable)data=SETDTR;
 if(EscapeCommFunction(ComHandle,data))return SIO_OK;
 return SIO_ERROR;
}

//設置RTS線狀態
int CAsynComm::setrts(bool enable)
{
 if(!IsOpen()) return SIO_ERROR;
 
 DWORD data=CLRRTS; 
 if(enable)data=SETRTS;
 if(EscapeCommFunction(ComHandle,data))return SIO_OK;
 return SIO_ERROR;
}

//設置緩衝區參數函數
//不允許在串口打開之後還可設置
int CAsynComm::setiosize(int isize,int osize)
{
 if(IsOpen()) return SIO_ERROR;
 if(isize<=0) return SIO_ERROR;
 if(osize<=0) return SIO_ERROR;
 InbufSize = isize;
 OutbufSize = osize;
 return SIO_OK;
}

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