Qt编写一个串口助手
一、初始化
-
初始化需要注意的是,需要用到关于图形的qcustomplot 库,添加相关的.h和.cpp文件并在.pro文件下加上
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
-
在添加图表时可以指定坐标轴,使每个图像对应他自己的坐标轴,
ui->widget->addGraph(ui->widget->xAxis, ui->widget->yAxis); ui->widget->graph(0)->setPen(QPen(Qt::red));
-
在添加图例名称时需要注意加上
customPlot->legend->setVisible(true);
才会显示, -
radioButton取消自动互斥
ui->radioButton->setAutoExclusive(false);ui->radioButton->setChecked(false);
二、后端数据处理
1. sysparam初始化串口的基本配置信息,并创建相对应的配置文件。
2. 数据接收
serial先在构造函数里给成员变量赋初值,添加默认命令到默认队列里,启动两个槽函数,一个由收到数据为信号调用数据处理函数,另一个由定时器为信号调用指令轮询函数。qt的信号槽机制可以跨越不同文件之间,让各个函数的调用更加方便。
SerialPort::SerialPort(QObject *parent) : QObject(parent)
{
recvData.clear();
regs.fAimPressure = 0;
regs.fCurrentPressure = 0;
QByteArray ba;
ba = QByteArray::fromHex("BB11");
pollingCmdList<<ba;
connect(&port, SIGNAL(readyRead()),
this, SLOT(dataHandler()));
connect(&pollingTimer, SIGNAL(timeout()),
this, SLOT(pollingHandler()));
}
(1)数据处理函数
串口发送过来的是字节数据,这里用一个共同体把一个8位长度为4的数组转化为32位的浮点型数据,判断接收数据的数组长度,长度超过4就将数据进行一个转换,并把转换后的数据放在类变量里。
void SerialPort::dataHandler()
{
union {
quint8 aucValue[4];
float fValue;
} nVal;
QByteArray data = port.readAll();
recvData.append(data);
if (recvData.length() >= 4) {
memcpy(nVal.aucValue, recvData.data(), 4);
regs.fCurrentPressure = nVal.fValue;
recvData.remove(0, 4);
emit serialDataArrive();
}
}
(2)指令轮询函数
处理的指令可以分为人为触发和默认触发,人为触发是指我们随机给单片机发送一些指令,默认指令是指单片机每次发送数据之前都需要一个指令,为了避免麻烦,就设置一个默认指令定时发送单片机。为了每次发送指令让人为指令的优先级最高,这里用两个队列去实现。 cmdQueue队列存放人为指令,pollingCmdList存放默认指令,每次执行前先判断cmdQueue是否为空,如果为空就将默认指令队列pollingCmdList插入到cmdQueue的队尾,如果cmdQueue非空就执行cmdQueue中的第一条指令,执行完就销毁。
void SerialPort::pollingHandler()
{
if (!port.isOpen()) {
return;
}
if (cmdQueue.isEmpty()) {
cmdQueue.append(pollingCmdList);
}
if (!cmdQueue.isEmpty()) {
QByteArray cmd = cmdQueue.first();
currentCmd = cmd;
sendCmd(cmd);
cmdQueue.removeFirst();
}
}
3.数据发送
数据发送给单片机需要在数据前面加一条指令AA12,同样我们要将一个32位的浮点型数据发送过去也需要一个共用体来转换。
void SerialPort::setPressure(float fVal) {
union {
quint8 aucValue[4];
float fValue;
} nVal;
nVal.fValue = fVal;
regs.fAimPressure=fVal;
QByteArray sendCmd = QByteArray::fromHex("AA12") + QByteArray((const char *)(nVal.aucValue), 4);
execCmd(sendCmd);
qDebug()<< " sendCmd:"<<sendCmd;
}
三、前端处理
1.在类变量里创建4个动态数组用于存放曲线的数据源
QVector<double> key;
QVector<double> value;
QVector<double> value2;
QVector<double> value3;*
2.通过定时器触发让三个图层动态显示数据,把类变量中存放的数据添加到动态数组中
value.push_back(serial.regs.fAimPressure);
value2.push_back(serial.regs.fCurrentPressure);
value3.push_back(serial2.regs.fForce);
key.push_back(now);
3.通过一个pushButton让图例实现自动缩放和手动缩放之间来回切换。通过遍历三个动态数组找到一个最大值做自动缩放的最大值。
if (autoRange) {
double max = 0;
double max2 = 0;
foreach (double val, value) {
if (val > max) {
max = val;
}
}
foreach (double val, value2) {
if (val > max) {
max = val;
}
}
foreach (double val, value3) {
if (val > max2) {
max2 = val;
}
}
max += 10;
max2 += 10;
4.让时间横轴进行缩放,如果时间超过一分钟时,将整个图例进行平移,没有的话就正常的移动。
if (key.last() > key.first() + 60) {
customPlot->xAxis->setRange(now - 60, now + 5);
} else {
customPlot->xAxis->setRange(key.first(), key.last() + 5);
}
5.用lineEdit做一个消息提示框,当on_pushButton_set_clicked触发时设置lineEdit里的文字,延时半秒后将lineEdit隐藏
void MainWindow::on_pushButton_set_clicked()
{
QByteArray data = ui->lineEdit->text().toUtf8();
qDebug()<< " 设置值:"<<data;
serial.setPressure(data.toFloat());
ui->label_setTip->setStyleSheet("QLabel{border:1px solid rgb(0, 0, 0); background-color:rgb(255, 255, 255)}");
ui->label_setTip->setText("设置成功!!");
sleep(500);
ui->label_setTip->setStyleSheet("background:transparent");
ui->label_setTip->clear();
}
延时函数(非阻塞延时)
void MainWindow::sleep(unsigned int msec) //msec为毫秒
{
QTime reachTime = QTime::currentTime().addMSecs(msec);
while (QTime::currentTime() < reachTime)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
}
6.同一按钮切换
void MainWindow::on_pushButton_clicked()
{
ui->widget->yAxis2->setTickLabels(false);
if(ui->pushButton->text() == "打开串口1")
{
QString portName = param.portBoard;
serial.open(portName);
ui->radioButton->setChecked(1);
ui->pushButton->setFixedSize(75,23);//固定样式
flushed =TRUE;
ui->pushButton->setStyleSheet(
"QPushButton{"
"color:rgba(255,0,0);"//红色
"border: 1px solid rgb(178, 34, 34);"
"}"
"QPushButton:hover{"
"border: 1px solid rgb(255, 165, 0);"
"}");
ui->pushButton->setText("关闭串口1");
}
else if(ui->pushButton->text()=="关闭串口1")
{
serial.close();
flushed =FALSE;
ui->radioButton->setChecked(0);
ui->pushButton->setFixedSize(75, 23);
ui->pushButton->setStyleSheet(
"QPushButton{"
"color:rgba(0,0,0);" //黑色
"border: 1px solid rgb(54, 100, 139);"
"}"
"QPushButton:hover{"
"border:1px solid rgb(0, 150, 136);"//橘红色
"}");
ui->pushButton->setText("打开串口1");
}
}