串口类

// SerialPort.h: interface for the CSerialPort class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_SERIALPORT_H__BCE7D852_C6B1_4AEE_925C_1485DBF44011__INCLUDED_)
#define AFX_SERIALPORT_H__BCE7D852_C6B1_4AEE_925C_1485DBF44011__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <afxmt.h>

typedef struct
{
	DWORD	dwPortNum;			/*  端口号		 */
	DWORD	dwBaudRate;			/*  波特率		 */
	BOOL	bParity;			/*  奇偶校验类型 */
	DWORD	dwNumberOfDataBit;	/*  数据位数目	 */
	DWORD	dwNumberOfStopBit;	/*  停止位数目	 */
	BOOL	bFluxControlType;	/*  流控方式	 */
}SerialPortInfo;

class CSerialPort  
{
	public:
		CEvent				m_EventComPort;
		CEvent				m_EventPurge;
		CCriticalSection	m_SectionLock;
	public:
		CSerialPort();
	
		//初始化串口
		BOOL Init(SerialPortInfo* stPortInfo);

		//取得串口当前配置
		void  GetState(DCB& dcb);
		//配置串口
		void  SetState(DCB& dcb);

		//打开串口
		BOOL OpenPort(SerialPortInfo* stPortInfo);
		//关闭串口
		BOOL ClosePort();

		BOOL Purge();

		//下发数据
		BOOL Write(BYTE* pInBuf,DWORD dwBufLen,DWORD* pdwStatus);
		//接收数据
		BOOL Read(BYTE* pOutBuf,DWORD dwBufLen,DWORD *pdwRealLen,DWORD* pdwStatus);

		DWORD	GetAvailableDataLen();
		BOOL	BeginListenCommEvent();
		BOOL	WaiteForAEvent();

		//获取最近一个错误码
		BOOL GetPortStatus()
		{
			return m_bPortStatus;
		}

		//设置串口状态
		void SetPortStatus(BOOL bPortStatus)
		{
			m_bPortStatus = bPortStatus;
		}

		//获取串口状态
		DWORD GetLastErrorCode()
		{
			return m_dwLastErrorCode;
		}
	
		virtual ~CSerialPort();
	private:
		
		HANDLE m_hPortHandle;

		DWORD m_dwLastErrorCode;
		BOOL m_bPortStatus;
};

#endif // !defined(AFX_SERIALPORT_H__BCE7D852_C6B1_4AEE_925C_1485DBF44011__INCLUDED_)

// SerialPort.cpp: implementation of the CSerialPort class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PortAccess.h"
#include "SerialPort.h"
#include "GloableDefine.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSerialPort::CSerialPort()
{
	m_dwLastErrorCode = ERROR_CODE_NO_ERROR;
	m_bPortStatus = FALSE;
	m_hPortHandle = NULL;
}

CSerialPort::~CSerialPort()
{
	if (m_hPortHandle)
	{
		CloseHandle(m_hPortHandle);
	}
}

BOOL CSerialPort:: Init(SerialPortInfo* stPortInfo)
{
	BOOL bRet = TRUE;

	CString strPortName = "";
	strPortName.Format("\\\\.\\COM%d", stPortInfo->dwPortNum);
	try
	{
		m_hPortHandle = CreateFile(	strPortName,
									GENERIC_READ|GENERIC_WRITE,
									0,
									NULL,
									OPEN_EXISTING,
									FILE_FLAG_OVERLAPPED,
									NULL);
		if (INVALID_HANDLE_VALUE == m_hPortHandle)
		{
			m_dwLastErrorCode = ERROR_CODE_INIT_PORT_FAILD;
			bRet = FALSE;
		}
		
		SetCommMask(m_hPortHandle, EV_RXCHAR);
		
		DCB dcb;
		GetState(dcb);
		
		dcb.BaudRate		= stPortInfo->dwBaudRate;
		dcb.Parity			= NOPARITY;
		dcb.ByteSize		= 8;
		dcb.StopBits		= ONESTOPBIT;
		dcb.fDsrSensitivity = FALSE;
		dcb.fOutxCtsFlow	= FALSE;
		dcb.fOutxDsrFlow	= FALSE;
		dcb.fOutX			= FALSE;
		dcb.fInX			= FALSE;
		
		SetState(dcb);
	}
	catch (DWORD dwError)
	{
		m_dwLastErrorCode = dwError;
		bRet = FALSE;
	}

	if (bRet)
	{
		SetPortStatus(TRUE);
	}
	return bRet;
}

void  CSerialPort::GetState(DCB& dcb)
{
	if(GetCommState(m_hPortHandle, &dcb) == 0) //function failed
	{
		throw COMERR_GETSTATE;
	}
}
void  CSerialPort::SetState(DCB& dcb)
{
	if(SetCommState(m_hPortHandle, &dcb) == 0) //function failed
	{
		throw COMERR_SETSTATE;
	}
}

BOOL CSerialPort::Purge()
{
	if(m_hPortHandle != INVALID_HANDLE_VALUE)
	{
		m_EventPurge.SetEvent();		
		m_SectionLock.Lock();
		PurgeComm(m_hPortHandle, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
		m_SectionLock.Unlock();
	}
	return TRUE;
}

BOOL CSerialPort::Read(BYTE* pOutBuf,DWORD dwBufLen,DWORD *pdwRealLen,DWORD* pdwStatus)
{
	OVERLAPPED OverLap;
	DWORD dwNoUse;
	BOOL bRet;

	memset(&OverLap, 0, sizeof(OverLap));

	for(;;)
	{
		m_SectionLock.Lock();	//

		if(GetAvailableDataLen() >= dwBufLen)
		{
			break;
		}
		
		BeginListenCommEvent();

		for(;;)
		{
			try
			{
				m_SectionLock.Unlock();
				break;
			}
			catch(...)
			{
				m_dwLastErrorCode = ERROR_CODE_UNKNOW;
			}
		}

		if(WaiteForAEvent() == FALSE)
		{
			if(pdwRealLen)
			{
				*pdwRealLen = 0;
			}
			return TRUE;
		}
	}
	//!!注,上面的等待理论上是有漏洞的,如果操作系统的串口缓冲区长度不到dwBufferLen,
	//则这个等待将永远不会返回。
	//m_SectionLock.Lock();
	bRet = ReadFile(m_hPortHandle, pOutBuf, dwBufLen, &dwNoUse, &OverLap);

	if(FALSE == bRet)
	{
		//如果发生错误
		if(::GetLastError() != ERROR_IO_PENDING)
		{
			m_SectionLock.Unlock();
			return FALSE;
		}
		else
		{
			//如果读操作正在进行
			if(GetOverlappedResult(m_hPortHandle, &OverLap, &dwNoUse, TRUE) == FALSE)
			{
				m_SectionLock.Unlock();
				return FALSE;
			}
		}
	}

	m_SectionLock.Unlock();


	if(pdwRealLen)
	{
		*pdwRealLen = dwBufLen;
	}
	return TRUE;
}

BOOL CSerialPort::Write(BYTE* pInBuf,DWORD dwBufLen,DWORD* pdwStatus)
{
	OVERLAPPED OverLap;
	DWORD dwWriteLen, dwNoUse;	
	memset(&OverLap, 0, sizeof(OverLap));
	
	m_SectionLock.Lock();
	BOOL bRet = WriteFile(m_hPortHandle, pInBuf, dwBufLen, &dwWriteLen, &OverLap);
	
	if(FALSE == bRet)
	{
		if(::GetLastError() != ERROR_IO_PENDING)
		{
			m_SectionLock.Unlock();
			return FALSE;
		}
		if(GetOverlappedResult(m_hPortHandle, &OverLap, &dwNoUse, TRUE) == FALSE)
		{
			m_SectionLock.Unlock();
			return FALSE;
		}
	}
	m_SectionLock.Unlock();
	return TRUE;
}

BOOL CSerialPort::OpenPort(SerialPortInfo* stPortInfo)
{
	return Init(stPortInfo);
}

BOOL CSerialPort::ClosePort()
{
	if(m_hPortHandle != INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hPortHandle);
		m_hPortHandle = INVALID_HANDLE_VALUE;
		SetPortStatus(FALSE);
	}
	return TRUE;
}

DWORD CSerialPort::GetAvailableDataLen()
{
	COMSTAT ComState;
	DWORD dwError;
	
	BOOL bRet = ClearCommError(m_hPortHandle, &dwError, &ComState);
	if(FALSE == bRet)
	{
		return DWORD(0);
	}
	
	return ComState.cbInQue;
}

BOOL CSerialPort::BeginListenCommEvent()
{
	DWORD dwEvent;
	OVERLAPPED	OverLap;
	
	memset(&OverLap, 0, sizeof(OverLap));
	OverLap.hEvent = m_EventComPort.m_hObject;
	
	BOOL bRet = WaitCommEvent(m_hPortHandle, &dwEvent, &OverLap);
	if(FALSE == bRet)
	{
		if(::GetLastError() != ERROR_IO_PENDING)
		{
			return FALSE;
		}
	}
	
	return TRUE;
}

BOOL CSerialPort::WaiteForAEvent()
{
	DWORD dwRet;
	
	HANDLE hEvent[] = {HANDLE(m_EventComPort), HANDLE(m_EventPurge)};
	for(;;)
	{
		dwRet = WaitForMultipleObjects(2, hEvent, FALSE, INFINITE);
		
		if(dwRet - WAIT_OBJECT_0  == 1)
		{
			return FALSE;
		}
		else if(dwRet - WAIT_OBJECT_0  == 0)
		{
			return TRUE;
		}
	}
}

CSerialPort First Version by Remon Spekreijse on 2000-02-08 http://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm Second Version by mrlong on 2007-12-25 https://code.google.com/p/mycom/ 增加 ClosePort 增加 WriteToPort 两个方法 增加 SendData 与 RecvData 方法 by liquanhai on 2011-11-04 http://blog.youkuaiyun.com/liquanhai/article/details/4955253 增加 ClosePort 中交出控制权,防止死锁问题 by liquanhai on 2011-11-06 http://blog.youkuaiyun.com/liquanhai/article/details/6941574 增加 ReceiveChar 中防止线程死锁 by viruscamp on 2013-12-04 https://github.com/viruscamp/CSerialPort 增加 IsOpen 判断是否打开 修正 InitPort 中 parity Odd Even 参数取值错误 修改 InitPort 中 portnr 取值范围,portnr>9 时特殊处理 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的 增加用户消息编号自定义,方法来自 CnComm by itas109 on 2014-01-10 http://blog.youkuaiyun.com/itas109/article/details/18358297 解决COM10以上端口无法显示的问题 扩展可选择端口,最大值MaxSerialPortNum可以自定义 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。 by liquanhai on 2014-12-18 增加一些处理措施,主要是对减少CPU占用率 by itas109 on 2016-05-07 http://blog.youkuaiyun.com/itas109 修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。 解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。 参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea 修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性 by itas109 on 2016-06-22 http://blog.youkuaiyun.com/itas109 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。 解决ReceiveChar只能接收单个字符的问题。 by itas109 on 2016-06-29 http://blog.youkuaiyun.com/itas109 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。 将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。 增加IsThreadSuspend方法,用于判断线程是否挂起。 改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收 by itas109 on 2016-08-02 http://blog.youkuaiyun.com/itas109 https://github.com/itas109 改进IsOpen方法,m_hComm增加INVALID_HANDLE_VALUE的情况,因为CreateFile方法失败返回的是INVALID_HANDLE_VALUE,不是NULL 改进ClosePort方法:增加串口句柄无效的判断(防止关闭死锁);m_hWriteEvent不使用CloseHandle关闭 改进CommThread、ReceiveChar、ReceiveStr和WriteChar方法中异常处理的判断,增加三种判断:串口打开失败(error code:ERROR_INVALID_HANDLE)、连接过程中非法断开(error code:ERROR_BAD_COMMAND)和拒绝访问(error code:ERROR_ACCESS_DENIED) 采用安全函数sprintf_s和strcpy_s函数替换掉sprintf和strcpy 改进QueryKey方法,用于查询注册表的可用串口值,可以搜索到任意的可用串口 改进InitPort方法,串口打开失败,增加提示信息:串口不存在(error code:ERROR_FILE_NOT_FOUND)和串口拒绝访问(error code:ERROR_ACCESS_DENIED) 加入viruscamp 取消对 MFC 的依赖 改进InitPort方法,如果上次串口是打开,再次调用InitPort方法,关闭串口需要做一定的延时,否则有几率导致ERROR_ACCESS_DENIED拒绝访问,也就是串口占用问题 初始化默认波特率修改为9600 修复一些释放的BUG 规范了一些错误信息,参考winerror.h -- error code definitions for the Win32 API functions 删除SendData和RecvData方法 by itas109 on 2016-08-10 http://blog.youkuaiyun.com/itas109 https://github.com/itas109 改进ReceiveStr方法,comstat.cbInQue = 0xcccccccc的情况(如串口异常断开),会导致RXBuff初始化失败 by itas109 on 2017-02-14 http://blog.youkuaiyun.com/itas109 https://github.com/itas109 兼容ASCII和UNICODE编码 ReceiveStr函数中发送函数SendMessage的第二个参数采用结构体形式,包括portNr串口号和bytesRead读取的字节数,可以处理16进制的时候0x00截断问题 精简不必要的函数SendData和RecvData 尽量的取消对 MFC 的依赖,Hkey2ComboBox函数暂时保留 其他小问题修改 博客:blog.youkuaiyun.com/itas109 Email:itas109@qq.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值