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

📄 serial.cpp

📁 从串口读取数据
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// Serial.cpp: implementation of the CSerial class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Serial.h"

#include "DataStruct.h"

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


#define MSG_READ	0
#define MSG_WRITE	1
//全局变量,暂时存储数据用的
DATA_GROUP	g_read_data_group;
DATA_GROUP	g_send_data_group;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSerial::CSerial()
{
	Init();
}

CSerial::~CSerial()
{
	UnInit();
}
/******************************************************************************
*功能:初始化
*说明:
*参数:
*返回:
******************************************************************************/
void CSerial::Init()
{
	m_h_father_wnd=NULL;
	m_u_message=0;

	m_bOpened = false;
	m_hCommPort=INVALID_HANDLE_VALUE;

	m_start_data_thread=CreateEvent(0,true,0,0);
	m_start_analysis_thread=CreateEvent(0,true,0,0);
	m_start_event_thread=CreateEvent(0,true,0,0);

	m_end_event_thread=CreateEvent(0,true,0,0);
	m_end_analysis_thread=CreateEvent(0,true,0,0);
	m_end_data_thread=CreateEvent(0,true,0,0);

	m_event_thread_end=CreateEvent(0,true,0,0);
	m_analysis_thread_end=CreateEvent(0,true,0,0);
	m_data_thread_end=CreateEvent(0,true,0,0);

	m_data_come_event=CreateEvent(0,true,0,0);
	m_event_come_event=CreateEvent(0,true,0,0);

	m_end_send_thread=CreateEvent(0,true,0,0);
	m_send_thread_end=CreateEvent(0,true,0,0);
	m_send_data_event=CreateEvent(0,true,0,0);
}
/******************************************************************************
*功能:释放资源
*说明:
*参数:
*返回:
******************************************************************************/
void CSerial::UnInit()
{
	Close();

	//关闭一直存活的事件对象
	CloseAndCleanHandle(m_start_event_thread);
	CloseAndCleanHandle(m_start_data_thread);
	CloseAndCleanHandle(m_start_analysis_thread);
	CloseAndCleanHandle(m_end_event_thread);
	CloseAndCleanHandle(m_end_analysis_thread);
	CloseAndCleanHandle(m_end_data_thread);
	CloseAndCleanHandle(m_event_thread_end);
	CloseAndCleanHandle(m_analysis_thread_end);
	CloseAndCleanHandle(m_data_thread_end);
	CloseAndCleanHandle(m_data_come_event);
	CloseAndCleanHandle(m_event_come_event);

	CloseAndCleanHandle(m_end_send_thread);
	CloseAndCleanHandle(m_send_thread_end);
	CloseAndCleanHandle(m_send_data_event);
}
/******************************************************************************
*功能:关闭串口
*说明:
*参数:
*返回:
******************************************************************************/
BOOL CSerial::Close()
{
	if(!m_bOpened)return TRUE;

	m_bOpened=false;

	HANDLE handle[4];
	handle[0]=m_event_thread_end;
	handle[1]=m_analysis_thread_end;
	handle[2]=m_data_thread_end;
	handle[3]=m_send_thread_end;

	SetEvent(m_end_event_thread);
	SetEvent(m_end_send_thread);

	::WaitForMultipleObjects(4,handle,TRUE,INFINITE);

	TRACE("已经关闭了四个线程!\n");
	Sleep(100);

	CloseAndCleanHandle(m_hCommPort);

	//关闭串口时,要重置一下这个信号
	ResetEvent(m_start_event_thread);
	ResetEvent(m_start_data_thread);
	ResetEvent(m_start_analysis_thread);
	ResetEvent(m_end_event_thread);
	ResetEvent(m_end_analysis_thread);
	ResetEvent(m_end_data_thread);
	ResetEvent(m_event_thread_end);
	ResetEvent(m_analysis_thread_end);
	ResetEvent(m_data_thread_end);
	ResetEvent(m_data_come_event);
	ResetEvent(m_event_come_event);

	ResetEvent(m_end_send_thread);
	ResetEvent(m_send_thread_end);
	ResetEvent(m_send_data_event);

	return TRUE;
}
/******************************************************************************
*功能:关闭并非法化句柄
*说明:
*参数:
*返回:
******************************************************************************/
void CSerial::CloseAndCleanHandle(HANDLE &handle)
{
	ASSERT(handle);
	if(handle==INVALID_HANDLE_VALUE)return;
	BOOL bRet=CloseHandle(handle);

	if(!bRet)
	{
		TRACE0( "close handle fail!\n" );
		ASSERT(0);
	}
	else
	{
		handle = INVALID_HANDLE_VALUE;
	}
}
/******************************************************************************
*功能:打开串口
*说明:
*参数:	nCom		:打开哪一个串口--------1,2
		dwBaudRate	:波特率----------------9600?
		byByteSize	:数据位----------------8
		byParity	:奇偶较验位------------0
		byStopBits	:停止位----------------1
*返回: 如果之前已经打开,TRUE,但参数不是最新设置的,而是上次打开串口时的数值;
		如果之前没有打开串口:
			成功,TRUE,失败,FALSE
******************************************************************************/
BOOL CSerial::Open(HWND hWnd,UINT Msg,BYTE nCom, DWORD dwBaudRate,
					BYTE byByteSize,BYTE byParity,BYTE byStopBits)
{
	if(m_bOpened)return TRUE;

	m_h_father_wnd=hWnd;
	m_u_message=Msg;

	CString szPortName;
	szPortName.Format("COM%d",nCom);

	try
	{
		//打开串口
		m_hCommPort = ::CreateFile(
			szPortName,
			GENERIC_READ | GENERIC_WRITE,
			0,
			0,
			OPEN_EXISTING,
			FILE_FLAG_OVERLAPPED,
			0);
		if(m_hCommPort==INVALID_HANDLE_VALUE)
		{
			TRACE("open com port failed!\n");
			CloseAndCleanHandle(m_hCommPort);
			return FALSE;
		}
		TRACE("open com port success!\n");

		//设置过滤事件
		if(!::SetCommMask(m_hCommPort,EV_RXCHAR | EV_ERR))//| EV_TXEMPTY 
		{
			TRACE("setcommmask fail!\n");
			CloseAndCleanHandle(m_hCommPort);
			return FALSE;
		}
		TRACE("SetCommMask success!\n");

		//设置DCB结构
		DCB dcb;
		memset(&dcb,0,sizeof(DCB));
		
		if(!::GetCommState(m_hCommPort,&dcb))
		{
			TRACE("GetCommState(m_hCommPort,&dcb) failed!\n");
			CloseAndCleanHandle(m_hCommPort);
			return FALSE;
		}
		TRACE("GetCommState success!\n");
		
		dcb.BaudRate	= dwBaudRate;
		dcb.ByteSize	= byByteSize;
		dcb.Parity		= byParity;
		if(byStopBits==1)
		{
			dcb.StopBits = ONESTOPBIT;
		}
		else if(byStopBits==2)
		{
			dcb.StopBits = TWOSTOPBITS;
		}
		else
		{
			dcb.StopBits = ONE5STOPBITS;
		}
//*现在不清楚流控怎么用,所以先不写了,默认就行了
//		dcb.fDsrSensitivity = 0;
//		dcb.fDtrControl		= DTR_CONTROL_ENABLE;
//		dcb.fOutxDsrFlow	= 0;
//*/		
		if(!::SetCommState(m_hCommPort,&dcb))
		{
			TRACE("SetCommState(m_hCommPort,&dcb)) failed!\n");
			CloseAndCleanHandle(m_hCommPort);
			return FALSE;
		}
		TRACE("SetCommState success!\n");

		//设置超时,我准备用事件判断接收到单个字符,所以超时这样设了
		COMMTIMEOUTS timeouts;
		memset(&timeouts,0,sizeof(COMMTIMEOUTS));
		timeouts.ReadIntervalTimeout		= MAXDWORD;
		timeouts.ReadTotalTimeoutConstant	= 0;
		timeouts.ReadTotalTimeoutMultiplier	= 0;
		timeouts.WriteTotalTimeoutConstant	= 0;
		timeouts.WriteTotalTimeoutMultiplier= 0;

		if(!::SetCommTimeouts(m_hCommPort,&timeouts))
		{
			TRACE("SetCommTimeouts(m_hCommPort,&timeouts) failed!\n");
			CloseAndCleanHandle(m_hCommPort);
			return FALSE;
		}
		TRACE("SetCommTimeouts success!\n");

//		m_end_read_thread	= CreateEvent(0,true,0,0);	//手动恢复信号

		AfxBeginThread(ReadEventProc,(LPVOID)this);
		AfxBeginThread(AnalysisEventProc,(LPVOID)this);
		AfxBeginThread(ReadDataProc,(LPVOID)this);
		AfxBeginThread(SendDataProc,(LPVOID)this);

		//首先启动读取数据线程,其它线程会依次被它启动
		//退出时,先退出接收事件线程,然后,事件分析线程,最后是数据接收线程
		SetEvent(m_start_data_thread);	

		m_bOpened = TRUE;
	}
	catch(...)
	{
		ASSERT(0);
		CloseAndCleanHandle(m_hCommPort);
	}

	return TRUE;
}
/******************************************************************************
*功能:读取数据线程
*说明:	每接收到一个字符,都会有事件发出。这个线程把接收到的第个字符消息,都
		发送给消息分析线程。
*参数:lpParam:即:CSerial类对象本身
*返回:0
******************************************************************************/
UINT CSerial::ReadEventProc(LPVOID lpParam)
{
	CSerial * pSerial = (CSerial *)lpParam;

	bool bQuit=false;

	DWORD dwEventMask=0;

	//接收到消息重叠
	OVERLAPPED ov;
	memset(&ov,0,sizeof(OVERLAPPED));
	ov.hEvent	= CreateEvent(0,true,0,0);

	//读取串口事件、退出线程信号
	HANDLE mask_handle[2];
	mask_handle[0] = pSerial->m_end_event_thread;
	mask_handle[1] = ov.hEvent;
	
	//检查有没有消息的标志量
	DWORD dwRet;
	BOOL  bRet;
	bool  bHasMessage  =false;
	bool  bMaskOverlapping = false;

	//错误处理
	DWORD dwErrors=0;
	COMSTAT comstate;
	memset(&comstate,0,sizeof(COMSTAT));

	//等等待启动信号,数据处理线程启动后,就会通知接收事件消息线程启动
	::WaitForSingleObject(pSerial->m_start_event_thread,INFINITE);
	TRACE("get event thread start!\n");

	//清空系统缓冲区
	pSerial->ClearSysReadBufferContent();

	while(!bQuit)
	{
		dwEventMask=0;
		bRet=::WaitCommEvent(pSerial->m_hCommPort,&dwEventMask,&ov);
		if(bRet)	//正好得到一个串口消息
		{
			bHasMessage=true;
		}
		else		//没有得到串口消息,看看是否在进行重叠操作吧
		{
			if(GetLastError()==ERROR_IO_PENDING)//正在进行重叠操作...
			{
				bMaskOverlapping=true;//正在进行重叠操作,直接跳出,到下边的处理代码
//				TRACE("已经进入到了重叠操作,errorcode=[%d][%d]\n",GetLastError(),dwEventMask);
			}
			else
			{
				TRACE("WaitCommEvent发生错误!errorcode=[%d][%d]\n",GetLastError(),dwEventMask);
				continue;
			}
		}
		if(bMaskOverlapping)	//如果是在进行叠操作,就等待...
		{
			bMaskOverlapping=false;
			dwRet=::WaitForMultipleObjects(2,mask_handle,false,INFINITE);
			switch(dwRet-WAIT_OBJECT_0)
			{
			case 0://退出线程
				{
					bQuit=true;
				}
				break;
			case 1://接收到消息
				{
					ResetEvent(ov.hEvent);//恢复串口事件信号
					bRet=::GetOverlappedResult(pSerial->m_hCommPort,&ov,&dwErrors,false);
					if(bRet)
					{
						bHasMessage=true;
					}
				}
				break;
			default://未知信号
				{
				}
				break;
			}
		}
		if(bHasMessage)//最终,如果有消息到来
		{
			bHasMessage=false;
/*			dwEventMask=0;
			bRet=::GetCommMask(pSerial->m_hCommPort,&dwEventMask);
			if(!bRet)
			{
				ResetEvent(ov.hEvent);//恢复串口事件信号
				TRACE0("GetCommMask(pSerial->m_hCommPort,&dwEventMask)\n");
				continue;
			}
			//他妈的,原来我用错了,GetCommMask只是取得SetCommMask设置的事件!
*/
			//有消息到来,并且成功得到了消息
			if(EV_ERR == (dwEventMask & EV_ERR) )				//如果发生了错误,就只处理错误
			{
				//发生错误了,要能够恢复才行
				::ClearCommError(pSerial->m_hCommPort,&dwErrors,&comstate);
				TRACE("出现了错误!\n");
			}
/*			else if(EV_TXEMPTY == (dwEventMask & EV_TXEMPTY) )	//完成发送所有数据
			{
				TRACE("数据发送完毕!\n");
				pSerial->NotifyWriteData();
			}
*/
			else if(EV_RXCHAR == (dwEventMask & EV_RXCHAR) )	//读取数据
			{
				static unsigned long l=0;
//				TRACE("事件接收线程接收到有字符到达的消息!nIndex=[%ld]\n",l++);
				SetEvent(pSerial->m_event_come_event);
			}
		}
	}

	pSerial->CloseAndCleanHandle(ov.hEvent);

	TRACE("get event thread end!\n");
//AfxMessageBox("get event thread end!");
	//通知接收数据线程关闭
	SetEvent(pSerial->m_end_analysis_thread);
	SetEvent(pSerial->m_event_thread_end);
	return 0;
}
/******************************************************************************
*功能:分析是否成功接收到一个数据包
*说明:每接收到一个字符,都会有一个消息发送过来。根据两个字符间是否超时来判断是否成功接收到一个数据包
*参数:
*返回:
******************************************************************************/
UINT CSerial::AnalysisEventProc(LPVOID lpParam)
{
	CSerial * pSerial = (CSerial *)lpParam;

	bool bQuit=false;

⌨️ 快捷键说明

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