当前位置:   article > 正文

Qt实现串口通信(C++实现串口通信小助手)---串口收发及串口数据解码、串口通信模拟器。_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

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

  1. #include <QtSerialPort/QSerialPort>
  2. #include <QtSerialPort/QSerialPortInfo>

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

    QSerialPort *serialPort;

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

  1. void Serial_port::InitPort()
  2. {
  3. const auto infos = QSerialPortInfo::availablePorts();
  4. for(const QSerialPortInfo &info : infos)
  5. {
  6. QSerialPort serial;
  7. serial.setPort(info);
  8. if(serial.open(QIODevice::ReadWrite))
  9. {
  10. ui->PortBox->addItem(info.portName());
  11. serial.close();
  12. }
  13. }
  14. QStringList baudList; //波特率
  15. QStringList parityList; //校验位
  16. QStringList dataBitsList; //数据位
  17. QStringList stopBitsList; //停止位
  18. // 波特率 //波特率默认选择下拉第三项:9600
  19. baudList<<"1200"<<"2400"<<"4800"<<"9600"
  20. <<"14400"<<"19200"<<"38400"<<"56000"
  21. <<"57600"<<"115200";
  22. ui->BaudBox->addItems(baudList);
  23. ui->BaudBox->setCurrentIndex(3);
  24. // 校验 //校验默认选择无
  25. parityList<<"无"<<"奇"<<"偶";
  26. ui->ParityBox->addItems(parityList);
  27. ui->ParityBox->setCurrentIndex(0);
  28. // 数据位 //数据位默认选择8位
  29. dataBitsList<<"5"<<"6"<<"7"<<"8";
  30. ui->DataBox->addItems(dataBitsList);
  31. ui->DataBox->setCurrentIndex(3);
  32. // 停止位 //停止位默认选择1位
  33. stopBitsList<<"1"<<"2";
  34. ui->StopBox->addItems(stopBitsList);
  35. ui->StopBox->setCurrentIndex(0);
  36. }

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

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

  1. void Serial_port::readData()
  2. {
  3. QByteArray buf;
  4. if (serialPort){
  5. buf = serialPort->readAll();
  6. if (!buf.isEmpty())
  7. {
  8. receBytes += buf.size();
  9. QString redata = QString("received:%1").arg(QString::number(receBytes));
  10. ui->sendlabel->setText(redata);
  11. QString myStrTemp = QString::fromLocal8Bit(buf); //支持中文显示
  12. if(ui->reDisplay->isChecked())
  13. {
  14. QString str = ui->textBrowser->toPlainText();
  15. str +=myStrTemp;
  16. ui->textBrowser->clear();
  17. ui->textBrowser->append(str);
  18. }
  19. }
  20. buf.clear();
  21. }
  22. }

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

    buf = serialPort->readAll();

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

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

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

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

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

  1. void Serial_port::on_sendButton_clicked()
  2. {
  3. //Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。ISO-8859-1编码是单字节编码,向下兼容ASCII
  4. //其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
  5. QString str = ui->lineEdit->text();
  6. if(!str.isEmpty())
  7. {
  8. auto isHexSend = ui->sHexRadio->isChecked();
  9. int len = str.length();
  10. if(len%2 == 1)
  11. {
  12. str = str.insert(len-1,'0');
  13. }
  14. QByteArray senddata;
  15. if(isHexSend)
  16. {
  17. StringToHex(str,senddata);
  18. serialPort->write(senddata);
  19. if(serialPort->write(senddata)<0)
  20. {
  21. QMessageBox::critical(this, tr("Error"), serialPort->errorString());
  22. }
  23. }
  24. else
  25. {
  26. if(serialPort->write(ui->lineEdit->text().toLocal8Bit())<0)
  27. {
  28. QMessageBox::critical(this, tr("Error"), serialPort->errorString());
  29. }
  30. }
  31. }
  32. }

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

  1. void Serial_port::StringToHex(QString str, QByteArray &senddata)
  2. {
  3. int hexdata,lowhexdata;
  4. int hexdatalen = 0;
  5. int len = str.length();
  6. senddata.resize(len/2);
  7. char lstr,hstr;
  8. for(int i=0; i<len; )
  9. {
  10. //char lstr,
  11. hstr=str[i].toLatin1();
  12. if(hstr == ' ')
  13. {
  14. i++;
  15. continue;
  16. }
  17. i++;
  18. if(i >= len)
  19. break;
  20. lstr = str[i].toLatin1();
  21. hexdata = convertHexChart(hstr);
  22. lowhexdata = convertHexChart(lstr);
  23. if((hexdata == 16) || (lowhexdata == 16))
  24. break;
  25. else
  26. hexdata = hexdata*16+lowhexdata;
  27. i++;
  28. senddata[hexdatalen] = (char)hexdata;
  29. hexdatalen++;
  30. }
  31. senddata.resize(hexdatalen);
  32. }
  1. char Serial_port::convertHexChart(char ch)
  2. {
  3. if((ch >= '0') && (ch <= '9'))
  4. return ch-0x30; // 0x30 对应 ‘0’
  5. else if((ch >= 'A') && (ch <= 'F'))
  6. return ch-'A'+10;
  7. else if((ch >= 'a') && (ch <= 'f'))
  8. return ch-'a'+10;
  9. // else return (-1);
  10. else return ch-ch;//不在0-f范围内的会发送成0
  11. }

四、解码

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

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

  1. void uart::ReadData()
  2. {
  3. ui.buffSize->setText(QString::number(serialPort.bytesAvailable()));
  4. int buffersize = ui.bufferSize->value();
  5. if (serialPort.bytesAvailable()>buffersize){ //更改过滤个数,提高通信速率
  6. requestData = serialPort.readAll().toHex();//转成 hex
  7. }
  8. if (!requestData.isEmpty() )
  9. {
  10. QByteArray temp = requestData.mid(QString(requestData).indexOf("55"), 22);
  11. unsigned char buffer[11] = { 0 };
  12. unsigned char sum = 0;
  13. for (int i = 0; i < 11; i++)
  14. {
  15. buffer[i] = (ByteArrayToHexchar(temp.at((i << 1) )) << 4 )+ ByteArrayToHexchar(temp.at((i << 1) + 1 ));
  16. if (i<10)
  17. sum = sum + buffer[i];
  18. }
  19. //sum = buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[5] + buffer[6];
  20. if ((buffer[0] == 0x55) && (buffer[1] == 0x53))
  21. if (sum == buffer[10])
  22. {
  23. float x, y, z;
  24. x = (buffer[3] << 8 | buffer[2]);
  25. y = (buffer[5] << 8 | buffer[4]);
  26. z = (buffer[7] << 8 | buffer[6]);
  27. x = x / 32768 * 180;
  28. y = y / 32768 * 180;
  29. z = z / 32768 * 180;
  30. ui.x->setText(QString::number(x, 'f', 2));
  31. ui.y->setText(QString::number(y, 'f', 2));
  32. ui.z->setText(QString::number(z, 'f', 2));
  33. //ui.textEdit->append(QString::number(z, 'f', 2));
  34. requestData.clear();
  35. }
  36. }
  37. if (!timer.isActive())
  38. timer.start(5000);//500ms定时器
  39. }

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

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

五、发报模拟器

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

  1. void Serial_port::handleLineEditClicked()
  2. {
  3. //QString curPath = QDir::currentPath();
  4. QString curPath = "../QSerial_port";
  5. //QFile file;
  6. QString aFileName = QFileDialog::getOpenFileName(this,QString("选择文件"),curPath,QString("TEXT(*.txt)"));
  7. ui->m_txt->setText(aFileName);
  8. }
  1. void Serial_port::on_Playbutton_clicked()
  2. {
  3. QString path = ui->m_txt->text();
  4. //qDebug()<<"是否成功获取"<<path_ce->text();
  5. if(path.isEmpty())
  6. return;
  7. QFile aFile(path);
  8. if(!aFile.exists()) //文件不存在
  9. qDebug() <<"the file not exist!!";
  10. //return;
  11. if(!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
  12. qDebug() <<"文件无法打开";
  13. //return;
  14. QTextStream aStream(&aFile); //用文件流读取文件
  15. while (!aStream.atEnd())
  16. {
  17. QString qstr = aStream.readLine(); //读取文件的一行文本
  18. //qDebug()<<qstr.length();
  19. int len = qstr.length();
  20. if(len%2 == 1)
  21. {
  22. qstr = qstr.insert(len-1,'0');
  23. }
  24. QByteArray senddata;
  25. StringToHex(qstr,senddata);
  26. //qDebug()<<senddata.size();
  27. for(int i=0;i<senddata.size();i++)
  28. {
  29. QByteArray tmpsenddata=senddata.mid(i,40);
  30. i=i+39;
  31. //qDebug()<<tmpsenddata.length();
  32. serialPort->write(tmpsenddata);
  33. if(!serialPort->waitForBytesWritten()) //这一句很关键,决定是否能发送成功
  34. {
  35. qDebug()<<"serial write error";
  36. }
  37. Sleep(1000);
  38. }
  39. }
  40. }

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

            serialPort->write(tmpsenddata);

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

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

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

Serial_port.h

  1. #ifndef SERIAL_PORT_H
  2. #define SERIAL_PORT_H
  3. #include <QMainWindow>
  4. #include <QtSerialPort/QSerialPort>
  5. #include <QtSerialPort/QSerialPortInfo>
  6. #include <QLabel>
  7. #include <QTimer>
  8. #include <windows.h>
  9. #include <QString>
  10. #include <QDebug>
  11. #include <dbt.h>
  12. #include <devguid.h>
  13. #include <setupapi.h>
  14. #include <initguid.h>
  15. #include <list>
  16. using namespace std;
  17. namespace Ui {
  18. class Serial_port;
  19. }
  20. class Serial_port : public QMainWindow
  21. {
  22. Q_OBJECT
  23. public:
  24. explicit Serial_port(QWidget *parent = nullptr);
  25. void InitPort();
  26. ~Serial_port();
  27. private slots:
  28. void readData(); //读取数据
  29. void timeToSend(); //定时发送
  30. void on_OpenButton_clicked();
  31. void on_r_clearButton_clicked();
  32. void on_s_clearButton_clicked();
  33. void on_sendButton_clicked();
  34. void StringToHex(QString str,QByteArray &senddata); //发送时进制转换
  35. char convertHexChart(char ch); //16进制转换
  36. void readToHex(); //将读取的数据以16进制显示
  37. void Mdisplay();
  38. //void pasingData(QByteArray &array); //解析串口数据
  39. void handleLineEditClicked();
  40. void on_Playbutton_clicked();
  41. private:
  42. Ui::Serial_port *ui;
  43. QSerialPort *serialPort;
  44. QTimer *time;
  45. static int sendBytes;
  46. static int receBytes;
  47. };
  48. #endif // SERIAL_PORT_H

Serial_port.cpp

  1. #include "Serial_port.h"
  2. #include "ui_Serial_port.h"
  3. #include <QMessageBox>
  4. #include "m_lineEdit.h"
  5. #include <QDebug>
  6. #include <QFileDialog>
  7. Serial_port::Serial_port(QWidget *parent) :
  8. QMainWindow(parent),
  9. ui(new Ui::Serial_port)
  10. {
  11. ui->setupUi(this);
  12. time = new QTimer(this);
  13. InitPort();
  14. //设置默认值
  15. ui->PortBox->setCurrentIndex(1);
  16. ui->BaudBox->setCurrentIndex(7);
  17. ui->StopBox->setCurrentIndex(0);
  18. ui->DataBox->setCurrentIndex(3);
  19. ui->ParityBox->setCurrentIndex(0);
  20. ui->sendButton->setEnabled(false);
  21. ui->sHexRadio->setEnabled(false);
  22. ui->sTextRadio->setChecked(true);
  23. ui->sTextRadio->setEnabled(false);
  24. ui->rTextRadio->setChecked(true);
  25. ui->rHexRadio->setChecked(false);
  26. ui->reDisplay->setChecked(true);
  27. connect(ui->reSendCheck, &QCheckBox::stateChanged,
  28. this, &Serial_port::timeToSend); //自动重发
  29. connect(time, &QTimer::timeout, this, &Serial_port::on_sendButton_clicked);//
  30. connect(ui->rHexRadio, &QRadioButton::toggled, this, &Serial_port::Mdisplay);
  31. connect(ui->m_txt, SIGNAL(clicked()), this, SLOT(handleLineEditClicked()));
  32. }
  33. int Serial_port::sendBytes = 0;
  34. int Serial_port::receBytes = 0;
  35. void Serial_port::InitPort()
  36. {
  37. const auto infos = QSerialPortInfo::availablePorts();
  38. for(const QSerialPortInfo &info : infos)
  39. {
  40. QSerialPort serial;
  41. serial.setPort(info);
  42. if(serial.open(QIODevice::ReadWrite))
  43. {
  44. ui->PortBox->addItem(info.portName());
  45. //qDebug()<<info.portName();
  46. serial.close();
  47. }
  48. }
  49. QStringList baudList; //波特率
  50. QStringList parityList; //校验位
  51. QStringList dataBitsList; //数据位
  52. QStringList stopBitsList; //停止位
  53. // 波特率 //波特率默认选择下拉第三项:9600
  54. baudList<<"1200"<<"2400"<<"4800"<<"9600"
  55. <<"14400"<<"19200"<<"38400"<<"56000"
  56. <<"57600"<<"115200";
  57. ui->BaudBox->addItems(baudList);
  58. ui->BaudBox->setCurrentIndex(3);
  59. // 校验 //校验默认选择无
  60. parityList<<"无"<<"奇"<<"偶";
  61. ui->ParityBox->addItems(parityList);
  62. ui->ParityBox->setCurrentIndex(0);
  63. // 数据位 //数据位默认选择8位
  64. dataBitsList<<"5"<<"6"<<"7"<<"8";
  65. ui->DataBox->addItems(dataBitsList);
  66. ui->DataBox->setCurrentIndex(3);
  67. // 停止位 //停止位默认选择1位
  68. stopBitsList<<"1"<<"2";
  69. ui->StopBox->addItems(stopBitsList);
  70. ui->StopBox->setCurrentIndex(0);
  71. }
  72. Serial_port::~Serial_port()
  73. {
  74. delete serialPort;
  75. delete time;
  76. delete ui;
  77. }
  78. void Serial_port::readData()
  79. {
  80. QByteArray buf;
  81. if (serialPort){
  82. buf = serialPort->readAll();
  83. if (!buf.isEmpty())
  84. {
  85. receBytes += buf.size();
  86. QString redata = QString("received:%1").arg(QString::number(receBytes));
  87. ui->sendlabel->setText(redata);
  88. QString myStrTemp = QString::fromLocal8Bit(buf); //支持中文显示
  89. if(ui->reDisplay->isChecked())
  90. {
  91. QString str = ui->textBrowser->toPlainText();
  92. str +=myStrTemp;
  93. ui->textBrowser->clear();
  94. ui->textBrowser->append(str);
  95. }
  96. }
  97. buf.clear();
  98. }
  99. }
  100. void Serial_port::timeToSend()
  101. {
  102. if(ui->reSendCheck->isChecked())
  103. {
  104. if(time->isActive())
  105. {
  106. return;
  107. }
  108. else
  109. {
  110. int ms = ui->spinBox->value();
  111. time->start(ms);
  112. }
  113. }
  114. else
  115. {
  116. if(time->isActive())
  117. {
  118. time->stop();
  119. }
  120. else
  121. {
  122. return;
  123. }
  124. }
  125. }
  126. void Serial_port::on_OpenButton_clicked()
  127. {
  128. if (ui->OpenButton->text() == tr("打开串口"))
  129. {
  130. serialPort = new QSerialPort;
  131. serialPort->setPortName(ui->PortBox->currentText());
  132. if(serialPort->open(QIODevice::ReadWrite))
  133. {
  134. //qDebug()<<ui->BaudBox->currentIndex();
  135. switch (ui->BaudBox->currentIndex()) {
  136. case 0:
  137. serialPort->setBaudRate(QSerialPort::Baud1200);
  138. break;
  139. case 1:
  140. serialPort->setBaudRate(QSerialPort::Baud2400);
  141. break;
  142. case 2:
  143. serialPort->setBaudRate(QSerialPort::Baud4800);
  144. break;
  145. case 3:
  146. serialPort->setBaudRate(QSerialPort::Baud9600);
  147. break;
  148. case 4:
  149. serialPort->setBaudRate(QSerialPort::Baud19200);
  150. break;
  151. case 5:
  152. serialPort->setBaudRate(QSerialPort::Baud38400);
  153. break;
  154. case 6:
  155. serialPort->setBaudRate(QSerialPort::Baud57600);
  156. break;
  157. case 7:
  158. serialPort->setBaudRate(QSerialPort::Baud115200);
  159. break;
  160. default:
  161. break;
  162. }
  163. switch (ui->StopBox->currentIndex()) {
  164. case 0:
  165. serialPort->setStopBits(QSerialPort::OneStop);
  166. break;
  167. case 1:
  168. serialPort->setStopBits(QSerialPort::TwoStop);
  169. break;
  170. default:
  171. break;
  172. }
  173. switch (ui->DataBox->currentIndex()) {
  174. case 0:
  175. serialPort->setDataBits(QSerialPort::Data5);
  176. break;
  177. case 1:
  178. serialPort->setDataBits(QSerialPort::Data6);
  179. break;
  180. case 2:
  181. serialPort->setDataBits(QSerialPort::Data7);
  182. break;
  183. case 3:
  184. serialPort->setDataBits(QSerialPort::Data8);
  185. break;
  186. default:
  187. break;
  188. }
  189. switch (ui->ParityBox->currentIndex()) {
  190. case 0:
  191. serialPort->setParity(QSerialPort::NoParity);
  192. break;
  193. case 1:
  194. serialPort->setParity(QSerialPort::OddParity);
  195. break;
  196. case 2:
  197. serialPort->setParity(QSerialPort::EvenParity);
  198. break;
  199. default:
  200. break;
  201. }
  202. ui->OpenButton->setText(tr("关闭串口"));
  203. ui->PortBox->setEnabled(false);
  204. ui->BaudBox->setEnabled(false);
  205. ui->StopBox->setEnabled(false);
  206. ui->DataBox->setEnabled(false);
  207. ui->ParityBox->setEnabled(false);
  208. ui->sendButton->setEnabled(true);
  209. ui->sTextRadio->setEnabled(true);
  210. ui->sHexRadio->setEnabled(true);
  211. connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readData);
  212. connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readToHex);
  213. }
  214. else
  215. {
  216. QMessageBox::critical(this, tr("Error"), serialPort->errorString());
  217. }
  218. }
  219. else
  220. {
  221. serialPort->clear();
  222. serialPort->close();
  223. serialPort->deleteLater();
  224. ui->sendButton->setEnabled(false);
  225. ui->OpenButton->setText(tr("打开串口"));
  226. ui->PortBox->setEnabled(true);
  227. ui->BaudBox->setEnabled(true);
  228. ui->StopBox->setEnabled(true);
  229. ui->DataBox->setEnabled(true);
  230. ui->ParityBox->setEnabled(true);
  231. ui->sHexRadio->setEnabled(false);
  232. ui->sTextRadio->setEnabled(false);
  233. }
  234. }
  235. void Serial_port::on_r_clearButton_clicked()
  236. {
  237. ui->textBrowser->clear();
  238. }
  239. void Serial_port::on_s_clearButton_clicked()
  240. {
  241. ui->lineEdit->clear();
  242. }
  243. void Serial_port::on_sendButton_clicked()
  244. {
  245. //Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。ISO-8859-1编码是单字节编码,向下兼容ASCII
  246. //其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
  247. QString str = ui->lineEdit->text();
  248. if(!str.isEmpty())
  249. {
  250. auto isHexSend = ui->sHexRadio->isChecked();
  251. int len = str.length();
  252. if(len%2 == 1)
  253. {
  254. str = str.insert(len-1,'0');
  255. }
  256. QByteArray senddata;
  257. if(isHexSend)
  258. {
  259. StringToHex(str,senddata);
  260. serialPort->write(senddata);
  261. if(serialPort->write(senddata)<0)
  262. {
  263. QMessageBox::critical(this, tr("Error"), serialPort->errorString());
  264. }
  265. }
  266. else
  267. {
  268. if(serialPort->write(ui->lineEdit->text().toLocal8Bit())<0)
  269. {
  270. QMessageBox::critical(this, tr("Error"), serialPort->errorString());
  271. }
  272. }
  273. }
  274. }
  275. void Serial_port::StringToHex(QString str, QByteArray &senddata)
  276. {
  277. int hexdata,lowhexdata;
  278. int hexdatalen = 0;
  279. int len = str.length();
  280. senddata.resize(len/2);
  281. char lstr,hstr;
  282. for(int i=0; i<len; )
  283. {
  284. //char lstr,
  285. hstr=str[i].toLatin1();
  286. if(hstr == ' ')
  287. {
  288. i++;
  289. continue;
  290. }
  291. i++;
  292. if(i >= len)
  293. break;
  294. lstr = str[i].toLatin1();
  295. hexdata = convertHexChart(hstr);
  296. lowhexdata = convertHexChart(lstr);
  297. if((hexdata == 16) || (lowhexdata == 16))
  298. break;
  299. else
  300. hexdata = hexdata*16+lowhexdata;
  301. i++;
  302. senddata[hexdatalen] = (char)hexdata;
  303. hexdatalen++;
  304. }
  305. senddata.resize(hexdatalen);
  306. }
  307. char Serial_port::convertHexChart(char ch)
  308. {
  309. if((ch >= '0') && (ch <= '9'))
  310. return ch-0x30; // 0x30 对应 ‘0’
  311. else if((ch >= 'A') && (ch <= 'F'))
  312. return ch-'A'+10;
  313. else if((ch >= 'a') && (ch <= 'f'))
  314. return ch-'a'+10;
  315. // else return (-1);
  316. else return ch-ch;//不在0-f范围内的会发送成0
  317. }
  318. void Serial_port::readToHex()
  319. {
  320. QByteArray temp = serialPort->readAll();
  321. auto isShow = ui->reDisplay->isChecked(); //接收显示?
  322. QDataStream out(&temp,QIODevice::ReadOnly); //将字节数组读入
  323. while(!out.atEnd())
  324. {
  325. qint8 outChar = 0;
  326. out>>outChar; //每字节填充一次,直到结束
  327. //十六进制的转换
  328. QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
  329. if(isShow){
  330. ui->textBrowser->insertPlainText(str.toUpper());//大写
  331. ui->textBrowser->insertPlainText(" ");//每发送两个字符后添加一个空格
  332. ui->textBrowser->moveCursor(QTextCursor::End);
  333. }
  334. }
  335. }
  336. void Serial_port::Mdisplay()
  337. {
  338. if(ui->rHexRadio->isChecked())
  339. {
  340. disconnect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readData);
  341. connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readToHex);
  342. }
  343. else
  344. {
  345. connect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readData);
  346. disconnect(serialPort, &QSerialPort::readyRead, this, &Serial_port::readToHex);
  347. }
  348. }
  349. void Serial_port::handleLineEditClicked()
  350. {
  351. //QString curPath = QDir::currentPath();
  352. QString curPath = "../QSerial_port";
  353. //QFile file;
  354. QString aFileName = QFileDialog::getOpenFileName(this,QString("选择文件"),curPath,QString("TEXT(*.txt)"));
  355. ui->m_txt->setText(aFileName);
  356. }
  357. void Serial_port::on_Playbutton_clicked()
  358. {
  359. QString path = ui->m_txt->text();
  360. //qDebug()<<"是否成功获取"<<path_ce->text();
  361. if(path.isEmpty())
  362. return;
  363. QFile aFile(path);
  364. if(!aFile.exists()) //文件不存在
  365. qDebug() <<"the file not exist!!";
  366. //return;
  367. if(!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
  368. qDebug() <<"文件无法打开";
  369. //return;
  370. QTextStream aStream(&aFile); //用文件流读取文件
  371. while (!aStream.atEnd())
  372. {
  373. QString qstr = aStream.readLine(); //读取文件的一行文本
  374. //qDebug()<<qstr.length();
  375. int len = qstr.length();
  376. if(len%2 == 1)
  377. {
  378. qstr = qstr.insert(len-1,'0');
  379. }
  380. QByteArray senddata;
  381. StringToHex(qstr,senddata);
  382. //qDebug()<<senddata.size();
  383. for(int i=0;i<senddata.size();i++)
  384. {
  385. QByteArray tmpsenddata=senddata.mid(i,40);
  386. i=i+39;
  387. //qDebug()<<tmpsenddata.length();
  388. serialPort->write(tmpsenddata);
  389. if(!serialPort->waitForBytesWritten()) //这一句很关键,决定是否能发送成功
  390. {
  391. qDebug()<<"serial write error";
  392. }
  393. Sleep(1000);
  394. }
  395. }
  396. }

最后提示一点,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

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/132949
推荐阅读
相关标签
  

闽ICP备14008679号