精简版CE串口类库

原创 2012年03月30日 16:52:16
#ifndef SERIAL_PORT_H
#define SERIAL_PORT_H

#include <afxwin.h> // MFC 核心组件和标准组件

/// <summary>
/// 描述:EVC串口类库
/// 日期:2012/3/30
/// </summary>
class CSerialPort : public CObject
{
public:
	CSerialPort()
	{
		m_hSerialPort = INVALID_HANDLE_VALUE;
		m_bWatchThreadLived = FALSE;
	}

	virtual ~CSerialPort()
	{
		Close(5000);
	}

	/// <summary>最大输入缓冲大小</summary>
	static CONST UINT32 s_MaxInQueue = 4096;

	/// <summary>最大输出缓冲大小</summary>
	static CONST UINT32 s_MaxOutQueue = 4096;

	/// <summary>获取系统错误消息</summary>
	/// <param name="psErrorMessage">输出的错误消息</param>
	static void GetLastError(CString& psErrorMessage)
	{
		if (DWORD dwLastError = ::GetLastError())
		{
			LPVOID lpMsgBuf;
			FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, dwLastError, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPTSTR) &lpMsgBuf, 0, NULL);
			psErrorMessage.Format(_T("(%d): %s"), dwLastError, (LPCTSTR)lpMsgBuf);
			LocalFree( lpMsgBuf );
		}
	}

	/// <summary>获取串口设备控制块</summary>
	/// <param name="lpDCB">输出的串口设备控制块</param>
	BOOL GetCommState(LPDCB lpDCB)
	{
		return ::GetCommState(m_hSerialPort, lpDCB);
	}

	/// <summary>设置串口设备控制块</summary>
	/// <param name="lpDCB">输入的串口设备控制块</param>
	BOOL SetCommState(LPDCB lpDCB)
	{
		return ::SetCommState(m_hSerialPort, lpDCB);
	}

	/// <summary>打开串口设备</summary>
	/// <param name="dwPort">端口号</param>
	/// <param name="dwBaudRate">波特率</param>
	/// <param name="byParity">奇偶校验</param>
	/// <param name="byByteSize">数据位</param>
	/// <param name="byStopBits">停止位</param>
	/// <example>CSerialPort::Open(1, 9600, 0, 8, 0)</example>
	/// <returns>是否打开成功</returns>
	virtual BOOL Open(DWORD dwPort, DWORD dwBaudRate = CBR_9600, BYTE byParity = NOPARITY,
		BYTE byByteSize = 8, BYTE byStopBits = ONESTOPBIT)
	{
		CString strPort;
		strPort.Format(_T("\\$device\\COM%d"), dwPort);
		return Open(strPort, dwBaudRate, byParity, byByteSize, byStopBits);
	}

	/// <summary>打开串口设备</summary>
	/// <param name="pszPort">端口名称</param>
	/// <param name="dwBaudRate">波特率</param>
	/// <param name="byParity">奇偶校验</param>
	/// <param name="byByteSize">数据位</param>
	/// <param name="byStopBits">停止位</param>
	/// <example>CSerialPort::Open(1, 9600, 0, 8, 0)</example>
	/// <returns>是否打开成功</returns>
	virtual BOOL Open(LPCTSTR pszPort, DWORD dwBaudRate = CBR_9600, BYTE byParity = NOPARITY,
		BYTE byByteSize = 8, BYTE byStopBits = ONESTOPBIT)
	{
		if (IsOpen()) return TRUE; // 防止重复创建串口句柄

		m_hSerialPort = CreateFile(pszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

		GetCommState(&m_tDeviceControlBlock);
		m_tDeviceControlBlock.BaudRate = dwBaudRate;
		m_tDeviceControlBlock.Parity = byParity;
		m_tDeviceControlBlock.ByteSize = byByteSize;
		m_tDeviceControlBlock.StopBits = byStopBits;
		m_tDeviceControlBlock.fParity = (byParity != NOPARITY);
		SetCommState(&m_tDeviceControlBlock); // 设置串口设备控制块
	
		SetupComm(m_hSerialPort, s_MaxInQueue, s_MaxOutQueue); // 初始化串口参数(不能在此处设置断言),即设置输入输出缓冲区大小
		
		GetCommTimeouts(m_hSerialPort, &m_tCommTimeOuts);		
		m_tCommTimeOuts.ReadIntervalTimeout = 100;// 字符读取间隔
		m_tCommTimeOuts.ReadTotalTimeoutMultiplier = 0;
		m_tCommTimeOuts.ReadTotalTimeoutConstant = 250;// 读取超时间隔
		m_tCommTimeOuts.WriteTotalTimeoutMultiplier = 0;
		m_tCommTimeOuts.WriteTotalTimeoutConstant = 250;// 写入超时间隔
		SetCommTimeouts(m_hSerialPort, &m_tCommTimeOuts); // 设置串口超时参数
	
		PurgeComm(m_hSerialPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); // 清空串口缓冲区数据

		m_bWatchThreadLived = TRUE;
		m_hWatchThread = CreateThread(NULL, 0, WatchThreadProc, this, 0, NULL); // 创建串口监视线程
	
		return TRUE;
	}
	
	/// <summary>关闭串口设备</summary>
	/// <returns>是否关闭成功</returns>
	virtual void Close(DWORD dwTimeout = 5000)
	{
		if (IsOpen())
		{
			m_bWatchThreadLived = FALSE;
			SetCommMask(m_hSerialPort, 0); // 停止监视串口接收事件

			PurgeComm(m_hSerialPort, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_RXABORT);  // 清空串口缓冲区数据

			if (WaitForSingleObject(m_hWatchThread, dwTimeout) != WAIT_OBJECT_0) 
			{
				TerminateThread(m_hWatchThread, WAIT_ABANDONED);
			}
			CloseHandle(m_hWatchThread);
			m_hWatchThread = NULL;

			CloseHandle(m_hSerialPort);
			m_hSerialPort = INVALID_HANDLE_VALUE;
		}		
	}
	
	/// <summary>获取串口设备状态</summary>
	/// <returns>是否已打开</returns>
	BOOL IsOpen()
	{
		return (m_hSerialPort != INVALID_HANDLE_VALUE);
	}
	
	/// <summary>将指定数量的数据写入串口</summary>
	/// <param name="pbyBuffer">输入缓冲区指针</param>
	/// <param name="dwOffset">输入缓冲区位置</param>
	/// <param name="dwCount">输入缓冲区大小</param>
	/// <returns>是否写入成功</returns>
	BOOL Write(const BYTE* pbyBuffer, DWORD dwOffset = 0, DWORD dwCount = 1)
	{
		ASSERT(IsOpen());

		DWORD dwCommError;
		if (ClearCommError(m_hSerialPort, &dwCommError, NULL) && dwCommError > 0)
			PurgeComm(m_hSerialPort, PURGE_TXABORT);

		DWORD dwBytesWritten;
		return WriteFile(m_hSerialPort, pbyBuffer + dwOffset, dwCount, &dwBytesWritten, NULL);
	}

	/// <summary>将指定数量的字符写入串口</summary>
	/// <param name="pszBuffer">输入缓冲区指针</param>
	/// <param name="dwOffset">输入缓冲区位置</param>
	/// <param name="dwCount">输入缓冲区大小</param>
	/// <returns>是否写入成功</returns>
	BOOL Write(LPCTSTR pszBuffer, DWORD dwOffset = 0, DWORD dwCount = 1)
	{
		return Write(pszBuffer, dwOffset * sizeof(TCHAR), dwCount * sizeof(TCHAR));
	}

	/// <summary>从串口读取指定数量的数据</summary>
	/// <param name="pbyBuffer">输出缓冲区指针</param>
	/// <param name="dwOffset">输出缓冲区位置</param>
	/// <param name="dwCount">输出缓冲区大小</param>
	/// <returns>是否读取成功</returns>
	BOOL Read(LPBYTE pbyBuffer, DWORD dwOffset = 0, DWORD dwCount = 1)
	{
		ASSERT(IsOpen());

		DWORD dwCommError;
		if (ClearCommError(m_hSerialPort, &dwCommError, NULL) && dwCommError > 0)
			PurgeComm(m_hSerialPort, PURGE_RXABORT);

		DWORD dwBytesRead;
		return ReadFile(m_hSerialPort, pbyBuffer + dwOffset, dwCount, &dwBytesRead, NULL);
	}

	/// <summary>从串口读取指定数量的字符</summary>
	/// <param name="pszBuffer">输出缓冲区指针</param>
	/// <param name="dwOffset">输出缓冲区位置</param>
	/// <param name="dwCount">输出缓冲区大小</param>
	/// <returns>是否读取成功</returns>
	BOOL Read(LPTSTR pszBuffer, DWORD dwOffset = 0, DWORD dwCount = 1)
	{
		return Read(pszBuffer, dwOffset * sizeof(TCHAR), dwCount * sizeof(TCHAR));
	}

	/// <summary>监视串口数据收发线程处理函数</summary>
	/// <returns>线程退出代码</returns>
	static DWORD WINAPI WatchThreadProc(LPVOID lpParam)
	{
		CSerialPort* pSender = (CSerialPort*) lpParam;
		
		return pSender->WatchThreadProc();
	}

	/// <summary>监视串口数据收发线程处理函数</summary>
	/// <returns>线程退出代码</returns>
	virtual DWORD WatchThreadProc()
	{
		ASSERT(IsOpen());

		SetCommMask(m_hSerialPort, EV_RXCHAR); // 设置监视串口数据接收事件

		DWORD dwCommEvent; // 串口事件掩码
		DWORD dwCommError;
		COMSTAT tComStat; // 串口设备数据
		while (m_bWatchThreadLived)
		{
			if (WaitCommEvent(m_hSerialPort, &dwCommEvent, NULL))
			{
				if (dwCommEvent & EV_RXCHAR)
				{
					if (ClearCommError(m_hSerialPort, &dwCommError, &tComStat)) // 获取串口错误信息
					{
						if (tComStat.cbInQue)
						{
							DataReceived(tComStat.cbInQue);
						}
					}
				}
			}
		}
		return 0; // 线程正常退出
	}

	/// <summary>串口接收事件处理函数</summary>
	/// <param name="dwBytesSize">接收到的数据大小</param>
	virtual void DataReceived(int nBytesSize) { ; }

private:
	/// <summary>串口设备句柄</summary>
	HANDLE m_hSerialPort;

	/// <summary>串口超时参数</summary>
	COMMTIMEOUTS m_tCommTimeOuts;

	/// <summary>串口设备控制块</summary>
	DCB m_tDeviceControlBlock;

	/// <summary>收发线程句柄</summary>
	HANDLE m_hWatchThread;

	/// <summary>收发线程是否活动</summary>
	BOOL m_bWatchThreadLived;
};

#endif // SERIAL_PORT_H

相关文章推荐

Hololens官方教程精简版 - 08. Sharing holograms(共享全息影像)

前言 注意:本文已更新到5.5.1f1版本 本篇集中学习全息影像“共享”的功能,以实现在同一房间的人,看到“同一个物体”。之所以打引号,是因为,每个人看到的并非同一个物体,只是空间位置等信...

创业圣经《从0到1》读书笔记精简版(首发虎嗅今日头条)

对比上一篇比较完整的个人笔记整理,本文是属于精简版本。首发虎嗅,并纳入头条,大家看今天虎嗅的微信公众号,或者到虎嗅官网首页头条就能看到。希望CSDN上面的朋友也会喜欢。...

解决(精简版Excel+2007)中,不能跨表引用公式和复制问题

 针对目前部分财务电脑在装Ghost Win 7后,系统里装的大部分都是完美者精减版Office 2007。这个版本中,目前发现两个重要错误,我们在这里提供解决的方法。 (原因:从office...

Hololens官方教程精简版 - 03. Gaze(凝视)

前言个人建议,学习Holograms 210之前,一定完成《Hololens官方教程精简版 - 02. Introduction with Device》的学习。本篇集中学习凝视功能,完成以下目标: ...

Android友盟分享集成微信QQ微博分享demo精简版

最近做项目用到了分享功能,介于友盟的全家桶,于是放弃shareSDK,选择友盟,虽然是完全按照友盟集成文档来做的,但是总是有些不顺畅,不能一下就搞定,这里记录一下过程,与遇到的坑 1.项目准备工...
  • K_Men
  • K_Men
  • 2016年10月25日 17:49
  • 3340

linux上FTP配置(精简版)

FTP简单配置,仅个人参考

精简版开发工具使用手记(图解)

以下记录一些精简版开发工具的使用情况。C#的精简开发工具SharpDevelop,VC++ 6.0绿色版,Java的开发工具JCreator,...。 一 SharpDevelop 5.0    ...

Delphi XE8,C++ Builder XE8,RAD Studio XE8 精简版 EMBARCADERO DELPHI XE8 LITE V11.X

DELPHI XE8 精简版 EMBARCADERO DELPHI XE8 LITE V11.X 版本说明: 由于 XE5 时代 Delphi 安装体积急剧膨胀(完整安装接近 10G,程序文件、安装...
  • MaxWoods
  • MaxWoods
  • 2015年04月16日 22:57
  • 10881

http之各大知识点总结精简版(url、状态码、头信息等)

http有如下知识点需要了解 一.http主要工作原理和概念描述 1.HTTP协议,即超文本传输协议,是服务器与本地浏览器之间信息传输的协议,基于TCP/IP通信协议。客户端向服务器发起请求时...

U大师U盘启动盘制作教程 详细图解步骤教你怎么装统(Win7PE精简版)

还在为重装系统发愁吗?还在为没有光驱,或是没有系统光盘而烦恼吗?未解决广大计算机用户的烦恼,U大师推出一款制作启动U盘的软件工具——U大师-U盘启动盘制作工具,可完成U盘一键安装系统的制作,可实现GH...
  • xlsxu
  • xlsxu
  • 2013年01月04日 14:59
  • 223
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:精简版CE串口类库
举报原因:
原因补充:

(最多只允许输入30个字)