Funpack3-3基于X-NUCLEO-IKS4A1板卡的环境数据采集系统
该项目使用了X-NUCLEO-IKS4A1,实现了环境变量采集的设计,它的主要功能为:对温度、湿度、气压采集并实现上位机采集。。
标签
嵌入式系统
Funpack活动
Python
传感器
X-NUCLEO-IKS4A1
sll
更新2024-07-08
80

X-NUCLEO-IKS4A1

项目介绍

  • 题目:使用板卡上的触摸按键,实现点按和左右滑动,实现传感器选择和切换,并将数据发送到上位机,功能选择的可视化也在上位机完成。
  • 项目实现
    • 上位机界面
      • 显示内容包括数据显示、传感器控制、数据曲线。
      • 数据清除按钮可清除接收的数据和数据曲线。
      • 下拉选项可以选择不同的数据,以控制MCU的数据采集和上位机的数据显示。关闭后,mcu向上位机发送的数据保持0,同时上位机曲线停止更新。开启后,MCU正常向上位机发送当前环境变量,曲线继续给更新。
      • 527293661962892.png
      • 458453393419844.png
    • 单片机实现
      • 使用X-NUCLEO-IKS4A1 和 Nucleo-64(G0B1RE)配合实现环境温度、湿度、气压的采集,并通过Uart将数据通过自定义协议发送至上位机或接收来此上位机的控制指令。
      • 使用滑动电极实现单击、左右滑动的识别,控制传感器的启停。
      • 左/右滑选择传感器(温度->湿度->气压)。
      • 单击左侧电极,控制传感器关闭,单击右侧电极,控制传感器开启,并同时向上位机发送指令。265275463306929.png

硬件介绍

上文提到使用的硬件为X-NUCLEO-IKS4A1 和 Nucleo-64(G0B1RE),X-NUCLEO-IKS4A1 板卡中涉及到的板卡有很多,此次设计主要包括环境变量采集和电极电荷采集。

  • STTS22H:低电压,超低功耗,0.5°C 精度的温度传感器(–40 °C 到 + 125 °C)
  • LPS22DF:超紧凑型压阻绝对压力传感器,可用作数字输出气压计,具有更低功耗和更小压力噪声。该器件包含传感元件和 IC 接口,该接口通过 I²C、MIPI I3CSM 或 SPI 接口实现传感元件与应用的通信,同时该器件也支持用于数据接口的广泛 Vdd IO。
  • SHT40-AD1B:数字式湿度和温度传感器
    • 相对湿度精度:+-1.8% RH
    • 温度精度:+-0.2°C
    • 工作范围:0~100% rh,-40~125°C
  • LSM6DSV16X:采用系统级封装,包含一个 3D 数字加速度计和一个 3D 数字陀螺仪(采用三核芯架构,通过专用的配置、处理和滤波功能在 3 个独立通道上处理加速度数据、角速度数据)。常开惯性模块,具有始终开启功能的同时提供低功耗特性,配嵌入式机器学习核心和 Qvar 静电传感器,嵌入式 Qvar(静电传感器)支持用户界面功能(单击、双击、三击、长按、左 / 右 - 右 / 左滑动)
  • 主控制器为STM32G0B1RE,板载STLINKV2,具有Arduino接口可以直接与X-NUCLEO-IKS4A1板卡连接使用。

设计思路

  • 为达到任务1设计要求,使用pyqt工具设计pc上位机,和单片机之间通过UATR通信交互数据。
  • 对于周期行的输出发送,采用消息列队保证数据的准确性。
  • 对于滑动电极的数据值判定,采用迟滞比较的方式,对数据滤波(-1,0,1);采用数字电路中序列检测的思想对点按、滑动、双击动作判断。

86044316114606.png

程序流程图

341913329491641.png

设计说明

单片机程序设计

  • 初始化,相关外设(I2C、Uart、GPIO),所使用的传感器。
    HAL_Init();

    SystemClock_Config();

    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART2_UART_Init();
    MX_TIM2_Init();

    Serial_Receive_Init();
    Serial_Send_Init();

    IKS4A1_ENV_SENSOR_Init(IKS4A1_STTS22H_0, ENV_TEMPERATURE);
    IKS4A1_ENV_SENSOR_ReadID(IKS4A1_STTS22H_0, &ID);
    IKS4A1_ENV_SENSOR_Disable(IKS4A1_STTS22H_0, ENV_TEMPERATURE);

    IKS4A1_ENV_SENSOR_Init(IKS4A1_LPS22DF_0, ENV_PRESSURE);
    IKS4A1_ENV_SENSOR_ReadID(IKS4A1_LPS22DF_0, &ID);
    IKS4A1_ENV_SENSOR_Disable(IKS4A1_LPS22DF_0, ENV_PRESSURE);

    IKS4A1_ENV_SENSOR_Init(IKS4A1_SHT40AD1B_0, ENV_HUMIDITY);
    IKS4A1_ENV_SENSOR_ReadID(IKS4A1_SHT40AD1B_0, &ID);
    IKS4A1_ENV_SENSOR_Disable(IKS4A1_SHT40AD1B_0, ENV_HUMIDITY);

    (void)IKS4A1_MOTION_SENSOR_Init(IKS4A1_LSM6DSV16X_0, MOTION_ACCELERO);
    (void)IKS4A1_MOTION_SENSOR_SetOutputDataRate(IKS4A1_LSM6DSV16X_0, MOTION_ACCELERO, 50.0); /* Algorithm frequency >= 50Hz */
    (void)IKS4A1_MOTION_SENSOR_SetFullScale(IKS4A1_LSM6DSV16X_0, MOTION_ACCELERO, 4 );/* FS = <-4g, 4g> */

  • 数据发送任务
    此函数10ms周期执行,首先将fifo中数据取出,若fifo中无数据,则返回失败,此次数据不发送。
    void Serial_Send_Data(void)
    {

    static sSendData_t tempbuf = {0};

    if(QUEUE_OK == Queue_Pop(&gSerial_Send_Queue, &(tempbuf.data)))
    {
    tempbuf.head = 0x5f;
    tempbuf.tail = 0xf5;
    HAL_UART_Transmit_DMA(&huart2, (uint8_t *)&tempbuf, sizeof(sSendData_t));
    }
    }

  • 数据接收任务
    此函数在串口空闲中断中执行,仅将数据简单处理,并置相应的标志位。
    void Serial_Receive_IDLECallback(UART_HandleTypeDef *huart)
    {
    uint8_t i = 0;
    uint32_t Receive_Len = 0;
    sReceive_Data_t ReceiveData = {0};
    if(RESET != __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))
    {
    HAL_UART_DMAStop(huart); // 停止DMA传输

    USART2->RDR; // 必须要读取数据寄存器中的数据,否则不能清除标志
    Receive_Len = RECEIVE_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); // 接收的数据长度 = DMA接收通道总长度 - 剩余长度
    for(i = 0; i<Receive_Len; i++)
    {
    if(gReceive_Buffer[i] == 0xF6 && gReceive_Buffer[i+4] == 0x6F)
    {
    ReceiveData.funcode = gReceive_Buffer[i+1];
    ReceiveData.data = gReceive_Buffer[i+2];
    Receive_Data_Deal(ReceiveData);

    }
    }
    memset(gReceive_Buffer, 0, Receive_Len);

    /* 调试用途,把接收的数据发送 */
    // HAL_UART_Transmit_DMA(huart,gReceive_Buffer, Receive_Len);
    HAL_UART_Receive_DMA(&huart2, gReceive_Buffer,1024);
    __HAL_UART_CLEAR_IDLEFLAG(huart); // 清楚空闲中断标志位
    }
    }


    static void Receive_Data_Deal(sReceive_Data_t RecData)
    {
    switch(RecData.funcode)
    {
    case code_none:

    break;

    case code_temp:
    if(RecData.data == 1)
    {
    IKS4A1_ENV_SENSOR_Enable(IKS4A1_STTS22H_0, ENV_TEMPERATURE);
    // IKS4A1_ENV_SENSOR_Init(IKS4A1_STTS22H_0, ENV_TEMPERATURE);
    flag_temp = 1;
    }
    else
    {
    IKS4A1_ENV_SENSOR_Disable(IKS4A1_STTS22H_0,ENV_TEMPERATURE);
    env_temp = .0;
    flag_temp = 0;
    }
    break;

    case code_hum:
    if(RecData.data == 1)
    {

    IKS4A1_ENV_SENSOR_Enable(IKS4A1_SHT40AD1B_0, ENV_HUMIDITY);
    // IKS4A1_ENV_SENSOR_Init(IKS4A1_SHT40AD1B_0, ENV_HUMIDITY);
    flag_hum = 1;
    }
    else
    {
    IKS4A1_ENV_SENSOR_Disable(IKS4A1_SHT40AD1B_0,ENV_HUMIDITY);
    env_humdity = .0;
    flag_hum = 0;
    }
    break;

    case code_pres:
    if(RecData.data == 1)
    {
    IKS4A1_ENV_SENSOR_Enable(IKS4A1_LPS22DF_0, ENV_PRESSURE);
    // IKS4A1_ENV_SENSOR_Init(IKS4A1_LPS22DF_0, ENV_PRESSURE);
    flag_press = 1;
    }
    else
    {
    IKS4A1_ENV_SENSOR_Disable(IKS4A1_LPS22DF_0,ENV_PRESSURE);
    env_pressure = .0;
    flag_press = 0;
    }

    break;

    default:
    break;

    }
    }

  • 滑动电极数据采集任务
    此函数参考ZERO给出的解决方案,使用迟滞比较器对数据做滤波并将数据抽象为(-1、0、1)。并通过序列检测,对动作进行识别。


上位机软件设计说明

概述

该程序使用 PyQt6 和 pyqtgraph 创建一个图形用户界面 (GUI) 应用程序,以实时接收和显示来自串口设备的传感器数据。程序包含多个类和方法,负责串口通信、数据处理、GUI 更新和用户交互。

功能描述

  1. 串口通信:
    • 通过串口读取数据帧,解析温度、湿度和压力传感器的数据。
    • 处理控制帧以打开或关闭传感器。
  2. 数据展示:
    • 在文本框中显示接收到的数据。
    • 实时绘制温度、湿度和压力数据曲线。
  3. 传感器控制:
    • 用户可以通过 GUI 控制传感器的状态(打开/关闭)。
  4. 信号和槽机制:
    • 使用 PyQt 的信号和槽机制在不同线程之间传递数据更新和控制指令。

类和方法

1. Communicate 类

定义了三个信号:

  • text_signal:用于更新文本显示。
  • plot_signal:用于更新数据绘图。
  • control_frame_signal:用于处理控制帧的更新。
2. DataReceiverThread 类

继承自 threading.Thread,负责从串口读取数据并处理。

  • 初始化
    def __init__(self, ser, communicator):
    初始化串口和信号通信对象。
  • 运行方法
    def run(self):
    持续读取串口数据并调用 process_buffer 处理数据。
  • 数据处理
    def process_buffer(self, buffer):
    解析数据帧,提取传感器类型和数值,发送相应的信号。
  • 停止线程
    def stop(self):
    停止线程运行。
3. PlotWindow 类

继承自 QWidget,用于绘制传感器数据曲线。

  • 初始化
    def __init__(self, title, unit, parent=None):
    设置绘图窗口标题、单位和布局。
  • 数据更新
    def update_data(self, value):
    更新绘图数据。
  • 绘图更新
    def update_plot(self):
    定时刷新绘图窗口。
  • 清除数据
    def clear_data(self):
    清除绘图数据。
4. MainWindow 类

继承自 QMainWindow,主应用窗口,包含多个布局和控件。

  • 初始化
    def __init__(self, ser):
    初始化窗口、布局、控件和信号连接。
  • 创建数据展示组
    def create_data_group_box(self):
    创建用于显示接收数据的文本框和清除按钮。
  • 创建传感器控制组
    def create_sensor_group_box(self):
    创建用于控制传感器的下拉菜单、状态切换按钮和复选框。
  • 创建绘图组
    def create_plot_group_box(self):
    创建用于显示温度、湿度和压力曲线的绘图窗口。
  • 文本更新槽
    def update_text_edit(self, text):
    更新文本框内容。
  • 绘图数据更新槽
    def update_plot_data(self, value, data_type):
    根据传感器类型更新相应的绘图数据。
  • 传感器开关
    def toggle_sensor(self):
    发送控制帧以切换传感器状态,并更新 UI。
  • 控制帧更新槽
    def update_control_frame(self, control_info):
    处理接收到的控制帧,更新传感器状态和 UI。
  • 清除数据
    def clear_data(self):
    清除所有显示数据和绘图数据。
  • 关闭事件
    def closeEvent(self, event):
    窗口关闭时停止数据接收线程。

主函数

def main():
try:
ser = serial.Serial('COM8', 115200, timeout=0)
except serial.SerialException as e:
QMessageBox.critical(None, "串口错误", f"打开串口失败: {e}")
sys.exit()

app = QApplication(sys.argv)
main_window = MainWindow(ser)
main_window.show()
sys.exit(app.exec())
  • 尝试打开串口。
  • 创建应用程序和主窗口。
  • 进入主事件循环。

小结

该程序通过串口接收传感器数据,并通过 PyQt6 实现图形界面,实时显示和控制传感器状态。程序结构清晰,采用多线程和信号槽机制确保数据处理和界面更新的实时性和响应性。

总结

  • STM的新传感器使用体验非常高,提供传感器库函数调用非常方便。
  • pyqt工具使用练习。
  • 协议练习,但是此次设计仅使用帧头帧尾做回数据校验。
  • 不足:
    • 设计之初想要一致MODBUS RTU协议作为数据的发送与接收,参考了一些代码和文档,但是在下手写代码的时候,问题还是有很多,最近晚上时间紧张,便没有继续。


附件下载
main.py
funpack_STM32Code.zip
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号