📄 serial.cpp
字号:
// 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 + -