目录

(1).参考资料

(2).QT界面布局实现

(3).数据和操作逻辑

  在上一章我们实现了下位机的协议制定,并通过串口通讯工具完成了对设备内外设(LED)的状态修改,下面就要进行上位机软件的实现了(事实上这部分不属于嵌入式Linux的内容,所以只在本章节讲述下上位机实现的流程和思路,后续维护更新不在进行详细说明,不过下位机界面实现肯定还会涉及这些技术),上位机的界面方案一般指在Windows平台的软件界面开发,如UWP,WINFORM/C#, WPF/C#, QT/C++等,如果说我的个人倾向的话,当然更喜欢的WINFORM/C#技术,一方面C#相对于C++更简单,不会因为复杂的模板和继承机制,导致出问题的报错比代码都长,另一方面网上的资料也多,遇到问题很容易找到解决办法,在我之前实现的应用中,也都是使用WINFORM技术,再加上对于QT/C++根本没有了解过,算是第一次接触(之前接触的都是无界面应用或者使用的Android/Java),不过对于嵌入式Linux来说,QT/C++是也是十分需要掌握的,既然都要学习,那么上位机选择QT/C++先来熟悉语法和基础,完成上位机QT界面和通讯协议的实现,这也是这篇文章耽误一段时间的原因,在QT还没有熟悉之前,参考例程写应用还可以,清晰的讲清楚还是很困难的,在应用接近大半个月后,也算有些心得,可以进行后续的进度了,下面开始本节的实现吧。

 

参考资料

  1. 开源QT例程项目

  2. 《QT5开发和实例》 -- 参考这本书不是因为写的有深度,而是因为里面全是例程,适合初学者了解

  3. 《C++ Primer Plus》

 

QT界面布局实现

  基于从Winform的界面开发经验,QT界面的布局也是类似,参考上面的例程项目,主要涉及的的窗体有:

  QFrame:基本控件的基类,用于将功能类似的结构整理在一起

  QLabel:标签控件,用于显示文字说明

  QPushButton:按键控件,执行按键动作

  QTextEdit:编辑文本框控件,用于输入或者显示文本

  QComboBox:选择框控件,支持下拉菜单的选择

  QLineEdit:行编辑框,用于行输入和显示文本

  在掌握基础的基本的布局编辑框后,就可以使用设计栏左边的控件框中,拖出如下的编辑框。

  在构建完成上述编辑框后,下面就要实现界面内容的填充,主要包含页面布局的显示,下拉框的完善,代码如下:

  //添加COM口
QStringList comList;
for (int i = ; i <= ; i++) {
comList << QString("COM%1").arg(i);
}
ui->combo_box_com->addItems(comList); //波特率选项
QStringList BaudList;
BaudList <<""<<""<<""<<""<<"";
ui->combo_box_baud->addItems(BaudList);
ui->combo_box_baud->setCurrentIndex(); //数据位选项
QStringList dataBitsList;
dataBitsList <<"" << "" << ""<<"";
ui->combo_box_data->addItems(dataBitsList);
ui->combo_box_data->setCurrentIndex(); //停止位选项
QStringList StopBitsList;
StopBitsList<<""<<"";
ui->combo_box_stop->addItems(StopBitsList);
ui->combo_box_stop->setCurrentIndex(); //校验位
QStringList ParityList;
ParityList<<"N"<<"Odd"<<"Even";
ui->combo_box_parity->addItems(ParityList);
ui->combo_box_parity->setCurrentIndex(); //设置协议类型
QStringList SocketTypeList;
SocketTypeList<<"TCP"<<"UDP";
ui->combo_box_socket_type->addItems(SocketTypeList);
ui->combo_box_parity->setCurrentIndex(); // //正则限制部分输入需要为数据
// QRegExp regx("[0-9]+$");
// QValidator *validator_time = new QRegExpValidator(regx, ui->line_edit_time);
// ui->line_edit_time->setValidator( validator_time );
// QValidator *validator_id = new QRegExpValidator(regx, ui->line_edit_dev_id);
// ui->line_edit_dev_id->setValidator( validator_id ); //默认按键配置不可操作
init_btn_disable(ui);
ui->btn_uart_close->setDisabled(true);
ui->btn_socket_close->setDisabled(true);

  至此,我们就完成了布局相关的代码。

 

数据和操作逻辑

  对于无界面的软件或者方案实现,我们主要关注的是数据在整个逻辑模型之间的流通,转移和处理,对于有界面的软件实现,其实这套逻辑也是存在的。除了涉及界面的处理,其它部分其实也是这套逻辑,不过是将部分数据的源头来自于界面的动作,并且将最后的输出结果从命令行转移到界面的窗口中,如果理解了这一点,就会发现其实带界面的应用实现并没有太困难,这也是我接触QT/C++很短时间就能将Winform和下位机经验快速转换的原因。对于这个项目来说,主要实现的背后数据逻辑包含以下三个方面:

  1.按键动作的信号和界面的输入信息处理

  2.硬件通讯相关的串口知识,socket通讯以及涉及的TCP和UDP协议传输

  3.协议相关的硬件实现和数据处理

  4.处理结果的界面输出显示

  其中按键部分的动作和界面输出显示都是QT界面背后的逻辑,包含信号槽的绑定和界面变量的操作方法,如下所示

 //获取设备ID信息
pMainUartProtocolThreadInfo->SetId(ui->line_edit_dev_id->text().toShort()); //界面显示的操作
if(ui->text_edit_test->document()->lineCount() > )
{
qDebug()<<"lines do";
ui->text_edit_test->setText(s);
}
else
{
ui->text_edit_test->append(s);
}

  这部分是涉及QT的基础知识,主要都是积累的技巧,难度不高,建议参考《QT5开发和实例》实例去学习。

  硬件串口知识和Socket知识就是应用实现需要的其它能力,包含对QextSerialPort和Socket接口的应用,此外为了满足多接口应用同时操作的需求,需要实现多线程的编程,其中串口的应用初始化配置主要包含的有

  flush:清空缓存区

  setBaudRate:设置波特率

  setDataBits:设置数据位

  setParity:设置奇偶校验位

  setStopBits:设置停止位

  setFlowControl:设置流量控制

  setTimeout:设置接收和发送超时时间

 pMainUartProtocolThreadInfo->m_pSerialPortCom = new QextSerialPort(ui->combo_box_com->currentText(), QextSerialPort::Polling);
pMainUartProtocolThreadInfo->m_bComStatus = pMainUartProtocolThreadInfo->m_pSerialPortCom->open(QIODevice::ReadWrite); if(pMainUartProtocolThreadInfo->m_bComStatus)
{
//清除缓存区
pMainUartProtocolThreadInfo->m_pSerialPortCom ->flush();
//设置波特率
pMainUartProtocolThreadInfo->m_pSerialPortCom ->setBaudRate((BaudRateType)ui->combo_box_baud->currentText().toInt());
//设置数据位
pMainUartProtocolThreadInfo->m_pSerialPortCom->setDataBits((DataBitsType)ui->combo_box_data->currentText().toInt());
//设置校验位
pMainUartProtocolThreadInfo->m_pSerialPortCom->setParity((ParityType)ui->combo_box_parity->currentText().toInt());
//设置停止位
pMainUartProtocolThreadInfo->m_pSerialPortCom->setStopBits((StopBitsType)ui->combo_box_stop->currentText().toInt());
pMainUartProtocolThreadInfo->m_pSerialPortCom->setFlowControl(FLOW_OFF);
pMainUartProtocolThreadInfo->m_pSerialPortCom ->setTimeout();
init_btn_enable(ui);
pMainUartProtocolThreadInfo->SetId(ui->line_edit_dev_id->text().toShort());
ui->btn_uart_close->setEnabled(true);
ui->btn_uart_open->setDisabled(true);
ui->btn_socket_open->setDisabled(true);
ui->btn_socket_close->setDisabled(true);
ui->combo_box_com->setDisabled(true);
ui->combo_box_baud->setDisabled(true);
ui->combo_box_data->setDisabled(true);
ui->combo_box_stop->setDisabled(true);
ui->combo_box_parity->setDisabled(true);
append_text_edit_test(QString::fromLocal8Bit("serial open success!"));
protocol_flag = PROTOCOL_UART;
}
else
{
pMainUartProtocolThreadInfo->m_pSerialPortCom->deleteLater();
pMainUartProtocolThreadInfo->m_bComStatus = false;
append_text_edit_test(QString::fromLocal8Bit("serial open failed!"));
}

  串口的通讯读写接口主要包含

  Write:数据发送接口

  Read:数据读取接口

 //设备写数据
int CUartProtocolThreadInfo::DeviceWrite(uint8_t *pStart, uint16_t nSize)
{
m_pSerialPortCom->write((char *)pStart, nSize);
return nSize;
} //设备读数据
int CUartProtocolThreadInfo::DeviceRead(uint8_t *pStart, uint16_t nMaxSize)
{
return m_pSerialPortCom->read((char *)pStart, nMaxSize);
}

  socket通讯的的初始化配置包含

  abort:中断当前的所有连接

  connectToHost:指定连接到指定的IP地址和端口

  waitForConnect:等待服务器的连接

  waitForBytesWritten:等待数据发送完成

  waitForReadyRead:等待数据可以接收

  此外,还包含和Socket通讯相关的

  信号:connect <-> 槽函数:slotConnected

  信号:diconnect <-> 槽函数:slotDisConnected

  信号:readyRead <-> 槽函数:dataReceived

  具体代码实现如下:

 void CTcpSocketThreadInfo::run()
{
bool is_connect;
int nLen;
int nStatus; m_pTcpSocket = new QTcpSocket();
m_pServerIp = new QHostAddress();
connect(m_pTcpSocket, SIGNAL(connected()), this, SLOT(slotConnected()));
connect(m_pTcpSocket, SIGNAL(disconnected()), this, SLOT(slotDisconnected()));
connect(m_pTcpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived())); for(;;)
{
if(m_nIsStop)
return; nStatus = m_pQueue->QueuePend(&SendBufferInfo);
if(nStatus == QUEUE_INFO_OK)
{
m_pTcpSocket->abort();
m_pTcpSocket->connectToHost(*m_pServerIp, m_nPort);
nLen = this->CreateSendBuffer(this->GetId(), SendBufferInfo.m_nSize,
SendBufferInfo.m_pBuffer, SendBufferInfo.m_IsWriteThrough);
is_connect = m_pTcpSocket->waitForConnected();
if(is_connect)
{
emit send_edit_test(QString("socket client ok"));
this->DeviceWrite(tx_buffer, nLen); //通知主线程更新窗口
emit send_edit_test(byteArrayToHexString("Sendbuf:", tx_buffer, nLen, "\n")); //等待发送和接收完成
m_pTcpSocket->waitForBytesWritten();
m_pTcpSocket->waitForReadyRead(); }
else
{
emit send_edit_test(QString("socket client fail\n"));
}
qDebug()<<"thread queue test OK\n";
}
}
}

  完成上述接口应用的实现,后续的逻辑就是涉及协议实现的部分,这部分的实现与协议相关的章节实现一致,具体如下,包含

  CreateSendBuffer:生成发送数据

  DeviceRead:数据接收

  DeviceWrite:数据发送

  CheckReceiveData:接收数据,并校验

  ExecuteCommand:执行指令的处理

  如此变完整实现了整个数据逻辑的框架,完成了从按键数据发送触发,协议数据发送和接收处理,接收界面显示的完整流程,最后实现如图所示的功能:

  至此,我们对于QT上位机界面的基本应用框架已经实现完毕,后续就是在该平台的基础上构建新的接口实现,满足不同应用的需求,因为本身这个系列是学习嵌入式Linux开发的,虽然后续肯定会在这份代码的基础上区完善上位机的应用,但对于上位机的说明目前就浅尝辄止了(毕竟本系列的实现并非嵌入式开发),不过在应用开发中,我对曾经学到的类的继承和派生,模板,lambda表达式都有了进一步的实践和应用,加深了相关的理解,也算是意义非凡了,至于代码的地址,参考第一章节的github开源地址中upper_app的内容,包含全部的代码实现。

嵌入式Linux学习笔记(六) 上位机QT界面实现和串口通讯实现的更多相关文章

  1. Linux学习笔记(六) 进程管理

    1.进程基础 当输入一个命令时,shell 会同时启动一个进程,这种任务与进程分离的方式是 Linux 系统上重要的概念 每个执行的任务都称为进程,在每个进程启动时,系统都会给它指定一个唯一的 ID, ...

  2. 嵌入式Linux学习笔记之第一阶段---基础篇

    嵌入式Linux学习分五个阶段 第一阶段: 01嵌入式环境搭建初期 02C语言语法概述 03C语言内存操作 04c语言函数 05linux基础 06gun基础 第二阶段: 01-linux之io系统编 ...

  3. c#上位机与三菱PLC(FX3U)串口通讯

    项目中会经常用到上位机与PLC之间的串口通信,本文介绍一下C#如何编写上位机代码 与三菱FX3U进行通讯 1. 第一种方法是自己写代码实现,主要代码如下: //对PLC的Y7进行置1 byte[] Y ...

  4. 嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发

    在成功构建了一个能够运行在开发板平台的系统后,下一步就要正式开始应用的开发(这里前提是有一定的C语言基础,对ARM体系的软/硬件,这部分有疑问可能要参考其它教程),根据需求仔细分解任务,可以发现包含的 ...

  5. 【转】嵌入式Linux学习笔记

    一  嵌入式系统定义: 应用于特定环境的硬件体系. 二  两样非常重要的能力: 1.  掌握各种新概念的能力 2.  调试的能力( 包括软件, 硬件 ) 三  需要的基础知识: 1.  操作系统理论基 ...

  6. 嵌入式Linux学习笔记(0)基础命令。——Arvin

    学习记录: 到今天为止ARM裸机开发学习进程:1.2.1-1.2.14 预科班知识Linux介绍学习进程:0.2.1-0.2.6 学习内容笔记: 学习了Linux的开发方式的优劣介绍 学习了常用文件夹 ...

  7. Linux学习笔记<六>

    进程与程序 1.子程序与父程序 PID是进程的ID,PPID是其父进程的ID 登录bash之后,就是获取了一个名为bash的PID,在这个环境上所执行的其他命令,就是其子程序 common@commo ...

  8. 嵌入式Linux学习笔记 NAND Flash控制器

    一.NAND Flash介绍和NAND Flash控制器的使用 NAND Flash在嵌入式系统中的作用,相当于PC上的硬盘 常见的Flash有NOR Flash和NAND Flash,NOR Fla ...

  9. 韦东山嵌入式Linux学习笔记02--如何给开发板烧录程序

    购买韦东山嵌入式开发板jz2440 v3会标配两根usb线和一根网线,OpenJtag需要单独购买, 我暂时还没买到该工具. 下面介绍usb烧录以及通过网线烧录程序. 1.usb烧录程序: 借助DNW ...

随机推荐

  1. (Python基础教程之十三)Python中使用httplib2 – HTTP GET和POST示例

    Python基础教程 在SublimeEditor中配置Python环境 Python代码中添加注释 Python中的变量的使用 Python中的数据类型 Python中的关键字 Python字符串操 ...

  2. php基本语法学习

    1.基本的 PHP 语法 PHP 脚本可以放在文档中的任何位置. PHP 脚本以 <?php 开始,以 ?> 结束: <?php// PHP 代码?>   2.简单的脚本-输出 ...

  3. js性能优化之---防抖函数

    使用场景 input的时时触发搜索功能 scroll事件的滚动条位置的监测 resize事件监听窗口变化等 举个栗子(input框时时触发搜索功能) 普通未防抖款 var textElement = ...

  4. 判断对象oStringObject是否为String

    1.操作符 (1)typeof操作符 格式:result=typeof variable 返回值: undefined 值未定义 boolean 布尔值 string 字符串 number 数值 ob ...

  5. Jquery动画,排队与并发

    一.事件绑定 1.鼠标事件:模拟触发 什么是模拟触发? 虽然没有点在按钮上,也可以触发按钮的事件处理函数. 如何:$元素.trigger("事件名") 即使没有点在指定的元素上,也 ...

  6. zz 关于插入意向间隔锁( insert intention gap lock)产生的死锁问题

    出处: http://www.cnblogs.com/sunss/p/3166550.html 昨天看到一个很有意思的死锁,拿来记录下: 环境:deadlock on 事务隔离级别: read com ...

  7. Java并发包2--ThreadLocal的使用及原理浅析

    ThreadLocal 是本地线程变量,是一个以ThreadLocal对象为key,任意对象为value的存储结构. 一.使用案例 1.定义线程类MyThread,代码如下: public class ...

  8. 可持续字典树 Perfect Security

    题目链接 题目大意:给你两个序列,第二个序列可以任意进行排列变换,然后由这两个序列一一异或得到答案序列,要求答案序列的字典序最小. 可持续字典树与第K大可持续线段树的区别主要在于每个节点上 ,它多了一 ...

  9. 【转】Mac系统常用快捷键大全

    Mac系统常用快捷键大全 通用 Command是Mac里最重要的修饰键,在大多数情况下相当于Windows下的Ctrl.所以以下最基本操作很好理解: Command + Z 撤销 Command + ...

  10. springboot使用Jwt处理跨域认证问题

    在前后端开发时为什么需要用户认证呢?原因是由于HTTP协定是不存储状态的,这意味着当我们透过账号密码验证一个使用者时,当下一个request请求时他就把刚刚的资料忘记了.于是我们的程序就不知道谁是谁了 ...