前言
本人之前所做项目需要使用风速仪进行风速检测,市面上所购买的风速仪产品可以多通过串口与上位机进行通信,为满足后续需求且提高更高的灵活性,本人决定采用QT软件自主开发风速仪上位机,同时读取多组风速仪数据,提高检测效率。
风速仪
产品概览
本项目所使用风速仪为标智仪表GM8904(其余通过串口通信的风速仪同样适用该上位机),产品图片如下
通信协议
首先介绍该风速仪的接口定义。
红色 | DC电源输入(3.6V-5V) |
---|---|
绿色 | RXD |
白色 | TXD |
黑色 | GND |
概述
本协议是用于约定本风速仪(下位机)与上位机(电脑或智能设备)进行命令控制和数据交换的通信协议,采用UART规范,便于开发人员进行二次开发。
涉及范围
《协议》中规定的功能主要有:
1)上位机通过命令控制风速测量模块完成指定任务,如获取风速、风温数据等;
2)接收超时、接收的响应不是确认响应、接收校验和不正确、接收长度字节非法,则为本次通讯失败故障,可立即重发一次命令。
专用术语
上位机:管理风速测量模块的专用只能设备;
下位机:本风速仪;
RS232:一种2线串行通信标准,可支持双工串行近程通讯;
物理接口
串行通信口电气标准
模块用TTL电平(3.3V)以RS232协议方式与上位机通讯。
信息传输方式
采用异步方式,1个起始位,8个数据位,1个停止位,无校验位。
数据传输速率
支持波特率19200bps
通信方式
基本过程
上位机与模块是主从关系,上位机为主节点,模块为从节点。模块上电或复位后,处于接收状态,等待上位机发出命令,模块以命令指定命息格式上报上机位。
数据帧格式
长度 | 命令字 | 信息域 | 校验和 |
---|
其中:
长度:1个字节的整形数,等于长度+命令字+信息域+校验字字节数之和
命令:1个字节,命令的信息;
信息域:命令的参数,0个字节或多字节。
校验字:1个字节,一帧字节的累加和。
注意:上位机无信息域
其中命令字各位定义如下
模块上传数据帧的格式定义如下
通信举例
例子1:
上位机发送:
长度 | 命令字 | 信息域 | 校验和 |
---|---|---|---|
03 | 80 | 83 |
模块发送:
长度 | 命令字 | 1,2字节 | 3字节 | 4,5字节 | 校验和 |
---|---|---|---|---|---|
03 | 80 | 00 0C | 01 | 02 F3 | 8A |
该例子结果为:当前风速1.2m/s,蒲氏风级1级,风温为华氏75.5度,并重置最大大风速值,平均风速值,最小风速值为当前风速值。
例子2:
上位机发送:
长度 | 命令字 | 信息域 | 校验和 |
---|---|---|---|
03 | 51 | 54 |
模块发送:
长度 | 命令字 | 1,2字节 | 3字节 | 4,5字节 | 校验和 |
---|---|---|---|---|---|
03 | 51 | 01 0C | 08 | 00 FC | 6A |
该例子结果为:最大风速值26.8Km/h, 蒲氏8级风,摄氏25.2度。
上位机开发
经过分析可知,上位机需求为
- 可同时读写多个风速仪发送的数据
- 计算并显示当前测量半径下的风量与风速
- 支持检测风速类型的选择(最大风速,最小风速,当前风速,平均风速)
- 支持风速与风量单位的选择
- 支持检测圆环半径的选择
- 支持检测数据下载
界面设计
根据上述需求,将上位机设计包含风速显示区、端口选择区、风速选择区、单位选择区、风量显示区、圆环半径选择区、功能选择区、风速存储区,具体界面设计如下:
各功能区说明如下:
- 风速显示区:显示各端口采集的风速
- 端口选择区:选择各测量点对应端口
- 风速选择区:选择所需测量的风速类型,包括最大风速、最小风速、当前风速以及平均风速
- 单位选择区:选择风速单位,包括m/s与km/h
- 风量显示区:显示当前检测圆环半径下的平均风量
- 圆环半径选择区:选择检测圆环的半径
- 功能选择区:提供风速检测开始/停止、数据清空以及数据下载功能
- 风速存储区:存储所测量风速以待后续下载
QT代码开发
串口参数设置函数
根据风速仪的通讯协议,设计如下串口初始函数
/*-----------------串口选择----------------*/
bool MyWidget::port_config()
{
portA.setPortName(ui->comboBox_A_com->currentText());
portAA.setPortName(ui->comboBox_AA_com->currentText());
portB.setPortName(ui->comboBox_B_com->currentText());
portBB.setPortName(ui->comboBox_BB_com->currentText());
portA.setBaudRate(QSerialPort::Baud19200);
portA.setParity(QSerialPort::NoParity);
portA.setDataBits(QSerialPort::Data8);
portA.setStopBits(QSerialPort::OneStop);
portA.setFlowControl(QSerialPort::NoFlowControl);
portAA.setBaudRate(QSerialPort::Baud19200);
portAA.setParity(QSerialPort::NoParity);
portAA.setDataBits(QSerialPort::Data8);
portAA.setStopBits(QSerialPort::OneStop);
portAA.setFlowControl(QSerialPort::NoFlowControl);
portB.setBaudRate(QSerialPort::Baud19200);
portB.setParity(QSerialPort::NoParity);
portB.setDataBits(QSerialPort::Data8);
portB.setStopBits(QSerialPort::OneStop);
portB.setFlowControl(QSerialPort::NoFlowControl);
portBB.setBaudRate(QSerialPort::Baud19200);
portBB.setParity(QSerialPort::NoParity);
portBB.setDataBits(QSerialPort::Data8);
portBB.setStopBits(QSerialPort::OneStop);
portBB.setFlowControl(QSerialPort::NoFlowControl);
portA.open(QSerialPort::ReadWrite);
portAA.open(QSerialPort::ReadWrite);
portB.open(QSerialPort::ReadWrite);
portBB.open(QSerialPort::ReadWrite);
return portA.isOpen()&&portAA.isOpen()&&portB.isOpen()&&portBB.isOpen();
}
各参数选择函数
通过反馈量确定当前选择的选项
/*-----------------测量模式选择-----------------*/
int MyWidget::wind_speed_choose()
{
if(ui->radioButton_speedave->isChecked() == true)
{
ui->radioButton_speedcur->setChecked(false);
ui->radioButton_speedmax->setChecked(false);
ui->radioButton_speedmin->setChecked(false);
return 2;
}
else if(ui->radioButton_speedmax->isChecked() == true)
{
ui->radioButton_speedcur->setChecked(false);
ui->radioButton_speedave->setChecked(false);
ui->radioButton_speedmin->setChecked(false);
return 3;
}
else if(ui->radioButton_speedmin->isChecked() == true)
{
ui->radioButton_speedave->setChecked(false);
ui->radioButton_speedmax->setChecked(false);
ui->radioButton_speedcur->setChecked(false);
return 4;
}
else
{
ui->radioButton_speedcur->setChecked(true);
ui->radioButton_speedave->setChecked(false);
ui->radioButton_speedmax->setChecked(false);
ui->radioButton_speedmin->setChecked(false);
return 1;
}
}
/*-----------------风速单位选择-----------------*/
int MyWidget::wind_speedunit_choose()
{
if(ui->radioButton_km_h->isChecked() == true)
{
ui->radioButton_m_s->setChecked(false);
return 2;
}
else
{
ui->radioButton_m_s->setChecked(true);
ui->radioButton_km_h->setChecked(false);