序言

    项目背景:底层硬件每10ms按照通讯协议通过串口上传40个8位数据,上位机制作软件接收数据并实时绘图。

项目参数:(1)每10ms传输40个8位数据;(2)每1s将接收数据按照通讯协议(分类:电压,电流和频率)绘图。

软件编写软件:QT。

初步估计难点:(1)QT串口接收函数readyRead()函数为不定时触发槽函数;(2)绘图个数太大,影响串口接收函数。

文章注意点:(1)记录按照学习顺序;(2)适合初级学者学习;(3)程序逻辑有不严谨之处。

作者思维:主攻基于单片机,DSP(TI公司6748),ARM(STM32F103,F407,H750)的逻辑编程。

软件编写时间:学习QT+编写程序+验证,共消耗2周。

以上为文章背景


第一部分:

软件制作前后,初步弄清楚了“面向过程”和“面向对象”编程的底层区别。

面向过程:程序从main函数第一行程序执行。程序前级影响后继变量,同时程序中包含中断问题,执行顺序执行的程序中插入中断程序。中断程序中变量的改变影响主循环中程序变量(全局变量和某些局部变量)。

面向对象:举个栗子:按钮按下,立刻改变内部变量参数,改变的变量立刻影响其他程序中参数的使用,可以立刻改变其他部分逻辑执行。当然面对对象编程中也包含面向过程的编程。

注意:只关注了底层的执行问题,至于new对象问题属于编程语言。仅仅使用QT,可以不关注语言差别。


第二部分:串口

这部分直接通过程序说明:

(1)串口号:

    QList<QSerialPortInfo> serialPortinfo = QSerialPortInfo::availablePorts();int count = serialPortinfo.count();for(int i = 0; i<count; i++){ui->SPUPORT->addItem(serialPortinfo.at(i).portName());}

程序说明:读取电脑外置接口中有多少串口,并将其显示到ui->SPUPORT中,其中ui->SPUPORT为ComboBox模块。读取之后获取串口序号字符串。

(2)波特率:

依旧采用ComboBox模块,同时加入编辑组合框(双击即可加入,基本操作,余下不再介绍),填入基本波特率:9600,19200和115200。同样程序中获取波特率字符串。

停止位,校验位与其他和上述一致,不作介绍。

(3)开始按钮:

QT的精髓应该就在按钮的设置,信号和槽函数。点击和触发函数。这点与ARM中触发中断一样,只是这个是手动触发,更直观。触发之后也是顺序执行。

bool MainWindow::mGetPortinfoma()
{mPortname = ui->SPUPORT->currentText();mBaudrate = ui->SPUBAUD->currentText();mSerial.setPortName(mPortname);if(mBaudrate == "9600"){mSerial.setBaudRate(QSerialPort::Baud9600);}else if(mBaudrate == "19200"){mSerial.setBaudRate(QSerialPort::Baud19200);}else{mSerial.setBaudRate(QSerialPort::Baud115200);}return mSerial.open(QSerialPort::ReadWrite);
}

此函数在“开始按钮”中执行。主要功能:(1)取串口号,(2)取波特率,(3)将串口号和波特率放入串口初始化函数(QT固定函数),(4)打开串口。需要注意点:从ComboBox模块中获得的参数都是字符串。然后根据获取的字符串进行判断。

注意:QT精髓点在于以下“开始按钮”的槽函数。按钮的一部分函数相当于单片机,ARM或者DSP中的参数初始化。另一部分函数相当于程序开始程序。这点感觉就是面向对象程序的好处,感觉很好。

void MainWindow::on_BEGINBUTTON_clicked()
{gnBeginFlag = !gnBeginFlag;if(gnBeginFlag == 0){ui->BEGINBUTTON->setText("开始");mSerial.close();ui->SPUPORT->setEnabled(true);ui->SPUBAUD->setEnabled(true);}else{ui->BEGINBUTTON->setText("停止");mGetPortinfoma();ui->SPUPORT->setEnabled(false);ui->SPUBAUD->setEnabled(false);}
}

其基本逻辑:(1)设置自身显示字符:开始和停止;(2)改变标志位:gnBeginFlag,以便在其他程序中作判断使用;(3)打开和关闭串口,使能和禁止参数设置。

(4)槽函数定义:

connect(&mSerial,SIGNAL(readyRead()),this,SLOT(SerialPort_Readyread()));

函数放在程序开始,链接串口接收数据,接收数据后的触发函数。

(5)槽函数(串口有数据后的中断函数):

void MainWindow::SerialPort_Readyread()
{if(gnBeginFlag == true)                                       //按钮按下{if(mSerial.bytesAvailable()>=40)                          //项目要求40个8位数据{ LEDControl();                                         //有数据时指示灯控制函数QByteArray recvData = mSerial.readAll();              //取串口数据(固定函数)for(int i=0;i<40;i++){gnReceiveBuffer[i] = gnReceiveBuffer[i+40];}for(int j=40;j<80;j++){gnReceiveBuffer[j] = recvData.at(j-40);}                                                      //2包,80个数据for(gnDataScanCnt=0;gnDataScanCnt<77;gnDataScanCnt++)  //遍历80个数据{if((gnReceiveBuffer[gnDataScanCnt] == 128)&&(gnReceiveBuffer[gnDataScanCnt+1] == 0)&&(gnReceiveBuffer[gnDataScanCnt+2] == 127)&&(gnReceiveBuffer[gnDataScanCnt+3] == 255))      //判断数据报头{gnDataHeadBegin = gnDataHeadEnd;gnDataHeadEnd = gnDataScanCnt;gnDataHeadCnt ++;                              //检测到报头个数}}gnDataHeadNum = gnDataHeadCnt;gnDataHeadCnt = 0;if(gnDataHeadNum ==2)                                  //2包数据,必须有2个报头{if((gnDataHeadEnd - gnDataHeadBegin) == 40)        //报头中间数据必须为40个{for(int k=gnDataHeadBegin;k<gnDataHeadEnd;k++){gnReceive[k-gnDataHeadBegin] = gnReceiveBuffer[k];}                       //将完成的一帧放入gnReceive数组中DATAExplain();                                 //数据处理函数}else                                    //以下为数据灯的控制{ui->DATALED->setFixedSize(20,20);ui->DATALED->setStyleSheet("QPushButton{border-style:solid;""            border-width:1px;""            border-color:black;""            border-radius:10px;""            background-color:red}");}}else{ui->DATALED->setFixedSize(20,20);ui->DATALED->setStyleSheet("QPushButton{border-style:solid;""            border-width:1px;""            border-color:black;""            border-radius:10px;""            background-color:red}");}}}
}

函数作用:串口接收到不等数据后,触发此函数。(疑惑:不知道接收多少会触发,这个才是整个程序中最不定的因素,找了网上很多说明,都没说明白这点)。当接收的数据大于40时(正好一帧数据),开始处理接收到的数据,将mSerial.readAll()中数据放入定义的数组中。因为不知道具体什么时候开始串口接收,也不确定何时出发此函数,所以必须将2包数据缓存,并且下位机发送的数据中必须有报头和报尾(可以不要),接收到2帧后放入开辟的数组gnReceiveBuffer中,一共80个8位数据。然后遍历所有数据找出报头(下位机报头为80007FFF,没办法,报头就是这样定的),2帧数据如果都正确的话必定有2个报头,将第一个报头和后面的数据取出,放入gnReceive数组中,就可处理完整的一帧数据了。

数据组织简单形式介绍:80007FFF+数据。如果是完整2帧的话,应该是80007FFF+第1帧数据+80007FFF+第2帧数据。但是没有办法判断何时开始和何时结束,接收到的数据应该是下面的形式:前次数据+80007FFF+第1帧数据+80007FFF+第2帧部分数据。然后下次数据的形式(程序中有对应处理函数):第1帧部分数据+80007FFF+第2帧数据+80007FFF+部分第3帧数据。所以每次程序都能得到1帧完整数据,但是处理时间晚数据接收10ms(两帧数据间隔时间)。

程序遗漏点:(1)数据包数据不完整,丢失报头或者报文,(2)接收到不只是40包数据,接收41包或者更多的时候,数据会丢失。程序中没有处理这两点,以后的绘图中也可以看出确实有的点消失了,一部分原因归咎于此点。

程序中加入了数据灯控制,当数据产生错误时,数据灯会变红,这段程序应该是常规操作,和“开始按钮”的逻辑一样。

(6)头文件函数

public:void LEDControl();void DATAExplain();
public:
//开始按钮bool gnBeginFlag;//串口定义变量bool mGetPortinfoma();QSerialPort mSerial;QString mPortname;QString mBaudrate;uint8_t gnReceiveBuffer[80];uint16_t gnDataScanCnt;uint8_t gnDataHeadCnt;uint8_t gnDataHeadBegin;uint8_t gnDataHeadEnd;uint8_t gnDataHeadNum;uint8_t gnReceive[50];public slots:void SerialPort_Readyread();
private slots:void on_BEGINBUTTON_clicked();

里面包含所有的定义参数。

(7)运行灯控制函数

void MainWindow::LEDControl()
{gnRunLedCnt++;if(gnRunLedCnt>1){gnRunLedCnt = 0;}if(gnRunLedCnt > 0){ui->RUNLED->setStyleSheet("QPushButton{border-style:solid;""            border-width:1px;""            border-color:black;""            border-radius:10px;""            background-color:green}");}else{ui->RUNLED->setStyleSheet("QPushButton{border-style:solid;""            border-width:1px;""            border-color:black;""            border-radius:10px;""            background-color:white}");}}

计数器0和1跳变,改变LED灯颜色的变化:绿和白变化。其实这个灯是按钮。只要设置弧度和大小就能把矩形的按钮变成圆形的按钮。同时改变其背景颜色,就可以当做一个LED灯去使用了。

第三部分:UI界面的设计与显示

图1                                                                     图2                                                                 图3

图1:ui设计界面,其中运行灯为按钮;图2:运行界面时,灯为黄色,串口与波特率可选;图3:点击开始按钮后,其显示变为停止,灯闪烁,串口号与波特率不可选。

第四部分:总结

(1)能用ui界面就用ui界面,代码写着还是麻烦。有开发人员强调直接写代码牛b,我不这样认为。只要能攒出来,做出需要的逻辑和效果,哪种方式都是可以的,都是很牛b的。

(2)能借鉴代码就借鉴代码,可以提高效率。能不自己写的函数,能直接调用库函数,或者别人封装好的库函数,就用已经弄好的库函数。不否认自己写一遍可以提高,但是项目那么紧,先把东西搞出来,然后自己留时间再去一点一点自己写之前想自己写的代码,毕竟手中有粮,心中不慌。

(3)程序需要一个星期,得报两个星期的时间吧。不是偷懒,而是(1)你需要调试,试着试着BUG就出来了,写的再快,还是有BUG的,留调试时间;(2)留自己学习时间,这个就不说了,真是想休息也可以的。

注:由于小伙伴需要源代码的时间不同,登录邮箱界面太多麻烦,所以建立了一个订阅号,如果有问题或者需要源码,可添加订阅号,留言后会发送源代码或者有任何问题可留言,将积极解决提出的问题。

QT串口动态实时显示大量数据波形曲线(一)========“串口设置与ui界面添加(灯与按钮)”相关推荐

  1. QT串口动态实时显示大量数据波形曲线(二)========“chart绘图与波形观察”

    上篇文章介绍了串口的设置,以下介绍初步画图. 前言 感觉绘图才是QT等面向对象编程的最好特例.做完之后能感觉到这点是和在单片机或者ARM上逻辑编程最大的区别. 绘图步骤: (1)采用QT自带chart ...

  2. python中texttable库显示实时数据_用Python串口实时显示数据并绘图pyqtgraph

    用Python串口实时显示数据并绘图 使用pyserial进行串口传输 一.安装pyserial以及基本用法 在cmd下输入命令pip install pyserial 注:升级pip后会出现 &qu ...

  3. 记录QT在实时显示摄像头数据时候切换TAB页出现异常

    最近在做一个项目,用一个TAB做页控件,里面放了不同的widget,其中Awidget是实时输出摄像头数据,是通过opencv转qimage,一开始正常,当我快速在A与B之间切换时就出现了如下错误: ...

  4. stm32串口传输数据第一个数据被吞_stm32串口发送数据复位 第一个数据丢失

    http://blog.csdn.net/kevinhg/article/details/40991655 STM32串口发送必须先检测状态,否则第一个字节无法发出,发送完毕,必须检测发送状态是否完成 ...

  5. stm32串口传输数据第一个数据被吞_STM32串口第一个字节丢失问题的分析过程

    STM32串口发送必须先检测状态,否则第一个字节无法发出,发送完毕,必须检测发送状态是否完成,否则,发送不成功,使用stm32f10x调试串口通讯时,发现一个出错的现象,硬件复位重启之后,发送测试数据 ...

  6. QT学习开发笔记(项目实战之智能家居物联 UI 界面开发 )

    智能家居物联 UI 界面开发 项目路径为 4/01_smarthome/01_smarthome/01_smarthome.pro,先看项目界面.项目界面如 下,采用暗黑主题设计,结合黄色作为亮色,让 ...

  7. Qt学习之 在UI界面添加图片

    第一步:添加qrc资源文件 (1)"添加新文件"~~"Qt"~~"Qt Resource File"~~填写文件名 (2)右击刚才新加的文件 ...

  8. 鸿蒙os息屏显示,ROG游戏手机2宣布;鸿蒙OS UI界面/功能模块曝光…

    ROG游戏手机2宣布 7月9日消息,ROG玩家国度官方微博宣布,ROG游戏手机2将于7月23日在北京凯迪拉克中兴M空间正式亮相.ROG官方强调,ROG游戏手机2与腾讯游戏强强联合,重新定义游戏手机. ...

  9. 串口收不到数据或者收到错误数据 串口乱码总结

    1.查看你使用的USB转串口是3.3V的串口还是5V串口,如果发送方使用的是3.3V然后你使用的USB转串口是5V是可以收到数据的但收到的数据都是错误的 2.查看波特率.奇偶校验.停止位等是否设置正确 ...

最新文章

  1. 电商适用电子面单接口对接案例
  2. 技术动态 | 大规模中文概念图谱CN-Probase正式发布
  3. android zxing 自动对焦,ZXing自动对焦问题
  4. 信息学奥赛一本通 1080:余数相同问题 2022.1.29 AC
  5. DocHub v2.3 发布,构建你自己的百度文库
  6. 线上CPU100%排查
  7. java项目打war包
  8. 大连理工大学在CVPR18大规模精细粒度物种识别竞赛中获得冠军
  9. Java编程判断一组学生成绩等级
  10. 18复变函数的积分(四)
  11. 网络爬虫之手机号测吉凶(纯属娱乐)
  12. 《与我长跑十年的女友就要嫁人了》…
  13. 【753. 破解保险箱】
  14. 项目经理人才培养专题︱优秀项目经理能力建设(一)
  15. 报错 SyntaxError: Unexpected token R in JSON at position 0 at JSON.parse (<anonymous>)的解决方法
  16. pdf修改文字内容怎么修改
  17. C++ Reference: Standard C++ Library reference: C Library: cstdio: FILE
  18. vue父子组件赋值操作时报错Avoid mutating a prop directly since the value will be overwritten whenever
  19. 消息称华为与中兴均将裁员解困
  20. 汽车保养技巧 十大汽车保养技巧

热门文章

  1. GPT-4充当评测老师效果惊艳!港中文深圳开源凤凰、Chimera等大模型
  2. 系统动力学模型_《典型海洋生态系统动力学模型构建、应用及发展》入选2020年经典中国国际出版工程...
  3. 水下目标检测——论文阅读与整理
  4. java mail 抄送多用户_javaMail邮件发送功能(多收件人,多抄送人,多密送人,多附件)...
  5. 企业如何做好数据资产的盘点|亿信华辰
  6. Helly定理与证明
  7. Java毕设项目智能导诊系统计算机(附源码+系统+数据库+LW)
  8. 李彦宏Vs周鸿祎:管理风格、领导准则之完全解剖
  9. 声音识别动画(上)-----矩形声音识别动画
  10. 基于pytorch使用LSTM进行文本情感分析