赞
踩
本文实现的控制小车程序用于控制自制的遥控小车,控制方式为点击窗口中的按钮实现控制或者通过键盘的方向键来控制。为方便Qt初学者能从本文中学到自己想要的东西,我将一些重要的流程环节都以单独的文章形式发布,初学者可从本文给予的链接跳转至自己想要的部分,同时每一部分我会尽量给出我的学习路程以及参考资料。
注:
上图:
说明:
这个部分的实现主要靠调用Qt库里的串口类函数从而实现各种功能。如果想详细了解Qt的串口实现,可以转这里:Qt串口的实现(Bilibili),跟着这个老师做一遍就对串口的实现能大概了解了。
本文主要使用到的串口相关函数如下:
//设置串口号,波特率,校验位等
setPortName()
setBaudRate()
setParity()
setDataBits()
setStopBits()
//打开串口以及读写串口
open()
write()
read()
如图
说明:
功能描述:点击配置按钮时程序作出响应,打开一个新的子窗口。具体原理为点击按钮时会发出信号,通过将该信号与一个自定义的函数绑定在一起,就能使信号触发时,函数就开始执行,这个函数就叫这个信号的槽函数。
代码解释:
void MainWindow::on_BtnSerialPortConfig_clicked()
{
newWindow *configWindow = new newWindow;
//setWindowModality:保证子窗口弹出时禁用主窗口
configWindow->setWindowModality(Qt::ApplicationModal);
configWindow->show();
}
详细操作过程可参考这里:QT通过点击按钮弹出新的窗口(CSDN)。
说明:
功能描述:点击连接按钮时,串口能够打开与关闭,并且窗口中的控件文字会发生相应的变化。要实现该功能需要一个标志变量mIsOpen用来表明当前的串口状态,每次点击连接按钮执行槽函数时会判断当前mIsOpen的值,如果当前串口连接,则槽函数执行断开串口的操作,如果当前串口断开,则槽函数执行打开串口的操作。
部分代码:
//===================槽函数===================// void MainWindow::on_BtnConnectPort_clicked() { if(mIsOpen) { //如果当前串口状态为打开,则这里执行关闭串口时的动作: mSerialPort.close(); //关闭串口 ui->BtnConnectPort->setText("连接");//更改按钮2文字为“连接“” ui->LabPortState->setText("未连接");//更改标签2文字为“未连接” ui->BtnSerialPortConfig->setEnabled(true);//允许按钮1使能 mIsOpen = false; } else { //如果当前串口状态为关闭,则这里执行打开串口时的动作: //1.读取配置文件 2.配置并开启串口 3.判断串口是否打开成功 readConfigFile(); if(startSerialPort()) { //如果成功打开串口,则执行这里的代码 mIsOpen = true; ui->BtnConnectPort->setText("断开"); ui->LabPortState->setText("已连接"); ui->BtnSerialPortConfig->setEnabled(false);//禁用按钮1 } else { //如果打开串口失败,则执行这里的代码 QMessageBox::warning(this,"警告","打开串口出错,请检查串口连接或参数设置"); } } } //===============读取配置文件的函数=================// void MainWindow::readConfigFile() { QSettings iniConfigFile(mFileName,QSettings::IniFormat);//新建QSettings类实例,mFileName为配置文件所在地址。 mPortName = iniConfigFile.value("serialport/portname").toString();//读取ini文件中的数据 ... } //===============配置并开启串口的函数=================// bool MainWindow::startSerialPort() { mSerialPort.setPortName(mPortName);//配置端口 ... return mSerialPort.open(QSerialPort::ReadWrite);//打开串口,打开成功则会返回true,反之则返回false }
代码说明:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); mIsOpen = false; //第一次打开时将标志变量mIsOpen置为false //第一次打开程序时会创建配置文件 //mFileName mFileName = QCoreApplication::applicationDirPath() + "/config.ini"; QFile file(mFileName); if(!file.exists()) //判断是否有配置文件,如果没有则在这里执行新建配置文件的动作 { file.open(QIODevice::WriteOnly); file.close(); //file.open()打开一个不存在的文件时会新建这个文件 makeConfigFile();//在这个函数中对ini文件写入数据 } } //=================对ini文件写入数据 void MainWindow::makeConfigFile() { QSettings iniConfigFile(mFileName,QSettings::IniFormat); iniConfigFile.setValue("serialport/portname","COM1"); ... }
显示组为主窗口的第二个部分,UI界面如图
说明:
功能描述:开启串口后,当串口中接收到数据时,显示窗口(BrosMessage)会将接收到的数据显示在窗口中。该功能主要用来接受遥控小车发出的验证信息,即监控小车的当前运动状态。
代码片段:
// MainWindow初始化函数中添加的代码
connect(&mSerialPort, SIGNAL(readyRead()), this, SLOT(on_readyRead()));//当串口有数据传来时,读取串口数据,显示在textbrowser上。
//on_readyRead()函数
void MainWindow::on_readyRead()
{
if(mIsOpen)
{
//当前串口已打开,这里执行接收文字并显示的动作
QByteArray recvData = mSerialPort.readLine();//readLine():按行读取串口数据
ui->BrosMessage->append(QString(recvData));//append():将数据显示到显示窗口中
}
}
代码说明:
功能描述:点击测试按钮(BtnConnectTest)时,程序立刻通过串口发出测试指令。
代码片段:
void MainWindow::on_BtnConnectTest_clicked()
{
mSerialPort.write(QByteArray::fromHex("1F"));//发送测试代码
}
代码说明:
功能描述:点击清空按钮(BtnClearMessage)时,程序将显示窗口(BrosMessage)中的所有文字清空。
代码片段:
void MainWindow::on_BtnClearMessage_clicked()
{
ui->BrosMessage->clear();
}
代码说明:
控制组为主窗口的第三个部分,UI界面如图:
说明:
前进: 00 右旋转: 04
后退: 01 左旋转: 05
右转: 02 加速: 06
左转: 03 减速: 07
直走: 0a 停止(刹车):ff
功能描述:某一个控制按钮按下,程序跳转到对应的槽函数,槽函数中实现向串口写数据的功能。
代码片段:
void MainWindow::on_BtnGo_clicked() //按钮 BtnGo 按下时的槽函数
{
mSerialPort.write(QByteArray::fromHex("00"));
}
void MainWindow::on_BtnBack_clicked() //按钮 BtnBack 按下时的槽函数
{
mSerialPort.write(QByteArray::fromHex("01"));
}
void MainWindow::on_BtnRight_clicked() //按钮 BtnRight 按下时的槽函数
{
mSerialPort.write(QByteArray::fromHex("02"));
}
...
代码说明:
菜单栏设计如下:
说明:
在QT中设计菜单栏可以直接使用交互界面进行UI设计,当然,前提是该窗口为mainwindow类型窗口,如果是dialog或者widget,就需要使用代码进行编写菜单栏。
功能描述:当点击菜单栏中的某个选项时,程序转到执行槽函数,从而弹出相应的窗口。
代码片段:
//信号绑定槽函数写法:
connect(ui->ActIllstrate, SIGNAL(triggered()), this, SLOT(on_DescriptionWindow()));
//槽函数写法:
void MainWindow::on_DescriptionWindow()
{
descriptionWidget *descriptionW = new descriptionWidget;
descriptionW->setWindowModality(Qt::ApplicationModal);
descriptionW->show();
}
代码说明:
子窗口界面设计如下:
说明:
功能描述:每次弹出子窗口时,程序能自动将当前已连接的所有串口端口名自动填充到串口名下拉框中。
代码片段:
newWindow::newWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::newWindow)
{
ui->setupUi(this);
//获取当前已连接的串口号,并将其填入到下拉框选项中
QList<QSerialPortInfo> InfoSerialPort = QSerialPortInfo::availablePorts();
int cnt = InfoSerialPort.count();
for(int i=0; i<cnt; i++)
{
ui->CboxPortName->addItem(InfoSerialPort.at(i).portName());
}
}
代码说明:
功能描述:点击确定按钮时,程序将当前下拉框的选项内容写到配置文件中,然后在关闭窗口。此过程的槽函数分为三步:1. 读取当前下拉框的内容,2. 写入配置文件,3. 关闭窗口。
代码片段:
void newWindow::on_BtnCommit_clicked() { readCbox(); makeConfigFile(); this->close(); } void newWindow::readCbox() { mPortName = ui->CboxPortName->currentText(); ... } void newWindow::makeConfigFile() { QSettings iniConfigFile(fileName,QSettings::IniFormat); iniConfigFile.setValue("serialport/portname",mPortName); ... }
代码说明:
功能描述:点击取消按钮时,直接等效于关闭该窗口。
代码片段:
void newWindow::on_BtnCancel_clicked()
{
this->close();
}
代码说明:
功能描述:每次弹出子窗口时,保证子窗口中的下拉框默认选项为当前配置选项。该过程分为两步:1. 读取配置文件,2. 将参数显示到下拉框中。要使每次初始化时都能执行此过程,将该过程添加到初始化的函数中就行。
代码片段:
newWindow::newWindow(QWidget *parent) : QWidget(parent), ui(new Ui::newWindow) { ui->setupUi(this); //获取当前已连接的串口号,并将其填入到下拉框选项中 QList<QSerialPortInfo> InfoSerialPort = QSerialPortInfo::availablePorts(); int cnt = InfoSerialPort.count(); for(int i=0; i<cnt; i++) { ui->CboxPortName->addItem(InfoSerialPort.at(i).portName()); } //设置下拉框中的默认选项为当前配置选项 readConfigFile(); setCbox(); } void newWindow::readConfigFile() { QFile file(fileName); QSettings iniConfigFile(fileName,QSettings::IniFormat); mPortName = iniConfigFile.value("serialport/portname").toString(); ... } void newWindow::setCbox() { ui->CboxPortName->setCurrentText(mPortName); ... }
代码说明:
键盘按键检测并没有UI设计,因此放在最后来讲。
功能描述:该部分的功能为当连接好遥控小车时,按下键盘上的上下左右按键可以分别控制遥控小车的前进后退转向。抬起按键时遥控小车又将归位。按键逻辑如下表所示:
up :前进 Ctrl+right :顺时针旋转
down :后退 Ctrl+left :逆时针旋转
right:右转 Z :加速
left :左转 X :减速
代码片段:
// keyPressEvent()函数重写 void MainWindow::keyPressEvent(QKeyEvent *event) { if(!event->isAutoRepeat())//解释见代码说明 { if(mKeyFlag)//判断是否按下Ctrl键 { switch (event->key()) //判断按下的是哪个按键 { case Qt::Key_Left: on_BtnCcw_clicked(); break; case Qt::Key_Right: on_BtnCw_clicked(); break; } } else { switch (event->key()) //判断按下的是哪个按键 { case Qt::Key_Up: on_BtnGo_clicked(); break; case Qt::Key_Down: on_BtnBack_clicked(); break; case Qt::Key_Left: on_BtnLeft_clicked(); break; case Qt::Key_Right: on_BtnRight_clicked(); break; case Qt::Key_Z: on_BtnSpeedUp_clicked(); break; case Qt::Key_X: on_BtnSpeedDown_clicked(); break; case Qt::Key_Control: //如果按下Ctrl键则将mKeyFlag设为true mKeyFlag = true; break; } } } } // keyReleaseEvent()函数重写 void MainWindow::keyReleaseEvent(QKeyEvent *event) { if(!event->isAutoRepeat()) { if(mKeyFlag)//判断是否按下Ctrl键 { switch (event->key()) //判断松开的是哪个按键 { case Qt::Key_Left: getStraight(); break; case Qt::Key_Right: getStraight(); break; case Qt::Key_Control: mKeyFlag = false; //如果松开Ctrl键则将mKeyFlag设为false break; } } else { switch (event->key()) //判断松开的是哪个按键 { case Qt::Key_Up: on_BtnStop_clicked(); break; case Qt::Key_Down: on_BtnStop_clicked(); break; case Qt::Key_Left: getStraight(); break; case Qt::Key_Right: getStraight(); break; } } } }
代码说明:
到这里,整个程序就差不多完成了。文章中有部分逻辑不清晰或概念讲述有问题的地方请评论指出,我会不定时查看评论并做出更改。
附:代码源码在此处:百度网盘(提取码:adm9 )
遥控小车硬件部分见此处:STM32遥控小车下位机及硬件连接部分(CSDN)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。