遇到一个默认波特率1.5M的终端设备,看了下手上常用的串口助手竟然没有这个选项,所以干脆自己用QT手撕一个。
开发环境:QT 5.12.0 mingw64
一、创建工程
1、新建创建QMainWindow工程,基类可以选择QMainWindow也可以选择Qwiget,这个网上参考很多,自己搜哈。
2、工程我命名为UART,UART.pro是工程文件。
二、设计UI
1、双击打开mainwindow.ui就是界面文件
2、弹出界面设计文件
3、界面中使用的组件都标在下图了
按照上图标的组件按途中摆放修改成我们需要的名字,然后使用布局工具对齐就行了,布局菜单如下图
3、把Plain text edit喝Text edit的背景设置成黑色
选中Plain text edit
找到QWidget中的palette选项打开
把
Base选项修改成黑色,这是背景颜色选项,把Text选项修改成绿色,这里Text一定要修改颜色,默认是黑色,背景修改成黑色后正常也看不出字。
最后点击确定,下面的发送框也是一样的设置。
4、编辑Combo box组件里面的选项
双击端口、波特率、数据位、校验位后面的空白部分弹出这个串口
分别填上下列内容
4、修改各个组件的变量,如下图,变量和代码中的变量是一一对应的,务必修改的一致,当然,也可以自定义
修改方法也很简单
选中要修改的组件,然后在objectname栏里修改成你想要的名字即可
三、编写代码
1、打开UART.pro文件,在core gui后面添加serialport,如下图
2、打开mainwindow.h粘贴代码如下
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QString>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QTimer>
#include <QPainter>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QSerialPort *serialPort;//定义串口指针
private slots:
/*手动连接槽函数*/
void manual_serialPortReadyRead();
/*以下为mainwindow.ui文件中点击“转到槽”自动生成的函数*/
void on_Button_openserial_clicked();
void on_Button_tx_clicked();
void on_Button_clearrecive_clicked();
void on_Button_cleartx_clicked();
void on_check_autosend_stateChanged(int arg1);
void on_Button_checkserial_clicked();
private:
Ui::MainWindow *ui;
// 发送、接收字节计数
long sendNum, recvNum;
QLabel *lblSendNum;
QLabel *lblRecvNum;
QLabel *lblPortState;
void setNumOnLabel(QLabel *lbl, QString strS, long num);
// 定时发送-定时器
QTimer *timSend;
//QTimer *timCheckPort;
};
#endif // MAINWINDOW_H
3、打开mainwindow.cpp粘贴代码如下
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QSerialPortInfo"
#include <QSerialPort>
#include <QMessageBox>
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStringList serialNamePort;
serialPort = new QSerialPort(this);
connect(serialPort,SIGNAL(readyRead()),this,SLOT(manual_serialPortReadyRead()));/*手动连接槽函数*/
/*找出当前连接的串口并显示到serailCb*/
//foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
//{
//serialNamePort<<info.portName();// 自动扫描当前可用串口,返回值追加到字符数组中
//}
//ui->serailCb->addItems(serialNamePort);// 可用串口号,显示到串口选择下拉框中
ui->serialCB->clear();
//通过QSerialPortInfo查找可用串口
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
ui->serialCB->addItem(info.portName());
}
// 发送、接收计数清零
sendNum = 0;
recvNum = 0;
// 状态栏
QStatusBar *sBar = statusBar();
// 状态栏的收、发计数标签
lblSendNum = new QLabel(this);
lblRecvNum = new QLabel(this);
lblPortState = new QLabel(this);
lblPortState->setText("Connected");
//设置串口状态标签为绿色 表示已连接状态
lblPortState->setStyleSheet("color:red");
// 设置标签最小大小
lblSendNum->setMinimumSize(100, 20);
lblRecvNum->setMinimumSize(100, 20);
lblPortState->setMinimumSize(550, 20);
setNumOnLabel(lblSendNum, "S: ", sendNum);
setNumOnLabel(lblRecvNum, "R: ", recvNum);
// 从右往左依次添加
sBar->addPermanentWidget(lblPortState);
sBar->addPermanentWidget(lblSendNum);
sBar->addPermanentWidget(lblRecvNum);
// 定时发送-定时器
timSend = new QTimer;
timSend->setInterval(1000);// 设置默认定时时长1000ms
connect(timSend, &QTimer::timeout, this, [=](){
on_Button_tx_clicked();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
//检测通讯端口槽函数
void MainWindow::on_Button_checkserial_clicked()
{
ui->serialCB->clear();
//通过QSerialPortInfo查找可用串口
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
ui->serialCB->addItem(info.portName());
}
}
/*手动实现接收数据函数*/
void MainWindow::manual_serialPortReadyRead()
{
QByteArray recBuf = serialPort->readAll();;
QString str_rev;
// 接收字节计数
recvNum += recBuf.size();
// 状态栏显示计数值
setNumOnLabel(lblRecvNum, "R: ", recvNum);
if(ui->check_hex_recive->checkState() == false){
if(ui->check_timestamp->checkState() == Qt::Checked){
QDateTime nowtime = QDateTime::currentDateTime();
str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";
str_rev += QString(recBuf).append("\r\n");
}
else{
// 在当前位置插入文本,不会发生换行。如果没有移动光标到文件结尾,会导致文件超出当前界面显示范围,界面也不会向下滚动。
//ui->recvEdit->appendPlainText(buf);
if(ui->check_changeline->checkState() == Qt::Checked){
str_rev = QString(recBuf).append("\r\n");
}
else
{
str_rev = QString(recBuf);
}
}
}else{
// 16进制显示,并转换为大写
QString str1 = recBuf.toHex().toUpper();//.data();
// 添加空格
QString str2;
for(int i = 0; i<str1.length (); i+=2)
{
str2 += str1.mid (i,2);
str2 += " ";
}
if(ui->check_timestamp->checkState() == Qt::Checked)
{
QDateTime nowtime = QDateTime::currentDateTime();
str_rev = "[" + nowtime.toString("yyyy-MM-dd hh:mm:ss") + "] ";
str_rev += str2.append("\r\n");
}
else
{
if(ui->check_changeline->checkState() == Qt::Checked)
str_rev += str2.append("\r\n");
else
str_rev = str2;
}
}
ui->recive_Edit->insertPlainText(str_rev);
ui->recive_Edit->moveCursor(QTextCursor::End);
}
/*打开串口*/
void MainWindow::on_Button_openserial_clicked()
{
/*串口初始化*/
QSerialPort::BaudRate baudRate;
QSerialPort::DataBits dataBits;
QSerialPort::StopBits stopBits;
QSerialPort::Parity checkBits;
// 获取串口波特率
// baudRate = ui->baundrateCb->currentText().toInt();直接字符串转换为 int 的方法
if(ui->baudrate_CB->currentText()=="1200")
baudRate=QSerialPort::Baud1200;
else if(ui->baudrate_CB->currentText()=="2400")
baudRate=QSerialPort::Baud2400;
else if(ui->baudrate_CB->currentText()=="4800")
baudRate=QSerialPort::Baud4800;
else if(ui->baudrate_CB->currentText()=="9600")
baudRate=QSerialPort::Baud9600;
else if(ui->baudrate_CB->currentText()=="19200")
baudRate=QSerialPort::Baud19200;
else if(ui->baudrate_CB->currentText()=="38400")
baudRate=QSerialPort::Baud38400;
else if(ui->baudrate_CB->currentText()=="57600")
baudRate=QSerialPort::Baud57600;
else if(ui->baudrate_CB->currentText()=="115200")
baudRate=QSerialPort::Baud115200;
else if(ui->baudrate_CB->currentText()=="128000")
baudRate=QSerialPort::Baud128000;
else if(ui->baudrate_CB->currentText()=="230400")
baudRate=QSerialPort::Baud230400;
else if(ui->baudrate_CB->currentText()=="256000")
baudRate=QSerialPort::Baud256000;
else if(ui->baudrate_CB->currentText()=="460800")
baudRate=QSerialPort::Baud460800;
else if(ui->baudrate_CB->currentText()=="921600")
baudRate=QSerialPort::Baud921600;
else if(ui->baudrate_CB->currentText()=="1500000")
baudRate=QSerialPort::Baud1500000;
else if(ui->baudrate_CB->currentText()=="3000000")
baudRate=QSerialPort::Baud3000000;
// 获取串口数据位
if(ui->databit_CB->currentText()=="8")
dataBits=QSerialPort::Data5;
else if(ui->databit_CB->currentText()=="7")
dataBits=QSerialPort::Data6;
else if(ui->databit_CB->currentText()=="6")
dataBits=QSerialPort::Data7;
else if(ui->databit_CB->currentText()=="5")
dataBits=QSerialPort::Data8;
// 获取串口停止位
if(ui->stopbit_CB->currentText()=="1")
stopBits=QSerialPort::OneStop;
else if(ui->stopbit_CB->currentText()=="1.5")
stopBits=QSerialPort::OneAndHalfStop;
else if(ui->stopbit_CB->currentText()=="2")
stopBits=QSerialPort::TwoStop;
// 获取串口奇偶校验位
if(ui->checkbit_CB->currentText() == "None"){
checkBits = QSerialPort::NoParity;
}else if(ui->checkbit_CB->currentText() == "Odd"){
checkBits = QSerialPort::OddParity;
}else if(ui->checkbit_CB->currentText() == "Even"){
checkBits = QSerialPort::EvenParity;
}
// 初始化串口属性,设置 端口号、波特率、数据位、停止位、奇偶校验位数
serialPort->setPortName(ui->serialCB->currentText());
serialPort->setBaudRate(baudRate);
serialPort->setDataBits(dataBits);
serialPort->setStopBits(stopBits);
serialPort->setParity(checkBits);
// 根据初始化好的串口属性,打开串口
// 如果打开成功,反转打开按钮显示和功能。打开失败,无变化,并且弹出错误对话框。
if(ui->Button_openserial->text() == "打开串口"){
if(serialPort->open(QIODevice::ReadWrite) == true){
//QMessageBox::
ui->Button_openserial->setText("关闭串口");
// 让端口号下拉框不可选,避免误操作(选择功能不可用,控件背景为灰色)
ui->serialCB->setEnabled(false);
}else{
QMessageBox::critical(this, "错误提示", "串口打开失败!!!\r\n该串口可能被占用\r\n请选择正确的串口");
}
//statusBar 状态栏显示端口状态
QString sm = "%1 OPENED, %2, 8, NONE, 1";
QString status = sm.arg(serialPort->portName()).arg(serialPort->baudRate());
lblPortState->setText(status);
lblPortState->setStyleSheet("color:green");
}else{
serialPort->close();
ui->Button_openserial->setText("打开串口");
// 端口号下拉框恢复可选,避免误操作
ui->serialCB->setEnabled(true);
//statusBar 状态栏显示端口状态
QString sm = "%1 CLOSED";
QString status = sm.arg(serialPort->portName());
lblPortState->setText(status);
lblPortState->setStyleSheet("color:red");
}
}
/*发送数据*/
void MainWindow::on_Button_tx_clicked()
{
QByteArray array;
//Hex复选框
if(ui->check_hexsend->checkState() == Qt::Checked){
//array = QString2Hex(data); //HEX 16进制
array = QByteArray::fromHex(ui->send_Edit->toPlainText().toUtf8()).data();
}else{
//array = data.toLatin1(); //ASCII
array = ui->send_Edit->toPlainText().toLocal8Bit().data();
}
if(ui->check_sendnewline->checkState() == Qt::Checked){
array.append("\r\n");
}
// 如发送成功,会返回发送的字节长度。失败,返回-1。
int a = serialPort->write(array);
// 发送字节计数并显示
if(a > 0)
{
// 发送字节计数
sendNum += a;
// 状态栏显示计数值
setNumOnLabel(lblSendNum, "S: ", sendNum);
}
}
// 状态栏标签显示计数值
void MainWindow::setNumOnLabel(QLabel *lbl, QString strS, long num)
{
// 标签显示
QString strN;
strN.sprintf("%ld", num);
QString str = strS + strN;
lbl->setText(str);
}
/*清空*/
void MainWindow::on_Button_clearrecive_clicked()
{
ui->recive_Edit->clear();
// 清除发送、接收字节计数
sendNum = 0;
recvNum = 0;
// 状态栏显示计数值
setNumOnLabel(lblSendNum, "S: ", sendNum);
setNumOnLabel(lblRecvNum, "R: ", recvNum);
}
void MainWindow::on_Button_cleartx_clicked()
{
ui->recive_Edit->clear();
// 清除发送字节计数
sendNum = 0;
// 状态栏显示计数值
setNumOnLabel(lblSendNum, "S: ", sendNum);
}
// 定时发送开关 选择复选框
void MainWindow::on_check_autosend_stateChanged(int arg1)
{
// 获取复选框状态,未选为0,选中为2
if(arg1 == 0){
timSend->stop();
// 时间输入框恢复可选
ui->spin_sendtime->setEnabled(true);
}else{
// 对输入的值做限幅,小于10ms会弹出对话框提示
if(ui->spin_sendtime->text().toInt() >= 10){
timSend->start(ui->spin_sendtime->text().toInt());// 设置定时时长,重新计数
// 让时间输入框不可选,避免误操作(输入功能不可用,控件背景为灰色)
ui->spin_sendtime->setEnabled(false);
}else{
ui->check_autosend->setCheckState(Qt::Unchecked);
QMessageBox::critical(this, "错误提示", "定时发送的最小间隔为 10ms\r\n请确保输入的值 >=10");
}
}
}
4、修改下串口的名字
要不然打开串口显示的是Qmainwindow之类的,看起来很low,这里我们修改成串口助手V1.0
修改方法如下图
四、保存上述所有的文件后单机运行
上图的绿色三角符号。
过一会然间就运行起来了
如图