Qt开发串口通信以及坐标显示程序并移植
来源:互联网 发布:lg家用中央空调 知乎 编辑:程序博客网 时间:2024/06/08 16:19
毕业了,整理下思绪,把我之前在实验室弄的一个Qt终端界面开发的程序的整体思路以及源代码共享开来,以便于更好地让Qt为人所知,为开源贡献点我的微薄之力。
一. 项目介绍
项目背景的简要介绍:通过串口从数据采集板传输数据到终端板上面,自行设计一个界面用来显示收集到的数据,以及画出所对应的坐标点。终端显示界面的板子是基于S3C6410芯片的,板子是师兄根据Mini6410开发板电路图画出来的,做了部分裁剪。
由于考虑到跨平台以及移植性问题,我选择了Qt进行开发,分别开发了Windows以及Linux版本,并且移植到开发板上面。以下截图1,图2所示:
图1 Windows版本 图2 Linux版本
编写控制Qt串口通信终端界面时候,串口通信部分是参考yafeilinux的串口通信教程:http://blog.csdn.net/yafeilinux/article/details/4717706 Windows, Linux需要相应改写第三方类,yafeilinux里面已经有介绍。由于我要用的poll查询机制,利用settime(sec),因此更改了"*_qextserialport.cpp"文件里面的对应函数,如下图3所示:
图3 修改*_qextserialport.cpp文件
下图3是Windows下的项目文件截图:
图3 Windows下项目源文件
二. Windows平台编写过程
整个项目我是利用QT的designer和手写代码互相配合完成的。新建了一个带有ui的项目以后,添加第三方串口类源文件。在ui里面画出相应的东西,我是用了tabWidget来切换几个界面,在主界面中还用了tableView表格来显示各个参数,如上图1所示,对应的源程序代码如下。
头文件mainwindow.h,定义声明了各种类和变量。
//******************* mainwindow.h ************//#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>#include <QString>#include <QStandardItemModel>//#include "posix_qextserialport.h"#include "win_qextserialport.h"#include <QDebug>#include <QTime>#include <QTimer>namespace Ui { class MainWindow;}class MainWindow : public QMainWindow{ Q_OBJECTpublic: explicit MainWindow(QWidget *parent = 0); ~MainWindow();// void paintEvent(QPaintEvent *);private: Ui::MainWindow *ui; Win_QextSerialPort *myCom;// Posix_QextSerialPort *myCom; QStandardItemModel *model; int row, column; //table size QTimer *myReadTimer; //采用polling查询的方式进行// QTimer *tableDisTimer; int flag_startToConvert; float QBArray2Float(QByteArray temp);// void paintEvent(QPaintEvent *); void draw(); //QPushButton *iButton; //QLabel *iLabel;protected: bool eventFilter(QObject *obj, QEvent *e);private slots:// int delayUpdate();// void delayUpdate(); void readMyCom(); void on_openMyComBtn_clicked(); void on_closeMyComBtn_clicked(); void on_helpBtn_clicked(); void on_inputBtn_clicked();};#endif // MAINWINDOW_H
mainwindow.cpp文件,具体实现各个子函数的功能。
//****************************** mainwindow.cpp *****************//#include "mainwindow.h"#include "ui_mainwindow.h"#include <QMessageBox>#include <QDebug>#include <QtGui>float QBArray2Float(QByteArray temp);MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){ ui->setupUi(this); ui->radarWidget->installEventFilter(this); // 使之能够直接在radarWidget中绘图 ui->closeMyComBtn->setEnabled(false); setWindowTitle(tr("汽车防撞雷达界面显示")); // 初始化第一个表格 row = 13; column = 3; model = new QStandardItemModel(row, column); ui->tableView->setModel(model); ui->tableView->verticalHeader()->hide(); // Hide the vertical No. model->setHeaderData(0, Qt::Horizontal, tr("目标")); model->setHeaderData(1, Qt::Horizontal, tr("距离/ (米)")); model->setHeaderData(2, Qt::Horizontal, tr("方位/ (度)"));}MainWindow::~MainWindow(){ delete ui;}// ASCII --> Floatfloat floatData[20][2] = {{0, 0}}; // 全局变量,让coordinate.cpp文件也可以调用int ti = 0, tj = 0; // 为了避免将i, j当成全局变量时候,其他函数可能会做相应的修改, 不能定义成static, // 不然外部文件coordinate.cpp就无法使用ti了。int dcFlag = 0; // 标志位,当其为1的时候表示坐标轴上也相应显示出图点//static int disFlag = 0; // 当为1时候表示经过延时后才显示出来数据,用定时器控制disFlag的值void MainWindow::readMyCom(){ int rflag = 0; int serialNum = 0; int num = 0; static int rNum = 2; // 一个目标有2个参数需要显示:距离、方位// myReadTimer->stop(); // turn off timer ui->radarWidget->update(); //刷新坐标系,实时显示图形// if(1 == disFlag) //延时显示标志// { model->removeRows(0, model->rowCount()); model->setRowCount(row); model->setColumnCount(column); ui->tableView->update();// } QByteArray testFrame = myCom->read(5); //帧头"Star"有5个字符,因此read(5) if(testFrame.startsWith("Star")) { dcFlag = 1; qDebug()<<"Start to go"<<endl; rflag = 1; // can receive frame for(int i = 0; i < 50; i++) { serialNum = myCom->bytesAvailable(); if(serialNum >= num) { serialNum = 0; break; } } QByteArray dataFrame = myCom->read(4); //从数据采集板上传送过来的数据是4个字节1组 num = QBArray2Float(dataFrame); for(ti = 0; ti < num; ti++) {// if(1 == disFlag) // 延时0.5s标志// { QStandardItem *itemTarget = new QStandardItem(QString::number(ti + 1)); model->setItem(ti, 0, itemTarget); //如何实现居中显示???// } for(tj = 0; tj < rNum; tj++) { dataFrame = myCom->read(4); floatData[ti][tj] = QBArray2Float(dataFrame);// if(1 == disFlag)// { QString str; QStandardItem *itemFloat = new QStandardItem(str.sprintf("%0.2f", floatData[ti][tj])); model->setItem(ti, tj + 1, itemFloat);// } } }// disFlag = 0; } else { qDebug()<<"receive"<<endl; rflag = 0; myCom->readAll(); // read off the data dcFlag = 0; // 同时坐标轴不显示红点 // myReadTimer->start(); } rflag = 0;// myReadTimer->start(); // turn on the timer}//void MainWindow::delayUpdate()//{// disFlag = 1;// return disFlag;//}//将传输过来的字符串直接转换浮点数函数float MainWindow::QBArray2Float(QByteArray temp){ unsigned int i; float f; temp.resize(4); i=0; i|=((unsigned char) temp.at(3)); i=i<<8; i|=((unsigned char) temp.at(2)); i=i<<8; i|=((unsigned char) temp.at(1)); i=i<<8; i|=((unsigned char) temp.at(0)); f = *(float*)&i; return f;}//打开串口的信号与槽自动关联函数void MainWindow::on_openMyComBtn_clicked(){// QString portName = "/dev/" + ui->portNameComboBox->currentText(); //获取串口名 QString portName = ui->portNameComboBox->currentText(); //获取串口名// myCom = new Posix_QextSerialPort(portName, QextSerialBase::Polling); myCom = new Win_QextSerialPort(portName, QextSerialBase::Polling); //定义串口对象,并传递参数,在构造函数里对其进行初始化 if(myCom->open(QIODevice::ReadWrite)) { //注意:得要先打开串口,然后再设置串口的参数,不然设置无效!!! myCom->flush(); //存入缓冲区内待读取 //设置波特率 if(ui->baudRateComboBox->currentText() == tr("9600")) //根据组合框内容对串口进行设置 myCom->setBaudRate(BAUD9600); else if(ui->baudRateComboBox->currentText() == tr("115200")) myCom->setBaudRate(BAUD115200); //设置数据位 if(ui->dataBitsComboBox->currentText() == tr("8")) myCom->setDataBits(DATA_8); else if(ui->dataBitsComboBox->currentText() == tr("7")) myCom->setDataBits(DATA_7); //设置奇偶校验 if(ui->parityComboBox->currentText() == tr("无")) myCom->setParity(PAR_NONE); else if(ui->parityComboBox->currentText() == tr("奇校验")) myCom->setParity(PAR_ODD); else if(ui->parityComboBox->currentText() == tr("偶校验")) myCom->setParity(PAR_EVEN); //设置停止位 if(ui->stopBitsComboBox->currentText() == tr("1")) myCom->setStopBits(STOP_1); else if(ui->stopBitsComboBox->currentText() == tr("2")) myCom->setStopBits(STOP_2); myCom->setFlowControl(FLOW_OFF); //设置数据流控制,我们使用无数据流的默认设置 //myCom->setTimeout(500); //设置延时 myCom->setTimeout(10); //设置延时 --Modify 改小点 ui->StatusLabel->setText(tr("串口状态:打开成功")); } else { ui->StatusLabel->setText(tr("串口状态:打开失败")); return; } //定义出具体定时器,然后触发后开始Polling查询 myReadTimer = new QTimer(this); myReadTimer->setInterval(10); connect(myReadTimer, SIGNAL(timeout()), this, SLOT(readMyCom())); //信号和槽函数关联,当串口缓冲区有数据时,进行读串口操作 this->myReadTimer->start(); //开始poll查询操作 // 定义延时计数器// tableDisTimer = new QTimer(this);// tableDisTimer->setInterval(500);// connect(tableDisTimer, SIGNAL(timeout()), this, SLOT(delayUpdate()));// this->tableDisTimer->start(); ui->openMyComBtn->setEnabled(false); ui->closeMyComBtn->setEnabled(true); ui->helpBtn->setEnabled(true); ui->portNameComboBox->setEnabled(false); ui->baudRateComboBox->setEnabled(false); ui->dataBitsComboBox->setEnabled(false); ui->stopBitsComboBox->setEnabled(false); ui->parityComboBox->setEnabled(false);}void MainWindow::on_closeMyComBtn_clicked(){ this->myReadTimer->stop(); //关闭poll操作// this->tableDisTimer->stop(); myCom->close(); model->removeRows(0, model->rowCount()); model->setRowCount(row); model->setColumnCount(column); dcFlag = 0; ui->radarWidget->update(); //刷新坐标系,实时显示背景 ui->openMyComBtn->setEnabled(true); ui->helpBtn->setEnabled(true); ui->portNameComboBox->setEnabled(true); ui->baudRateComboBox->setEnabled(true); ui->dataBitsComboBox->setEnabled(true); ui->stopBitsComboBox->setEnabled(true); ui->parityComboBox->setEnabled(true); ui->StatusLabel->setText(tr("串口状态:关闭"));}void MainWindow::on_helpBtn_clicked(){ QMessageBox::about(this, tr("帮助信息"), tr("1.选定好具体串口设置,点击打开串口即可收到信息" "\n" "2.防撞雷达项目小组共同努力制作" "\n" "3.Qt界面作者联系方式:周军 QQ: 380774082" "\n" "4.欢迎访问个人博客 http://blog.csdn.net/jjzhoujun2010"));}// 通过终端板子串口将数据下传到数据采集板中void MainWindow::on_inputBtn_clicked(){ QByteArray absoluteByte = ui->absoluteLineEdit->text().toAscii().data(); QByteArray fastByte = ui->fastLineEdit->text().toAscii().data();// BYTE allByte_char[5] = {'0'}; //QT中的回车只有一个字符\n,而windows下需要解释为\r\n; 根据FPGA接收端程序设计,需要中间加个0 QByteArray allByte = absoluteByte + '0' + fastByte + '\r' + '\n'; myCom->write(allByte); qDebug()<<absoluteByte<<fastByte<<allByte;}
coordinate.cpp文件中,由于我是在tabWidget里面绘图的,不能直接调用QPainter类,相应的解决方法有两种:方法(1): 在子窗口ui.---派生一个类,在PaintEvent里画图。 我所遇到的问题:不清楚自己构造的派生类如何跟ui.---相关联起来,归根到底就是自己C++使用得不熟悉。方法(2):事件过滤法。给ui.---安装事件监视器,在QEvent::paint事件时QPainter paint(ui->---)画图。我使用的是第二种方法。
坐标图显示如下图4所示,由于在QT里面慢慢画直线、弧线等各种坐标点很麻烦,工作量大,因此我就确定好尺寸后,用PS自己弄了背景图片,计算好相应的坐标点,直接在上面显示目标点。
图4 坐标图显示
//*************************** coordinate.cpp *********************#include "mainwindow.h"#include "ui_mainwindow.h"#include <QPainter>#include <stdlib.h>#include <QtCore/qmath.h>// radarWidget中画图bool MainWindow::eventFilter(QObject *obj, QEvent *e){ if(obj == ui->radarWidget) { if(e->type() == QEvent::Paint) { draw(); qDebug()<<"Workinggggggggggggggggggggggggggg................................"; return true; } else { qDebug()<<"Waiting"; } } return QMainWindow::eventFilter(obj, e);}void MainWindow::draw(){ extern float floatData[20][2]; // 声明下在mainwindow.cpp里面定义的全局变量 extern int dcFlag; // dcFlag = 1 时候相当不断刷新, 等于0时候相当于关闭,进行清屏操作,留背景 extern int ti; QPainter painter(ui->radarWidget); QPixmap pix; pix.load(":/coordinate05-18.png"); painter.drawPixmap(0, 0, 400, 440, pix); painter.setBrush(Qt::red); //先设定好画笔颜色 painter.translate(194, 432); // 变换坐标原点 float radian = 0.140; //设定弧度,让其按照指定的角度偏移// dcFlag = 1; qDebug()<<dcFlag<<endl; if(dcFlag == 1) { int x = 0; // 2012-05-18,考虑角度偏移的坐标确定,改小号图像 for(int j = 0; j < ti; j++) // ti为全局变量 不能实时传送,只能当ti等于最后的数的时候才传过来?? { x = int(floatData[j][0] / 10); switch(x) { case 0: if(floatData[j][1] == 0) { painter.drawEllipse(0, -(floatData[j][0] * 4.5), 10, 10); // 0m - 10m内,0度偏角 } else if(floatData[j][1] == 4) { painter.drawEllipse((floatData[j][0] * 4.5) * qSin(radian), -((floatData[j][0] * 4.5) * qCos(radian)), 10, 10); // 4度偏角 } qDebug()<<(floatData[j][0] * 4.5)<<(floatData[j][0] * 4.5) * qSin(radian) <<((floatData[j][0] * 4.5) * qCos(radian)); break; case 1: if(floatData[j][1] == 0) painter.drawEllipse(0, -(45 + (floatData[j][0] - 10) * 4.4), 10, 10); // 10m - 20m内 else if(floatData[j][1] == 4) painter.drawEllipse((45 + (floatData[j][0] - 10) * 4.4) * qSin(radian), -((45 + (floatData[j][0] - 10) * 4.4) * qCos(radian)), 10, 10); // 10m - 20m内 break; case 2: if(floatData[j][1] == 0) painter.drawEllipse(0, -(89 + (floatData[j][0] - 20) * 4.2), 10, 10); // 20m - 30m内 else if(floatData[j][1] == 4) painter.drawEllipse((89 + (floatData[j][0] - 20) * 4.2) * qSin(radian), -(89 + (floatData[j][0] - 20) * 4.2) * qCos(radian), 10, 10); break; case 3: if(floatData[j][1] == 0) painter.drawEllipse(0, -(131 + (floatData[j][0] - 30) * 4.2), 10, 10); // 30m - 40m内 else if(floatData[j][1] == 4) painter.drawEllipse((131 + (floatData[j][0] - 30) * 4.2) * qSin(radian), -(131 + (floatData[j][0] - 30) * 4.2) * qCos(radian), 10, 10); // 4度偏角 break; case 4: if(floatData[j][1] == 0) painter.drawEllipse(0, -(174 + (floatData[j][0] - 40) * 4.3), 10, 10); // 40m - 50m内 else if(floatData[j][1] == 4) painter.drawEllipse((174 + (floatData[j][0] - 40) * 4.3) * qSin(radian), -(174 + (floatData[j][0] - 40) * 4.3) * qCos(radian), 10, 10); // 4度偏角 break; case 5: if(floatData[j][1] == 0) painter.drawEllipse(0, -(218 + (floatData[j][0] - 50) * 4.4), 10, 10); // 50m - 60m内 else if(floatData[j][1] == 4) painter.drawEllipse((218 + (floatData[j][0] - 50) * 4.4) * qSin(radian), -(218 + (floatData[j][0] - 50) * 4.4) * qCos(radian), 10, 10); break; case 6: if(floatData[j][1] == 0) painter.drawEllipse(0, -(261 + (floatData[j][0] - 60) * 4.3), 10, 10); // 60m - 70m内 else if(floatData[j][1] == 4) painter.drawEllipse((261 + (floatData[j][0] - 60) * 4.3) * qSin(radian), -(261 + (floatData[j][0] - 60) * 4.3) * qCos(radian), 10, 10); break; case 7: if(floatData[j][1] == 0) painter.drawEllipse(0, -(305 + (floatData[j][0] - 70) * 4.4), 10, 10); // 70m - 80m内 else if(floatData[j][1] == 4) painter.drawEllipse((305 + (floatData[j][0] - 70) * 4.4) * qSin(radian), -(305 + (floatData[j][0] - 70) * 4.4) * qCos(radian), 10, 10); break; case 8: if(floatData[j][1] == 0) painter.drawEllipse(0, -(343 + (floatData[j][0] - 80) * 3.8), 10, 10); // 80m - 90m内 else if(floatData[j][1] == 4) painter.drawEllipse((343 + (floatData[j][0] - 80) * 3.8) * qSin(radian), -(343 + (floatData[j][0] - 80) * 3.8) * qCos(radian), 10, 10); break; case 9: if(floatData[j][1] == 0) painter.drawEllipse(0, -(378 + (floatData[j][0] - 90) * 3.5), 10, 10); // 90m - 100m内 else if(floatData[j][1] == 4) painter.drawEllipse((378 + (floatData[j][0] - 90) * 3.5) * qSin(radian), -(378 + (floatData[j][0] - 90) * 3.5) * qCos(radian), 10, 10); break; case 10: if(floatData[j][1] == 0) painter.drawEllipse(0, -(410 + (floatData[j][0] - 100) * 3.2), 10, 10); // 90m - 100m内 else if(floatData[j][1] == 4) painter.drawEllipse((410 + (floatData[j][0] - 100) * 3.2) * qSin(radian), -(410 + (floatData[j][0] - 100) * 3.2) * qCos(radian), 10, 10); break; default: break; } qDebug()<<x<<floatData[j][0]<<floatData[j][1]<<j<<endl; } qDebug()<<ti<<endl; // testing local coordinate// painter.drawEllipse(0, 0, 10, 10); // 0m// painter.drawEllipse(0, -(45), 10, 10); // 10m// painter.drawEllipse(0, -(89), 10, 10); // 20m// painter.drawEllipse(0, -(131), 10, 10); // 30m// painter.drawEllipse(0, -(174), 10, 10); // 40m// painter.drawEllipse(0, -(218), 10, 10); // 50m// painter.drawEllipse(0, -(261), 10, 10); // 60m// painter.drawEllipse(0, -(305), 10, 10); // 70m// painter.drawEllipse(0, -(343), 10, 10); // 80m// painter.drawEllipse(0, -(378), 10, 10); // 90m// painter.drawEllipse(0, -(410), 10, 10); // 100m//// painter.drawEllipse(45 * qSin(radian), -(45 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(89 * qSin(radian), -(89 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(131 * qSin(radian), -(131 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(174 * qSin(radian), -(174 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(218 * qSin(radian), -(218 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(261 * qSin(radian), -(261 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(305 * qSin(radian), -(305 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(343 * qSin(radian), -(343 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(378 * qSin(radian), -(378 * qCos(radian)), 10, 10); // 4度偏角// painter.drawEllipse(410 * qSin(radian), -(410 * qCos(radian)), 10, 10); // 4度偏角 }}
//************************ main.cpp *****************//// 2012-05-18 版本,去掉延时显示,进行实时显示,未完成的部分:1.坐标轴的坐标对应的地方,以及刷新的问题;2.调试界面的实现。#include <QtGui/QApplication>#include <QTextCodec> //加入头文件#include "mainwindow.h"int main(int argc, char *argv[]){ QApplication a(argc, argv); QTextCodec::setCodecForTr(QTextCodec::codecForLocale()); //使程序可处理中文 QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312")); // For Window 7// QTextCodec::setCodecForTr(QTextCodec::codecForName("System")); // For all// QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB2312"));// QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312")); MainWindow w; w.show(); return a.exec();}
三、Linux版本
四、移植到Mini6410上面运行
五、总结
《Mini6410 Qt4和Qtopia编程开发指南》:http://download.csdn.net/detail/jjzhoujun2010/4393908
原创文章,欢迎转载,转载请注明:blog.csdn.net/jjzhoujun2010
作者:Dream Fly
- Qt开发串口通信以及坐标显示程序并移植
- Qt开发串口通信以及坐标显示程序并移植
- Qt开发串口通信以及坐标显示程序并移植
- QT程序怎么移植到开发板并运行
- QT程序移植到开发板并运行 解决方案
- Qt编写串口通信程序详解
- 详解 Qt 串口通信程序全程图文
- qt编写串口通信程序的介绍
- Qt编写串口通信程序 讲解
- vs2013用qt编写串口通信程序
- linux下QT开发串口通信助手
- 如何使用CnComm开发串口通信程序
- 如何使用CnComm开发串口通信程序
- 如何使用CnComm开发串口通信程序
- 如何用Delphi开发串口通信程序
- 移植qt库到arm开发板上 并测试 简单qt_ARM程序
- vs2013 + QT组件开发串口程序
- 《Qt串口通信》--实时显示接收的数据
- ORACLE解决大数据量导入排重
- C# this
- 是在传统pc的路上走呢,还是跟潮流走移动互联网
- FCKeditor.net上传图片提示“没有权限”的解决方法
- 一个简单的测试框架(C#版)
- Qt开发串口通信以及坐标显示程序并移植
- android-x86 eclipse 网络设置(经验)
- ajax技巧
- 网页中嵌入百度影音播放器的代码
- WTL学习之对话框和控件
- 让你从此分清码,候选码和主码!!!
- 线程(生产者消费者问题)
- DSP OMAP 程序耗时测定 CPU周期 两种方法
- poj 1860 Currency Exchange