精华内容
下载资源
问答
  • qt编写上位机与stm32通信

    万次阅读 多人点赞 2018-04-12 20:38:33
    好的文章 1、Qt5.5.1实现通用串口程序...2、QT实现串口通信 https://www.2cto.com/kf/201610/558768.html 资源下载:https://download.csdn.net/download/hellybobo/9950904?web=web 本文采用的的开发环境是...

    好的文章
    1、Qt5.5.1实现通用串口程序https://www.2cto.com/kf/201607/524028.html
    2、QT实现串口通信 https://www.2cto.com/kf/201610/558768.html

    资源下载:https://download.csdn.net/download/hellybobo/9950904?web=web

    本文采用的的开发环境是VS2010+Qt5.5.1版本,所有程序不是通过Qt Creator编译的,如果有需要可以介绍VS2010和Qt环境的搭建和简单的使用。

    QSerialPort

    QSerialPort这个类是从QT5.1开始引入的,之前都是通过QIODevice自己定义串口类,从而实现串口程序的开发。现在引入这个类了,将会非常方便的开发串口程序。为了使用这个类,需要在工程目录和附加依赖项中加入include的路径,以及链接库的路径,以及链接库的名称:

    项目—>属性—> C++ —> 常规 —>C:\Qt\Qt5.5.1\5.5\msvc2010\include\QtSerialPort项目—>属性—> 输入++ —> 附加依赖项—>Qt5SerialPort.lib(如果是Debug版本,则是Qt5SerialPortd.lib)版本源文件或者头文件需要加入#include 串口的信息可以通过QSerialPortInfo类获得,通过这个类,你可以正确的确定你要开启的串口,同时可以获得串口的描述性信息以及厂家信息。串口有三种打开模式,即ReadOnly,WriteOnly,以及ReadWrite。同时可以设置其停止位,波特率,数据位,校验方式以及流控,对应的函数方式分别为:setStopBits(),setBaudRates(),setDataBits(),setParity(),以及setFlowControl()。
    串口数据的写入是通过writeData(const char * data, qint64 maxSize)以及write(const char * data, qint64 maxSize),前者是protected属性,只能在子类中访问,而后者则是public属性。在串口打开并且具有可写属性,即可通过write函数写入数据。

    串口数据的读入是通过 readData(char * data, qint64 maxSize) ,read(qint64 maxSize)实现的,如果需要一次性读完所有的数据,则可以通过readAll()全部读取串口缓冲区中的数据。

    串口内部的缓冲区大小可以通过:setReadBufferSize(qint64 size)实现。当设定缓冲区大小时,串口只能接收size大小的数据流,因此存在数据丢书的可能。当设置为0的时候,并不是指的缓冲区大小为0,而是无穷大,这样就可以保存数据的全部接收完整。这是缓冲区大小的默认属性。

    是否有新的数据读入缓冲区是通过readReady()信号来来确定的。这是通过时间驱动的方式来确定是否有数据可以读入。此外还有waitForReadyRead()来等待轮询是否有数据到达串口,但是这是一种阻塞性读入,个人觉得不是太好,所以写串口的时候采用的是事件驱动的方式来确定是够有数据可以读入。

    QSerialPortInfo

    Use the static functions to generate a list of QSerialPortInfo objects. Each QSerialPortInfo object in the list represents a single serial port and can be queried for the port name, system location, description, and manufacturer. The QSerialPortInfo class can also be used as an input parameter for the setPort() method of the QSerialPort class.

    可能会用得比较多的函数是description(),manufacturer(),以及serialNumber()。从而得到描述性信息,比如通信端口。USB转串口等描述串口的信息、串口的生产商信息以及串口名,在电脑上表现为COM~等。

    如何获取电脑上所有的串口

    void CameraSerialPort::getSerialPortID()
    {
         serialInfo=new QSerialPortInfo();
         serialList=serialInfo->availablePorts();
        int nSerialnum=serialList.length();
        for(int i=0;i<nserialnum;i++) qstring="" serialdesp="serialList[i].description();" serialname="serialList[i].portName();" serialportassitant.serialportcombobox-="">addItem(serialName);
        }
    
        QString currentPort=serialPortAssitant.serialPortcomboBox->currentText();
        portToOpen=currentPort;
        QString  portStatus=currentPort+" closed";
        serialPortAssitant.status->statusInfo->setText(portStatus.toUpper());
        QPalette font_palette;
        font_palette.setColor(QPalette::WindowText,Qt::red);
        serialPortAssitant.status->statusInfo->setPalette(font_palette);

    因为直接从自己的项目文件拷过来的源码,这里稍微介绍一下属性:

    1、变量的定义,在头文件中,这里没有贴出来,截取定义如下:

    QSerialPortInfo* serialInfo;
    QList<qserialportinfo>serialList;</qserialportinfo>

    2、QListavailablePorts() 返回的是一个关于QSerialPortInfo的列表,在数据结构QList中存储。
    3、serialPortcomBox是一个QComboBox,下拉列表。

    4、最后几行是用来显示串口的状态信息,达到的效果如图:
    这里写图片描述

    打开串口并且通过串口写数据

    得到串口信息后,就可以选择端口,进行打开和读写数据。贴出代码,然后在给分析分析:

    void CameraSerialPort::Write()
    {
        <span style="background-color: rgb(255, 204, 51);">QString sendMsg=serialPortAssitant.sendLine->text();</span>
        <span style="background-color: rgb(255, 204, 0);">QByteArray temp=sendMsg.toLatin1();</span>
        if(IsSendByHex)
        {
            temp=HexStrToByteArray(sendMsg);
        }
        <span style="background-color: rgb(255, 204, 0);">char *sendContent=temp.data();</span>
        <span style="background-color: rgb(255, 204, 0);">qint64 sendedNum=serialPort->write(sendContent,temp.count())</span>;
    
        //---------------判断发送数据是否成功----------------------//
        if(sendedNum==-1)
        {
            errorValue=serialPort->error();
            if(IsShowCurrentTime)
            {
                errorInfo=" ";
                currentTime=QDateTime::currentDateTime();
                timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
                errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                errorInfo+=timeinfo;
                errorInfo+="\n";
            }
            else
            {
                errorInfo=" ";
                errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                errorInfo+="\n";
            }
            serialPortAssitant.ReciveWidget->append(errorInfo+getValueContent(errorValue));
            return;
        }
        //-------------显示发送数据-----------------------//
    
        //temp的size的依据是是否以16进制发送
        sendCount+=temp.count();
        serialPortAssitant.status->TxByte->setText(QString::number(sendCount));
        QString showSendMsg;
        if(IsShowSendMsg)
        {
            if(IsShowCurrentTime)
            {
    
                currentTime=QDateTime::currentDateTime();
                timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
                showSendMsg+=QString::fromLocal8Bit("发送数据 : ");
                showSendMsg+=timeinfo;
                showSendMsg+="\n";
                //判断显示16进制还是ACSII字符
                if(IsSendByHex)
                    showSendMsg+=ByteArrayToHexStr(temp);
                else
                    showSendMsg+=temp;
            }   
            else
            {
                showSendMsg=QString::fromLocal8Bit("发送数据 : ");
                if(IsSendByHex)
                    showSendMsg+=ByteArrayToHexStr(temp);
                else
                    showSendMsg+=temp;
    
            }   
            serialPortAssitant.ReciveWidget->append(showSendMsg);
    
        }   
        IsWrittenSuccessed=true;
    }
    void CameraSerialPort::sendData()
    {
        if(!IsSerialPortOpen)
        {
                if(serialPort!=NULL)
                {
                    serialPort->close();
                }
                <span style="background-color: rgb(255, 204, 0);">serialPort=new QSerialPort(portToOpen);</span>
                if(serialPort==NULL)
                {
                    errorValue=serialPort->error();
                    QString errorContent=getValueContent(errorValue);
                    if(IsShowCurrentTime)
                    {
                            errorInfo=" ";
                            currentTime=QDateTime::currentDateTime();
                            timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
                            errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                            errorInfo+=timeinfo;
                            errorInfo+="\n";
                    }
                        else
                    {
                            errorInfo=" ";
                            errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                            errorInfo+="\n";
                    }
                        serialPortAssitant.ReciveWidget->append(errorInfo +errorContent+QString::fromLocal8Bit(", 请重新选择正确的端口\n"));
                        return;     
                }
    
            <span style="background-color: rgb(255, 204, 0);">if(!serialPort->open(QIODevice::ReadWrite))</span>
            {
                errorValue=serialPort->error();
                QString errorContent=getValueContent(errorValue);
                if(IsShowCurrentTime)
                {
                    errorInfo=" ";
                    currentTime=QDateTime::currentDateTime();
                    timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
                    errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                    errorInfo+=timeinfo;
                    errorInfo+="\n";
                }
                else
                {
                    errorInfo=" ";
                    errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                    errorInfo+="\n";
                }
                serialPortAssitant.ReciveWidget->append(errorInfo +errorContent);
                return;     
            }
            <span style="background-color: rgb(255, 0, 0);">connect(serialPort,SIGNAL(readyRead()),this,SLOT(onReadyRead()));</span>
            serialPort->setDataBits(QSerialPort::Data8);
            serialPort->setStopBits(QSerialPort::OneStop);
            serialPort->setParity(QSerialPort::NoParity);
            serialPort->setFlowControl(QSerialPort::NoFlowControl);
    
            QString serialStatusInfo;
            serialStatusInfo=serialPortAssitant.serialPortcomboBox->currentText().toUpper();
            serialStatusInfo+=" OPENED";
            serialStatusInfo+=" , ";
            serialStatusInfo+=QString::number(serialPort->baudRate());
            serialStatusInfo+=" , ";
            serialStatusInfo+=serialPortAssitant.DataWidthcomboBox->currentText();
            serialStatusInfo+=" , ";
            serialStatusInfo+=serialPortAssitant.ParityWidthcomboBox->currentText().toUpper();
            serialStatusInfo+=" , ";
            serialStatusInfo+=serialPortAssitant.StopWidthcomboBox->currentText();
            serialStatusInfo+=" , ";
            serialStatusInfo+=serialPortAssitant.FLowControlcomboBox->currentText().toUpper();
            QPalette font_palette;
            font_palette.setColor(QPalette::WindowText,Qt::darkCyan);
            serialPortAssitant.status->statusInfo->setText(serialStatusInfo);
            serialPortAssitant.status->statusInfo->setPalette(font_palette);
    
            serialPortAssitant.sendBtn->setText(QString::fromLocal8Bit("发送"));
            IsSerialPortOpen=true;
        }
    
    else
        {
            if(IsRepeatSend)
            {
                repeatSend->start();
            }
            Write();
        }
    }

    首先看write函数:以下是从write函数中抽离出来的几行关键代码:

            QString sendMsg=serialPortAssitant.sendLine->text();
    <span style="white-space:pre">  </span>QByteArray temp=sendMsg.toLatin1();
        char *sendContent=temp.data();
        qint64 sendedNum=serialPort->write(sendContent,temp.count());

    1、第一行是从QLineEdit获取需要发送的数据信息;

    2、第二行到第三行代码是需要把QString转换为char *的数据类型。

    3、第四行则是通过QIODevice类的成员函数write写出数据。

    4、剩余的部分是一些细节的错误提示显示以及显示信息,以及非常重要的是以ASCII形式发出数据还是以16进制发送数据。

    接着是sendData()函数。

    serialPort=new QSerialPort(portToOpen);
    if(!serialPort->open(QIODevice::ReadWrite))
    connect(serialPort,SIGNAL(readyRead()),this,SLOT(onReadyRead()));   

    1、第一行是通过portToOpen实例化QSerialPort,构造函数为: QSerialPort(const QString & name, QObject * parent = Q_NULLPTR)

    2、第二行是打开串口,打开模式是ReadWrite,可写可读

    3、第三行是通过readRead()信号来实现串口数据的读出,事件驱动的方式。这一行我在源代码中加了红色,原因是,一定要在打开串口后,实现readyRead()信号和对应的槽函数的连接,如果在没有初始化串口成功后,然后信号才能启动。我最开始在CameraSerialPort这个类的初始化中就定义了这个信号槽的链接,一直没有读到串口数据,结果在网上找了半天的原因,也没有找到这个问题。想了一下,然后把代码移到了这里,就可以了。

    打开串口和发送数据的结果如图:

    这里写图片描述

    这里写图片描述

    串口接收数据

    void CameraSerialPort::Read()
    {
        <span style="background-color: rgb(255, 204, 0);">if(serialPort->bytesAvailable()<0)</span>
        {
            serialPortAssitant.ReciveWidget->setText("No data");
            return;
        }   
    
    
    
        <span style="background-color: rgb(255, 204, 0);">QByteArray temp;</span>
        <span style="background-color: rgb(255, 204, 0);">temp=serialPort->readAll();</span>
    
        QString  receiveMsg;
        if(IsReceiveByHex)
            receiveMsg=ByteArrayToHexStr(temp);
        else
            receiveMsg=temp;
    
        if(receiveMsg.isEmpty())
        {
            if(IsShowCurrentTime)
            {
                errorInfo=" ";
                currentTime=QDateTime::currentDateTime();
                timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
                errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                errorInfo+=timeinfo;
                errorInfo+="\n";
            }
            else
            {
                errorInfo=" ";
                errorInfo=QString::fromLocal8Bit("错误提示信息   ");
                errorInfo+="\n";
            }
            serialPortAssitant.ReciveWidget->append(errorInfo +QString::fromLocal8Bit("没有读到数据\n"));
            return;
        }
    
        //接收到的字节数以初始的bytes为依据
        reciveCount+=temp.count();
        serialPortAssitant.status->RxByte->setText(QString::number(reciveCount));
        QString showReciveMsg;
        if(IsShowCurrentTime)
            {
    
                currentTime=QDateTime::currentDateTime();
                timeinfo=currentTime.toString("yyyy__MM__d__hh:mm:ss");
                showReciveMsg+=QString::fromLocal8Bit("接收数据 : ");
                showReciveMsg+=timeinfo;
                showReciveMsg+="\n";
                showReciveMsg+=receiveMsg;
            }   
            else
            {
                showReciveMsg=QString::fromLocal8Bit("接收数据 : ");
                showReciveMsg+=receiveMsg;
            }   
            serialPortAssitant.ReciveWidget->append(showReciveMsg);
    
    }
    
    void CameraSerialPort::onReadyRead()
    {
        Read();
    }

    1、读入数据之前,需要判断缓冲区是否有数据,有数据才去读数据

    2、如果有数据,则全部读出缓冲区数据

    总结

    大致的有关Qt的串口类就介绍完了,主要是QSerialPort以及QSerialPortInfo两个类的使用,当然也需要了解QIODevice。

    展开全文
  • 通过自定义通信协议,达到以串口发送指令,使STM32单片机能够根据指令控制继电器的通断时间,从而方便开关机的实验。 上位机的使用方法: 自定义通信协议( ee 00 12 04 ff) ee:帧数据的头部 00 12:十六进制数 --》...

    目标:
    通过自定义通信协议,达到以串口发送指令,使STM32单片机能够根据指令控制继电器的通断时间,从而方便开关机的实验。

    上位机的使用方法:
    自定义通信协议( ee 00 12 04 ff)
    ee:帧数据的头部
    00 12:十六进制数 --》十进制为18 则代表导通时间为18s
    04:继电器的断开时间十进制为4 则代表为断开时间为4s
    ff:帧的尾部
    注意:
    在输入数据时,输入ee的头部不能出现空格。
    在未输入指令时,系统默认导通60s,断开4s;
    在输入指令时导通时间需至少2s,断开时间需至少2s。
    注意单片机板卡是否支持USB转TTL电路;
    先插入USB再启动上位机软件。
    
    
    
    
    
    第一部分:完成上位机的编写
    
    #ifndef RELAYSYS_H
    #define RELAYSYS_H
    
    #include <QWidget>
    #include <QSerialPort> //提供访问串口的功能
    #include <QSerialPortInfo> //提供系统中存在的串口信息
    #include <qtimer.h>
    #include <QDebug>
    #include <QStringList>
    #include <QMessageBox>
    #include <QTextBlock>
    
    namespace Ui {
    class RelaySys;
    }
    
    class RelaySys : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit RelaySys(QWidget *parent = 0);
        ~RelaySys();
    
        void serialPortInit(void); //串口初始化
    
        char convertCharToHex(char ch);
        int QString2HexInt(QString str);
    
    private slots:
        void on_pushButton_OpenSerial_clicked();
    
        void on_pushButton_send_clicked();  //发送按钮槽函数
        void on_serialPortRecv(void); //串口接收槽函数
        void on_timeUpData(void);
        void on_pushButton_clearRecv_clicked();
    
        void on_pushButton_clearSend_clicked();
    
    private:
        Ui::RelaySys *ui;
    
        QSerialPort *serial; //创建串口对象
        QTimer *timer = new QTimer(); //创建定时器,用于在规定的时间内对接收到的数据进行处理
        bool flag = true;
        QByteArray buf; //接收串口发送过来的数据
        int sendBuffer[5];
    };
    
    #endif // RELAYSYS_H
    
    

    源文件

    #include "relaysys.h"
    #include "ui_relaysys.h"
    
    RelaySys::RelaySys(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::RelaySys)
    {
        ui->setupUi(this);
    
        this->setWindowTitle("继电器控制软件");
        serial = new QSerialPort;
        serialPortInit();
    
        //连接信号槽函数,当下位机发送数据QSerialPortInfo会发送readRead信号
        connect(serial,SIGNAL(readyRead()),this,SLOT(on_serialPortRecv()));
    
        connect(timer,SIGNAL(timeout()),this,SLOT(on_timeUpData()));
    }
    
    
    RelaySys::~RelaySys()
    {
        delete serial;
        delete ui;
    }
    
    
    void RelaySys::serialPortInit()
    {
        foreach (const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
        {
            ui->comboBox_port->addItem(info.portName());//添加串口号名字
            serial->setPort(info);
    
        }
    #if 1
        QStringList baudList;
        QStringList checkList;
        QStringList dataBitList;
        QStringList stopBitList;
    
        baudList << "4800" << "9600" << "14400" << "19200"
                 << "38400"<< "56000"<< "57600" << "115200";
        ui->comboBox_baud->addItems(baudList);
        ui->comboBox_baud->setCurrentIndex(7);
    
        checkList << "No" << "Even" << "Odd" << "Space" << "Mark";
        ui->comboBox_check->addItems(checkList);
        ui->comboBox_check->setCurrentIndex(0);
    
        dataBitList << "5 bit" << "6 bit" << "7 bit" << "8 bit";
        ui->comboBox_dataBit->addItems(dataBitList);
        ui->comboBox_dataBit->setCurrentIndex(3);
    
        stopBitList << "1 bit" << "1.5 bit" << "2 bit";
        ui->comboBox_stopBit->addItems(stopBitList);
        ui->comboBox_stopBit->setCurrentIndex(0);
    
    #else
        //增加波特率下拉框选项
        ui->comboBox_baud->addItem("4800");
        ui->comboBox_baud->addItem("9600");
        ui->comboBox_baud->addItem("14400");
        ui->comboBox_baud->addItem("19200");
        ui->comboBox_baud->addItem("38400");
        ui->comboBox_baud->addItem("56000");
        ui->comboBox_baud->addItem("57600");
        ui->comboBox_baud->addItem("115200");
    
        //默认选择第7个115200这个波特率
        ui->comboBox_baud->setCurrentIndex(7);
    
        //增加数据位下拉框选项
        ui->comboBox_dataBit->addItem("5");
        ui->comboBox_dataBit->addItem("6");
        ui->comboBox_dataBit->addItem("7");
        ui->comboBox_dataBit->addItem("8");
        ui->comboBox_dataBit->setCurrentIndex(3);
    
    
        //增加停止位下拉框选项
        ui->comboBox_stopBit->addItem("1 bit");
        ui->comboBox_stopBit->addItem("1.5 bit");
        ui->comboBox_stopBit->addItem("2 bit");
        ui->comboBox_stopBit->setCurrentIndex(0);
    
        ui->comboBox_check->addItem("NONE");
    
        /*
        ui->comboBox_port->clear();
        //获取可连接的所有的串口的名字
        QList<QSerialPortInfo>serialportInfos = QSerialPortInfo::availablePorts();
        qDebug()<< "Total numbers of ports:"<<serialportInfos.count();
    
        //用于循环访问serialportInfos集合中所需要的信息
        foreach (const QSerialPortInfo info,serialportInfos)
        {
            ui->comboBox_port->addItem(info.portName());//添加串口号名字
        }
        */
    
     #endif
    }
    
    void RelaySys::on_pushButton_OpenSerial_clicked()
    {
        qDebug() << ui->pushButton_OpenSerial->text() << endl;
    
        if(ui->pushButton_OpenSerial->text() == "打开")
        {
            serial->setPortName(ui->comboBox_port->currentText()); //设置串口名
    
            if(!serial->open(QIODevice::ReadWrite))//用ReadWrite的模式尝试打开串口
            {
    
                 QMessageBox::about(NULL,"提示","串口打开\r\n不存在或被其它程序占用");
                 qDebug() << "打开失败!" << endl;
                    return;
            }
            else
            {
                qDebug()<<  endl << "打开成功" << endl;
                //设置波特率
                serial->setBaudRate(ui->comboBox_baud->currentText().toInt());
    
                //设置校验位
                switch(ui->comboBox_check->currentIndex())
                {
                    case 0: serial->setParity(QSerialPort::NoParity);break;
                    case 1: serial->setParity(QSerialPort::EvenParity);break;
                    case 2: serial->setParity(QSerialPort::OddParity);break;
                    case 3: serial->setParity(QSerialPort::SpaceParity);break;
                    case 4: serial->setParity(QSerialPort::MarkParity);break;
                    default: break;
                }
    
                //设置数据位
                switch(ui->comboBox_dataBit->currentIndex())
                {
                    case 0: serial->setDataBits(QSerialPort::Data5);break;
                    case 1: serial->setDataBits(QSerialPort::Data6);break;
                    case 2: serial->setDataBits(QSerialPort::Data7);break;
                    case 3: serial->setDataBits(QSerialPort::Data8);break;
                    default: break;
                }
                //设置停止位
                switch(ui->comboBox_stopBit->currentIndex())
                {
                    case 0: serial->setStopBits(QSerialPort::OneStop);break;
                    case 1: serial->setStopBits(QSerialPort::OneAndHalfStop);break;
                    case 2: serial->setStopBits(QSerialPort::TwoStop);break;
                    default: break;
                }
                //设置流控
                serial->setFlowControl(QSerialPort::NoFlowControl);
    
                ui->comboBox_baud->setEnabled(false);
                ui->comboBox_check->setEnabled(false);
                ui->comboBox_dataBit->setEnabled(false);
                ui->comboBox_port->setEnabled(false);
                ui->comboBox_stopBit->setEnabled(false);
                ui->pushButton_OpenSerial->setText("关闭");
            }
         }
        else
        {
            if(serial->isOpen()) //如果串口打开则将其关闭
            {
                serial->clear();
                serial->close();
            }
            ui->pushButton_OpenSerial->setText("打开");
            ui->comboBox_baud->setEnabled(true);
            ui->comboBox_check->setEnabled(true);
            ui->comboBox_dataBit->setEnabled(true);
            ui->comboBox_port->setEnabled(true);
            ui->comboBox_stopBit->setEnabled(true);
    
            qDebug() << "关闭成功" << endl;
        }
    
    }
    
    void RelaySys::on_pushButton_send_clicked()
    {
    
        int i = 0;
        short value = 0; //记录导通的有效时间
        int SIZE = 0; //接收个数
        char *pBuf  = (char *)&value;
        QString temp;
    
        //获取textEdit中的内容 toPlainText()获取文本函数
        QString str = ui->textEdit->toPlainText();
    
        QStringList list = str.split(" ");//以指定的字符“ ”空格分割支付串
        SIZE = sizeof(sendBuffer)/sizeof(sendBuffer[0]);
        //qDebug() << "SIZE = " << SIZE;
    
        if(list.length() > SIZE && list[SIZE] != NULL)
        {
            QMessageBox::about(NULL, "提示", "请输入有效的指令!");
            return ;
        }
    
        for(i = 0; i < SIZE; i++)
        {
            temp = list[i];
            if(temp.length() > 2 || temp.length() == 1 )
            {
                QMessageBox::about(NULL, "提示", "请输入有效的指令!");
                return ;
            }
            sendBuffer[i] = QString2HexInt(temp); //转化为十进制
            /*sendBuffer[i] = temp.toInit(&ok,16);*/
             qDebug() << sendBuffer[i] << " ";
        }
    
        pBuf[0] = sendBuffer[2];
        pBuf[1] = sendBuffer[1];
        //qDebug() << "value == " << value << endl;
    
        //对帧数据进行校验
        if(list[0] != "ee" || value < 2 || sendBuffer[3] < 2 || list[4] != "ff")
        {
            QMessageBox::about(NULL, "提示", "输入的指令不正确,请重新输入;格式: ee 导通时间>=2s 断开时间>=2s ff");
            return ;
        }
        else
        {
            QByteArray buffer;
            buffer.resize(SIZE);
            for(int i = 0; i < SIZE; i++)
            {
                buffer[i] = sendBuffer[i];
            }
            serial->write(buffer);
        }
    
    }
    
    
    void RelaySys::on_timeUpData(void)
    {
    
        timer->stop();
        //时间到了处理数据
    
    
    }
    
    void RelaySys::on_serialPortRecv(void)
    {
        timer->start(100); //使用定时器定时接收数据,非阻塞机制,100ms后自动认为接收数据完成
        //buf = serial->readAll();
        buf.append(serial->readAll());
        int i = 0;
        QString RecvBuffer; //存放十六进制数据
        QString st;  //使能够显示中文
        QString str;
        if(!buf.isEmpty()) //若非空则表示有数据接收
        {
            st = QString::fromLocal8Bit(buf); //需能显示中文
            qDebug() << "st = " << st << endl;
    
            if(ui->checkBox_show16->isChecked())
            {
                str = buf.toHex().data(); //转换成十六进制
                str = str.toUpper(); //转换成大写
                for(i = 0; i < str.length(); i += 2)
                {
                   QString temp = str.mid(i, 2);
                   RecvBuffer += temp;
                   RecvBuffer += " ";
                }
                ui->plainTextEdit->appendPlainText(RecvBuffer);
            }
            else
            {
                ui->plainTextEdit->appendPlainText(st);
            }
        }
    
    
        QString line = QString::number(ui->plainTextEdit->document()->lineCount());
        // QMessageBox::information(this,"information",line);
        int line_val = line.toInt();
        qDebug() << "line = " << line_val;
        // QString data = ui->plainTextEdit->document()->findBlockByLineNumber(line_val).text();
        //qDebug() << "data = " << data;
    
        buf.clear();
    
    }
    
    
    //将一个字节的字符串转化为整型  十六进制转化为十进制  如“0x1f"--->31
    int RelaySys::QString2HexInt(QString str)
    {
        QByteArray byte;
        byte = str.toLatin1(); //单字节编码兼容ASCII
    
        int high = convertCharToHex(byte[0]);
        int low = convertCharToHex(byte[1]);
        return high*16 + low;
    }
    
    
    char RelaySys::convertCharToHex(char ch)
     {
         if((ch >= '0') && (ch <= '9'))
             return ch - 0x30; //0x30 -->48
         else if((ch >= 'A') && (ch <= 'F'))
             return ch - 'A' +10;
         else if((ch >= 'a') && (ch <= 'f'))
             return ch - 'a' + 10;
         else
             return -1;
     }
    
    void RelaySys::on_pushButton_clearRecv_clicked()
    {
          ui->plainTextEdit->clear();
    }
    
    void RelaySys::on_pushButton_clearSend_clicked()
    {
        ui->textEdit->clear();
    }
    
    

    在这里插入图片描述


    第二部分:关于keil的工程创建及功能实现
    

    main.c

    #include "main.h"
    #include "core_cm0.h"
    #include "relay.h"
    #include "usart.h"
    #include "delay.h"
    #include "TIM_delay.h"
    
    extern uint8_t gRecvBuff[5];
    extern uint8_t gRecvFlag;
    extern uint32_t gCount;
    
    void SysTick_Delay_Ms( __IO uint32_t ms)
    {
    	uint32_t i;	
    	SysTick_Config(SystemCoreClock/1000);
    	
    	for(i=0;i<ms;i++)
    	{
    		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1
    		// 当置1时,读取该位会清0
    		while( !((SysTick->CTRL)&(1<<16)) );
    	}
    	// 关闭SysTick定时器
    	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
    }
    
     
     
    void SYSTICK_INIT(void)
    {
    		SysTick_Config(SystemCoreClock / 1000);	//Set SysTick Timer for 1ms interrupts
    		//SysTick_Config(SystemCoreClock / 200);	//Set SysTick Timer for 5ms interrupts
    		//SysTick_Config(SystemCoreClock / 100);	//Set SysTick Timer for 10ms interrupts
    		//SysTick_Config(SystemCoreClock / 10);	//Set SysTick Timer for 100ms interrupts     
    }
     
    
    void delay(int num)
    {
    	unsigned int  i = 0;
    	while(num--)
    	{
    		for(i = 0; i < 1650; i++);
    	}
    
    }
    /**
      * @brief  Main program
      * @param  None
      * @retval None
      */
    int main(void)
    {
    		uint8_t i = 0;
    		uint16_t num = 0;
    		uint8_t *pBuf = (uint8_t *)&num;
    	
    		uint16_t OnTimes = 0;
    		uint32_t tempCnt = gCount;
    		USART_Config(115200);
    		delay_init();
    	
    		TIMx_Init(999,47);
    		relay_init();
    	
    	
    //		printf("format: 0xEE 0xMS1 0xMS0 0xFF \n");
    //		delay_ms(10);
    //		printf("ms1:导通时间,ms0:断开时间\n");
    
    	
    	loop:	
    		while (1)
    		{
    			
    					if(gRecvFlag == 0) //未收到指令时,默认是导通60s,断开4s
    					{
    						relay_on();
    						OnTimes++;
    						printf("Times = %d\n",OnTimes);
    						
    						for(i = 0; i < 60; i++)
    						{
    							if(gCount == 1) //若第一条指令到来,退出默认状态 关闭导通
    							{
    								//printf("gCount = %d\n",gCount);
    								tempCnt = gCount;
    								relay_off(); 
    								OnTimes = 0;
    								delay_ms(1000); //不加延时无法看出新指令执行效果
    								break;
    							}
    							delay_ms(1000);
    						}
    						
    						relay_off();
    						for(i = 0; i < 4; i++)
    						{
    							if(gCount == 1) //若第一条指令到来则,退出默认状态
    							{
    								tempCnt = gCount;
    								relay_off();
    								OnTimes = 0;
    								delay_ms(1000);
    								break;
    							}
    							delay_ms(1000);
    						}
    						
    					}
    					else
    					{
    						while(!gRecvFlag); //收到指令
    //					for(i = 0; i < 4; i++)
    //					{
    //						printf("gRecvBuff[%d] =  %x\n",i,gRecvBuff[i]);
    //					}
    						relay_on();
    						OnTimes++;
    						printf("Times = %d\n",OnTimes);
    						
    						pBuf[0] = gRecvBuff[2];
    						pBuf[1] = gRecvBuff[1];
    					
    						//printf("0x%02x \n",num);
    						for( i = 0; i < num; /* i < gRecvBuff[1]*10; */ i++)
    						{
    							
    							if(tempCnt + 1 ==  gCount) 
    							{
    								//printf("tempCnt = %d\n",tempCnt);
    								tempCnt = gCount;  //tempCnt++;
    								relay_off();
    								OnTimes = 0;
    								delay_ms(2000);
    								goto loop;
    							}
    							delay_ms(1000);
    						
    						}		
    						relay_off();
    						for( i = 0; i < gRecvBuff[3]; i++)
    						{
    							if(tempCnt + 1 ==  gCount)
    							{
    								tempCnt = gCount; //tempCnt++;
    								relay_off();
    								OnTimes = 0;
    								delay_ms(2000);
    								goto loop;
    							}
    			
    							delay_ms(1000);
    						}	
    					}						
    		}
    }
    
    
    
    
    
    
    
    #ifdef  USE_FULL_ASSERT
    
    void assert_failed(uint8_t* file, uint32_t line)
    { 
    
      while (1)
      {
      }
    }
    #endif
     
    
    
    
    

    relay.h

    #ifndef RELAY_H_
    #define RELAY_H_
    #include "stm32f0xx.h"
    
    void relay_init(void);
    void relay_on(void);
    void relay_off(void);
    
    
    #endif /* RELAY_H_*/
    
    

    relay.c

    #include "relay.h"
    
    void relay_init(void)
    { 
    	 GPIO_InitTypeDef  GPIO_InitStructure;
    	 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);	//时钟配置
    		
    	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_11;				
    	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 	
    	 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽模式输出
    	 //GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉(浮空)
    	 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉模式 
    	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;	
    	
    	 GPIO_SetBits(GPIOB, GPIO_Pin_11);
    	 GPIO_Init(GPIOB, &GPIO_InitStructure);
    }
    
    void relay_on(void)
    {
    	GPIO_SetBits(GPIOB,GPIO_Pin_2);
    	//GPIOB->BSRR = GPIO_Pin_2;
    	GPIO_ResetBits(GPIOB, GPIO_Pin_11);
    
    }
    
    void relay_off(void)
    {
    	GPIO_ResetBits(GPIOB,GPIO_Pin_2);
    	//GPIOB->BRR = GPIO_Pin_2;
    	GPIO_SetBits(GPIOB, GPIO_Pin_11);
    
    }
    
    
    

    delay.h

    #ifndef DELAY_H_
    #define DELAY_H_
    #include "stm32f0xx.h"
    #include "core_cm0.h"
    
    void delay_init(void);
    
    void delay_us(uint32_t nus);
    
    void delay_ms(uint32_t nms);
    
    
    void Delay_ms(uint32_t nms);
    void Delay_us(uint32_t nus);
    
    
    #endif /*DELAY_H_*/
    
    

    delay.c

    #include "delay.h"
    
    static uint8_t  fac_us=0;							//us延时倍乘数			   
    static uint16_t  fac_ms=0;							//ms延时倍乘数,在ucos下,代表每个节拍的ms数
    
    
    //查询方式
    void delay_init(void)
    {
    	//SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
    	SysTick->CTRL = 0xFFFFFFFB;
    	fac_us = SystemCoreClock / 48000000;				 
    	fac_ms = (uint16_t)fac_us * 1000;				  
    
    }
    
    
    void delay_us(uint32_t nus)
    {		
    	unsigned int temp;	    	 
    	SysTick->LOAD = nus * fac_us; 					//时间加载	  		 
    	SysTick->VAL = 0x00;        					//清空计数器
    	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//打开定时器,开始倒数	  
    	do
    	{
    		temp = SysTick->CTRL;  //读取当前倒计数值
    	}while((temp&0x01) && !(temp&(1<<16)));		//等待时间到达   
    	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭定时器
    	SysTick->VAL = 0X00;      					 //清空计数器	 
    }
    
    #if 1
    void delay_ms(uint32_t nms)
    {	 		  	  
    	unsigned int temp;		   
    	SysTick->LOAD= (uint32_t)nms * fac_ms;				//时间加载(SysTick->LOAD为24bit)
    	SysTick->VAL = 0x00;							//清空计数器
    	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;	//开始倒数  
    	do
    	{
    		temp = SysTick->CTRL;
    	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
    	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭定时器
    	SysTick->VAL = 0X00;       					//清空计数器	  	    
    } 
    
    #else
    void delay_ms(uint32_t nms)
    {	
    	uint32_t i = 0;
    	SysTick_Config(48000);
    	for(i = 0; i < nms; i++)
    	{
    		while(!((SysTick->CTRL) & ( 1<<16)));
    		
    	}
    	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
    }
    
    #endif
    
    /******************************************/
    uint32_t TimingDelay = 0;
    void Delay_us(uint32_t nus)
    {
    	SysTick_Config(SystemCoreClock/100000); //10us中断一次
    	TimingDelay = nus;
    	while(TimingDelay != 0);
    }
    
    void Delay_ms(uint32_t nms)
    {
    	SysTick_Config(SystemCoreClock/1000); //1ms中断一次
    	TimingDelay = nms;
    	while(TimingDelay != 0);
    }
    
    
    usart.h
    
    #ifndef USART_H_
    #define USART_H_
    #include "stm32f0xx.h"
    #include <stdio.h>
    
    
    void USART_Config(uint32_t BaudRate);
    
    void USART1_Send_Char(unsigned char c);
    void USART1_Send_String(char *s );
    void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
    void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
    void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
    void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);
    
    
    
    #endif /*USART_H_*/
    
    

    usart.c

    #include "usart.h"
    #include "relay.h"
    #include "delay.h"
    
    uint8_t gRecvBuff[5];
    uint8_t gRecvFlag = 0;
    
    void USART_Config(uint32_t BaudRate)
    { 
        GPIO_InitTypeDef GPIO_InitStructure;  
    		USART_InitTypeDef USART_InitStructure;
    		NVIC_InitTypeDef 	NVIC_InitStructure;
    	
    		NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    		NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    		NVIC_Init(&NVIC_InitStructure);
    	
    	  //配置时钟
    		RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA , ENABLE);
    		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE );	
    		
    	
    		GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1); //Tx
    		GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1); //Rx   											 
    		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;                 
    		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; 
    		GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    		GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    		GPIO_Init(GPIOA, &GPIO_InitStructure);   
    
    		USART_InitStructure.USART_BaudRate = BaudRate;
    		USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    		USART_InitStructure.USART_StopBits = USART_StopBits_1;
    		USART_InitStructure.USART_Parity = USART_Parity_No;
    		USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    		USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    		USART_Init(USART1, &USART_InitStructure);
    		
    	
    		USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //接收一个字节中断
    		//USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //接收一帧数据中断
    		USART_Cmd(USART1, ENABLE);
      
    }
    
    
    
    
    
    
    
    uint8_t h = 0;
    uint32_t gCount = 0;
    void USART1_IRQHandler(void)
    {
    	uint16_t temp = 0;
    	USART_ClearITPendingBit(USART1,USART_IT_RXNE);
    	
    	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收数据寄存器不为空
    	{
    	
    		temp = USART_ReceiveData(USART1);
    		if((temp == 0xee) && (h == 0))
    		{
    			h = 1;
    			gRecvBuff[0] = temp;
    		}
    		else if( h == 1)
    		{
    			h = 2;
    			gRecvBuff[1] = temp;
    		}
    		else if(h == 2)
    		{
    			h = 3;
    			gRecvBuff[2] = temp;
    		}
    		else if(h == 3)
    		{
    			h = 4;
    			gRecvBuff[3] = temp;
    		}
    		else if(h == 4)
    		{
    			gRecvFlag = 1;
    			h = 0;
    			gRecvBuff[4] = temp;
    			gCount++; //记录有效指令条数
    			//printf("第%d条指令\n",gCount);
    		}
    		else
    		{
    			h = 0;
    		}
    		
    		while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
    	
    	}
    }
    
    
    
    
      // 重定义  
    //int fputc(int ch, FILE *f)
    //{
    //	while (!(USART1->ISR & USART_FLAG_TXE));
    //	USART1->TDR = ch;
    //  
    //  return (ch);
    //}
    
    
    
    ///重定向c库函数printf到串口,重定向后可使用printf函数
    int fputc(int ch, FILE *f)
    {
    		/* 发送一个字节数据到串口 */
    		USART_SendData(USART1, (uint8_t) ch);
    		
    		/* 等待发送完毕 */
    		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
    	
    		return (ch);
    }
    
    int fgetc(FILE *f)
    {
    		/* 等待串口输入数据 */
    		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    
    		return (int)USART_ReceiveData(USART1);
    }
    
    
    void USART1_Send_Char(unsigned char c)
    {
    	while(!((USART1->ISR)&USART_FLAG_TXE));
    	USART1->TDR=c;	
    }
    
    void USART1_Send_String(char *s )
    {
    	while (*s)
    	USART1_Send_Char(*s++);
    }
    
    
    
    /*****************  发送一个字节 **********************/
    void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
    {
    	/* 发送一个字节数据到USART */
    	USART_SendData(pUSARTx,ch);
    		
    	/* 等待发送数据寄存器为空 */
    	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
    }
    
    /****************** 发送8位的数组 ************************/
    void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
    {
      uint8_t i = 0;
    	
    	for(i=0; i< num; i++)
      {
    	    /* 发送一个字节数据到USART */
    	    Usart_SendByte(pUSARTx,array[i]);	
      
      }
    	/* 等待发送完成 */
    	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
    }
    
    /*****************  发送字符串 **********************/
    void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
    {
    	unsigned int k = 0;
      do 
      {
          Usart_SendByte( pUSARTx, *(str + k) );
          k++;
      } while(*(str + k)!='\0');
      
      /* 等待发送完成 */
      while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
      {}
    }
    
    /*****************  发送一个16位数 **********************/
    void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
    {
    	uint8_t temp_h, temp_l;
    	
    	/* 取出高八位 */
    	temp_h = (ch&0XFF00)>>8;
    	/* 取出低八位 */
    	temp_l = ch&0XFF;
    	
    	/* 发送高八位 */
    	USART_SendData(pUSARTx,temp_h);	
    	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    	
    	/* 发送低八位 */
    	USART_SendData(pUSARTx,temp_l);	
    	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
    }
    
    

    总结:
    大体的功能基本具备,能够实现目标,但还存在一些问题,编写的代码并未完全利用,对于QT中的串口、定时器、各种进制之间的转换还需注意,串口发送及接收可以更好的处理;基于单片机的系统定时器(滴答定时器),硬件定时,通过串口传输数据等知识点需学习掌握。

    遇到的问题:

    1. 能控制继电器但不能够正常工作,查看开发板的原理图时需注意继电器的工作电压,以及连接STM32核心板的引脚,便于控制继电器的工作;

    2. 未能够实现精确延时,需注意每个芯片的晶振频率;

    3. 需对传输的数据进行校验,在新指令到来时的处理需考虑清楚,容易出错;

    4. 对QT串口发送到单片机的数据是何种类型需了解;

    5. 获取程序异常状态信息,通过看门狗实现;

    6. 单片机能发送数据,上位机未能接收。

    展开全文
  • Qt上位机串口通信,实现了与STM32单片机的数据收发,按钮控制LED、蜂鸣器。资源包括STM32单片机源程序、Qt源工程、打包好的.exe上位机软件程序。内含配置说明文档,适合新手学习Qt串口开发。 友情提示:Qt源程序...
  • Qt学习了几周,做一个串口助手巩固一下最近学习的内容。 遇到的问题1: write函数只能发送一次数据,想要继续发送必须重新关闭打开串口,每次只能发送一次数据 ...遇到的问题2:STM32只能发送数据给...

    Qt学习了几周,做一个串口助手巩固一下最近学习的内容。

    遇到的问题1: write函数只能发送一次数据,想要继续发送必须重新关闭打开串口,每次只能发送一次数据
    解决办法:在网上找不到类似的问题,机缘巧合下发现别人Qt工程封装好的.exe文件可以正常多条发送,在自己电脑的Qt环境下编译就只能发送一条数据,怀疑是环境的问题,卸载后下载最新版本Qt,解决了该问题。

    遇到的问题2:STM32只能发送数据给上位机,接收不到Qt上位机的数据
    解决办法:原因是Qt发送的是字符串,不是16进制数据,而且单片机串口中断服务函数的机制是每条数据的帧尾必须是0x0d 0x0a,这条数据才为有效数据,才能够被STM32单片机接收。因此发送的每条数据除了需要把字符串转换为16进制以外,还需要在数据增加单片机标志帧尾。
    需要注意的是不同的单片机串口协议可能是不同的,我使用的是正点原子STM32F1单片机。可以通过打开两个串口助手,向设置好的虚拟串口发送数据,点选hex接收,查看该单片机串口通讯协议的校验帧。

    上位机图片:
    在这里插入图片描述

    下面放上Qt工程与单片机的源代码链接:
    https://download.csdn.net/download/Uncle_Tan123/12238290

    头文件:

    #ifndef DIALOG_H
    #define DIALOG_H
    
    #include <QDialog>
    #include <QSerialPort>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Dialog; }
    QT_END_NAMESPACE
    
    class Dialog : public QDialog
    {
        Q_OBJECT
    
    public:
        Dialog(QWidget *parent = nullptr);
        ~Dialog();
    
    public:
        bool getSerialPortConfig(void);
    
    private slots:  //槽函数,遇到相应信号就触发
        void on_btn_open_clicked();
        void on_btn_send_clicked();
        void on_SerialPort_readyRead();
    
        void StringToHex(QString str, QByteArray &senddata);
        char ConvertHexChar(char ch);
    private:
        Ui::Dialog *ui;
        bool mIsOpen;  //串口按钮是否打开
         //串口对象指针及配置参数
        QSerialPort *mSerialPort;
        QString mPortName;
        QString mBaudRate;
        QString mParity;
        QString mDataBits;
        QString mStopBits;
    };
    #endif // DIALOG_H
    
    

    CPP文件:

    #include "dialog.h"
    #include "ui_dialog.h"
    #include <QSerialPortInfo>
    #include <QSerialPort>
    #include <QList>
    #include <QDebug>
    #include <QTextEdit>
    
    Dialog::Dialog(QWidget *parent)
        : QDialog(parent)
        , ui(new Ui::Dialog)
    {
        ui->setupUi(this);
    
        mSerialPort = new QSerialPort; //创建一个串口对象
    
        mIsOpen = false; //初始化按钮状态标志位
        ui->btn_send->setEnabled(mIsOpen);
    
        //识别系统的所有可用串口号,并添加到下拉列表中
        QList<QSerialPortInfo> serialPortInfo = QSerialPortInfo::availablePorts();
        int count = serialPortInfo.count();
        for(int i = 0;i < count;i++)
        {
            ui->Cboxport->addItem(serialPortInfo.at(i).portName());
        }
    
        //等待一个触发信号,接收串口数据
        connect(mSerialPort, SIGNAL(readyRead()), this, SLOT(on_SerialPort_readyRead()));
    }
    
    Dialog::~Dialog()
    {
        delete ui;
    }
    
    bool Dialog::getSerialPortConfig()  //配置串口
    {
        //获取串口配置
        mPortName = ui->Cboxport->currentText();
        mBaudRate = ui->Cboxboudrate->currentText();
        mParity = ui->Cboxparity->currentText();
        mDataBits = ui->Cboxdatabits->currentText();
        mStopBits = ui->Cboxstopbits->currentText();
    
        //设置串口
        //串口号
        mSerialPort->setPortName(mPortName);
        //波特率
        if("115200" == mBaudRate)
        {
            mSerialPort->setBaudRate(QSerialPort::Baud115200);
        }
        else
        {
            mSerialPort->setBaudRate(QSerialPort::Baud9600);
        }
        //校验位
        if("EVEN" == mParity)
        {
            mSerialPort->setParity(QSerialPort::EvenParity);
        }
        else if("ODD" == mParity)
        {
            mSerialPort->setParity(QSerialPort::OddParity);
        }
        else
        {
            mSerialPort->setParity(QSerialPort::NoParity);
        }
        //数据位
        if("5" == mDataBits)
        {
            mSerialPort->setDataBits(QSerialPort::Data5);
        }
        else if("6" == mDataBits)
        {
            mSerialPort->setDataBits(QSerialPort::Data6);
        }
        else if("7" == mDataBits)
        {
            mSerialPort->setDataBits(QSerialPort::Data7);
        }
        else
        {
            mSerialPort->setDataBits(QSerialPort::Data8);
        }
        //停止位
        if("1.5" == mStopBits)
        {
            mSerialPort->setStopBits(QSerialPort::OneAndHalfStop);
        }
        if("2" == mStopBits)
        {
            mSerialPort->setStopBits(QSerialPort::TwoStop);
        }
        else
        {
            mSerialPort->setStopBits(QSerialPort::OneStop);
        }
        qDebug() << "配置";
        return mSerialPort->open(QSerialPort::ReadWrite);
    }
    void Dialog::on_btn_open_clicked()  //打开关闭按钮状态
    {
        if(true == mIsOpen)
        {
            //当前已经打开了串口,点击后将按钮更新为关闭状态
            mSerialPort->close();
            ui->btn_open->setText("打开");
            mIsOpen = false;
            //此时可以配置串口
            ui->Cboxport->setEnabled(true);
            ui->Cboxboudrate->setEnabled(true);
            ui->Cboxparity->setEnabled(true);
            ui->Cboxdatabits->setEnabled(true);
            ui->Cboxstopbits->setEnabled(true);
            ui->btn_send->setEnabled(mIsOpen);
            qDebug() << "关闭";
        }
        else
        {
            //当前处于关闭串口状态,打开前需要配置串口
            //getSerialPortConfig();
            if(true == getSerialPortConfig())
            {
                mIsOpen = true;
                ui->btn_open->setText("关闭");
                qDebug() << "成功打开串口" << mPortName;
                ui->Cboxport->setEnabled(false);
                ui->Cboxboudrate->setEnabled(false);
                ui->Cboxparity->setEnabled(false);
                ui->Cboxdatabits->setEnabled(false);
                ui->Cboxstopbits->setEnabled(false);
                ui->btn_send->setEnabled(mIsOpen);
            }
    //        else
    //        {
    //            mIsOpen = false;
    //        }
        }
    }
    
    void Dialog::on_btn_send_clicked() //发送按钮
    {
        if(true == mIsOpen)
        {
            //mSerialPort->write(ui->textEdit_send->toPlainText().toLatin1());// toPlainText(将文本编辑的文本转换为纯文本)
            //mSerialPort->write(ui->textEdit_send->toPlainText().toStdString().c_str());
            //注意:write函数的传递参数是QString,先转换为C++标准的string,再转为char型
            QString str = ui->textEdit_send->toPlainText();
            int len = str.length();
            if(len%2 == 1)
            {
                str = str.insert(len - 1, '0');
            }
            QByteArray senddata;  //写入编辑框的数据
            StringToHex(str,senddata);
    
            int length = senddata.length();  //在帧尾追加STM32的关键帧
            senddata[length + 1] = 0x0d;
            senddata[length + 2] = 0x0a;
            mSerialPort->write(senddata);
        }
    }
    
    void Dialog::on_SerialPort_readyRead()  //读取串口数据
    {
        if(true == mIsOpen)
        {
            QByteArray recvData = mSerialPort->readAll();
            //ui->textEdit_Recv->setPlainText(QString(recvData)); //显示内容每次会被覆盖
            ui->textEdit_Recv->append(QString(recvData));
            qDebug() << "正在接收数据";
        }
    }
    
    void Dialog::StringToHex(QString str, QByteArray &senddata) //字符串转换为十六进制数据0-F
    {
        int hexdata,lowhexdata;
        int hexdatalen = 0;
        int len = str.length();
        senddata.resize(len/2);
        char lstr,hstr;
    
    
        for(int i=0; i<len; )
        {
            //char lstr,
            hstr=str[i].toLatin1();
            if(hstr == ' ')
            {
                i++;
                continue;
            }
            i++;
            if(i >= len)
                break;
            lstr = str[i].toLatin1();
            hexdata = ConvertHexChar(hstr);
            lowhexdata = ConvertHexChar(lstr);
            if((hexdata == 16) || (lowhexdata == 16))
                break;
            else
                hexdata = hexdata*16+lowhexdata;
            i++;
            senddata[hexdatalen] = (char)hexdata;
            hexdatalen++;
        }
        senddata.resize(hexdatalen);
    }
    
    
    char Dialog::ConvertHexChar(char ch)
    {
        if((ch >= '0') && (ch <= '9'))
            return ch-0x30;
        else if((ch >= 'A') && (ch <= 'F'))
            return ch-'A'+10;
        else if((ch >= 'a') && (ch <= 'f'))
            return ch-'a'+10;
        else return ch-ch;//不在0-f范围内的会发送成0
    }
    
    展开全文
  • Qt与stm32通信绘制波形

    2017-08-25 21:32:07
    Qt上位机,接收发送数据,stm32与Qt进行串口通信
  • QT与STM32串口通信的问题记录

    千次阅读 2017-02-23 16:29:49
    前期已经完成的事情:1、QT串口通信的编写,使用QT5...目前遇到的问题:1、用QT编写的上位机与stm32通信存在问题,缓冲区不能清零,就发多少就会接到后面,不会每次都清空再填充。  2、但是串口助手没有这种问题,那

    前期已经完成的事情:1、QT串口通信的编写,使用QT5自带的QtSerialPort类(串口类)。

                        2、stm32串口通信程序的编写,实现与串口助手的正常通信。

    目前遇到的问题:1、用QT编写的上位机与stm32的通信存在问题,缓冲区不能清零,就发多少就会接到后面,不会每次都清空再填充。

                    2、但是串口助手没有这种问题,那就肯定是上位机的问题。开始一直在怀疑我自己写的程序,后来发现上位机并没有致命的错误。

      

    分析问题:1、stm32串口接受中断判断的是\r\n(0x0d,0x0a),而QT上位机发出去的没有\r\n(我猜测是这个原因),然后将结束判断位变成(0x38,0x39),就实现了功能。


    总结:总的来说是这样的,没有对串口中断接受有深刻的理解,对通信传输没有摸透。

    通信传输的基本模式为:字符串(char)--->ASCII码----->二进制--->ASCII码--->字符串(char)

    这是最基本的模式,所以关于数据帧的封装,帧头帧尾的定义就迎刃而解了。


    最后再说一些关于stm32的串口通信的问题:初始化函数就不提了,几个寄存器的配置。


    void USART1_IRQHandler(void)函数是串口1 的中断响应函数,当串口 1 发生了相应的中
    断后,就会跳到该函数执行。这里我们设计了一个小小的接收协议:通过这个函数,配合一个
    数组
    USART_RX_BUF[],一个接收状态寄存器USART_RX_STA(此寄存器其实就是一个全局
    变量,由作者自行添加。由于它起到类似寄存器的功能,这里暂且称之为寄存器) 实现对串口
    数据的接收管理。
    USART_RX_BUF 的大小由USART_REC_LEN 定义,也就是一次接收的数据
    最大不能超过
    USART_REC_LEN 个字节。USART_RX_STA 是一个接收状态寄存器其各的定义
    如表
    5.3.1.1 所示:

    USART_RX_STA
    bit15 bit14 bit13~0
    接收完成标志 接收到0X0D 标志 接收到的有效数据个数

    5.3.1.1 接收状态寄存器位定义表
    设计思路如下:
    当接收到从电脑发过来的数据,把接收到的数据保存在
    USART_RX_BUF 中,同时在接收
    状态寄存器(
    USART_RX_STA)中计数接收到的有效数据个数,当收到回车(回车的表示由2
    个字节组成: 0X0D 0X0A)的第一个字节0X0D 时,计数器将不再增加,等待0X0A 的到来,
    而如果
    0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。如果顺利接收到0X0A
    则标记
    USART_RX_STA 的第15 位,这样完成一次接收,并等待该位被其他程序清除,从而开
    始下一次的接收,而如果迟迟没有收到 0X0D,那么在接收数据超过USART_REC_LEN 的时候,
    则会丢弃前面的数据,重新接收。
     



    void USART1_IRQHandler(void)
    {
    u8 res;
    if(USART1->SR&(1<<5))//接收到数据
    {  
    res=USART1->DR; 
    if((USART_RX_STA&0x8000)==0)//接收未完成
    {
    if(USART_RX_STA&0x4000)//接收到了0x0d
    {
    if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
    else USART_RX_STA|=0x8000; //接收完成了 
    }else //还没收到0X0D
    {
    if(res==0x0d)USART_RX_STA|=0x4000;
    else
    {
    USART_RX_BUF[USART_RX_STA&0X3FFF]=res;
    USART_RX_STA++;
    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收  
    }  
    }
    }       

    刚好为\r\n.如果结束符不是\r\n,那就用最后两位结束符的ASCII码来代替0x0d,0x0A.(注意顺序)

    展开全文
  • 问题描述: 1.QT5.5的串口通信,运用的是QSerialPort类,并利用的是write()函数实现,QT的上位机软件向STM32的下位机发送数组数组。 2. 将要发送的结构体,...4. 而在STM32的下位机接受中,QT上位机软件尽可实现一次...
  • 话不多说先看看效果: 光照:L=2.50勒克斯 气压:P=946.17pa ...通过UART口上位计算机经行信息交互。配上DS3231实现定时自动发送数据的功能。 存储功能。上位机能够收下位机传输的数据并存储于E盘的
  • 文章目录设计需求UDPTCP和UDP的区别UI界面设计界面设计UDP代码设计实现打开UDP关闭UDP发送数据关闭UDP上位机板间通信协议板子通信代码设计UDP上位机数据处理显示HEX形式转十进制显示QString::number 设计需求 ...
  • 使用自带MAC层的32位闪存微控制器STM32F407外置PHY芯片LAN8720在LWIP协议栈的基础上实现以太网通信功能的系统设计方案,并通过LWIP协议栈的UDP通信与基于QT5.8平台上编写的上位机客户端建立数据通信,并实时发送...
  • 基于MIT和CSDN上的开源代码开发的简单上位机软件; Windows 10 + QT5.8.0;...测试: keil + stm32主控。下位机软件在测试阶段,暂不给出。 提供的下载地址:http://download.csdn.net/detail/u013828589/9773798
  • Qt_USB-HID_timer.zip

    2020-02-27 13:17:16
    STM32CubeMX实现STM32 USBHID双向64字节通信(Qt上位机部分),使用Qt完成与STM32的usbhid通信,实现主动发送和定时器定时接收功能。
  • 本设计是一款电力线路漏电监测远程控制系统,本次设计使用了指定硬件平台:STM32F407( Cortex-M4微控制器),以STM32F407核心板为主体,结合GSM模块,构建了一个无线电力线路监测远程控制系统。 本系统主要由...
  • 多线程的串口通信-1

    2020-04-22 00:23:19
    多线程的串口通信1 下位机STM32发送的数据格式串口参数1.1下位机上传的数据格式:1.2串口参数2 上位机程序2.1Future:~~2.2串口参数同上~~3 QT程序设计QT走过的坑--多线程 参考文献: vodka QSerialPort多线程...
  • project.rar

    2020-02-04 15:15:14
    Qt上位机,接收发送数据,stm32与Qt进行串口通信,可直接运行-
  • STM32 推荐购买正点原子开发板,附送配套教程。 单片机开发需要熟练掌握C语言,了解硬件电路。 通用IO -> 按键输入 -> 串口通信 -> 外部中断 -> 定时器中断 -> PWM输出 (ADC、DAC转换和I2C、CAN通信建议掌握) FPGA...
  • wenshidushangweiji.rar

    2020-08-29 13:21:18
    上位机使用了QT5.9.0进行编程,与STM32单片机之间进行串口通信,可以自动搜索串口,能够实时显示温湿度数据,同时在坐标系上显示温湿度数据的动态变化曲线。
  • 节点主控将采集到的数据通过网络发送到STM32主控,STM32显示相关数据,并将数据转发至上位机。 软件模块说明(介绍应用软件关键部分的逻辑、采用的实现方式等)主控平台软件: (1)4.3 TFFLCD (2)界面设计使用SteamWin...
  • LabVIEW上位机与串口通信 Latex入门 Markdown中插入数学公式的方法 使用Markdown书写 推荐一个不错的代码搜索引擎 插入视频测试 读书写作 一个程序猿从金庸开始的读书历程 拾掇起来的年华 一首诗熟记三十六计 ...
  • 7、接口方式:总线接口(可与STM32 FSMC总线连接); 8、接口形式:40p FPC连接器,0.5mm间距; 9、供电方式:4~24V宽电压供电,内建 DCDC 电路; 10、其他特性:内建stm8 CPU 作为模块管理,可写入唯一ID,网络物理地址...
  • 本网络照相机基于STM32H7+RTThread平台,采集摄像头数据,并通过无线网络传送到服务器,提供SD卡配网、手动拍摄、定时拍摄、照片推送等功能,并提供windows上位机提供控制和照片显示功能。 主要功能有: 格式化sd卡:...

空空如也

空空如也

1 2
收藏数 30
精华内容 12
关键字:

qt上位机与stm32通信