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文件

//************************ 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版本

  我是在Fedora9 里面进行Linux版本的编写的,只需把相应的第三方类修改,相对应的定义也改掉,串口名称那里也要改,并添加前缀“/dev”,其他部分相同。

四、移植到Mini6410上面运行

  移植前需要修改下项目文件里ui的串口名称,改成ttySAC0, ttySAC1....之类的,然后在安装了交叉编译链,Qt4.7交叉编译环境后,在Qt里面选择对应的编译链进行编译,具体做法参见《Mini6410 Qt4和Qtopia编程开发指南》。

五、总结

  从接手项目任务到完成大概花了3个月时间,期间由于其他事情断断续续地编程,Qt知识是从零开始学习的,以及由于个人C++水平有限,编写的程序可能会不合理的地方,忘高手指导。通过完成此次的项目,我对于C++的学习又深入一步,以及学会了Qt这种跨平台的SDK。我会分别把Windows版本,Linux版本的源文件上传上来,供大家参考,互相学习。(备注:Linux版本的由于当初是直接在实验室弄好,跟今天修改的Windows版本略有不同,因为当初是为了考虑嵌入式开发板的屏幕小,把选择串口参数部分去掉,直接在程序里面默认设定好了,需要修改的同学直接在里面修改即可。)
Windows版本:http://download.csdn.net/detail/jjzhoujun2010/4393863
Linux版本:http://download.csdn.net/detail/jjzhoujun2010/4393867

Mini6410 Qt4和Qtopia编程开发指南》:http://download.csdn.net/detail/jjzhoujun2010/4393908

原创文章,欢迎转载,转载请注明:blog.csdn.net/jjzhoujun2010

作者:Dream Fly



原创粉丝点击