⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serialcomm.cpp

📁 研华ADAN4050 检测程序(自做) 压缩文件中 无密码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// SerialComm.cpp: implementation of the CSerialComm class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SerialComm.h"

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

CSerialComm::CSerialComm()
{
#if !defined(SC_MSCOMOBJECT)

	m_bAsyncOutput = FALSE; //同步写方式
	m_hCOM = NULL; //串行通讯端口句柄

	::ZeroMemory(&m_stDCB, sizeof(DCB));
	::ZeroMemory(&m_stCTO, sizeof(COMMTIMEOUTS));
	::ZeroMemory(m_atcCurCOM, sizeof(TCHAR) * SC_MAXCOMNAME);
	::ZeroMemory(&m_stROverlapped, sizeof(m_stROverlapped));
	::ZeroMemory(&m_stWOverlapped, sizeof(m_stWOverlapped));
	m_stWOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	m_stROverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	m_bShowCommConfigDialog = FALSE;

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

CSerialComm::~CSerialComm()
{
	Close();

#if !defined(SC_MSCOMOBJECT)

	if (NULL != m_stROverlapped.hEvent)
	{
		::CloseHandle(m_stROverlapped.hEvent);
	}
	if (NULL != m_stWOverlapped.hEvent)
	{
		::CloseHandle(m_stWOverlapped.hEvent);
	}

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

//获取当前系统中安装的串行通讯端口
//<参数>
//	pppCOMs: 用于接收一个指向串行通讯端口名集的指针
//<返回值>
//	返回当前系统中安装的串行通讯端口名集。
int CSerialComm::GetCOMs(LPCTSTR ** pppCOMs)
{
#if !defined(SC_MSCOMOBJECT)

	if (NULL == pppCOMs)
		return int(0);

	static TCHAR atcCOMs[SC_MAXCOMNUM][SC_MAXCOMNAME];
	static LPTSTR apCOMs[SC_MAXCOMNUM];
	::ZeroMemory(atcCOMs, sizeof(TCHAR) * SC_MAXCOMNUM * SC_MAXCOMNAME);

	int iCount = 0;
	HANDLE hCOM = NULL;

	for (int i = 0; i < SC_MAXCOMNUM; i++)
	{
		apCOMs[i] = atcCOMs[i];
		::_stprintf(atcCOMs[iCount], _T("COM%d"), i+1);

		//打开串口
		hCOM = ::CreateFile(atcCOMs[iCount],
			GENERIC_READ|GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			FILE_FLAG_OVERLAPPED,
			NULL);

		//如果串口打开失败...
		if (INVALID_HANDLE_VALUE == hCOM || NULL == hCOM)
		{
			DWORD dwLastError = ::GetLastError();
			if (ERROR_FILE_NOT_FOUND != dwLastError)
				++iCount;
			else
				::ZeroMemory(atcCOMs[iCount], sizeof(TCHAR) * SC_MAXCOMNAME);
		}
		else
		{
			::CloseHandle(hCOM);
			++iCount;
		}
	}

	if (0 < iCount)
		*pppCOMs = (LPCTSTR *)apCOMs;

	return int(iCount);

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

//打开指定串行通讯端口并指定其波特率和校验方式
//<参数>
//	lpszCOMName: 指定端口名称
//	dwBaudrate: 指定波特率
//	dwParity: 指定校验方式
//<返回值>
//	如果成功返回TRUE,否则返回FALSE。
BOOL CSerialComm::Open(LPCTSTR lpszCOMName, DWORD dwBaudrate/* = 0xFFFFFFFFl*/, DWORD dwParity/* = 0xFFFFFFFFl*/)
{
#if !defined(SC_MSCOMOBJECT)

	Close();
	if (NULL == lpszCOMName || FALSE != m_bShowCommConfigDialog)
		return BOOL(FALSE);

	//打开串口
	m_hCOM = ::CreateFile(lpszCOMName,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,
		NULL);
	//如果串口打开失败...
	if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
	{
		m_hCOM = NULL;
		return BOOL(FALSE);
	}
	//设置缺省配置
	SetDefaultConfig(dwBaudrate, BYTE(dwParity));
	::lstrcpyn(m_atcCurCOM, lpszCOMName, SC_MAXCOMNAME - 1);
	return BOOL(TRUE);

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

//关闭当前使用的串行端口
//<参数> 无
//<返回值> 无
void CSerialComm::Close()
{
#if !defined(SC_MSCOMOBJECT)

	if (NULL != m_hCOM)
	{
		::PurgeComm(m_hCOM, PURGE_RXABORT | PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
		::CloseHandle(m_hCOM);
		m_hCOM=NULL;
		::ZeroMemory(m_atcCurCOM, sizeof(TCHAR) * SC_MAXCOMNAME);
	}

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

//判断当前串行通讯端口是否已经打开
//<参数> 无
//<返回值>
//	如果已经打开则返回已打开串口的名字,否则返回NULL。
LPCTSTR CSerialComm::IsOpened()
{
#if !defined(SC_MSCOMOBJECT)

	return LPCTSTR((NULL != m_hCOM && INVALID_HANDLE_VALUE != m_hCOM) ? m_atcCurCOM : NULL);

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

#if !defined(SC_MSCOMOBJECT)
//设置当前串行通讯端口的缺省配置
//<参数>
//	dwBaudrate: 指定波特率,如果为0xFFFFFFFF表示保持原有设置值
//	ucParity: 指定校验方式,如果为0xFFFF表示保持原有设置值
//<返回值> 无
void CSerialComm::SetDefaultConfig(DWORD dwBaudrate, BYTE ucParity)
{
	if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
		return;

	if (FALSE != ::GetCommState(m_hCOM, &m_stDCB))
	{
		if (0xFFFFFFFF != dwBaudrate)
			m_stDCB.BaudRate = FormatBaudrate(dwBaudrate);

		if (0xFF != ucParity)
		{
			m_stDCB.Parity = FormatParity(ucParity);
			m_stDCB.fParity = BOOL(m_stDCB.Parity != NOPARITY);
		}

		m_stDCB.DCBlength = sizeof(DCB);	//DCB数据结构长度
		m_stDCB.ByteSize = 8;				//数据位
		m_stDCB.StopBits = ONESTOPBIT;	//停止位

		m_stDCB.fAbortOnError = FALSE;	//发生通讯错误时不终止读写
		m_stDCB.fBinary = TRUE;			//以二近制方式通讯

		::SetCommState(m_hCOM, &m_stDCB);
	}

	if (FALSE != ::GetCommTimeouts(m_hCOM, &m_stCTO))
	{
		int iBits = 1 + m_stDCB.ByteSize + ((m_stDCB.StopBits == ONESTOPBIT) ? 1 : 2);
		m_stCTO.ReadIntervalTimeout = 10; //10ms的区间超时
		m_stCTO.ReadTotalTimeoutMultiplier = (iBits * 1000) / m_stDCB.BaudRate; //传输一个字节所需的时间
		if (0 == m_stCTO.ReadTotalTimeoutMultiplier)
			m_stCTO.ReadTotalTimeoutMultiplier = 1;
		m_stCTO.ReadTotalTimeoutConstant = m_stCTO.ReadTotalTimeoutMultiplier * 10;
		m_stCTO.WriteTotalTimeoutMultiplier = ((iBits + 2) * 1000) / m_stDCB.BaudRate; //传输一个字节所需的时间
		if (0 == m_stCTO.WriteTotalTimeoutMultiplier)
			m_stCTO.WriteTotalTimeoutMultiplier = 1;
		m_stCTO.WriteTotalTimeoutConstant = m_stCTO.WriteTotalTimeoutMultiplier * 10;
		::SetCommTimeouts(m_hCOM, &m_stCTO);
	}
}
#endif //if !defined(SC_MSCOMOBJECT)

//从当前串行通讯端口读取数据
//<参数>
//	pBuff: 指定用于接收数据的缓冲区
//	lReadSize: 指定期望读取的数据长度
//<返回值>
//	返回正确读取的字节数。如果指定一个NULL缓冲区或期望读取的数据长度不大于0,
//则返回当前系统内部读缓冲区中的字节数。出错时返回值小于0。
long CSerialComm::Read(LPVOID pBuff, long lReadSize)
{
#if !defined(SC_MSCOMOBJECT)

	if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
		return long(-1);

	//如果入口参数无效则试图返回输入缓冲区中的数据长度
	if (NULL == pBuff || 0 > lReadSize)
	{
		COMSTAT stCOMStat;
		DWORD dwErr;
		if (FALSE == ::ClearCommError(m_hCOM, &dwErr, &stCOMStat))
			return long(-2);
		return long(stCOMStat.cbInQue);
	}

	DWORD dwReadedSize = 0;
	long lReaded = 0;
	int iCount = (lReadSize + 3) >> 2;

	while (iCount > 0)
	{
		--iCount;
		::ResetEvent(m_stROverlapped.hEvent);
		//如果读取失败...
		if (FALSE == ::ReadFile(m_hCOM, LPBYTE(pBuff)+lReaded, lReadSize, &dwReadedSize, &m_stROverlapped))
		{
			//如果是重叠IO...
			if (ERROR_IO_PENDING == GetLastError())
			{
				if (FALSE != ::GetOverlappedResult(m_hCOM, &m_stROverlapped, &dwReadedSize, TRUE))
				{
					lReaded += long(dwReadedSize);
					lReadSize -= long(dwReadedSize);
					if (0 >= lReadSize || 0 == dwReadedSize)
						return long(lReaded);
					else
					{
						iCount = min((lReadSize + 3) >> 2, iCount);
					}
				}
			} //if (ERROR_IO_PENDING == GetLastError())
		} //if (FALSE == ::ReadFile)
		else
		{
			lReaded += long(dwReadedSize);
			//读取完成
			return long(lReaded);
		}
	} //while(dwSize)
	return long(lReaded);

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

//向当前串行通讯端口写数据
//<参数>
//	pData: 指定待写数据
//	lWriteSize: 指定待写数据长度
//<返回值>
//	返回正确写出的字节数。如果指定pData为NULL或待写数据长度不大于0,
//则返回当前系统内部写缓冲区中的字节数。出错时返回值小于0。
long CSerialComm::Write(LPVOID pData, long lWriteSize)
{
#if !defined(SC_MSCOMOBJECT)

	if (INVALID_HANDLE_VALUE == m_hCOM || NULL == m_hCOM)
		return long(-1);

	COMSTAT stCOMStat;
	DWORD dwErr = 0;

	//如果入口参数无效则试图返回输入缓冲区中的数据长度
	if (NULL == pData || 0 > lWriteSize)
	{
		if (FALSE == ::ClearCommError(m_hCOM, &dwErr, &stCOMStat))
			return long(-2);
		return long(stCOMStat.cbOutQue);
	}

	DWORD dwWritedSize = 0;
	long lWritedTatol = 0;

	if (FALSE != m_bAsyncOutput)
	{
		::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE);
		dwWritedSize = 0;
	}

	while (0 < lWriteSize)
	{
		::ResetEvent(m_stWOverlapped.hEvent);
		//如果发送失败...
		if (FALSE == ::WriteFile(m_hCOM, LPBYTE(pData) + lWritedTatol, DWORD(lWriteSize), &dwWritedSize, &m_stWOverlapped))
		{
			//如果是重叠IO...
			if (ERROR_IO_PENDING == ::GetLastError())
			{
				//如果重叠IO失败...
				if (FALSE == ::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE))
				{
					if (FALSE != m_bAsyncOutput)
						return long(lWritedTatol);
					DWORD dwWait = m_stCTO.WriteTotalTimeoutMultiplier * lWriteSize + m_stCTO.WriteTotalTimeoutConstant;
					if (WAIT_TIMEOUT == ::WaitForSingleObject(m_stWOverlapped.hEvent, dwWait))
						return long(-3);
					if (FALSE == ::GetOverlappedResult(m_hCOM, &m_stWOverlapped, &dwWritedSize, FALSE))
						return long(-4);
				}
				if (0 == dwWritedSize)
					return long(lWritedTatol);
				lWritedTatol += long(dwWritedSize);
				lWriteSize -= long(dwWritedSize);
			} //if (ERROR_IO_PENDING == ::GetLastError())
			else
				return long(-5);
		}
		else
		{
			lWritedTatol += long(dwWritedSize);
			lWriteSize -= long(dwWritedSize);
		}
	}

	if (FALSE != m_bAsyncOutput)
		return long(lWritedTatol);

	//清空软件缓冲区
	::FlushFileBuffers(m_hCOM);
	DCB stDCB;
	if (FALSE != ::GetCommState(m_hCOM, &stDCB))
	{
		int iBits = 1 + stDCB.ByteSize + ((stDCB.StopBits == ONESTOPBIT) ? 1 : 2); //一个字节的实际传输位数
		int iBytesTime = (iBits * 1000 * min(16 + 2, dwWritedSize + 2)) / stDCB.BaudRate; //传输若干字节所需要的时间
		::Sleep(iBytesTime); //等待硬件缓冲区空
	}
	return long(lWritedTatol);

#else //if !defined(SC_MSCOMOBJECT)
#endif
}

//对Win32API CommConfigDialog函数的封装
//<参数> 无
//<返回值>
//	如果成功返回TRUE,否则返回FALSE。
BOOL CSerialComm::CommConfigDialog()
{
#if !defined(SC_MSCOMOBJECT)

	if (NULL != IsOpened())
	{
		DWORD dwCCSize;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -