首先说明一点,这个源代码基础类最初是老外写的,我只是把它从MFC编译器移植过来到QT下面,不是很熟悉QT的线程模式,所以沿用了这种C++通讯多线程模式的,目前只能适用于windows系统。
其中我也调整了一些地方,增加了派生类,主要用于通讯协议的解析。原先是准备采用两个overlapped结构体,来取消线程锁的criticalsection锁,想象中这样读写可以并行更快一些,但是发现效果不理想,大多数的下位机单片机设备一般都是单线程的,即使上位机与串口是全双工的,也最好是单线程通讯为佳。
如果大家有好的建议,或者是有好的优化方式,欢迎留言,或者站内信息,谢谢!
//----------------------------TSerial.h---------------------//
#ifndef TSERIAL_H
#define TSERIAL_H
#include <QObject>
#include <windows.h>
//一致性宏定义--MFC习惯的变量名转移
#define DWORD quint32
#define BYTE quint8
#define LPBYTE quint8*
#define LPDWORD quint32*
class CTSerial : public QObject
{
Q_OBJECT
public:
// contruction and destruction
CTSerial();
virtual ~CTSerial();
// port initialisation
BOOL InitPort(UINT portnr = 1, UINT baud = 9600, char parity = 'N', UINT databits = 8, UINT stopsbits = 1, DWORD dwCommEvents = EV_RXCHAR, UINT nBufferSize = 512);
// start/stop comm watching
BOOL StartMonitoring();
//BOOL RestartMonitoring();
//BOOL StopMonitoring();
DWORD GetWriteBufferSize();
DWORD GetCommEvents();
DCB GetDCB();
void WriteToPort(LPBYTE pIn,int nsz);
void ProcessErrorMessage(const char *pErrorText);
void PushTo(BYTE);
void CloseAll();
void ResetTimeoutLimit(int nLimit);
//virtual BYTE CheckIfTimeout()=0;//设置各自的超时检验机制
//virtual void SetSlotOperation()=0;//设置各自的槽函数调用模式
protected:
// protected memberfunctions
static void CommThread(LPVOID pParam);
virtual void ReceiveChar(COMSTAT comstat) = 0;
void WriteChar();
// thread
uintptr_t m_Thread;
// synchronisation objects
CRITICAL_SECTION m_csCommunicationSync;
BOOL m_bThreadAlive;
// handles
HANDLE m_hShutdownEvent;
HANDLE m_hComm;
HANDLE m_hWriteEvent;
// Event array.
// One element is used for each event. There are two event handles for each port.
// A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).
// There is a general shutdown when the port is closed.
HANDLE m_hEventArray[3];
// structures
OVERLAPPED m_ov;
//OVERLAPPED m_ovbp;
COMMTIMEOUTS m_CommTimeouts;
DCB m_dcb;
// misc
UINT m_nPortNr;
int nCacheSize;
LPBYTE m_szWriteBuffer;
LPBYTE m_szReadBuffer;
int nReadStart,nReadPos;
DWORD m_dwCommEvents;
DWORD m_nWriteBufferSize;
int nWriteLen;
int nAlreadyTimout;
int nReplyTimeset, ncountfortimeout;
int nLastReplyMoment,naccumulate;
};
class CBoxPort : public CTSerial//用于一台传感器的串口数据解析,仅供参考
{
Q_OBJECT
private:
int nAI[20];
float AIf;
BYTE uDI[20];//0 1 2 3代表DI输入量 [9]-上位机写入的DO的数据字段
int nDIcnts;
//int ntotalAI;
DWORD dwtemp;
BYTE ucodes;
int nReadNeed;//实际需要读取的长度
BYTE uExit;
uintptr_t hBox;
BYTE uCodeNeed;
int nsleepms;//查询的周期
int nacccount,nacclen;//用于累积发送通知用
BYTE uClientAddr;//从站的地址
int ntotalcnt, ncur, npos;//用于实时数据的累积
double *pdbVal;
public:
double dbAIValue[20];
BYTE uDIBit;//需要关注哪一位
CBoxPort();
void InitAllVariables();
int SendQueryCmd();
static void theFunc(LPVOID pp);
void openBox();//子线程仅用于等间隔向串口发送传感器的查询用心跳包,获取传感器状态值
void closeBox();//关闭这个查询子线程
void resetVariables(int ms,int addr);
void resetSavedbuf(double* pin, int ln);
int retrieveSavedPos();
void PopData();
signals:
void PostDataMsg(int nflag);//收到了相关的数据信息
//void PostCRCMsg(BYTE uuflag);
protected:
virtual void ReceiveChar(COMSTAT comstat);
int GetIOInfo();
void DebugPrint(LPBYTE pin, int len);
};
#endif // TSERIAL_H
//-----------------------------TSerial.cpp-------------------------//
#include "TSerial.h"
#include <assert.h>
#include <QDebug>
unsigned short MyAcc_16(BYTE *Array,DWORD Len)
{
unsigned short IX,IY,CRC;
CRC=0xFFFF;//set all 1
if(Len<=0)
CRC = 0;
else
{
Len--;
for (IX=0;IX<=Len;IX++)
{
CRC=CRC^(unsigned int)(Array[IX]);
for(IY=0;IY<=7;IY++)
{
if((CRC&1)!=0 )
CRC=(CRC>>1)^0xA001;
else
CRC=CRC>>1;
}
}
}
return (CRC<<8)|(CRC>>8);
}
unsigned short MyAcc2_16(LPBYTE pIn,DWORD len)
{
int i,k;
unsigned short uu,tt;
k = len/2;
uu = 0;
for(i=0;i<k;i++)
{
tt = pIn[2*i+1];
uu += (tt<<8) + pIn[2*i];
}
return uu;
}
//
// Constructor
//
CTSerial::CTSerial()
{
m_hComm = NULL;
// initialize overlapped structure members to zero
m_ov.Offset = 0;
m_ov.OffsetHigh = 0;
m_ov.InternalHigh = 0;
m_ov.Internal = 0;
// create events
m_ov.hEvent = NULL;
m_hWriteEvent = NULL;
m_hShutdownEvent = NULL;
m_szWriteBuffer = NULL;
m_bThreadAlive = FALSE;
nWriteLen = 0;
nCacheSize = 0;
nReadStart = nReadPos = 0;
nReplyTimeset = 0;
nAlreadyTimout = 0;
ncountfortimeout = 2;
naccumulate = 0;
nLastReplyMoment = GetTickCount();
//m_ovbp.Offset = 0;
//m_ovbp.OffsetHigh = 0;
//m_ovbp.hEvent = NULL;
}
//
// Delete dynamic memory
//
CTSerial::~CTSerial()
{
//do
//{
// SetEvent(m_hShutdownEvent);
//} while (m_bThreadAlive);
//TRACE("Thread ended\n");
//delete [] m_szWriteBuffer;
}
//
// Initialize the port. This can be port 1 to 4.
//
BOOL CTSerial::InitPort(UINT portnr, // portnumber (1..4)
UINT baud, // baudrate
char parity, // parity
UINT databits, // databits
UINT stopbits, // stopbits
DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc
UINT writebuffersize) // size to the writebuffer
{
assert(portnr > 0 && portnr < 20);
// if the thread is alive: Kill
if (m_bThreadAlive)
{
do
{
SetEvent(m_hShutdownEvent);
} while (m_bThreadAlive);
qDebug()<<"Thread ended";
}
// create events
m_ov.Offset = 0;
m_ov.OffsetHigh = 0;
m_ov.InternalHigh = 0;
m_ov.Internal = 0;
if (m_ov.hEvent != NULL)
ResetEvent(m_ov.hEvent);
m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
//ResetEvent(m_ov.hEvent);
m_ovbp.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);
// initialize the event objects
m_hEventArray[0] = m_hShutdownEvent; // highest priority
m_hEventArray[1] = m_hWriteEvent;
m_hEventArray[2] = m_ov.hEvent;
// initialize critical section
InitializeCriticalSection(&m_csCommunicationSync);
if (m_szWriteBuffer != NULL)
delete [] m_szWriteBuffer;
m_szWriteBuffer = new BYTE[writebuffersize*2];
m_szReadBuffer = m_szWriteBuffer + writebuffersize;
nReadPos = nReadStart = 0;
nCacheSize = writebuffersize;
nAlreadyTimout = 0;
m_nPortNr = portnr;
m_nWriteBufferSize = writebuffersize;
m_dwCommEvents = dwCommEvents;
TCHAR *szPort = new TCHAR[50];
TCHAR *szBaud = new TCHAR[50];
// now it critical!
EnterCriticalSection(&m_csCommunicationSync);
// if the port is already opened: close it
if (m_hComm != NULL)
{
CloseHandle(m_hComm);
m_hComm = NULL;
}
// prepare port strings
if(portnr<10)swprintf(szPort, L"COM%d", portnr);
else
swprintf(szPort, L"\\\\.\\COM%d", portnr);
swprintf(szBaud, L"baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);
// get a handle to the port
m_hComm = CreateFileW(szPort, // communication port string (COMX)
GENERIC_READ | GENERIC_WRITE, // read/write types
0, // comm devices must be opened with exclusive access
NULL, // no security attributes
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // Async I/O
0); // template must be 0 for comm devices
if (m_hComm == INVALID_HANDLE_VALUE)
{
// port not found
delete [] szPort;
delete [] szBaud;
return FALSE;
}
// set the timeout values
m_CommTimeouts.ReadIntervalTimeout = 1000;
m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;
m_CommTimeouts.ReadTotalTimeoutConstant = 1000;
m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;
m_CommTimeouts.WriteTotalTimeoutConstant = 1000;
// configure
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 (BuildCommDCBW(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;
// flush the port
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
// release critical section
LeaveCriticalSection(&m_csCommunicationSync);
//qDebug()<<"Initialisation for communicationport"<<portnr<<"completed.";
return TRUE;
}
//
// The CommThread Function.
//
void CTSerial::CommThread(LPVOID pParam)
{
CTSerial *port = (CTSerial*)pParam;
port->m_bThreadAlive = TRUE;
// Misc. variables
//DWORD BytesTransfered = 0;
unsigned long Event = 0;
unsigned long CommEvent = 0;
unsigned long dwError = 0;
COMSTAT comstat;
ZeroMemory(&comstat,sizeof(COMSTAT));
BOOL bResult = TRUE;
port->m_ov.Internal = 0;
port->m_ov.InternalHigh = 0;
port->m_ov.Offset = 0;
port->m_ov.OffsetHigh = 0;
// Clear comm buffers at startup
if (port->m_hComm) // check if the port is opened
PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
// begin forever loop. This loop will run as long as the thread is alive.
for (;;)
{
// Make a call to WaitCommEvent(). This call will return immediatly
// because our port was created as an async port (FILE_FLAG_OVERLAPPED
// and an m_OverlappedStructerlapped structure specified). 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. If this event handle
// is set to the non-signeled state, it will be set to signeled when a
// character arrives at the port.
// we do this for each port!
// port->m_ov.Internal = 0;
// port->m_ov.InternalHigh = 0;
// port->m_ov.Offset = 0;
// port->m_ov.OffsetHigh = 0;
bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
if (!bResult)
{
// If WaitCommEvent() returns FALSE, process the last error to determin
// the reason..
switch (dwError = GetLastError())
{
case ERROR_IO_PENDING:
{
// This is a normal return value if there are no bytes
// to read at the port.
// Do nothing and continue
//qDebug()<<"===waitcommevent ERROR_IO_PENDING";
break;
}
case 87:
{
// Under Windows NT, this value is returned for some reason.
// I have not investigated why, but it is also a valid reply
// Also do nothing and continue.
qDebug()<<"===waitcommevent ERROR_87";
break;
}
default:
{
// All other error codes indicate a serious error has
// occured. Process this error.
//port->ProcessErrorMessage("WaitCommEvent()");
qDebug()<<"===waitcommevent ERROR_for "<<dwError;
break;
}
}
}
else
{
bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
if (comstat.cbInQue == 0)
{
Sleep(0);
continue;
}
} // end if bResult
Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
//qDebug()<<"COMM ev"<<Event<<" with read "<<comstat.cbInQue;
//if(Event==2 && !comstat.cbInQue)continue;
//qDebug()<<"Waitcommevent for data len="<<comstat.cbInQue;
switch (Event)
{
case 0:
{
port->m_bThreadAlive = FALSE;
// Kill this thread. break is not needed, but makes me feel better.
_endthread();
break;
}
case 1: // read event
{
port->WriteChar();
//qDebug()<<"COMM send info len="<<port->nWriteLen;
break;
}
case 2: // write event
{
GetCommMask(port->m_hComm, &CommEvent);
if (CommEvent & EV_RXCHAR)
{
//qDebug()<<"my serial port read info len="<<comstat.cbInQue;
port->ReceiveChar(comstat);
}
break;
}
} // end switch
} // close forever loop
}
//
// start comm watching
//
BOOL CTSerial::StartMonitoring()
{
nReplyTimeset=ncountfortimeout=0;
nLastReplyMoment = 0;
naccumulate = 0;
if (!(m_Thread = _beginthread(CommThread,0,this)))
return FALSE;
qDebug()<<"Thread started";
return TRUE;
}
//
// Restart the comm thread
//
//
// If there is a error, give the right message
//
void CTSerial::ProcessErrorMessage(const char *pErrorText)
{
qDebug()<<pErrorText;
}
//
// Write a string to the port
//
void CTSerial::WriteToPort(LPBYTE pIn,int nsz)
{
assert(m_hComm != 0);
nWriteLen = nsz;
//memset(m_szWriteBuffer,0,m_nWriteBufferSize);
memcpy_s(m_szWriteBuffer,m_nWriteBufferSize,pIn,nsz);
// set event for write
SetEvent(m_hWriteEvent);
}
//
// Return the device control block
//
DCB CTSerial::GetDCB()
{
return m_dcb;
}
//
// Return the communication event masks
//
DWORD CTSerial::GetCommEvents()
{
return m_dwCommEvents;
}
//
// Return the output buffer size
//
DWORD CTSerial::GetWriteBufferSize()
{
return m_nWriteBufferSize;
}
void CTSerial::WriteChar()
{
BOOL bWrite = TRUE;
BOOL bResult = TRUE;
unsigned long BytesSent = 0;
ResetEvent(m_hWriteEvent);
// Gain ownership of the critical section
EnterCriticalSection(&m_csCommunicationSync);
if (bWrite)
{
// Initailize variables
m_ov.Offset = 0;
m_ov.OffsetHigh = 0;
m_ov.InternalHigh = 0;
m_ov.Internal = 0;
//m_ovbp.Offset = 0;
//m_ovbp.OffsetHigh = 0;
// m_ovbp.InternalHigh = 0;
// m_ovbp.Internal = 0;
// Clear buffer
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
bResult = WriteFile(m_hComm,m_szWriteBuffer,nWriteLen,&BytesSent,&m_ov);
//bResult = WriteFile(m_hComm,m_szWriteBuffer,nWriteLen,&BytesSent,&m_ovbp);
// deal with any error codes
if (!bResult)
{
DWORD dwError = GetLastError();
switch (dwError)
{
case ERROR_IO_PENDING:
{
// continue to GetOverlappedResults()
BytesSent = 0;
bWrite = FALSE;
break;
}
default:
{
// all other error codes
ProcessErrorMessage("WriteFile()");
}
}
}
else
{
LeaveCriticalSection(&m_csCommunicationSync);
}
} // end if(bWrite)
if (!bWrite)
{
bWrite = TRUE;
bResult = GetOverlappedResult(m_hComm,&m_ov,&BytesSent,TRUE);
//bResult = GetOverlappedResult(m_hComm,&m_ovbp,&BytesSent,TRUE);
LeaveCriticalSection(&m_csCommunicationSync);
// deal with the error code
if (!bResult)
{
ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
}
} // end if (!bWrite)
// Verify that the data size send equals what we tried to send
if (BytesSent != nWriteLen)
{
qDebug()<<"WARNING: WriteFile() error.. Bytes Sent: "<<BytesSent<<" Message Length: "<<nWriteLen;
}
//qDebug()<<"-----Bytes Sent: "<<BytesSent;
}
void CTSerial::PushTo(BYTE uu)
{
if(nReadPos<nCacheSize)
{
m_szReadBuffer[nReadPos] = uu;
nReadPos += 1;
}
}
void CTSerial::CloseAll()
{
int ik = 0;
do
{
ik++;
SetEvent(m_hShutdownEvent);
Sleep(50);
if(ik>50)
break;
} while (m_bThreadAlive);
qDebug()<<"Thread end!";
if(m_szWriteBuffer)
{
delete [] m_szWriteBuffer;
m_szWriteBuffer = NULL;
}
if(m_hComm != NULL)
{
CloseHandle(m_hComm);
m_hComm = NULL;
}
if(m_ov.hEvent != NULL)
{
CloseHandle(m_ov.hEvent);
m_ov.hEvent = NULL;
}
// if(m_ovbp.hEvent != NULL)
// {
// CloseHandle(m_ovbp.hEvent);
// m_ovbp.hEvent = NULL;
// }
if(m_hShutdownEvent)
{
CloseHandle(m_hShutdownEvent);
m_hShutdownEvent = NULL;
}
if(m_hWriteEvent)
{
CloseHandle(m_hWriteEvent);
m_hWriteEvent = NULL;
}
}
void CTSerial::ResetTimeoutLimit(int nLimit)
{
nAlreadyTimout = 0;
nReplyTimeset = 0;
ncountfortimeout = nLimit;
naccumulate = 0;
}
//---------------------------------------------------------------继承类
unsigned char auchCRCHi[ ] =
{
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00,
0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80,
0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00,
0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80,
0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00,
0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00,
0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40
};
unsigned char auchCRCLo[ ] =
{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE,
0x0E,0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19,
0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C,
0xDC,0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13,
0xD3,0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2,
0x32,0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD,
0x3D,0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8,
0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F,
0xEF,0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6,
0x26,0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61,
0xA1,0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64,
0xA4,0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B,
0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A,
0xBA,0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75,
0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70,
0xB0,0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57,
0x97,0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E,
0x5E,0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49,
0x89,0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C,
0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43,
0x83,0x41, 0x81, 0x80, 0x40
};
unsigned short CRC16ModBus(unsigned char *puchMsg, unsigned short usDataLen)
{
unsigned char uchCRCHi = 0xFF;
unsigned char uchCRCLo = 0xFF;
unsigned char uIndex;
unsigned short i = 0;
while (usDataLen-- > 0)
{
uIndex = (unsigned char)(uchCRCHi ^ puchMsg[i++]);
uchCRCHi = (unsigned char)(uchCRCLo ^ auchCRCHi[uIndex]);
uchCRCLo = auchCRCLo[uIndex];
}
//return (unsigned short)(( unsigned short)uchCRCHi << 8 | uchCRCLo);
return (unsigned short)(( unsigned short)uchCRCLo << 8 | uchCRCHi);
}
CBoxPort::CBoxPort() : CTSerial()
{
nReadNeed = 37;
uClientAddr = 0x2;
ucodes = 0;
nDIcnts = 0;
uDIBit = 0;
nsleepms = 200;
ZeroMemory(nAI,sizeof(int)*20);
ZeroMemory(uDI,20);
ZeroMemory(dbAIValue,sizeof(double)*20);
uExit = 1;
uCodeNeed = 0x3;
resetSavedbuf(NULL,0);
}
void CBoxPort::InitAllVariables()
{
//ntotalAI = 0;
nDIcnts = 0;
uDIBit = 0;
ZeroMemory(nAI,sizeof(int)*20);
ZeroMemory(uDI,20);
ZeroMemory(dbAIValue,sizeof(double)*20);
}
int CBoxPort::SendQueryCmd()
{
nWriteLen = 8;
LPBYTE pSendBlock = m_szWriteBuffer;
pSendBlock[0] = uClientAddr;
pSendBlock[1] = uCodeNeed;
pSendBlock[2] = pSendBlock[3] = pSendBlock[4] = 0;
pSendBlock[5] = 0x10;
unsigned short kk = CRC16ModBus(pSendBlock,6);
pSendBlock[6] = (kk&0xFF);
pSendBlock[7] = (kk>>8);
nReadNeed = 37;
//ucodes = 0x3;
if(nWriteLen)
SetEvent(m_hWriteEvent);
return nWriteLen;
}
void CBoxPort::ReceiveChar(COMSTAT comstat)
{
BOOL bRead = TRUE;
BOOL bResult = TRUE;
unsigned long dwError = 0;
unsigned long BytesRead = 0;
unsigned char RXBuff;
ResetEvent(m_ov.hEvent);
for (;;)
{
EnterCriticalSection(&m_csCommunicationSync);
bResult = ClearCommError(m_hComm, &dwError, &comstat);
LeaveCriticalSection(&m_csCommunicationSync);
if (comstat.cbInQue == 0)
{
CancelIo(m_hComm);
break;
}
EnterCriticalSection(&m_csCommunicationSync);
if (bRead)
{
bResult = ReadFile(m_hComm, // Handle to COMM port
&RXBuff, // RX Buffer Pointer
1, // Read one byte
&BytesRead, // Stores number of bytes read
&m_ov); // pointer to the m_ov structure
// deal with the error code
if (!bResult)
{
switch (dwError = GetLastError())
{
case ERROR_IO_PENDING:
{
// asynchronous i/o is still in progress
// Proceed on to GetOverlappedResults();
bRead = FALSE;
break;
}
default:
{
// Another error has occured. Process this error.
qDebug()<<"ReadFile()";
break;
}
}
}
else
{
// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
bRead = TRUE;
}
} // close if (bRead)
if (!bRead)
{
bRead = TRUE;
bResult = GetOverlappedResult(m_hComm, // Handle to COMM port
&m_ov, // Overlapped structure
&BytesRead, // Stores number of bytes read
TRUE); // Wait flag
// deal with the error code
if (!bResult)
{
qDebug()<<"GetOverlappedResults() in ReadFile()";
}
} // close if (!bRead)
LeaveCriticalSection(&m_csCommunicationSync);
// notify parent that a byte was received
//::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM)RXBuff, (LPARAM)port->m_nPortNr);
PushTo(RXBuff);
} // end forever loop
GetIOInfo();
}
int CBoxPort::GetIOInfo()
{
if(nReadPos<nReadNeed)
return 0;
//DebugPrint(m_szReadBuffer,nReadPos);
int i,cnt,len,ssl;
LPBYTE pIn = m_szReadBuffer;
len = nReadPos;
cnt = nReadPos-5;
BYTE uua,uub,uSaved;
uSaved = 0;
unsigned short ucrc = 0;
for(i=0;i<=cnt;i++)
{
if((pIn[i]!=uClientAddr)||(pIn[i+1]!=uCodeNeed)||(pIn[i+2]!=cnt))
{
//::PostMessageW(m_pOwner->m_hWnd,2020,0,pIn[i]);
//emit PostDataMsg(1);//从站地址错误
continue;
}
ssl = pIn[i+2]/4;
if(i+4+pIn[i+2]<len)
{
ucrc = CRC16ModBus(pIn+i,nReadNeed-2);
uua = (ucrc&0xFF);
uub = (ucrc>>8);
if(uub!=pIn[i+3+pIn[i+2]+1] || uua!=pIn[i+3+pIn[i+2]])
{
//::PostMessageW(m_pOwner->m_hWnd,2030,0,0);
//TRACE0("Error: get comm packet with wrong CRC\n");
//qDebug()<<"Error: get comm packet with wrong CRC";
//emit PostDataMsg(0);//CRC错误
continue;
}
if(ssl>=2)
{
nAI[0] = (int(pIn[i+3])<<8) + pIn[i+4];
nAI[0] = (nAI[0]<<16) + (int(pIn[i+5])<<8) + pIn[i+6];
dbAIValue[0] = double(nAI[0])/1000;
qDebug()<<"Rx: "<<dbAIValue[0];
PopData();
nacccount += 1;
if(nacccount>=nacclen)
{
nacccount = 0;
emit PostDataMsg(200);//获取数据
}
}
i = i+5+4*ssl;
}
else
{
uSaved = 1;
if(i)
{
memcpy(pIn,pIn+i,len-i);
nReadPos = len-i;
}
break;
}
}
qDebug()<<"saved:"<<uSaved<<" , pos:"<<nReadPos;
if(!uSaved)
nReadPos = 0;
return 0;
}
void CBoxPort::DebugPrint(LPBYTE pin, int len)
{
if(pin && len>0)
{
QString sa, ss;
int i;
for(i=0;i<len;i++)
{
ss.asprintf("%02X ",pin[i]);
sa += ss;
}
qDebug()<<"rx: "<<sa;
}
}
void CBoxPort::theFunc(LPVOID pp)
{
CBoxPort *pb = (CBoxPort *)pp;
while(1)
{
if(pb->uExit)
break;
if(pb->m_bThreadAlive)
pb->SendQueryCmd();
Sleep(pb->nsleepms);
}
CloseHandle((HANDLE)pb->hBox);
}
void CBoxPort::openBox()
{
uExit = 0;
hBox = _beginthread(theFunc,0,this);
}
void CBoxPort::closeBox()
{
uExit = 1;
}
void CBoxPort::resetVariables(int ms,int addr)
{
if(ms>9)
nsleepms = ms;
else
nsleepms = 100;
nacccount = 0;
nacclen = 1000/nsleepms;
uClientAddr = addr;
}
void CBoxPort::resetSavedbuf(double* pin, int ln)
{
ntotalcnt = ln;
ncur = npos = 0;
pdbVal = pin;
}
int CBoxPort::retrieveSavedPos()
{
return npos;
}
void CBoxPort::PopData()
{
if(npos>=ntotalcnt)
{
int kk = ntotalcnt/2;
memcpy(pdbVal,pdbVal+kk,(kk-1)*sizeof(double));
pdbVal[kk-1] = dbAIValue[0];
npos = kk;
}
else
{
pdbVal[npos] = dbAIValue[0];
npos += 1;
}
qDebug()<<"the double pos: "<<npos;
}
//----------使用 -----mainwindow.h
#include "TSerial.h"
class MainWindow : public QMainWindow//主窗口类
{
CBoxPort mserial;
public:
void SerialPortInit();//UI相关的函数类
bool RetrieveSerialSet();
void SerialPortClose();
private slots:
void theDispMsg(int msg);//主界面的数据解析与显示相关
...
};
//------------调用与使用--------mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
......
SerialPortInit();
connect(&mserial,SIGNAL(PostDataMsg(int)),this,SLOT(theDispMsg(int)));
}
void MainWindow::SerialPortInit()
{
daqFlag = 0;
nserialport = 0;
nbaudList = 9600;
QString qs;
qs.asprintf("%d",nbaudList);
ui->comboBox_2->addItem("9600");
ui->comboBox_2->addItem("19200");
ui->comboBox_2->addItem("38400");
ui->comboBox_2->addItem("57600");
ui->comboBox_2->addItem("115200");
ui->comboBox_2->setCurrentIndex(0);
ui->comboBox_3->addItem("1Hz");
ui->comboBox_3->addItem("5Hz");
ui->comboBox_3->addItem("10Hz");
ui->comboBox_3->addItem("20Hz");
ui->comboBox_3->setCurrentIndex(1);
ui->lineEdit->setText("02");
serial = new QSerialPort;
int ks = 0;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
serial->setPort(info);
if(serial->open(QIODevice::ReadWrite))
{
ui->comboBox->addItem(info.portName());
serial->close();
ks += 1;
}
}
if(ks)ui->comboBox->setCurrentIndex(0);
}
bool MainWindow::RetrieveSerialSet()
{
int idx = ui->comboBox_2->currentIndex();
if(!idx)nbaudList = 9600;
else if(idx==1)nbaudList = 19200;
else if(idx==2)nbaudList = 38400;
else if(idx==3)nbaudList = 57600;
else if(idx==4)nbaudList = 115200;
//nbaudList = 9600*(idx+1);
idx = ui->comboBox->currentIndex();
QString ssa = ui->comboBox->itemText(idx);
QString s2 = ssa.right(1);
nserialport = s2.toInt();
idx = ui->comboBox_3->currentIndex();
if(idx==0)idx = 1000;
else if(idx==1)idx = 200;
else if(idx==2)idx = 100;
else if(idx==3)idx = 50;
//mserial.nsleepms = idx;
s2 = ui->lineEdit->text();
int id = s2.toInt();
if(id<0)
id = 2;
mserial.resetVariables(idx,id);
ZeroMemory(dbSample,sizeof(double)*400);
mserial.resetSavedbuf(dbSample,50);
if(mserial.InitPort(nserialport,nbaudList))
{
mserial.StartMonitoring();
mserial.openBox();
return true;
}
return false;
}
void MainWindow::SerialPortClose()
{
mserial.closeBox();
mserial.CloseAll();
}
void MainWindow::theDispMsg(int msg)
{
if(msg==0)//CRC error
{
ui->textEdit->setText("位移端口CRC错误");
}
else if(msg==1)//从站地址错误
{
ui->textEdit->setText("位移端口ID有误");
}
else if(msg==200)//需要刷新数据信息
{
//qDebug()<<" get value from serial :"<<mserial.dbAIValue[0];
ui->lcdNumber->display(QString::number(mserial.dbAIValue[0],'f',3));
}
}
void MainWindow::redrawCurves()//图谱曲线的刷新当中调用每次串口读取的数据存储数组
{
QCustomPlot *pc = new QCustomPlot(ui->widget);
...
lns = mserial.retrieveSavedPos();
...
}
void MainWindow::on_pushButton_clicked()//这个仅用于演示怎么启动串口停止串口的数据读写
{
//开始或结束测量
if(!daqFlag)
{
//start
if(RetrieveSerialSet())
{
daqFlag = 1;
ui->pushButton->setText("停止测量");
ui->textEdit->setText("采集已经启动.");
ui->comboBox->setEnabled(false);
ui->comboBox_2->setEnabled(false);
ui->comboBox_3->setEnabled(false);
ui->lineEdit->setEnabled(false);
nClockTimer = startTimer(1000);
}
else
ui->textEdit->setText("启动失败请检查配置.");
}
else
{
//stop
killTimer(nClockTimer);
SerialPortClose();
daqFlag = 0;
ui->pushButton->setText("启动测量");
ui->textEdit->setText("采集已经停止.");
ui->comboBox->setEnabled(true);
ui->comboBox_2->setEnabled(true);
ui->comboBox_3->setEnabled(true);
ui->lineEdit->setEnabled(true);
}
}
代码里面的派生类仅供参考,其中,使用的方法简单来讲,创建类的实例,然后依次调用
#1 InitPort() 初始化串口
#2 StartMonitoring() 启动串口的读写线程;
#3 当串口获取到数据时,可自定义相应的操作函数来进行响应。比如上述派生类当中GetIOInfo()每间隔一个完成字符串就执行一次槽函数。
#4 数据写入串口相关:如果需要下发指令时,可参考派生类的函数int SendQueryCmd();来设定下发字符串内容,并发送一个Event通知子线程进行写入操作;至于上述例程当中的派生类openbox()函数采用了单独的一个子线程,来每间隔XX毫秒,自动下发一次查询指令。如果串口没有这个需求,可以不调用它;不调用openbox() closebox()
异步操作会立即返回,但系统会等待读取写入操作本次完成后,才执行后续函数功能。子线程占用的开销更小。
#5 串口的关闭与退出
结束通讯时,直接调用CloseAll();即可关闭。
基类采用了虚函数virtual void ReceiveChar(COMSTAT comstat) = 0;便于派生类当中根据实际的串口需求,自定义协议数据流解析函数。
大家有优化或者好的建议可留言给我,谢谢