【Qt开发】串口协议的上位机项目实战 GUI_2355

【Qt开发】串口协议的上位机项目实战 GUI_2355

【从0自学Qt】串口协议上位机项目实战 GUI

串口协议

串口配置

波特率 115200
数据长度 8位
停止位 1
校验位 无
起始位 1

帧构成

在这里插入图片描述

帧说明

帧起始: 两个字节,固定值0x23 0x55。
数据域长度: 两个字节,表示数据域的长度。
设备地址: 两个字节,默认值 0x52 0x07。
数据域: 功能码和数据。其中功能码COM一个字节,数据DATA可有可无,具体看功能码定义。
帧校验: 两个字节,按位累加,从帧头开始到数据结束。
帧结束: 两个字节,固定值0x0D 0x0A。
数据域为小端格式,其他为大端格式。

结构体定义

typedef struct	
{	
uint8_t START[2];	起始位
uint8_t BCNT[2];	数据长度,表示从ADDR字节的后一字节开始,到校验码的前一字节为止,数据的长度;也是命令位+数据位的长度。
BCNT=len(COM+DATA[])
uint8_t ADDR[2];	地址位
uint8_t COM;	命令字节
uint8_t DATA[25];	数据字节
uint8_t CHK[2];	校验码 表示校验码前所有字节之和。
CHK=START0+START1+BCNT0+BCNT1+ADDR0+ADDR1+COM+DATA[]
uint8_t STOP[2];	停止位
}GUI_Struct;	

在这里插入图片描述

如接收的数据为:
23 55 00 04 52 07 01 AB 12 45 XX 0D 0A
起始位23 55
数据长度4
ADDR为52 07
命令为01
数据字节AB 12 45
校验码XX
停止位 0D 0A

数据格式

常见的有以下几种数据格式
在这里插入图片描述

字符串则由单个字符逐一拼接而成;其他数据格式则另外规定。
其中,所有多字节数据均为小端格式,在发送数据时,先发低位再发高位。
比如:
浮点数里面 如 0x40 80 00 00表示4.0f,其中 00是低位 40是高位:
以小端格式发送,则依次发送0x00 0x00 0x80 0x40。
在采用C语言对数据进行处理时,建议遇到浮点数接收时,直接采用memcpy()函数从地址上进行复制,复制字节数为4或8(双精度浮点);同样的,在发送浮点数数据时,也采用memcpy()函数将变量值复制到要发送的DATA数组中,避免大小端格式出错。
在执行某些命令时,部分变量类型需要扩充到多字节,如bool4类型。其本质就是把bool类型增加了3个字节,且高位字节全为0。在进行处理时,可以直接作为uint32_t 类型进行判断,0为false,非0为true,软件上强制将true(非0的数据)转换成1。

协议应用

功能响应和命令的执行

下位机作为从机设备,上位机为主机设备,下位机接收到正确的数据,且设备地址位与从机地址相同时,才会正常响应。从机响应时,发出的数据中的设备地址为从机自身的地址。

上位机响应

部分命令中,上位机在接收到从机发送过来的命令后,如果命令正确响应,则回复一条相同命令字但数据域长度为1(数据内容为空)的命令。
上位机具有发送广播命令的功能,其命令字为0x00,数据域长度为1。下位机接收到广播命令后,立即返回相同的命令。
主机通常是对从机进行控制、读取、写入的设备,通常不需要对从机上报的命令进行回复和响应,特殊命令规定的除外。

下位机响应

从机也只响应一种或几种命令,也可以根据需要只上报数据。
为了配合上位机使用,从机必须设置图表命令用于上报数据(见后文)。

另外,建议从机可以设置的几种常见响应状态如下:
(1)正常响应并执行命令时,接收到请求的功能码后会以相同的功能码返回;或带上相应的DATA数据返回。
响应功能码 = 请求功能码。
(2)异常响应时,接收到请求功能码后会以错误码0xFF作为功能码返回,无DATA数据。
此处的异常响应包括设置参数错误(超出量程等等),或功能码对应的后续DATA数据格式错误。
异常功能码 = 0xFF。
(3)广播命令响应时,接收到请求的广播命令码后会以相同的功能码返回。
广播命令码 = 0x00。
(4)无法执行命令响应时,接收到请求的命令码后会以广播命令的功能码返回。
无法执行命令即通信协议格式没有问题,但功能码未定义。
无法执行命令码 = 广播命令码。
(5)当通信协议物理层异常时,不会有任何响应,包括串口协议出错,通信格式不正确,校验码有误,设备地址不正确等等,都不会有任何响应。

图表命令(0x01)

上位机和下位机都应支持此命令。
下位机在进行数据上报时,通过图表命令发送数据。
图表命令数据长度为17,数据内容为1字节COM、16字节DATA,其中DATA为4个4字节的数据,目前支持发送int32、uint32、float、bool类型,且按四字节对齐。
当发送更短字节的数据时,如uint8格式数据,需要将其高位置为0,也就是另外三个高位字节都为0;同理bool类型。
在bool类型的判断中,0x00000000为false,其他为true。
以下展示的是一条图表命令的数组,其中第一变量为float类型,第二变量为int32类型,第三变量为uint8类型,第四变量为bool类型。
23 55 00 11 52 07 01 00 00 80 3F 10 20 30 AA 00 00 00 FF 00 00 00 01 03 AC 0D 0A

下位机图表命令

下位机进行数据上报时,可以选择采用此命令进行上报,每次最多发送四个不同类型的数据。若数据个数不足四个,则将后续数据统一设置为0。
以下展示的是一条图表命令的数组,其中第一变量为float类型,第二变量为int32类型,第三变量、第四变量未定义。
23 55 00 11 52 07 01 00 00 80 3F 10 20 30 AA 00 00 00 00 00 00 00 00 02 AC 0D 0A

上位机图表命令

上位机接收到图表命令后,若解析成功,则立即返回以图表命令为COM,数据长度为1的数组,并通过解析后进行数据绘图和记录。

上位机基本操作

在这里插入图片描述

如图为上位机主界面。
所有操作前都需要执行打开串口操作,而在2355协议的基本配置中,需要设置好全局地址,这样才能与相应地址的下位机进行交互。
通过设置数据域长度、命令字节、以及数据字节,可以生成2355的数组,打开串口后通过“发送数组”进行发送。

上位机信息显示窗口

主要有三个用于显示信息的窗口,Log、COM、Sent,分别对应LOG信息、串口接收和主动发送、主机回应窗口。
其保存的文件路径为目录“./Save”下,每次运行时会按时间戳新建三个txt文件,在此之后所有的信息都会被记录在此文件下。可以通过按钮进行手动保存,若退出程序,则自动保存。
Log信息窗口可以设置最大显示行数,设置为0则为无限制。
COM信息窗口可以设置最大接收超时时间和时间戳显示。最大接收超时时间仅控制做2355协议解析时的等待时间,与响应时间无关,无论设置多大都不影响协议的解析,仅在开启调试模式时能够观察得到现象变化。时间戳显示控制COM和Sent信息窗口的时间戳显示,以及是否主动显示串口主动发送的信息。
Sent信息窗口可以选择多线程执行命令,该线程用于命令解析成功后,执行命令的方式。

图表命令配置

在图表命令配置中,选择变量类型后,才会进行图表数据的解析和绘图。
绘制的图标横坐标为数据读取次数,纵坐标为值,范围为最大至最小值的10%范围。
图表可以通过左键框选放大和右键缩小,并设置卡尔曼滤波相关参数。卡尔曼滤波有三种状态:开启、半开启、全开启,分别对应无滤波、一级滤波、二级滤波,同时可以设置Q/R值。
放大显示图表则会在新窗口中单独打开图表,可以进行更新、保存操作,保存路径为目录“./Save/Charts”下,每次保存时以秒为最小单位进行时间戳命令保存。
图表设置中,需要选择好数据的类型,如果选择None则不进行绘图和记录操作。

附录:C语言到C++的入门知识点(主要适用于C语言精通到Qt的C++开发入门)

C语言与C++的不同

C语言是一门主要是面向工程的语言
C++则是面向对象

C语言中 某些功能实现起来较为繁琐
比如结构体定义:

一般写作:

typedef struct stu_A
{
}A;

也可以写作:

typedef struct 
{
}A;

但 大括号后面的名称是不可省去的

不过 C++的写法就比较简单
除了支持上述写法外

也支持直接声明

typedef struct A
{
}

另外 C++是完全支持C语言库和语法的
不过C++里面的库也有些很方便的高级功能用法 只不过实现起来可能不如C的速度快

再者 C语言与C++的编译流程不一样
C语言没有函数重载 所以给编译器传参就是直接传函数名称
但是C++除了传函数名称外 还会穿函数的参数、类型等等 以实现函数重载

C++中写C语言代码

上文提到 C++可以完全兼容C的写法
但是编译流程也还是不一样
所以如果在编译层面进行C语言代码编译 则通常用以下方法:

extern "C"
{
...
}

表面大括号内的内容用C的方法进行编译

另外 如果还是用C++的编译器 但要实现C语言函数 则需要用到C语言的库

在C语言中 我们一般用如下方法导入库

#include <stdio.h>

此方法同样适用于C++ 但是C++可以更方便的写成去掉.h的方式
比如:

#include <iostream>

在C++中 为了调用C语言的库 可以采用在原库名称前加一个"c"的方式导入
如:

#include <cstdio>

这样就可以使用printf等函数了 甚至比C++的std方法更快

C语言到C++的知识点

在这里插入图片描述

Qt开发中需要了解的C++基础知识

namespace

C++面向对象的特性下诞生的一个名称
表示某个函数、变量在某个集合下 用作namespace
比如 <iostream>库中的关键字cin在std下 则写作std::cin
std就是namespace
::表示某空间下的某某
前面是空间名称 后面是变量、函数名称

using namespace可以告诉编译器以下都用xx名称空间
比如:

using namespace std;
cout<<"a";

如果没有告诉编译器所使用的空间名称 则要写成:

std::cout<<"a";

同样 可以自定义某一段代码属于哪个空间:

namespace xx
{
...
}

输入输出

在C++中 用iostream作为输入输出流的库

#include <iostream>

用cin和cout关键字进行输入和输出
如:

using namespace std;
int a=0;
cin>>a; //输入到a

cout<<a;  //输出a

类比scanf和printf
同样 还有一个关键字endl表示换行
cout和cin的传参是不固定的
由编译器自行裁定

字符串类型

在C语言中 常用char *表示字符串
但是在C++中 可以直接用string类型
比如:

char * s="456";
string str="123";

由于cout的特性 这两种字符串都可以直接打印
但如果使用C语言中printf的打印方式时 采用%s方式打印字符串 则不能传入string类型

class类

C++的核心就是class
同Python等支持面向对象的语言一样
可以理解成一个支持函数、继承、自动初始化、销毁的结构体
在class类中 有private私有、public公有变量
前者只能内部访问 后者可以外部调用使用
如:

class A
{
public:
int a;
private:
int b;
}

a可以用A.a的方式方位 b则外部无法访问

构造函数和析构函数(解析函数)

构造函数可以理解成对类的初始化 反之析构函数则是退出时进行销毁前的函数
两者需要与类的名称相同 析构函数则在前面加一个~表示非
如:

class A
{
public:
int a;
A();
~A();
private:
int b;
}

A::A()
{
...
}

A::~A()
{
...
}

构造函数可以定义传参 析构函数则不行

类的继承

如果有两个类A和B 想让A里面包含B 则可以写作继承的写法
继承后 A类的变量可以直接调用B下面的成员
如:

class B
{
int b;
}
class A: public B
{
int a;
}

在定义A后 可以访问到B的成员b 当然 继承也可以私有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式拳铁编曲MikeZhou

光电帝国,光联万物!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值