Qt5实现串口通信

时间:2023-01-04 23:45:35

qt实现串口通信示例


1.串口通信简介

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

Qt5实现串口通信

2.Qt中的串口通信

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

Qt5实现串口通信Qt5实现串口通信

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

#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号口短接,发送数据效果如图。

Qt5实现串口通信

Qt5实现串口通信

3.和校验

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

和校验即通过数据的累加和去验证数据的正确与否。上图是一帧需要发送的数据可以看从左到右分别为帧头,功能字,数据帧与和校验,还可以加上数据位数。帧头和功能字可以根据自己的需求进行定制,数据位即需要发送的数据,最后的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();
}


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