📄 serialport.cpp
字号:
#include "stdAfx.h"
#include "SerialPort.h"
#include <assert.h>
//构造函数
CSerialPortEx::CSerialPortEx()
{
//初始化overlapped结构
m_ov.Offset = 0;
m_ov.InternalHigh = 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;
}
//初始化串口,可以是从串口1到串口4
BOOL CSerialPortEx::InitPort(CWnd*pPortOwner, //父窗口(接收消息)
UINT portnr, //串口号(1...4)
UINT baud, //数据转输率
char parity, //校验位
UINT databits, //数据位
UINT stopbits, //停止位
DWORD dwCommEvents, //串口状态事件
UINT writebuffersize) //输出缓冲区大小
{
assert(portnr>0 && portnr<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 = CreateEvent(NULL,TRUE,FALSE,NULL);
if(m_hWriteEvent != NULL)
ResetEvent(m_hWriteEvent);
m_hWriteEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if(m_hShutdownEvent != NULL)
ResetEvent(m_hShutdownEvent);
m_hShutdownEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
//初始化监视事件对象
m_hEventArray [0] = m_hShutdownEvent; //最高优先级
m_hEventArray[1] = m_ov.hEvent;
m_hEventArray[2] = m_hWriteEvent;
//初始化临界区
InitializeCriticalSection(&m_csCommunicationSync);
//保存父窗口与串口号
m_pOwner = pPortOwner;
m_nPortNr =portnr;
//创建输出缓冲区
if(m_szWriteBuffer != NULL)
delete[] m_szWriteBuffer;
m_szWriteBuffer = new BYTE[writebuffersize];
m_nWriteBufferSize = writebuffersize;
//保存串口状态事件
m_dwCommEvents = dwCommEvents;
BOOL 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);
printf(szBaud,"baud = %d parity =%c data = %d stop = %d",baud,parity,databits,stopbits);
//打开串口
HANDLE m_hComm = CreateFile(szPort, //串口名称字符串,如“COM1"
GENERIC_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::StopMonioring()
{
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 Event = 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_RXABORT);
//开始无限循环,当该线程Alive时该循环一直进行
for(;;)
{
//检测线路状态,等待SetCommMask 指定事件之一发生,事实上因为CSerialPortEx
//以异步方式操作串口,所以该调用将立刻返回
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
{
//WaitCommEvent 以后,要用ClearCommError清除事件的Flag,以便进行下一轮
//WaitCommEvent,同时这个API可以获得更详细的事件信息
bResult = ClearCommError(port->m_hComm,&dwError,&comstat);
//确认串口中无字符则无须进入主监视过程,重新开始循环
if(comstat.cbInQue == 0)
continue;
} //end if bResult
//主监视函数,该函数将阻塞本线程直至等待的某一事件发生
Event=WaitForMultipleObjects(3,port->m_hEventArray,FALSE,INFINTTE);
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_DELECTED,
(WPARAM)0,(LPARAM)port->m_nPortNr);
if(CommEvent & EV_RXFLAG)
::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_RXFLAG_DELECTED,
(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_DELECTED,
(WPARAM)0,(LPARAM) port->m_nPortNr);
if(CommEvent & EV_RING)
::SendMessage(port->m_pOwner->m_hWnd,WM_COMM_RING_DELECTED,
(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;
}//end switch
} //结束串口主循环
return 0;
}
}
//ReceiveChar 不需用户调用,串口主线程在有数据到达串口时自动调用该函数。
//它将读取到的数据通过自定义消息WM_COMM_RXCHAR发送给父窗口m_pOwner,
//调用者只需在该窗口内处理WM_COMM_RXCHAR消息即可。
void CSerialPortEx::ReceiveChar(CSerialPortEx* port,COMSTAT comstat)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -