QT5.12案例 串口调试助手

未完成部分:显示发送、自动保存、自动重发、设置背景、将之前发送内容再次显示到发送框等

一、创建SerialTool.pro项目

        窗口类MainWindow

        在SerialTool.pr中添加QT += serialport

二、UI界面制作

8dc0f6704931417eb39383bfd966ab3a.png

d11a0e96e56a4934adf2a3d16ca12ec4.png7a1faeb4b6f74ebdae991054958982dd.png

 三、创建资源文件(.qrc)——暂时添加一张图,用于串口调试助手的图标
项目右击——Add new...——Qt——Qt Resource File——命名为res(添加目录一定要在项目目录下,默认是)——完成——Add Prefix——前缀本文改成/res——Add Files——在项目目录或项目目录的子文件夹中选取图片(可逐个添加,也可选取多张一起添加)——点击打开——运行则可看见所添加的资源

四、serialtool.h代码

#ifndef SERIALTOOL_H
#define SERIALTOOL_H

#include <QMainWindow>
#include <QPaintEvent>
#include <QPainter>
#include <QPixmap>
#include <QIcon>
#include <QAction>
#include <QTime>
#include <QTimer>
#include <QDateTime>
#include <QMessageBox>
#include <QSerialPort>    //提供访问串行端口的功能
#include <QSerialPortInfo>
#include <QByteArray>

QT_BEGIN_NAMESPACE
namespace Ui { class SerialTool; }
QT_END_NAMESPACE

class SerialTool : public QMainWindow
{
    Q_OBJECT

public:
    SerialTool(QWidget *parent = nullptr);
    ~SerialTool();

private:
    Ui::SerialTool *ui;

private:
    void paintEvent(QPaintEvent *);//添加背景等,暂时未编辑
    void systeminit();//初始化
    void updataserial();//串口更新
    char convertHexFromChar(char ch);//字符转HEX(16进制)

    QSerialPort * Gangge_port=nullptr;//所用串口
    QTimer * timer;//定时器,用于更新串口
    QStringList portStringList;
    QString currentCOM = "";//更新串口用到
    int send_buf_len=0;//用于统计发送字节数
    int receive_buf_len=0;//用于统计接收字节数

private slots:
    void on_openbutton_clicked();//UI文件中所在按钮右击所添加的槽函数
    void on_SendButton_clicked();//UI文件中所在按钮右击所添加的槽函数
    void Receive_data();
    //自定义槽函数,需连接信号与槽(前面不能如前加on_,否则
    //会有提示::connectSlotsByName: No matching signal for on_Receive_data()
    //原因可上网查找
    //添加on_,在UI文件绘制到屏幕时就调用on_Receive_data(),
    //而此时串口信号和槽函数的代码还没有被执行,所以槽函数此时还连接不到对象,故弹出警告。
};
#endif // SERIALTOOL_H

五、serialtool.c代码

#include "serialtool.h"
#include "ui_serialtool.h"

SerialTool::SerialTool(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::SerialTool)
{
    ui->setupUi(this);
    systeminit();
}


SerialTool::~SerialTool()
{
    delete ui;
}

void SerialTool::systeminit()
{
    setWindowIcon(QIcon(":/res/res/default.ico"));//图标
    ui->SendButton->setEnabled(false);//发送按钮默认为非

    connect(ui->actionquit,&QAction::triggered,this,[=](){
        this->close();
    });//退出按钮实现

    connect(ui->actionsd,&QAction::triggered,this,[=](){
        ui->receiveview->clear();
    });//清屏按钮实现

    //获取串口号
    ui->portname->clear();
    portStringList.clear();
    Gangge_port=new QSerialPort();
    //foreach用于遍历,不能修改,把availablePorts中的内容依次赋给info
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) //扫描可用串口
    {
        portStringList << info.portName();
    }
    ui->portname->addItems(portStringList);//显示可用端口

    timer=new QTimer(this);
    connect(timer,&QTimer::timeout,this,&SerialTool::updataserial);//定时更新端口
    timer->start(1000);

    //初始化配置串口
    Gangge_port->setParity(QSerialPort::NoParity); //校验位
    Gangge_port->setDataBits(QSerialPort::Data8);//数据位
    Gangge_port->setStopBits(QSerialPort::OneStop);//停止位
    Gangge_port->setFlowControl(QSerialPort::NoFlowControl);//流控制一般没用
}

//更新并检测串口函数实现
void SerialTool::updataserial()
{
    QStringList newPortStringList;
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        newPortStringList += info.portName();
    }
    if(newPortStringList.size() != portStringList.size())
    {
        portStringList = newPortStringList;
        ui->portname->clear();
        ui->portname->addItems(portStringList);
    }
    if(currentCOM != ui->portname->currentText()) //串口突然断开连接了
    {
        currentCOM = ui->portname->currentText();
        if("关闭串口" == ui->openbutton->text())
        {
            on_openbutton_clicked();
        }
    }
}

void SerialTool::paintEvent(QPaintEvent *)
{
}

void SerialTool::on_openbutton_clicked()
{
    if(ui->openbutton->text()=="打开串口")
    {
        if(ui->portname->currentText()=="")
        {
            return;
        }

        Gangge_port->setPortName(ui->portname->currentText());//设置串口
        Gangge_port->setBaudRate(ui->boundrate->currentText().toInt());//设置波特率

        //bool bOK = Gangge_port->open(QIODevice::ReadWrite);
        //QIODevice类是输入/输出设备的基类,QSerialPort是QIODevice类型的对象
        bool bOK = Gangge_port->open(QSerialPort::ReadWrite);//用ReadWrite的模式尝试打开串口
        if(!bOK)
        {
            QMessageBox::critical(this,"提示","无法打开串口,请检查是否被占用!",QMessageBox::Yes,QMessageBox::Yes);
            return;
        }

        switch(ui->partity->currentIndex())//设置数据校验形式
        {
            case 0:
                Gangge_port->setParity(QSerialPort::NoParity);//无校验,没有奇偶校验位发送,Value=0
                break;
            case 1:
                Gangge_port->setParity(QSerialPort::EvenParity);//偶校验,如果字符数据位中"1"的数目是偶数,则校验位应为"0",如果是奇数则为"1"。,Value=2
                break;
            case 2:
                Gangge_port->setParity(QSerialPort::OddParity);//奇校验,如果字符数据位中"1"的数目是偶数,校验位为"1",如果"1"的数目是奇数,校验位应为"0",Value=3
                break;
            case 3:
                Gangge_port->setParity(QSerialPort::MarkParity);//校验位始终为1,Value=4
                break;
            case 4:
                Gangge_port->setParity(QSerialPort::SpaceParity);//校验位始终为0,Value=5
                break;
            default:
                Gangge_port->setParity(QSerialPort::NoParity);//没有奇偶校验位发送
                break;
        }
        switch(ui->databits->currentIndex())//设置数据位
        {
        case 0:
            Gangge_port->setDataBits(QSerialPort::Data8);
            break;
        case 1:
            Gangge_port->setDataBits(QSerialPort::Data7);
            break;
        case 2:
            Gangge_port->setDataBits(QSerialPort::Data6);
            break;
        case 3:
            Gangge_port->setDataBits(QSerialPort::Data5);
            break;
        default:
            Gangge_port->setDataBits(QSerialPort::Data8);
            break;
        }
        switch (ui->stopbits->currentIndex()) //设置停止位
        {
        case 0:
            Gangge_port->setStopBits(QSerialPort::OneStop);
            break;
        case 1:
            Gangge_port->setStopBits(QSerialPort::OneAndHalfStop);
            break;
        case 2:
            Gangge_port->setStopBits(QSerialPort::TwoStop);
            break;
        default:
            Gangge_port->setStopBits(QSerialPort::OneStop);
            break;
        }
        Gangge_port->setFlowControl(QSerialPort::NoFlowControl); //设置无流控
        Gangge_port->setReadBufferSize(8192);                    //数据缓冲区长度

        connect(Gangge_port,&QSerialPort::readyRead,this,&SerialTool::Receive_data,Qt::DirectConnection);//连接串口接收信号与槽函数
        //第5个参数一般不填,为默认值 Qt::AutoConnection

        ui->portname->setEnabled(false);
        ui->boundrate->setEnabled(false);
        ui->partity->setEnabled(false);
        ui->databits->setEnabled(false);
        ui->stopbits->setEnabled(false);
        ui->openbutton->setText("关闭串口");
        ui->SendButton->setEnabled(true);//打开发送按钮
        }
    else
    {
        //如果串口已经打开了 先给他关闭了
        if(Gangge_port->isOpen())
        {
            ui->openbutton->setText("打开串口");
            ui->SendButton->setEnabled(false);//发送按钮

            Gangge_port->close();
            delete Gangge_port;//不加以下三行,每次开关都会多加一行空白行
            Gangge_port=nullptr;
            systeminit();//不加这一行会闪退,错误:The process was ended forcefully
            ui->portname->setEnabled(true);
            ui->databits->setEnabled(true);
            ui->partity->setEnabled(true);
            ui->stopbits->setEnabled(true);
            ui->boundrate->setEnabled(true);
        }
    }
}

//发送按钮按下
void SerialTool::on_SendButton_clicked()
{
    if(ui->openbutton->text()=="打开串口")
        return;

    QString sendstring = ui->senddata->text();

    ui->sendcomboBox->setMaxVisibleItems(20);//设置最大显示下列项 超过要使用滚动条拖拉
    ui->sendcomboBox->setMaxCount(20);//设置最大下拉项 超过将不显示
    ui->sendcomboBox->insertItem(0,sendstring);//指定位置插入单个item(最前面插入)
    ui->sendcomboBox->setCurrentIndex(0);//默认显示当前插入的值

    char cDataHigh,cDataLow;//每个字节的高位、低位
    int isize=ui->senddata->text().size();
    //是否HEX发送(分成字节,每两位一个字节)
    if(ui->sendHex->isChecked())
    {
        int iSendDataSize = 0;
        QByteArray sendData;
        for(int i=0;i<isize-1;)
        {
            char tempbyte_h=ui->senddata->text().at(i).toLatin1();//转换成ASCII码,Latin1代表ASCII,Local8Bit代表unicode
            if(tempbyte_h==' ')
            {
                    i+=1;//遇到空格跳过
            }
            else
            {
                char tempByte_l = ui->senddata->text().at(i+1).toLatin1();//转化成ASCII码的形式,便于符号之间的运算
                i+=2;//i<isize-1,避免错误 ASSERT: "uint(i) < uint(size())" in file
                cDataHigh = SerialTool::convertHexFromChar(tempbyte_h);
                cDataLow  = SerialTool::convertHexFromChar(tempByte_l);
                char sendChar =(char)((cDataHigh<<4)+cDataLow);//一个字符一个字节,八位一个字节(十六进制)
                sendData[iSendDataSize]=sendChar;
                iSendDataSize++;
            }
        }
        if(isize%2==1)//输入senddata为奇数位,在下一位补零
        {
            char tempbyte_h=ui->senddata->text().at(isize-1).toLatin1();
            cDataHigh = SerialTool::convertHexFromChar(tempbyte_h);
            cDataLow  = SerialTool::convertHexFromChar(0);
            char sendChar =(char)((cDataHigh<<4)+cDataLow);
            sendData[iSendDataSize]=sendChar;
            iSendDataSize++;
        }

        send_buf_len+=iSendDataSize;
        ui->TXLenLabel->setText(QString::number(send_buf_len)+" bytes");//显示发送byte

        Gangge_port->write(sendData);
    }
    else if(ui->sendASCII->isChecked())
    {
        //toLatin1、toLocal8Bit都是QString转QByteArray的方法,Latin1代表ASCII,Local8Bit代表unicode
        Gangge_port->write((sendstring).toLatin1());//以字符形式发送
        //Gangge_port->write(sendstring.toLocal8Bit().data());//另一种写法
    }


}

//字符转换成HEX码,不在0~F之内的转换成0
char  SerialTool::convertHexFromChar(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(0);
    }
}

void SerialTool::Receive_data()
{
    QByteArray array=Gangge_port->readAll();//读取数据
    QString revstring;

    //自动换行
    QTextCursor txtcur= ui->receiveview->textCursor();
    txtcur.setPosition(0);
    txtcur.movePosition(QTextCursor::EndOfLine,QTextCursor::KeepAnchor);
    QString qstr= txtcur.selectedText();//读取首行
    if(ui->receiveenter->isChecked()&&qstr!="")//除去首行外,点击自动换行则在数据显示之前换行
    {
        ui->receiveview->insertPlainText("\n");
    }

    //显示时间
    if(ui->timebox->isChecked())
    {
        QDateTime time = QDateTime::currentDateTime();
        QString str = time.toString("yyyy-MM-dd hh:mm:ss dddd");
        ui->receiveview->insertPlainText(str+": ");
    }

    //数据转换
    if(ui->receiveHex->isChecked())
    {
        //第一种写法,直接把串口接收到的数据转换为HEX码
        //QByteArray byteArray;
        // ui->receivewidget->addItem(byteArray.append(array).toHex().toUpper());//接收串口数据十六进制数据显示

        //第二种写法,每一个字节后加一个空格
        QDataStream outArray(&array,QIODevice::ReadOnly);
        qint8 outChar = 0;
        QString str;
        while(!outArray.atEnd())
        {
            outArray >> outChar;
            revstring = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
            str+=revstring+' ';
        }
        ui->receiveview->insertPlainText(str.toUpper());
    }
    else if(ui->receiveASCII->isChecked())
    {
        revstring = QString::fromLocal8Bit(array);
        ui->receiveview->insertPlainText(revstring);
    }

    receive_buf_len += array.length();
    ui->RXLenLabel->setText(QString::number(receive_buf_len)+" bytes");//显示接收byte

    ui->receiveview->insertPlainText(" ");
    ui->receiveview->moveCursor(QTextCursor::End);//自动滚动到底部
}

 六、运行(只是演示)

1、所用硬件,两块zigbee,一个是协调器,一个是终端

        目的1(测试HEX)

                (1)终端连接上位机串口显示终端短地址+长地址

                (2)上位机串口调试助手给协调器发送短地址+命令,协调器返回发送内容并发送命令给所指定终端,终端执行操作并返回数据,协调器显示

aa4d4b0c0bb14712ab4ffd55deeb714b.png

         目的2:测试ASCII9b40e305afa3471c876014ec25a8ebdf.png

数据显示有时有问题,暂未处理                

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值