Windows串口通信VC++编程实例

1、先入为主,实例开始,可以编写测试程序向串口发送数据,并用串口监测工具Access Port监测串口通信数据

a、设计串口类

class ComDevice
{
public:
	ComDevice();
	~ComDevice();
	//打开串口
	int OpenCom(char *,LPDCB);//char指向串口逻辑名,LPDCB指向设备控制块数据结构
	int CloseCom();//关闭串口
	int Read(char *,DWORD);//读串口收到的数据(一次)
	int Write(char *,DWORD,DWORD);//向串口发送数据
public:
	DWORD		m_error_code;//最后一次操作错误码
	DCB		m_state;//串口配置
	COMMTIMEOUTS	m_Timeouts;//读写超时时间
	HANDLE		m_handle;//串口句柄
};

b、打开串口并初始化

int ComDevice::OpenCom(char *port,LPDCB lpDCB)
{
	this->m_error_code = 0;//操作错误码复位
	
	/*如果串口已经打开*/
	if(this->m_handle != NULL)
		return -1;
	
	//打开串口
	this->m_handle = CreateFile(port,GENERIC_READ | GENERIC_WRITE,//读写方式打开
								0,
								NULL,
								OPEN_EXISTING,//已经创建,则打开即可
								FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,//重叠方式(异步)
								NULL);
	if(this->m_handle == INVALID_HANDLE_VALUE)
	{
		this->m_error_code = ::GetLastError();//获取错误码
		this->m_handle = NULL;
		return -2;
	}
	//初始化串口信息
	//设置串口读写缓冲区大小
	if(!SetupComm(this->m_handle,4200,4200))
		return -1;
		
	//终止所有的异步数据读写操作并清空读写缓冲区
	if(!PurgeComm(this->m_handle,PURGE_TXABORT | PURGE_RXABORT |
			PURGE_TXCLEAR | PURGE_RXCLEAR))
		return -1;
	if(!GetCommState(this->m_handle,&m_state))
		return -1;
	/*设置一些需要修改的信息即可,其他使用系统默认值*/
	this->m_state.DCBlength = sizeof(lpDCB);
	this->m_state.BaudRate = lpDCB->BaudRate;//传输速率
	this->m_state.ByteSize = lpDCB->ByteSize;//指定端口当前使用数据位数
	this->m_state.Parity = lpDCB->Parity;//奇偶校验
	this->m_state.StopBits = lpDCB->StopBits;//指定当前串口使用停止位数	
	this->m_state.fOutxDsrFlow = lpDCB->fOutxDsrFlow;//指定DSR是否用于检测发送流控制,当该成员为TRUE,而DSR为OFF时,发送将被挂起,直到DSR置ON。
	this->m_state.fDtrControl = lpDCB->fDtrControl;//DTR流量控制	
	this->m_state.fOutxCtsFlow = lpDCB->fOutxCtsFlow;//指定CTS是否用于检测发送流控制
	this->m_state.fRtsControl = lpDCB->fRtsControl;//RTS流量控制
		
	if(!SetCommState(this->m_handle,&(this->m_state)))
		return -1;
	return 0;
}
c、读串口数据

int ComDevice::Read(char *readBuff,DWORD readLen)
{
	COMSTAT state;
	DWORD error_ = 0;
	DWORD len = 0;
	this->m_error_code = 0;
	if(this->m_handle == NULL)
		return -1;
	
	//读取DTR信号判断串口连接是否有效
	if(this->m_state.fOutxDsrFlow == true)
	{
		DWORD sig_mask = 0;
		GetCommModemStatus(this->m_handle,&sig_mask);
		if((sig_mask & MS_DSR_ON) != MS_DSR_ON)//fOutxDsrFlow为true时,DSR必须为ON,才能发送数据
			return -1;
	}
	
	//清除串口错误
	if(!ClearCommError(this->m_handle,&error_,&state))
	{
	this->m_error_code = ::GetLastError();
	return -1;
	}
	//设置超时
	if(!SetCommTimeouts(this->m_handle,&m_Timeouts))
	{
	this->m_error_code = ::GetLastError();
	return -1;
	}
	OVERLAPPED os_read;//重叠结构体(用于异步I/O操作)
	memset(&os_read,0,sizeof(OVERLAPPED));//初始化结构体
	if((os_read.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL)//创建重叠事件
	{
		this->m_error_code = ::GetLastError();
		return -2;
	}
	if(!::ReadFile(this->m_handle,readBuff,readLen,&len,&os_read))
	{
		//判断是否读完
		if((this->m_error_code = ::GetLastError()) == ERROR_IO_PENDING)
			::GetOverlappedResult(this->m_handle,&os_read,&len,TRUE);//获取重叠操作结果
	}
	
	this->m_error_code = ::GetLastError();
	::CloseHandle(os_read.hEvent);//关闭重叠事件句柄
	return (int)len; 
}
d、向串口写数据

int ComDevice::Write(char *SndBuff,DWORD SndLen,DWORD mTime)
{
	COMSTAT state;
	DWORD error_ = 0;
	DWORD len = 0;
	
	this->m_error_code = 0;
	if(this->m_handle == NULL)
		return -1;
	if(this->m_state.fOutxDsrFlow == TRUE)
	{
		DWORD sig_mask = 0;
		::GetCommModemStatus(this->m_handle,&sig_mask);
		if((sig_mask & MS_DSR_ON) != MS_DSR_ON)
			return -1;
	}
	
	if(!PurgeComm(this->m_handle,PURGE_TXABORT | PURGE_RXABORT |
		PURGE_TXCLEAR | PURGE_RXCLEAR))
	{
		this->m_error_code = ::GetLastError();
		return -1;
	}
	
	COMMTIMEOUTS time_out;
	time_out.ReadIntervalTimeout = MAXDWORD;
	time_out.ReadTotalTimeoutConstant = 0;
	time_out.ReadTotalTimeoutMultiplier = 0;
	time_out.WriteTotalTimeoutConstant = mTime
	time_out.WriteTotalTimeoutMultiplier = mTime;
	
	if(!::SetCommTimeouts(this->m_handle,&time_out))
	{
		this->m_error_code = ::GetLastError();
		return -1;
	}
	
	if(!ClearCommError(this->m_handle,&error_,&state))
	{
		this->m_error_code = ::GetLastError();
		return -1;
	}
	
	OVERLAPPED os_write;
	memset(&os_write,0,sizeof(OVERLAPPED));
	if((os_write.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL)
	{
		this->m_error_code = ::GetLastError();
		return -2;
	}
	if(!::WriteFile(this->m_handle,SndBuff,SndLen,&len,&os_write))
	{
		if((this->m_error_code = ::GetLastError()) == ERROR_IO_PENDING)
			::GetOverlappedResult(this->m_handle,&os_write,&len,TRUE);
	}
	this->m_error_code = ::GetLastError();
	::CloseHandle(os_write.hEvent);
	
	return len;
}

e、关闭串口

int ComDevice::CloseCom()
{
	this->m_error_code = 0;
	if(this->m_handle == NULL)
		return 0;
	if(!EscapeCommFunction(this->m_handle,CLRDTR))//关闭DTR信号
		this->m_error_code = GetLastError();
	if(!PurgeComm(this->m_handle,PURGE_TXABORT | PURGE_RXABORT |
		PURGE_TXCLEAR | PURGE_RXCLEAR))
	{
		this->m_error_code = ::GetLastError();
		//return -1;
	}
	if(! ::CloseHandle(this->m_handle))
	{
		this->m_error_code = ::GetLastError();
		return -2;
	}
	this->m_handle = NULL;
	return 0;
}

2、windows串口操作API介绍:

windows中串口和其它通信设备被当做文件处理,和linux差不多,串口的打开、读取、写入、关闭函数和文件操作函数一致。

a、串口打开函数
HANDLE CreateFile(LPCTSTR lpszName , DWORD fdwAccess , DWORD fdwShareMode , LPSECURITY_ATTRIBUTES lpsa , DWORD fwdCreate ,
DWORD fdwATTRsAndFlags , HANDLE hTemplateFile);
参数介绍:
lpszName :指定要打开的串口的逻辑名,用字符串表示,例如"COM1"、"COM2"
fdwAccess:指定串口的打开方式,读(GENERIC_READ)、写(GENERIC_WRITE),或者读写兼有(GENERIC_READ | GENERIC_WRITE)
fdwShareMode:指定串口的共享属性。对于不能共享的串口,必须设置为0
lpsa:引用安全性属性结构。设置为NULL时,系统为串口分配却横安全性属性
fwdCreate:指定如果CreateFile()正被其他文件调用时采取的动作。设置为OPEN_EXISTING表示不用创建新的串口句柄,直接打开已经存在的串口,并返回串口句柄
fdwATTRsAndFlags:描述串口的属性,设置为,FILE_FLAG_OVERLAPPED,表示串口I/O可以异步执行

hTemplateFile:hTemplateFile:指向模板文件的句柄,当端口处于打开状态时,不使用该参数,因而必须置成0

b、设置串口缓冲区大小
串口打开成功之后,调用SetupComm(),设置串口I/O缓冲区大小,函数原型如下:

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

c、关闭串口

CloseHandle(HANDLE hObject);

d、获取串口当前配置
BOOL GetCommState(HANDLE hFile, LPDCB lpDCB);
参数说明:
hFile:由CreateFile()函数返回的指向已打开串口的句柄。

• lpDCB:一个非常重要的结构—设备控制块DCB ( Device Control Block )。

DCB结构的主要参数说明如下:
• DCBLength: 一字节为单位指定的DCB结构的大小。
•Baudrate: 用于指定串口设备通信的数据传输速率,它可以是实际的数据传输速率数值,也可以是下列数据之一:CBR_110, CBR_19200, CBR_300, CBR_38400, CBR_600, CBR_56000, CBR_1200, CBR_57600, CBR_2400, CBR_115200, CBR_4800, CBR_12800, CBR_9600, CBR_25600, CBR_14400。
•fBinary: 指定是否允许二进制。Win32API不支持非二进制传输,因此这个参数必须设置为TRUE,如果设置为FALSE则不能正常工作。
•fParity: 指定是否允许奇偶校验,如果这个参数设置为TRUE,执行奇偶校验并报告错误信息。
•fOutxCtsFlow: 指定CTS是否用于检测发送流控制。当该成员为TRUE,而CTS为OFF时,发送将被挂起,直到CTS置ON。
•fOutxDsrFlow: 指定DSR是否用于检测发送流控制,当该成员为TRUE,而DSR为OFF时,发送将被挂起,直到DSR置ON。
•fDtrControl: 指定DTR流量控制
• fDsrSensitivity: 指定通信驱动程序对DTR信号线是否敏感,如果该位置设为TRUE时,DSR信号为OFF,接收的任何字节将被忽略。
•fTXContinueOnXoff: 指定当接收缓冲区已满,并且驱动程序已经发送出XoffChar字符时发送是否停止。当该成员为TRUE时,在接收缓冲区内接收到了缓冲区已满的字节XoffLim,并且驱动程序已经发送出XoffChar字符终止接收字节之后,发送继续进行。该成员为FALSE时,接收缓冲区接收到代表缓冲区已空的字节XonLim,并且驱动程序已经发送出恢复发送的XonChar字符后,发送可以继续进行。
•fOutX: 该成员为TRUE时,接收到XoffChar之后停止发送,接收到XonChar之后发送将重新开始。
•fInX: 该成员为TRUE时,接收缓冲区内接收到代表缓冲区满的字节XoffLim之后,XoffChar发送出去,接收缓冲区接收到代表缓冲区已空的字节XonLim之后,XonChar发送出去。
•fErrorChar: 当该成员为TRUE,并且fParity为TRUE时,就会用ErrorChar成员指定的字符来代替奇偶校验错误的接收字符。
•fNull: 指明是否丢弃接收到的NULL( ASCII 0 )字符,该成员为TRUE时,接收时去掉空(零值)字节;反之则不丢弃。
•fRtsControl: 指定 RTS 流量控制,可以取表2中的值。0值和DTR_CONTROL_HANDSHAKE等价。
•fAbortOnError: 如果发送错误,指定是否可以终止读、写操作。如果该位为TRUE,当发生错误时,驱动程序以出错状态终止所有的读写操作。只有当应用程序调用ClearCommError()函数处理后,串口才能接收随后的通信操作。
•fDummy2: 保留的位,没有使用。
•wReserved:没有使用,必须为零。
•XonLim: 指定在XOFF字符发送之前接收到缓冲区中可允许的最小字节数。
•XoffLim: 指定在XOFF字符发送之前缓冲区中可允许的最小可用字节数
•ByteSize: 指定端口当前使用的数据位数。
•Parity: 指定端口当前使用的奇偶校验方法。它的可能值如表3所示。
•StopBits: 指定串口当前使用的停止位数( ONESTOPBIT/ONE5STOPBITS / TWOSTOPBITS)
•XonChar: 指明发送和接收的XON字符值,它表明允许继续传输。
•XoffChar: 指明发送和接收的XOFF字符值,它表示暂停数据传输。
•ErrorChar: 本字符用来代替接收到的奇偶校验发生错误的字符。
•EofChar: 用来表示数据的结束。
•EvtChar: 事件字符。当接收到此字符的时候,会产生一个事件。
•wReserved1: 保留的位,没有使用。
e、设置串口信息
BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
调用GetCommState()获取串口设备控制块信息之后,可以修改DCB中相应信息,然后调用SetCommState()设置修改的DCB。
f、缓冲区控制
PurgeComm(HANDLE hFile , DWORD dwFlags)
dwFlags取值:
PURGE_TXABORT 即使发送操作没有完成,也终止所有的重叠发送操作,立即返回
PURGE_RXABORT 即使接收操作没有完成,也终止所有的重叠接收操作,立即返回
PURGE_TXCLEAR 清除发送缓冲区
PURGE_RXCLEAR 清除接收缓冲区

g、读串口操作

BOOLReadFile(HANDLE hFile , LPVOID lpBuffer , DWORD nNumberOfBytesToRead , LPDWORDlpNumberOfBytesRead , LPOVERLAPPED lpOverlapped)

参数介绍:

·hFile:指向标识的句柄。对串口来说,就是由CreateFile函数返回的句柄。该句柄必须拥有GENERIC_READ的权限。

·lpBuffer:指向一个缓冲区,该缓冲区主要用来存放从串口设备中读取的数据。

·nNumberOfBytesToRead:指定要从串口设备读取的字节数。

·lpNumberOfBytesRead:指向调用该函数读出的字节数。ReadFile()在读操作前,首先将其设置为0。Windows NT/2000中当lpOverlapped没有设置时,lpNumberOfBytesRead必须设置。当lpOverlapped设置时,lpNumberOfBytesRead可以不设置。这是可以调用GetOverlappedResult()函数获取实际的读取数值。Windows 9x中这个参数一定要设置。

·lpOverlapped:是一个OVERLAPPED的结构,该结构将在后面介绍。如果hFile以FILE_FLAG_OVERLAPPED方式常见,则需要此结构;否则,不需要此结构。 

h、写串口数据

BOOL WriteFile(HANDLE hFile , LPVOID lpBuffer , DWORDnNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWriten, LPOVERLAPPED lpOverlapped)

参数介绍:

·hFile:指向标识的句柄。对串口来说,就是由CreateFile函数返回的句柄。该句柄必须拥有GENERIC_READ的权限。

·lpBuffer:指向一个缓冲区,该缓冲区主要用来存放待发送数据。

·nNumberOfBytesToWrite:指定向串口写入的字节数。

·lpNumberOfBytesWriten:指向调用该函数已写入的字节数。WriteFile()在写操作前,首先将其设置为0。Windows NT/2000中当lpOverlapped没有设置时,lpNumberOfBytesWriten必须设置。当lpOverlapped设置时,lpNumberOfBytesWriten可以不设置。这是可以调用GetOverlappedResult()函数获取实际的写入数值。Windows 9x中这个参数一定要设置。

·lpOverlapped:是一个OVERLAPPED的结构,该结构将在后面介绍。如果hFile以FILE_FLAG_OVERLAPPED方式常见,则需要此结构;否则,不需要此结构。

i、异步I/O操作

OVERLAPPED结构:

typedef struct_OVERLAPPED{

         DWORD Iternal;//系统保留,指定一个和系统相关的状态

         DWORD IteralHigh;//系统保留,指出发送或接收数据长度

         DWORD Offset;//指定文件传送的起始位置

         DWORD OffsetHigh;//指定文件传送字节便宜的高位字

         HANDLE hEvent;//指定I/O操作完成后出发的事件

}OVERLAPPED; 

异步I/O可有GetOverlappedResult()函数获取操作结果

BOOLGetOverlappedResult(HANDLE hFile,LPOVERLAPPED lpOverlapped,LPDWORDlpNumberOfBytesTransfered,BOOL bWait);

 j、超时设置

超时结构体定义:

typedef struct_COMMTIMEOUTS{

         DWORD ReadIntervalTimeout;//指定通信线路上两个字符到达之间的最大时间间隔(ms)

         DWORD ReadTotalTimeoutMutiplier;//计算读操作总时间的系数(单位:ms)

         DWORD ReadTotalTimeoutConstant;//计算读操作总时间的常数(单位:ms)

         DWORD WriteTotalTimeoutMutiplier;//计算写操作总时间的系数(单位:ms)

         DWORD WriteTotalTimeoutCaonstant;//计算写操作总时间的常数(单位:ms)

} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

 获取当前超时参数:

BOOLGetCommTimeouts(HANDLE hFile,LPCOMMTIMEOUTS lpCommTimeouts)

设置超时参数:

BOOLSetCommTimeouts(HANDLE hFile,LPCOMMTIMEOUTS lpCommTimeouts)


你可能感兴趣的:(mfc,dll,vc++)