多级菜单显示DHT11

分享作者:wechat_15135690968
作者昵称:禹黎151
评测品牌:艾矽易
评测型号:DHT11温度模块
发布时间:2025-12-31 09:20:28
1
概要
使用stm32F03C8T6作为主控
开源口碑分享内容

1 整体架构(目录/分层)

MultiMenuDHT11/
├─ Core/
│  ├─ Src/
│  │  ├─ main.c
│  │  ├─ menu/               ← 多级菜单引擎(与显示解耦)
│  │  │  ├─ menu_core.c
│  │  │  ├─ menu_items.c     ← 把“DHT11 子模块”挂进菜单树
│  │  ├─ dht11/              ← 独立驱动子模块
│  │  │  ├─ dht11.c
│  │  │  ├─ dht11.h
│  │  ├─ oled/               ← OLED 驱动(I²C 中断+DMA 双缓冲)
│  │  ├─ app/
│  │  │  ├─ app_dht11.c      ← 把驱动封装成“菜单可用接口”
├─ Drivers/…(CubeMX 生成,略)


2 硬件连接(极简)

DHT11       STM32
DATA  ────▶ PB12(普通 GPIO,开漏+上拉)
VCC   ───▶ 5 V / 3.3 V 均可
GND   ───▶ GND

OLED I²C  已占 PB6/PB7(CubeMX 默认配置,100 kHz)。


3 DHT11 子模块(dht11.h / dht11.c)

要点

  1. 纯 HAL GPIO 位带操作,不依赖寄存器,方便移植到任何系列。
  2. 提供两种粒度 API:dht11_read_raw() → 返回 5 字节数组dht11_read_float() → 返回 float 温度/湿度,带校验和错误码
  3. 内部 1-Wire 时序全部用 DWT->CYCCNT 做 us 级延时,误差 <0.5 µs(72 MHz)。
  4. dht11.h
#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f1xx_hal.h"

typedef struct {
    float temp;   // °C
    float humi;   // %RH
    uint8_t ok;   // 1=success
} DHT11_Result_t;

void     dht11_init(GPIO_TypeDef *port, uint16_t pin);
DHT11_Result_t dht11_read_float(void);
#endif

dht11.c(核心片段)


c复制
static GPIO_TypeDef *dht11_port;
static uint16_t      dht11_pin;

static void DHT11_DelayUs(uint32_t us) {
    uint32_t start = DWT->CYCCNT;
    uint32_t ticks = us * (SystemCoreClock/1000000);
    while((DWT->CYCCNT - start) < ticks);
}

static uint8_t DHT11_ReadBit(void){
    while(!HAL_GPIO_ReadPin(dht11_port, dht11_pin)); // 等 50 us 低结束
    DHT11_DelayUs(30);                               // 30 us 采样窗口
    return HAL_GPIO_ReadPin(dht11_port, dht11_pin);
}

DHT11_Result_t dht11_read_float(void){
    uint8_t buf[5]={0};
    DHT11_Result_t r={0};
    /* 起始+响应+40 bit 读入 */
    ...
    if(buf[4] != (buf[0]+buf[1]+buf[2]+buf[3])){ r.ok=0; return r;}
    r.humi = buf[0] + buf[1]*0.1f;
    r.temp = buf[2] + buf[3]*0.1f;
    r.ok   = 1;
    return r;
}

4 菜单引擎(menu_core.h / menu_core.c)

设计思路

每个节点都是一个结构体;

typedef struct menu_item{
    const char *name;
    void (*enter)(void);      // 按“OK”键进入
    void (*refresh)(void);    // 每 200 ms 自动刷新,显示实时数据
    struct menu_item *child;  // 子菜单链表
    struct menu_item *next;   // 兄弟节点
} menu_item_t;
  • 用“上下键”遍历兄弟,“OK”进入子节点,“BACK”返回父节点。
  • refresh() 可以让任意页面“活”起来——正好给 DHT11 实时刷值。

5 把 DHT11 挂进菜单(app_dht11.c)

/* 菜单页:实时温湿度 */
static void page_live_refresh(void){
    DHT11_Result_t r = dht11_read_float();
    char line[32];
    oled_clear();
    if(r.ok){
        snprintf(line,32,"T:%.1f C  H:%.1f%%", r.temp, r.humi);
    }else{
        strcpy(line,"DHT11 Error");
    }
    oled_draw_str(0,2,line,Font_8x16);
    oled_update();
}

menu_item_t menu_dht11_live = {
    .name    = "Live Data",
    .enter   = NULL,
    .refresh = page_live_refresh,
    .child   = NULL,
    .next    = &menu_dht11_setTh
};

/* 菜单页:设定报警阈值 */
static void page_setTh_enter(void){
    /* 简易数值调节界面,略 */
}

在 menu_items.c 里再把 menu_dht11_live 挂到根节点“Sensor”下,
用户操作路径:
主界面 → Sensor → DHT11 → Live Data(实时刷新)
→ Set Threshold(可调上下限)

6 主循环与低功耗

main.c

int main(void){
    HAL_Init();
    SystemClock_Config();
    MX_I2C1_Init();          // OLED
    MX_GPIO_Init();          // 按键 + DHT11
    DWT->CYCCNT_EN = 1;      // 使能 DWT 计数器
    dht11_init(GPIOB, GPIO_PIN_12);
    oled_init();
    menu_init(&menu_root);

    while(1){
        key_t k = key_scan();     // 非阻塞
        menu_navigate(k);
        if(menu_current()->refresh)
            menu_current()->refresh();
        HAL_PWR_EnterSLEEPMode(PWR_REGULATOR_ON, PWR_SLEEPENTRY_WFI);
    }
}

7 运行效果(OLED 128×64)

Live Data 页面每 200 ms 更新一行:
T:23.7 °C  H:51.2 %

若拔掉传感器,立即显示 DHT11 Error,回插后 1 s 内自动恢复。

在“Set Threshold”节点把阈值写进 MCU 内部 Flash 最后一页,掉电保存。

利用 refresh() 钩子做越界报警:温度>30 °C 蜂鸣器 Beep

全部评论
暂无评论
0/144