📄 serialport.cpp
字号:
#include "stdafx.h"
#include "SerialPort.h"
#include <assert.h>
//
// 构造函数
//
CSerialPort::CSerialPort()
{
m_hComm = NULL;
// 重叠结构成员置0
m_ov.Offset = 0;
m_ov.OffsetHigh = 0;
// 创建事件
m_ov.hEvent = NULL;
m_hWriteEvent = NULL;
m_hShutdownEvent = NULL;
m_szWriteBuffer = NULL;
m_bThreadAlive = FALSE;
}
//
// 析构函数
//
CSerialPort::~CSerialPort()
{
do
{
SetEvent(m_hShutdownEvent);
} while (m_bThreadAlive);
TRACE("Thread ended\n");
delete [] m_szWriteBuffer;
}
//
// 初始化串行口
//
BOOL CSerialPort::InitPort(CWnd* pPortOwner, // 接受串行口信息的窗口
UINT portnr, // 串口数
UINT baud, // 波特率
char parity, // 奇偶
UINT databits, // 数据位
UINT stopbits, // 停止位
DWORD dwCommEvents, // EV_RXCHAR, EV_CTS 等
UINT writebuffersize) // 写缓冲大小
{
assert(portnr > 0 && portnr < 5);
assert(pPortOwner != NULL);
// 线程是激活的则杀掉
if (m_bThreadAlive)
{
do
{
SetEvent(m_hShutdownEvent);//设置关闭事件
} while (m_bThreadAlive);
TRACE("Thread ended\n");
}
// 建立事件
if (m_ov.hEvent != NULL)
ResetEvent(m_ov.hEvent); //设置事件"m_ov.hEvent"为无信号状态
m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hWriteEvent != NULL)
ResetEvent(m_hWriteEvent);//设置事件"m_hWriteEvent"为无信号状态
m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//无继承,手动复位,无信号原始状态,无名的事件
if (m_hShutdownEvent != NULL)
ResetEvent(m_hShutdownEvent);//设置事件"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;
if (m_szWriteBuffer != NULL)
delete [] m_szWriteBuffer;
m_szWriteBuffer = new char[writebuffersize];
m_nPortNr = portnr;
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);
sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);
// 获得串口句柄
m_hComm = CreateFile(szPort, // 串口名
GENERIC_READ | GENERIC_WRITE, // 读写类型
0, // 设置为独占方式
NULL, // 无安全属性
OPEN_EXISTING, // OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // 异步I/O
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;// 设置RTS为高
if (BuildCommDCB(szBaud, &m_dcb))
{
if (SetCommState(m_hComm, &m_dcb))
; // 正常操作...继续
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);
TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);
return TRUE;
}
//
// 串口线程函数
//
UINT CSerialPort::CommThread(LPVOID pParam)
{
// 指派一个void 指针,使其找回类 CSerialPort
CSerialPort *port = (CSerialPort*)pParam;
// 设置一状态变量为TRUE,指示线程已运行.
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_TXABORT);
// 死循环,线程存在一直循环
for (;;)
{
// 呼叫函数 WaitCommEvent(). 这种呼叫立即返回,因为异步口设置
//为(FILE_FLAG_OVERLAPPED) m_OverlappedStructerlapped 结构被指定.
// This call will cause the
// m_OverlappedStructerlapped element m_OverlappedStruct.hEvent,
// which is part of the m_hEventArray to
// be placed in a non-signeled state if there are no bytes available to be read,
// or to a signeled state if there are bytes available.
// 如果事件句柄被设置为无信号状态,在串口有一个字符到来时设置为信号状态
bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
if (!bResult)
{
// 如果 WaitCommEvent() 返回为假, 处理错误
switch (dwError = GetLastError())
{
case ERROR_IO_PENDING:
{
// 常返回值,指示在输入缓冲区中无字符
break;
}
case 87:
{
// 这是NT下的返回值,此值为其他原因
break;
}
default:
{
// 其他的错误码
//port->ProcessErrorMessage("WaitCommEvent()");
break;
}
}
}
else
{
// 如果 WaitCommEvent() 返回真值,检查在读缓冲区是否真的
// 有输入字符,
//一次从缓冲区读取超过一个字符时,第一个字符到达时 会引起
//函数WaitForMultipleObjects()停止等待,函数WaitForMultipleObjects()
//返回时,将重置在m_OverlappedStruct.hEvent中的事件句柄为
//无信号状态
// If in the time between the reset of this event and the call to
// ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
// to the signeled state. When the call to ReadFile() occurs, it will
// read all of the bytes from the buffer, and the program will
// loop back around to WaitCommEvent().
//
// At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
// but there are no bytes available to read. If you proceed and call
// ReadFile(), it will return immediatly due to the async port setup, but
// GetOverlappedResults() will not return until the next character arrives.
//
// It is not desirable for the GetOverlappedResults() function to be in
// this state. The thread shutdown event (event 0) and the WriteFile()
// event (Event2) will not work if the thread is blocked by GetOverlappedResults().
//
// The solution to this is to check the buffer with a call to ClearCommError().
// This call will reset the event handle, and if there are no bytes to read
// we can loop back through WaitCommEvent() again, then proceed.
// If there are really bytes to read, do nothing and proceed.
bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
if (comstat.cbInQue == 0)//输入缓冲区无字符
continue;
} // bResult结束
// 主等待函数,等待事件发生,他正常地阻塞线程,直到要求的事件发生
Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
switch (Event)
{
case 0:
{
// 关闭事件. 指示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)
// 从串口读字符
ReceiveChar(port, comstat);
break;
}
case 2: // 写事件
{
WriteChar(port);
GetCommMask(port->m_hComm, &CommEvent);
if (CommEvent & EV_TXEMPTY)
{
MessageBox(NULL, NULL, "完成", MB_ICONSTOP);
}
// 从串口写字符事件
break;
}
} // switch 结束
} // 关闭死循环
return 0;
}
//
// 启动串口监测线程
//
BOOL CSerialPort::StartMonitoring()
{
if (!(m_Thread = AfxBeginThread(CommThread, this)))
return FALSE;
TRACE("Thread started\n");
return TRUE;
}
//
// 重新启动串口线程
//
BOOL CSerialPort::RestartMonitoring()
{
TRACE("Thread resumed\n");
m_Thread->ResumeThread();
return TRUE;
}
//
//停止串口线程
//
BOOL CSerialPort::StopMonitoring()
{
TRACE("Thread suspended\n");
m_Thread->SuspendThread();
return TRUE;
}
//
// 显示错误信息
//
void CSerialPort::ProcessErrorMessage(char* ErrorText)
{
char *Temp = new char[200];
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 缺省语言
(LPTSTR) &lpMsgBuf,
0,
NULL
);
sprintf(Temp, "WARNING: %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr);
MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);
LocalFree(lpMsgBuf);
delete[] Temp;
}
//
// 写一个字符
//
void CSerialPort::WriteChar(CSerialPort* port)
{
BOOL bWrite = TRUE;
BOOL bResult = TRUE;
DWORD BytesSent = 0;
ResetEvent(port->m_hWriteEvent);
// 获取临界区所有权
EnterCriticalSection(&port->m_csCommunicationSync);
if (bWrite)
{
// 变量初始化
port->m_ov.Offset = 0;
port->m_ov.OffsetHigh = 0;
// 缓冲区刷新
PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
bResult = WriteFile(port->m_hComm, // 串口句柄
port->m_szWriteBuffer, // 写缓冲区
strlen((char*)port->m_szWriteBuffer),// 发送大小
&BytesSent, // 被发送的字节
&port->m_ov); // 重叠结构
// 处理错误码
if (!bResult)
{
DWORD dwError = GetLastError();
switch (dwError)
{
case ERROR_IO_PENDING:
{
// GetOverlappedResults()函数继续
BytesSent = 0;
bWrite = FALSE;
break;
}
default:
{
// 其他错误码
port->ProcessErrorMessage("WriteFile()");
}
}
}
else
{
LeaveCriticalSection(&port->m_csCommunicationSync);
}
} // bWrite结束
if (!bWrite)
{
bWrite = TRUE;
bResult = GetOverlappedResult(port->m_hComm,//串口句柄
&port->m_ov, //重叠结构
&BytesSent,// 发送数量
TRUE); // 等待标志
LeaveCriticalSection(&port->m_csCommunicationSync);
// 处理错误码
if (!bResult)
{
port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
}
} // bWrite结束
// 校验发送的数量与要发送的是否一致
if (BytesSent != strlen((char*)port->m_szWriteBuffer))
{
TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));
}
}
//
// 字符收到,通知串口拥有者
//
void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)
{
BOOL bRead = TRUE;
BOOL bResult = TRUE;
DWORD dwError = 0;
DWORD BytesRead = 0;
unsigned char RXBuff;
for (;;)
{
// 获取串口临界区的所有权,保证无其他的目标使用串口
EnterCriticalSection(&port->m_csCommunicationSync);
// ClearCommError() 更新结构 COMSTAT 清除其他的错误
bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
LeaveCriticalSection(&port->m_csCommunicationSync);
// 死循环开始,直到读取所有字符
if (comstat.cbInQue == 0)
{
// 所有的字符读完,转出
break;
}
EnterCriticalSection(&port->m_csCommunicationSync);
if (bRead)
{
bResult = ReadFile(port->m_hComm,//端口句柄
&RXBuff, // 缓冲区
1, // 读一个字符
&BytesRead, // 发送数量
&port->m_ov);//重叠结构
// 错误码处理
if (!bResult)
{
switch (dwError = GetLastError())
{
case ERROR_IO_PENDING:
{
// 异步在操作中
// 继续GetOverlappedResults()处理
bRead = FALSE;
break;
}
default:
{
// 其他错误码
port->ProcessErrorMessage("ReadFile()");
break;
}
}
}
else
{
// ReadFile() 操作完毕 呼叫GetOverlappedResults()
//不一定必要
bRead = TRUE;
}
} // bRead 结束
if (!bRead)
{
bRead = TRUE;
bResult = GetOverlappedResult(port->m_hComm,//端口句柄
&port->m_ov, //重叠结构
&BytesRead, // 读字符数量
TRUE); // 等待标志
// 处理错误码
if (!bResult)
{
port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");
}
} // bRead结束
LeaveCriticalSection(&port->m_csCommunicationSync);
// 通知父对象有字符收到
::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);
} // 死循环结束
}
//
// 向串口写字符串
//
void CSerialPort::WriteToPort(char* string)
{
assert(m_hComm != 0);
memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
strcpy(m_szWriteBuffer, string);
// 设置写事件
SetEvent(m_hWriteEvent);
}
//
// 返回设备控制块
//
DCB CSerialPort::GetDCB()
{
return m_dcb;
}
//
// 返回通信事件屏蔽
//
DWORD CSerialPort::GetCommEvents()
{
return m_dwCommEvents;
}
//
// 返回写缓冲区大小
//
DWORD CSerialPort::GetWriteBufferSize()
{
return m_nWriteBufferSize;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -