{"code":1,"msg":"成功","time":"1793358080","data":{"title":"〖2026嵌入式大赛〗基于 RT-Thread 的大棚环境监测终端","content":"## 一、项目概述\r\n\r\n### 1.1 背景\r\n\r\n大棚种植中，空气温度、空气湿度和光照强度是三项最基础的环境参数。温度过高会造成作物徒长与落花，湿度过高易诱发灰霉病、霜霉病，光照不足则直接影响光合速率与产量。小型大棚和家庭种植场景往往没有预算上环境监控系统：市售方案多为\"网关 + 无线节点 + 云平台\"的架构，需要联网、需要服务器、需要持续订阅，硬件成本和维护门槛都不低。\r\n\r\n\r\n### 1.2 目标与设计原则\r\n\r\n| 原则 | 具体做法 |\r\n| --- | --- |\r\n| **低成本** | 只使用开发板板载资源 + 两支低价传感器（DHT11、BH1750），不购买 Wi-Fi 模块、服务器、ADC 模块、存储卡 |\r\n| **离线可用** | 全部数据显示、交互、报警在设备本地完成，不依赖网络和云平台 |\r\n| **响应可靠** | 触摸、采样、显示、报警各司其职，任何一路阻塞不影响其它功能 |\r\n| **可诊断** | 提供完整 MSH 命令集，现场可查采样有效位、触摸坐标、UI 耗时、线程状态 |\r\n\r\n\r\n### 1.3 设计范围\r\n\r\n| 模块 | 实现内容 |\r\n| --- | --- |\r\n| 传感器采集 | DHT11 单总线时序解码、BH1750 I²C 读取，双通道独立有效位 |\r\n| 数据处理 | 互斥保护的全局快照、双消息队列分发、60 点 RAM 环形历史 |\r\n| 报警 | 连续 3 次确认、温湿度回差、PWM 蜂鸣器驱动 |\r\n| 界面 | 四页面缓存式 LVGL 界面、曲线、统计、阈值设置 |\r\n| 持久化 | 阈值经后台线程异步写入 LittleFS `\/data` |\r\n| 可维护性 | 12 条 MSH 诊断命令、模块化分层、集中式配置开关 |\r\n\r\n---\r\n\r\n## 二、功能介绍\r\n\r\n### 2.1 四页面触摸界面\r\n\r\n界面为 480×272 浅色主题，固定顶部状态栏 + 底部导航栏，四个页面**一次性创建后缓存**，切换时只做隐藏\/显示，不重建控件。\r\n\r\n#### Home — 实时数据\r\n\r\n- 三张数据卡片：空气温度、空气湿度、光照强度，各自独立配色。\r\n- 每张卡片下方显示该通道当前生效的**报警上限**。\r\n- 底部摘要面板：显示系统状态文案（正常 \/ 超限 \/ 传感器不可用）与采样序号。\r\n- 顶部右侧状态徽标：`WAITING` \/ `NO DATA` \/ `PARTIAL` \/ `ALARM` \/ `LIVE`。\r\n\r\n#### Trends — 曲线\r\n\r\n- 三个按钮切换查看 **Temperature \/ Air humidity \/ Light**。\r\n- 三条曲线**不共用纵轴**：温湿度单位不同，光照量级差三个数量级，强行挤在一张图上既不可读也会超出 LVGL 坐标数值范围。\r\n- 光照曲线内部按 100 lux 缩放绘制，但坐标刻度标签还原为真实 lux 值。\r\n- 保留最近 **60 次采样**的 RAM 环形缓冲。离开曲线页期间**仍在持续积累**，回来能看到完整窗口。\r\n\r\n#### Report — 统计\r\n\r\n- 三通道的**平均值、最小值、最大值**，以及报警样本计数。\r\n- 统计基于同一份 60 点环形缓冲，随采样滚动更新。\r\n\r\n#### Menu — 阈值设置\r\n\r\n- 三个通道的上限值，各配 `-` \/ `+` 按钮。\r\n- 步长：温度 1 ℃、湿度 1 % RH、光照 100 lux。\r\n- `Cancel` 放弃草稿返回首页；`Apply limits` 提交。\r\n- 提交后先在 RAM 生效（UI 与报警立即使用新值），再由后台线程异步写入 Flash。\r\n\r\n### 2.2 报警机制\r\n\r\n- **上限报警**：温度、湿度、光照各自独立判断，可同时触发。\r\n- **连续确认**：新报警需要**连续 3 次**满足条件的采样才成立，避免单次噪声误报。\r\n- **回差（Hysteresis）**：温度与湿度带 2 ℃ \/ 2 % RH 回差，即报警在超过上限时触发，但要降到\"上限 − 回差\"以下才解除，防止在阈值附近反复通断。\r\n- **蜂鸣器输出**：任一通道报警时，板载 BZ1 以 4 kHz \/ 50 % 占空比鸣响；全部解除后关闭。\r\n\r\n### 2.3 数据有效性设计\r\n\r\n每个采样带一个 `valid_mask` 位域：\r\n\r\n| 位 | 含义 |\r\n| --- | --- |\r\n| `0x01` | 空气温度有效 |\r\n| `0x02` | 空气湿度有效 |\r\n| `0x04` | 光照有效 |\r\n\r\n- 温湿度通常一起有效（`0x03`），三项全有效为 `0x07`。\r\n- 某通道无效时，界面显示 `--`，绝不显示占位数字。\r\n- 连续约 8 秒（4 个采样周期）没有新采样，判定为**数据过期**，实时读数清空并回到 `NO DATA`。\r\n\r\n### 2.4 MSH 诊断命令\r\n\r\n现场调试不需要额外工具，串口接上即可：\r\n\r\n| 命令 | 用途 |\r\n| --- | --- |\r\n| `gh_status` | 采样序号、有效位、报警位、消息丢弃计数、当前阈值 |\r\n| `gh_sensor_test` | 立即读一次 DHT11 + BH1750，打印各通道有效位与数值 |\r\n| `gh_dht11_test` | 单独走 DHT11 读取路径 |\r\n| `gh_threshold` | 查看阈值 |\r\n| `gh_threshold 350 850 80000` | 设置阈值（35.0 ℃ \/ 85.0 % RH \/ 80000 lux） |\r\n| `gh_buzzer_test 500` | 蜂鸣器鸣响 500 ms |\r\n| `gh_alarm_test` | 发布 3 个超限测试样本，验证报警链路 |\r\n| `gh_hwinfo` | 打印板型与外设配置 |\r\n| `gh_touch` \/ `gh_touch on` \/ `gh_touch off` | 触摸事件计数、开启\/关闭坐标打印 |\r\n| `gh_touch rotate 0` \/ `180` | 运行时切换触摸方向（诊断用） |\r\n| `gh_ui_perf` | UI 版本、切页次数、数据更新次数、最大更新耗时 |\r\n| `lvgl_perf` | LVGL 整轮处理最大耗时与超过 100 ms 的次数 |\r\n| `list_device` \/ `list_thread` | RT-Thread 外设与线程状态 |\r\n\r\n---\r\n\r\n## 三、硬件框架说明\r\n\r\n### 3.1 主控平台\r\n\r\n| 项目 | 参数 |\r\n| --- | --- |\r\n| 开发板 | ArtInChip **D12X-DEMO68-V1.2**（PCB 丝印确认） |\r\n| CPU | THead **E907**，32 位 RISC-V（RV32），带单精度 FPU |\r\n| 主频 | CPU 396 MHz \/ AXI 200 MHz \/ AHB 200 MHz \/ APB 100 MHz |\r\n| 内存 | 片内 + **8 MB PSRAM** |\r\n| 存储 | **16 MB SPI NOR Flash**（Flash ID `0x852018`） |\r\n| 显示 | 480×272 RGB 电容触摸屏，GE 硬件加速 |\r\n| 触摸 | **GT911**，I²C 总线 `i2c0`，复位 `PA.10`，中断 `PA.11` |\r\n| 调试\/升级 | 板载 USB-UART0（丝印 `USB TO UART0 UPGRADE`），CH343 转串口 |\r\n| 操作系统 | RT-Thread 4.1.1 + LVGL 8.3.1 |\r\n\r\n### 3.2 传感器选型与接线\r\n\r\n| 传感器 | 测量对象 | 接口 | 引脚 |\r\n| --- | --- | --- | --- |\r\n| **DHT11** | 空气温度、空气湿度 | 单总线 GPIO 时序 | `PC.6`（J8 排孔 PC6 丝印位） |\r\n| **BH1750** | 光照强度 | I²C1，默认地址 `0x23`（兼容 `0x5c`） | SCL `PA.2`（白色接口 TX1）、SDA `PA.3`（白色接口 RX1） |\r\n\r\n**关键接线注意：**\r\n\r\n1. 传感器使用板上已核实的 **3V3 \/ GND**，DHT11 与 BH1750 **必须共地**。\r\n2. 传感器 I²C 走 **`i2c1`**，与触摸屏的 **`i2c0`** 是两条独立总线，**不可混接**。\r\n3. J8 为未焊接通孔，**必须焊接排针或可靠固定**。用手指按压接触会造成时序抖动，是 DHT11 读取失败的最常见原因。\r\n4. 本项目关闭了 SDMC1（TF 卡控制器），**把 `PC.6` 释放给 DHT11**。不要随意重新启用 TF 驱动，否则会抢占引脚。\r\n5. UART1 未启用，`PA2\/PA3` 专供 I²C1 使用，改引脚前必须检查 pinmux。\r\n\r\n### 3.3 蜂鸣器\r\n\r\n| 项目 | 参数 |\r\n| --- | --- |\r\n| 器件 | 板载 **BZ1** |\r\n| 驱动 | `PC.7` \/ **PWM1_B**，经 NPN 三极管驱动 |\r\n| 频率 | 4 kHz（周期 250 000 ns，脉宽 125 000 ns，即 50 % 占空比） |\r\n| RT-Thread 设备 | PWM 设备名 `pwm`，通道 **1** |\r\n\r\n### 3.4 硬件连接框图\r\n\r\n```text\r\n                    ┌──────────────────────────────────────┐\r\n                    │   ArtInChip D12X  (E907 RISC-V)      │\r\n                    │   396 MHz \/ 8MB PSRAM \/ 16MB NOR     │\r\n                    └──────────────────────────────────────┘\r\n      PC.6  ──────────────┤ GPIO 单总线\r\n                          │        ┌──────────────┐\r\n                          ├────────┤   DHT11      │  空气温度 \/ 空气湿度\r\n                          │        └──────────────┘\r\n                          │\r\n      PA.2 (SCL) ─────────┤ I2C1   ┌──────────────┐\r\n      PA.3 (SDA) ─────────┤        │   BH1750     │  光照强度 (0x23 \/ 0x5c)\r\n                          │        └──────────────┘\r\n                          │\r\n      PA.10 (RST) ────────┤        ┌──────────────┐\r\n      PA.11 (INT) ────────┤ I2C0   │   GT911      │  电容触摸\r\n                          │        └──────────────┘\r\n                          │\r\n      PC.7 \/ PWM1_B ──────┤        ┌──────────────┐\r\n                          ├────────┤   BZ1 蜂鸣器 │  超限报警\r\n                          │        └──────────────┘\r\n                          │\r\n                          │        ┌──────────────┐\r\n      RGB 接口 ───────────┤        │ 480×272 屏   │  本地显示\r\n                          │        └──────────────┘\r\n                          │\r\n      UART0 ──────────────┤ CH343  ──  USB-C  ──  PC（调试 \/ 烧录）\r\n                          └──────────────────────────────────────┘\r\n```\r\n\r\n### 3.5 为什么不需要额外硬件\r\n\r\n| 常见需求 | 本方案如何省掉 |\r\n| --- | --- |\r\n| 联网上传 | 数据在本机屏幕直接呈现，用户走到棚边就能看 |\r\n| 云端历史存储 | 60 点 RAM 环形缓冲 + 统计页，满足现场趋势判断 |\r\n| 外部 RTC | 用 RT-Thread tick 计时，采样序号替代绝对时间戳 |\r\n| 外部 ADC | DHT11 数字单总线、BH1750 数字 I²C，全程无需模拟采样 |\r\n| 独立存储卡 | 阈值只需一个几十字节的小文件，用板载 NOR 的 LittleFS 分区即可 |\r\n\r\n---\r\n\r\n## 四、软件框架说明\r\n\r\n### 4.1 RT-Thread 组件使用情况\r\n\r\n本作品不是\"在 RT-Thread 上跑裸机代码\"，而是实际使用了 RT-Thread 的多项内核与组件能力：\r\n\r\n| 能力 | 具体使用 |\r\n| --- | --- |\r\n| 线程 | 6 个应用\/驱动线程，优先级 12~26 分层 |\r\n| 互斥锁 | 保护状态快照与阈值（`ghlock`）、保护传感器总线访问（`ghslock`） |\r\n| 消息队列 | 两条独立队列：`ghalarm`（采样→报警）、`ghui`（采样→界面） |\r\n| 信号量 | `ghsave` 触发阈值异步落盘，并做多次 Apply 的合并 |\r\n| 设备框架 | PIN（DHT11）、I²C（BH1750、GT911）、PWM（蜂鸣器）、UART（控制台） |\r\n| MSH \/ Finsh | 12 条自定义诊断命令 |\r\n| DFS + LittleFS | `\/data\/greenhouse_limits.bin` 阈值持久化 |\r\n| 自动初始化 | `INIT_APP_EXPORT` 挂载应用入口，`INIT_APP_EXPORT` 挂载 LVGL 线程 |\r\n\r\n### 4.2 软件分层\r\n\r\n```text\r\n┌───────────────────────────────────────────────────────────┐\r\n│  应用层  greenhouse_app.c  (INIT_APP_EXPORT 启动编排)      │\r\n├───────────────────────────────────────────────────────────┤\r\n│  表现层  gh_ui.c        LVGL 四页面 \/ 曲线 \/ 统计 \/ 菜单   │\r\n│          gh_console.c   MSH 诊断命令                       │\r\n├───────────────────────────────────────────────────────────┤\r\n│  业务层  gh_alarm.c     连续确认 \/ 回差 \/ 报警状态机        │\r\n│          gh_data.c      互斥状态快照 \/ 双消息队列 \/ 持久化  │\r\n├───────────────────────────────────────────────────────────┤\r\n│  驱动层  gh_sensors.c   DHT11 位时序 + BH1750 I²C 采样      │\r\n│          gh_board.c     板型自检 \/ PWM 蜂鸣器               │\r\n├───────────────────────────────────────────────────────────┤\r\n│  系统层  RT-Thread 4.1.1 内核 + DFS\/LittleFS + 设备框架     │\r\n│          LVGL 8.3.1 + ArtInChip GE \/ FB \/ 触摸驱动          │\r\n└───────────────────────────────────────────────────────────┘\r\n```\r\n\r\n### 4.3 线程模型\r\n\r\n| 线程名 | 优先级 | 栈 | 职责 |\r\n| --- | --- | --- | --- |\r\n| `ghsensor` | 12 | 4096 | 每 2 s 采样一次 DHT11 + BH1750，发布到数据层 |\r\n| `ghalarm` | 13 | 3072 | 阻塞在报警队列，做连续确认与回差判断，驱动蜂鸣器 |\r\n| GT911 读取 | 18 | — | 中断 + 20 ms 轮询兜底，产出触摸事件 |\r\n| LVGL | 20 | 32768 | 唯一允许操作 LVGL 对象的线程，内含 50 ms 轮询定时器 |\r\n| `ghnet` | 24 | 6144 | 预留网络线程，当前离线镜像中空转 |\r\n| `ghsave` | 26 | 4096 | 阻塞在信号量，异步写阈值文件 |\r\n\r\n> RT-Thread 中数字越小优先级越高。传感器高于报警、报警高于触摸与 UI，保证采样不被界面绘制拖慢；保存线程最低，写 Flash 绝不阻塞交互。\r\n\r\n### 4.4 数据流\r\n\r\n```text\r\n                 ┌──────────────┐\r\n                 │  DHT11 (PC.6)│\r\n                 └──────┬───────┘\r\n                        │ GPIO 位时序\r\n                 ┌──────▼───────┐\r\n                 │ BH1750 (I2C1)│\r\n                 └──────┬───────┘\r\n                        │\r\n                  ghsensor 线程 (2 s)\r\n                        │  gh_sample_t + valid_mask\r\n                 ┌──────▼────────────────────┐\r\n                 │  gh_data  互斥锁状态快照   │\r\n                 └───┬───────────────────┬───┘\r\n          ghalarm 队列│                   │ghui 队列\r\n                 ┌───▼────────┐    ┌─────▼──────────────┐\r\n                 │  ghalarm   │    │ LVGL 线程 50ms 定时器│\r\n                 │ 连续确认   │    │ 取队列 → 渲染当前页  │\r\n                 │ 回差判断   │    └─────┬──────────────┘\r\n                 └───┬────────┘          │\r\n                     │ PWM1_B            │ 用户点击\r\n              ┌──────▼──────┐      ┌─────▼──────┐\r\n              │  BZ1 蜂鸣器 │      │ GT911 触摸 │\r\n              └─────────────┘      └────────────┘\r\n                                        │\r\n                    菜单 Apply ─────────┘\r\n                          │ gh_data_set_thresholds\r\n                    ┌─────▼──────┐\r\n                    │ RAM 立即生效│\r\n                    └─────┬──────┘\r\n                          │ 信号量通知\r\n                    ┌─────▼──────────────────┐\r\n                    │ ghsave 线程 → LittleFS │\r\n                    │ \/data\/greenhouse_limits.bin │\r\n                    └────────────────────────┘\r\n```\r\n\r\n### 4.5 模块职责\r\n\r\n| 文件 | 职责 |\r\n| --- | --- |\r\n| `greenhouse_app.c` | `INIT_APP_EXPORT` 入口，按序初始化板级、数据、报警、UI、网络、传感器 |\r\n| `gh_config.h` | 全部硬件配置与功能开关的唯一集中点 |\r\n| `gh_types.h` | 采样结构、阈值结构、状态结构、有效位与报警位定义 |\r\n| `gh_sensors.c\/.h` | DHT11 启动信号与 40 位时序解码、BH1750 唤醒与一次性测量、采样线程 |\r\n| `gh_data.c\/.h` | 互斥保护的全局状态快照、阈值校验、两条消息队列、后台持久化 |\r\n| `gh_alarm.c\/.h` | 原始超限判断 → 连续 3 次确认 → 回差维持 → 蜂鸣器开关 |\r\n| `gh_ui.c\/.h` | 四页面构建与缓存、导航、曲线重绘、统计聚合、菜单草稿与提交 |\r\n| `gh_board.c\/.h` | 板型信息、硬件自检、PWM 蜂鸣器封装 |\r\n| `gh_console.c` | 8 条 MSH 诊断命令（另 4 条在 UI 与触摸驱动中） |\r\n| `gh_network.c\/.h` | JSON 生成；HTTP 服务端源码保留但由开关关闭 |\r\n\r\n### 4.6 关键技术问题与解决\r\n\r\n#### ① 触摸方向与显示旋转必须解耦\r\n\r\n本板显示需要旋转 **180°** 才能正立，但 GT911 上报的原始坐标**已经**与画面一致。早期版本让触摸方向跟随显示旋转自动翻转，导致点击位置上下颠倒：\r\n\r\n```text\r\n[TOUCH] DOWN raw=(308,265) UI=(171,6)    ← 点底部 Report，被翻到顶部\r\n[TOUCH] DOWN raw=(408,259) UI=(71,12)    ← 点底部 Menu，被翻到顶部\r\n```\r\n\r\n解决方式：**两者独立配置**。\r\n\r\n```c\r\n\/* defconfig *\/\r\nCONFIG_AICFB_ROTATE_180=y\r\nCONFIG_AIC_FB_ROTATE_DEGREE=180\r\n\r\n\/* lv_conf.h —— 不再跟随显示旋转 *\/\r\n#define AIC_LVGL_TOUCH_ROTATION 0\r\n```\r\n\r\n同时保留 `gh_touch rotate 0\/180` 运行时命令，便于现场诊断。v1.1.2 起默认值即为 0°，开机无需任何命令。\r\n\r\n#### ② 快速点击丢事件\r\n\r\nLVGL 的输入回调只保存\"最后一个坐标 + 状态\"。当一次按下与抬起落在同一帧内，LVGL 下次读取时只能看到一个状态，点击被吞掉。\r\n\r\n解决方式：在驱动侧引入 **16 深度触摸事件环形队列**，`input_read` 每次消费一个事件并用 `continue_reading` 告知 LVGL 还有后续事件；队列满时丢弃最旧事件，**保证最新的抬起事件不丢失**。\r\n\r\n#### ③ 跨线程操作 LVGL 导致卡顿与崩溃\r\n\r\nLVGL 不是线程安全的。早期用独立 UI 线程 + `lv_async_call()` 跨线程投递，出现刷新撕裂与偶发卡死。\r\n\r\n解决方式：**所有 LVGL 对象操作收敛到 LVGL 线程内**。`gh_ui_start()` 只标记数据层就绪，真正的建页由 `gh_ui_lvgl_init()` 在 `lv_user_gui_init()` 中调用；数据更新由 LVGL 线程内的 50 ms 定时器主动轮询消息队列，不做任何跨线程回调。\r\n\r\n#### ④ 页面重建导致的卡顿\r\n\r\n早期每次切页都销毁重建控件，一次切页创建近百个对象，明显掉帧。\r\n\r\n解决方式：**四页一次性创建并缓存**，切页只切换 `LV_OBJ_FLAG_HIDDEN`；同时按变化刷新——数据没变就不重绘。电脑端回归测试连续 **2000 次切页**，缓存控件稳定保持 **93 个**、不增长，稳定态无堆内存增长，LVGL 内存占用约 35 680 字节。\r\n\r\n#### ⑤ UI 回调里同步写 Flash\r\n\r\n在触摸回调里直接写 Flash 会阻塞 LVGL 线程，表现为点击后界面\"顿一下\"，严重时掉帧。\r\n\r\n解决方式：Apply 时**只在 RAM 生效**并释放一个信号量，由最低优先级的 `ghsave` 线程异步落盘，且连续多次 Apply 会合并为一次写。\r\n\r\n#### ⑥ DHT11 单总线时序\r\n\r\nDHT11 用高低电平持续时间编码 0\/1，对时序敏感：\r\n\r\n- 主机拉低 **≥18 ms** 作为起始信号（实现取 20 ms）；\r\n- 释放总线后等待传感器响应低\/高\/低；\r\n- 每一位以约 50 µs 低电平开始，随后高电平 **26~28 µs 为 0，70 µs 为 1**，实现以 40 µs 为判决门限；\r\n- 40 位结束后校验 `data[0..3]` 之和与 `data[4]`；\r\n- 解析后做范围校验（湿度 0~100 %，温度 −40~80 ℃），越界即判为读取失败。\r\n\r\n配合 `valid_mask`，任何一步失败都不会污染已有数据，界面回到 `--`。\r\n\r\n### 4.7 配置开关\r\n\r\n所有关键开关集中在 `gh_config.h`：\r\n\r\n```c\r\n#define GH_USE_SIMULATED_SENSORS   0    \/* 0 = 真实采样 *\/\r\n#define GH_USE_DHT11               1\r\n#define GH_USE_BH1750              1\r\n#define GH_DHT11_GPIO              \"PC.6\"\r\n#define GH_I2C_BUS_NAME            \"i2c1\"\r\n#define GH_ENABLE_LVGL_UI          1\r\n#define GH_ENABLE_REAL_BUZZER      1\r\n#define GH_ENABLE_HTTP_SERVER      0    \/* 板载无 Wi-Fi 模块 *\/\r\n#define GH_SENSOR_PERIOD_MS        2000\r\n#define GH_QUEUE_DEPTH             8\r\n#define GH_ALARM_CONFIRM_SAMPLES   3\r\n```\r\n\r\n### 4.8 构建与烧录\r\n\r\n- **构建**：Luban-Lite SDK（SCons）交叉编译，`scons -j 8` 后自动打包为 `.img`，并由发布脚本按镜像内部版本号归档、比对 SHA256、拒绝覆盖同版本不同内容。\r\n- **烧录**：AiBurn v1.5.1，串口方式，按住 UBOOT + 按 RESET 进入 BROM，加载 `.img` 整包烧录。\r\n- **调试**：板载 USB-UART0，115200 8N1，进入 `aic \/>` MSH 提示符。\r\n\r\n---","updatetime":"2026-10-19 09:35:26","author":"GQBV_4810"}}