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

📄 gpscomm.cpp

📁 非常好用的GPS串口通信
💻 CPP
字号:
///////////////////////////////////////////////////////////////////////////////
// GpsComm.cpp : implementation of the CGpsComm class
//
// Copyright (c) 2004,上海合众思壮科技有限责任公司GIS部
//
// All rights reserved
//
// 文件名称:GpsComm.cpp
//
// 摘要    :通讯基类
//
// 作者    :Hansom
//
// 当前版本:1.1
//
// 完成日期:2004年04月12日
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "GpsComm.h"
#include "Nmea0183.h"

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

const long MSG_SIZE		= 256;							// 定义消息数常量
const long MSG_NUMBER	= 8;							// 定义消息的记录数目


///////////////////////////////////////////////////////////////////////////////
// 初始化静态成员
CGpsComm *CGpsComm::c_GpsCommObj = NULL;

///////////////////////////////////////////////////////////////////////////////
// 返回当前使用的通讯对象指针 
IGpsComm *GetCurrentGpsComm()
{
	return CGpsComm::GetGpsCommObjPtr();
}

///////////////////////////////////////////////////////////////////////////////
// 创建接口指针,并返回需要的指针类型
IGpsComm *CreateGpsComm(GPS_SERIAL_TYPE type)
{
	IGpsComm *pGpsCommPtr = CGpsComm::GetGpsCommObjPtr();

	switch (type)
	{
	case Gps_Type_Nmea0183:
		pGpsCommPtr = new CNmea0183;	// 创建0183类指针
		break;
	case Gps_Type_GarMin:
		//pGpsCommPtr = new CGarMin;	// 创建指向GarMin类的指针
		break;
	default:
		pGpsCommPtr = NULL;				// 创建失败,没有指向任何类的对象
		break;
	}

	return pGpsCommPtr;
}

///////////////////////////////////////////////////////////////////////////////
// 关闭并删除GPSCOMM对象
BOOL CloseGpsComm()
{
	CGpsComm *pGpsCommPtr = CGpsComm::GetGpsCommObjPtr();
	if(pGpsCommPtr != NULL)
	{
		delete pGpsCommPtr;
		pGpsCommPtr = NULL;
	}
	
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// 通讯基类构造函数
CGpsComm::CGpsComm()
{
	m_hComm				= NULL;
	m_Thread			= NULL;
	m_bThreadAlive		= FALSE;
	memset(&m_SerialPara, 0, sizeof(ST_COMM_PARA));
	
	m_pchRevBuffer		= NULL;
	m_nRevRPtr			= 0;
	m_nRevWPtr			= 0;
	m_nMsgCur			= 0;

	// 初始化读写缓冲区和消息队列
	m_pchRevBuffer		= new char[BUFFER_SIZE];

	m_struMsg			= new ST_MSG[MSG_NUMBER];
	for(int i = 0; i < MSG_NUMBER; i++)
	{
		m_struMsg[i].pMsg = new char[MSG_SIZE];
	}

	c_GpsCommObj = this;

	InitializeCriticalSection(&m_csCommunicationSync);
}

///////////////////////////////////////////////////////////////////////////////
// 通讯基类析构函数
CGpsComm::~CGpsComm()
{
	if(m_hComm != NULL)		//关闭端口句柄
	{
		EnterCriticalSection(&m_csCommunicationSync);
		CloseHandle(m_hComm);
		LPDWORD lpExitCode = 0;   
		GetExitCodeThread(m_hComm, lpExitCode);
		TerminateThread(m_hComm, DWORD(lpExitCode));
		m_hComm = NULL;
		LeaveCriticalSection(&m_csCommunicationSync);
	}

	if(m_Thread != NULL)	//关闭读线程句柄
	{
		LPDWORD lpExitCode = 0;   
		GetExitCodeThread(m_Thread, lpExitCode);
		TerminateThread(m_Thread, DWORD(lpExitCode));
		m_Thread = NULL;
	}

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

	for(int i = 0; i < MSG_NUMBER; i++)
	{
		if(m_struMsg[i].pMsg != NULL)
		{
			delete [] m_struMsg[i].pMsg;
			m_struMsg[i].pMsg = NULL;
		}
	}

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

	m_listWnd.RemoveAll();
}

///////////////////////////////////////////////////////////////////////////////
// 启动端口任务
BOOL CGpsComm::OpenComm(CWnd *pwnd, GPS_DATA_TYPE dwMsgType)
{
	if(pwnd == NULL)
	{
		return FALSE;
	}

	ST_LOG_WND LogWnd;
	LogWnd.pWnd = pwnd;
	LogWnd.dwMsgType = dwMsgType;

	// 将窗口句柄加入活动列表
	m_listWnd.AddHead(LogWnd);

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// 开始端口任务
BOOL CGpsComm::Start()
{
	//如果线程处于激活状态,那么先要关闭该线程
	if(m_bThreadAlive)
	{
		do
		{
			LPDWORD lpExitCode = 0;   
			GetExitCodeThread(m_Thread, lpExitCode);
			m_bThreadAlive = FALSE;
			TerminateThread(m_Thread, DWORD(lpExitCode));
		}
		while (m_bThreadAlive);
	}

	// 如果串口已经打开,需要关闭串口
	if(m_hComm != NULL)
	{
		EnterCriticalSection(&m_csCommunicationSync);
		CloseHandle(m_hComm);
		m_hComm = NULL;
		LeaveCriticalSection(&m_csCommunicationSync);
	}

	CString strPort;
	strPort.Format(L"COM%d:", m_SerialPara.cPort);

	// 创建串口HANDLE
	m_hComm = CreateFile(strPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,OPEN_EXISTING, 0, NULL);
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		TCHAR	msgstr[MSG_NUMBER];		//发送错误信息
		m_hComm = NULL;
		wsprintf(msgstr, L"无法打开端口 %d. 错误号:%d", m_SerialPara.cPort, GetLastError());
		AfxMessageBox(msgstr, MB_ICONERROR, MB_OK);
		return FALSE;
	}

	// 设置串口参数
	DCB dcb;
	GetCommState(m_hComm, &dcb);
	dcb.BaudRate		=	DWORD(m_SerialPara.lBaud);
	dcb.fParity			=	BYTE(m_SerialPara.cParity);
	dcb.StopBits		=	BYTE(m_SerialPara.cStopBits);
	dcb.Parity			=	BYTE(m_SerialPara.cParity);
	dcb.ByteSize		=	BYTE(m_SerialPara.cDataBits);
	dcb.fNull			=	FALSE;
	dcb.fAbortOnError	=	FALSE;
	if(!SetCommState(m_hComm, &dcb))
	{
		CloseHandle(m_hComm);
		m_hComm = NULL;
		return FALSE;
	}

	// 设置超时时间
	COMMTIMEOUTS CommTimeouts;
	CommTimeouts.ReadIntervalTimeout			= MAXDWORD;
	CommTimeouts.ReadTotalTimeoutMultiplier		= MAXDWORD;
	CommTimeouts.ReadTotalTimeoutConstant		= 50;
	CommTimeouts.WriteTotalTimeoutMultiplier	= MAXDWORD;
	CommTimeouts.WriteTotalTimeoutConstant		= 0;
	if(!SetCommTimeouts(m_hComm, &CommTimeouts) || !SetCommMask(m_hComm, EV_BREAK | EV_ERR | EV_RXCHAR))
	{
		CloseHandle(m_hComm);
		m_hComm = NULL;
		return FALSE;
	}

	// 清除串口数据
	PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

	if(IsPortOpen())
	{
		// 创建线程,如果失败则返回
		if (!(m_Thread = AfxBeginThread((AFX_THREADPROC)CommThread, this)))
		{
			return FALSE;
		}

		m_bThreadAlive	= TRUE;
		m_nRevRPtr		= 0;
		m_nRevWPtr		= 0;
		m_nMsgCur		= 0;
	}
	else
	{
		return FALSE;
	}

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// 暂停端口任务
BOOL CGpsComm::Stop()
{
	if(m_hComm != NULL)		//关闭端口句柄
	{
		EnterCriticalSection(&m_csCommunicationSync);
		CloseHandle(m_hComm);
		LPDWORD lpExitCode = 0;   
		GetExitCodeThread(m_hComm, lpExitCode);
		TerminateThread(m_hComm, DWORD(lpExitCode));
		m_hComm = NULL;
		LeaveCriticalSection(&m_csCommunicationSync);
	}

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// 暂停端口任务
BOOL CGpsComm::CloseComm(CWnd * pwnd)
{
	if(pwnd == NULL)
	{
		return TRUE;
	}

	ST_LOG_WND LogWnd;
	POSITION oldPos = NULL;

	// This method retrieves the position of the head element of this list
	POSITION pos = m_listWnd.GetHeadPosition();
	while(pos != NULL)
	{
		oldPos = pos;
		LogWnd = (ST_LOG_WND)m_listWnd.GetNext(pos);
		
		// 找到需要注销的窗口句柄将其移出活动列表
		if(LogWnd.pWnd == pwnd)
		{
			m_listWnd.RemoveAt(oldPos);
			break;
		}
	}

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// 得到需要的消息
BOOL CGpsComm::GetNeedMsg(DWORD dwMsgType, GPS_DATA_TYPE emMsg)
{
	return (((dwMsgType & ENUM_All_GPS) && (emMsg < ENUM_ALL_NAVI)) ||
			((dwMsgType & ENUM_ALL_NAVI)) && (emMsg > ENUM_ALL_NAVI));
};

///////////////////////////////////////////////////////////////////////////////
// 设置当前串口参数
BOOL CGpsComm::SetCommPara(const ST_COMM_PARA &pSerialPara)
{
	// 串口参数设置
	memcpy(&m_SerialPara, &pSerialPara, sizeof(ST_COMM_PARA));

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// 得到端口参数
ST_COMM_PARA CGpsComm::GetCommPara() const
{
	return m_SerialPara;
}

///////////////////////////////////////////////////////////////////////////////
// 函数名:ReceiveData
// 功能  :读取串口接收缓冲区的所有数据,写到接收缓冲区中。
//		   并调用协议的成员函数将数据转换为对应的消息结构
//		   发送消息到当前注册的需要数据的窗口。
// 返回值:无
///////////////////////////////////////////////////////////////////////////////
void CGpsComm::ReceiveData()
{
	DWORD	dwError		= 0;
	DWORD	BytesRead	= 0;
	DWORD	dwReadNum	= 0;
	DWORD	dwNum		= 0;
 	BOOL	bResult;
	char	RXBuff;
	COMSTAT comstat;

	if(m_hComm == NULL)
	{
		return;
	}
	
	bResult	= ClearCommError(m_hComm, &dwError, &comstat);
	dwNum	= comstat.cbInQue;

	while(dwNum > 0)
	{
		// 从串口读出一个字符
		bResult = ReadFile(m_hComm, &RXBuff, 1, &BytesRead, NULL);
		// 发生读错误
		if(!bResult)
		{
			DWORD dwRead = ::GetLastError();
				
			if(dwRead == ERROR_OPERATION_ABORTED)
			{
				::ClearCommError(m_hComm, &dwError, &comstat);
			}
			break;
		}

		if (1 != BytesRead) 
		{
			break;
		}

		// 将字符写入接收缓冲区
		WriteData(RXBuff);
		dwReadNum++;
		dwNum--;
	}

	if(dwReadNum > 0)
	{
		// 有新数据读出
		int nRtn;
		do
		{
			nRtn = this->SearchOneFrame(m_pchRevBuffer, BUFFER_SIZE, m_nRevRPtr, 
											   m_nRevWPtr, m_struMsg + m_nMsgCur);
			if(nRtn == 0)
			{
				break;	// 结束
			}

			// 帧正确
			if(nRtn > 0)
			{
				// 可以在此处输出帧数据

				POSITION pos = m_listWnd.GetHeadPosition();
				ST_LOG_WND LogWnd;
				for(int i = 0; i < m_listWnd.GetCount(); i++)
				{
					LogWnd = (ST_LOG_WND)m_listWnd.GetNext(pos);
					if((LogWnd.pWnd != NULL) && 
					   (GetNeedMsg(LogWnd.dwMsgType, (m_struMsg+m_nMsgCur)->MsgHead.enumMsgType)))
					{
						::PostMessage(LogWnd.pWnd->m_hWnd, 
							          WM_COMM_RXCHAR, (WPARAM)(m_struMsg + m_nMsgCur), 0);
					}
				}
				
				m_nMsgCur++;
				if(m_nMsgCur >= MSG_NUMBER)
				{
					m_nMsgCur = 0;
				}
			}
		}while(nRtn != 0);
	}
}
/*
BOOL	CGpsComm::SendData(strucMsg* pMsg)
{
	DWORD BytesSent = 0;
	BOOL bResult;

	//串口未打开
	if(m_hComm == NULL)
		return FALSE;

	//将数据结果转换为协议帧格式
	int nFrmLen;
	int nFrmNo = 0;
	do
	{//nFrmNo代表帧序号,为零代表无后续帧,该帧为结束帧
		nFrmLen = m_pProtocol->TranslateData(pMsg,m_pchSendBuffer,nFrmNo);
		if(nFrmLen == 0)
			return FALSE;

		EnterCriticalSection(&m_csCommunicationSync);
		bResult = WriteFile(m_hComm,m_pchSendBuffer,nFrmLen,&BytesSent,NULL);
		LeaveCriticalSection(&m_csCommunicationSync);

		if((!bResult) || ((int)BytesSent != nFrmLen))
		//写错误
			return FALSE;
		//连续发多帧,间隔100ms
		if(nFrmNo != 0)
			Sleep(100);
	}while(nFrmNo != 0);

	return TRUE;
}
*/
///////////////////////////////////////////////////////////////////////////////
// 串口监听线程
UINT CGpsComm::CommThread(LPVOID pParam)
{
	CGpsComm *port = (CGpsComm*)pParam;
	if(port == NULL)
	{
		return 0;
	}

	DWORD	CommEvent	=	0;
	DWORD	dwError		=	0;
	DWORD	dwBytesRead	=	0;
	char	RxBuff		=	0;
	BOOL	bResult		=	TRUE;
	COMSTAT comstat;
	
	// 用ReadFile()监听串口
	while(port->IsPortOpen())
	{
		if(NULL == port->m_hComm)
		{
			break;
		}

		EnterCriticalSection(&port->m_csCommunicationSync);
		bResult = ReadFile(port->m_hComm, &RxBuff, 1, &dwBytesRead, NULL);
		LeaveCriticalSection(&port->m_csCommunicationSync);

		if(NULL == port->m_hComm)
		{
			break;
		}
		
		if(!bResult)
		{
			// 读出错
			DWORD dwRead = ::GetLastError();
				
			if(dwRead == ERROR_OPERATION_ABORTED)
			{
				::ClearCommError(port->m_hComm, &dwError, &comstat);
			}
			continue;
		}

		if(1 != dwBytesRead) 
		{
			continue;
		}

		// 将字符写入接收缓冲区
		port->WriteData(RxBuff);
		// 接收剩余其他字符
		EnterCriticalSection(&port->m_csCommunicationSync);
		port->ReceiveData();
		LeaveCriticalSection(&port->m_csCommunicationSync);
	}

	return 0;
}

///////////////////////////////////////////////////////////////////////////////
// 函数名:	WriteData
// 功能  :	将一个字符写入接收缓冲区
// 参数  :	chData:	被写入的字符
// 返回值:	无
///////////////////////////////////////////////////////////////////////////////
void CGpsComm::WriteData(char chData)
{
	// 将字符写入接收缓冲区	
	m_pchRevBuffer[m_nRevWPtr] = chData;
	m_nRevWPtr++;

	if(m_nRevWPtr >= BUFFER_SIZE)
	{
		m_nRevWPtr = 0;		// 收满缓冲区
	}

	if(m_nRevWPtr == m_nRevRPtr)
	{
		m_nRevRPtr++;
		if(m_nRevRPtr >= BUFFER_SIZE)
		{
			m_nRevRPtr = 0;
		}
	}
}

⌨️ 快捷键说明

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