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

📄 serialobject.cpp

📁 一个基于API的串口通信程序,多线程有bug,大家修改修改后使用
💻 CPP
字号:
// SerialObject.cpp: implementation of the CSerialPort class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SerialObject.h"

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

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSerialPort::CSerialPort():
			m_hOwnerWnd(NULL),
			m_nPortNr(1),
			m_lpszWriteBuffer(NULL),
			m_dwWriteBufferSize(0),
			m_dwCommEvent(0),
			m_bThreadAlive(FALSE),
			m_pThread(NULL),
			//m_csCommunicationSync;
			m_hComm(INVALID_HANDLE_VALUE),
			m_hShutdownEvent(NULL),
			m_hWriteEvent(NULL),
			//m_hEventArray[3];
			//m_ov;
			//m_CommTimeouts;
			//m_strRec;
			m_nToSend(0)
{
	memset(&m_ov, sizeof(OVERLAPPED), '\0');
	memset(&m_dcb, sizeof(DCB), 0);
	m_ov.hEvent = NULL;
	m_ov.Offset = 0;
	m_ov.OffsetHigh = 0;
}


CSerialPort::~CSerialPort()
{
	if (m_pThread != NULL)
	{
		TerminateMonitoring();
	}

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

	if (m_hComm != INVALID_HANDLE_VALUE)
	{
		::CloseHandle(m_hComm);
		::DeleteCriticalSection(&m_csCommunicationSync);
	}

	if (m_hShutdownEvent != NULL)
	{
		::CloseHandle(m_hShutdownEvent);
	}

	if (m_hWriteEvent != NULL)
	{
		::CloseHandle(m_hWriteEvent);
	}

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

}



BOOL CSerialPort::InitPort(HWND hOverWnd, UINT portnr/* = 1*/, UINT baud/* = 9600*/,
		char parity/* = 'N'*/, UINT databits/* = 8*/, UINT stopbits/* = 1*/, 
		DWORD dwCommEvent/* = EV_RXCHAR | EV_CTS*/, UINT nWriteBufferSize/* = 512*/)
{
	ASSERT(hOverWnd != NULL);
	ASSERT(portnr > 0 && portnr < 5);

	//如果有本端口已经有监控线程了,先终止它
	if (m_pThread != NULL)
	{
		TerminateMonitoring();
	}

	//创建或复位
	if (m_ov.hEvent != NULL)
	{
		ResetEvent(m_ov.hEvent);
	}
	else
	{
		m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	}

	//创建或复位
	if (m_hWriteEvent != NULL)
	{
		ResetEvent(m_ov.hEvent);
	}
	else
	{
		m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	}

	//创建或复位
	if (m_hShutdownEvent != NULL)
	{
		ResetEvent(m_ov.hEvent);
	}
	else
	{
		m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	}

	//用于WaitForMultipleObject
	m_hEventArray[0] = m_hShutdownEvent;  //shutdown 在最WaitForMultipleObject的前面
	m_hEventArray[1] = m_ov.hEvent;
	m_hEventArray[2] = m_hWriteEvent;


	//初始化临界对象, 在析构时用DeleteCriticalSection释放
	InitializeCriticalSection(&m_csCommunicationSync);

	m_hOwnerWnd = hOverWnd;
	m_nPortNr = portnr;
	m_dwCommEvent = dwCommEvent;
	//初始化写缓冲区
	if (m_lpszWriteBuffer != NULL)
	{
		delete[] m_lpszWriteBuffer;
	}
	m_lpszWriteBuffer = new BYTE[nWriteBufferSize];
	m_dwWriteBufferSize = nWriteBufferSize;

	//打开串口
	EnterCriticalSection(&m_csCommunicationSync);
	if (m_hComm != NULL)
	{
		::CloseHandle(m_hComm);
		m_hComm = NULL;
	}
	
	CString strCom;
	strCom.Format("COM%d", portnr);
	m_hComm = ::CreateFile(strCom,			//lpFileName
			GENERIC_READ | GENERIC_WRITE,	//dwDesiredAccess
			0,								//dwShareMode, 因为不能共享,必须为0
			NULL,							//lpSecurityAttributes, 为NULL时分配缺省的安全属性
			OPEN_EXISTING,					//dwCreationDisposition, 这里为直接打开已经存在的端口
			FILE_FLAG_OVERLAPPED,			//dwFlagsAndAttributes, 对于口串口硬件,唯一意义是,设置异步访问与否
			NULL							//hTemplateFile, 串口没有模板文件,必须设为NULL
			);
	
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		DWORD dwError = GetLastError();
		TRACE("打开串行端口失败:%ld\n", dwError);
		return FALSE;
	}

	//	
	m_CommTimeouts.ReadIntervalTimeout = 1000L;
	m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000L;
	m_CommTimeouts.ReadTotalTimeoutConstant = 1000L;
	m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000L;
	m_CommTimeouts.WriteTotalTimeoutConstant = 1000L;

	//设置端口
	if (!::SetCommTimeouts(m_hComm, &m_CommTimeouts))
	{
		::PurgeComm(m_hComm, 
			PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
		LeaveCriticalSection(&m_csCommunicationSync);
		ProcessErrorMessage("");
		return FALSE;
	}

	if (!::SetCommMask(m_hComm, dwCommEvent))
	{
		::PurgeComm(m_hComm, 
			PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
		LeaveCriticalSection(&m_csCommunicationSync);
		ProcessErrorMessage("");
		return FALSE;
	}

	if (!::GetCommState(m_hComm, &m_dcb))
	{
		::PurgeComm(m_hComm, 
			PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
		LeaveCriticalSection(&m_csCommunicationSync);
		ProcessErrorMessage("");
		return FALSE;
	}
	//m_dcb.fRtsControl = RTS_CONTROL_ENABLE;

	char szBaud[50];
	sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits); 
	if (!::BuildCommDCB(szBaud, &m_dcb))
	{
		::PurgeComm(m_hComm, 
			PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
		LeaveCriticalSection(&m_csCommunicationSync);
		ProcessErrorMessage("");
		return FALSE;
	}

	LeaveCriticalSection(&m_csCommunicationSync);
	return TRUE;
}

void CSerialPort::ProcessErrorMessage(LPCTSTR ErrorText) const
{
	AfxMessageBox(ErrorText);
}

UINT CSerialPort::CommThread(LPVOID pParam)
{
	CSerialPort *port = (CSerialPort *)pParam;
	port->m_bThreadAlive = TRUE;
	
	//保证端口是可用的
	ASSERT(port->m_hComm != INVALID_HANDLE_VALUE);

	DWORD	bytesTransfered = 0;
	DWORD	dwEvtMask = 0;
	DWORD	dwEventIndex = 0;

	DWORD	dwError = 0;
	COMSTAT	comstat;
	BOOL	bResult = TRUE;

	//清除
	PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | 
							PURGE_RXABORT | PURGE_TXABORT);

	while(1)
	{
		//
		bResult = ::WaitCommEvent(port->m_hComm, &dwEvtMask, &port->m_ov);
		if (!bResult)
		{
			dwError = GetLastError();
			switch(dwError)
			{
			case ERROR_IO_PENDING:
				break;
			case 87:
				break;
			default:
				{
					port->ProcessErrorMessage("");
					TRACE("---------\n");
					::ClearCommError(port->m_hComm, &dwError, &comstat);
					break;
				}
			}
		}
		else
		{
			bResult = ::ClearCommError(port->m_hComm, &dwError, &comstat);
			if (comstat.cbInQue == 0)
			{
				continue;
			}
		}

		//
		dwEventIndex = ::WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
		switch(dwEventIndex)
		{
		case 0:	//m_hShutdownEvent
			{
				port->m_bThreadAlive = FALSE;
				AfxEndThread(100);
				break;
			}
			break;
		case 1://串口事件
			{
				GetCommMask(port->m_hComm, &dwEvtMask);

				if (dwEvtMask & EV_CTS)
					::SendMessage(port->m_hOwnerWnd, WM_COM_CTS_DETECTED,
						(WPARAM)0, (LPARAM)port->m_nPortNr);
				
				if (dwEvtMask & EV_RXFLAG)
					::SendMessage(port->m_hOwnerWnd, WM_COM_RXFLAG_DETECTED,
						(WPARAM)0, (LPARAM)port->m_nPortNr);
				
				if (dwEvtMask & EV_BREAK)
					::SendMessage(port->m_hOwnerWnd, WM_COM_BREAK_DETECTED,
						(WPARAM)0, (LPARAM)port->m_nPortNr);
				
				if (dwEvtMask & EV_ERR)
					::SendMessage(port->m_hOwnerWnd, WM_COM_ERR_DETECTED,
						(WPARAM)0, (LPARAM)port->m_nPortNr);
				
				if (dwEvtMask & EV_RING)
					::SendMessage(port->m_hOwnerWnd, WM_COM_RING_DETECTED,
						(WPARAM)0, (LPARAM)port->m_nPortNr);

				if (dwEvtMask & EV_RXCHAR)
				{
					ReceiveChar(port, comstat);
				}
			}
			break;
		case 2://写
			{
				WriteChar(port);
			}
			break;
		default:
			break;
		}

	}//end while(1)

	return 0L;
}

void CSerialPort::ReceiveChar(CSerialPort *port, COMSTAT comstat)
{
	BOOL bRead = TRUE;
	BOOL bResult = TRUE;
	DWORD dwError = 0;
	DWORD bytesRead = 0;
	unsigned char RXBuffer = 0;
	int nCounter = 0;
	while(1)	//此处用while, 等测试后 去掉
	{
		nCounter++;
		EnterCriticalSection(&port->m_csCommunicationSync);
		bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
		LeaveCriticalSection(&port->m_csCommunicationSync);

		//while的出口
		if (comstat.cbInQue == 0)
		{
			break;
		}

		EnterCriticalSection(&port->m_csCommunicationSync);

		bResult = ::ReadFile(port->m_hComm,
							&RXBuffer,
							1,
							&bytesRead,
							&port->m_ov);
		if (!bResult)
		{
			dwError = GetLastError();

			switch(dwError)
			{
			case ERROR_IO_PENDING:
				{
					bRead = FALSE;
					break;
				}
				break;
			default:
				{
					bRead = TRUE;	//出错了,不用在等待读取了
					port->ProcessErrorMessage("");
				}
				break;
			}

		}
		else
		{
			bRead = TRUE;
		}

		//在这里等待,直到读取
		if (!bRead)
		{
			bRead = TRUE;
			bResult = GetOverlappedResult(port->m_hComm,
										&port->m_ov,
										&bytesRead,
										TRUE);
			if (!bResult)
			{
				port->ProcessErrorMessage("");
			}
		}//end if (!bRead)

		LeaveCriticalSection(&port->m_csCommunicationSync);


		::SendMessage(port->m_hOwnerWnd, WM_COM_RXCHAR_DETECTED,
				(WPARAM)RXBuffer, (LPARAM)port->m_nPortNr);
	}//end while(1)

}


void CSerialPort::WriteChar(CSerialPort *port)
{
	BOOL	bWrite = TRUE;
	BOOL	bResult = TRUE;
	DWORD	bytesSend = 0;

	ResetEvent(port->m_hWriteEvent);
	EnterCriticalSection(&port->m_csCommunicationSync);
	
	port->m_ov.Offset = 0;
	port->m_ov.OffsetHigh = 0;
	PurgeComm(port->m_hComm, 
		PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_RXABORT);

	bResult = WriteFile(port->m_hComm,
						port->m_lpszWriteBuffer,
						port->m_nToSend,
						&bytesSend,
						&port->m_ov);
	if (!bResult)
	{
		DWORD dwError = GetLastError();
		switch(dwError)
		{
		case ERROR_IO_PENDING:
			{
				bytesSend =0;
				bWrite = FALSE;
			}
			break;
		default:
			{
				bytesSend = 0;
				bWrite = TRUE;			//已经出错,不用等待"写操作"完成了
				//port->ProcessErrorMessage("");
				TRACE("==================\n");
			}
			break;
		}
	}
	else
	{
		LeaveCriticalSection(&port->m_csCommunicationSync);
	}

	//等待写操作,直到完成
	if (!bWrite)
	{
		bWrite = TRUE;
		bResult = GetOverlappedResult(port->m_hComm, 
									&port->m_ov,
									&bytesSend,
									TRUE);
		LeaveCriticalSection(&port->m_csCommunicationSync);

		if (!bResult)
		{
			port->ProcessErrorMessage("");
		}
	}

}

void CSerialPort::WriteToPort(const BYTE *lpszWriteBuffer, int nLength)
{
	ASSERT(m_lpszWriteBuffer != NULL);
	ASSERT(nLength > 0);

	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("");
		return;
	}
	
	memset(m_lpszWriteBuffer, 0, m_dwWriteBufferSize);
	memcpy(m_lpszWriteBuffer, lpszWriteBuffer, nLength);
	m_nToSend = nLength;
	SetEvent(m_hWriteEvent);
}

void CSerialPort::WriteToPort(CString &strWrite)
{
	int nLength = strWrite.GetLength();
	memset(m_lpszWriteBuffer, 0, m_dwWriteBufferSize);
	memcpy(m_lpszWriteBuffer, strWrite.GetBuffer(nLength), nLength);
	m_nToSend = nLength;
	SetEvent(m_hWriteEvent);
}

inline DCB CSerialPort::GetDCB() const
{
	return m_dcb;
}

inline DWORD CSerialPort::GetWriteBufferSize() const
{
	return m_dwWriteBufferSize;
}

inline DWORD CSerialPort::GetCommEvent() const
{
	return m_dwCommEvent;
}

BOOL CSerialPort::StartMonitoring()
{
	if (m_pThread != NULL)
	{
		AfxMessageBox("已经起动了监视线程!");
		return FALSE;
	}
	
	m_pThread = AfxBeginThread(CommThread, this);
	if (m_pThread == NULL)
	{
		TRACE0("创建线程失败!\n");
	
		return FALSE;
	}
	//m_pThread->m_bAutoDelete = FALSE;
	TRACE1("%d\n", m_pThread->m_bAutoDelete);
	return TRUE;
}


BOOL CSerialPort::SuspendMonitoring()
{
	if (m_bThreadAlive && (m_pThread != NULL))
	{
		m_pThread->SuspendThread();
		m_bThreadAlive = FALSE;
	}
	return TRUE;
}

BOOL CSerialPort::ResumeMonitoring()
{
	if (!m_bThreadAlive && (m_pThread != NULL))
	{
		m_pThread->ResumeThread();
		m_bThreadAlive = TRUE;
		return TRUE;
	}
	return FALSE;
}

BOOL CSerialPort::TerminateMonitoring()
{
	if (m_pThread != NULL)
	{
		if (!m_bThreadAlive)
		{
			m_pThread->ResumeThread();
			m_bThreadAlive = TRUE;
		}

		do
		{
			SetEvent(m_hShutdownEvent);
		} while(m_bThreadAlive);

	//	m_pThread = NULL;
		m_bThreadAlive = FALSE;
		return TRUE;
	}

	return FALSE;
}

⌨️ 快捷键说明

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