【嵌入式开发】监测系统——用QT编写嵌入式下位机界面

  • 一、登陆界面
  • 二、自定义弹出式小键盘
    • 2.1设计思路
    • 2.2具体实现
  • 三、通讯协议
  • 四、检测界面
  • 五、历史信息显示
  • 六、总结

本文为嵌入式课程的课程设计记录。
监测系统整体分为 上位机下位机,上位机使用C#在VS上进行开发,下位机使用Qt进行编写与编译,下载到开发板(itop4412)上运行。 本文主要介绍使用Qt进行嵌入式界面开发的部分
下位机需要完成的 任务为:

  • 监测itop4412开发板上的摄像头,当有移动物体时发送警报信息
  • 记录开发板上的滑动电阻阻值,以曲线的形式记录历史信息
  • 电子相册,显示历史摄像头信息

一、登陆界面


登录界面比较简单,在Qt上对按钮进行了一些修饰,让其变得更加美观了一些。

    QStringList qss;qss.append(QString("QPushButton{background-color:rgba(134,183,200,30);border:1px solid #5F92B2;\border-radius:5px;color:black}"));qss.append(QString("QPushButton:hover{background-color:rgba(0,130,150,60);border:1px solid #5F92B2;\border-radius:5px;color:black;}"));qss.append(QString("QPushButton:hover:pressed{background-color:rgba(85,170,255,90); border:1px solid #3C80B1;\border-radius:5px;color:black;}"));this->setStyleSheet(qss.join(""));

注意:Qt中初始化QString时如果内容太长想换行,换行前需要在末尾输入反斜杠‘\’表示行未结束
一个注册按钮,一个登录按钮,密码输入栏可以选择密码可见,当点击了发送按钮的时候,下位机会通过TCP将登录信息发送给上位机,上位机存储了用户信息,将信息比对之后返回给下位机登陆结果。

二、自定义弹出式小键盘

2.1设计思路

为什么需要大费心思地去写一个小键盘?
因为开发板上的按键不够,也没有外接式键盘,好比如今的智能手机,怎么会用键盘来占用屏幕的空间呢?
自定义的弹出式小键盘效果图如下,当双击输入文本框的时候,小键盘会自动弹出到文本框的下方,根据文本框的位置不同小键盘会自动调整自己的位置:

设计思路:将小键盘绘制成一个独立的ui界面,这个ui上由多个按键进行一定的排列组成,在其他窗口需要调用小键盘的时候,只需要实例化小键盘然后显示就可以实现弹出的功能。这种设计思路具体有两种实现方式:

  • 一个按键对应一个槽函数
  • 所有按键对应同一个槽函数

很显然,第一种实现方式有很明显的弊端:有大量重复的代码、扩展性差。而第二种方法很好地解决了这两个弊端,难点就是如何在这个共同的槽函数中分辨出是哪个按键出发了槽函数。在Qt的信号与槽机制中有一个用法,可以用动态映射查看触发信号的发送者,这样一来就可以完美解决问题了。

那么这动态映射怎么工作的呢?
需要知道的是在Qt中,信号与槽都是建立在Object对象的基础上的,我们通过信号与槽将不同的对象联系起来,或者给对象赋予不同的功能,这种机制简洁易操作。当某个对象emit一个signal的时候,它就是一个sender,系统会记录下当前是谁emit了这个signal,所以你在对应的slot中可以通过Qt的函数sender()得到当前是谁触发了槽函数

比如我们在具体的实现过程中需要知道是哪个按键触发了槽函数:

QPushButton* button = qobject_cast<QPushButton*>(sender());

然后通过该button的信息我们可以判断具体的按键,然后执行相应的工作就可以啦。

2.2具体实现

前面说了小键盘的设计思路是设计成一个独立的ui界面,这个ui界面其实就是一个类,那么我们在某一个界面需要使用小键盘的时候,我们实例化这个类,然后通过show()函数将其显出出来就可以了
小键盘的ui界面如下:

槽函数内容:

//在ui界面中将每个按键的名称都对应好按键所代表的含义
void keyboard::keyclick_SLOT()
{QPushButton* button = qobject_cast<QPushButton*>(sender());QString text = button->text();qDebug()<<"Key : "<<text;if(button->objectName() == QString("pushButton_del")){//删除按键//qDebug()<<"del";emit sendMsg(QString("del"));}else if(button->objectName() == QString("pushButton_clo")){//关闭按键//qDebug()<<"close";emit sendMsg(QString("close"));}else if(button->objectName() == QString("pushButton_clr")){//清空按键emit sendMsg(QString("clear"));}else{//数字和字母按键emit sendMsg(text); //信号传递给主窗口}
}

在代码中可以看到,我们使用了自定义的消息函数sendMsg(),其定义方法为在相应的h文件中申明signals,

signals:void sendMsg(QString);

现在我需要在登陆界面实例化这个小键盘:

//step1在登陆界面头文件里的class中申明变量
keyboard* keyBoard;//keyboard是小键盘类的名称//step2在登录界面cpp文件中实例化小键盘
keyBoard = new keyboard(this); //键盘实例化
connect(keyBoard,SIGNAL(sendMsg(QString)),this,SLOT(keyboard_REV(QString)));//SIGNAL(sendMsg(QString))信号是我们自定义的消息//step3当焦点事件变成文本框时,通过槽函数显示小键盘
void LoginWin::editchange_SLOT()
{//这个槽的触发信号是有文本框被选中currentEdit = qobject_cast<QLineEdit*>(sender());//使用了动态映射,记录当前被选中的文本框qDebug()<<"edit = "<<currentEdit;keyBoard->move(this->x()+currentEdit->x(),this->y()+currentEdit->y()+keyBoard->height()/2);keyBoard->show();
}

接受槽函数:

void LoginWin::keyboard_REV(QString text)
{if(currentEdit){if(text == QString::fromLocal8Bit("del")){qDebug()<<"del";currentEdit->backspace();}else if(text == QString::fromLocal8Bit("close")){qDebug()<<"close";keyBoard->hide();}else if(text == QString::fromLocal8Bit("clear")){qDebug()<<"clear";currentEdit->clear();}else{qDebug()<<"insert";currentEdit->insert(text);}}
}

ps:小键盘还是需要改一下样式的。

三、通讯协议

上位机和下位机的通讯协议使用了TCP协议,通讯内容分了两个部分:

  1. 文件(图片)的传输
  2. 短消息的传输,例如阻值信息、登录信息、心跳包等。

其中图片的传输通过搭建FTP进行传输,短消息的传输通过socket套接字完成。
消息数据帧格式的具体定义见下表:

- 第一字节 第二字节 第三字节 第四字节 第五字节
查询(阻值、周期) 0 0
查询(心跳包) 0 1
应答(阻值、周期) 1 阻值字长(≥4) 阻值 周期字长 周期
收到确认 1 1/0
登录信息 2 账号字长 账号 密码字长 密码
登录结果 2 1/0
动态监测 3

消息总共分为四类,分别是查询消息、应答消息、登录消息、动态监测消息,数据帧的第一个字节为消息类型,根据不同的消息,第二字节有些是后面的字节长度,有些是内容。确认消息第二字节的0代表没有收到,1代表收到了阻值消息,登录结果的第二字节1代表了登陆正确,0代表登录失败。
上位机和下位机的消息通讯按照上表所示的消息格式发送消息,接收方同样按照该格式对接受到的消息进行解析。

四、检测界面

监测界面的效果如下:

监控界面共分为一下几个部分:

  • 图像显示
  • 历史阻值曲线显示
  • 当前阻值显示
  • 警报信息(检测到有物体移动时报警)
  • 设置采样频率

图像显示使用QLabel控件,历史阻值曲线使用Qt的重绘函数paintEvent()。

根据我自己理解,paintEvent这个重绘函数功能很强大,如果是绘制一个ui的部分的话,这个函数个关键是掌握setViewport和setWindow两个函数,及如何设置坐标。目前我们编写Qt程序的时候通常是使用图形界面来绘制ui,而paintEvent函数是直接代码来绘制ui。这个函数一般的触发条件是update(),可以使用定时器来调用update()函数来绘制ui界面。

网上也有一些很好地解释setViewport和setWindow函数的博客,这两个函数当初也是把我整的死去活来,在这里我依据自己的理解通俗地解释一下这两个函数。简单来说,setViewport是用来决定显示在窗口的哪个位置的,例如我只需要绘制整个界面的右上部分,我们不妨称这个右上部分为窗口I;而setWindow是用来设置窗口I的坐标的,我们绘制界面使用QPainter就离不开坐标系。
现在我们假设一些条件:

  • 窗口尺寸为300*200
  • 需要绘制的部分左上角坐标为(200,10),大小为100*100。

setViewport函数原型为:

inline void QPainter::setViewport(int x, int y, int w, int h)
{setViewport(QRect(x, y, w, h));
}
//x:右上角水平坐标
//y:右上角垂直坐标
//w:窗口宽度
//h:窗口高度
setViewport(200,10,100,100);


蓝色窗口就是主窗口,上面代码的意思是设置绘制区域为红色方框区域。这个函数还是很容易理解的,关键是setWindow这个函数,设置自定义的坐标系,这种设定的目的是为了让坐标系符合我们习惯的形式
首先我们需要了解,在Qt中坐标系的设定是这样的:

这个坐标系我们称之为逻辑坐标系,左上点为坐标的原点,需要注意的是,不管我们如何变换窗口和视角,绘画的参考坐标系都是上面这个坐标系。但是我们很多时候用这个坐标系很不方便,我们可以定义一个我们自己的坐标系,setWindow就是实现这个自定义的坐标系的。已经说过了,不管怎么变换,绘画的参考坐标系都是上面这个坐标系,setWindow也不会改变这个参考坐标系,那么定义自己的坐标系又是什么意思呢?
准确来说,setWindow改变的是我将逻辑坐标系中哪个部分放到窗口I中(窗口I是我们上面设定的那个窗口)。假如:painter.setWindow(10, 10, 100, 100);

上图中红框左上角坐标为(10,10),宽高为(100,100),则上面这个代码的意思就是将红框里的内容进行压缩之后放到我定义的视角窗口中,这里压缩的意思是我的红色窗口的长宽和窗口I的大小可能并不一致,所以需要伸缩变换。
那么我们怎么利用setWindow来定义自己的坐标系呢?再看代码:

painter.setWindow(0, 100, 100, -100);

根据我们上面的解释,这个代码的意思是我们将左上角坐标为(0,100),宽高为(100,-100)的区域显示到我的视角窗口中。这里宽高加负号的意思就是我将x或者y方向进行反向了。也很好理解,高-100就将向下的y轴变成向上的y轴,高的绝对值还是100。这样设置的效果是:

黑色坐标系是逻辑坐标系,红色坐标系是我们设定的坐标系,红色坐标系是符合我们使用习惯的坐标系。我们在使用画笔绘图时可以以红色坐标系为我们的坐标系,但是我们必须清楚,我们“画在红色坐标系”上的内容实际上通过转换映射到了黑色逻辑坐标系上。而自定义坐标的原点和长宽就决定了我们将逻辑坐标系里哪些部分展示到窗口I内。
总结一下就是:

  • setViewPort设置了显示窗口的位置和大小
  • 所有绘制的内容最后都绘制到了逻辑坐标系下,而setWindow就设置了我们将逻辑坐标系下哪部分内容放到显示窗口中显示(这也解释了为什么setWindow宽高被解释成缩放系数,因为你逻辑坐标系下10001000大小的内容放到100100的显示窗口中不就是缩小了嘛)

ps:有个需要注意的地方,如果你的压缩比例差的很大,比如横坐标压缩了100倍而纵坐标却没有压缩,那么你画竖线的时候需要画的很粗很粗才能显示出来。
Qtc上的运行效果图为;

为了效果,我把曲线的显色调整了一下,低于3000曲线为蓝色表示过低,高于7000为红色表示过高,在这之间的为绿色表示正常。

五、历史信息显示


这个做的就比较简单了,左右选择按钮,左右两侧分别有预览窗口(其实就是缩小版的图片显示),按照按钮的逻辑更新QL able的显示内容就可以实现了。

六、总结

嵌入式开发本是一个软硬件结合的工作,本文主要讲解了软件部分(Qt部分),另外我认为比较重要的还有环境的搭建和通讯,有时间再更。咕~

【嵌入式开发】监测系统——用QT编写下位机相关推荐

  1. [转载]Java嵌入式开发之一-简介使用Java编写Palm OS程序的解决方案

    Java嵌入式开发之一-简介使用Java编写Palm OS程序的解决方案 现在,使用Java语言为 Palm OS编写程序的领域还没有完全统一,并且也有许多程度上的差异,目前,市面上有好几种不同的可用 ...

  2. Qt5 学习之路及嵌入式开发教程4:代码编写实现信号槽例子

    Qt5 学习之路及嵌入式开发教程4:代码编写实现信号槽例子 整体设计思路: 第一步:先设置整体界面控件的位置及大小: 第二步:添加代码,实现信号槽功能: 第三步:调试运行程序,实现程序功能. 一.整体 ...

  3. AI嵌入式开发:NVIDIA Jetson Xavier NX刷机(2)

    NVIDIA Jetson Xavier NX开发: 刷机 step1:下载文件 step2:安装格式化工厂: step3 安装Etcher 远程连接 刷机 https://developer.nvi ...

  4. keil C语言编程 位地址定义,Keil C编写下位机程序的小技巧和注意点

    1. 用C直接操作DPTR方式 我们平常用Keil C访问总线一般是定义 unsigned char xdata  *fardptr; fardptr=0xbf00; *fardptr=0; 来访问, ...

  5. 嵌入式开发-各种干货

    1.Jar包上传到FTP 2.登录Ubuntu,修改jar包的属性sudo chmod 777 xxxxx.jar 3.运行jar nohup java -jar xxxxxx.jar 4.结束服务: ...

  6. 基于STM32-消防栓监测系统毕业设计---论文(附加最全面的从硬件电路设计->驱动程序设计->阿里云物联网搭建->安卓APP设计)

    设计展示视频连接:消防栓监测系统视频 驱动程序工程文件:消防栓监测系统驱动程序工程(全寄存器开发的) 消防栓监测系统所有设计资料:全栈设计,如下图所示 消防栓监测系统论文(图片看不清的可以下载文档看) ...

  7. 基于QT做上位机开发,实现FPGA通过cyusb3014芯片完成数据的收发

    <基于QT做上位机开发,实现FPGA通过cyusb3014芯片完成数据的收发> #任务要求: 要求用qt编写上位机程序,实现FPGA通过cyusb3014芯片完成数据的收发.下面是采用通过 ...

  8. mico3165嵌入式开发板IAR环境搭建以及Demo运行

    最近跟着老师做毕设,题目是有关于 嵌入式开发的系统安全测试,首先得搭一个集成的开发环境,研究了几天,终于基本把大致的东西弄好了,这里写下来以供经验所需. 开发所用的板子是mico3165,搭载了mic ...

  9. Linux嵌入式开发——C编程

    文章目录 Linux嵌入式开发--C编程 一.编写C程序 1.1.设置vim编辑器 1.2.编写C程序 二.编译C程序 三.make工具和Makefile文件 3.1.编写C程序 C文件 H文件 3. ...

最新文章

  1. spring boot 2.0 源码分析(二)
  2. Science:豆科植物如何建造“固氮工厂”?Murray组在根瘤共生机制取得重要进展...
  3. doctype的三种类型
  4. 2013年全国首届CISA认证培训强化班成功举办
  5. SQL数据库恢复后出现对象名无效(SQL Server备份还原时造成孤立用户的解决方案
  6. Local Binary Convolutional Neural Networks ---卷积深度网络移植到嵌入式设备上?
  7. [Vijos 1143]三取方格数
  8. C语言试题六十五之请编写函数实现猴子吃桃问题
  9. servlet中url-pattern之/与/*的区别
  10. (数据库系统概论|王珊)第二章关系数据库-第二节、第三节:关系操作和关系完整性
  11. mysql数据库ACID实现原理
  12. Discuz在Firefox下无法切换至编辑器状态解决(Z)
  13. vimb java,我可以让vim接受\b而不是\lt;和\gt ;?
  14. HighCharts/Highstock使用小结,使用汉化及中文帮助文档
  15. 对GUID的一点探讨
  16. noip2017棋盘(超级详细)
  17. Py之Scipy:Scipy库(高级科学计算库)的简介、安装、使用方法之详细攻略
  18. 微信开发者平台学习笔记
  19. C语言 - 计算n的阶乘(n!)
  20. Java基础--面向对象(上)

热门文章

  1. 招聘人员最喜欢问的问题有哪些
  2. 【附源码】计算机毕业设计java在线课堂管理平台设计与实现
  3. 涉密计算机u盘管理,涉密U盘管理规定.doc
  4. 软件测试之测试用例的设计
  5. 利用公有云cvm 公网IP实现内网穿透,远程登录内网服务器
  6. 拼团小程序源码_微信小程序拼团系统为什么很多商家开发
  7. linux拷贝文件断电后丢失,linux突然断电重启,配置文件丢失/程序无法打开/文件损坏...
  8. sqlite3查询表中最后一条记录
  9. 点在多边形内算法,JS判断一个点是否在一个复杂多边形的内部
  10. Verilog语言__Verilog简介及设计基础