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

📄 serialportctrl.cpp

📁 自己写的VC++串口通信代码
💻 CPP
字号:
// SerialPortCtrl.cpp: implementation of the CSerialPortCtrl class.
//
#include "stdafx.h"
#include "SerialPortCtrl.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSerialPortCtrl::CSerialPortCtrl()
{
	m_hCom = NULL;
	m_strPort = "COM0";
	m_bComOpen = false;

	m_cInBuf = NULL;
	m_InBufSz = INBUFSZ;
	m_OutBufSz = OUTBUFSZ;
	m_dwEvt = EV_RXCHAR;//;//EV_RXFLAG | 
	//端口设备控制块,m_Dcb;
	//超时结构,m_ComTimeOuts;

	memset(&m_OLRead,0,sizeof(OVERLAPPED));
	memset(&m_OLWrite,0,sizeof(OVERLAPPED));
}

CSerialPortCtrl::~CSerialPortCtrl()
{
	CloseCom();
}

bool CSerialPortCtrl::OpenCom(LPCTSTR port/* = "COM1"*/)
{
	if(m_strPort == port&&(m_hCom||m_bComOpen))
		return true;	//指定的端口已经打开
	if((m_hCom||m_bComOpen)&&m_strPort != port)
		CloseCom();//正打开的端口不是指定的,则先关闭

	m_hCom = CreateFile(port,
						GENERIC_READ |GENERIC_WRITE,
						0,
						NULL,
						OPEN_EXISTING,
						FILE_ATTRIBUTE_NORMAL |FILE_FLAG_OVERLAPPED, //支持重叠操作
						NULL);
	if(m_hCom == INVALID_HANDLE_VALUE)
	{	//打开端口失败
		AfxMessageBox("打开端口"+CString(port)+"失败.");
		DWORD dwError = GetLastError();
			//进行相应处理.....
		return m_bComOpen;
	}
	else
	{	//成功打开指定端口
		m_bComOpen = true;	//更新端口状态
		m_strPort  = port;

		m_ComTimeOuts.ReadIntervalTimeout		 = MAXDWORD;//
		m_ComTimeOuts.ReadTotalTimeoutMultiplier = 0;//
		m_ComTimeOuts.ReadTotalTimeoutConstant   = 0;
		m_ComTimeOuts.WriteTotalTimeoutMultiplier= 10;
		m_ComTimeOuts.WriteTotalTimeoutConstant  = 50;

		m_OLRead.hEvent = CreateEvent(NULL,true,false,NULL);
		m_OLWrite.hEvent = CreateEvent(NULL,true,false,NULL);

		GetCommState(m_hCom, &m_Dcb);
		m_Dcb.DCBlength = sizeof(DCB);
		m_Dcb.BaudRate = 9600;
		m_Dcb.ByteSize = 8;
//		m_Dcb.EvtChar  = '\r';
		//设置输入、输出缓冲大小
	//	m_InBufSz = INBUFSZ;
	//	m_OutBufSz = OUTBUFSZ;

		SetCom();	//用默认参数,设置端口
	}

	return m_bComOpen;
}

bool CSerialPortCtrl::CloseCom()
{	//关闭已经打开的端口
	if(!m_hCom || !m_bComOpen)//没有端口被打开
		return true;

	FlushFileBuffers(m_hCom);
	if(m_cInBuf)
	{
		delete[] m_cInBuf;
		m_cInBuf = NULL;
	}

	bool bre,bwe;	//关闭异步事件
	if(m_OLRead.hEvent)
		bre = CloseHandle(m_OLRead.hEvent);
	if(m_OLWrite.hEvent)
		bwe = CloseHandle(m_OLWrite.hEvent);

	SetCommMask(m_hCom,0);	//清楚端口事件标志

	m_bComOpen = !CloseHandle(m_hCom);	//关闭端口,并更新端口状态
	if(!m_bComOpen&&bre&&bwe)
	{
		m_strPort = "COM0";
		memset(&m_OLRead,0,sizeof(OVERLAPPED));
		memset(&m_OLWrite,0,sizeof(OVERLAPPED));
	}
	else
		AfxMessageBox("关闭端口"+m_strPort+"失败.");

	return (!m_bComOpen&&bre&&bwe);
}

bool CSerialPortCtrl::SetCom()
{	//需要事先填充相关结构
	//m_hCom,m_Dcb,m_InBufSz,m_OutBufSz,m_dwEvt,m_ComTimeOuts.
	bool btouts = SetCommTimeouts(m_hCom,&m_ComTimeOuts);
	bool bstate	= SetCommState(m_hCom,&m_Dcb);
	bool bbufsz = SetupComm(m_hCom,m_InBufSz,m_OutBufSz);

	if(m_cInBuf) delete[] m_cInBuf;
	m_cInBuf = new DTIN[m_InBufSz];

	if(m_cInBuf) memset(m_cInBuf,0,m_InBufSz*sizeof(DTIN));

	PurgeComm(m_hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

	bool bdwevt = SetCommMask(m_hCom,m_dwEvt);

	bool bres = btouts&&bstate&&bbufsz&&m_OLRead.hEvent&&m_OLWrite.hEvent&&bdwevt;
	if(!bres)
	{
		AfxMessageBox("端口设置失败,若端口未打开,请先打开端口.");
		DWORD dwError = GetLastError();
			//进行相应处理......
		return !CloseCom();
	}
	else
	{
		ResetEvent(m_OLRead.hEvent);
		ResetEvent(m_OLWrite.hEvent);
	}

	return bres;
}

DWORD CSerialPortCtrl::WriteCom(DTOUT outbuf,DWORD towrite)
{
	if( !m_bComOpen || m_hCom == NULL )
	{
	//	AfxMessageBox("端口未打开,无法完成写操作。");
		return 0;
	}

	DWORD dwErrFlags;
	DWORD dwWritten;
	COMSTAT comStat;

	ClearCommError(m_hCom,&dwErrFlags,&comStat);

	dwWritten = 0;
	for(DWORD i=0; i<towrite; i++)
	{
		WriteComByte(outbuf[i]);
		dwWritten++;
	}

	return dwWritten; //返回实际写入的字节数
}

bool CSerialPortCtrl::WriteComByte(DTOUTB uc)
{
	BOOL bWriteStat;
	DWORD dwBytesWritten;

	bWriteStat = WriteFile( m_hCom,(LPSTR) &uc,1, &dwBytesWritten, &m_OLWrite );
	if( !bWriteStat && ( GetLastError() == ERROR_IO_PENDING ) )
	{
		if( WaitForSingleObject(m_OLWrite.hEvent,1000))
			dwBytesWritten = 0;
		else
		{
			GetOverlappedResult(m_hCom, &m_OLWrite, &dwBytesWritten,true);
			m_OLWrite.Offset += dwBytesWritten;
		}
	}

	return true;
}

DWORD CSerialPortCtrl::ReadCom(DTIN* pinbuf,DWORD toread)
{	//m_hCom非空,根据当前缓冲区中的实际数据,调整要读取的数据数量
	if( !m_bComOpen || m_hCom == NULL )
	{
	//	AfxMessageBox("端口未打开,无法完成读操作。");
		return 0;
	}

	DWORD dwErrFlags;
	COMSTAT comStat;
	ClearCommError(m_hCom,&dwErrFlags,&comStat);

	DWORD dwbnum = min(toread,comStat.cbInQue);
	if(dwbnum == 0) return dwbnum; //端口无数据

	memset(m_cInBuf,0,m_InBufSz*sizeof(DTIN));//
	DWORD onereaded = 0,allreaded = 0;
	for(DWORD i = 0;i<dwbnum;i++)
	{
		bool brf = ReadFile(m_hCom,m_cInBuf+i,sizeof(DTIN),&onereaded,&m_OLRead);
//*
		if(!brf)
		{	//
			DWORD dwLastError = GetLastError();
			if(dwLastError == ERROR_IO_PENDING)
			{
				if(WaitForSingleObject(m_OLRead.hEvent,INFINITE))
					allreaded += onereaded;
			}
		}
		else
			allreaded += onereaded;
//*/
	}

	if(pinbuf) pinbuf = m_cInBuf;

	return allreaded;
}

//* 多线程有效,线程间通信有效,可以监测到SetCommMask创建的EV_RXCHAR事件,但读取端口错误
UINT SPWatcher(LPVOID SPWatInfo)
{
	OVERLAPPED OlWce;	//用于通信事件监视函数的重叠结构
	SPWATCHINFO* pWatInfo = (SPWATCHINFO*)SPWatInfo;
	CSerialPortCtrl* pCom = pWatInfo->pSP;	//端口句柄
	if(!pCom->m_hCom)
	{
		AfxMessageBox("端口监视线程: 端口句柄无效.");
		return 1;
	}

	memset(&OlWce,0,sizeof(OVERLAPPED));
	OlWce.hEvent = CreateEvent(NULL,true,false,NULL);
	if(!OlWce.hEvent)
	{
		AfxMessageBox("端口监视线程: 创建事件句柄失败.");
		return 1;
	}


	DWORD dwLength;
	DWORD dwLastErr;
	DWORD dwEvtMask;

	COMSTAT ComStat;
	DWORD dwErrorFlags;

	while(pCom->m_bComOpen)//true
	{	//端口打开时,循环监视
		ClearCommError(pCom->m_hCom, &dwErrorFlags, &ComStat);
		if(ComStat.cbInQue)//
		{//
			WaitForSingleObject(pWatInfo->hEvt,INFINITE);//
			ResetEvent(pWatInfo->hEvt);//
//
			dwLength = pCom->ReadCom();
		
		//	Beep(1000, 50);

			SendMessage(pWatInfo->hParent,WK_EVT_SPIN,dwEvtMask, min(dwLength, ComStat.cbInQue));
		//	SetEvent(pWatInfo->hEvt);

			continue;
		}//

		dwEvtMask = 0;

		if(!WaitCommEvent(pCom->m_hCom,&dwEvtMask,&OlWce))
		{	//出现端口事件则稍作处理,通知主线程读取端口数据
			dwLastErr = GetLastError();

			if(dwLastErr == ERROR_IO_PENDING)
			{	//等待监视函数成功完成并返回
				DWORD dwTrans = 0;
				GetOverlappedResult(pCom->m_hCom, &OlWce, &dwTrans, true);
			}
			else
			{
				CloseHandle(OlWce.hEvent);
				return 0;//
			}
		}
	}

	//线程结束,则销毁相应资源
	CloseHandle(OlWce.hEvent);
	return 1;
}

⌨️ 快捷键说明

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