C++ and MFC SerialPort X64 platform

55 篇文章 0 订阅
4 篇文章 0 订阅

you can see on the Internet is based on the MFC serial port of X86 platform.

Then,I will  introduce the method of the X64 platform on the MFC to achieve the serialport demo.Controls that do not use MFC.

  • (1) download the SerialPort source code,I also post code on my blog,you can refer to the following code:
  • SerialPort.h
/*
**	FILENAME			CSerialPort.h
**
**	PURPOSE				This class can read, write and watch one serial port.
**						It sends messages to its owner when something happends on the port
**						The class creates a thread for reading and writing so the main
**						program is not blocked.
**
**	CREATION DATE		15-09-1997
**	LAST MODIFICATION	12-11-1997
**
**	AUTHOR				Remon Spekreijse
**
**
************************************************************************************
**  author: mrlong date:2007-12-25
**
**  改进
**	1) 增加 ClosePort
**	2) 增加 WriteToPort 两个方法
**	3) 增加 SendData 与 RecvData 方法
**************************************************************************************
***************************************************************************************
**  author:liquanhai date:2011-11-06
**
**  改进
**	1) 增加 ClosePort 中交出控制权,防止死锁问题
**	2) 增加 ReceiveChar 中防止线程死锁
**************************************************************************************
***************************************************************************************
**  author:viruscamp date:2013-12-04
**
**  改进
**	1) 增加 IsOpen 判断是否打开
**	2) 修正 InitPort 中 parity Odd Even 参数取值错误
**	3) 修改 InitPort 中 portnr 取值范围,portnr>9 时特殊处理
**	4) 取消对 MFC 的依赖,使用 HWND 替代 CWnd,使用 win32 thread 函数而不是 MFC 的
**	5) 增加用户消息编号自定义,方法来自 CnComm
***************************************************************************************
***************************************************************************************
**  author: itas109  date:2014-01-10
**  Blog:blog.csdn.net/itas109
**
**  改进
**    1) 解决COM10以上端口无法显示的问题
**    2) 扩展可选择端口,最大值MaxSerialPortNum可以自定义
**    3) 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。
***************************************************************************************
**  author: itas109  date:2016-05-06
**  Blog:blog.csdn.net/itas109
**
**  改进
**    1) 修复每次打开串口发送一次,当串口无应答时,需要关闭再打开或者接收完数据才能发送的问题。
**		 解决办法:在m_hEventArray中调整m_hWriteEvent的优先级高于读的优先级。CommThread(LPVOID pParam)函数中读写的位置也调换。
**		 参考:http://zhidao.baidu.com/link?url=RSrbPcfTZRULFFd2ziHZPBwnoXv1iCSu_Nmycb_yEw1mklT8gkoNZAkWpl3UDhk8L35DtRPo5VV5kEGpOx-Gea
**    2) 修复停止位在头文件中定义成1导致SetCommState报错的问题,应为1对应的停止位是1.5。UINT stopsbits = ONESTOPBIT
**    3) switch(stopbits)和switch(parity)增加默认情况,增强程序健壮性
** ***************************************************************************************
**  author: itas109  date:2016-06-22
**  Blog:blog.csdn.net/itas109
**
**  改进
**  1) 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。
**      解决ReceiveChar只能接收单个字符的问题。
** ***************************************************************************************
**  author: itas109  date:2016-06-29
**  Blog:blog.csdn.net/itas109
**
**  改进
**  1) 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。
**		将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。
**	2) 增加IsThreadSuspend方法,用于判断线程是否挂起。
**	3) 改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。
**  4) 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收
** ***************************************************************************************
**  author: itas109  date:2016-08-02
**  Blog:blog.csdn.net/itas109
**  改进
**  1) 改进IsOpen方法,m_hComm增加INVALID_HANDLE_VALUE的情况,因为CreateFile方法失败返回的是INVALID_HANDLE_VALUE,不是NULL
**  2) 改进ClosePort方法:增加串口句柄无效的判断(防止关闭死锁);m_hWriteEvent不使用CloseHandle关闭
**  3) 改进CommThread、ReceiveChar、ReceiveStr和WriteChar方法中异常处理的判断,增加三种判断:串口打开失败(error code:ERROR_INVALID_HANDLE)、连接过程中非法断开(error code:ERROR_BAD_COMMAND)和拒绝访问(error code:ERROR_ACCESS_DENIED)
**  4) 采用安全函数sprintf_s和strcpy_s函数替换掉sprintf和strcpy
**  5) 改进QueryKey方法,用于查询注册表的可用串口值,可以搜索到任意的可用串口
**  6) 改进InitPort方法,串口打开失败,增加提示信息:串口不存在(error code:ERROR_FILE_NOT_FOUND)和串口拒绝访问(error code:ERROR_ACCESS_DENIED)
**  7) 加入viruscamp 取消对 MFC 的依赖
**  8) 改进InitPort方法,如果上次串口是打开,再次调用InitPort方法,关闭串口需要做一定的延时,否则有几率导致ERROR_ACCESS_DENIED拒绝访问,也就是串口占用问题
**  9) 初始化默认波特率修改为9600
**  10)修复一些释放的BUG
**  11)规范了一些错误信息,参考winerror.h --  error code definitions for the Win32 API functions
** ***************************************************************************************
**  author: itas109  date:2016-08-10
**  Blog:blog.csdn.net/itas109
**  改进
**  1) 改进ReceiveStr方法,comstat.cbInQue = 0xcccccccc的情况(如串口异常断开),会导致RXBuff初始化失败
** ***************************************************************************************
**  author: itas109  date:2017-02-14
**  Blog:blog.csdn.net/itas109
**  改进
**  1)  兼容ASCII和UNICODE编码
**  2)  ReceiveStr函数中发送函数SendMessage的第二个参数采用结构体形式,包括portNr串口号和bytesRead读取的字节数,可以处理16进制的时候0x00截断问题
**  3)  精简不必要的函数SendData和RecvData
**  4)  尽量的取消对 MFC 的依赖,Hkey2ComboBox函数暂时保留
**  5)  其他小问题修改
** ***************************************************************************************
**  author: itas109  date:2017-03-12
**  Blog:blog.csdn.net/itas109
**  改进
**  1)  增加宏定义_AFX,用于处理MFC的必要函数Hkey2ComboBox
**  2)  进一步去除MFC依赖,修改AfxMessageBox函数
** ***************************************************************************************
**  author: itas109  date:2017-12-16
**  Blog:blog.csdn.net/itas109
**  改进
**	1)	支持DLL输出
**  2)  去除QueryKey和Hkey2ComboBox,采用CSerialPortInfo::availablePorts()函数代替
**  3)  增加CSerialPortInfo类,目前只有availablePorts静态函数,用于获取活跃的串口到list
**  4)  增加命名空间itas109
**  5)  精简不必要的头文件
**  6)	InitPort和~CSerialPort()中直接整合ClosePort()
** ***************************************************************************************
**  author: itas109  date:2018-02-14
**  Blog:blog.csdn.net/itas109
**  改进
**	1)	★修复不能连续发送的问题 ★ fix can not continue send error
**  2)  ★一次性写入尽可能多的数据到串口 ★ try best to send mutil data once in WriteChar funtion
**  3)  修复BYTE内存设置的问题 fix BYTE memset error
**  4)  在构造函数中初始化和释放临界区 initialize and delete critical section in Constructor
**  5)  精简代码
*/

#ifndef __CSERIALPORT_H__
#define __CSERIALPORT_H__


#include "stdio.h"
#include "tchar.h"

#include "Windows.h"

#include <string>
#include <list>

struct serialPortInfo
{
	UINT portNr;//串口号
	DWORD bytesRead;//读取的字节数
};

struct serialPortBuffer
{
	int len;
	PBYTE buffer;
};

#ifndef WM_COMM_MSG_BASE 
#define WM_COMM_MSG_BASE		    WM_USER + 109		//!< 消息编号的基点  
#endif

#define WM_COMM_BREAK_DETECTED		WM_COMM_MSG_BASE + 1	// A break was detected on input.
#define WM_COMM_CTS_DETECTED		WM_COMM_MSG_BASE + 2	// The CTS (clear-to-send) signal changed state. 
#define WM_COMM_DSR_DETECTED		WM_COMM_MSG_BASE + 3	// The DSR (data-set-ready) signal changed state. 
#define WM_COMM_ERR_DETECTED		WM_COMM_MSG_BASE + 4	// A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 
#define WM_COMM_RING_DETECTED		WM_COMM_MSG_BASE + 5	// A ring indicator was detected. 
#define WM_COMM_RLSD_DETECTED		WM_COMM_MSG_BASE + 6	// The RLSD (receive-line-signal-detect) signal changed state. 
#define WM_COMM_RXCHAR				WM_COMM_MSG_BASE + 7	// A character was received and placed in the input buffer. 
#define WM_COMM_RXFLAG_DETECTED		WM_COMM_MSG_BASE + 8	// The event character was received and placed in the input buffer.  
#define WM_COMM_TXEMPTY_DETECTED	WM_COMM_MSG_BASE + 9	// The last character in the output buffer was sent.  
#define WM_COMM_RXSTR               WM_COMM_MSG_BASE + 10   // Receive string

#define MaxSerialPortNum 200   ///有效的串口总个数,不是串口的号 //add by itas109 2014-01-09
#define IsReceiveString  1     //采用何种方式接收:ReceiveString 1多字符串接收(对应响应函数为Wm_SerialPort_RXSTR),ReceiveString 0一个字符一个字符接收(对应响应函数为Wm_SerialPort_RXCHAR)

namespace itas109{
	class _declspec(dllexport) CSerialPort
	{
	public:
		// contruction and destruction
		CSerialPort();
		virtual ~CSerialPort();

		// port initialisation		
		// UINT stopsbits = ONESTOPBIT   stop is index 0 = 1 1=1.5 2=2 
		// 切记:stopsbits = 1,不是停止位为1。
		// by itas109 20160506
		BOOL		InitPort(HWND pPortOwner, UINT portnr = 1, UINT baud = 9600,
			TCHAR parity = _T('N'), UINT databits = 8, UINT stopsbits = ONESTOPBIT,
			DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512,

			DWORD ReadIntervalTimeout = 1000,
			DWORD ReadTotalTimeoutMultiplier = 1000,
			DWORD ReadTotalTimeoutConstant = 1000,
			DWORD WriteTotalTimeoutMultiplier = 1000,
			DWORD WriteTotalTimeoutConstant = 1000);

		// start/stop comm watching
		///控制串口监视线程
		BOOL		 StartMonitoring();//开始监听
		BOOL		 ResumeMonitoring();//恢复监听
		BOOL		 SuspendMonitoring();//挂起监听
		BOOL         IsThreadSuspend(HANDLE hThread);//判断线程是否挂起 //add by itas109 2016-06-29

		DWORD		 GetWriteBufferSize();///获取写缓冲大小
		DWORD		 GetCommEvents();///获取事件
		DCB			 GetDCB();///获取DCB

		///写数据到串口
		void		WriteToPort(char* string, size_t n); // add by mrlong 2007-12-25
		void		WriteToPort(PBYTE Buffer, size_t n);// add by mrlong
		void		ClosePort();					 // add by mrlong 2007-12-2  
		BOOL		IsOpened();

		std::string GetVersion();

	protected:
		// protected memberfunctions
		void		ProcessErrorMessage(TCHAR* ErrorText);///错误处理
		static DWORD WINAPI CommThread(LPVOID pParam);///线程函数
		static void	ReceiveChar(CSerialPort* port);
		static void ReceiveStr(CSerialPort* port); //add by itas109 2016-06-22
		static void	WriteChar(CSerialPort* port);

	private:
		// thread
		HANDLE			    m_Thread;
		BOOL                m_bIsSuspened;///thread监视线程是否挂起

		// synchronisation objects
		CRITICAL_SECTION	m_csCommunicationSync;///临界资源
		BOOL				m_bThreadAlive;///监视线程运行标志

		// handles
		HANDLE				m_hShutdownEvent;  //stop发生的事件
		HANDLE				m_hComm;		   // 串口句柄 
		HANDLE				m_hWriteEvent;	 // write

		// Event array. 
		// One element is used for each event. There are two event handles for each port.
		// A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).
		// There is a general shutdown when the port is closed. 
		///事件数组,包括一个写事件,接收事件,关闭事件
		///一个元素用于一个事件。有两个事件线程处理端口。
		///写事件和接收字符事件位于overlapped结构体(m_ov.hEvent)中
		///当端口关闭时,有一个通用的关闭。
		HANDLE				m_hEventArray[3];

		// structures
		OVERLAPPED			m_ov;///异步I/O
		COMMTIMEOUTS		m_SerialPortTimeouts;///超时设置
		DCB					m_dcb;///设备控制块

		// owner window
		HWND				m_pOwner;

		// misc
		UINT				m_nPortNr;		///串口号
		PBYTE				m_szWriteBuffer;///写缓冲区
		std::list<serialPortBuffer> m_bufferList;
		serialPortBuffer m_bufferStruct;
		DWORD				m_dwCommEvents;
		DWORD				m_nWriteBufferSize;///写缓冲大小

		size_t				m_nWriteSize;//写入字节数 //add by mrlong 2007-12-25
	};

	class _declspec(dllexport) CSerialPortInfo
	{
	public:
		CSerialPortInfo();
		~CSerialPortInfo();

		static std::list<std::string> availablePorts();
	};
};

#endif __CSERIALPORT_H__
  • SerialPort.cpp
/*
**	FILENAME			CSerialPort.cpp
**
**	PURPOSE				This class can read, write and watch one serial port.
**						It sends messages to its owner when something happends on the port
**						The class creates a thread for reading and writing so the main
**						program is not blocked.
**
**	CREATION DATE		15-09-1997
**	LAST MODIFICATION	12-11-1997
**
**	AUTHOR				Remon Spekreijse
**
**  2007-12-25 mrlong    https://code.google.com/p/mycom/
**  2011-11-06 liquanhai http://blog.csdn.net/liquanhai/article/details/6941574
**  2013-12-04 viruscamp https://github.com/viruscamp
**  2014-01-10 itas109   http://blog.csdn.net/itas109
**  2014-12-18 liquanhai http://blog.csdn.net/liquanhai/article/details/6941574
**  2016-05-06 itas109   http://blog.csdn.net/itas109
**  2016-06-22 itas109   http://blog.csdn.net/itas109
**  2016-06-29 itas109   http://blog.csdn.net/itas109
**  2016-08-02 itas109   http://blog.csdn.net/itas109
**  2016-08-10 itas109   http://blog.csdn.net/itas109
**  2017-02-14 itas109   http://blog.csdn.net/itas109
**  2017-03-12 itas109   http://blog.csdn.net/itas109
**  2017-12-16 itas109   http://blog.csdn.net/itas109
**  2017-02-14 itas109   http://blog.csdn.net/itas109
*/

#include "SerialPort.h"
#include "assert.h"


using namespace itas109;

//获取注册表指定数据到list
bool getRegKeyValues(std::string regKeyPath, std::list<std::string> & portsList)
{
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383

	HKEY hKey;

	TCHAR		achValue[MAX_VALUE_NAME];					// buffer for subkey name
	DWORD		cchValue = MAX_VALUE_NAME;					// size of name string 
	TCHAR		achClass[MAX_PATH] = TEXT("");				// buffer for class name 
	DWORD		cchClassName = MAX_PATH;					// size of class string 
	DWORD		cSubKeys = 0;								// number of subkeys 
	DWORD		cbMaxSubKey;								// longest subkey size 
	DWORD		cchMaxClass;								// longest class string 
	DWORD		cKeyNum;									// number of values for key 
	DWORD		cchMaxValue;								// longest value name 
	DWORD		cbMaxValueData;								// longest value data 
	DWORD		cbSecurityDescriptor;						// size of security descriptor 
	FILETIME	ftLastWriteTime;							// last write time 

	int iRet = -1;
	bool bRet = false;

	std::string m_keyValue;

	TCHAR m_regKeyPath[MAX_KEY_LENGTH];

	TCHAR strDSName[MAX_VALUE_NAME];
	memset(strDSName, 0, MAX_VALUE_NAME);
	DWORD nValueType = 0;
	DWORD nBuffLen = 10;

#ifdef UNICODE
	int iLength;
	const char * _char = regKeyPath.c_str();
	iLength = MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, NULL, 0);
	MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, m_regKeyPath, iLength);
#else
	strcpy_s(m_regKeyPath, MAX_KEY_LENGTH, regKeyPath.c_str());
#endif

	if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_regKeyPath, 0, KEY_READ, &hKey))
	{
		// Get the class name and the value count. 
		iRet = RegQueryInfoKey(
			hKey,                    // key handle 
			achClass,                // buffer for class name 
			&cchClassName,           // size of class string 
			NULL,                    // reserved 
			&cSubKeys,               // number of subkeys 
			&cbMaxSubKey,            // longest subkey size 
			&cchMaxClass,            // longest class string 
			&cKeyNum,                // number of values for this key 
			&cchMaxValue,            // longest value name 
			&cbMaxValueData,         // longest value data 
			&cbSecurityDescriptor,   // security descriptor 
			&ftLastWriteTime);       // last write time 

		if (!portsList.empty())
		{
			portsList.clear();
		}

		// Enumerate the key values. 
		if (cKeyNum > 0 && ERROR_SUCCESS == iRet)
		{
			for (int i = 0; i < (int)cKeyNum; i++)
			{
				cchValue = MAX_VALUE_NAME;
				achValue[0] = '\0';
				if (ERROR_SUCCESS == RegEnumValue(hKey, i, achValue, &cchValue, NULL, NULL, NULL, NULL))
				{
					if (ERROR_SUCCESS == RegQueryValueEx(hKey, (LPCTSTR)achValue, NULL, &nValueType, (LPBYTE)strDSName, &nBuffLen))
					{
#ifdef UNICODE
						int iLen = WideCharToMultiByte(CP_ACP, 0, strDSName, -1, NULL, 0, NULL, NULL);
						char* chRtn = new char[iLen*sizeof(char)];
						WideCharToMultiByte(CP_ACP, 0, strDSName, -1, chRtn, iLen, NULL, NULL);
						m_keyValue = std::string(chRtn);
						delete chRtn;
						chRtn = NULL;
#else
						m_keyValue = std::string(strDSName);
#endif
						portsList.push_back(m_keyValue);

					}
				}
			}
		}
		else
		{

		}
	}

	if (portsList.empty())
	{
		bRet = false;
	}
	else
	{
		bRet = true;
	}


	RegCloseKey(hKey);

	return bRet;
}

//
// Constructor
//
CSerialPort::CSerialPort()
{
	m_hComm = NULL;

	// initialize overlapped structure members to zero
	///初始化异步结构体
	m_ov.Offset = 0;
	m_ov.OffsetHigh = 0;

	// create events
	m_ov.hEvent = NULL;
	m_hWriteEvent = NULL;
	m_hShutdownEvent = NULL;

	m_szWriteBuffer = NULL;

	m_bThreadAlive = FALSE;
	m_nWriteSize = 1;
	m_bIsSuspened = FALSE;

	// create events
	m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	// initialize the event objects
	///事件数组初始化,设定优先级别
	m_hEventArray[0] = m_hShutdownEvent;	// highest priority
	//为避免有些串口设备无数据输入,但一直返回读事件,使监听线程阻塞,
	//可以将读写放在两个线程中,或者修改读写事件优先级
	//修改优先级有两个方案:
	//方案一为监听线程中WaitCommEvent()后,添加如下两条语句:
	//if (WAIT_OBJECT_O == WaitForSingleObject(port->m_hWriteEvent, 0))
	//	ResetEvent(port->m_ov.hEvent);
	//方案二为初始化时即修改,即下面两条语句:
	m_hEventArray[1] = m_hWriteEvent;
	m_hEventArray[2] = m_ov.hEvent;

	// initialize critical section
	///初始化临界资源
	InitializeCriticalSection(&m_csCommunicationSync);
}

//
// Delete dynamic memory
//
CSerialPort::~CSerialPort()
{
	ClosePort();

	// close Handles  
	if (m_hShutdownEvent != NULL)
	{
		CloseHandle(m_hShutdownEvent);
	}
	if (m_ov.hEvent != NULL)
	{
		CloseHandle(m_ov.hEvent);
	}
	if (m_hWriteEvent != NULL)
	{
		CloseHandle(m_hWriteEvent);
	}

	// delete critical section
	//释放临界资源
	DeleteCriticalSection(&m_csCommunicationSync);
}

//
// Initialize the port. This can be port 1 to MaxSerialPortNum.
///初始化串口。只能是1-MaxSerialPortNum
//
//parity:
//  n=none
//  e=even
//  o=odd
//  m=mark
//  s=space
//data:
//  5,6,7,8
//stop:
//  1,1.5,2 
//
BOOL CSerialPort::InitPort(HWND pPortOwner,	// the owner (CWnd) of the port (receives message)
	UINT  portnr,		// portnumber (1..MaxSerialPortNum)
	UINT  baud,			// baudrate
	TCHAR  parity,		// parity 
	UINT  databits,		// databits 
	UINT  stopbits,		// stopbits 
	DWORD dwCommEvents,	// EV_RXCHAR, EV_CTS etc
	UINT  writebuffersize,// size to the writebuffer

	DWORD   ReadIntervalTimeout,
	DWORD   ReadTotalTimeoutMultiplier,
	DWORD   ReadTotalTimeoutConstant,
	DWORD   WriteTotalTimeoutMultiplier,
	DWORD   WriteTotalTimeoutConstant)

{
	assert(portnr > 0 && portnr < MaxSerialPortNum);
	assert(pPortOwner != NULL);

	ClosePort();

	// set buffersize for writing and save the owner
	m_pOwner = pPortOwner;

	if (m_szWriteBuffer != NULL)
	{
		delete[] m_szWriteBuffer;
		m_szWriteBuffer = NULL;
	}
	m_szWriteBuffer = new BYTE[writebuffersize];

	m_nPortNr = portnr;

	m_nWriteBufferSize = writebuffersize;
	m_dwCommEvents = dwCommEvents;

	BOOL bResult = FALSE;
	TCHAR *szPort = new TCHAR[MAX_PATH];
	TCHAR *szBaud = new TCHAR[MAX_PATH];

	/*
	多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,
	无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每
	个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和
	LeaveCriticalSection函数。
	*/
	// now it critical!
	EnterCriticalSection(&m_csCommunicationSync);

	// if the port is already opened: close it
	///串口已打开就关掉
	if (m_hComm != NULL)
	{
		CloseHandle(m_hComm);
		m_hComm = NULL;
	}

	// prepare port strings
	_stprintf_s(szPort, MAX_PATH, _T("\\\\.\\COM%d"), portnr);///可以显示COM10以上端口//add by itas109 2014-01-09

	// stop is index 0 = 1 1=1.5 2=2
	int mystop;
	int myparity;
	switch (stopbits)
	{
	case 0:
		mystop = ONESTOPBIT;
		break;
	case 1:
		mystop = ONE5STOPBITS;
		break;
	case 2:
		mystop = TWOSTOPBITS;
		break;
		//增加默认情况,因为stopbits=1.5时,SetCommState会报错。
		//一般的电脑串口不支持1.5停止位,这个1.5停止位似乎用在红外传输上的。
		//by itas109 20160506
	default:
		mystop = ONESTOPBIT;
		break;
	}
	myparity = 0;
	parity = _totupper(parity);
	switch (parity)
	{
	case _T('N'):
		myparity = 0;
		break;
	case _T('O'):
		myparity = 1;
		break;
	case _T('E'):
		myparity = 2;
		break;
	case _T('M'):
		myparity = 3;
		break;
	case _T('S'):
		myparity = 4;
		break;
		//增加默认情况。
		//by itas109 20160506
	default:
		myparity = 0;
		break;
	}
	_stprintf_s(szBaud, MAX_PATH, _T("baud=%d parity=%c data=%d stop=%d"), baud, parity, databits, mystop);

	// get a handle to the port
	/*
	通信程序在CreateFile处指定串口设备及相关的操作属性,再返回一个句柄,
	该句柄将被用于后续的通信操作,并贯穿整个通信过程串口打开后,其属性
	被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&&dcb)读取
	当前串口设备控制块DCB设置,修改后通过SetCommState(hComm,&&dcb)将其写
	入。运用ReadFile()与WriteFile()这两个API函数实现串口读写操作,若为异
	步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读
	写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING,
	表明I/O操作悬挂,即操作转入后台继续执行。此时,可以用WaitForSingleObject()
	来等待结束信号并设置最长等待时间
	*/
	m_hComm = CreateFile(szPort,						// communication port string (COMX)
		GENERIC_READ | GENERIC_WRITE,	// read/write types
		0,								// comm devices must be opened with exclusive access
		NULL,							// no security attributes
		OPEN_EXISTING,					// comm devices must use OPEN_EXISTING
		FILE_FLAG_OVERLAPPED,			// Async I/O
		0);							// template must be 0 for comm devices

	///创建失败
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		//add by itas109 2016-08-02
		//串口打开失败,增加提示信息
		switch (GetLastError())
		{
			//串口不存在
		case ERROR_FILE_NOT_FOUND:
		{
									 TCHAR Temp[200] = { 0 };
									 _stprintf_s(Temp, 200, _T("COM%d ERROR_FILE_NOT_FOUND,Error Code:%d"), portnr, GetLastError());
									 MessageBox(NULL, Temp, _T("COM InitPort Error"), MB_ICONERROR);
									 break;
		}
			//串口拒绝访问
		case ERROR_ACCESS_DENIED:
		{
									TCHAR Temp[200] = { 0 };
									_stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,Error Code:%d"), portnr, GetLastError());
									MessageBox(NULL, Temp, _T("COM InitPort Error"), MB_ICONERROR);
									break;
		}
		default:
			break;
		}
		// port not found
		delete[] szPort;
		delete[] szBaud;

		return FALSE;
	}

	// set the timeout values
	///设置超时
	m_SerialPortTimeouts.ReadIntervalTimeout = ReadIntervalTimeout * 1000;
	m_SerialPortTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier * 1000;
	m_SerialPortTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant * 1000;
	m_SerialPortTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier * 1000;
	m_SerialPortTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant * 1000;

	// configure
	///配置
	///分别调用Windows API设置串口参数
	if (SetCommTimeouts(m_hComm, &m_SerialPortTimeouts))///设置超时
	{
		/*
		若对端口数据的响应时间要求较严格,可采用事件驱动方式。
		事件驱动方式通过设置事件通知,当所希望的事件发生时,Windows
		发出该事件已发生的通知,这与DOS环境下的中断方式很相似。Windows
		定义了9种串口通信事件,较常用的有以下三种:
		EV_RXCHAR:接收到一个字节,并放入输入缓冲区;
		EV_TXEMPTY:输出缓冲区中的最后一个字符,发送出去;
		EV_RXFLAG:接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区
		在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事
		件的发生。SetCommMask(hComm,0)可使WaitCommEvent()中止
		*/
		if (SetCommMask(m_hComm, dwCommEvents))///设置通信事件
		{

			if (GetCommState(m_hComm, &m_dcb))///获取当前DCB参数
			{
				m_dcb.EvtChar = 'q';
				m_dcb.fRtsControl = RTS_CONTROL_ENABLE;		// set RTS bit high!
				m_dcb.BaudRate = baud;  // add by mrlong
				m_dcb.Parity = myparity;
				m_dcb.ByteSize = databits;
				m_dcb.StopBits = mystop;

				//if (BuildCommDCB(szBaud &m_dcb))///填写DCB结构
				//{
				if (SetCommState(m_hComm, &m_dcb))///配置DCB
					; // normal operation... continue
				else
					ProcessErrorMessage(_T("SetCommState()"));
				//}
				//else
				//	ProcessErrorMessage("BuildCommDCB()");
			}
			else
				ProcessErrorMessage(_T("GetCommState()"));
		}
		else
			ProcessErrorMessage(_T("SetCommMask()"));
	}
	else
		ProcessErrorMessage(_T("SetCommTimeouts()"));

	delete[] szPort;
	delete[] szBaud;

	// flush the port
	///终止读写并清空接收和发送
	PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

	// release critical section
	///释放临界资源
	LeaveCriticalSection(&m_csCommunicationSync);

	//TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);

	return TRUE;
}

//
//  The CommThread Function.
///线程函数
///监视线程的大致流程:
///检查串口-->进入循环{WaitCommEvent(不阻塞询问)询问事件-->如果有事件来到-->到相应处理(关闭\读\写)}
//
DWORD WINAPI CSerialPort::CommThread(LPVOID pParam)
{
	// Cast the void pointer passed to the thread back to
	// a pointer of CSerialPort class
	CSerialPort *port = (CSerialPort*)pParam;

	// Set the status variable in the dialog class to
	// TRUE to indicate the thread is running.
	///TRUE表示线程正在运行
	port->m_bThreadAlive = TRUE;

	// Misc. variables
	DWORD BytesTransfered = 0;
	DWORD Event = 0;
	DWORD CommEvent = 0;
	DWORD dwError = 0;
	COMSTAT comstat;

	BOOL  bResult = TRUE;

	// Clear comm buffers at startup
	///开始时清除串口缓冲
	if (port->m_hComm)		// check if the port is opened
	{
		PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
		//TRACE("Clear comm buffers");
	}

	// begin forever loop.  This loop will run as long as the thread is alive.
	///只要线程存在就不断读取数据
	for (;;)
	{

		// Make a call to WaitCommEvent().  This call will return immediatly
		// because our port was created as an async port (FILE_FLAG_OVERLAPPED
		// and an m_OverlappedStructerlapped structure specified).  This call will cause the 
		// m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to 
		// be placed in a non-signeled state if there are no bytes available to be read,
		// or to a signeled state if there are bytes available.  If this event handle 
		// is set to the non-signeled state, it will be set to signeled when a 
		// character arrives at the port.

		// we do this for each port!

		/*
		WaitCommEvent函数第3个参数1pOverlapped可以是一个OVERLAPPED结构的变量指针
		,也可以是NULL,当用NULL时,表示该函数是同步的,否则表示该函数是异步的。
		调用WaitCommEvent时,如果异步操作不能立即完成,会立即返回FALSE,系统在
		WaitCommEvent返回前将OVERLAPPED结构成员hEvent设为无信号状态,等到产生通信
		事件时,系统将其置有信号
		*/

		bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);///表示该函数是异步的

		if (!bResult)
		{
			// If WaitCommEvent() returns FALSE, process the last error to determin
			// the reason..
			///如果WaitCommEvent返回Error为FALSE,则查询错误信息
			switch (dwError = GetLastError())
			{
			case ERROR_IO_PENDING: 	///正常情况,没有字符可读 erroe code:997
			{
										// This is a normal return value if there are no bytes
										// to read at the port.
										// Do nothing and continue
										break;
			}
			case ERROR_INVALID_PARAMETER:///系统错误 erroe code:87
			{
											 // Under Windows NT, this value is returned for some reason.
											 // I have not investigated why, but it is also a valid reply
											 // Also do nothing and continue.
											 break;
			}
			case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
			{
										 port->m_hComm = INVALID_HANDLE_VALUE;
										 TCHAR Temp[200] = { 0 };
										 _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,WaitCommEvent() Error Code:%d"), port->m_nPortNr, GetLastError());
										 MessageBox(NULL, Temp, _T("COM WaitCommEvent Error"), MB_ICONERROR);
										 break;
			}
			case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
			{
										  port->m_hComm = INVALID_HANDLE_VALUE;
										  break;
			}
			case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
			{
									   port->m_hComm = INVALID_HANDLE_VALUE;
									   TCHAR Temp[200] = { 0 };
									   _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,WaitCommEvent() Error Code:%d"), port->m_nPortNr, GetLastError());
									   MessageBox(NULL, Temp, _T("COM WaitCommEvent Error"), MB_ICONERROR);
									   break;
			}
			default:///发生其他错误,其中有串口读写中断开串口连接的错误(错误22)
			{
						// All other error codes indicate a serious error has
						//发生错误时,将串口句柄置为无效句柄
						port->m_hComm = INVALID_HANDLE_VALUE;
						// occured.  Process this error.
						port->ProcessErrorMessage(_T("WaitCommEvent()"));
						break;
			}
			}
		}
		else	///WaitCommEvent()能正确返回
		{
			// If WaitCommEvent() returns TRUE, check to be sure there are
			// actually bytes in the buffer to read.  
			//
			// If you are reading more than one byte at a time from the buffer 
			// (which this program does not do) you will have the situation occur 
			// where the first byte to arrive will cause the WaitForMultipleObjects() 
			// function to stop waiting.  The WaitForMultipleObjects() function 
			// resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
			// as it returns.  
			//
			// If in the time between the reset of this event and the call to 
			// ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
			// to the signeled state. When the call to ReadFile() occurs, it will 
			// read all of the bytes from the buffer, and the program will
			// loop back around to WaitCommEvent().
			// 
			// At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
			// but there are no bytes available to read.  If you proceed and call
			// ReadFile(), it will return immediatly due to the async port setup, but
			// GetOverlappedResults() will not return until the next character arrives.
			//
			// It is not desirable for the GetOverlappedResults() function to be in 
			// this state.  The thread shutdown event (event 0) and the WriteFile()
			// event (Event2) will not work if the thread is blocked by GetOverlappedResults().
			//
			// The solution to this is to check the buffer with a call to ClearCommError().
			// This call will reset the event handle, and if there are no bytes to read
			// we can loop back through WaitCommEvent() again, then proceed.
			// If there are really bytes to read, do nothing and proceed.

			bResult = ClearCommError(port->m_hComm, &dwError, &comstat);

			if (comstat.cbInQue == 0)
				continue;
		}	// end if bResult

		///主等待函数,会阻塞线程
		// Main wait function.  This function will normally block the thread
		// until one of nine events occur that require action.
		///等待3个事件:关断/读/写,有一个事件发生就返回
		//if multiple objects signal it will get the index of the first one
		//it means it lost some signal //by itas109 2017-12-17
		Event = MsgWaitForMultipleObjects(3, ///3个事件
			port->m_hEventArray, ///事件数组
			FALSE, ///有一个事件发生就返回
			INFINITE,
			QS_ALLEVENTS);///超时时间

		//deal with mutil handle tigger at the same time	//by itas109 2017-12-17
		if (Event >= WAIT_OBJECT_0 && Event < WAIT_OBJECT_0 + 3)
		{
			for (int i = Event - WAIT_OBJECT_0 - 1; i < 3; i++)
			{
				if (WaitForSingleObject(port->m_hEventArray[i], 0) == WAIT_OBJECT_0)
				{
					//TRACE("WaitForSingleObject : %d",i);

					switch (Event)
					{
					case WAIT_OBJECT_0 + 0:
					{
											  // Shutdown event.  This is event zero so it will be
											  // the higest priority and be serviced first.
											  ///关断事件,关闭串口
											  CloseHandle(port->m_hComm);
											  port->m_hComm = NULL;
											  port->m_bThreadAlive = FALSE;

											  // Kill this thread.  break is not needed, but makes me feel better.
											  //AfxEndThread(100);
											  ::ExitThread(100);

											  break;
					}
					case WAIT_OBJECT_0 + 1: // write event 发送数据
					{
												// Write character event from port
												WriteChar(port);
												break;
					}
					case WAIT_OBJECT_0 + 2:	// read event 将定义的各种消息发送出去
					{
												GetCommMask(port->m_hComm, &CommEvent);
												if (CommEvent & EV_RXCHAR) //接收到字符,并置于输入缓冲区中
												{
													if (IsReceiveString == 1)
													{
														ReceiveStr(port);//多字符接收
													}
													else if (IsReceiveString == 0)
													{
														ReceiveChar(port);//单字符接收
													}
													else
													{
														//默认多字符接收
														ReceiveStr(port);//多字符接收
													}
												}

												if (CommEvent & EV_CTS) //CTS信号状态发生变化
													::SendMessage(port->m_pOwner, WM_COMM_CTS_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
												if (CommEvent & EV_RXFLAG) //接收到事件字符,并置于输入缓冲区中 
													::SendMessage(port->m_pOwner, WM_COMM_RXFLAG_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
												if (CommEvent & EV_BREAK)  //输入中发生中断
													::SendMessage(port->m_pOwner, WM_COMM_BREAK_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
												if (CommEvent & EV_ERR) //发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY 
													::SendMessage(port->m_pOwner, WM_COMM_ERR_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
												if (CommEvent & EV_RING) //检测到振铃指示
													::SendMessage(port->m_pOwner, WM_COMM_RING_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);

												break;
					}
					case WAIT_FAILED:
					{
										// 函数呼叫失败
										break;
					}
					case WAIT_TIMEOUT:
					{
										 // 超时
										 //TRACE("WaitForMultipleObjects Timeout");
										 break;
					}
					default:
					{
							   //MessageBox(NULL, _T("Receive Error!"), _T("COM Receive Error"), MB_ICONERROR);
							   break;
					}

					} // end switch
				}
			}
		}

	} // close forever loop

	return 0;
}

//
// start comm watching
///开启监视线程
//
BOOL CSerialPort::StartMonitoring()
{
	//if (!(m_Thread = AfxBeginThread(CommThread, this)))
	if (!(m_Thread = ::CreateThread(NULL, 0, CommThread, this, 0, NULL)))
		return FALSE;
	//TRACE("Thread started\n");
	return TRUE;
}

//
// Restart the comm thread
///从挂起恢复监视线程
//
BOOL CSerialPort::ResumeMonitoring()
{
	//TRACE("Thread resumed\n");
	//m_Thread->ResumeThread();
	::ResumeThread(m_Thread);
	return TRUE;
}

//
// Suspend the comm thread
///挂起监视线程
//
BOOL CSerialPort::SuspendMonitoring()
{
	//TRACE("Thread suspended\n");
	//m_Thread->SuspendThread();
	::SuspendThread(m_Thread);
	return TRUE;
}

BOOL CSerialPort::IsThreadSuspend(HANDLE hThread)
{
	DWORD   count = SuspendThread(hThread);
	if (count == -1)
	{
		return FALSE;
	}
	ResumeThread(hThread);
	return (count != 0);
}

//
// If there is a error, give the right message
///如果有错误,给出提示
//
void CSerialPort::ProcessErrorMessage(TCHAR* ErrorText)
{
	TCHAR Temp[200] = { 0 };

	LPVOID lpMsgBuf;

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR)&lpMsgBuf,
		0,
		NULL
		);

	_stprintf_s(Temp, 200, _T("WARNING:  %s Failed with the following error: \n%s\nPort: %d\n"), ErrorText, (TCHAR*)lpMsgBuf, m_nPortNr);
	MessageBox(NULL, Temp, _T("Application Error"), MB_ICONSTOP);

	LocalFree(lpMsgBuf);
}

//
// Write a character.
//
void CSerialPort::WriteChar(CSerialPort* port)
{
	if (port->m_bufferList.size() == 0)
	{
		return;
	}

	// Gain ownership of the critical section
	EnterCriticalSection(&port->m_csCommunicationSync);

	BOOL bWrite = TRUE;
	BOOL bResult = TRUE;

	DWORD BytesSent = 0;
	//DWORD SendLen = port->m_nWriteSize;

	ResetEvent(port->m_hWriteEvent);

	//声明i为迭代器   
	std::list<serialPortBuffer>::iterator i;

	//in general, for mutil send data can deal once.
	for (i = port->m_bufferList.begin(); i != port->m_bufferList.end(); )
	{
		//TRACE("port->m_bufferList size :%d", port->m_bufferList.size());

		PBYTE sendData = (*i).buffer;
		int sendLen = (*i).len;

		if (bWrite)
		{
			// Initailize variables
			port->m_ov.Offset = 0;
			port->m_ov.OffsetHigh = 0;

			// Clear buffer
			PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

			//TRACE("m_szWriteBufferMap Data : %s, size : %d", (*i).buffer, (*i).len);

			//bResult = WriteFile(port->m_hComm,							// Handle to COMM Port
			//	port->m_szWriteBuffer,					// Pointer to message buffer in calling finction
			//	SendLen,	// add by mrlong
			//	//strlen((char*)port->m_szWriteBuffer),	// Length of message to send
			//	&BytesSent,								// Where to store the number of bytes sent
			//	&port->m_ov);							// Overlapped structure

			bResult = WriteFile(port->m_hComm,							// Handle to COMM Port
				sendData,					// Pointer to message buffer in calling finction
				sendLen,	// add by mrlong
				//strlen((char*)port->m_szWriteBuffer),	// Length of message to send
				&BytesSent,								// Where to store the number of bytes sent
				&port->m_ov);							// Overlapped structure

			// deal with any error codes
			if (!bResult)
			{
				DWORD dwError = GetLastError();
				switch (dwError)
				{
				case ERROR_IO_PENDING:
				{
										 // continue to GetOverlappedResults()
										 BytesSent = 0;
										 bWrite = FALSE;
										 break;
				}
				case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
				{
											 port->m_hComm = INVALID_HANDLE_VALUE;
											 TCHAR Temp[200] = { 0 };
											 _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,WriteFile() Error Code:%d"), port->m_nPortNr, GetLastError());
											 MessageBox(NULL, Temp, _T("COM WriteFile Error"), MB_ICONERROR);
											 break;
				}
				case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
				{
											  port->m_hComm = INVALID_HANDLE_VALUE;
											  break;
				}
				case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
				{
										   port->m_hComm = INVALID_HANDLE_VALUE;
										   TCHAR Temp[200] = { 0 };
										   _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,WriteFile() Error Code:%d"), port->m_nPortNr, GetLastError());
										   MessageBox(NULL, Temp, _T("COM WriteFile Error"), MB_ICONERROR);
										   break;
				}
				default:
				{
						   // all other error codes
						   port->ProcessErrorMessage(_T("WriteFile()"));
				}
				}
			}
			else
			{
				//LeaveCriticalSection(&port->m_csCommunicationSync);
				//write ok
			}
		} // end if(bWrite)

		if (!bWrite)
		{
			bWrite = TRUE;

			bResult = GetOverlappedResult(port->m_hComm,	// Handle to COMM port 
				&port->m_ov,		// Overlapped structure
				&BytesSent,		// Stores number of bytes sent
				TRUE); 			// Wait flag

			//LeaveCriticalSection(&port->m_csCommunicationSync);

			// deal with the error code 
			if (!bResult)
			{
				port->ProcessErrorMessage(_T("GetOverlappedResults() in WriteFile()"));
			}
			else
			{
				//write ok
			}
		} // end if (!bWrite)

		//release and remove the data sended ok  add by itas109 2018-01-29
		if (bResult)
		{
			if (sendData)
			{
				delete sendData;
				sendData = NULL;
			}

			i = port->m_bufferList.erase(i);
		}
		else
		{
			i++;
		}

	}

	LeaveCriticalSection(&port->m_csCommunicationSync);

	// Verify that the data size send equals what we tried to send
	//if (BytesSent != SendLen /*strlen((char*)port->m_szWriteBuffer)*/)  // add by 
	//{
	//TRACE(_T("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n"), BytesSent, _tcsclen((TCHAR*)port->m_szWriteBuffer));
	//}
}

//
// Character received. Inform the owner
//
void CSerialPort::ReceiveChar(CSerialPort* port)
{
	BOOL  bRead = TRUE;
	BOOL  bResult = TRUE;
	DWORD dwError = 0;
	DWORD BytesRead = 0;
	COMSTAT comstat;
	unsigned char RXBuff;

	for (;;)
	{
		//add by liquanhai 2011-11-06  防止死锁
		if (WaitForSingleObject(port->m_hShutdownEvent, 0) == WAIT_OBJECT_0)
			return;

		// Gain ownership of the comm port critical section.
		// This process guarantees no other part of this program 
		// is using the port object. 

		EnterCriticalSection(&port->m_csCommunicationSync);

		// ClearCommError() will update the COMSTAT structure and
		// clear any other errors.
		///更新COMSTAT

		bResult = ClearCommError(port->m_hComm, &dwError, &comstat);

		LeaveCriticalSection(&port->m_csCommunicationSync);

		// start forever loop.  I use this type of loop because I
		// do not know at runtime how many loops this will have to
		// run. My solution is to start a forever loop and to
		// break out of it when I have processed all of the
		// data available.  Be careful with this approach and
		// be sure your loop will exit.
		// My reasons for this are not as clear in this sample 
		// as it is in my production code, but I have found this 
		// solutiion to be the most efficient way to do this.

		///所有字符均被读出,中断循环
		if (comstat.cbInQue == 0)
		{
			// break out when all bytes have been read
			break;
		}

		EnterCriticalSection(&port->m_csCommunicationSync);

		if (bRead)
		{
			///串口读出,读出缓冲区中字节
			bResult = ReadFile(port->m_hComm,		// Handle to COMM port 
				&RXBuff,				// RX Buffer Pointer
				1,					// Read one byte
				&BytesRead,			// Stores number of bytes read
				&port->m_ov);		// pointer to the m_ov structure
			// deal with the error code 
			///若返回错误,错误处理
			if (!bResult)
			{
				switch (dwError = GetLastError())
				{
				case ERROR_IO_PENDING:
				{
										 // asynchronous i/o is still in progress 
										 // Proceed on to GetOverlappedResults();
										 ///异步IO仍在进行
										 bRead = FALSE;
										 break;
				}
				case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
				{
											 port->m_hComm = INVALID_HANDLE_VALUE;
											 TCHAR Temp[200] = { 0 };
											 _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
											 MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
											 break;
				}
				case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
				{
											  port->m_hComm = INVALID_HANDLE_VALUE;
											  break;
				}
				case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
				{
										   port->m_hComm = INVALID_HANDLE_VALUE;
										   TCHAR Temp[200] = { 0 };
										   _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
										   MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
										   break;
				}
				default:
				{
						   // Another error has occured.  Process this error.
						   port->ProcessErrorMessage(_T("ReadFile()"));
						   break;
						   //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多
				}
				}
			}
			else///ReadFile返回TRUE
			{
				// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
				bRead = TRUE;
			}
		}  // close if (bRead)

		///异步IO操作仍在进行,需要调用GetOverlappedResult查询
		if (!bRead)
		{
			bRead = TRUE;
			bResult = GetOverlappedResult(port->m_hComm,	// Handle to COMM port 
				&port->m_ov,		// Overlapped structure
				&BytesRead,		// Stores number of bytes read
				TRUE); 			// Wait flag

			// deal with the error code 
			if (!bResult)
			{
				port->ProcessErrorMessage(_T("GetOverlappedResults() in ReadFile()"));
			}
		}  // close if (!bRead)

		LeaveCriticalSection(&port->m_csCommunicationSync);

		// notify parent that a byte was received
		//避免线程互相等待,产生死锁,使用PostMessage()代替SendMessage()
		PostMessage(port->m_pOwner, WM_COMM_RXCHAR, (WPARAM)RXBuff, (LPARAM)port->m_nPortNr);
		//::SendMessage((port->m_pOwner), Wm_SerialPort_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
	} // end forever loop

}

//
// str received. Inform the owner
//
void CSerialPort::ReceiveStr(CSerialPort* port)
{
	BOOL  bRead = TRUE;
	BOOL  bResult = TRUE;
	DWORD dwError = 0;
	DWORD BytesRead = 0;
	COMSTAT comstat;
	serialPortInfo commInfo;

	for (;;)
	{
		//add by liquanhai 2011-11-06  防止死锁
		if (WaitForSingleObject(port->m_hShutdownEvent, 0) == WAIT_OBJECT_0)
			return;

		// Gain ownership of the comm port critical section.
		// This process guarantees no other part of this program 
		// is using the port object. 

		EnterCriticalSection(&port->m_csCommunicationSync);

		// ClearCommError() will update the COMSTAT structure and
		// clear any other errors.
		///更新COMSTAT

		bResult = ClearCommError(port->m_hComm, &dwError, &comstat);

		LeaveCriticalSection(&port->m_csCommunicationSync);

		// start forever loop.  I use this type of loop because I
		// do not know at runtime how many loops this will have to
		// run. My solution is to start a forever loop and to
		// break out of it when I have processed all of the
		// data available.  Be careful with this approach and
		// be sure your loop will exit.
		// My reasons for this are not as clear in this sample 
		// as it is in my production code, but I have found this 
		// solutiion to be the most efficient way to do this.

		///所有字符均被读出,中断循环
		//0xcccccccc表示串口异常了,会导致RXBuff指针初始化错误
		if (comstat.cbInQue == 0 || comstat.cbInQue == 0xcccccccc)
		{
			// break out when all bytes have been read
			break;
		}

		//如果遇到'\0',那么数据会被截断,实际数据全部读取只是没有显示完全,这个时候使用memcpy才能全部获取
		unsigned char* RXBuff = new unsigned char[comstat.cbInQue + 1];
		if (RXBuff == NULL)
		{
			return;
		}
		RXBuff[comstat.cbInQue] = '\0';//附加字符串结束符

		EnterCriticalSection(&port->m_csCommunicationSync);

		if (bRead)
		{
			///串口读出,读出缓冲区中字节
			bResult = ReadFile(port->m_hComm,		// Handle to COMM port 
				RXBuff,				// RX Buffer Pointer
				comstat.cbInQue,					// Read cbInQue len byte
				&BytesRead,			// Stores number of bytes read
				&port->m_ov);		// pointer to the m_ov structure
			// deal with the error code 
			///若返回错误,错误处理
			if (!bResult)
			{
				switch (dwError = GetLastError())
				{
				case ERROR_IO_PENDING:
				{
										 // asynchronous i/o is still in progress 
										 // Proceed on to GetOverlappedResults();
										 ///异步IO仍在进行
										 bRead = FALSE;
										 break;
				}
				case ERROR_ACCESS_DENIED:///拒绝访问 erroe code:5
				{
											 port->m_hComm = INVALID_HANDLE_VALUE;
											 TCHAR Temp[200] = { 0 };
											 _stprintf_s(Temp, 200, _T("COM%d ERROR_ACCESS_DENIED,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
											 MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
											 break;
				}
				case ERROR_INVALID_HANDLE:///打开串口失败 erroe code:6
				{
											  port->m_hComm = INVALID_HANDLE_VALUE;
											  break;
				}
				case ERROR_BAD_COMMAND:///连接过程中非法断开 erroe code:22
				{
										   port->m_hComm = INVALID_HANDLE_VALUE;
										   TCHAR Temp[200] = { 0 };
										   _stprintf_s(Temp, 200, _T("COM%d ERROR_BAD_COMMAND,ReadFile() Error Code:%d"), port->m_nPortNr, GetLastError());
										   MessageBox(NULL, Temp, _T("COM ReadFile Error"), MB_ICONERROR);
										   break;
				}
				default:
				{
						   // Another error has occured.  Process this error.
						   port->ProcessErrorMessage(_T("ReadFile()"));
						   break;
						   //return;///防止读写数据时,串口非正常断开导致死循环一直执行。add by itas109 2014-01-09 与上面liquanhai添加防死锁的代码差不多
				}
				}
			}
			else///ReadFile返回TRUE
			{
				// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
				bRead = TRUE;
			}
		}  // close if (bRead)

		///异步IO操作仍在进行,需要调用GetOverlappedResult查询
		if (!bRead)
		{
			bRead = TRUE;
			bResult = GetOverlappedResult(port->m_hComm,	// Handle to COMM port 
				&port->m_ov,		// Overlapped structure
				&BytesRead,		// Stores number of bytes read
				TRUE); 			// Wait flag

			// deal with the error code 
			if (!bResult)
			{
				port->ProcessErrorMessage(_T("GetOverlappedResults() in ReadFile()"));
			}
		}  // close if (!bRead)

		LeaveCriticalSection(&port->m_csCommunicationSync);

		commInfo.portNr = port->m_nPortNr;
		commInfo.bytesRead = BytesRead;
		// notify parent that some byte was received
		::SendMessage((port->m_pOwner), WM_COMM_RXSTR, (WPARAM)RXBuff, (LPARAM)&commInfo);

		//释放
		delete[] RXBuff;
		RXBuff = NULL;

	} // end forever loop

}

//
// Return the device control block
//
DCB CSerialPort::GetDCB()
{
	return m_dcb;
}

//
// Return the communication event masks
//
DWORD CSerialPort::GetCommEvents()
{
	return m_dwCommEvents;
}

//
// Return the output buffer size
//
DWORD CSerialPort::GetWriteBufferSize()
{
	return m_nWriteBufferSize;
}

BOOL CSerialPort::IsOpened()
{
	return m_hComm != NULL && m_hComm != INVALID_HANDLE_VALUE;//m_hComm增加INVALID_HANDLE_VALUE的情况 add by itas109 2016-07-29
}

void CSerialPort::ClosePort()
{
	MSG message;

	//增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题 add by itas109 2016-06-29
	if (IsThreadSuspend(m_Thread))
	{
		ResumeThread(m_Thread);
	}

	//串口句柄无效  add by itas109 2016-07-29
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		CloseHandle(m_hComm);
		m_hComm = NULL;
		return;
	}

	do
	{
		SetEvent(m_hShutdownEvent);
		//add by liquanhai  防止死锁  2011-11-06
		if (::PeekMessage(&message, m_pOwner, 0, 0, PM_REMOVE))
		{
			::TranslateMessage(&message);
			::DispatchMessage(&message);
		}
	} while (m_bThreadAlive);

	//此处的延时很重要,因为如果串口开着,发送关闭指令到彻底关闭需要一定的时间,这个延时应该跟电脑的性能相关
	Sleep(50);//add by itas109 2016-08-02

	// if the port is still opened: close it 
	if (m_hComm != NULL)
	{
		CloseHandle(m_hComm);
		m_hComm = NULL;
	}

	// Reset Handles  
	if (m_hShutdownEvent != NULL)
	{
		ResetEvent(m_hShutdownEvent);
	}
	if (m_ov.hEvent != NULL)
	{
		ResetEvent(m_ov.hEvent);
	}
	if (m_hWriteEvent != NULL)
	{
		ResetEvent(m_hWriteEvent);
	}

	if (m_szWriteBuffer != NULL)
	{
		delete[] m_szWriteBuffer;
		m_szWriteBuffer = NULL;
	}
}


void CSerialPort::WriteToPort(char* string, size_t n)
{
	assert(m_hComm != 0);
	PBYTE m_bufferTemp = NULL;
	m_bufferTemp = new BYTE[n];

	//memset(m_szWriteBuffer, 0, n);
	//memcpy(m_szWriteBuffer, string, n);
	memset(m_bufferTemp, 0, n);
	memcpy(m_bufferTemp, string, n);
	m_nWriteSize = n;

	//use list is duing to that the send data may same. but map's key can not same
	m_bufferStruct.buffer = m_bufferTemp;
	m_bufferStruct.len = n;
	m_bufferList.push_back(m_bufferStruct);

	// set event for write
	SetEvent(m_hWriteEvent);
}

void CSerialPort::WriteToPort(BYTE* Buffer, size_t n)
{
	assert(m_hComm != 0);
	PBYTE m_bufferTemp = NULL;
	m_bufferTemp = new BYTE[n];

	//memset(m_szWriteBuffer, 0, n);
	//memcpy(m_szWriteBuffer, string, n);
	memset(m_bufferTemp, 0, n);
	memcpy(m_bufferTemp, Buffer, n);
	m_nWriteSize = n;

	m_bufferStruct.buffer = m_bufferTemp;
	m_bufferStruct.len = n;
	m_bufferList.push_back(m_bufferStruct);

	// set event for write
	SetEvent(m_hWriteEvent);
}

std::string CSerialPort::GetVersion()
{
	std::string m_version = "CSerialPort 3.0.1.180214";
	return m_version;
}

CSerialPortInfo::CSerialPortInfo()
{

}

CSerialPortInfo::~CSerialPortInfo()
{

}

std::list<std::string> CSerialPortInfo::availablePorts()
{
	std::list<std::string> portsList;
	///仅是XP/Win7系统的注册表位置,其他系统根据实际情况做修改
	std::string m_regKeyPath = std::string("HARDWARE\\DEVICEMAP\\SERIALCOMM");
	getRegKeyValues(m_regKeyPath, portsList);
	return portsList;
}
  • (2) import the SerialPort lib,you can refer to the following code: 
#include "SerialPort.h"
  • (3) Add the necessary code to the header file
using namespace itas109;
extern CSerialPort m_SerialPort;

CString Ascii2Hex(unsigned char * str_ASCII, int str_len);
afx_msg CString Ascii2Hex1(CString str_ASCII);
LRESULT OnReceiveStr(WPARAM str, LPARAM commInfo);


public:
	CComboBox m_PortNr;
	CComboBox m_BaudRate;
	CButton m_OpenCloseCtrl;
	CEdit m_Send;
	CEdit m_ReceiveCtrl;
	afx_msg void OnBnClickedButtonOpenClose();
	afx_msg void OnBnClickedButtonSend();
	afx_msg void OnClose();
  • (4) *Dlg.cpp ,The corresponding code implementation part of UI
int BaudRate[] = { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200 };
CSerialPort m_SerialPort;

 OnInitDialog(),you can Initialize the serial port scan.

BOOL CserialcommunicationDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	CString temp;
	//添加波特率到下拉列表
	for (int i = 0; i < sizeof(BaudRate) / sizeof(int); i++)
	{
		temp.Format(_T("%d"), BaudRate[i]);
		m_BaudRate.AddString((LPCTSTR)temp);
	}

	temp.Format(_T("%d"), 38400);
	m_BaudRate.SetCurSel(m_BaudRate.FindString(0, temp));

	//获取串口号
	CSerialPortInfo a;
	list<string> m_portsList = CSerialPortInfo::availablePorts();
	list<string>::iterator itor;
	TCHAR m_regKeyValue[255];
	for (itor = m_portsList.begin(); itor != m_portsList.end(); ++itor)
	{
#ifdef UNICODE
		int iLength;
		const char * _char = (*itor).c_str();
		iLength = MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, NULL, 0);
		MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, m_regKeyValue, iLength);
#else
		strcpy_s(m_regKeyValue, 255, (*itor).c_str());
#endif
		m_PortNr.AddString(m_regKeyValue);
	}
	m_PortNr.SetCurSel(1);

	OnBnClickedButtonOpenClose();

	m_Send.SetWindowText(_T("12345678"));

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

 OnBnClickedButtonOpenClose(),you can open or close the serialport ,you can refer to the flowing code.

void CserialcommunicationDlg::OnBnClickedButtonOpenClose()
{
	// TODO: 在此添加控件通知处理程序代码
	//GetDlgItem(IDC_SendEdit)->SetFocus();
	CString temp;
	m_OpenCloseCtrl.GetWindowText(temp);///获取按钮的文本
	UpdateData(true);
	if (temp == _T("关闭串口"))///表示点击后是"关闭串口",也就是已经关闭了串口
	{
		m_SerialPort.ClosePort();
		m_OpenCloseCtrl.SetWindowText(_T("打开串口"));///设置按钮文字为"打开串口"
	}
	///打开串口操作
	else if (m_PortNr.GetCount() > 0)///当前列表的内容个数
	{

		int SelPortNO, SelBaudRate;
		UpdateData(true);
		m_PortNr.GetWindowText(temp);///CString temp
		temp.Delete(0, 3);
		SelPortNO = _tstoi(temp);

		m_BaudRate.GetWindowText(temp);
		SelBaudRate = _tstoi(temp);

		if (m_SerialPort.InitPort(this->GetSafeHwnd(), SelPortNO, SelBaudRate))
		{

			m_SerialPort.StartMonitoring();
			m_OpenCloseCtrl.SetWindowText(_T("关闭串口"));
		}
		else
		{
			AfxMessageBox(_T("串口已被占用!"));
		}
	}
	else
	{
		AfxMessageBox(_T("没有发现串口!"));
	}
}

OnBnClickedButtonSend(),you can use the WriteToPort to send the pc command to the MCU,Then,you need receive the MCU to the PC data.NOW,please.

void CserialcommunicationDlg::OnBnClickedButtonSend()
{
	// TODO: 在此添加控件通知处理程序代码
	GetDlgItem(IDC_SendEdit)->SetFocus();
	CString temp;
	UpdateData(true);
	m_OpenCloseCtrl.GetWindowText(temp);
	if (temp == "打开串口")///没有打开串口
	{
		AfxMessageBox(_T("请首先打开串口"));
		return;
	}

	m_Send.GetWindowText(temp);
	size_t len = _tcsclen(temp) + 1;;
	char* m_str = NULL;
	size_t* converted = 0;
	m_str = new char[len];
#ifdef UNICODE
	wcstombs_s(converted, m_str, len, temp.GetBuffer(0), _TRUNCATE);
#else
	m_str = temp.GetBuffer(0);
#endif
	m_SerialPort.WriteToPort(m_str, len);
}

OnReceiveStr(), you can processing the  MCU data to PC.

LRESULT CserialcommunicationDlg::OnReceiveStr(WPARAM str, LPARAM commInfo)
{
	struct serialPortInfo
	{
		UINT portNr;//串口号
		DWORD bytesRead;//读取的字节数
	}*pCommInfo;
	pCommInfo = (serialPortInfo*)commInfo;

	CString recv_str_data((char*)str);
	CString Str_display;
#if 0 //测试代码
//==================================================================
//m_ReceiveCtrl.SetWindowText(str1);//接收到什么显示什么
//int nLength = m_ReceiveCtrl.GetWindowTextLength();
选定当前文本的末端
//m_ReceiveCtrl.SetSel(nLength, nLength);
l追加文本
//m_ReceiveCtrl.ReplaceSel(str1);
//==================================================================  
#endif // 0 //测试代码

	int len_read = 0;//当前接收到的数据量的个数
	//------------------------------------------------------------------------
	len_read = pCommInfo->bytesRead;//接收到的所有数据有‘\0’,所以发送12345678得到的长度为9
	int len = _tcslen(recv_str_data.GetBuffer(0));//获取接受数据的长度
	//------------------------------------------------------------------------
	recv_str_data = Ascii2Hex((unsigned char*)str, len_read);//去掉空格之后的16进制数据
	//------------------------------------------------------------------------
	//===================================================================================
	//将CString类型转为十六进制时的一种方法
	//===================================================================================
	//CString recv_str_HEX = CString("A55A06010D0AA55A");//测试代码
	CString recv_str_HEX = recv_str_data;//测试代码
	char *stops, s[3];
	unsigned char recv_HEX[200];存放的是转换好的HEX数据
	int length_recv = recv_str_HEX.GetLength();
	for (size_t i = 0; i < length_recv/2; i++)
	{
		s[0] = recv_str_HEX.GetAt(i * 2);
		s[1] = recv_str_HEX.GetAt(i * 2 + 1);
		s[2] = 0x0;
		recv_HEX[i] = (unsigned char)strtoul(s, &stops, 16);
	}
	//===================================================================================
	//协议解析
	//===================================================================================
	//===================================================================================
	//自动工作模式解析
	//===================================================================================
	if ((recv_HEX[1] == 0x5a)
    {
    ;
    }
}

other code.,I suggest the you can do this using the following code.

CString CserialcommunicationDlg::Ascii2Hex(unsigned char* str_ASCII, int str_len)
{
	CString str_Hex, temp;
	for (int i = 0; i < str_len; i++)
	{
		temp.Format(_T("%2X"), str_ASCII[i]);
		str_Hex += temp;
	}
	return str_Hex;
}
CString CserialcommunicationDlg::Ascii2Hex_space(unsigned char* str_ASCII, int str_len)
{
	CString str_Hex, temp;
	for (int i = 0; i < str_len; i++)
	{
		temp.Format(_T("%2X "), str_ASCII[i]);
		str_Hex += temp;
	}
	return str_Hex;
}
CString CserialcommunicationDlg::Ascii2Hex(TCHAR* str_ASCII, int str_len)
{
	CString str_Hex, temp;
	for (int i = 0; i < str_len; i++)
	{
		temp.Format(_T("%2X "), str_ASCII[i]);
		str_Hex += temp;
	}
	return str_Hex;
}
  • (5)Add processing code according to your own requirements

I hope I can help you,If you have any questions, please  comment on this blog or send me a private message. I will reply in my free time.
 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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.csdn.net/liquanhai/article/details/4955253 增加 ClosePort 中交出控制权,防止死锁问题 by liquanhai on 2011-11-06 http://blog.csdn.net/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.csdn.net/itas109/article/details/18358297 解决COM10以上端口无法显示的问题 扩展可选择端口,最大值MaxSerialPortNum可以自定义 添加QueryKey()和Hkey2ComboBox两个方法,用于自动查询当前有效的串口号。 by liquanhai on 2014-12-18 增加一些处理措施,主要是对减少CPU占用率 by itas109 on 2016-05-07 http://blog.csdn.net/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.csdn.net/itas109 增加ReceiveStr方法,用于接收字符串(接收缓冲区有多少字符就接收多少字符)。 解决ReceiveChar只能接收单个字符的问题。 by itas109 on 2016-06-29 http://blog.csdn.net/itas109 解决RestartMonitoring方法和StopMonitoring方法命令不准确引起的歧义,根据实际作用。 将RestartMonitoring更改为ResumeMonitoring,将StopMonitoring更改为SuspendMonitoring。 增加IsThreadSuspend方法,用于判断线程是否挂起。 改进ClosePort方法,增加线程挂起判断,解决由于线程挂起导致串口关闭死锁的问题。 增加IsReceiveString宏定义,用于接收时采用单字节接收还是多字节接收 博客:blog.csdn.net/itas109 Email:itas109@qq.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值