📄 serialport.cpp
字号:
#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 + -