?? 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 + -