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

📄 serialport.cpp

📁 基于双机互联系统的SPCP串口通信协议编写的串口通信底层类源代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#include "stdafx.h"
#include "SerialPort.h"
#include <assert.h>
//构造函数
CSerialPortEx::CSerialPortEx()
{
	//初始化overlapped结构
	m_ov.Offset=0;
	m_ov.OffsetHigh=0;
    //初始化事件句柄
	m_ov.hEvent=NULL;
	m_hWriteEvent=NULL;
	m_hShutdownEvent=NULL;
	//初始化其他成员
	m_hComm=NULL;
	m_szWriteBuffer=NULL;
	m_bThreadAlive=FALSE;    //默认使用ReadChar模式读取数据
	m_bBlockRead=FALSE;
}
//析构函数,关闭串口,停止监视线程并释放动态分配的内存
CSerialPortEx::~CSerialPortEx()
{
	do
	{
		SetEvent(m_hShutdownEvent);
	}while(m_bThreadAlive);
	delete [] m_szWriteBuffer;
}
//初始化串口
BOOL CSerialPortEx::InitPort(CWnd* pPortOwner,      //父窗口(接受消息)
							 UINT portnr,           //串口号
							 UINT baud,             //数据传输波特率
							 char parity,           //检验位
							 UINT databits,         //数据位
							 UINT stopbits,         //停止位
							 DWORD dwCommEvents,    //串口状态事件
							 UINT writebuffersize)  //输出缓冲区尺寸
{
	assert(portnr>0 && porter < 5);
	assert(pPortOwner != NULL);

	//若当前监视线程正在运行,则先Kill该线程
	if(m_bThreadAlive)
	{
		do
		{
			SetEvent(m_hShutdownEvent);
		}while (m_bThreadAlive);
	}
	//创建事件
	if(m_ov.hEvent!=NULL)
		ResetEvent(m_ov.hEvent);
	m_ov.hEvent=CraeteEvent(NULL,TURE,FALSE,NULL);

	if(m_hWriteEvent!=NULL)
		ResetEvent(m_hWriteEvent);
	m_hWriteEvent=CraeteEvent(NULL,TURE,FALSE,NULL);

	if(m_hShutdownEvent!=NULL)
		ResetEvent(m_hShutdownEvent);
    m_hShutdownEvent=CraeteEvent(NULL,TURE,FALSE,NULL);

	//初始化监视事件对象
	m_hEventArray[0]=m_hShutdownEvent; //最高优先级
    m_hEventArray[1]=m_ov.hEvent;
    m_hEventArray[2]=m_hWriteEvent;

	//初始化临界区
	InitializeCriticalSection(&m_csCommuincationSync);

	//保存父窗口与串口号
	m_pOwner=pPortOwner;
	m_nPortNr=portnr;

	//创建输出缓冲区
	if(m_szWriteBuffer!=NULL)
		delete [] m_szWriteBuffer;
	m_szWriteBuffer = new BYTE[writebuffersize];
    m_nWriteBufferSize = writebuffersize;

	//保存串口状态事件
	m_dwCommEvent = dwCommEvents;
	BOLL bResult = FALSE;
	char* szPort = new char[50];
	char* szBaud = new char[50];

	//进入临界区
	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);

	//打开串口
	HANDLE m_hComm=CreateFile(szPort,   //串口名称字符串,如“COM1”
		GENNRIC_READ|GENERIC_WRITE,     //读写
		0,                              //以独占方式打开
		NULL,                           //未设置安全属性
		OPEN_EXISTING,                  //串口设备必须设置该值
		FILE_FLAG_OVERLAPPED,           //使用异步I/O
		0);                             //串口设备该参数必须设为0
	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		//出错处理
		delete [] szPort;
		delete [] szBaud;
		return FALSE;
	}

	//设置超时时间
	m_CommTimeouts.ReadIntervalTimeout = 1000;
    m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;
    m_CommTimeouts.ReadTotalTimeoutConstant = 1000;
    m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;
    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);
	return TRUE;
}

//创建串口主监视线程
BOOL CSerialPortEx::StartMonitoring()
{
	if(!(m_Thread = AfxBeginThread(CommThread,this)))
		return FALSE;
	return TRUE;
}

//重新开始串口主线程
BOOL CSerialPortEx::RestartMonitoring()
{
	TRACE("Thread resumed\n");
	m_Thread->ResumeThread();
	return TRUE;
}

//中止串口主线程
BOOL CSerialPortEx::StopMonitoring();
{
	TRACE("Thread suspended\n");
	if(m_bThreadAlive)
	{
		m_Thread->SuspendThread();
		m_bThreadAlive=FALSE;
	}
	return TRUE;
}

//串口主监视线程
UINT CSerialPortEx::CommThread(LPVOID pParam)
{
	CSerialPortEx* port=(CSerialPortEx*)pParam;
	//表示当前监视线程正在运行
	port->m_bThreadAlive= TRUE;
	DWORD BytesTransfered = 0;
	DWORD Events = 0;
	DWORD CommEvent = 0;
	DWORD dwError = 0;
	COMSTAT comstat;
	BOOL bResult = TRUE;
	//清空串口缓冲区
	if(port->m_hComm)
		PurgeComm(port->m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|
		PURGE_RXABORT|PURGE_TXABORT);
	//开始无限循环,当该线程alive时该循环一直进行
	for(;;)
	{
		//检测线路状态,等待SetCommMsak指定事件之一发生
		bResult = WaitCommEvent(port->m_hComm,%Event,&port->m_ov);
		if(!bResult)
		{
			switch(dwError = GetLastError())
			{
			case ERROR_IO_PENDING:
				{
					//串口没有数据时的正常返回值
					break;
				}
			case 87:
				{
					//在windows NT下的正常返回值
					break;
				}
			default:
				{
					//其他返回值表示有错误发生
					port->ProcessErrorMessage("WaitCommEvent()");
					break;
				}
			}
		}
		else
		{
			//用ClearCommError清除事件的Flag,以便下一轮
			bResult = ClearCommError(port->m_hComm,&dwError,&comstat);
			//确认串口无字符则无须进入主监视进程,重新开始循环分
			if(comstat.cbInQue == 0)
				continue;
		}
		//主监视函数,该函数将阻塞本线程直至等待的某一事件发生
		Event = WaitForMultipleObject(3,port->m_hEventArray,FALSE,INFINITE);
		switch(Event)
		{
		case 0:                                 //将中止本线程
			{
				port->m_bThreadAlive = FALSE;
				AfxEndThread(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)
					if(port->m_bBlockRead)  //检测是ReadBlock还是ReadChar模式
						ReceiveChar(port,comstat);
					break;
			}
		case 2:
			{
				WriteChar(port);
				break;
			}
		}
	}//结束串口主循环
	return 0;
}

void CSerialPortEx::ReceiveChar(CSerialPortEx* port,COMSTAT comstat)
{
	BOOL bSend = TRUE;
	BOOL bResult = TRUE;
	DWORD dwError = 0;
	DWORD BytesRead = 0;
	unsigned char RXBuff = 0;
	//开始无限循环,每次读一个字节发送至父窗口,直至缓冲区所有数据读取完毕
	for(;;)
	{
		EnterCriticalSection(&port->m_csCommunicationSync);          //进入临界区

⌨️ 快捷键说明

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