前言
本章节将会给大家带来Qt串口通讯软件的简单实例
一、串口是什么?
串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。典型地,串口用于ASCII码字符的传输。通信使用3根线完成,分别是地线、发送、接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
串口: 串口是一个泛称,UART,TTL,RS232,RS485都遵循类似的通信时序协议,因此都被通称为串口。
UART接口: 通用异步收发器(Universal Asynchronous Receiver/Transmitter),UART是串口收发的逻辑电路,这部分可以独立成芯片,也可以作为模块嵌入到其他芯片里,单片机、SOC、PC里都会有UART模块。
COM口: 特指台式计算机或一些电子设备上的D-SUB外形(一种连接器结构,VGA接口的连接器也是D-SUB)的串行通信口,应用了串口通信时序和RS232的逻辑电平。
USB口: 通用串行总线,和串口完全是两个概念。虽然也是串行方式通信,但由于USB的通信时序和信号电平都和串口完全不同,因此和串口没有任何关系。USB是高速的通信接口,用于PC连接各种外设,U盘、键鼠、移动硬盘、当然也包括“USB转串口”的模块。(USB转串口模块,就是USB接口的UART模块)
波特率: 这是一个衡量符号传输速率的参数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。一般调制速率大于波特率,比如曼彻斯特编码)。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
数据位: 这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
停止位: 用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
奇偶校验位: 在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
流控制: 流控可以使数据接收设备在不能接收数据时通知数据发送设备,使其停止发送。串口的流控经常采用硬件流控和软件流控两种方式。
二、图示实例
使用Configure Virtual Serial Port Driver工具模拟串口
GitHub 使用git 命令下载
git clone https://github.com/dhn111/Qt.git
三、实例实例解析
先在 ” .pro “ 文件中添加 如下图所示:
QT +=serialport
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QComboBox>
#include <QWidget>
#include <QGridLayout>
#include <QMessageBox>
#include <QLabel>
#include <QTextEdit>
#include <QPushButton>
#include <QSerialPort> //提供访问串口的功能
#include <QSerialPortInfo> //提供系统中存在的串口的信息
#include <QDebug>
#include <QMessageBox>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
QComboBox *ComboBox_1; //串口端口
QComboBox *ComboBox_2; //波特率
QComboBox *ComboBox_3; //校验位
QComboBox *ComboBox_4; //数据位
QComboBox *ComboBox_5; //停止位
QComboBox *ComboBox_6; //流控制
QLabel *Label_1; //串口端口
QLabel *Label_2; //波特率
QLabel *Label_3; //校验位
QLabel *Label_4; //数据位
QLabel *Label_5; //停止位
QLabel *Label_6; //流控制
QLabel *Label_7; //发送数据窗口
QLabel *Label_8; //接受数据窗口
QTextEdit *TextEdit_1; //数据发送窗口
QTextEdit *TextEdit_2; //数据接受窗口
QPushButton *PushBtton_1; //打开串口按钮
QPushButton *PushBtton_2; //数据发送按钮
QSerialPort *myport; //串口
QGridLayout *layout; //布局
bool OpenPort; //是否已打开串口
void botelv(const QString &arg); //设置波特率
void jiaoyanwei(int arg); //设置校验位
void shujuwei(const QString &arg); //设置数据位
void tingzhiwei(const QString &arg); //设置停止位
void liukongzhi(int arg1); //设置流控制
public slots :
void ON_PushBtton_1(); //打开串口槽函数
void ON_PushBtton_2(); //发送数据槽函数
void readPort(); //接受串口数据
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ComboBox_1 = new QComboBox(); //创建串口组合窗
ComboBox_2 = new QComboBox(); //创建波特率组合窗
ComboBox_3 = new QComboBox(); //创建校验位组合窗
ComboBox_4 = new QComboBox(); //创建数据位组合窗
ComboBox_5 = new QComboBox(); //创建停止位组合窗
ComboBox_6 = new QComboBox(); //创建流控制组合窗
Label_1 = new QLabel("串口端口");
Label_2 = new QLabel("波特率");
Label_3 = new QLabel("校验位");
Label_4 = new QLabel("数据位");
Label_5 = new QLabel("停止位");
Label_6 = new QLabel("流控制");
Label_7 = new QLabel("发送数据窗口");
Label_8 = new QLabel("接受数据窗口");
TextEdit_1 = new QTextEdit(); //发送数据窗口
TextEdit_2 = new QTextEdit(); //接受数据窗口
PushBtton_1 = new QPushButton("打开");//打开串口按钮
PushBtton_2 = new QPushButton("发送");//数据发送按钮
QWidget *centerWindow = new QWidget; //新建widget 类
this->setCentralWidget(centerWindow); //中间的空间会铺满整个widget
layout = new QGridLayout(this);
layout->addWidget(ComboBox_1,1,2); //创建组合窗 0行,1列
layout->addWidget(ComboBox_2,2,2);
layout->addWidget(ComboBox_3,3,2);
layout->addWidget(ComboBox_4,4,2);
layout->addWidget(ComboBox_5,5,2);
layout->addWidget(ComboBox_6,6,2);
layout->addWidget(Label_1,1,1); //创建组合窗 0行,1列
layout->addWidget(Label_2,2,1);
layout->addWidget(Label_3,3,1);
layout->addWidget(Label_4,4,1);
layout->addWidget(Label_5,5,1);
layout->addWidget(Label_6,6,1);
layout->addWidget(Label_7,7,1); //7行,1列
layout->addWidget(PushBtton_1,7,2); //7行,2列
layout->addWidget(TextEdit_1,8,1,1,2); //8行,1列,长1,宽1
layout->addWidget(Label_8,9,1); //9行,1列,长1,宽1
layout->addWidget(PushBtton_2,9,2); //9行,2列
layout->addWidget(TextEdit_2,10,1,1,2); //10行,1列,长1,宽1
centerWindow->setLayout(layout); //获取layout并设置控件位置
myport = new QSerialPort(); //创建串口对象
connect(myport, &QSerialPort::readyRead, this, &MainWindow::readPort);//绑定读取串口信息的信号和槽
connect(PushBtton_1,SIGNAL(clicked(bool)),this,SLOT(ON_PushBtton_1()));//连接两个信号和槽
connect(PushBtton_2,SIGNAL(clicked(bool)),this,SLOT(ON_PushBtton_2()));//连接两个信号和槽
//获取允许使用的串口信息
QList<QSerialPortInfo> port = QSerialPortInfo::availablePorts();
if(port.isEmpty())
{
ComboBox_1->addItem("无效");
}
else
{
foreach(const QSerialPortInfo &port_info, port)
{
ComboBox_1->addItem(port_info.portName());
}
}
ComboBox_2->addItem("1200");
ComboBox_2->addItem("2400");
ComboBox_2->addItem("4800");
ComboBox_2->addItem("38400");
ComboBox_2->addItem("115200");
ComboBox_3->addItem("OddParity");
ComboBox_3->addItem("EvenParity");
ComboBox_3->addItem("NoParity");
ComboBox_4->addItem("5");
ComboBox_4->addItem("6");
ComboBox_4->addItem("7");
ComboBox_4->addItem("8");
ComboBox_5->addItem("1");
ComboBox_5->addItem("1.5");
ComboBox_5->addItem("2");
ComboBox_6->addItem("Hardware");
ComboBox_6->addItem("Software");
ComboBox_6->addItem("NoFlow");
OpenPort = false;
}
//ui函数未使用
MainWindow::~MainWindow()
{
}
void MainWindow::ON_PushBtton_1() //打开串口槽函数实现
{
if(OpenPort == false) //打开串口
{
//指定串口信息(串口名称)
QSerialPortInfo *port = new QSerialPortInfo(ComboBox_1->currentText());
//绑定串口
myport->setPort(*port);
/******
* currentText 获取字符串
* currentIndex 获取枚举整数
********/
botelv(ComboBox_2->currentText()); //设置波特率
jiaoyanwei(ComboBox_3->currentIndex()); //设置校验位
shujuwei(ComboBox_4->currentText()); //设置数据位
tingzhiwei(ComboBox_5->currentText()); //设置停止位
liukongzhi(ComboBox_6->currentIndex()); //设置流控制
//打开串口
if(!myport->open(QIODevice::ReadWrite))
{
QMessageBox::warning(this,"警告","打开串口失败!");
return;
}
PushBtton_1->setText(tr("关闭串口"));
OpenPort = true;
}
else //关闭串口
{
myport->close();
PushBtton_1->setText(tr("打开串口"));
OpenPort = false;
}
}
void MainWindow::ON_PushBtton_2() //数据发送槽函数实现
{
QString data = TextEdit_1->toPlainText(); //获取数据
QByteArray arg = data.toLatin1();
char *temp = arg.data();
myport->write(temp); //发送数据
}
//接受串口数据
void MainWindow::readPort()
{
QByteArray buf;
buf = myport->readAll(); //接受数据
QString data = buf;
//qDebug() << data << endl;
TextEdit_2->append(data); //写入到文本框中
}
//设置波特率
void MainWindow::botelv(const QString &arg)
{
switch(arg.toInt())
{
case 1200:
myport->setBaudRate(QSerialPort::Baud1200);
break;
case 2400:
myport->setBaudRate(QSerialPort::Baud2400);
break;
case 9600:
myport->setBaudRate(QSerialPort::Baud9600);
break;
case 38400:
myport->setBaudRate(QSerialPort::Baud38400);
break;
case 115200:
myport->setBaudRate(QSerialPort::Baud115200);
//qDebug()<<"Baud115200";
break;
}
}
//设置校验位
void MainWindow::jiaoyanwei(int arg)
{
switch(arg)
{
case 0:
myport->setParity(QSerialPort::OddParity);
break;
case 1:
myport->setParity(QSerialPort::EvenParity);
break;
case 2:
myport->setParity(QSerialPort::NoParity);
//qDebug()<<"NoParity";
break;
}
}
//设置数据位
void MainWindow::shujuwei(const QString &arg1)
{
switch(arg1.toInt())
{
case 5:
myport->setDataBits(QSerialPort::Data5);
break;
case 6:
myport->setDataBits(QSerialPort::Data6);
break;
case 7:
myport->setDataBits(QSerialPort::Data7);
break;
case 8:
myport->setDataBits(QSerialPort::Data8);
//qDebug()<<"Data8";
break;
}
}
//设置停止位
void MainWindow::tingzhiwei(const QString &arg)
{
switch(arg.toInt())
{
case 1:
myport->setStopBits(QSerialPort::OneStop);
//qDebug()<<"OneStop";
break;
case 2:
myport->setStopBits(QSerialPort::TwoStop);
break;
}
}
//设置流控制
void MainWindow::liukongzhi(int arg)
{
switch(arg)
{
case 0:
myport->setFlowControl(QSerialPort::HardwareControl);
break;
case 1:
myport->setFlowControl(QSerialPort::SoftwareControl);
break;
case 2:
myport->setFlowControl(QSerialPort::NoFlowControl);
//qDebug()<<"NoFlowControl";
break;
}
}
四、总结
QT上位机软件实现串口通讯在嵌入式领域有着广泛的应用。