这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【FRDMMCXW71|Zephyr】获取DHT11温湿度数据

共1条 1/1 1 跳转至

【FRDMMCXW71|Zephyr】获取DHT11温湿度数据

高工
2026-04-12 11:08:06     打赏

【前言】

在E起DIY中有些工程师可能是买到DHT11的,因此需要使用Zephyr来驱动他,但是我需要官方的库好象没有成功的驱动。因此写了个驱动来实现。

【硬件连接】

由于这块开发板的IO复用比较多,因此我偿试了好多个IO,最后选择了PTA16做为DHT11的数据接口。

是开发板上的J1第D0脚上,也方便查找。

image.png

【软件实现】

在zephyr\app\下面新建工程。并添加对应的文件:

app/dht11_test/
├── CMakeLists.txt
├── prj.conf
├── west.yml
├── boards/
│   └── frdm_mcxw71.overlay   # DHT11 on PTA16
└── src/
    ├── main.c                 # 主程序
    ├── dht11.c               # DHT11 驱动 (DWT 微秒延时)
    └── dht11.h                # 驱动头文件
【关键配置】
【1. 设备树配置 (overlay)】
在 `boards/<board>.overlay` 中配置 GPIO:
/ {
    aliases {
        dht11 = &dht11_node;
    };
    dht11_node: dht11 {
        compatible = "aosong,dht";
        dio-gpios = <&gpioa 16 GPIO_ACTIVE_HIGH>;
        status = "okay";
    };
};
/* 确保 GPIO 控制器已启用 */
&gpioa {
    status = "okay";
};
/* 如果该引脚被其他外设占用,需要禁用 */
&lpuart0 {
    status = "disabled";
};

【重要】:- 必须使用 `GPIO_ACTIVE_HIGH`,不能使用 `GPIO_ACTIVE_LOW`- 如果引脚被其他外设(如 UART)占用,必须禁用该外设
【2. 修改引脚】
在 `src/main.c` 中修改 GPIO 引脚号:
#define
 DHT11_PIN 16  /* PTA16 */

【DHT11通信协议】

DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次 通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零。

操作流程如下:  

一次完整的数据传输为40bit,高位先出。

数据格式:8bit湿度整数数据+8bit湿度小数数据

  +8bi温度整数数据+8bit温度小数数据

   +8bit校验和

  数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位。

用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集, 用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集, 如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后 转换到低速模式。

1.通讯过程如图1所示

image.png

总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必 须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后, 等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束 后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换 到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。

image.png总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉 高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定 了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线 50us,随后总线由上拉电阻拉高进入空闲状态。

数字0信号表示方法如下图所示

image.png数字1信号表示方法.如下图所示

image.png

通过上描数据通信协议的学习,工程中dht11.c的代码如下:

/*
 * DHT11 Driver using ARM DWT cycle counter for precise microsecond delays
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>

#include "dht11.h"

/* ARM Cortex-M DWT cycle counter */
static uint32_t dwt_cycles_per_us(void)
{
    static uint32_t cycles_per_us = 0;
    if (cycles_per_us == 0) {
        cycles_per_us = SystemCoreClock / 1000000;
    }
    return cycles_per_us;
}

static inline void dwt_delay_us(uint32_t us)
{
    volatile uint32_t *demcr = (uint32_t *)0xE000EDFC;
    volatile uint32_t *dwt_ctrl = (uint32_t *)0xE0001000;
    volatile uint32_t *dwt_cyccnt = (uint32_t *)0xE0001004;

    /* Enable trace and cycle counter */
    *demcr |= (1 << 24);
    *dwt_ctrl |= (1 << 0);

    uint32_t start = *dwt_cyccnt;
    uint32_t cycles = us * dwt_cycles_per_us();

    while ((*dwt_cyccnt - start) < cycles) {
        __NOP();
    }
}

static inline int get_pin(const struct device *gpio_dev, int pin)
{
    return gpio_pin_get(gpio_dev, pin);
}

/* Wait for pin to reach target state, then measure how long it stays there (in us) */
static int wait_and_measure_us(const struct device *gpio_dev, int pin, int wait_high)
{
    volatile uint32_t *demcr = (uint32_t *)0xE000EDFC;
    volatile uint32_t *dwt_ctrl = (uint32_t *)0xE0001000;
    volatile uint32_t *dwt_cyccnt = (uint32_t *)0xE0001004;
    uint32_t cycles_per_us = dwt_cycles_per_us();

    /* Enable DWT */
    *demcr |= (1 << 24);
    *dwt_ctrl |= (1 << 0);

    /* First: wait for pin to reach target state */
    int wait_count = 0;
    while (get_pin(gpio_dev, pin) != (wait_high ? 1 : 0)) {
        wait_count++;
        if (wait_count > 100000) {
            return 1000;  /* Timeout */
        }
    }

    /* Second: measure how long it stays in that state */
    uint32_t start = *dwt_cyccnt;
    int measure_count = 0;

    while (get_pin(gpio_dev, pin) == (wait_high ? 1 : 0)) {
        measure_count++;
        if (measure_count > 100000) {
            return 1000;  /* Timeout */
        }
    }

    uint32_t elapsed_cycles = *dwt_cyccnt - start;
    return elapsed_cycles / cycles_per_us;
}

int dht11_init(const struct device *gpio_dev, int pin)
{
    if (!gpio_dev || !device_is_ready(gpio_dev)) {
        return -ENODEV;
    }

    int ret = gpio_pin_configure(gpio_dev, pin, GPIO_INPUT | GPIO_PULL_UP);
    if (ret < 0) {
        return ret;
    }

    return 0;
}

int dht11_read(const struct device *gpio_dev, int pin, uint8_t *humidity, uint8_t *temperature)
{
    uint8_t data[5] = {0};
    int ret;

    if (!gpio_dev || !humidity || !temperature) {
        return -EINVAL;
    }

    /* Send start signal: output low for 18ms */
    ret = gpio_pin_configure(gpio_dev, pin, GPIO_OUTPUT_INACTIVE);
    if (ret < 0) {
        return ret;
    }
    dwt_delay_us(18000);  /* 18ms */

    /* Release bus: input with pull-up */
    ret = gpio_pin_configure(gpio_dev, pin, GPIO_INPUT | GPIO_PULL_UP);
    if (ret < 0) {
        return ret;
    }

    /* Wait for DHT ACK low (~80us low) */
    int ack_low = wait_and_measure_us(gpio_dev, pin, 0);
    if (ack_low >= 1000) {
        return -ETIMEDOUT;  /* No response */
    }

    /* Wait for DHT ACK high (~80us high) */
    int ack_high = wait_and_measure_us(gpio_dev, pin, 1);
    if (ack_high >= 1000) {
        return -ETIMEDOUT;
    }

    /* Read 40 bits */
    for (int i = 0; i < 40; i++) {
        /* Wait for bit start (low) */
        int low_dur = wait_and_measure_us(gpio_dev, pin, 0);
        if (low_dur >= 1000) {
            return -EIO;
        }

        /* Measure high duration to determine bit value */
        int high_dur = wait_and_measure_us(gpio_dev, pin, 1);

        /* Store bit: 0 bit ~30us, 1 bit ~70us */
        int byte_idx = i / 8;
        int bit_idx = 7 - (i % 8);
        if (high_dur > 40) {
            data[byte_idx] |= (1 << bit_idx);
        }
    }

    /* Verify checksum */
    uint8_t sum = data[0] + data[1] + data[2] + data[3];
    if (sum != data[4]) {
        return -EIO;  /* Checksum error */
    }

    *humidity = data[0];
    *temperature = data[2];

    return 0;
}
DHT11 的 0/1 位通过高电平持续时间区分:0 bit: 高电平约 27-30μs1 bit: 高电平约 70-80μs
/* 根据实测值调整阈值(应介于 0 bit 和 1 bit 之间)*/
if (high_dur > 40) {  /* 阈值 ~40-50us */
    data[byte_idx] |= (1 << bit_idx);
}

在程序的设计是主要是需要一个精确的us的延时来实时对单总线的bit数据位的测量。

修改main.c中的代码实现测试:

/*
 * DHT11 Test Application
 *
 * Hardware: FRDM-MCXW71, PTA16 connected to DHT11 DATA pin
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/device.h>

#include "dht11.h"

#define DHT11_PIN 16

int main(void)
{
    int ret;
    uint8_t humidity, temperature;

    printk("DHT11 Test starting...\n");

    const struct device *gpio_dev = DEVICE_DT_GET(DT_NODELABEL(gpioa));
    if (!gpio_dev) {
        printk("GPIOA not found\n");
        return 0;
    }

    ret = dht11_init(gpio_dev, DHT11_PIN);
    if (ret < 0) {
        printk("DHT11 init failed: %d\n", ret);
        return 0;
    }
    printk("DHT11 initialized\n");

    while (1) {
        ret = dht11_read(gpio_dev, DHT11_PIN, &humidity, &temperature);
        if (ret == 0) {
            printk("H=%d%% T=%d°C OK\n", humidity, temperature);
        } else {
            printk("Read failed: %d\n", ret);
        }

        k_sleep(K_SECONDS(3));
    }

    return 0;
}


【效果】

编译下载后,通过串口打印如下:

image.png

附工程源码:

dht11_test.zip





关键词: Zephyr     FRDMMCXW71     DHT11         

共1条 1/1 1 跳转至

回复

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