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

📄 serialport.cpp

📁 本程序是一个宾馆程控电话系统
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
**	文件名称:			CSerialPort.cpp
**
**	描述:				这个类能读,写和监视一个端口
**						当端口发生事件时他能给所有者发送消息
**						这个类创建一个线程来读和写,因此,主程序不会阻塞
**
**	作者:				李洋
*/

#include "stdafx.h"
#include "SerialPort.h"

#include <assert.h>	//	当结果有错误时,输出此错误的诊断信息

//
// Constructor
//	构造函数
//
CSerialPort::CSerialPort()
{
	m_hComm = NULL;	//	com口句柄
	
	//	初始化overlapped结构成员变量为0
	m_ov.Offset = 0;	
	m_ov.OffsetHigh = 0;	
	
	//	初始化事件句柄
	m_ov.hEvent = NULL;	
	m_hWriteEvent = NULL;	
	m_hShutdownEvent = NULL;	
	
	m_szWriteBuffer = NULL;	
	
	m_bThreadAlive = FALSE;	//	TRUE 表示线程正在运行
}

//
// Delete dynamic memory
//	析构函数,删除动态存储器
//
CSerialPort::~CSerialPort()
{
	do
	{
		SetEvent(m_hShutdownEvent);
	} while (m_bThreadAlive);	//	TRUE 表示线程正在运行
	
	TRACE("Thread ended\n");	//	printf
	
	delete [] m_szWriteBuffer;
}

//
//初始化端口,你能初始化 1 到4
//
BOOL CSerialPort::InitPort(CWnd* pPortOwner,	// 父窗口(接收消息)
						   UINT  portnr,		// 串口号(1-4)
						   UINT  baud,			// 波特率
						   char  parity,		// 校验位
						   UINT  databits,		// 数据位 
						   UINT  stopbits,		// 停止位
						   DWORD dwCommEvents,	// 串口状态事件
						   UINT  writebuffersize)	//	输出缓冲区尺寸
{
	assert(portnr > 0 && portnr < 9);	//	当结果有错误时,输出此错误的诊断信息
	assert(pPortOwner != NULL);	//	当结果有错误时,输出此错误的诊断信息
	
	
	//如果线程在运行,结束线程
	//TRUE 表示线程正在运行
	if (m_bThreadAlive)
	{
		do
		{
			SetEvent(m_hShutdownEvent);	//	设置事件为关闭
		} while (m_bThreadAlive);
		TRACE("Thread ended\n");
	}
	
	//	创建事件
	if (m_ov.hEvent != NULL)
		ResetEvent(m_ov.hEvent);
	m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	
	if (m_hWriteEvent != NULL)
		ResetEvent(m_hWriteEvent);
	m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	
	if (m_hShutdownEvent != NULL)
		ResetEvent(m_hShutdownEvent);
	m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	
	//	初始化监视事件对象
	m_hEventArray[0] = m_hShutdownEvent;	// highest priority	最高优先权
	m_hEventArray[1] = m_ov.hEvent;	//	写事件还是读事件在 overlapped结构体中
	m_hEventArray[2] = m_hWriteEvent;
	
	//	初始化临界区
	InitializeCriticalSection(&m_csCommunicationSync);
	
	// 保存父窗口,串口号
	m_pOwner = pPortOwner;
	m_nPortNr = portnr;	
	
	//	创建输出缓冲区
	if (m_szWriteBuffer != NULL)
		delete [] m_szWriteBuffer;
	m_szWriteBuffer = new char[writebuffersize];
	
	m_nWriteBufferSize = writebuffersize;	//	m_nWriteBuffersize为	返回输出缓冲区的大小
	
	//	保存串口状态事件
	m_dwCommEvents = dwCommEvents;	//	事件代码
	
	BOOL bResult = FALSE;
	char *szPort = new char[50];	//	动态分配空间
	char *szBaud = new char[50];	//	动态分配空间
	
	//	进入临界区
	//try
	//{
		EnterCriticalSection(&m_csCommunicationSync);
		
		//如果端口已打开,则先关闭
		if (m_hComm != NULL)
		{
			CloseHandle(m_hComm);
			m_hComm = NULL;
		}
		
		// 准备串口设置
		sprintf(szPort, "COM%d", portnr);
		sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);
		
		//	打开串口
		m_hComm = CreateFile(szPort,						// 串口名称字符串,如:com1
			GENERIC_READ | GENERIC_WRITE,	// 读写
			0,								// 以独占方式打开
			NULL,							// 未设置安全属性
			OPEN_EXISTING,					// 串口设备必须设置OPEN_EXISTING
			FILE_FLAG_OVERLAPPED,			// 异步	I/O 方式
			0);							// 串口设备该参数必须设为0
		
		if (m_hComm == INVALID_HANDLE_VALUE)
		{
			//端口没有找到
			delete [] szPort;
			delete [] szBaud;
			
			return FALSE;
		}
		
		//	设置超时时间
		m_CommTimeouts.ReadIntervalTimeout =MAXDWORD;
		m_CommTimeouts.ReadTotalTimeoutMultiplier =1000;
		m_CommTimeouts.ReadTotalTimeoutConstant = 1000;
		m_CommTimeouts.WriteTotalTimeoutMultiplier = 19200;
		m_CommTimeouts.WriteTotalTimeoutConstant = 1000;
		
		//	配置串口
		if (SetCommTimeouts(m_hComm, &m_CommTimeouts))	//	设置超时
		{						   
			if (SetCommMask(m_hComm, dwCommEvents))
			{
				if (GetCommState(m_hComm, &m_dcb))	//	获得端口配置
				{
					m_dcb.fRtsControl =RTS_CONTROL_ENABLE;		// set RTS bit high!
					if (BuildCommDCB(szBaud, &m_dcb))
					{
						if (SetCommState(m_hComm, &m_dcb))
							; // normal operation... continue
						//正常的操作……继续
						else
							ProcessErrorMessage("SetCommState()");
					}
					else
						ProcessErrorMessage("BuildCommDCB()");
				}
				else
					ProcessErrorMessage("GetCommState()");
			}
			else
				ProcessErrorMessage("SetCommMask()");
		}
		else
			ProcessErrorMessage("SetCommTimeouts()");
		
		delete [] szPort;
		delete [] szBaud;
		
		//	此函数能清除输入输出缓冲区的所有字节,并能停止没有读或写完的操作
		PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
		
		//	离开临界区
		LeaveCriticalSection(&m_csCommunicationSync);
		
		TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);
		
		
		return TRUE;
	//}
	//catch(CException ce)
	//{
	//	assert(portnr > 0 && portnr < 5);	//	当结果有错误时,输出此错误的诊断信息
	//	AfxMessageBox("yun");
		//return FALSE;
	//}
}

//
//  The CommThread Function.
//	线程函数
//
UINT CSerialPort::CommThread(LPVOID pParam)
{
	// Cast the void pointer passed to the thread back to
	// a pointer of CSerialPort class
	CSerialPort *port = (CSerialPort*)pParam;
	
	//	TRUE 表示线程正在运行
	port->m_bThreadAlive = TRUE;	
	
	//	其他的变量
	DWORD BytesTransfered = 0; 
	DWORD Event = 0;
	DWORD CommEvent = 0;
	DWORD dwError = 0;	//	错误信息
	COMSTAT comstat;	//	COMSTAT 结构体类型
	BOOL  bResult = TRUE;
	
	//	在启动时,清空串口缓冲区
	if (port->m_hComm)	//	检查端口是否是打开的
		PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
	
	//	开始无限循环。当线程alive时这个循环就一直进行
	for (;;) 
	{ 
		
		//检查线路状态,等待SetCommMask指定事件之一发生,
		//事实上因为CSerialPort以异步方式操作串口,所以改调用将会立即返回
		
		bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
		
		if (!bResult)  
		{ 
			//	如果WaitCommEvent()返回FALSE,处理最后确定的错误
			//	原因是..
			switch (dwError = GetLastError()) 
			{ 
			case ERROR_IO_PENDING: 	
				{ 
					//串口没有数据时什么也不做正常返回
					break;
				}
			case 87:
				{
					// 在 Windows NT下,该值将会返回,原因不明,但它是正常的返回值
					break;
				}
			default:
				{
					// 其他返回指标时有错误发生
					port->ProcessErrorMessage("WaitCommEvent()");
					break;
				}
			}
		}
		else
		{
			//WaitCommEvent以后,要用ClearCommError清除事件的Flag,以便进行下一轮
			//WaitCommEvent,同时这个API可以获得更祥细的事件信息
			bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
			
			//确认串口中无字符则无须进入主监视过程,重新开始循环
			if (comstat.cbInQue == 0)
				continue;
		}	// end if bResult
		
		// 主监视函数,该函数将阻塞本线程直至等待的某一事件发生
		Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
		
		switch (Event)	//循环监视事件
		{
		case 0:	//	Shutdown事件,将关闭本线程(最高级)
			{
				port->m_bThreadAlive = FALSE;
				AfxEndThread(100);	//结束线程100为返回代码,此函数只能在线程内调用
				break;
			}
		case 1:	//	处理串口状态事件
			{
				GetCommMask(port->m_hComm, &CommEvent);	//	获取指定设备的事件代码
				if (CommEvent & EV_CTS)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_RXFLAG)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_BREAK)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_ERR)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				if (CommEvent & EV_RING)
					::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);
				
				if (CommEvent & EV_RXCHAR)
					
					//	从端口收到字符
					ReceiveChar(port, comstat);
				
				break;
			}  
		case 2: // 处理写事件
			{
				
				//	向端口写字符
				WriteChar(port);
				break;
			}
			
		} // end switch
		
	} // close forever loop
	
	return 0;
}

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

//
//	重启线程
//
BOOL CSerialPort::RestartMonitoring()
{
	TRACE("Thread resumed\n");
	m_Thread->ResumeThread();
	return TRUE;	
}


//
//	挂起线程
//
BOOL CSerialPort::StopMonitoring()

⌨️ 快捷键说明

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