通俗易懂玩QT:串口调试助手制作(内附主要源代码)

串口调试助手制作

一、认识串口

定义

  • 串口即串行接口
  • 也称串行通信接口或串行通讯接口
  • 通常指COM接口
  • 采用串行通信方式的扩展接口。

分类

  • 根据信息传送方向
    • 单工 - 单线单向
    • 半双工 - 双线单向
    • 全双工 - 双线双向
  • 根据串口标准
    • RS-232
    • RS-422
    • RS-485

特性

  • 数据只能一位一位地顺序传送。
  • 数据传输率是115kbps~230kbps
  • 传输数据与传输速率成反比

二、串口类的实现

  1. 串口类的构造函数:
SerialPort::SerialPort(QObject *parent) :
    QObject(parent),
    isOpen(false) {
    serialPort = new QSerialPort(); //初始化串口
}
  1. 串口类的析构函数:

SerialPort::~SerialPort() {
    if(isOpen) {
        close();
    }
}
  1. 串口类的端口扫描函数(本来以为可以做成串口热插拔的,但是涉及到注册表,这里就算了):
QList<portInfo> SerialPort::scan() {
    //设置变量来存放端口号
    QList<portInfo> ports;
    portInfo port;

    //循环获取串口的端口名
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        port.discription = info.description() == "" ? info.portName() : QString("%1:%2").arg(info.portName()).arg(info.description());
        port.portName = info.portName();
        ports << port;
    }

    return ports;
}
  1. 串口类的开启函数(这里串口的初始化结构数据和嵌入式的一样,需要设置波特率、数据位、停止位、奇偶校验、硬件控制流参数):
//开启串口
bool SerialPort::open(portInitInfo pInitInfo) {
    isOpen = false;

    serialPort -> setPortName(pInitInfo.portName);

    if(serialPort -> open(QIODevice::ReadWrite)) {
        serialPort -> setBaudRate(pInitInfo.baudRate);          //波特率
        serialPort -> setDataBits(pInitInfo.dataBits);          //数据位
        serialPort -> setStopBits(pInitInfo.stopBits);          //停止位
        serialPort -> setParity(pInitInfo.parity);              //奇偶校验
        serialPort -> setFlowControl(QSerialPort::NoFlowControl);    //硬件控制流

        connect(serialPort, &QSerialPort::readyRead, this, &SerialPort::readyRead);

        isOpen = true;
    }
    else {
        qDebug() << "[Serialport Error]:" << serialPort->errorString();
    }

    return isOpen;
}
  1. 串口类的数据写入函数:
void SerialPort::write(QString s) {
    if(isOpen) {
        serialPort -> write(s.toUtf8());
    }
}
  1. 串口类的数据读取函数:
//读取串口数据
QByteArray SerialPort::read() {
//    serialPort -> read(rec_data.data(), serialPort -> readBufferSize());
//    return rec_data;
    return serialPort -> readAll();
}
  1. 串口类的关闭函数:
void SerialPort::close() {
    isOpen = false;

    serialPort -> clear();
    serialPort -> close();
}
  1. 串口类的获取函数:
QSerialPort *SerialPort::getSerialPort() const {
    return serialPort;
}

三、串口类与 ui 界面的关联

  1. ui 界面的初始化
//QComboBox init and set default value.
 ui -> uart_port -> addItem("", "");
 for(int i = 0; i < sport -> scan().length(); i ++) {
     ui -> uart_port -> addItem(sport -> scan()[i].portName, sport -> scan()[i].discription);
 }
 ui -> uart_port -> setCurrentText("");

 ui -> uart_buad -> addItem(QString("1200"), 1200);
 ui -> uart_buad -> addItem(QString("2400"), 2400);
 ui -> uart_buad -> addItem(QString("4800"), 4800);
 ui -> uart_buad -> addItem(QString("9600"), 9600);
 ui -> uart_buad -> addItem(QString("19200"), 19200);
 ui -> uart_buad -> addItem(QString("38400"), 38400);
 ui -> uart_buad -> addItem(QString("57600"), 57600);
 ui -> uart_buad -> addItem(QString("115200"), 115200);
 ui -> uart_buad -> setCurrentText("115200");

 ui -> uart_data_bits -> addItem(QString("5"), 5);
 ui -> uart_data_bits -> addItem(QString("6"), 6);
 ui -> uart_data_bits -> addItem(QString("7"), 7);
 ui -> uart_data_bits -> addItem(QString("8"), 8);
 ui -> uart_data_bits -> setCurrentText("8");

 ui -> uart_stop_bits -> addItem(QString("1"), 1);
 ui -> uart_stop_bits -> addItem(QString("1.5"), 2);
 ui -> uart_stop_bits -> addItem(QString("2"), 3);
 ui -> uart_stop_bits -> setCurrentText("1");

 ui -> uart_parity -> addItem(QString("无校验"), 0);
 ui -> uart_parity -> addItem(QString("奇校验"), 1);
 ui -> uart_parity -> addItem(QString("偶校验"), 2);
 ui -> uart_parity -> setCurrentText("无校验");

 //QStatusBar init
 ui -> statusBar -> addWidget(new QLabel("串口信息:"));
 currentPort = new QLabel("请选择串口");
 if(sport -> scan().length() == 0) {
     currentPort -> setText("暂无串口信息");
 }
 ui -> statusBar -> addWidget(currentPort);
 inOutData = new QLabel(QString("Tx:%1   Rx:%2").arg(0).arg(0));
 ui -> statusBar -> addWidget(inOutData);

 //QPlainTextEdit init
 ui -> rec_text_edit -> setMaximumBlockCount(999);    //限制展现数据的行数
 ui -> rec_text_edit -> setReadOnly(true);

 //QPushButton init.
 ui -> btn_update -> setIcon(QIcon(":medias/icons/brush.png"));

 //QLineEdit init.
 ui -> send_time_interval -> setText("1000");    //1000 ms
 ui -> send_time_interval -> setValidator(new QRegExpValidator(QRegExp("[0-9]+$"))); //只能输入数字
  1. 槽函数的初始化
//更新串口端口值
connect(ui -> uart_port, &QComboBox::currentTextChanged, [=] {
    if(ui -> uart_port -> currentData().toString() != "") {
        currentPort -> setText(ui -> uart_port -> currentData().toString());
    }
    else if(sport -> scan().length() == 0) {
        currentPort -> setText("暂无串口信息");
    }
    else {
        currentPort -> setText("请选择串口");
    }
    portInfo.portName = ui -> uart_port -> currentText();
});

//更新串口波特率值
connect(ui -> uart_buad, &QComboBox::currentTextChanged, [=] {
    portInfo.baudRate = ui -> uart_buad->currentData().toInt();
});

//更新串口通信数据位值
connect(ui -> uart_data_bits, &QComboBox::currentTextChanged, [=] {
    switch(ui -> uart_data_bits -> currentData().toInt()) {
        case 5:
            portInfo.dataBits = QSerialPort::Data5;
            break;
        case 6:
            portInfo.dataBits = QSerialPort::Data6;
            break;
        case 7:
            portInfo.dataBits = QSerialPort::Data7;
            break;
        case 8:
            portInfo.dataBits = QSerialPort::Data8;
            break;
        default:
            portInfo.dataBits = QSerialPort::Data8;
            break;
    }
});

//更新串口通信停止位值
connect(ui -> uart_stop_bits, &QComboBox::currentTextChanged, [=] {
    switch(ui -> uart_stop_bits -> currentData().toInt()) {
        case 1:
            portInfo.stopBits = QSerialPort::OneStop;
            break;
        case 2:
            portInfo.stopBits = QSerialPort::OneAndHalfStop;
            break;
        case 3:
            portInfo.stopBits = QSerialPort::TwoStop;
            break;
        default:
            portInfo.stopBits = QSerialPort::OneStop;
            break;
    }
});

//更新串口通信校验值
connect(ui -> uart_parity, &QComboBox::currentTextChanged, [=] {
    switch(ui -> uart_parity -> currentData().toInt()) {
        case 0:
            portInfo.parity = QSerialPort::NoParity;
            break;
        case 1:
            portInfo.parity = QSerialPort::OddParity;
            break;
        case 2:
            portInfo.parity = QSerialPort::EvenParity;
            break;
        default:
            portInfo.parity = QSerialPort::NoParity;
            break;
    }
});

//开启与关闭串口端口按钮
connect(ui -> btn_open, &QPushButton::clicked, [=] {
    isPortOpen = !isPortOpen;   //翻转串口状态

    //通过串口开启标志位判断串口是否被开启
    if(isPortOpen) {
        //判断串口端口值是否为空,防止无效操作造成程序错误
        if(portInfo.portName != "") {
            //判断串口的可用性,防止无效操作对程序体验造成影响
            if(sport -> open(portInfo)) {
                ui -> btn_open -> setText("关闭串口");

                ui -> uart_port -> setDisabled(true);
                ui -> uart_buad -> setDisabled(true);
                ui -> uart_data_bits -> setDisabled(true);
                ui -> uart_stop_bits -> setDisabled(true);
                ui -> uart_parity -> setDisabled(true);
            }
            else {
                isPortOpen = false; //由于串口不可用,所以串口未被开启,isPortOpen 值为 false

                QMessageBox::warning(this, "温馨提示", "串口异常,请稍后重新尝试!",
                                                               QMessageBox::StandardButton('O' + 'K'), QMessageBox::StandardButton('O' + 'K'));
            }


        }
        else {
            isPortOpen = false; //由于未选择串口,所以串口未被开启,isPortOpen 值为 false

            QMessageBox::warning(this, "温馨提示", "请选择串口!\t\t",
                                 QMessageBox::StandardButton('O' + 'K'), QMessageBox::StandardButton('O' + 'K'));
        }
    }
    else {
        sport -> close();

        ui -> btn_open -> setText("打开串口");

        ui -> uart_port -> setDisabled(false);
        ui -> uart_buad -> setDisabled(false);
        ui -> uart_data_bits -> setDisabled(false);
        ui -> uart_stop_bits -> setDisabled(false);
        ui -> uart_parity -> setDisabled(false);
    }
});

//根据串口通信将获取到的数据展现至 QPlainEditText
connect(sport -> getSerialPort(), &QSerialPort::readyRead, [=] {
    ui -> rec_text_edit -> appendPlainText(QString::fromLocal8Bit(sport -> read() + '\b'));
});

//清空串口接收的数据
connect(ui -> btn_clear_rec, &QPushButton::clicked, [=] {
    ui -> rec_text_edit -> clear();
});

//串口发送按钮
connect(ui -> btn_send, &QPushButton::clicked, [=] {
    if(isPortOpen) {
        if(isLineSend || isLoopSend) {
            isTimerOpen = !isTimerOpen;

            ui -> data_line_send -> setDisabled(true);
            ui -> data_loop_send -> setDisabled(true);
            ui -> send_time_interval -> setDisabled(true);

            if(isTimerOpen) {
                timer -> start(ui -> send_time_interval ->text().toInt());

                ui -> btn_send -> setText("暂停");
            }
            else {
                timer -> stop();

                ui -> btn_send -> setText("发送");
                ui -> data_line_send -> setDisabled(false);
                ui -> data_loop_send -> setDisabled(false);
                ui -> send_time_interval -> setDisabled(false);
            }
        }
        else {
            sport -> write(ui -> send_text_edit -> toPlainText());
        }
    }
    else {
        QMessageBox::warning(this, "温馨提示", "无效操作,请先打开串口!",
                             QMessageBox::StandardButton('O' + 'K'), QMessageBox::StandardButton('O' + 'K'));
    }

});

//清空串口发送的数据
connect(ui -> btn_clear_send, &QPushButton::clicked, [=] {
    ui -> send_text_edit -> clear();
});

//更新串口端口数据按钮
connect(ui -> btn_update, &QPushButton::clicked, [=] {
    bool reset = 1;
    QString temp  = portInfo.portName;

    ui -> uart_port -> clear();
    ui -> uart_port -> addItem("", "");
    for(int i = 0; i < sport -> scan().length(); i ++) {
        if(sport -> scan()[i].portName.compare(temp) == 0) reset = 0;
        ui -> uart_port -> addItem(sport -> scan()[i].portName, sport -> scan()[i].discription);
    }

    if(reset){
        portInfo.portName = "";
    }
    else {
        ui -> uart_port -> setCurrentText(temp);
    }

    if(isPortOpen) ui -> btn_open ->click();
});

//串口通过定时器规则发送数据
connect(timer, &QTimer::timeout, [=] {
    static int n = 0;

    if(isLineSend) {
        sport -> write(ui -> send_text_edit -> document() -> findBlockByLineNumber(n ++).text());

        if(n >= ui -> send_text_edit -> document() -> lineCount()) {
            n = 0;

            if(!isLoopSend) {
                isTimerOpen = false;

                timer -> stop();

                ui -> btn_send -> setText("发送");
                ui -> data_line_send -> setDisabled(false);
                ui -> data_loop_send -> setDisabled(false);
                ui -> send_time_interval -> setDisabled(false);
            }
        }
    }
    else {
        sport -> write(ui -> send_text_edit -> toPlainText());
    }
});

//逐行通过串口发送数据
connect(ui -> data_line_send, &QCheckBox::stateChanged, [=] {
    isLineSend = ui -> data_line_send -> checkState();
});

//循环通过串口发送数据
connect(ui -> data_loop_send, &QCheckBox::stateChanged, [=] {
    isLoopSend = ui -> data_loop_send -> checkState();
});
  1. 串口的初始化
portInfo.portName = "";
portInfo.baudRate = 115200;
portInfo.dataBits = QSerialPort::Data8;
portInfo.stopBits = QSerialPort::OneStop;
portInfo.parity = QSerialPort::NoParity;

源码地址:

后续还可以根据自己的需求添加功能,本来打算做数据的十六进制转换的,中途遇到了一些问题,还在进一步研究中,等有空了再补充,串口的 QSerialPort 类学习,后续会补上。

学习分享,一起成长!以上为小编的经验分享,若存在不当之处,请批评指正!

你可能感兴趣的:(QT学习,qt,串口调试助手)