一、项目介绍
项目基于 NXP FRDM-MCXN236 开发板,搭配 AHT20 采集环境温度和相对湿度,通过 J10 串口输出原始值、整数 EMA 滤波值、时间戳、序列号、错误码、报警状态和队列丢弃计数,并通过 128×64SSD1306 OLED 显示实时结果。板载 led0 用于表示初始化、正常、阈值报警和传感器故障状态。
1.1 硬件介绍
- FRDM-MCXN236: 是恩智浦(NXP)推出的一款紧凑型、可扩展的高性价比开发板,专为基于 MCX N23x 系列微控制器的快速原型设计而打造。该开发板的核心是一颗 MCX N236 微控制器,搭载 Arm Cortex-M33 内核,主频最高可达 150 MHz,配备 1MB 双组闪存(支持完整 ECC RAM)和 352KB ECC RAM,并集成 SmartDMA 加速器,能够高效地将工作负载分配到模拟与数字外设上。

- Grove - AHT20 I2C 工业级温湿度传感器: 新一代温湿度传感器,采用双排扁平无引脚 SMD 封装,适用于回流焊接。AHT20 配备了新设计的 ASIC 芯片:改进的 MEMS 半导体电容式湿度传感器和标准片上温度传感器。与上一代温湿度传感器相比,AHT20 在恶劣环境下的性能更加稳定,实际上,AHT20 适用于大多数工业场景。

- SSD1306 OLED: 一款应用广泛的 0.96 寸OLED 显示屏模块。

- DHT11:是可选 GPIO 双向时序传感器,不是 I2C 设备。

硬件连接表
器件 | 信号 | 开发板连接 | 地址/参数 |
|---|---|---|---|
AHT20 | SDA | Arduino D14 / PIO1_16 |
|
AHT20 | SCL | Arduino D15 / PIO1_17 | 100 kHz |
AHT20 | VCC/GND | 按模块额定电压连接 | 共地、需适当上拉 |
SSD1306 | SDA | Arduino D14 / PIO1_16 |
|
SSD1306 | SCL | Arduino D15 / PIO1_17 | 100 kHz |
SSD1306 | VCC/GND | 按模块额定电压连接 | 共地 |
J10 | UART | 板级 console | 115200 8N1 |
板载 LED |
|
| active-low |
DHT11(可选) | DATA | Arduino D2 / PIO2_0 |
|
1.2 功能概览
- AHT20 以 500 ms 周期采样。
- 不同传感器分别维护周期、序列号、滤波和报警状态。
- 温湿度使用定点单位:m°C 与 0.001%RH。
- 整数 EMA 的 α 为 0.25。
- 实现高温、低温、高湿、低湿独立报警。
- 报警进入和解除均需要连续 3 个样本确认,并带温度/湿度回差。
- 连续 3 次无效样本进入故障,连续 2 次有效样本退出故障。
- 串口与 OLED 由输出线程统一更新。
- OLED 失败时串口、采集、处理和 LED 不停止。
- 队列拥塞时丢弃最旧样本,采集线程不阻塞。
- DHT11 通过应用 Kconfig 与传感器注册表可选加入,无需修改三个主任务。
1.3 设计思路
1.3.1 系统框图
1.3.2 三任务消息流图
队列生产者使用 K_NO_WAIT。若队列已满,则先非阻塞移除最旧元素并增加对应 丢弃计数,再写入最新数据。消息结构不包含线程栈指针,生命周期由值复制保证。
1.3.3 职责边界
线程 | 允许 | 禁止 |
|---|---|---|
acquisition | 传感器初始化、周期采样、单位转换、写 raw 队列 | 滤波、报警、串口、OLED |
processing | 数据校验、EMA、报警/故障状态机、LED 逻辑状态、写 processed 队列 | 访问传感器、串口、OLED |
output | 串口格式化、OLED 初始化和全页刷新 | 采样、修改滤波或报警状态 |
二、功能实现
2.1 软件流程图
2.2 实现过程
2.2.1 AHT20 适配器
aht20_adapter.c 通过 DEVICE_DT_GET(DT_NODELABEL(env_aht20)) 取得由 overlay 定义的设备。初始化先检查 device_is_ready()。采样顺序为 sensor_sample_fetch()、温度 sensor_channel_get()、湿度 sensor_channel_get()。sensor_value.val1/val2 使用 64 位中间值转换为 m°C 和 0.001%RH,并检查 int32_t 范围。
2.2.2 队列覆盖策略
K_MSGQ_DEFINE 静态创建两个队列。生产者先尝试 K_NO_WAIT 写入;若返回队列已满,则用 K_NO_WAIT 读取并丢弃最旧消息,增加原子丢弃计数,再重试写入。因此输出变慢不会反向阻塞采集。
2.2.3 整数 EMA
每个传感器拥有独立 env_filter_state。第一条有效样本直接初始化;后续计算 $ filtered = filtered + (raw - filtered) / 4 $ 差值使用 int64_t,避免两个 int32_t 相减时溢出。无效样本不更新滤波器。 进入传感器故障后,第一条恢复有效样本重置并重新初始化 EMA;故障标志仍需第二条连续成功样本才解除。
2.2.4 报警与故障恢复
状态 | 进入条件 | 解除条件 |
|---|---|---|
高温 | 滤波温度连续 3 次 | 连续 3 次 |
低温 | 滤波温度连续 3 次 | 连续 3 次 |
高湿 | 滤波湿度连续 3 次 | 连续 3 次 |
低湿 | 滤波湿度连续 3 次 | 连续 3 次 |
传感器故障 | 连续 3 次无效样本 | 连续 2 次有效样本 |
四类环境报警分别维护进入计数、解除计数和 active 状态。无效样本会打断尚未完成的进入/解除连续计数;通信错误只进入故障计数,不参与普通阈值判断。传感器故障、环境报警、正常、初始化按照该顺序决定 LED 表现。
2.2.5 OLED 显示
显示设备通过 DEVICE_DT_GET(DT_CHOSEN(zephyr_display)) 取得。显示服务依次检查设备 ready、CFB 初始化、显示宽高、字体数量、字体尺寸、字体设置、blanking off、清帧和 finalize 的结果。每次刷新先清帧、写入完整页面,再 finalize。任一步失败都会向输出线程返回错误;输出线程随后禁用 OLED 更新并继续串口输出。
无效样本或故障状态显示 --.-,不会无提示显示历史值。
2.2.6 LED 工作项
LED 使用 GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios),不硬编码端口或引脚。一个 k_work_delayable 实现:
- 初始化:125 ms 快速翻转。
- 正常:500 ms 翻转。
- 报警:常亮。
- 故障:100 ms 亮、100 ms 灭、100 ms 亮、700 ms 灭的双闪。
处理线程只更新逻辑状态,不通过 sleep 产生闪烁。
三、功能展示
3.1 串口展示
设计格式:
[timestamp ms][sensor][seq=n] raw=... filtered=... state=... error=... drops=raw/processed

3.2 OLED 展示
设计正常页面:
ENV MONITOR
T: 25.1 C
H: 60.9 %
AHT20 #0025

3.3 LED 与报警测试
湿度超限报警

温度超限报警

四、总结
遇到的问题
- Arduino I2C 与 I3C1 引脚冲突。 两者复用 PIO1_16/PIO1_17。项目明确选择
flexcomm5_lpi2c5,并在应用级 overlay 中禁用i3c1,避免两套控制器同时配置引脚。 - AHT20 驱动名称与器件名称不同。 v4.4.0 使用
aosong,aht20binding,但 Kconfig 驱动符号是CONFIG_DHT20。项目没有编造CONFIG_AHT20。
心得体会
本项目形成了一个基于Zephyr v4.4.0的模块化环境监测系统设计。采集、处理和输出职责分离,既能满足小于1秒的刷新要求,也便于后续增加传感器和新的输出方式。


