介绍一个简单的串口类,同一时间只能读或写,但这个类比较简单,不必每次都要使用P.J和龚建伟等的串口类,这个类没有开启线程,就是一个简单的串口操作类。
先看下头文件的声明,主要就几个函数,打开串口,读写串口数据,关闭串口与确认串口状态
#pragma once
#ifndef _PC_SERIAL_H__
#define _PC_SERIAL_H__
class CSerial:public CObject
{
public:
CSerial();
virtual ~CSerial();
BOOL OpenComm(int nPort,LONG32 nPaud,char parity = 'N',
DWORD dwCommEvents = EV_RXCHAR,UINT databits = 8, UINT stopbits = 1);
BOOL IsOpen();
void CloseComm();
int WriteComm(BYTE ch,int delay);
int WriteCommData(BYTE* buff,int len,int delay = 1,int flag = 0);
int ReadCommData(BYTE* buff,int len,int timeout);
int ReadCommData(int timeout);
int m_nPort;
int m_nPaud;
private:
//int m_hComm;
HANDLE m_hComm;
};
#endif
接下来看具体的实现,首先是构造析构函数
CSerial::CSerial()
{
m_hComm = INVALID_HANDLE_VALUE;
}
CSerial::~CSerial()
{
CloseComm();
}
接下来是打开串口函数,这里有默认的串口设置
BOOL CSerial::OpenComm(int nPort,LONG32 nPaud,char parity /* = */,
DWORD dwCommEvents /* = EV_RXCHAR */,UINT databits /* = 8 */, UINT stopbits /* = 1 */)
{
COMMTIMEOUTS tous;
DCB dcb;
CString strComm;
memset(&tous,0,sizeof(tous));
tous.ReadIntervalTimeout = MAXWORD;
//在串口号大于10时,需要修改其字符串形式
if (nPort >=10)
{
strComm.Format(_T("\\\\.\\%d"),nPort);
}
else
strComm.Format(_T("COM%d"),nPort);
m_hComm = CreateFile(strComm,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
if (m_hComm!=INVALID_HANDLE_VALUE)
{
GetCommState(m_hComm,&dcb);
dcb.BaudRate = nPaud;
dcb.Parity = parity;
dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = 0;
dcb.fOutX = dcb.fInX = 0;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.ByteSize = databits;
dcb.StopBits = stopbits;
SetCommState(m_hComm,&dcb);
SetupComm(m_hComm,4096,4096); //串口缓冲区大小设置,InQueue与OutQueue均为4k
SetCommTimeouts(m_hComm,&tous);
EscapeCommFunction(m_hComm,SETDTR);
EscapeCommFunction(m_hComm,SETRTS);
m_nPaud = nPaud;
m_nPort = nPort;
return TRUE;
}
return FALSE;
}
接下来是写串口数据函数, 对其中数据可以分为一次性全部写入与一个字节一个字节进行写入两种方式,大多数情况下,是使用前一种方式写入数据
int CSerial::WriteComm(BYTE ch,int delay)
{
return(WriteCommData(&ch,1,delay));
}
int CSerial::WriteCommData(BYTE* buff,int len,int delay /* = 1 */,int flag /* = 0 */)
{
int i=0;
unsigned long number = 0;
if (flag == 0)
{
if(delay!=0)
Sleep(delay);
WriteFile(m_hComm,buff,len,&number,NULL);
return ((int)number);
}
//为了一个字节一个字节的写入
else
{
for (i=0;i<len;i++)
{
if(delay!=0)
Sleep(delay);
WriteFile(m_hComm,buff+i,1,&number,NULL);
//当不再写入数据时,退出循环
if(number<1)
break;
}
return i;
}
}
然后便是读取数据了,然后这里也是分为两类,第一种是用于已知要接收数据对象的形式,用于上下层设计好接口方式的程序中,第二种则是类似等待数据的形式,当无法获知串口何时会有数据上传时,可以在while循环中使用该函数来作为等待
//根据接口定义已经获知需要读取的数据大小
int CSerial::ReadCommData(BYTE* buff,int len,int timeout)
{
DWORD start;
COMSTAT stat;
DWORD dwError;
int i=0;
unsigned long number = 0;
start = GetTickCount();
do
{
//清除COMM标志
int err = GetLastError();
ClearCommError(m_hComm,&dwError,&stat);
//担心一次readfile操作无法完全获取到需要的数据,
//故对该操作的长度进行限定并判断
ReadFile(m_hComm,buff+i,len-i,&number,NULL);
//若是该此读取出来的数据大小与剩下需要的大小相同,则说明已经读取完毕
if(number == (unsigned long)(len-i))
return len;
//若是还可以读取出数据,说明还有,将读取出来的数据进行迭代
else if(number>0)
i+=number;
else if(timeout>5)
Sleep(1);
} while (GetTickCount()-start <(unsigned int)timeout);
return i;
}
int CSerial::ReadCommData(int timeout)
{
BYTE ch;
DWORD start;
COMSTAT stat;
DWORD dwError;
unsigned long number = 0;
start = GetTickCount();
do
{
int err = GetLastError();
ClearCommError(m_hComm,&dwError,&stat);
//一个字节一个字节进行读取
ReadFile(m_hComm,&ch,1,&number,NULL);
//说明串口中是存在数据的
if(number>0)
return (int)ch;
} while (GetTickCount()-start <(unsigned int)timeout);
return -1;
}
然后是最后两个函数了
void CSerial::CloseComm()
{
if (m_hComm!=INVALID_HANDLE_VALUE)
{
Sleep(5);
CloseHandle(m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
}
}
BOOL CSerial::IsOpen()
{
return (m_hComm!=INVALID_HANDLE_VALUE);
}
就这样,一个简单的串口类就算完成了,以上这些代码并不是我的原创,只是觉得这个类比较简洁,于是就拿来用了。