QT异步串口通讯代码示例

首先说明一点,这个源代码基础类最初是老外写的,我只是把它从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;便于派生类当中根据实际的串口需求,自定义协议数据流解析函数。

大家有优化或者好的建议可留言给我,谢谢

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值