Qt 实现串口调试实用程序(下)

本文是 Qt 实现串口调试实用程序(上)的内容补充,所实现的程序以及所用环境与该文章无异。

功能补充

为接收显示区增加显示为十六进制形式的功能。要完成该功能,程序从串口接收缓冲中每读取 1 字节,就要以十六进制的格式向接收显示区追加。由于接收程序的实现是逐字节读取,因此只需要在接收时利用 sprintf() 函数把字符转换为十六进制即可。因此,假设以十六进制形式显示的复选框名称为 checkBoxHex,接收数据的槽函数中

    Recv+=QString::fromLocal8Bit(QBArecv);

应修改为:

    if(ui->checkBoxHex->isChecked())
    {
        QString Temp;
        foreach(QChar dat,QBArecv)
            Recv+=Temp.sprintf("%02X ",(unsigned int)dat.unicode());
    }
    else Recv+=QString::fromLocal8Bit(QBArecv);

QSerialPort::readyRead 这一信号可以看出,QSerialPort 库在接收其所认定的一帧数据后就会发出这一信号,因此可根据需要增加接收时间戳这一功能。该功能需要下面的引入。

#include "QDateTime"

并且,在接收数据的槽函数中上述程序段之前添加

QDateTime current_date_time =QDateTime::currentDateTime();
QString current_date =current_date_time.toString("yyyy.MM.dd hh:mm:ss");
if(ui->checkBoxAddTS->isChecked()) Recv+=current_date+" < ";

为了让时间戳总位于行首,还需要在

else Recv+=QString::fromLocal8Bit(QBArecv);

之后添加:

if(ui->checkBoxAddTS->isChecked()) Recv+="\r\n";

以在显示一个数据帧后都追加换行符。

要实现发送数据时追加换行符的功能,只需要在发送数据按钮按下后判断相应复选框是否勾选,并在调用发送函数之前在要发送的数据后追加换行符。假设控制追加换行符的复选框名称为 checkBoxCRLF,则在发送数据的函数中

auto TXdata=ui->sendText->toPlainText();

之后添加:

if(ui->checkBoxCRLF->isChecked())TXdata.append("\r\n");

细节优化

程序构建并运行后,串口设置中端口下拉列表是空的。这是因为程序初始化时并不会主动扫描串口,用户需要按一下“扫描串口”按钮,下拉列表才会被填充。为了让程序初始化后立即加载可用串口列表,串口按钮的 clicked() 槽函数应在 MainWindow 的构造函数的末尾被调用一次。

常用的串口波特率一般被认为是 115200 位每秒,数据位为 8 位,停止位为 1 位,校验位和流控制均不需要。在这些设置中,波特率和数据位下拉列表的首项并不是常用设置。使用方便起见,在构造函数的末尾添加改变下拉列表选项的程序段如下。

ui->baudRateBox->setCurrentIndex(9);
ui->portDataBitBox->setCurrentIndex(3);

其中传入的参数根据下拉列表的安排决定。

在串口开启后,除波特率以外的串口设置在本程序中并不希望用户修改,因此在串口开启后需要禁用对应控件,在串口关闭后重新启用。因此在开启(关闭)串口按钮的 clicked() 槽函数中

if(myserial->open(QIODevice::ReadWrite))

内,添加

ui->baudRateBox->setDisabled(true);
ui->portDataBitBox->setDisabled(true);
ui->stopbBox->setDisabled(true);
ui->parityBox->setDisabled(true);
ui->flowctlBox->setDisabled(true);

而在

if(!myserial->isOpen())

的 else 内,添加

ui->portNameBox->setEnabled(true);
ui->portDataBitBox->setEnabled(true);
ui->stopbBox->setEnabled(true);
ui->parityBox->setEnabled(true);
ui->flowctlBox->setEnabled(true);

增加接收时间戳功能在测试时常出现输出只有时间戳的空行,排查后发现该问题来源于接收函数接收到的空帧。因此在接收函数中针对增加时间戳的功能,在 Recv 字符串初始化后添加

if(QBArecv.length())
    if(ui->checkBoxAddTS->isChecked()) Recv+=current_date+" < ";

且在

ui->IDC_RecvStatic->insertPlainText(Recv);

前,添加

if(QBArecv.length())
    if(ui->checkBoxAddTS->isChecked()) Recv+="\r\n";

另外,使用 insertPlainText()TextBrowser 追加字符串而不是 append() 是为了避免 append() 函数添加多余的换行符。但是使用这种方法,当其中的文本过多而需要滚动查看时,显示区并不会自动滚动到末尾。为了解决这一问题,应在接收显示区的 textChanged() 槽函数中添加:

ui->recvBox->moveCursor(QTextCursor::End);

使光标移动至末尾。

测试

保存并构建工程,按下运行 (Run) 按钮,程序运行后出现如图所示的界面。

端口选择了 /dev/ttyS0,即开发板上的硬件 UART 0,其它选项均为初始化值。此时开启串口,程序会弹出如图所示的对话框,且串口不能开启。

选择事先连接到开发板的 CH340G 模块,其位于 /dev/ttyUSB0,再次开启串口。此时串口能够开启。在发送区填写任意文本并按下发送数据,接收显示区应出现同样的内容,且 CH340G 模块上的 TX 和 RX 指示灯均应当闪烁,如下图所示。

勾选“增加接收时间戳”,再次发送数据,接收显示区如下图所示。可见同一组数据被拆分为 3 帧接收。

勾选“以十六进制显示”,再次发送数据,接收显示区如下图所示。

改变波特率至更低或者更高,再次发送数据时,接收显示区完整显示数据所需的时长有可见的变化,同时时间戳出现的次数也会变化,如下图。

按一下“清空接收区”,接收显示区变为空白;勾选 RTS 或 DTR,对应引脚变为低电平。

问题说明

在编写程序时,Qt Creator 在编辑器中显示 use of undeclared identifier 'MainWindow'。Stack Overflow 上给出的解决方案是在帮助 (Help) -> 关于插件 (About Plugins…) 的对话框中取消勾选 ClangCodeModel 并重新启动 Qt Creator,如下图所示。