精华内容
下载资源
问答
  • qt实现串口通信

    2016-10-18 15:26:32
    Qt5实现串口通信
  • QT实现串口通信

    2014-01-03 10:41:49
    在VS下,使用QT和C++实现串口通信小助手,里面有波特率,校验位,数据位,停止位等的设置,可以选择哪个串口,可以控制串口的关闭和打开。
  • Qt实现串口通信源码

    2017-03-18 10:41:17
    此资源是Qt实现串口通信源码,用qt模仿串口调试助手做的界面,特别适合初学者快速掌握qt。
  • Qt实现串口通信示例 前言:以下串口通信示例并不完全属于原创,参考了现有网上前辈们的资源,最后结合部分个人的思想,所以下述博客会将实现的原理及代码的案例进行公开。 这里我们先上效果图: 一、串口通信...

                                                         Qt实现串口通信示例

    前言:以下串口通信示例,参考了现有网上前辈们的资源,最后结合部分个人的思想,所以下述博客会将实现的原理及代码的案例进行公开。

    这里我们先上效果图:

    一、串口通信简介

    串口通信是上下位机进行通信的一种常用的通信协议,大部分单片机中都有一到多个串口资源经过简单的配置就可以实现上下位机的通信,下图是串口通信协议中的一种形式。如果你不是用硬件描述语言去实现一个串口,这部分了解下即可。常用的是8位数据位加起始位加停止位,因此串口是只能发送0-255区间的数据。因此想要发送int或者float型的数据需要按高地位或者到内存中取出数据来发送,此时就需要通信协议来确保数据的准确性,接下来将依次进行介绍。

    二、串口通信各参数的含义

    了解完串口通信的大致含义,我们再编程时候比较关心的还是串口通信中波特率、奇偶校验、数据位以及停止位的含义。下面我们引用一位前辈作出的解释,有关本文中涉及到的引用会在本文最后附上原创的出处,为了美观,不在相应的地方附上版权声明了。

    简介

    串口是一种非常通用的设备通信的协议(不要与通用串行总线Universal Serial Bus(USB)混淆)。大多数计算机包含两个基于RS232的串口。串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS-232口。同时,串口通信协议也可以用于获取远程采集设备的数据。
    串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。
    典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配:
    波特率

    这是一个衡量符号传输速率的参数。它表示每秒钟传送的符号的个数。例如300波特表示每秒钟发送300个符号。当我们提到时钟周期时,我们就是指波特率,例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
    数据位

    这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
    停止位

    用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
    奇偶校验位

    在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不是真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
    2.补充

    比特率
    在数字信道中,比特率是数字信号的传输速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数bit/s(bps)、每秒千比特数(Kbps)或每秒兆比特数(Mbps)来表示(此处K和M分别为1000和1000000,而不是涉及计算机存储器容量时的1024和1048576)。
    波特率
    波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud)。 波特率与比特率的关系为:比特率=波特率X单个调制状态对应的二进制位数。
    显然,两相调制(单个调制状态对应1个二进制位)的比特率等于波特率;四相调制(单个调制状态对应2个二进制位)的比特率为波特率的两倍;八相调制(单个调制状态对应3个二进制位)的比特率为波特率的三倍;依次类推。
    RS232是要用在近距离传输上最大距离为30M
    RS485用在长距离传输最大距离1200M

    三、Qt中的串口通信

    qt中集成了QSerialport类,可以直接使用该类实现串口通信。这部分内容看到网上有的说在建立工程的时候要选择Qt Project  Setting ,不过我在使用Qt5.12建立工程的时候仍然按照普通项目工程建立(这里不再额外叙述怎么New 一个Project了),如果Qt版本低的话,工程失败情况下可以另行找工程建立方法。

    在qt中使用串口通信仍然比较简单,只需要在.pro文件中加入下述代码就完成了。

    QT       += core gui serialport
    

    然后在工程的头文件中引用相关的头文件。

    #include <QtSerialPort/QSerialPort>
    #include <QtSerialPort/QSerialPortInfo>
    

    接着在类中将串口类进行实例化。

        QSerialPort *serialPort;
    

    以上的过程,我们就已经准备好了串口通信所需要的必要条件,接下来要使用串口通信,少不了的要对串口实例化的对象进行参数的设定,下面我们对串口进行初始化。

    void Serial_port::InitPort()
    {
        const auto infos = QSerialPortInfo::availablePorts();
        for(const QSerialPortInfo &info : infos)
        {
            QSerialPort serial;
            serial.setPort(info);
            if(serial.open(QIODevice::ReadWrite))
            {
                ui->PortBox->addItem(info.portName());
                serial.close();
            }
        }
        QStringList baudList;   //波特率
        QStringList parityList; //校验位
        QStringList dataBitsList;   //数据位
        QStringList stopBitsList;   //停止位
        // 波特率    //波特率默认选择下拉第三项:9600
        baudList<<"1200"<<"2400"<<"4800"<<"9600"
               <<"14400"<<"19200"<<"38400"<<"56000"
              <<"57600"<<"115200";
        ui->BaudBox->addItems(baudList);
        ui->BaudBox->setCurrentIndex(3);
        // 校验      //校验默认选择无
        parityList<<"无"<<"奇"<<"偶";
        ui->ParityBox->addItems(parityList);
        ui->ParityBox->setCurrentIndex(0);
        // 数据位      //数据位默认选择8位
        dataBitsList<<"5"<<"6"<<"7"<<"8";
        ui->DataBox->addItems(dataBitsList);
        ui->DataBox->setCurrentIndex(3);
        // 停止位      //停止位默认选择1位
        stopBitsList<<"1"<<"2";
        ui->StopBox->addItems(stopBitsList);
        ui->StopBox->setCurrentIndex(0);
    }
    

    其中初始化函数 中首先获取计算机中有效的端口号,然后将端口号的名称给端口选择控件。相应的波特率等也是赋予选择控件相应的值。setCurrentIndex()是给控件设定默认下拉项的index。

    经过对串口对象指针的初始化,就已经完成串口通信所要求的各参数配置,一个完整的通信当然也少不了串口的收数了,下面给出串口数据的收报函数。

    void Serial_port::readData()
    {
        QByteArray buf;
        if (serialPort){
    
        buf = serialPort->readAll();
    
        if (!buf.isEmpty())
        {
    
            receBytes += buf.size();
            QString redata = QString("received:%1").arg(QString::number(receBytes));
            ui->sendlabel->setText(redata);
            QString myStrTemp = QString::fromLocal8Bit(buf); //支持中文显示
            if(ui->reDisplay->isChecked())
            {
                QString str = ui->textBrowser->toPlainText();
                str +=myStrTemp;
                ui->textBrowser->clear();
                ui->textBrowser->append(str);
            }
        }
        buf.clear();
        }
    
    }
    

    其实,从上面的函数中我们就能发现qt中串口的收报函数很简单,只是需要下面的一行命令,就能够完成了串口的收数功能。

        buf = serialPort->readAll();
    

    而相应的我们并不会满足这样单调的功能,所以在收报函数中加入接收总字节数显示以及显示收到的内容,这样的功能。

            receBytes += buf.size();
            QString redata = QString("received:%1").arg(QString::number(receBytes));
            ui->sendlabel->setText(redata);
    
                ui->textBrowser->append(str);
    

    说完了串口通信的收报,我们先不提发报的事情,从相关的串口助手上我们都知道,在显示接收的数据时候,很多情况下要求显示收到报文的16进制形式,所以这里我们再写另外一种收报函数(以16进制接收并显示)。

    void Serial_port::readToHex()
    {
        QByteArray temp = serialPort->readAll();
        auto isShow = ui->reDisplay->isChecked();         //接收显示?
        QDataStream out(&temp,QIODevice::ReadOnly);    //将字节数组读入
        while(!out.atEnd())
        {
               qint8 outChar = 0;
               out>>outChar;   //每字节填充一次,直到结束
               //十六进制的转换
               QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
               if(isShow){
                   ui->textBrowser->insertPlainText(str.toUpper());//大写
                   ui->textBrowser->insertPlainText(" ");//每发送两个字符后添加一个空格
                   ui->textBrowser->moveCursor(QTextCursor::End);
               }
        }
    
    }
    

    最后让我们完成串口通信的发报功能,这部分其实很简单,但为了追求发报功能的完整性,所以我们丰富这部分函数的功能,同样的将发送功能分为文本发送和以16进制形式的发送。

    void Serial_port::on_sendButton_clicked()
    {
        //Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。ISO-8859-1编码是单字节编码,向下兼容ASCII
       //其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
        QString str = ui->lineEdit->text();
    
        if(!str.isEmpty())
        {
            auto isHexSend = ui->sHexRadio->isChecked();
    
            int len = str.length();
            if(len%2 == 1)
            {
                str = str.insert(len-1,'0');
            }
            QByteArray senddata;
            if(isHexSend)
            {
                StringToHex(str,senddata);
                serialPort->write(senddata);
    
                if(serialPort->write(senddata)<0)
                {
                    QMessageBox::critical(this, tr("Error"), serialPort->errorString());
                }
            }
            else
            {
                if(serialPort->write(ui->lineEdit->text().toLocal8Bit())<0)
                {
                    QMessageBox::critical(this, tr("Error"), serialPort->errorString());
                }
            }
    
        }
    
    }
    

    字符转换为16进制函数如下:

    void Serial_port::StringToHex(QString str, QByteArray &senddata)
    {
        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 = convertHexChart(hstr);
                   lowhexdata = convertHexChart(lstr);
                   if((hexdata == 16) || (lowhexdata == 16))
                       break;
                   else
                       hexdata = hexdata*16+lowhexdata;
                   i++;
                   senddata[hexdatalen] = (char)hexdata;
                   hexdatalen++;
               }
               senddata.resize(hexdatalen);
    
    }
    
    char Serial_port::convertHexChart(char ch)
    {
        if((ch >= '0') && (ch <= '9'))
                    return ch-0x30;  // 0x30 对应 ‘0’
                else if((ch >= 'A') && (ch <= 'F'))
                    return ch-'A'+10;
                else if((ch >= 'a') && (ch <= 'f'))
                    return ch-'a'+10;
        //        else return (-1);
        else return ch-ch;//不在0-f范围内的会发送成0
    
    }
    

    四、解码

    对于串口通信来说,收报是重要的一步但也不是最后的一步,在进行开发时,我们不但要完成数据的接收,同样要根据协议双方的约定完成报文的解码,再进行相应算法的开发,解码的复杂程度依赖于协议的复杂程度,这里我们仅仅提供一种简单的解码校验方式。

    假设上图是我们接收的一个完整报的形式,其中ox55和0x53表示报文头,中间的校验位等暂不考虑,最后的sum表示和校验位。和校验即通过数据的累加和去验证数据的正确与否。上图是一帧需要发送的数据可以看从左到右分别为帧头,功能字,数据帧与和校验,还可以加上数据位数。帧头和功能字可以根据自己的需求进行定制,数据位即需要发送的数据,最后的SUM即为无符号char型数据,将SUM之前的数据求和(不用管数据的溢出)即可。下位机按这种方式将数据发送上来上位机只要找到帧头即可进行解码并且对数据进行验证,这里附一个简单的上位机解码的程序,之前用来解码下位机发送来的欧拉角数据使用的程序。

    void uart::ReadData()
    {
    	ui.buffSize->setText(QString::number(serialPort.bytesAvailable()));
    	int buffersize = ui.bufferSize->value();
    	if (serialPort.bytesAvailable()>buffersize){   //更改过滤个数,提高通信速率
    	requestData = serialPort.readAll().toHex();//转成 hex
                }
    	if (!requestData.isEmpty() )
    	{
    		QByteArray temp = requestData.mid(QString(requestData).indexOf("55"), 22);
    		unsigned char buffer[11] = { 0 };
    		unsigned char sum = 0;
    		for (int i = 0; i < 11; i++)
    		{
    			buffer[i] = (ByteArrayToHexchar(temp.at((i << 1) )) << 4 )+ ByteArrayToHexchar(temp.at((i << 1) + 1 ));
    			if (i<10)
    				sum = sum + buffer[i];
    		}
    		//sum = buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[5] + buffer[6];
    		if ((buffer[0] == 0x55) && (buffer[1] == 0x53))
    		if (sum == buffer[10])
    		{
    			float x, y, z;
    			x = (buffer[3] << 8 | buffer[2]);
    			y = (buffer[5] << 8 | buffer[4]);
    			z = (buffer[7] << 8 | buffer[6]);
    			x = x / 32768 * 180;
    			y = y / 32768 * 180;
    			z = z / 32768 * 180;
    			ui.x->setText(QString::number(x, 'f', 2));
    			ui.y->setText(QString::number(y, 'f', 2));
    			ui.z->setText(QString::number(z, 'f', 2));
    			//ui.textEdit->append(QString::number(z, 'f', 2));
    			
    			requestData.clear();
    		}
    	}
    	
    	if (!timer.isActive())
    		timer.start(5000);//500ms定时器
    }
    

    当然如果不考虑和校验位置的话就更简单了,假设我们的协议规定了一个完整报的长度为40字节,报文头为ox03和0x66,然后我们要从第15位和第16位解码出一个int数据,这里将去掉大部分个人的内容。

            if(temp.size()==40)
            {
                if((recvdata[0]==0x03)&&(recvdata[1]==0x66))
                {
                    target_data.e=(int)(recvdata[15]+recvdata[16]*256);
                    
                }
            }
    

    五、发报模拟器

    这里我们嵌入到demo中作为其中的一个小功能了,其中路径后面的输入框,在进行单击时候会打开文件系统框要求你去选取txt文件,然后点击播放按钮将读取txt中的内容(这里我的txt所有的数据都在一行,如果是分行的txt需要你自己去更改相应的代码)然后我这里每次模拟发报40个字节。

    void Serial_port::handleLineEditClicked()
    {
        //QString curPath = QDir::currentPath();
        QString curPath = "../QSerial_port";
        //QFile file;
        QString aFileName = QFileDialog::getOpenFileName(this,QString("选择文件"),curPath,QString("TEXT(*.txt)"));
        ui->m_txt->setText(aFileName);
    
    }
    
    void Serial_port::on_Playbutton_clicked()
    {
        QString path = ui->m_txt->text();
        //qDebug()<<"是否成功获取"<<path_ce->text();
    
        if(path.isEmpty())
            return;
        QFile aFile(path);
        if(!aFile.exists())     //文件不存在
            qDebug() <<"the file not exist!!";
            //return;
        if(!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
            qDebug() <<"文件无法打开";
            //return;
        QTextStream aStream(&aFile);    //用文件流读取文件
        while (!aStream.atEnd())
        {
            QString qstr = aStream.readLine();   //读取文件的一行文本
            //qDebug()<<qstr.length();
            int len = qstr.length();
            if(len%2 == 1)
            {
                qstr = qstr.insert(len-1,'0');
            }
            QByteArray senddata;
            StringToHex(qstr,senddata);
            //qDebug()<<senddata.size();
            for(int i=0;i<senddata.size();i++)
            {
                QByteArray tmpsenddata=senddata.mid(i,40);
                i=i+39;
                //qDebug()<<tmpsenddata.length();
                serialPort->write(tmpsenddata);
    
                if(!serialPort->waitForBytesWritten())   //这一句很关键,决定是否能发送成功
                {
                     qDebug()<<"serial write error";
                }
    
                Sleep(1000);
            }
    
        }
    
    }
    

    到这里基本就完成了串口通信的全部内容(上述代码设计到了工程的ui控件,请自行修改),另外额外提供一点,在模拟器中如果循环对串口数据进行发报,我们都知道,核心的代码就是

                serialPort->write(tmpsenddata);
    

    但是当我们在进行发报的时候,会发现哪怕程序执行了write功能,收报方仍然无法收到,这里有人提出qt中的串口通信在发送后必须要进行发送是否成功的判断才能够正常工作,当然,这一点在循环中执行write是最明显看到的,单个命令可能是正常的,所以这里我们必须要加上判断。

                if(!serialPort->waitForBytesWritten())   //这一句很关键,决定是否能发送成功
                {
                     qDebug()<<"serial write error";
                }
    

    好了到此Qt的串口通信就完全结束了,为了更直观的看到各功能的实现,下面给出完整的代码,如果还不明白,点击最后链接下载完整工程唠!

    Serial_port.h

    #ifndef SERIAL_PORT_H
    #define SERIAL_PORT_H
    
    #include <QMainWindow>
    #include <QtSerialPort/QSerialPort>
    #include <QtSerialPort/QSerialPortInfo>
    #include <QLabel>
    #include <QTimer>
    #include <windows.h>
    #include <QString>
    #include <QDebug>
    #include <dbt.h>
    #include <devguid.h>
    #include <setupapi.h>
    #include <initguid.h>
    #include <list>
    using namespace std;
    
    namespace Ui {
    class Serial_port;
    }
    
    class Serial_port : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit Serial_port(QWidget *parent = nullptr);
        void InitPort();
        ~Serial_port();
    
    private slots:
        void readData();    //读取数据
        void timeToSend();  //定时发送
    
        void on_OpenButton_clicked();
    
        void on_r_clearButton_clicked();
    
        void on_s_clearButton_clicked();
    
        void on_sendButton_clicked();
    
        void StringToHex(QString str,QByteArray &senddata); //发送时进制转换
        char convertHexChart(char ch);  //16进制转换
        void readToHex();   //将读取的数据以16进制显示
        void Mdisplay();
        //void pasingData(QByteArray &array); //解析串口数据
    
        void handleLineEditClicked();
    
        void on_Playbutton_clicked();
    
    private:
        Ui::Serial_port *ui;
        QSerialPort *serialPort;
        QTimer *time;
        static   int sendBytes;
        static   int receBytes;
    };
    #endif // SERIAL_PORT_H
    

    Serial_port.cpp

    #include "Serial_port.h"
    #include "ui_Serial_port.h"
    #include <QMessageBox>
    #include "m_lineEdit.h"
    #include <QDebug>
    #include <QFileDialog>
    
    
    Serial_port::Serial_port(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::Serial_port)
    {
        ui->setupUi(this);
        time = new QTimer(this);
        InitPort();
        //设置默认值
        ui->PortBox->setCurrentIndex(1);
        ui->BaudBox->setCurrentIndex(7);
        ui->StopBox->setCurrentIndex(0);
        ui->DataBox->setCurrentIndex(3);
        ui->ParityBox->setCurrentIndex(0);
    
        ui->sendButton->setEnabled(false);
        ui->sHexRadio->setEnabled(false);
        ui->sTextRadio->setChecked(true);
        ui->sTextRadio->setEnabled(false);
        ui->rTextRadio->setChecked(true);
        ui->rHexRadio->setChecked(false);
        ui->reDisplay->setChecked(true);
    
        connect(ui->reSendCheck, &QCheckBox::stateChanged,
                this, &Serial_port::timeToSend);         //自动重发
        connect(time, &QTimer::timeout, this, &Serial_port::on_sendButton_clicked);//
    
        connect(ui->rHexRadio, &QRadioButton::toggled, this, &Serial_port::Mdisplay);
    
        connect(ui->m_txt, SIGNAL(clicked()), this, SLOT(handleLineEditClicked()));
    
    
    }
    
    int Serial_port::sendBytes = 0;
    int Serial_port::receBytes = 0;
    
    void Serial_port::InitPort()
    {
        const auto infos = QSerialPortInfo::availablePorts();
        for(const QSerialPortInfo &info : infos)
        {
            QSerialPort serial;
            serial.setPort(info);
            if(serial.open(QIODevice::ReadWrite))
            {
                ui->PortBox->addItem(info.portName());
                //qDebug()<<info.portName();
                serial.close();
            }
        }
        QStringList baudList;   //波特率
        QStringList parityList; //校验位
        QStringList dataBitsList;   //数据位
        QStringList stopBitsList;   //停止位
        // 波特率    //波特率默认选择下拉第三项:9600
        baudList<<"1200"<<"2400"<<"4800"<<"9600"
               <<"14400"<<"19200"<<"38400"<<"56000"
              <<"57600"<<"115200";
        ui->BaudBox->addItems(baudList);
        ui->BaudBox->setCurrentIndex(3);
        // 校验      //校验默认选择无
        parityList<<"无"<<"奇"<<"偶";
        ui->ParityBox->addItems(parityList);
        ui->ParityBox->setCurrentIndex(0);
        // 数据位      //数据位默认选择8位
        dataBitsList<<"5"<<"6"<<"7"<<"8";
        ui->DataBox->addItems(dataBitsList);
        ui->DataBox->setCurrentIndex(3);
        // 停止位      //停止位默认选择1位
        stopBitsList<<"1"<<"2";
        ui->StopBox->addItems(stopBitsList);
        ui->StopBox->setCurrentIndex(0);
    }
    
    Serial_port::~Serial_port()
    {
        delete serialPort;
        delete time;
        delete ui;
    }
    
    void Serial_port::readData()
    {
        QByteArray buf;
        if (serialPort){
    
        buf = serialPort->readAll();
    
        if (!buf.isEmpty())
        {
    
            receBytes += buf.size();
            QString redata = QString("received:%1").arg(QString::number(receBytes));
            ui->sendlabel->setText(redata);
            QString myStrTemp = QString::fromLocal8Bit(buf); //支持中文显示
            if(ui->reDisplay->isChecked())
            {
                QString str = ui->textBrowser->toPlainText();
                str +=myStrTemp;
                ui->textBrowser->clear();
                ui->textBrowser->append(str);
            }
        }
        buf.clear();
        }
    
    }
    
    void Serial_port::timeToSend()
    {
        if(ui->reSendCheck->isChecked())
        {
            if(time->isActive())
            {
                return;
            }
            else
            {
                int ms = ui->spinBox->value();
                time->start(ms);
            }
        }
        else
        {
            if(time->isActive())
            {
                time->stop();
            }
            else
            {
                return;
            }
        }
    
    }
    
    void Serial_port::on_OpenButton_clicked()
    {
        if (ui->OpenButton->text() == tr("打开串口"))
        {
            serialPort = new QSerialPort;
    
            serialPort->setPortName(ui->PortBox->currentText());
    
            if(serialPort->open(QIODevice::ReadWrite))
            {
                //qDebug()<<ui->BaudBox->currentIndex();
                switch (ui->BaudBox->currentIndex()) {
                case 0:
                    serialPort->setBaudRate(QSerialPort::Baud1200);
                    break;
                case 1:
                    serialPort->setBaudRate(QSerialPort::Baud2400);
                    break;
                case 2:
                    serialPort->setBaudRate(QSerialPort::Baud4800);
                    break;
                case 3:
                    serialPort->setBaudRate(QSerialPort::Baud9600);
                    break;
                case 4:
                    serialPort->setBaudRate(QSerialPort::Baud19200);
                    break;
                case 5:
                    serialPort->setBaudRate(QSerialPort::Baud38400);
                    break;
                case 6:
                    serialPort->setBaudRate(QSerialPort::Baud57600);
                    break;
                case 7:
                    serialPort->setBaudRate(QSerialPort::Baud115200);
                    break;
                default:
                    break;
                }
    
                switch (ui->StopBox->currentIndex()) {
                case 0:
                    serialPort->setStopBits(QSerialPort::OneStop);
                    break;
                case 1:
                    serialPort->setStopBits(QSerialPort::TwoStop);
                    break;
                default:
                    break;
                }
    
                switch (ui->DataBox->currentIndex()) {
                case 0:
                    serialPort->setDataBits(QSerialPort::Data5);
                    break;
                case 1:
                    serialPort->setDataBits(QSerialPort::Data6);
                    break;
                case 2:
                    serialPort->setDataBits(QSerialPort::Data7);
                    break;
                case 3:
                    serialPort->setDataBits(QSerialPort::Data8);
                    break;
                default:
                    break;
                }
    
                switch (ui->ParityBox->currentIndex()) {
                case 0:
                    serialPort->setParity(QSerialPort::NoParity);
                    break;
                case 1:
                    serialPort->setParity(QSerialPort::OddParity);
                    break;
                case 2:
                    serialPort->setParity(QSerialPort::EvenParity);
                    break;
                default:
                    break;
                }
    
                ui->OpenButton->setText(tr("关闭串口"));
                ui->PortBox->setEnabled(false);
                ui->BaudBox->setEnabled(false);
                ui->StopBox->setEnabled(false);
                ui->DataBox->setEnabled(false);
                ui->ParityBox->setEnabled(false);
                ui->sendButton->setEnabled(true);
                ui->sTextRadio->setEnabled(true);
                ui->sHexRadio->setEnabled(true);
                connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readData);
                connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readToHex);
            }
            else
            {
                QMessageBox::critical(this, tr("Error"), serialPort->errorString());
            }
        }
        else
        {
            serialPort->clear();
            serialPort->close();
            serialPort->deleteLater();
    
            ui->sendButton->setEnabled(false);
            ui->OpenButton->setText(tr("打开串口"));
            ui->PortBox->setEnabled(true);
            ui->BaudBox->setEnabled(true);
            ui->StopBox->setEnabled(true);
            ui->DataBox->setEnabled(true);
            ui->ParityBox->setEnabled(true);
            ui->sHexRadio->setEnabled(false);
            ui->sTextRadio->setEnabled(false);
    
        }
    
    }
    
    void Serial_port::on_r_clearButton_clicked()
    {
        ui->textBrowser->clear();
    
    }
    
    void Serial_port::on_s_clearButton_clicked()
    {
        ui->lineEdit->clear();
    }
    
    void Serial_port::on_sendButton_clicked()
    {
        //Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。ISO-8859-1编码是单字节编码,向下兼容ASCII
       //其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
        QString str = ui->lineEdit->text();
    
        if(!str.isEmpty())
        {
            auto isHexSend = ui->sHexRadio->isChecked();
    
            int len = str.length();
            if(len%2 == 1)
            {
                str = str.insert(len-1,'0');
            }
            QByteArray senddata;
            if(isHexSend)
            {
                StringToHex(str,senddata);
                serialPort->write(senddata);
    
                if(serialPort->write(senddata)<0)
                {
                    QMessageBox::critical(this, tr("Error"), serialPort->errorString());
                }
            }
            else
            {
                if(serialPort->write(ui->lineEdit->text().toLocal8Bit())<0)
                {
                    QMessageBox::critical(this, tr("Error"), serialPort->errorString());
                }
            }
    
        }
    
    }
    
    void Serial_port::StringToHex(QString str, QByteArray &senddata)
    {
        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 = convertHexChart(hstr);
                   lowhexdata = convertHexChart(lstr);
                   if((hexdata == 16) || (lowhexdata == 16))
                       break;
                   else
                       hexdata = hexdata*16+lowhexdata;
                   i++;
                   senddata[hexdatalen] = (char)hexdata;
                   hexdatalen++;
               }
               senddata.resize(hexdatalen);
    
    }
    
    char Serial_port::convertHexChart(char ch)
    {
        if((ch >= '0') && (ch <= '9'))
                    return ch-0x30;  // 0x30 对应 ‘0’
                else if((ch >= 'A') && (ch <= 'F'))
                    return ch-'A'+10;
                else if((ch >= 'a') && (ch <= 'f'))
                    return ch-'a'+10;
        //        else return (-1);
        else return ch-ch;//不在0-f范围内的会发送成0
    
    }
    
    void Serial_port::readToHex()
    {
        QByteArray temp = serialPort->readAll();
        auto isShow = ui->reDisplay->isChecked();         //接收显示?
        QDataStream out(&temp,QIODevice::ReadOnly);    //将字节数组读入
        while(!out.atEnd())
        {
               qint8 outChar = 0;
               out>>outChar;   //每字节填充一次,直到结束
               //十六进制的转换
               QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
               if(isShow){
                   ui->textBrowser->insertPlainText(str.toUpper());//大写
                   ui->textBrowser->insertPlainText(" ");//每发送两个字符后添加一个空格
                   ui->textBrowser->moveCursor(QTextCursor::End);
               }
        }
    
    }
    
    void Serial_port::Mdisplay()
    {
        if(ui->rHexRadio->isChecked())
        {
            disconnect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readData);
            connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readToHex);
        }
        else
        {
            connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readData);
            disconnect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readToHex);
        }
    
    }
    
    void Serial_port::handleLineEditClicked()
    {
        //QString curPath = QDir::currentPath();
        QString curPath = "../QSerial_port";
        //QFile file;
        QString aFileName = QFileDialog::getOpenFileName(this,QString("选择文件"),curPath,QString("TEXT(*.txt)"));
        ui->m_txt->setText(aFileName);
    
    }
    
    void Serial_port::on_Playbutton_clicked()
    {
        QString path = ui->m_txt->text();
        //qDebug()<<"是否成功获取"<<path_ce->text();
    
        if(path.isEmpty())
            return;
        QFile aFile(path);
        if(!aFile.exists())     //文件不存在
            qDebug() <<"the file not exist!!";
            //return;
        if(!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
            qDebug() <<"文件无法打开";
            //return;
        QTextStream aStream(&aFile);    //用文件流读取文件
        while (!aStream.atEnd())
        {
            QString qstr = aStream.readLine();   //读取文件的一行文本
            //qDebug()<<qstr.length();
            int len = qstr.length();
            if(len%2 == 1)
            {
                qstr = qstr.insert(len-1,'0');
            }
            QByteArray senddata;
            StringToHex(qstr,senddata);
            //qDebug()<<senddata.size();
            for(int i=0;i<senddata.size();i++)
            {
                QByteArray tmpsenddata=senddata.mid(i,40);
                i=i+39;
                //qDebug()<<tmpsenddata.length();
                serialPort->write(tmpsenddata);
    
                if(!serialPort->waitForBytesWritten())   //这一句很关键,决定是否能发送成功
                {
                     qDebug()<<"serial write error";
                }
    
                Sleep(1000);
            }
    
        }
    
    }
    

    最后提示一点,ui中的lineedit为自定义控件,需要提升相应功能,及添加对应类。

    六、虚拟串口工具

    对于程序来说,最方便的测验程序的时候不需要接硬件就能测试,这里介绍给大家一款能够创建虚拟串口的工具Virtual Serial Port Driver,以及串口小助手,当然咱们自己写的就是小助手,为了严重功能确实好用,那也可以下载个网上的,或者运行两个,有关工具的介绍这里就不提了,百度上讲的已经很好了,具体的资源也很容易百度到,这里也不提供了,百度对该工具的使用介绍链接(点击)。

    七、参考文献

    1.文中第一部分,串口通信的简介、和校验解码、串口通信图片及报文示图片来源于CSDN博主「WAmani」的原创文章,链接为:https://blog.csdn.net/wamani/article/details/52849043

    2.文中第二部分,串口通信各参数的含义来源于CSDN博主「guomutian911」的原创文章,链接为:https://blog.csdn.net/guomutian911/article/details/47044603/

    3.文中一些功能的实现来源于CSDN博主「苍雨流痕」的原创文章,链接为https://blog.csdn.net/hellolru/article/details/80838824

    八、资源下载

    点击下载。或者访问链接:https://download.csdn.net/download/zijinmu69/12376049

     

    展开全文
  • Qt实现串口通信总结

    2018-08-23 10:39:00
    Qt实现串口通信总结 注意:Qt5发布之前,Qt实现串口通信一般是采用第三方类库qextserialport。Qt5发布后自带了QtSerialPort能够支持串口通信。 1、Qextserialport类介绍 在Qt5之前的版本中并没有特定的串口...

    注意: Qt5发布之前,Qt实现串口通信一般是采用第三方类库qextserialport。Qt5发布后自带了QtSerialPort 能够支持串口通信。

    1、Qextserialport类介绍

         在Qt5之前的版本中并没有特定的串口控制类,现在大部分人使用的是第三方写的qextserialport类,本文章主要是讲解怎样利用此类实现串口通信。

    2、文件下载地址:

    最新文件下载在文章最后!

         http://sourceforge.net/projects/qextserialport/files/

    3、文件内容:

        3.1.下载到的文件为qextserialport-1.2win-alpha ,解压并打开后其内容如下。

       

    (1)doc文件夹中的文件内容是QextSerialPort类和QextBaseType的简单的说明,我们可以使用记事本程序将它们打开。

    (2)examples文件夹中是几个例子程序,可以看一下它的源码,不过想运行它们好像会出很多问题啊。

    (3)html文件夹中是QextSerialPort类的使用文档。

    (4)然后就是剩下的几个文件了。其中qextserialenumerator.cpp及qextserialenumerator.h文件中定义的QextSerialEnumerator类是用来获取平台上可用的串口信息的。不过,这个类好像并不怎么好用,而且它不是我们关注的重点,所以下面就不再介绍它了。

     (5)qextserialbase.cpp和qextserialbase.h文件定义了一个QextSerialBase类,win_qextserialport.cpp和win_qextserialport.h文件定义了一个Win_QextSerialPort类,posix_qextserialport.cpp和posix_qextserialport.h文件定义了一个Posix_QextSerialPort类,qextserialport.cpp和qextserialport.h文件定义了一个QextSerialPort类。这个QextSerialPort类就是我们上面所说的那个,它是所有这些类的子类,是最高的抽象,它屏蔽了平台特征,使得在任何平台上都可以使用它。

                                                                  

    在Windows下是:

    qextserialbase.cpp和qextserialbase.h 以及win_qextserialport.cpp和win_qextserialport.h

    在Linux下是:

    qextserialbase.cpp和qextserialbase.h 以及posix_qextserialport.cpp和posix_qextserialport.h

    而在Windows下我们可以使用事件驱动EventDriven方式,也可以使用查询Polling方式,但是在Linux下我们只能使用查询Polling方式。

    4、串口通信的实现

       4.1  声明串口对象 :

    Win_QextSerialPort  *myCom;      //Windows系统内
    Posix_QextSerialPort *myCom;      //Linux系统内

      4.2 串口定义:

    //Windows中有两种查询模式,一种polling模式,一种EventDriven模式
    myCom = new Win_QextSerialPort("COM1",QextSerialBase::Polling);                 //
    myCom = new Win_QextSerialPort("COM1",QextSerialBase::EventDriven);
    //Linux中只有Polling模式
    myCom = new Posix_QextSerialPort("/dev/ttyS0",QextSerialBase::Polling);

        事件驱动方式EventDriven就是使用事件处理串口的读取,一旦有数据到来,就会发出readyRead()信号,我们可以关联该信号来读取串口的数据。在事件驱动的方式下,串口的读写是异步的,调用读写函数会立即返回,它们不会冻结调用线程。

        查询方式Polling则不同,读写函数是同步执行的,信号不能工作在这种模式下,而且有些功能也无法实现。但是这种模式下的开销较小。我们需要自己建立定时器来读取串口的数据。

        在Windows下支持以上两种模式,而在Linux下只支持Polling模式。

      4.3 串口打开模式

    myCom ->open(QIODevice::ReadWrite);    //打开模式
    QIODevice::Unbuffered 0x0020 描述
    QIODevice::NotOpen 0x0000  
    QIODevice::ReadOnly 0x0001  
    QIODevice::WriteOnly 0x0002  
    QIODevice::ReadWrite ReadOnly | WriteOnly  
    QIODevice::Append 0x0004  
    QIODevice::Truncate 0x0008  
    QIODevice::Text 0x0010  

     

    4.4 串口的配置函数

    复制代码
    myCom->setBaudRate(BAUD9600);          //波特率设置,我们设置为9600
    myCom->setDataBits(DATA_8);            //数据位设置,我们设置为8位数据位
    myCom->setParity(PAR_NONE);           //奇偶校验设置,我们设置为无校验
    myCom->setStopBits(STOP_1);            //停止位设置,我们设置为1位停止位
    myCom->setFlowControl(FLOW_OFF);      //控制流
    
    myCom->setTimeout(long);              //设置时间间隔
    复制代码

        setTimeout(long)参数决定了Polling查询模式的读取串口的速度。

    4.5 串口工作

    connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom()));    //EventDriven模式下才能触发readyRead()信号
    connect(readTimer,SIGNAL(timeout()),this,SLOT(readMyCom()));  //Polling模式定时器触发timeout()信

    4.6 串口读取数据

    QByteArray temp = myCom->readAll();    //返回读取的字节
    int byteLen = myCom->bytesAvailable(); //返回串口缓冲区字节数

    4.7 串口写数据

    myCom -> Write(const char * data, qint64 maxSize );          //
    myCom -> Write(const char * data );                          //
    myCom -> Write(const QByteArray & byteArray);                //
    int byteLen = myCom->bytesToWrite();                        //输出写数据的字节数
    //bytesWritten()信号函数来获取已经发送的数据的大小。

    5、Qt5以后版本QSerialPort介绍

    5.1  .pro文件内需要加上如下代码

    QT += serialport               //加在第一行或者第二行

    5.2 网上一段利用QSerialPort写的串口通信代码(转自:http://www.doc88.com/p-645858545768.html

    复制代码
    #include "dialog.h"
    #include "ui_dialog.h"
    
    Dialog::Dialog(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::Dialog)
    {
        ui->setupUi(this);
        read_port = new mythread(this);
        connect(this->read_port, SIGNAL(read_port_data()), this,  SLOT(display_data()));
    }
    
    Dialog::~Dialog()
    {
        //delete this->my_serialport;
        delete ui;
    }
    
    void Dialog::on_pushButton_clicked()                   //open serial,打开串口
    {
        this->read_port->start();
    }
    
    void Dialog::display_data()                            //显示数据
    {
        ui->textBrowser->setText(this->read_port->requestData);
    }
    复制代码
    复制代码
    //对话框头文件
    #ifndef DIALOG_H
    #define DIALOG_H
    
    #include "mythread.h"
    
    namespace Ui 
    {
        class Dialog;
    }
    
    class Dialog : public QDialog
    {
        Q_OBJECT
    public:
        explicit Dialog(QWidget *parent = 0);
        ~Dialog();
    private slots:
        void on_pushButton_clicked();
        void display_data();
    private:
        Ui::Dialog *ui;
        mythread *read_port;
    };
    
    #endif // DIALOG_H
    复制代码
    复制代码
    #include "mythread.h"
    
    mythread::mythread(QObject *parent) :
        QThread(parent)
    {
        my_serialport = new QSerialPort(this);
    }
    
    void mythread::run()
    {
        this->my_serialport->setPortName("com3");
        this->my_serialport->open(QIODevice::ReadWrite);
        this->my_serialport->setBaudRate(115200);
        this->my_serialport->setDataBits(QSerialPort::Data8);
        this->my_serialport->setParity(QSerialPort::NoParity);
        this->my_serialport->setStopBits(QSerialPort::OneStop);
        this->my_serialport->setFlowControl(QSerialPort::NoFlowControl);
    
        while(1)
        {
            requestData = "";
            while(this->my_serialport->waitForReadyRead(10))
            {
                requestData += this->my_serialport->readAll();
            }
    
            if(!requestData.isEmpty())
            {
                emit(this->read_port_data());
                qDebug() << "===============" <<requestData;
            }
        }
    }
    复制代码

     6、如何抓取串口数据

    6.1 、使用超级终端

        Windows XP之前(包括Windows XP)版本系统自带,程序位置:

                               开始 ->程序->附件->通讯 ->超级终端。

    6.2、使用串口调试助手

          串口调试助手软件非常多:例如CommAssistant.exe、

    3、使用Bus Hound工具(推荐使用)

        下载地址:http://www.xz7.com/dir/208/248/200604121464.html

    6.4 虚拟串口调试工具

         常用的两款虚拟串口调试工具:

         1、VSPD下载地址: http://www.cr173.com/soft/21406.html

         2、VSPM下载地址:http://www.onlinedown.net/soft/58180.htm

         通过虚拟软件,进行虚拟设置后,并以串口调试助手工具配合工作。这样就可以在没有物理串口的条件下进行调试测试等。

    转载于:https://www.cnblogs.com/h2zZhou/p/9522332.html

    展开全文
  • Qt实现串口通信(利用windows API实现串口枚举,多线程、生产者消费者模型实现数据解析); 自定义事件,向UI界面提供数据交互接口; 自定义队列,支持线程安全。
  • QT实现串口通信(附源码下载)

    万次阅读 2016-10-23 11:45:23
    1 Qextserialport类介绍  在Qt5之前的版本中并没有特定的串口控制类,... 注意:Qt5发布之前,Qt实现串口通信一般是采用第三方类库qextserialport。Qt5发布后自带了QtSerialPort 能够支持串口通信。 2 文件下载地址

    1 Qextserialport类介绍
         在Qt5之前的版本中并没有特定的串口控制类,现在大部分人使用的是第三方写的qextserialport类,本文章主要是讲解怎样利用此类实现串口通信。

         注意:Qt5发布之前,Qt实现串口通信一般是采用第三方类库qextserialport。Qt5发布后自带了QtSerialPort 能够支持串口通信。

    2  文件下载地址

    http://blog.csdn.net/wangzhen209/article/details/52605514 

    文件内容:

    http://blog.csdn.net/wangzhen209/article/details/52605514 

            (实在懒得贴图了,哈哈)

    4 串口通信的实现

    4.1  声明串口对象 :

    Win_QextSerialPort  *myCom;      //Windows系统内
    Posix_QextSerialPort *myCom;      //Linux系统内


    4.2 串口定义:


    //Windows中有两种查询模式,一种polling模式,一种EventDriven模式
    myCom = new Win_QextSerialPort("COM1",QextSerialBase::Polling);                 //设置为查询模式
    myCom = new Win_QextSerialPort("COM1",QextSerialBase::EventDriven);      //设置为事件模式
    //Linux中只有Polling模式
    myCom = new Posix_QextSerialPort("/dev/ttyS0",QextSerialBase::Polling);


        事件驱动方式EventDriven就是使用事件处理串口的读取,一旦有数据到来,就会发出readyRead()信号,我们可以关联该信号来读取串口的数据。在事件驱动的方式下,串口的读写是异步的,调用读写函数会立即返回,它们不会冻结调用线程。
        查询方式Polling则不同,读写函数是同步执行的,信号不能工作在这种模式下,而且有些功能也无法实现。但是这种模式下的开销较小。我们需要自己建立定时器来读取串口的数据。
        在Windows下支持以上两种模式,而在Linux下只支持Polling模式。

     4.3 串口打开模式

    myCom ->open(QIODevice::ReadWrite);    //打开模式  表示以可读可写的方式打开串口
    QIODevice::Unbuffered    0x0020 描述
    QIODevice::NotOpen       0x0000  
    QIODevice::ReadOnly      0x0001  
    QIODevice::WriteOnly     0x0002  
    QIODevice::ReadWrite     ReadOnly | WriteOnly  
    QIODevice::Append        0x0004  
    QIODevice::Truncate      0x0008  
    QIODevice::Text          0x0010


    4.4 串口的配置函数

    myCom->setBaudRate(BAUD9600);          //波特率设置,我们设置为9600
    myCom->setDataBits(DATA_8);            //数据位设置,我们设置为8位数据位
    myCom->setParity(PAR_NONE);           //奇偶校验设置,我们设置为无校验
    myCom->setStopBits(STOP_1);            //停止位设置,我们设置为1位停止位
    myCom->setFlowControl(FLOW_OFF);      //控制流
    myCom->setTimeout(long);              //设置时间间隔

    setTimeout(long)参数决定了Polling查询模式的读取串口的速度。


    4.5 串口工作

    connect(myCom,SIGNAL(readyRead()),this,SLOT(readMyCom()));    //EventDriven模式下才能触发readyRead()信号
    connect(readTimer,SIGNAL(timeout()),this,SLOT(readMyCom()));  //Polling模式定时器触发timeout()信号
    4.6 串口读取数据
    QByteArray temp = myCom->readAll();    //返回读取的字节
    int byteLen = myCom->bytesAvailable(); //返回串口缓冲区字节数


    4.7 串口写数据
    myCom -> Write(const char * data, qint64 maxSize );          //
    myCom -> Write(const char * data );                          //
    myCom -> Write(const QByteArray & byteArray);                //
    int byteLen = myCom->bytesToWrite();                        //输出写数据的字节数
    //bytesWritten()信号函数来获取已经发送的数据的大小。

    5 实际编程

    界面如下


      可以看到,我在界面上放了两LlinEedit,用来显示收发的数据,几个PushButton,用来发送数据,清空收发送区,两个Check Box,用来选择是否是HEX发送或HEX接收


    MainWindow.h

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
        
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
        static QByteArray QString2Hex(QString str); //转换为16进制函数
        static char ConvertHexChar(char ch);
    private slots:
        void ReadMycom();  //读取串口中的数据
        void on_SendButton_clicked();
    
        void on_Close_clicked();
    
        void on_ClearReceive_clicked();
    
        void on_ClearSend_clicked();
    
    private:
        Ui::MainWindow *ui;
        Win_QextSerialPort *myCom;
        QTimer *timer;  //用于声明定时器
    };
    
      私有地几个槽函数我相信看函数的名字也能猜到他们实现什么功能,因此就不赘述。

    MainWindow.cpp

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        timer=new QTimer(this);
        myCom = new Win_QextSerialPort("COM5",QextSerialBase::Polling); //设置串口的查询模式
    
        myCom->open(QIODevice::ReadWrite);  //以可读可写的方式打开串口
        if(!myCom->isOpen())
        {
            qDebug("no com open\n");
        }
        //串口参数设置
        myCom->setBaudRate(BAUD115200);
        myCom->setDataBits(DATA_8);
        myCom->setParity(PAR_NONE);
        myCom->setStopBits(STOP_1);
        myCom->setFlowControl(FLOW_OFF);
        //this->ui->SendNumber->setDecMode();
        //设置时间间隔
        myCom->setTimeout(100);
        timer->start(300);
    
    
        QObject::connect(timer,SIGNAL(timeout()),
                         this,SLOT(ReadMycom()));
    
        ui->setupUi(this);
    }
      我们的程序是以查询的方式去读取串口中的数据,首先设置定时器,当定时器时间到时,去执行槽函数ReadMycom()函数,该函数的作用是读取串口中的数据。



    /*
    函数名:
    函数功能:读取串口中的数据
    函数类型:槽函数
    函数返回值:无
    */
    void MainWindow::ReadMycom()
    {
        int byteLen = myCom->bytesAvailable(); //返回串口缓冲区字节
        if(byteLen<=0) return;  //减小内存占用
        qDebug("byteLen=%d\n",byteLen);
        //串口内有数据才往下继续执行
        QByteArray temp = myCom->readAll();    //返回读取的字节
        this->ui->textEdit->clear();
        if(this->ui->HexShow->isChecked())  //如果是16进制显示
        {
           this->ui->textEdit->setText(temp.toHex());
            //temp.toHex();
        }
        else
        {
           this->ui->textEdit->setText(temp);
        }
        this->ui->ReceiveNumber->display(byteLen);
    }

      注意:增加了一个判断

        int byteLen = myCom->bytesAvailable(); //返回串口缓冲区字节

        if(byteLen<=0) return;  //减小内存占用

       如果不这样的话,每次定时读取都会延时,内存占用很高,当然,如果采用事件机制的话,这里不需要任何延时或者判断。


    /*
    函数名:
    函数功能:发送数据
    函数类型:槽函数
    返回值:无
    */
    void MainWindow::on_SendButton_clicked()
    {
        QString input=this->ui->SDendData->text();
        QByteArray hexByte;
        if(input.isEmpty())
        {
            qDebug("no message\n");
        }
        else
        {
            if(this->ui->HexSend->isChecked())  //选中16进制发送数据
            {
                hexByte=QString2Hex(input);
                myCom->write(hexByte);
            }
            else
            {
                myCom->write(input.toAscii());
            }
        }
        this->ui->SendNumber->display(input.size());
    
    
    }
    
    void MainWindow::on_Close_clicked()
    {
        close();
    }
    /*
    在测试时发现,当底层硬件发送数据的间隔时间太短时容易在成串口读写数据错误,因为
    本程序是使用定时器的方法去读取串口数据
    */
    
    void MainWindow::on_ClearReceive_clicked()
    {
        this->ui->textEdit->clear();
    }
    
    void MainWindow::on_ClearSend_clicked()
    {
        this->ui->SDendData->clear();
    }
    /*
    函数名:
    函数功能:将QString类型转换为hex形式
    函数类型:
    函数参数:
    返回值:
    */
    QByteArray MainWindow::QString2Hex(QString str)
    {
        QByteArray senddata;
        qDebug("QString2Hex\n");
                int hexdata,lowhexdata;
                int hexdatalen = 0;
                int len = str.length();
                senddata.resize(len/2);
                char lstr,hstr;
                for(int i=0; i<len; )
                {
                    hstr=str[i].toAscii();
                    if(hstr == ' ')
                    {
                        i++;
                        continue;
                    }
                    i++;
                    if(i >= len)
                        break;
                    lstr = str[i].toAscii();
                    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);
                return senddata;
    }
    /*
    函数功能:将单个字符串转换为hex
    */
    char MainWindow::ConvertHexChar(char ch)
    {
        qDebug("ConvertHexChar\n");
        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 (-1);
    }
    

      与其他程序相比,本程序实现了HEX发送,当在界面选择HEX发送时,程序就会执行到


      通过调用QString2Hex()函数转为16进制,采用16进制发送的原因是很多单片机只吃16进制数据。



    源代码下载地址:

    http://download.csdn.net/detail/qq_27312943/9661418


    参考资料:

    1 http://blog.csdn.net/wangzhen209/article/details/52605514 本文主要是参考这一篇文章,但是原文章中没有给出全部的代码以及实现16进制的发送方式,本文进行了增添。

    2 http://blog.csdn.net/huwei2003/article/details/36418471 介绍了有关串口通信的基本知识,对于串口通信不明白的可以参考这篇文章

    3 http://www.cnblogs.com/feiyangqingyun/p/3483764.html 


    注意事项:

    1 在程序中没有采用QT5的QtSerialPort 类主要是我们在一次实际开发当中,调用这个类的一个readall()函数时,明明串口中有数据,但是无法读出来,最后采用了第三方库qextserialport得到了解决。


    展开全文
  • Qt5实现串口通信

    万次阅读 2016-10-18 15:01:34
    qt实现串口通信示例 1.串口通信简介 串口通信是上下位机进行通信的一种常用的通信协议,大部分单片机中都有一到多个串口资源经过简单的配置就可以实现上下位机的通信,下图是串口通信协议中的一中形式。如果你不是...

    qt实现串口通信示例


    1.串口通信简介

    串口通信是上下位机进行通信的一种常用的通信协议,大部分单片机中都有一到多个串口资源经过简单的配置就可以实现上下位机的通信,下图是串口通信协议中的一种形式。如果你不是用硬件描述语言去实现一个串口,这部分了解下即可。常用的是8位数据位加起始位加停止位,因此串口是只能发送0-255区间的数据。因此想要发送int或者float型的数据需要按高地位或者到内存中取出数据来发送,此时就需要通信协议来确保数据的准确性,接下来将依次进行介绍。

    2.Qt中的串口通信

    qt中集成了QSerialport类,可以直接使用该类实现串口通信。在开始使用这部分的时候也遇到不少困难,现在一步步说起如何通过QT实现通过串口资源进行通信。在建工程的时候记得勾选QSerialport选项,如果建工程的时候没选可以按图中的方式再设置或者通过添加附加依赖项添加也可以。


    首先在头文件中实例化串口。

    #include <QtSerialPort/QSerialPort>
    #include <QtSerialPort/QSerialPortInfo>
    
    QSerialPort serialPort;
    

    然后需要查看系统上的串口资源,然后将串口资源显示在界面上。

    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
    	qDebug() << "Name        : " << info.portName();
    	qDebug() << "Description : " << info.description();
    	qDebug() << "Manufacturer: " << info.manufacturer();
    	//将串口资源显示在界面
    	if (ui.comboBox->currentText() != info.portName())
    		ui.comboBox->addItem(info.portName());
    }
    
    找到串口资源后需要对其一些属性进行配置,包括波特率等等,通过槽函数实现。

    void uart::ComOpen()
    {
    	ui.pushButton->setText("Open");
    	serialPort.setPortName(ui.comboBox->currentText());//选取串口
    	serialPort.open(QIODevice::ReadWrite);             //打开串口
    	serialPort.setBaudRate(ui.BaudRate->currentText().toInt());
    	//qDebug() << ui.BaudRate->currentText().toInt() << endl;
    	serialPort.setDataBits(QSerialPort::Data8);
    	serialPort.setParity(QSerialPort::NoParity);
    	serialPort.setStopBits(QSerialPort::OneStop);
    	serialPort.setFlowControl(QSerialPort::NoFlowControl);
    	timer.start(5000);//500ms定时器
    }
    

    这样串口部分已经可以正常工作了,串口发来数据怎么检测呢?QSerialport中提供了readRead()信号,当有数据发来的时候就调用ReadData函数。这样就可以实现串口数据的读取了。需要对数据进行解码等操作都可以在该函数中进行,稍后会稍微说下简单的和校验。

    QObject::connect(&serialPort, SIGNAL(readyRead()), this, SLOT(ReadData()));//发来数据就读取
    
    void uart::ReadData()
    {
    	ui.buffSize->setText(QString::number(serialPort.bytesAvailable()));
    	int buffersize = ui.bufferSize->value();
    	if (serialPort.bytesAvailable()>buffersize){   //更改过滤个数,提高通信速率
    	//requestData = serialPort.readAll().toHex();//转成 hex
    	requestData = serialPort.readAll();//字符型
    	ui.textEdit->append(requestData);
    	}
    	if (!timer.isActive())
    		timer.start(5000);//500ms定时器
    }
    

    当然还需要对数据的发送指令,这部分很简单,直接上代码。

    void uart::dataSend()
    {
    		QString data = ui.lineEdit->text();
    		QByteArray DATA;
    		DATA = data.toLatin1();
    		serialPort.write(DATA);
    }
    

    这样实现了串口的收发。为了测试下程序找来一根串口线将2,3号口短接,发送数据效果如图。


    3.和校验

    在数据发送的过程中由于其他信号的干扰可能会出现数据的丢失等问题,可能这些数据作为下位机的输入会产生破坏性的影响,因此在数据通信的过程中需要加入通信协议保证数据的准确性,这里稍微介绍一种简单的校验方法即和校验。

    和校验即通过数据的累加和去验证数据的正确与否。上图是一帧需要发送的数据可以看从左到右分别为帧头,功能字,数据帧与和校验,还可以加上数据位数。帧头和功能字可以根据自己的需求进行定制,数据位即需要发送的数据,最后的SUM即为无符号char型数据,将SUM之前的数据求和(不用管数据的溢出)即可。下位机按这种方式将数据发送上来上位机只要找到帧头即可进行解码并且对数据进行验证,这里附一个简单的上位机解码的程序,之前用来解码下位机发送来的欧拉角数据使用的程序。这里我发送的数据为0x55---YawH+SUM

    void uart::ReadData()
    {
    	ui.buffSize->setText(QString::number(serialPort.bytesAvailable()));
    	int buffersize = ui.bufferSize->value();
    	if (serialPort.bytesAvailable()>buffersize){   //更改过滤个数,提高通信速率
    	requestData = serialPort.readAll().toHex();//转成 hex
                }
    	if (!requestData.isEmpty() )
    	{
    		QByteArray temp = requestData.mid(QString(requestData).indexOf("55"), 22);
    		unsigned char buffer[11] = { 0 };
    		unsigned char sum = 0;
    		for (int i = 0; i < 11; i++)
    		{
    			buffer[i] = (ByteArrayToHexchar(temp.at((i << 1) )) << 4 )+ ByteArrayToHexchar(temp.at((i << 1) + 1 ));
    			if (i<10)
    				sum = sum + buffer[i];
    		}
    		//sum = buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[5] + buffer[6];
    		if ((buffer[0] == 0x55) && (buffer[1] == 0x53))
    		if (sum == buffer[10])
    		{
    			float x, y, z;
    			x = (buffer[3] << 8 | buffer[2]);
    			y = (buffer[5] << 8 | buffer[4]);
    			z = (buffer[7] << 8 | buffer[6]);
    			x = x / 32768 * 180;
    			y = y / 32768 * 180;
    			z = z / 32768 * 180;
    			ui.x->setText(QString::number(x, 'f', 2));
    			ui.y->setText(QString::number(y, 'f', 2));
    			ui.z->setText(QString::number(z, 'f', 2));
    			//ui.textEdit->append(QString::number(z, 'f', 2));
    			
    			requestData.clear();
    		}
    	}
    	
    	if (!timer.isActive())
    		timer.start(5000);//500ms定时器
    }
    
    在数据转换的时候遇到一个问题即将字节数据转换成十六进制,没有找到合适的函数只能通过笨法实现,要是有大神知道还请指点一二。

    unsigned char uart::ByteArrayToHexchar(unsigned 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 (-1);
    }

    4.附代码

    uart.h
    #ifndef UART_H
    #define UART_H
    #include <QDebug>
    #include <QtWidgets/QMainWindow>
    #include "ui_uart.h"
    #include <QtSerialPort/QSerialPort>
    #include <QtSerialPort/QSerialPortInfo>
    
    #include <QTimer>
    //#include <windows.h> 
    class uart : public QMainWindow
    {
    	Q_OBJECT
    
    public:
    	uart(QWidget *parent = 0);
    	~uart();
        
    private:
    	QSerialPort serialPort;
    	QTimer timer;
    	QByteArray requestData;
    	Ui::uartClass ui;
    
    private slots:
        void ReadData();
    	void ComOpen();
    	void dataSend();
    	void Exit();
    	void timeout();
    	unsigned char ByteArrayToHexchar(unsigned char ch);
    };
    
    #endif // UART_H
    

    uart.cpp

    #include "uart.h"
    
    
    uart::uart(QWidget *parent)
    	: QMainWindow(parent)
    {
    	ui.setupUi(this);
    	//serialPort = 0;
    	//寻找可用串口
    	foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    	{
    		qDebug() << "Name        : " << info.portName();
    		qDebug() << "Description : " << info.description();
    		qDebug() << "Manufacturer: " << info.manufacturer();
    		if (ui.comboBox->currentText() != info.portName())
    			ui.comboBox->addItem(info.portName());
    		
    	}
    	QObject::connect(&serialPort, SIGNAL(readyRead()), this, SLOT(ReadData()));//发来数据就读取
    }
    
    uart::~uart()
    {
    
    }
    void uart::timeout()
    {
    	serialPort.clear();
    }
    
    void uart::ComOpen()
    {
    	ui.pushButton->setText("Open");
    	serialPort.setPortName(ui.comboBox->currentText());//选取串口
    	serialPort.open(QIODevice::ReadWrite);             //打开串口
    	serialPort.setBaudRate(ui.BaudRate->currentText().toInt());
    	//qDebug() << ui.BaudRate->currentText().toInt() << endl;
    	serialPort.setDataBits(QSerialPort::Data8);
    	serialPort.setParity(QSerialPort::NoParity);
    	serialPort.setStopBits(QSerialPort::OneStop);
    	serialPort.setFlowControl(QSerialPort::NoFlowControl);
    	timer.start(5000);//500ms定时器
    }
    unsigned char uart::ByteArrayToHexchar(unsigned 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 (-1);
    }
    
    void uart::ReadData()
    {
    	ui.buffSize->setText(QString::number(serialPort.bytesAvailable()));
    	int buffersize = ui.bufferSize->value();
    	if (serialPort.bytesAvailable()>buffersize){   //数据帧个数
    	
    	
    	requestData = serialPort.readAll();          //字符型   
    	ui.textEdit->append(requestData);
    	}
    
    	
    	if (!timer.isActive())
    		timer.start(5000);//500ms定时器
    }
    void uart::dataSend()
    {
    	  
    		QString data = ui.lineEdit->text();
    		QByteArray DATA;
    		DATA = data.toLatin1();
    		serialPort.write(DATA);
    		ReadData();
    	
    }
    
    void uart::Exit()
    {
    	serialPort.close();
    	close();
    }


    最后附完成工程文件下载,点击下载





    展开全文
  • QT编写串口调试助手实现串口通信qt项目 qt项目 一、.pro文件添加串口 添加模块: QT += serialport 二、mainwindow.h 添加头文件: #include <QtSerialPort/QSerialPort> #include <QtSerialPort/...
  • Qt 串口通信

    2021-03-17 22:16:28
    Qt实现串口通信(C++实现串口通信小助手)—串口收发及串口数据解码、串口通信模拟器。》 如果你需要代码的话,直接拷贝复制到你的项目就可以用了,除了Qt的库以外没有用任何第三方库,所以应该很容易移植的。 H...
  • 本文所将内容是在Qt实现串口通信,文字描述不多,基本上是代码实现。之前本人做过类似的小程序,不过那个是基于线程的。那么接下来看看本篇文字吧!首先,加入了“打开串口”,“关闭串口”“传送数据”三个按钮,...
  • QT5串口通信(Hex格式传输)

    千次阅读 2018-10-25 10:38:29
    CSDN-宗师之路:QT实现串口通信(附源码下载) CSDN-xz-c:C++将10进制字符串转为16进制字符串 QT实现串口通信 在工程文件(.pro)中添加串口通信相关运行库:QT += serialport 在头文件中添加#include &amp;lt;...
  • 串口通信Qt实现

    2019-04-10 21:46:37
    串口通信Qt实现,基于CSerialPort 类实现串口的通信连接
  • Qt5串口通信

    2021-02-04 18:30:33
    简单的串口通信实现了基本的功能:收发16进制,自动发送,清除接收框等 另附有串口助手与虚拟串口软件 简单的串口通信实现了基本的功能:收发16进制,自动发送,清除接收框等 另附有串口助手与虚拟串口软件
  • 原文地址::... 相关文章 1、QT实现串口通信(附源码下载)----http://blog.csdn.net/qq_27312943/article/details/52900527 2、QT5串口通信——基础篇----http://blog.csdn.net/k33192
  • QT5串口通信程序源码

    2020-04-25 14:21:29
    QT5串口通信编程源码,实现了包括基本发送接受以及循环发送功能,简单易看懂,欢迎大家下载学习 欢迎大家下载学习 欢迎大家下载学习

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 415
精华内容 166
关键字:

qt实现串口通信