zoukankan      html  css  js  c++  java
  • 由基于qml,c++的串口调试工具浅谈qml与c++混合编程

       最近在做一个基于sim900 的串口通信工具,基于qml和c++来实现。

          首先,对于串口,qt有自带的QSerialPort,可以实现同步,和异步通信,qt creator也有自带的例子,本例子是从其中一个名为“terminal”的例子学习了qt如何实现异步通信(c++),然后通过qml来写界面,逻辑部分由c++实现。

        通过qmlc++混合编程基于QSerialPort的异步通信(记得在pro中加上QT+=serialport),主要步骤包括下面几个:

    1.使用setPort()或者setPortName()方指定想要访问的串口设备。

    2.以只读或者只写或者读写模式调用open()方法打开串口。(注意:串口都是以互斥的方式访问,这也就是说我们不能打开一个已经打开的串口。)

    3.成功打开之后,QSerialPort尝试着获取串口当前的配置并初始化它。你也可以使用setBaudRate(),setDataBits(),setParity(),setStopBits()和setFlowControl()方法重新配置它,

    4.如果串口用读写模式打开,你就可以调用read()或者write()方法,可选的还有readline()和readAll()方法。可以使用close()方法来关闭串口和取消I/O操作。

    下边叙述本程序: serial.h,serial.cpp为主函数,main.qml为主界面,Settings.qml为串口设置界面:

    [javascript] view plaincopy
     
    1. /////////////////main.qml///////////////////////////  
    [plain] view plaincopy
     
    1. import QtQuick 2.1  
    2. import QtQuick.Controls 1.1  
    3. import QtQuick.Layouts 1.1  
    4. Rectangle{  
    5.      800  
    6.     height: 600  
    7.     color: "lightblue"  
    8.     Settings{  
    9.         id:settingwindow  
    10.         visible: false  
    11.     }  
    12.   
    13.     Column{  
    14.         anchors.fill: parent  
    15.         spacing: 50  
    16.         Row{  
    17.             spacing: 50  
    18.             Button{  
    19.                  60  
    20.                 text: "Open"  
    21.                 onClicked: {  
    22.                     settingwindow.visible=true//使设置窗口可见,通过设置串口的apply按钮触发的serialtest.openAndSetPort函数打开和设置串口  
    23.                 }  
    24.             }  
    25.             Button{  
    26.                  60  
    27.                 text: "Close"  
    28.                 onClicked: {  
    29.                     serialtest.closePort()//关闭串口  
    30.                     Qt.quit()  
    31.                 }  
    32.             }  
    33.         }  
    34.   
    35.         Grid{  
    36.             rows:2  
    37.             columns:4  
    38.             rowSpacing: 20  
    39.             columnSpacing: 40  
    40.   
    41.             Label{  
    42.                 height: 25  
    43.                 text: "Send Data : "  
    44.                 verticalAlignment :Text.AlignVCenter  
    45.             }  
    46.   
    47.             TextField {  
    48.                 id: textInput1  
    49.                  300  
    50.                 height: 25  
    51.                 placeholderText: qsTr("Send Data")  
    52.                 font.pixelSize: 12  
    53.             }  
    54.             Label{  
    55.                 height: 25  
    56.                 text: "Number of Send Data: "+serialtest.sendnumber//显示发送数据计数  
    57.                 verticalAlignment :Text.AlignVCenter  
    58.             }  
    59.             Button{  
    60.                 id:sendData  
    61.                  60  
    62.                 text: "Send"  
    63.                 onClicked: {  
    64.                     serialtest.sendto(textInput1.text);//触发发送数据函数  
    65.                 }  
    66.             }  
    67.   
    68.   
    69.             Label{  
    70.                 height: 25  
    71.                 text: "Receive Data : "  
    72.                 verticalAlignment :Text.AlignVCenter  
    73.             }  
    74.             Rectangle{  
    75.                 height: 300  
    76.                  300  
    77.                 color: "lightgreen"  
    78.                 radius: 10  
    79.                 Label{  
    80.                     anchors.fill: parent  
    81.                     id: textreceive  
    82.                     font.pixelSize: 12  
    83.                     text:serialtest.receivedata  
    84.                 }  
    85.             }  
    86.   
    87.             Label{  
    88.                 height: 25  
    89.                 text: "Number of receive Data: "+serialtest.receivenumber//显示接收数据计数  
    90.                 verticalAlignment :Text.AlignVCenter  
    91.             }  
    92.             Button{  
    93.                  60  
    94.                 text: "Clear"  
    95.                 onClicked: {//清空接收数据显示,将数据计数清零  
    96.                     serialtest.receivedata=""  
    97.                     serialtest.sendnumber="0"  
    98.                     serialtest.receivenumber="0"  
    99.                     serialtest.clearnumber();  
    100.                 }  
    101.             }  
    102.   
    103.         }  
    104.   
    105.     }  
    106. }  
    
    
    
    
    [html] view plaincopy
     
    1. /////////////////////Settings.qml/////////////////////////////////////////  
    [html] view plaincopy
     
    1. import QtQuick 2.1  
    2. import QtQuick.Controls 1.1  
    3. import QtQuick.Window 2.0  
    4. Window{  
    5.     id:setwindow  
    6.      300  
    7.     height: 300  
    8.     Column{  
    9.         id: maincolumn  
    10.         anchors.fill: parent  
    11.         spacing: 10  
    12.   
    13.         Rectangle{  
    14.             anchors.horizontalCenter: parent.horizontalCenter  
    15.             height: 1  
    16.              parent.width  
    17.         }  
    18.         Label{  
    19.             anchors.horizontalCenter: parent.horizontalCenter  
    20.             text: "Set Serial Port"  
    21.             font.pointSize:12  
    22.             font.bold: true  
    23.   
    24.         }  
    25.         Grid{  
    26.             id:selectgrid  
    27.             anchors.horizontalCenter: parent.horizontalCenter  
    28.             rows:6  
    29.             columns: 2  
    30.             columnSpacing: 20  
    31.             rowSpacing: 10  
    32.             Label{  
    33.                 id:selectlabel  
    34.                 height: 20  
    35.                 text: "PortName:"  
    36.                 font.pointSize:9  
    37.                 horizontalAlignment : Text.AlignHCenter  
    38.                 verticalAlignment :Text.AlignVCenter  
    39.             }  
    40.             ComboBox {  
    41.                 id :firstcombo  
    42.                  maincolumn.width/2  
    43.                 currentIndex: 2  
    44.                 model: [ "COM1", "COM2", "COM3" ,"COM4" ,"COM5" ,"COM6" ]  
    45.             }  
    46.             Label{  
    47.                 text: "BaudRate:"  
    48.                 height: 20  
    49.                 font.pointSize:selectlabel.font.pointSize  
    50.                 horizontalAlignment : Text.AlignHCenter  
    51.                 verticalAlignment :Text.AlignVCenter  
    52.             }  
    53.             ComboBox {  
    54.                 id: baudRate  
    55.                 firstcombo.width  
    56.                 currentIndex: 0  
    57.                 model: [ "9600", "19200", "38400","115200" ]  
    58.             }  
    59.             Label{  
    60.                 text: "Data bits:"  
    61.                 height: 20  
    62.                 font.pointSize:selectlabel.font.pointSize  
    63.                 horizontalAlignment : Text.AlignHCenter  
    64.                 verticalAlignment :Text.AlignVCenter  
    65.             }  
    66.             ComboBox {  
    67.                 id:dataBits  
    68.                 firstcombo.width  
    69.                 currentIndex: 3  
    70.                 model: [ "5", "6", "7", "8" ]  
    71.             }  
    72.             Label{  
    73.                 text: "Parity:"  
    74.                 height: 20  
    75.                 font.pointSize:selectlabel.font.pointSize  
    76.                 horizontalAlignment : Text.AlignHCenter  
    77.                 verticalAlignment :Text.AlignVCenter  
    78.             }  
    79.             ComboBox {  
    80.                 id:parity  
    81.                 firstcombo.width  
    82.                 currentIndex: 0  
    83.                 model: [ "None", "Even", "Odd", "Mark", "Space" ]  
    84.             }  
    85.             Label{  
    86.                 height: 20  
    87.                 text: "Stop bits:"  
    88.                 font.pointSize:selectlabel.font.pointSize  
    89.                 horizontalAlignment : Text.AlignHCenter  
    90.                 verticalAlignment :Text.AlignVCenter  
    91.             }  
    92.             ComboBox {  
    93.                 id:stopBits  
    94.                 firstcombo.width  
    95.                 currentIndex: 0  
    96.                 model: [ "1", "1.5", "2" ]  
    97.             }  
    98.             Label{  
    99.                 height: 20  
    100.                 text: "Flow control:"  
    101.                 font.pointSize:selectlabel.font.pointSize  
    102.                 horizontalAlignment : Text.AlignHCenter  
    103.                 verticalAlignment :Text.AlignVCenter  
    104.             }  
    105.             ComboBox {  
    106.                 id:flowControl  
    107.                 currentIndex: 0  
    108.                 firstcombo.width  
    109.                 model: [ "None", "RTS/CTS", "XON/XOFF" ]  
    110.             }  
    111.         }  
    112.         Button{  
    113.              60  
    114.             text: "Apply"  
    115.             anchors.horizontalCenter: parent.horizontalCenter  
    116.             onClicked: {  
    117.                 serialtest.openAndSetPort(firstcombo.currentIndex,baudRate.currentIndex,dataBits.currentIndex  
    118.                                            ,parity.currentIndex,stopBits.currentIndex,flowControl.currentIndex)  
    119.                 //触发此函数,由combobox控件的currentIndex作为函数变量,(所有combobox的model值和顺序都和serialtest.openAndSetPort一致,这样就可以通过传递index来获取当前设置信息)  
    120.                 setwindow.visible=false  
    121.             }  
    122.         }  
    123.   
    124.     }  
    125.   
    126. }  
    [html] view plaincopy
     
    1. <span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="html"><span style="font-family: Arial, Helvetica, sans-serif;">////////////////////////////</span><span style="font-family: Arial, Helvetica, sans-serif;">serialset.h/////////////////////////////</span>  
    
    
    #include <QObject>
    #include <QtSerialPort/QSerialPort>
    class SerialTest : public QSerialPort
    {
        Q_OBJECT
        Q_PROPERTY(QString receivedata READ receivedata WRITE setreceivedata NOTIFY receivedataChanged)//从串口收到的数据
        Q_PROPERTY(QString sendnumber READ sendnumber WRITE setsendnumber NOTIFY sendnumberChanged)//发送的数据字节统计
        Q_PROPERTY(QString receivenumber READ receivenumber WRITE setreceivenumber NOTIFY receivenumberChanged)//接收的数据字节统计
    public:
        struct Settings {//端口设定结构体
            QString name;
            qint32 baudRate;
            QSerialPort::DataBits dataBits;
            QSerialPort::Parity parity;
            QSerialPort::StopBits stopBits;
            QSerialPort::FlowControl flowControl;
        };
        SerialTest(QSerialPort *parent = 0);
    
    
    
    
        QString receivedata(void);
        void setreceivedata(QString receivedata);
    
    
        QString sendnumber();
        void setsendnumber(QString sendnumber);
    
    
        QString receivenumber();
        void setreceivenumber(QString receivenumber);
    
    
        Q_INVOKABLE void openAndSetPort(int PortNameIndex,int BaudRateIndex,int DatabitsIndex,int ParityIndex,int StopbitsIndex,int FlowcontrolIndex);//打开并设定端口;
        Q_INVOKABLE void closePort();//关闭端口;
        Q_INVOKABLE void sendto(QString sendmessage);//发送数据;
        Q_INVOKABLE void clearnumber();//数据统计清零;
    
    
    signals:
        void receivedataChanged();
    
    
        void receivenumberChanged();
        void sendnumberChanged();
    
    
    
    
    public slots:
        void receivefrom();//信号(收到数据激发的信号)响应函数
    
    
    private:
        QString m_receivedata;
        QString m_sendnumber,m_receivenumber;
    };
    
    
    #endif // SERIALTEST_H
    
    
    
    
    [html] view plaincopy
     
    1.   
    [html] view plaincopy
     
    1. ///////////////////////////serialset.cpp////////////////////////////  
    
    
    [html] view plaincopy
     
    1. <span style="font-family: Arial, Helvetica, sans-serif; color: rgb(0, 0, 128);">#include</span><span style="font-family: Arial, Helvetica, sans-serif; color: rgb(0, 128, 0);"><iostream></span>  
    
    
    SerialTest::Settings currentsetting;//定义设定值结构体的结构体变量
    QSerialPort serialtest;
    
    
    qint64 c_sendnumber,c_receivenumber;
    
    
    SerialTest::SerialTest(QSerialPort *parent):QSerialPort (parent),m_receivedata("Receive Label"),m_receivenumber("0"),m_sendnumber("0")
    {
        QObject::connect(&serialtest, SIGNAL(readyRead()),this, SLOT(receivefrom()));//将端口收到数据产生的信号绑定receivefrom()函数;
    }
    
    
    //打开端口并设置:函数的参数(……Index由qml中combobox的currentIndex决定),由按钮触发
    void SerialTest::openAndSetPort(int PortNameIndex,
                                    int BaudRateIndex,
                                    int DatabitsIndex,
                                    int ParityIndex,
                                    int StopbitsIndex,
                                    int FlowcontrolIndex)
    {
        ////////////////////1.得到当前选择的各项设置//////////////////////////////
        //得到当前端口名
        QString allname[6]={"COM1","COM2","COM3","COM4","COM5","COM6"};//列举所有的端口名
        currentsetting.name=allname[PortNameIndex];//由qml里表示name的combobox的currentIndex来确定当前的name
        std::cout<<" ok setPortName to "+ currentsetting.name.toStdString()<< std::endl;//通过输出来验证设定成功
    
    
        //得到当前波特率
        qint32 allbauRate[4]={9600,19200,38400,115200};
        currentsetting.baudRate=allbauRate[BaudRateIndex];
    
    
        //得到当前发送位数
        QSerialPort::DataBits allDatabits[4]={QSerialPort::Data5,
                                              QSerialPort::Data6,
                                              QSerialPort::Data7,
                                              QSerialPort::Data8};
        currentsetting.dataBits=allDatabits[DatabitsIndex];
    
    
        //得到当前Parity
        QSerialPort::Parity allparity[5]={QSerialPort::NoParity,
                                          QSerialPort::EvenParity,
                                          QSerialPort::OddParity,
                                          QSerialPort::MarkParity,
                                          QSerialPort::SpaceParity};
        currentsetting.parity=allparity[ParityIndex];
    
    
        //得到当前停止位
        QSerialPort::StopBits allstopBits[3]={QSerialPort::OneStop,
                                              QSerialPort::OneAndHalfStop,
                                              QSerialPort::TwoStop};
        currentsetting.stopBits=allstopBits[StopbitsIndex];
    
    
        //得到当前FlowControl
        QSerialPort::FlowControl allflowControl[3]={QSerialPort::NoFlowControl,
                                                    QSerialPort::HardwareControl,
                                                    QSerialPort::SoftwareControl};
        currentsetting.flowControl=allflowControl[FlowcontrolIndex];
    
    
    
    
    ////////////////////2.设定当前端口名//////////////////////////////
        serialtest.setPortName(currentsetting.name);
    
    
    ////////////////////3.打开这一端口并按照当前设置信息进行设置//////////////////////////////
        if (serialtest.open(QIODevice::ReadWrite))//打开这一端口
        {
            std::cout<<"open port sucess"<<std::endl;
    
    
            if(serialtest.setBaudRate(currentsetting.baudRate)//设置各项信息
                    && serialtest.setDataBits(currentsetting.dataBits)
                    && serialtest.setParity(currentsetting.parity)
                    && serialtest.setStopBits(currentsetting.stopBits)
                    && serialtest.setFlowControl(currentsetting.flowControl))
            {
                std::cout<<"set sucess"<<std::endl;
            }
        }
    }
    
    
    
    
    ////////////////////4.发送数据//////////////////////////////
    void SerialTest::sendto(QString sendmessage)//此函数由qml里的send按钮触发,sendmessage来源于qml文本框的当前文本,
    {
        QByteArray data = sendmessage.toLocal8Bit()+'
    ';//将QString转为QByteArray,并加上'
    '(回车符),因为芯片要求在回车符之后再返回数据
        qint64 testwritenumber=serialtest.write(data);//写入数据
    
    
        m_receivedata=m_receivedata+"
    ";//加上换行符便于显示
    
    
        c_sendnumber=c_sendnumber+testwritenumber-1;//发送数据字节数统计(减去回车符)
        setsendnumber(QString ::number(c_sendnumber));//更新发送的数据字节总数
    
    
    
    
    }
    
    
    void SerialTest::setsendnumber(QString sendnumber)//更新发送的数据字节总数,触发sendnumberChanged()的消息响应函数sendnumber()来更新显示
    {
        m_sendnumber=sendnumber;
        emit sendnumberChanged();
    }
    
    
    QString SerialTest::sendnumber()//响应sendnumberChanged()消息
    {
        return m_sendnumber;
    }
    
    
    ////////////////////4.接收数据//////////////////////////////
    void SerialTest::receivefrom()//由readyRead()消息出发(在前边进行绑定),当串口收到数据此消息被激活(对于串口,每发送出去一个字节,都会将此字节返回,触发readyread消息,当芯片有特殊指令时,收到的信息更多,比如对sim900,发送0000,芯片就会受到0000,但是发送AT,会受到 AT OK)
    {
        QByteArray data = serialtest.readAll();//读取所有收到的数据
    
    
        QString receivedata=data.data();//将QByteArray转为QString来显示
        m_receivedata= m_receivedata+receivedata;//将某次收到的数据进行累加,因为如果不累加的话每次有readyread就会触发此函数,会重置m_receivedata,覆盖之前收到的数据
        emit receivedataChanged();//发送消息触发receivedata(),更新当前收到的数据显示receivedata
    
    
        qint64 testreadnumber=data.length();//接收数据字节数统计
        c_receivenumber=c_receivenumber+testreadnumber;
    
    
        setreceivenumber(QString ::number(c_receivenumber));//更新接收的数据字节总数
    }
    
    
    void SerialTest::setreceivenumber(QString receivenumber)//更新接收的数据字节总数
    {
        m_receivenumber=receivenumber;
        emit receivenumberChanged();;
    }
    
    
    QString SerialTest::receivenumber()//响应receivenumberChanged()消息
    {
        return m_receivenumber;
    }
    
    
    
    
    QString SerialTest::receivedata()//qml读取receivedata值的时候就会触发此函数,或者emit receivedataChanged()更新当前收到的数据显示时触发
    {
        return m_receivedata;
    }
    void SerialTest::setreceivedata(QString receivedata)//其任务已被receive from函数完成,但是在清空数据时用到这个函数
    {
        m_receivedata=receivedata;
        emit receivedataChanged();
    }
    
    
    
    
    
    
    ////////////////////5.关闭端口//////////////////////////////
    void SerialTest::closePort()//由按钮出发
    {
        serialtest.close();
        std::cout<<"close port sucess"<<std::endl;
    }
    
    
    
    
    
    
    ////////////////////6.清空计数//////////////////////////////
    void SerialTest::clearnumber()//由按钮出发
    {
        c_sendnumber=0;
        c_receivenumber=0;
    }
    
    
    
    
    

        

       首先,打开并设置串口: 由main.qml里的名为“Open”的按钮打开Settings.qml设置界面(即使settings窗口其可见),然后转入settings.qml,设置各个combobox之后,通过点击Apply按钮触发SerialTest::openAndSetPort函数(通过Q_INVOKABLE在serialtest.h中定义使得能够在qml里边访问),函数变量即为当前qml里各个combobox的currentIndex,由有各个combobox的model值和顺序与SerialTest::openAndSetPort函数中每个参数的可选值相同,所以可以由qml中各个combobox的currentIndex得到SerialTest::openAndSetPort函数中每个端口参数的值,然后由得到的设定值name,打开端口,设置其他端口参数。SerialTest::openAndSetPort函数执行完以后,设置Settings.qml对应的设置窗口不可见,回到主窗口。

        第二,发送数据:在主窗口以senddata为名的textfield控件中输入要发送的内容,点击send按钮,触发SerialTest::sendto(QString sendmessage)函数(通过Q_INVOKABLE定义在serialtest.h中定义),其中变量来源于用户输入textfield的text内容,需要将其转为qbytearray来发送,注意:转换后加了' ',这是因为芯片要求在回车符(' ')之后再返回数据,比如对sim900芯片,发送0000,芯片就会收到0000,但是发送AT' ',会受到 AT' 'OK。所以加上' ',然后进行写操作,发送qbytearray数据到串口,并对发送的自己数进行计数,计数由定义的 Q_PROPERTY(QString sendnumber READ sendnumber WRITE setsendnumber NOTIFY sendnumberChanged)来完成, 执行setsendnumber(QString::number(c_sendnumber)),将当前计数的值进行设定,此函数更新m_sendnumber的值为当前计数,并emit sendnumberChanged()发送消息,使qml中text:"NumberofSendData:"+serialtest.sendnumber(56行)更新serialtest.sendnumber值,这时就回来通过读取QStringSerialTest::sendnumber()函数,而返回值m_sendnumber就是当前计数值,这时c++传值到qml的方法,如果要在qml向c++传值,只需在qml里执行SerialTest::setsendnumber(QStringsendnumber)函数,但是前提是在头文件里将此函数设置为Q_INVOKABLE函数或者在public slots:内定义函数,另一种qml向c++传值方法就是在定义一个函数,同样需要设置为Q_INVOKABLE函数或者在publicslots:内定义,然后在qml里使用,将qml的值由此函数送到c++即可,有时候还需要在qml里使用function先做一些处理。 

        第三,接收数据:对于串口来说,每发送一个字节的数据,就会返回收到这个数据,这时候使用QSerialPort就会产生一个信号:readyRead()。将此信号与函数receivefrom()进行绑定: QObject::connect(&serialtest, SIGNAL(readyRead()),this, SLOT(receivefrom())),就可以在发送完数据后得到readyRead()信号时触发SerialTest::receivefrom()函数,读取数据,转换为QString来显示,这里有一个问题需要注意,就是readyRead()信号可能多次产生,可能收到的数据还没有显示,新的数据又来了,将其覆盖,所以有个方法就是每次send之后收到的所有消息进行字符串累加,这样就可以避免这个问题(搞了好久才搞定的)。此函数后续的emit就不说了,和前面的类似,只不过前边的是显示计数,这里显示接受的数据,而且这里把set函数及Q_PROPERTY的WRITE函数的功能放到receivefrom函数来实现了(主要就是更新m_receivedata值和emit receivedataChanged()消息两个功能)。下边receivenumber和签署sendnumber一样。 最后关闭窗口和清空计数,分别有两个按钮来响应,需要将两个函数设置成Q_INVOKABLE使得qml能够调用。

    最后,本文只是个人的程序说明,具体的qml c++混合编程可以参考foruok大神的博客文章(大神要出书了:《qt quick核心编程》大家快去支持)http://blog.csdn.net/foruok/article/details/32698603

    最后附上本程序的github源码地址

    https://github.com/zing235/TestSerial.git

    http://blog.csdn.net/u010423298/article/details/41791799

  • 相关阅读:
    JAVA整合阿里云OSS实现文件上传功能
    JAVA实现导出excel功能,封装通用导出工具类
    SpringBoot整合quartz实现动态启动,停止定时任务功能
    JAVA微信支付——企业付款(企业向微信用户个人付款、转账)
    Windows系统安装Redis服务
    MySQL设置表中字段的数据唯一性
    XSS工具类,清除参数中的特殊字符
    用伪代码表示第4题中各题的算法
    用N-S图表示第4题中各题的算法
    用N-S图表示第4题中各题的算法
  • 原文地址:https://www.cnblogs.com/findumars/p/5125258.html
Copyright © 2011-2022 走看看