1 概述
简单易用,使我们编写的软件代码能够在时间长久之后,仍然能够一眼就明白其中的含义和使用过程,因此,简单易用是我们编写软件的目标。反之,如果你写的代码复杂晦涩难懂,就算功能是正常的,但是交由第三方使用的时候的,第三方回感到无比的痛苦,那种感受谁用谁知道。
本着简单易懂的原则,编写了WIN32下的C++串口类,并提供的了简单使用实例,该类能够满足没有特殊要求的用户使用。
2 超简单串口类介绍
该类对接收串口数据,采用一个单独的线程对接收串口数据进行了封装,当然也保留了最原始的接收数据方法,使用者可以自己编写接收串口数据方法。
类中用到的枚举常量:
/// <summary>流控制标志</summary>
enum EComPortHandshake
{
None = 0,
Software = 1,
Hardware = 2
};
/// <summary>奇偶检验位标志</summary>
enum EComPortParity
{
NoParity = 0, // No parity (default)
OddParity = 1, // Odd parity
EvenParity = 2, // Even parity
MarkParity = 3, // Mark parity
SpaceParity = 4 // Space parity
};
/// <summary>停止位标志</summary>
enum EComPortStopBits
{
OneStopBits = 0, // 1 stopbit (default)
One5StopBits = 1, // 1.5 stopbit
TwoStopBits = 2 // 2 stopbits
};
/// <summary>事件掩码标志</summary>
enum EComPortEvtMask {
EV_RXCHAR_ = 0x0001, // Any Character received
EV_RXFLAG_ = 0x0002, // Received certain character
EV_TXEMPTY_ = 0x0004, // Transmitt Queue Empty
EV_CTS_ = 0x0008, // CTS changed state
EV_DSR_ = 0x0010, // DSR changed state
EV_RLSD_ = 0x0020, // RLSD changed state
EV_BREAK_ = 0x0040, // BREAK received
EV_ERR_ = 0x0080, // Line status error occurred
EV_RING_ = 0x0100, // Ring signal detected
EV_PERR_ = 0x0200, // Printer error occured
EV_RX80FULL_ = 0x0400, // Receive buffer is 80 percent full
EV_EVENT1_ = 0x0800, // Provider specific event 1
EV_EVENT2_ = 0x1000 // Provider specific event 2
};
/// <summary>串口清理标志</summary>
enum EComPortPurgeFlags {
PURGE_TXABORT_ = 0x0001, // Kill the pending/current writes to the comm port.
PURGE_RXABORT_ = 0x0002, // Kill the pending/current reads to the comm port.
PURGE_TXCLEAR_ = 0x0004, // Kill the transmit queue if there.
PURGE_RXCLEAR_ = 0x0008 // Kill the typeahead buffer if there.
};
enum EComPortBaudRate {
CBR_110_ = 110,
CBR_300_ = 300,
CBR_600_ = 600,
CBR_1200_ = 1200,
CBR_2400_ = 2400,
CBR_4800_ = 4800,
CBR_9600_ = 9600,
CBR_14400_ = 14400,
CBR_19200_ = 19200,
CBR_38400_ = 38400,
CBR_56000_ = 56000,
CBR_57600_ = 57600,
CBR_115200_ = 115200,
CBR_128000_ = 128000,
CBR_256000_ = 256000
};
串口类的接口形式如下:
class IAsyncComPort
{
public:
IAsyncComPort() {}
/// <summary>
/// 打开串口
/// </summary>
/// <param name="portNum">串口号</param>
/// <param name="baudRate">波特率</param>
/// <param name="dataBits">数据位, Number of bits/byte, 4-8</param>
/// <param name="parity">奇偶校验, 0-4=None,Odd,Even,Mark,Space</param>
/// <param name="stopBits">停止位, 0,1,2 -- 1, 1.5, 2</param>
/// <param name="handshake">流控制, 0-无, 1-软件, 2-硬件</param>
/// <returns>0-正常, 其它-错误代码</returns>
virtual unsigned long Open(unsigned short portNum, EComPortBaudRate baudRate, unsigned char dataBits, EComPortParity parity, EComPortStopBits stopBits, EComPortHandshake handshake = EComPortHandshake::None) = 0;
virtual void SetComPortReceiveOneCharCallbackFunc(std::function<void (uint8_t data, uint16_t portNum)> func) = 0;
virtual void SetComPortReceiveFullOneMsgCallbackFunc(std::function<void(uint8_t *datas, int32_t len, uint16_t portNum)> func) = 0;
/// <summary>
/// 设置波特率
/// </summary>
/// <param name="baudRate">波特率</param>
/// <returns>0-正常, 其它-错误代码</returns>
virtual unsigned long SetBaudRate(unsigned long baudRate) = 0;
/// <summary>
/// 设置停止位
/// </summary>
/// <param name="stopBits">停止位, 0,1,2 = 1, 1.5, 2</param>
/// <returns>0-正常, 其它-错误代码</returns>
virtual unsigned long SetStopBits(EComPortStopBits stopBits) = 0;
/// <summary>
/// 设置数据位
/// </summary>
/// <param name="dataBits">数据位, 4-8</param>
/// <returns>0-正常, 其它-错误代码</returns>
virtual unsigned long SetDataBits(unsigned char dataBits) = 0;
/// <summary>
/// 设置奇偶校验
/// </summary>
/// <param name="parity">奇偶校验, 0-4=None,Odd,Even,Mark,Space</param>
/// <returns>0-正常, 其它-错误代码</returns>
virtual unsigned long SetParity(EComPortParity parity) = 0;
/// <summary>
/// 设置流控制
/// </summary>
/// <param name="handshake">流控制, 0-无, 1-软件, 2-硬件</param>
/// <returns>0-正常, 其它-错误代码</returns>
virtual unsigned long SetHandShake(EComPortHandshake handshake) = 0;
/// <summary>
/// 向串口写入数据
/// </summary>
/// <param name="datas">要写入的字节数组</param>
/// <returns>返回值, true Or false</returns>
virtual bool Write(unsigned char *pBuffer, long bufferLen) = 0;
/// <summary>
/// 向串口写入1个字节
///</summary>
/// <param name="data">要写入的字节</param>
/// <returns>返回值, true Or false</returns>
virtual bool Write(unsigned char data) = 0;
/// <summary>
/// 从串口读出数据
/// </summary>
/// <param name="datas">用于存放数据的缓冲数组</param>
/// <param name="numberOfBytesRead">从穿口中读取到的字节数</param>
/// <returns>返回值, TRUE Or FALSE</returns>
virtual bool Read(unsigned char *pBuffer, long bufferLen, unsigned long *pNumberOfBytesRead) = 0;
/// <summary>
/// 从串口读出1个字节的数据
/// </summary>
/// <param name="data">用于存放数据的缓冲数组</param>
/// <returns>返回值, TRUE Or FALSE</returns>
virtual bool Read(unsigned char *pBuffer) = 0;
/// <summary>
/// 关闭串口
/// </summary>
virtual void Close() = 0;
/// <summary>
/// 设置串口掩码
/// </summary>
/// <param name="dwEvtMask">掩码值</param>
/// <returns>返回值, TRUE Or FALSE</returns>
virtual bool SetPortMask(EComPortEvtMask dwEvtMask) = 0;
/// <summary>
/// 获取或设置接收线程的是否启动或已经启动
/// </summary>
virtual void EnableReceiveThread(bool bEnable) = 0;
/// <summary>
/// 设置消息的长度(字节数), 同步头信息.(如果 msgLen小于0 且syncHeader为null, 则默认50个字节为一条消息.syncHeader=null, 则认为没有同步头)
/// </summary>
/// <param name="msgLen">一条完整消息的长度, 字节数(含同步头数组的字节数)</param>
/// <param name="syncHeader">同步头字节数组</param>
virtual bool SetRecvMsgLengthAndSyncHeader(long msgLen, unsigned char *pSyncHeader, long syncHeaderLen) = 0;
virtual unsigned short GetPortNum() const = 0;
virtual unsigned long GetBaudRate() const = 0;
virtual unsigned char GetDataBits() const = 0;
virtual unsigned char GetParity() const = 0;
virtual unsigned char GetStopBits() const = 0;
virtual unsigned long GetHandShake() const = 0;
virtual void Release() = 0;
public:
virtual ~IAsyncComPort() { OutputDebugString(_T("~IAsyncComPort Destructor.\n")); }
};
#if defined(__cplusplus)
extern "C"
{
#endif
IAsyncComPort *CreateAsyncComPortObject();
void ReleaseAsyncComPortObject(IAsyncComPort **p);
#if defined(__cplusplus)
}
#endif
其中CreateAsyncComPortObject用于创建串口类对象的指针,而ReleaseAsyncComPortObject则用于释放串口类的指针。
也可以stl中的智能指针对指针对象进行管理。
3 串口类的特点
该串口类的一个非常大的特点:简单易用。采用异步读写的方式,接收串口数据采用单独一个线程进行封装,可以让该类自动完成同步头的对比,当接收到1条完整的消息后,调用回调函数通知用户;也可以设置回调函数,自己完成同步头和校验和的对比工作。
其使用步骤如下:
IAsyncComPort *port = CreateAsyncComPortObject();
port->SetRecvMsgLengthAndSyncHeader(4, nullptr, 0);
port->SetComPortReceiveFullOneMsgCallbackFunc( … );
port->Open(…);
然后就可以在循环中发送数据,在设置的回调函数中处理接收到的串口数据。
在软件退出时,调用如下过程:
port->EnableReceiveThread(false);
ReleaseAsyncComPortObject(&port); // 如果采用了智能指针管理该指针,则可以不用调用该函数。
4 使用实例
下面时使用实例中的主要代码段(详细过多,就不粘贴了):
生成指针对象,并由智能指针管理。
初始化,设置接收消息的回调函数,打开串口,开始工作。
退出软件时,关闭接收串口数据的线程,完成串口资源的清理工作。