这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【e起DIY】低功耗蓝牙温湿度计:跑通DHT20

共1条 1/1 1 跳转至

【e起DIY】低功耗蓝牙温湿度计:跑通DHT20

高工
2026-06-09 22:15:32     打赏

        这个阶段的目标很简单:让FRDM-MCXW72这块板子能采到温湿度数据,并按2秒一次的节奏从串口打印出来。最终目标是做一个蓝牙温湿度计。这阶段只做温湿度采集这一段。

  • 板子:NXP FRDM-MCXW72(恩智浦的一块开发板,自带lpi2c1在 Arduino 头上)

  • 传感器:Grove-TH Sensor V2.0(板子上的核心其实是奥松电子的DHT20

  • 接口:I2C(默认地 0x38)

  • 输出:串口(115200 8N1)每2秒打一次温度和湿度

        工程基线是 Zephyr 自带的hello_world模板,原来只打一句Hello World!。这次要把它升级成真正能采数据的程序。

DHT20传感器

                DHT20是奥松电子出的一款数字式温湿度传感器,I2C 接口。几个关键点:
  • 供电:2.0V~5.5V,3.3V 直接接上就行

  • I2C 地址:0x38(7-bit),速率标准 100kHz 或快速 400kHz 都行

  • 数据格式:触发测量后读7个字节,里头20 bit湿度 +20 bit温度 + 1字节 CRC

  • 转换公式(来自 datasheet):

    • 温度 T = S/2^20 × 200 − 50(单位 °C)

    • 湿度 RH = S/2^20 × 100(单位 %)

        我们这次其实不用自己算公式,Zephyr SDK 里已经有人把 DHT20 的驱动写好了,我们直接用就行。但作为背景知识知道一下也好——下面写代码的时候你会看到这些公式。

        这次手里的传感器板子实际上也是这次指定的传感器之一:

d35ca415-5c44-4b6a-abf8-1b039c4a7f08.png

Zephyr 里怎么加一个传感器

        Zephyr 的传感器接入方式非常标准化,整个套路几乎适用于所有传感器:

  1. 在设备树(devicetree)里声明这个设备——告诉Zephyr它在哪个总线上、地址多少

  2. 在prj.conf里打开对应驱动

  3. 应用层用统一的sensor API 读数据:sample_fetch触发一次采集 + channel_get取具体通道

        这三步几乎对所有传感器都成立。差别只在设备树里compatible字符串不一样、读的通道不一样而已。

实操:三步接入 DHT20

第 1 步:写设备树 overlay(app.overlay)

        Zephyr板级设备树(frdm_mcxw72.dts)已经把&lpi2c1打开并配好引脚了(PTB4 SDA / PTB5 SCL,也就是Arduino接口的D14/D15)。我不需要再去动板级DTS,只要写一个overlay就能往这条总线上挂东西。
&lpi2c1 {    dht20: dht20@38 {        compatible = "aosong,dht20";        reg = <0x38>;        status = "okay";    }; };

解释一下这几行:
  • &lpi2c1 { ... }:往 lpi2c1 总线节点里塞东西

  • dht20: dht20@38:节点名字叫 dht20(前面那个 dht20: 是 label,方便后面 C 代码用 DT_NODELABEL(dht20) 引用),@38 是它在总线上的地址

  • compatible = "aosong,dht20":这是关键。Zephyr 的 DHT20 驱动就是靠这串字符串匹配来认领这个节点的

  • reg = <0x38>:I2C 地址 0x38

  • status = "okay":默认设备树里所有节点都是 disabled,写上它才真的启用

        到这里,Zephyr 编译时就会帮我们生成一段 C 代码,把这个节点变成一个 struct device,名字大概叫 dht20@38。

第 2 步:开驱动(prj.conf)

        prj.conf是Kconfig配置文件,告诉Zephyr这次构建要打开哪些功能。

CONFIG_I2C=y CONFIG_SENSOR=y CONFIG_DHT20=y
  • CONFIG_I2C:DHT20 是 I2C 设备,I2C 驱动得开

  • CONFIG_SENSOR:Zephyr 统一的 sensor 子系统,相当于一个"传感器框架"

  • CONFIG_DHT20:DHT20 的具体驱动。dht20.c 里那个 bool "DHT20 ..." 的 Kconfig 项就是它

        这里有个小细节:DHT20 Kconfig里有这么一句

depends on DT_HAS_AOSONG_DHT20_ENABLED || DT_HAS_AOSONG_AHT20_ENABLED ||DT_HAS_AOSONG_AM2301B_ENABLED

        意思就是"必须设备树里有这个节点,我才会被编进去"。所以overlay写错的话,驱动不会被链接,能少踩点坑。

第 3 步:写应用代码(src/main.c)

#define DHT20_NODE DT_NODELABEL(dht20) static const struct device *dht20 = DEVICE_DT_GET(DHT20_NODE);

        DT_NODELABEL(dht20)拿到设备树里那个dht20: label对应的节点,DEVICE_DT_GET把它转成一个device句柄。这一步是编译期完成的——如果节点不存在或者被disable了,直接编译报错,不用等到运行时才发现。

        主循环就两件事:

sensor_sample_fetch(dht20);                          // 触发一次测量 sensor_channel_get(dht20, SENSOR_CHAN_AMBIENT_TEMP, &temperature);  // 读温度 sensor_channel_get(dht20, SENSOR_CHAN_HUMIDITY, &humidity);         // 读湿度

        SENSOR_CHAN_AMBIENT_TEMP是「环境温度」的语义通道,温度、湿度、加速度、陀螺仪……每个传感器读什么,都是用这套枚举挑的,写代码时不用关心具体寄存器和协议。

        每次读完之后k_msleep(2000)等2秒,就完成我们最初的目标了。

坑1:Zephyr的printk不支持%f

        第一次写完代码用%.2f打印温度,烧进去一看:

DHT20: *float* degC, *float* %RH DHT20: *float* degC, *float* %RH

        那个*float*不是我的变量,是*%f这几个字符被原样打出来。原因是Zephyr默认链 picolibc(一个精简版的libc),为了省体积把浮点格式说明符砍了。

        修复办法就一句:Zephyr的sensor_value用「整数 + 微值」两个int 表示一个数(val1 = 整数,val2 = 百万分之一精度的余项)。手动拼一下就行:

static void print_sensor_value(const struct sensor_value *v) {    printk("%d.%02d", v->val1, abs(v->val2) / 10000); }

跑起来之后串口就是这样的:

0d8044e5-ed87-407a-b81c-0480b691b4b1.png

        回头看,整个阶段其实就干了一件事:把一个陌生的传感器接进 Zephyr。以后再加任何I2C传感器(Grove 系列、Adafruit 模块、自家板子上的芯片)套路都一样:
  1. 查datasheet拿I2C地址

  2. overlay加节点(compatible 写对就行)

  3. prj.conf 开对应驱动

  4. 应用层用sensor API读

        驱动部分Zephyr都已经写好了,绝大多数情况我们用不到直接戳寄存器。下一阶段真正要面对的,是怎么把温湿度数据通过蓝牙 BLE 周期性地广播出去——这才是蓝牙温湿度计的重点。



















关键词: DHT20    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]