串口使用和CSerial类

1 串口通信的基本原理

串口通信中无论是写入串口还是读取串口,都是对缓冲区操作的。可以理解为写串口就是向输出缓冲区写入内容,读取串口就是从输入串口缓冲区读取内容。但是何时打开串口,何时发送数据,何时接受数据都是未知的。所以在串口通信时一般是一个主动一个被动。通信双方有一定的协议,就是事先协商好的数据格式。接收方接收到数据后,返回一个应答标志,告诉发送方已经接收到数据了。如果接收错误则返回接收错误标志,或者一直等待接收到正确的数据再返回。至于接收方怎么才知道有数据发送过来了,对于单片机之类的可以使用串口中断或者巡检的方式。对于工控机之类只能使用巡检的方式了。前段时间做一个检测系统,用到串口通信,顺手就写了一个类,这里分享出来。

2 串口通信的基本操作步骤

无论哪种操作方式,串口通信一般都通过四个步骤来完成:
1、打开串口;
2、配置串口;
3、读写串口;
4、关闭串口;

2.1 打开串口

在Windows中使用串口通信一般有两种方式,一种是使用Windows中的API,另一种方式使用MFC中的控件。这里采用API的方式。

HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);

lpFileName,要打开的串口号,如“COM1”;

dwDesiredAccess,串口访问的类型,可以是只读、只写、可读可写。其取值可以是GENERIC_READ、GENERIC_WRITE或者他们的组合;

dwShareMode,共享属性,由于串口不能共享,该参数必须置为0;

lpSecurityAttributes,引用的安全类型,一般设置为NULL;

dwCreationDistribution,创建文件的标志,对于串口操作该参数必须置为OPEN_EXISTING;

dwFlagsAndAttributes,串口通信是同步还是异步,0表示同步。FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED表示异步;

hTemplateFile:对串口而言该参数必须置为NULL。

异步方式打开串口示例代码:

CreateFile(
m_strCom,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
NULL);
2.2 配置串口

串口打开需要配置一些参数,如DCB结构、输入输出缓冲区大小、设置超时结构。
配置DCB结构,该结构中可以配置波特率、数据位、奇偶校验和停止位之类的信息;
设置该结构的时候需要用到几个函数:

BOOL GetCommState(HANDLE hFile, LPDCB lpDCB);
BOOL SetCommState(HANDLE hFile, LPDCB lpDCB);

示例代码如下:

DCB dcb;
GetCommState(m_hCom, &dcb);
dcb.BaudRate = m_dwBaudRate;
dcb.ByteSize = m_byteSize;
dcb.Parity = m_byteCheck;
dcb.StopBits = m_byteStop;
SetCommState(m_hCom, &dcb);

设置串口缓冲区大小:

BOOL SetupComm( HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue);

示例代码如下:

DWORD dwInQueue = 1024;
DEWORD dwOutQueue = 1024;
SetupComm(hCom, dwInQueue, dwOutQueue);

设置超时:

BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);

该函数第一个参数不用多说,第二个参数是个结构体。

typedef struct _COMMTIMEOUTS {
    DWORD ReadIntervalTimeout;
    DWORD ReadTotalTimeoutMultiplier;
    DWORD ReadTotalTimeoutConstant;
    DWORD WriteTotalTimeoutMultiplier;
    DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

ReadIntervalTimeout,读取操作过程中两个字符之间的延时,当读取串口的时候如果两个字符传输的时间差如果超过这个时间的话,读取串口函数就会返回;

ReadTotalTimeoutMultiplier,读取操作计算总超时时每个字符读取的时间,其实就是估算的每个字符传输需要的时间;

ReadTotalTimeoutConstant,一次串口读取超时时间的固定值,其实就是担心估计的两个字符之间传输时间不准确,然后又加上的一个超时时间;

读取总超时时间的计算方法如下:

读取操作总超时 = ReadTotalTimeoutMultiplier*读取字符数 + ReadTotalTimeoutConstant;

读取串口的时候有两种超时,一种是两个传输字符之间的时间间隔;如果读取两个字符之间的时间超过ReadIntervalTimeout的话,读取串口的操作就会返回。另一种是读取总时间超时,如果读取操作时间超过刚计算的总超时的话,读取操作也会返回;这里说的返回与串口的同步操作和异步操作中说的返回不同。同步和异步那种返回是指函数的返回,这里的返回是指串口读取操作的返回;

WriteTotalTimeoutMultiplier,同读操作相关的参数;

WriteTotalTimeoutConstant,同读操作相关参数;

写操作总超时时间计算方法如下:

写操作总超时 = WriteTotalTimeoutMultiplier*写入字符数 + WriteTotalTimeoutConstant;

写入操作只有一种超时,只有总超时;
一般做以下设置:

TimeOuts.ReadIntervalTimeout = MAXDWORD;     // 把间隔超时设为最大,
                                             //把总超时设为0将导致ReadFile立即返回并完成操作
TimeOuts.ReadTotalTimeoutMultiplier = 0;     //读时间系数
TimeOuts.ReadTotalTimeoutConstant = 0;       //读时间常量
TimeOuts.WriteTotalTimeoutMultiplier = 50;   //总超时=时间系数*要求读/写的字符数+时间常量
TimeOuts.WriteTotalTimeoutConstant = 2000;   //设置写超时以指定WriteComm成员函数中的

这样设置后读取完所有字符后就会返回,写完操作后也会返回;

2.3 读写串口

使用两个函数ReadFile和WriteFile。

ReadFile:

BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped);

hFile,要读取串口的句柄;

lpBuffer,要接收数据的缓冲区;

nNumberOfBytesToRead,要读取数据的字节数;

lpNumberOfBytesRead,DWORD指针,保存实际读入的数据的个数;

lpOverlapped,OVERLAPPED结构体,如果是同步串口通信串口设置为NULL。异步串口通信操作需要一个OVERLAPPED结构体指针;

异步读取数据示例代码如下:

DWORD dwRead;//这个值需要根据实际要读取的数据
DWORD dwReadTrue = 0;
BOOL bRead;
//清除错误标志
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
dwRead = (dwLength <= ComStat.cbInQue)?dwLength:ComStat.cbInQue;
if ( !dwRead ) return MAXDWORD;//输入缓存区里面没有内容
OVERLAPPED osRead;
memset(&osRead, 0, sizeof(OVERLAPPED));
HANDLE hEventRecv = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
osRead.hEvent = hEventRecv;
bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, &osRead);
if ( !bRead && (ERROR_IO_PENDING == GetLastError()) )//读操作未完成
{
	GetOverlappedResult(m_hCom, &osRead, &dwReadTrue, TRUE);//等待读操作完成,暂时这样操作
	PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
	ResetEvent(m_hEventRecv);
	return dwReadTrue;
}
else if ( !bRead )
{
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
	return MAXDWORD;
}
return dwReadTrue;

WriteFile:

BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);

hFile,要写入的串口句柄;

lpBuffer,要写入的数据;

nNumberOfBytesToWrite,要写入数据的字节数;

lpNumberOfBytesWritten,一个DWORD指针,实际写入数据字节数;

lpOverlapped,OVERLAPPED结构体,如果是同步串口通信串口设置为NULL。异步串口通信操作需要一个OVERLAPPED结构体指针;

写入数据示例代码如下:

DWORD dwToWrite = dwLength;
DWORD dwWritten = 0;
BOOL bWrite;
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
OVERLAPPED osWrite;
memset(&osWrite, 0, sizeof(OVERLAPPED));
HANDLE hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
osWrite.hEvent = hEventSend;
bWrite = WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, &osWrite);
if ( !bWrite && (ERROR_IO_PENDING == GetLastError()) )//串口写操作未完成
{
	GetOverlappedResult(
	m_hCom,
	&osWrite,
	&dwWritten,
	TRUE);//等待写操作完成,这里暂时使用这种操作方式
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
	ResetEvent(m_hEventSend);
	return dwWritten;
}
else if ( !bWrite )//串口写入错误
{
	ClearCommError( m_hCom, &dwErrorFlags, &ComStat );
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
	return MAXDWORD;
}
return dwWritten;
2.4 关闭串口

串口属于系统资源,打开了使用后要关闭掉。调用以下函数就行了:

BOOL CloseHandle(HANDLE hObject);
2.5 还有些需要用到的函数
BOOL PurgeComm(HANDLE hFile, DWORD dwFlags);

参数dwFlags指定要完成的操作,可以如下值:

PURGE_TXABORT 无论串口处于什么状态,中断所有写操作并立即返回;

PURGE_RXABORT 无论串口处于什么状态,中断所有读操作并立即返回;

PURGE_TXCLEAR 清空输出缓冲区;

PURGE_RXCLEAR 清空输入缓冲区;

3 CSerial成员函数和成员变量

为了方便使用,对串口的一些API函数进行了封装:CSerial类。下表中有CSerial类的说明,具体的使用会有一个使用案例。CSerial类是对串口使用的一个封装,主要包括打开串口,串口读写功能。该类使用比较简单,不用复杂的配置,那俩可以马上使用。还可以共享串口,不同的CSerial对象可以共享同一个串口,类中有一个对串口的引用计数,当与该串口绑定的对象都析构后会自动关闭该串口。类成员函数如下表:

成员函数 说明
CSerial(); 构造函数;
CSerial(CString strCom, DWORD dwBaudRate = 9600, BYTE byteSize = 8, BYTE byteCheck = NOPARITY, BYTE byteStop = ONESTOPBIT, BOOL bSync = FALSE); 构造函数;
WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); 向串口写入数据
ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); 从串口读出数据;
ClearError(); 清除串口错误,该函数会清除串口错误标志位。
Purge(); 同API函数中的PurgeComm()
SetComString(CString strCom); 设置串口号,比如“COM1”。只有当实例化CSerial对象是没有传入串口号或者想要修改串口号的时候使用该函数;
SetBaudRate(DWORD dwBaudRate); 设置串口通信的波特率,默认为9600;
SetByteSize(BYTE byteSize); 设置串口通信的数据位,默认为8位;
SetCheck(BYTE byteCheck); 设置串口通信的奇偶校验,默认为不校验;
SetStopBit(BYTE byteStopBit); 设置串口通信的停止位,默认为一位停止位;
SetSync(BOOL bSync); 设置是串口通信是同步还是异步,默认是同步通信;
SetInQue(DWORD dwInQue); 设置输入缓冲区大小,默认为1024;
SetOutQue(DWORD dwOutQue); 设置输出缓冲区大小,默认为1024;
SetTimeouts(COMMTIMEOUTS timeouts); 设置超时设置,如果更改默认超时设置的时候,调用该函数;
GetComString(); 获取串口通信的串口号;
GetBaudRate(); 获取串口通信的波特率;
GetByteSize(); 获取串口通信的数据位数;
GetCheck(); 获取串口通信是否使用奇偶校验;
GetStopBit(); 获取串口通信使用了几位停止位;
GetSync(); 获取串口通信的通信方式是同步还是异步;
GetInQue(); 获取串口通信的输入缓冲区大小;
GetOutQue(); 获取串口通信的输出缓冲区大小;
GetComStatus(); 获取串口的状态,一般查看串口是否正确打开;

4 使用说明

-------------------------------
CSerial();

构造函数,当实例化对象的时候,不传入参数则调用该函数。如果不传入参数该对象不能用来通信,需要通过SetComString(CString strCom);函数来设置该对象的串口号来正常通信;

-------------------------------
CSerial(
CString strCom,
DWORD dwBaudRate = 9600,
BYTE byteSize = 8,
BYTE byteCheck = NOPARITY,
BYTE byteStop = ONESTOPBIT,
BOOL bSync = FALSE);

构造函数,需要传入串口号。其他参数都有默然值。如果需要更改的话,可以通过Setxx()函数来设置相应的参数;

-------------------------------
DWORD WriteData(
unsigned char *pBuffer,
DWORD dwLength,
DWORD dwTimeout = 1000);

pBuffer,要写入的数据;

dwLength,写入数据的长度;

dwTimeout,异步通信的时超时时间,暂时没有使用,可以不管;

-------------------------------
DWORD ReadData(
unsigned char *pBuffer,
DWORD dwLength,
DWORD dwTimeout = 1000);

pBuffer,读取数据缓冲区;

dwLength,读取数据长度;

dwTimeout,异步通信时的超时时间,暂时没有使用可以不管;

-------------------------------
void Purge(DWORD dwFlags);

与API中的PurgeComm函数功能一样,dwFlags的取值可以是PURGE_TXABORT、PUTGE_RXABORT、PURGE_TXCLEAR、PURGE_RXCLEAR或者他们的组合;

-------------------------------

使用CSerial进行串口通信的例子:

CSerial SerialMeter("COM1");
DWORD dwWritten,dwReadTrue;
unsigned char TxData[11];
unsigned char RxData[11] = {0};
dwWritten = SerialMeter.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
	SerialMeter.ClearError();
	return;
}
Sleep(100);
dwReadTrue = m_SerialMeter.ReadData(RxData,11);
if ( MAXDWORD == dwReadTrue )
{
	m_SerialMeter.ClearError();
	return;
}

串口共享的时候只需要把一个对象赋值给另一个对象,或者用另一个对象来初始化一个对象就可以了。其他的就像使用单独的一个对象一样。串口的打开和关闭由类自己管理。串口共享串口例子:

CSerial SerialMeter("COM1");
CSerial SeralVelocty = SerialMeter;
DWORD dwWritten,dwReadTrue;
unsigned char TxData[11];
unsigned char RxData[11] = {0};
// 第一个串口对象读写
dwWritten = SerialMeter.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
	SerialMeter.ClearError();
	return;
}
Sleep(100);
dwReadTrue = m_SerialMeter.ReadData(RxData,11);

if ( MAXDWORD == dwReadTrue )
{
	m_SerialMeter.ClearError();
	return;
}
// 另一个串口对象读写
Sleep(100);
dwWritten = SeralVelocty.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
	SeralVelocty.ClearError();
	return;
}
Sleep(100);
dwReadTrue = SeralVelocty.ReadData(RxData,11);
if ( MAXDWORD == dwReadTrue )
{
	SeralVelocty.ClearError();
	return;
}

源代码如下:
头文件:

//CSerial类头文件
#pragma once

class CSerial
{
public:
	CSerial();
	CSerial(
		CString strCom,
		DWORD dwBaudRate = 9600,
		BYTE byteSize = 8,
		BYTE byteCheck = NOPARITY,
		BYTE byteStop = ONESTOPBIT,
		BOOL bSync = FALSE);
	~CSerial();
	inline CSerial(const CSerial &com)
	{
		m_hCom = com.m_hCom;
		m_hEventSend = com.m_hEventSend;
		m_hEventRecv = com.m_hEventRecv;
		m_bSync = com.m_bSync;
		m_bIsFirst = com.m_bIsFirst;
		m_strCom = com.m_strCom;
		m_dwBaudRate = com.m_dwBaudRate;
		m_byteSize = com.m_byteSize;
		m_byteCheck = com.m_byteCheck;
		m_byteStop = com.m_byteStop;
		m_dwInQueue = com.m_dwInQueue;
		m_dwOutQueue = com.m_dwOutQueue;
	    m_Timeouts = com.m_Timeouts;
		m_nInitResult = com.m_nInitResult;
		m_pnReusecount = com.m_pnReusecount;
		if ( m_pnReusecount )
		{
			(* m_pnReusecount)++;
		}
	}
	inline CSerial & operator = (const CSerial &com)
	{
		m_hCom = com.m_hCom;
		m_hEventSend = com.m_hEventSend;
		m_hEventRecv = com.m_hEventRecv;
		m_bSync = com.m_bSync;
		m_bIsFirst = com.m_bIsFirst;
		m_strCom = com.m_strCom;
		m_dwBaudRate = com.m_dwBaudRate;
		m_byteSize = com.m_byteSize;
		m_byteCheck = com.m_byteCheck;
		m_byteStop = com.m_byteStop;
		m_dwInQueue = com.m_dwInQueue;
		m_dwOutQueue = com.m_dwOutQueue;
		m_Timeouts = com.m_Timeouts;
		m_nInitResult = com.m_nInitResult;
		m_pnReusecount = com.m_pnReusecount;
		if ( m_pnReusecount )
		{
			(* m_pnReusecount)++;
		}
		return * this;
	}
private:
	int *m_pnReusecount;
	BOOL m_bIsFirst;         //是否是第一次成功打开串口
	HANDLE m_hCom;           //串口句柄
	HANDLE m_hEventSend;     //发送数据事件
	HANDLE m_hEventRecv;     //接收数据事件

	BOOL m_bSync;            //同步传输还是异步传输,TRUE则为同步,FALSE为异步,默认为异步

	CString m_strCom;        //串口端口
	DWORD m_dwBaudRate;      //波特率
	BYTE m_byteSize;         //数据位
	BYTE m_byteCheck;        //校验方式
	BYTE m_byteStop;         //停止位

	DWORD m_dwInQueue;       //串口输入缓冲区     
	DWORD m_dwOutQueue;     //串口输出缓冲区
	//超时相关变量
	COMMTIMEOUTS m_Timeouts;

	int m_nInitResult;
public:
	DWORD WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000);  //dwTimeout为占位符,暂时未用,返回,MAXDWORD表示写入错误
	DWORD ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000);   //dwTimeout为占位符,暂时未用,返回,MAXDWORD表示读取错误
	void CloseCom();
	int InitCom();//返回1,表示没有错误,返回其他表示错误
	void ClearError();
	void Purge(DWORD dwFlags);

	int SetComString(CString strCom);
	int SetBaudRate(DWORD dwBaudRate);
	int SetByteSize(BYTE byteSize);
	int SetCheck(BYTE byteCheck);
	int SetStopBit(BYTE byteStopBit);
	int SetSync(BOOL bSync);
	int SetInQue(DWORD dwInQue);
	int SetOutQue(DWORD dwOutQue);
	int SetTimeouts(COMMTIMEOUTS timeouts);


	CString GetComString();
	DWORD GetBaudRate();
	BYTE GetByteSize();
	BYTE GetCheck();
	BYTE GetStopBit();
	BOOL GetSync();
	DWORD GetInQue();
	DWORD GetOutQue();
	int GetComStatus();//InitCom的返回值,观察串口是否打开成功,0表示没有串口名称,1表示打开成功,MAXINI32表示串口打开错误
};

源文件:

#include "stdafx.h"
#include "Serial.h"

CSerial::CSerial()
{
	m_pnReusecount = NULL;
	m_hCom = NULL;
	m_hEventRecv = NULL;
	m_hEventSend = NULL;
	m_strCom = _T("");
	m_dwBaudRate = 9600;
	m_byteSize = 8;
	m_byteCheck = NOPARITY;
	m_byteStop = ONESTOPBIT;
	m_bSync = TRUE;
	m_bIsFirst = TRUE;
	//缓冲区变量初始化
	m_dwInQueue = 1024;
	m_dwOutQueue = 1024;
	//超时变量初始化
	COMMTIMEOUTS timeout = {MAXDWORD, 0, 0, 50, 1000};
	m_Timeouts = timeout;
}

CSerial::CSerial(
	CString strCom,
	DWORD dwBaudRate,
	BYTE byteSize,
	BYTE byteCheck,
	BYTE byteStop,
	BOOL bSync)
{
	m_pnReusecount = NULL;
	m_hCom = NULL;
	m_hEventRecv = NULL;
	m_hEventSend = NULL;
	m_strCom = strCom;
	m_dwBaudRate = dwBaudRate;
	m_byteSize = byteSize;
	m_byteCheck = byteCheck;
	m_byteStop = byteStop;
	m_bSync = bSync;
	m_bIsFirst = TRUE;
	//缓冲区变量初始化
	m_dwInQueue = 1024;
	m_dwOutQueue = 1024;
	//超时变量初始化
	COMMTIMEOUTS timeout = {MAXDWORD, 0, 0, 50, 1000};
	m_Timeouts = timeout;
	m_nInitResult = InitCom();
}

CSerial::~CSerial()
{
	if (m_pnReusecount)
	{
		(* m_pnReusecount)--;
		if( 0 >= *m_pnReusecount )
		{
			CloseCom();
			delete m_pnReusecount;
		}
	}
}

void CSerial::CloseCom()
{
	if ( m_hEventRecv )
	{
		CloseHandle(m_hEventRecv);
		m_hEventRecv = NULL;
	}
	if ( m_hEventSend )
	{
		CloseHandle(m_hEventSend);
		m_hEventSend = NULL;
	}
	if ( m_hCom )
	{
		CloseHandle(m_hCom);
		m_hCom = NULL;
	}
}

int CSerial::InitCom()
{
	if ( m_strCom == _T("") ) return 0;//如果串口传入为空,则进行串口初始化
	if ( m_hCom )
	{
		CloseHandle(m_hCom);
	}
	if ( m_bSync )
	{
		m_hCom = CreateFile(m_strCom, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	}
	else
	{
		m_hCom = CreateFile(m_strCom, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
	}
	if ( m_hCom == INVALID_HANDLE_VALUE )//串口打开失败
	{
		m_hCom = NULL;
		return 2;
	}
	//设置缓冲区大小,默认为1024;
	SetupComm(m_hCom, m_dwInQueue, m_dwOutQueue);
	//超时设置
	SetCommTimeouts(m_hCom, &m_Timeouts);
	//配置串口
	DCB dcb;
	GetCommState(m_hCom, &dcb);
	dcb.BaudRate = m_dwBaudRate;
	dcb.ByteSize = m_byteSize;
	dcb.Parity = m_byteCheck;
	dcb.StopBits = m_byteStop;
	SetCommState(m_hCom, &dcb);
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
	if ( m_bIsFirst )
	{
		m_pnReusecount = new int;
		* m_pnReusecount = 1;
		m_bIsFirst = FALSE;
	}
	return 1;
}

void CSerial::ClearError()
{
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
	if ( m_hEventRecv ) ResetEvent(m_hEventRecv);
	if ( m_hEventSend ) ResetEvent(m_hEventSend);
}

DWORD CSerial::WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout)
{
	if ( !m_hCom ) return MAXDWORD;
	DWORD dwToWrite = dwLength;
	DWORD dwWritten = 0;
	BOOL bWrite;
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
	if ( m_bSync )//同步
	{
		bWrite =WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, NULL);
		if ( bWrite )
		{
			return dwWritten;
		}
		else
		{
			return MAXDWORD;
		}
	}
	else//异步
	{
		OVERLAPPED osWrite;
		memset(&osWrite, 0, sizeof(OVERLAPPED));
		if ( !m_hEventSend )
		{
			m_hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
		}
		osWrite.hEvent = m_hEventSend;
		bWrite = WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, &osWrite);
		if ( !bWrite && (ERROR_IO_PENDING == GetLastError()) )//串口写操作未完成
		{
			GetOverlappedResult(m_hCom, &osWrite, &dwWritten, TRUE);//等待写操作完成,这里暂时使用这种操作方式
			PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
			ResetEvent(m_hEventSend);
			return dwWritten;
		}
		else if ( !bWrite )//串口写入错误
		{
			ClearCommError( m_hCom, &dwErrorFlags, &ComStat );
			PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
			return MAXDWORD;
		}
		return dwWritten;
	}
}

DWORD CSerial::ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout)
{
	if ( !m_hCom ) return MAXDWORD;
	DWORD dwRead;
	DWORD dwReadTrue = 0;
	BOOL bRead;
	//清除错误标志
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	dwRead = (dwLength <= ComStat.cbInQue)?dwLength:ComStat.cbInQue;
	if ( !dwRead ) return MAXDWORD;//输入缓存区里面没有内容
	if ( m_bSync )//同步
	{
		bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, NULL);
		if ( bRead )
		{
			return dwReadTrue;
		}
		else
		{
			return MAXDWORD;
		}
	}
	else//异步
	{
		OVERLAPPED osRead;
		memset(&osRead, 0, sizeof(OVERLAPPED));
		if ( !m_hEventRecv )
		{
			m_hEventRecv = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
		}
		osRead.hEvent = m_hEventRecv;
		bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, &osRead);
		if ( !bRead && (ERROR_IO_PENDING == GetLastError()) )//读操作未完成
		{
			GetOverlappedResult(m_hCom, &osRead, &dwReadTrue, TRUE);//等待读操作完成,暂时这样操作
			PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
			ResetEvent(m_hEventRecv);
			return dwReadTrue;
		}
		else if ( !bRead )
		{
			ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
			PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
			return MAXDWORD;
		}
		return dwReadTrue;
	}
}

void CSerial::Purge(DWORD dwFlags)
{
	PurgeComm(m_hCom, dwFlags);
}

int CSerial::SetComString(CString strCom)
{
	CString strTemp = m_strCom;
	m_strCom = strCom;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_strCom = strTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetBaudRate(DWORD dwBaudRate)
{
	DWORD dwTemp = m_dwBaudRate;
	m_dwBaudRate = dwBaudRate;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_dwBaudRate = dwTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetByteSize(BYTE byteSize)
{
	BYTE byteTemp = m_byteSize;
	m_byteSize = byteSize;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_byteSize = byteTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetCheck(BYTE byteCheck)
{
	BYTE byteTemp = m_byteCheck;
	m_byteCheck = byteCheck;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_byteCheck = byteTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetStopBit(BYTE byteStopBit)
{
	BYTE byteTemp = m_byteStop;
	m_byteStop = byteStopBit;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_byteStop = byteTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetSync(BOOL bSync)
{
	BOOL bTemp = m_bSync;
	m_bSync = bSync;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_bSync = bTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetInQue(DWORD dwInQue)
{
	DWORD dwTemp = m_dwInQueue;
	m_dwInQueue = dwInQue;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_dwInQueue = dwTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetOutQue(DWORD dwOutQue)
{
	DWORD dwTemp = m_dwOutQueue;
	m_dwOutQueue = dwOutQue;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_dwOutQueue = dwTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetTimeouts(COMMTIMEOUTS timeouts)
{
	COMMTIMEOUTS timeoutTemp = m_Timeouts;
	m_Timeouts = timeouts;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_Timeouts = timeoutTemp;
		return -1;
	}
	return 0;
}

CString CSerial::GetComString()
{
	return m_strCom;
}

DWORD CSerial::GetBaudRate()
{
	return m_dwBaudRate;
}

BYTE CSerial::GetByteSize()
{
	return m_byteSize;
}

BYTE CSerial::GetCheck()
{
	return m_byteCheck;
}

BYTE CSerial::GetStopBit()
{
	return m_byteStop;
}

BOOL CSerial::GetSync()
{
	return m_bSync;
}

DWORD CSerial::GetInQue()
{
	return m_dwInQueue;
}

DWORD CSerial::GetOutQue()
{
	return m_dwOutQueue;
}

int CSerial::GetComStatus()
{
	return m_nInitResult;
}

原文链接: https://www.cnblogs.com/konglongdanfo/p/9135253.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    串口使用和CSerial类

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/275290

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月15日 上午12:55
下一篇 2023年2月15日 上午12:56

相关推荐