分享

利用回调函数实现DLL与Qt主程序的数据交互,进一步实现对Qt主程序中的信号触发

 海洋619 2015-01-30

    


Qt并没有提供串口通讯的接口,为了实现Qt程序的串口通讯,多数开发者都采用了一个第三方接口win_qextserialport。这个接口是完全基于Qt类库的,很容易就可以把它加载到自己的程序里边。但在实际应用过程中,发现了一个奇怪的现象:


我的上位机程序是要通过控制串口(USB转的)来实现与下位机的通讯,经过测试,在相同的设置下,上位机程序和串口调试软件能够正常通讯,下位机和串口调试软件也能够正常通讯。按理说,这个时候上下位机也就应该能够正常地通讯了,但事实却很残酷:它们无法沟通,下位机接不到上位机的数据,上位机也接不到下位机的数据,----无论我如何调节相关设置、重新开关机,都无济于事。


我不知道win_qextserialport到底怎么了,实在无暇去深究。因为时间比较紧,我不得不尽早尝试新的串口通讯接口。


最直接的就是调用Windows API了,但那一堆堆冗长的接口函数实在繁琐。幸运的是有个大牛发布了一个C++串口通讯程序接口(CnComm.h头文件源代码,最新版是1.5),非常方便。因为它需要在VC下编译,所以我必须把它打包成DLL然后提供给Qt主程序调用。到这里,我面临很多的问题:


1 大牛的接口是C++的,Qt可以容易地实现对DLL里的函数的调用,但如何调用Dll里的类?


2 我需要在串口接收到数据后,把数据传回主程序,并马上在Qt主程序里释放一个信号(signal),以通知主程序处理。如何实现?


3 DLL里根本不知道Qt主程序里的相关的类,更不知道Qt中的emit为何物,怎么传递Qt主程序里的类给他?


 


最后是通过回调函数来实现的。


回调函数,就是把一个函数A的指针传递给另一个函数B,由函数B再调用函数A,这样就可以实现模块间的交互操作。如果再把函数A的指针传递给函数B的同时,也把相关的参数一起传递给函数B,那么就可以实现模块间的数据交互。


例如:


int sumit(int x, int y)

{

    return x + y ;

}


void testit(int a, int b, int (*func)(int, int))

{

    QString strs = QString::number(func(a,b));

    qDebug(strs.toAscii());

}


 


可以这样调用: testit(1,2,sumit);

打印输出值:3


 


下面是DLL与Qt主程序的主要实现代码:


1 DLL代码


#include 'CnComm.h'

class communicate;                                          //Qt中类的前向声明,通知DLL这是一个类

typedef void(*Emit)(communicate*, char*, int);     //函数指针类型定义


class HRComm : public CnComm   

{   

private:

     Emit emitSignal ;             //信号释放函数的指针, 用于指向回调函数

     communicate * pComm;   //Qt中类实例的指针,指向Qt主程序中的类实例,作回调函数的实参,以便在Qt主程序中进行信号释放


     char *pDataBuffer;           //接收数据缓存指针

     int  iLength;                    //接收到的字节数

     void OnReceive()             //重载接收函数

    {

         int dataLen = Read(pDataBuffer, iLength);   //读取串口数据,返回实际接收的数据字节数

         emitSignal(pComm,pDataBuffer,dataLen);   //回调在此发生!传数据到到Qt主程序中,并把释放信号的类实例指针回传。

    }


public:

     void SetSignal(communicate*  commn, char *pData, int len, Emit func);  //设置类参数, 把Qt主程序里的函数指针和其参数传到该串口通讯类中

};


 


void HRComm::SetSignal(communicate *commn, char *pData, int len,  Emit func)

{

     pDataBuffer = pData;   


     iLength = len;             

     emitSignal = func;       //传递Qt主程序里的函数(回调函数)指针

     pComm = commn;      //传递Qt主程序里的类实例指针

}


 


HRComm hrComm;     //实例化一个串口通讯类


 


bool openPort(int port, int baudrate)  

{

    return hrComm.Open(port, baudrate);

}




void closePort(void)



      hrComm.Close();

}




void sendData(char *buff, int len)

{

      hrComm.Write(buff, len);

}




void setReceiveSignal( communicate *commn,char *pData, int len,  Emit pFunc)   

{

     hrComm.SetSignal(commn, pData, len, pFunc);

}


 


Qt主程序通过调用openPort、closePort、sendData和setReceiveSignal分别实现打开串口、关闭串口、发送数据和回调函数传递。


 


2 Qt 主程序主要实现代码


 1)communicate.h


 #ifndef COMMUNICATE_H

#define COMMUNICATE_H

#include 'win_qextserialport.h'

#include <QObject>


class Signal;

class QString;

class QByteArray;

class QLibrary; 


class communicate: public QObject

{

    Q_OBJECT

public:

    QLibrary  *lib;

    int iDataLen;

    char *pData;


    communicate(QWidget *parent = 0);

    Win_QextSerialPort *myCom;

    int iPortNumber;

    void Connect(void);


    bool b_checked;

    void Open(int portNumber);

    bool TestPort(int portNumber);

    void SetSignalEmit(char *data, int len);


signals:

    void signalData(char *pData, int len);

public slots:

    void Close(void);

    void GotData(char *pData, int len);


};


#endif // COMMUNICATE_H


 


 2)communicate.cpp


#include 'communicate.h'

#include 'data.h'

#include <iostream>

#include <QLibrary>

typedef bool(*OpenPort)(int, int);

typedef void(*ClosePort)(void);

typedef void(*SendData)(char*, int);

typedef void(*SetSignal)(communicate*, char*, int, void(*pf)(communicate*, char*, int));


 //回调函数。C++中,类成员函数不可以作回调函数,要不然就不用这么麻烦把communicate *comm传来传去了。

void SetIt(communicate *comm, char *data, int len) 

{

    qDebug('GOT DATA!');

    QByteArray * bytes = new QByteArray;

    for (int i=0; i<len;i++)

        bytes->append(*(data+i));

    qDebug(bytes->toHex());

    comm->SetSignalEmit(data, len);

    comm->b_checked = true;

}


 


void communicate::Connect(void)

{

    connect(myCom, SIGNAL(readyRead()),this,SLOT(readCom()));

}


communicate::communicate(QWidget *parent)

{

    b_checked = false;

    strpc  = SUNG_ID_CODE; 


    iDataLen = 1024;

    pData = new char[1024];


    lib = new QLibrary('CommuModual.dll');

    if(lib->load())

    {

        qDebug('dll is loaded');

    }

    else

        qDebug('dll loading failed.');


    connect(this, SIGNAL(signalData(char*,int)),this, SLOT(GotData(char*,int)));


}




void communicate::GotData(char*,int)

{


    b_checked = true;

}


 


void communicate::SetSignalEmit(char *data, int len)

{

    emit this->signalData(data, len);

}


void communicate::Open(int portNumber)

{

    OpenPort openPort = (OpenPort)lib->resolve('openPort');

    if (openPort)

    {

        qDebug('openPort loaded');

        if(openPort(portNumber,115200))

            qDebug('port opened');

        else

            qDebug('port opening failed.');

    }

    else

        qDebug('openPort loading failed');


}


bool communicate::TestPort(int portNumber)

{

    iPortNumber = portNumber;

    SetSignal setSignal = (SetSignal)lib->resolve('setReceiveSignal');

    if (setSignal)

    {

        qDebug('SetReceiveSignal loaded');

        setSignal(this,this->pData, this->iDataLen,SetIt); //设置信号与回传

        qDebug('Signal is set');

    }


    Open(portNumber); //打开串口

    SendData sendData = (SendData)lib->resolve('sendData');

    if (sendData)

    {

        qDebug('sendData loaded');

        QString strtemp = SUNG_ID_CODE;

        int len = strtemp.count();

        char *dt = new char[len+3];

        *dt = 0xFF;

        *(dt+1) = INST_ID_CODE;

        for (int i=0; i<len;i++)

            *(dt+i+2) = strtemp.at(i).toAscii();

        *(dt+len+2) = 0xFF;

        sendData(dt,len+3);

        qDebug('data is sent');

        Sleep(20);

        delete []dt;

        dt = NULL;

    }

    else

        qDebug('sendData loading failed');

    return b_checked;    

}




void communicate::Close()

{

   ClosePort closePort = (ClosePort)lib->resolve('closePort');

   if (closePort)

   {

       qDebug('closePort loaded');

       closePort();

       qDebug('port is closed');

   }

   else

       qDebug('closePort loading failed');


   delete lib;

   lib = NULL;


}


 


3)main.cpp


#include 'communicate.h'


void main(int argc, char *argv[])

{


    communicate  commn;

    commn.TestPort(13); //测试:打开串口13,设置回调函数、数据传输参数


    while(!commn.b_checked)

        ;

}


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多