上图为DHT11温度传感器

DHT11 是一种常用的 温湿度传感器,它内部集成了 温度测量元件、湿度测量元件 以及 单片机控制芯片,通过 单总线(One-Wire)通信协议 与主控 MCU(如 STM32、Hi3861、Arduino 等)进行数据交互。

组成部分 功能
湿度传感元件 检测空气相对湿度(电容式或电阻式)
温度传感元件 检测环境温度(通常是 NTC 热敏电阻)
内部 ADC 与单片机 将模拟信号转换为数字信号,并打包输出
单总线接口 用一根数据线与外部 MCU 通信

DHT11  工作方式(单总线时序)

DHT11 通过 单根数据线 进行 双向通信,使用 时序编码 来区分 0 和 1,没有标准 UART、I²C 或 SPI。

四个阶段:
① 主机起始信号(Start Signal)   
      MCU 主动向 DHT11 发起通信:
                            MCU 先将总线电平拉低30ms,然后将总线电平拉高35us。
② DHT11 响应信号(Response Signal)

        DHT11 检测到主机信号后,开始应答: DHT11 拉低 88µs,拉高 92 µs

        这表示 DHT11 已准备好发送数据。

③ 数据传输阶段(共 40 位)

        DHT11 发送 5 字节(共 40 位)数据.。

字节 含义
Byte1 湿度整数部分
Byte2 湿度小数部分(DHT11 固定为 0)
Byte3 温度整数部分
Byte4 温度小数部分(DHT11 固定为 0)
Byte5 校验和(前四字节相加的低 8 位)

④ 数据位时序编码(区分 0 和 1)

信号 低电平时间 高电平时间 表示位
起始低电平 50 µs 起始标志
数据高电平 23~27 µs 表示“0”
数据高电平  68~74 µs 表示“1”
  • 每一位开始都先拉低约 50µs;

  • 然后根据高电平时间判断是 0 还是 1。

下图是DHT11的时序图

DHT11代码实现:

IO11 连接 DHT11 对应的 Data 引脚,同时 Hi3861 提供 VCC + GND
IO11 在整个的 DTH11 数据读取过程中,需要进行【输出到输入】模式的转换。
IO11 引脚在空闲状态/初始化状态 ==> GPIO 输出工作模式,默认电平为高电平
IO11 引脚进入到数据接收状态 ==> GPIO 输入工作模式,默认电平为低电平模式/浮空模式

dht11_source.h

#ifndef _DHT11_SOURCE_H
#define _DHT11_SOURCE_H
/*
C 语言标准库三剑客
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
以下是需要导入的 OpenHarmony 相关头文件
ohos_init.h OpenHarmony OS 系统初始化相关头文件
cmsis_os2.h 实时操作系统头文件
hi_timer.h 海思提供的 timer 定时器头文件
*/
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_timer.h"
#include "hi_time.h" // 对应 hi_udelay us 延时函数头文件
#include "hi_errno.h" // 使用 Hi3861 提供的官方错误宏定义
// 海思(OHOS) 提供的标准 IO 头文件,所有对外接口的模式,相关函数
#include "hi_io.h"
// 海思(OHOS) 提供的标准 GPIO 头文件,针对 GPIO 相关控制
#include "hi_gpio.h"
#include "hi_pwm.h"
#define DHT11_PIN HI_IO_NAME_GPIO_11
#define DHT11_PIN_FUNC HI_IO_FUNC_GPIO_11_GPIO
#define TIMEOUT_COUNT (100)
/**
* @brief DHT11 对应引脚初始化函数
*/
void dht11_init(void);
/**
* @brief DTH11 对应引脚修改为输入模式,用于在发送完成起始信号之后
* 进行数据接收, GPIO 输入模式修改。
*/
void dht11_gpio_input(void);
/**
* @brief DTH11 对应引脚修改为输出模式,DHT11 数据接收完毕,对应引脚恢复到
* 输出模式空闲状态
*/
void dht11_gpio_output(void);
/**
* @brief MCU 发送起始信号到 DHT11, MCU 对应 IO 口空闲情况下处于高电平状态
* 1. Tbe 低电平 30 ms
* 2. Tgo 高电平 35 us
* 转换 IO 工作状态从输出 ==> 输入状态,释放数据总线。
*/
void dht11_start_signal(void);
/**
* @brief 读取 MCU 对应 DHT11 IO 引脚的电平情况函数,返回值是
* hi_gpio_value 类型,对应 HI_GPIO_VALUE0 或者 HI_GPIO_VALUE1
*
* @return 返回值对应 IO 引脚电平情况
*/
hi_gpio_value get_dht11_gpio_input_val(void);
/**
dht11_source.c
* @brief DHT11 发送响应信号到 MCU 对应引脚,MCU 需要读取对应引脚的电平情况持续时间
* 进行判断当前 DHT11 响应是否正常
* 1. Trel DHT11 发送低电平信号,持续时间 88 us
* 2. Treh DHT11 发送高电压循环,持续时间 92 us
* 综合考虑,统一持续时间/超时判断时间 Timeout ==> 100 us
*
* @return 响应成功,返回 HI_ERR_SUCCESS,失败返回 HI_ERR_S_FAILURE
*/
hi_s8 dht11_response(void);
/**
* @brief DHT11 读取一位数据内容,数据总量是 40 位
*
* @return 根据高电平持续时间,TH1 对应 1,TH0 对应 0
*/
hi_u8 dht11_read_bit(void);
/**
* @brief DHT11 读取一个字节数据,数据总量是 5 个字节
*
* @return 返回是一个字节数据。
*/
hi_u8 dht11_read_byte(void);
/**
* @brief DHT11 传感器数据解析函数,将数据内容通过【校验操作】之后,按照
* 数据顺序完成温度和湿度数据解析
*
* @param tem 温度整型值
* @param tem_dec 温度小数部分数据,按照整数方式处理,降低 MCU 处理计算压力
* @param hum 湿度整型值
* @param hum_dec 湿度小数部分数据,按照整数方式处理,降低 MCU 处理计算压力
* @return 读取成功返回 HI_ERR_SUCCESS,失败返回 HI_ERR_S_FAILURE
*/
hi_s8 dht11_read_data(hi_u32 *tem, hi_u32 *tem_dec,
hi_u32 *hum, hi_u32 *hum_dec);
#endif

dht11_source.c

#include "dht11_source.h"
void dht11_init(void)
{
    hi_gpio_init();
    /*
    设置 DHT11 对应引脚初始化为 GPIO 输出工作模式,且默认电平为高电平
    */
    hi_io_set_func(DHT11_PIN, DHT11_PIN_FUNC);
    hi_gpio_set_dir(DHT11_PIN, HI_GPIO_DIR_OUT);
    hi_io_set_pull(DHT11_PIN, HI_IO_PULL_UP);
}
void dht11_gpio_input(void)
{
/*
    DHT11 对应引脚修改为 GPIO 输入工作模式,且默认电平为低电平,也可以选择浮空模式,
    在 Hi3861 中存在一定的隐患。
    */
    hi_gpio_set_dir(DHT11_PIN, HI_GPIO_DIR_IN);
    // hi_io_set_pull(DHT11_PIN, HI_IO_PULL_DOWN);
}
void dht11_gpio_output(void)
{
    hi_gpio_set_dir(DHT11_PIN, HI_GPIO_DIR_OUT);
    // hi_io_set_pull(DHT11_PIN, HI_IO_PULL_UP);
}
void dht11_start_signal(void)
{
    /*
    1. 明确 MCU 对应 IO 引脚为输出工作模式,且明确为高电平
    */
    dht11_gpio_output();
    hi_gpio_set_ouput_val(DHT11_PIN, HI_GPIO_VALUE1);
    /*
    2. Tbe 拉低电平,持续时间 30 ms
    hi_void hi_udelay(hi_u32 us);
    hi3861 提供的延时函数,单位 us
    */
    hi_gpio_set_ouput_val(DHT11_PIN, HI_GPIO_VALUE0);
    hi_udelay(30 * 1000);
    /*
    3. Tgo 拉高电平,持续时间 35 us
    */
    hi_gpio_set_ouput_val(DHT11_PIN, HI_GPIO_VALUE1);
    hi_udelay(35);
    /*
    4. MCU 对应 DHT11 IO 引脚拉低电平,且修改为输入模式,释放数据总线
    */
    hi_gpio_set_ouput_val(DHT11_PIN, HI_GPIO_VALUE0);
    dht11_gpio_input();
}
hi_gpio_value get_dht11_gpio_input_val(void)
{
    hi_gpio_value value = HI_GPIO_VALUE0;
    hi_gpio_get_input_val(DHT11_PIN, &value);
    return value;
}
hi_s8 dht11_response(void)
{
    hi_u32 timeout = 0;
    /*
    1. 【等低】 ==> 滤高
    过滤 MCU 或者 DHT11 剩余或者相关干扰的高电平信号,明确进入到
    DHT11 响应信号开始阶段 Trel
    */
    while (get_dht11_gpio_input_val() == HI_GPIO_VALUE1)
    {
    // 延时 1 us
        hi_udelay(1);
        timeout += 1;
        if (TIMEOUT_COUNT == timeout)
        {
            // 表示当前 DHT11 响应超时
            return HI_ERR_S_FAILURE; // #define HI_ERR_S_FAILURE (-1)
        }
    }
    timeout = 0;
    /*
    2. 【等高】 ==> Trel 过程
    DHT11 开始发送低电平信号到 MCU ,MCU 根据低电平持续时间进行判断,是否
    出现异常情况。
    */
    while (get_dht11_gpio_input_val() == HI_GPIO_VALUE0)
    {
        // 延时 1 us
        hi_udelay(1);
        timeout += 1;
        if (TIMEOUT_COUNT == timeout)
        {
            // 如果 Trel 持续时间超出 100 us,可以认为 DHT11 异常
            return HI_ERR_S_FAILURE;
        }
    }
    timeout = 0;
    /*
    3. 【等低】 ==> Treh 过程
    DHT11 开始发送高电平信号到 MCU,MCU 读取对应 IO 进行持续高电平时间判断
    */
    while (get_dht11_gpio_input_val() == HI_GPIO_VALUE1)
    {
        // 延时 1 us
        hi_udelay(1);
        timeout += 1;
        if (TIMEOUT_COUNT == timeout)
        {
            // 如果 Treh 持续时间超出 100 us,可以认为 DHT11 异常
            return HI_ERR_S_FAILURE;
        }
    }
    return HI_ERR_SUCCESS; // #define HI_ERR_SUCCESS 0
}
hi_u8 dht11_read_bit(void)
{
    hi_u32 timeout = 0;
    /*
    1. 【等高】 ==> Tlow
    DHT11 发送 40 位数据的固定间隔低电平,等待出现高电平情况,进入
    TH0 或者 TH1 分析
    */
    while (get_dht11_gpio_input_val() == HI_GPIO_VALUE0)
    {
        // 延时 1 us
        hi_udelay(1);
        timeout += 1;
        if (TIMEOUT_COUNT == timeout)
        {
            return HI_ERR_S_FAILURE;
        }
    }
    /*
    2. 【延时控制】 ==> 【重点】
    如果以上 while 循环正常结束,表示当前 DHT11 Tlow 阶段结束,同时
    DHT11 发送高电平数据
    延时控制 40 ~ 50 us 之后,MCU 再次读取 DHT11 数据线电平情况
    TH0 23 ~ 27 us
    TH1 68 ~ 74 us
    */
    hi_udelay(40);
    timeout = 0;
    /*
    3. 【读取判断】
    3.1 如果 MCU 读取到的电平情况为低电平,对应当前 DHT11 发送数据 0
    3.2 如果 MCU 读取到电平情况下为高电平
    进入【等低】 ==> 过滤必要的 TH1 高电平数据,且控制时间。
    */
    if (get_dht11_gpio_input_val() == HI_GPIO_VALUE1)
    {
    // 3.2 MCU 读取到电平情况为高电平,需要对高电平信号进行过滤
        while (get_dht11_gpio_input_val() == HI_GPIO_VALUE1)
        {
            hi_udelay(1);
            timeout += 1;
            if (TIMEOUT_COUNT == timeout)
            {
                 return HI_ERR_S_FAILURE;
            }
        }
        // 以上 while 过滤正常结束,返回 1
        return 1;
        }
        else
        {
        // 3.1 MCU 读取到的电平情况为低电平,直接返回 0,对应 TH0
        return 0;
    }
}
hi_u8 dht11_read_byte(void)
{
    hi_u8 byte = 0;
    for (size_t i = 0; i < 8; i++)
    {
        byte <<= 1;
        byte |= dht11_read_bit();
    }
    return byte;
}
hi_s8 dht11_read_data(hi_u32 *tem, hi_u32 *tem_dec,
hi_u32 *hum, hi_u32 *hum_dec)
{
    hi_u8 dht11_sensor_data[5] = {0};
    for (size_t i = 0; i < 5; i++)
    {
        dht11_sensor_data[i] = dht11_read_byte();
    }
    if (dht11_sensor_data[0] + dht11_sensor_data[1] + dht11_sensor_data[2] +
    dht11_sensor_data[3] != dht11_sensor_data[4])
    {
        return HI_ERR_S_FAILURE;
    }
    *hum = dht11_sensor_data[0];
    *hum_dec = dht11_sensor_data[1];
    *tem = dht11_sensor_data[2];
    *tem_dec = dht11_sensor_data[3];
    return HI_ERR_SUCCESS;
}
dht11_demo.c
/*
C 语言标准库三剑客
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
以下是需要导入的 OpenHarmony 相关头文件
ohos_init.h OpenHarmony OS 系统初始化相关头文件
cmsis_os2.h 实时操作系统头文件
hi_timer.h 海思提供的 timer 定时器头文件
*/
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_timer.h"
#include "hi_time.h" // 对应 hi_udelay us 延时函数头文件
#include "hi_errno.h" // 使用 Hi3861 提供的官方错误宏定义
// 海思(OHOS) 提供的标准 IO 头文件,所有对外接口的模式,相关函数
#include "hi_io.h"
// 海思(OHOS) 提供的标准 GPIO 头文件,针对 GPIO 相关控制
#include "hi_gpio.h"
#include "dht11_source.h"
void dht11_thread(void *arg);
static void dht11_testTask(void)
{
    osThreadAttr_t thread_attr;
    memset(&thread_attr, 0, sizeof(osThreadAttr_t));
    thread_attr.name = "DHT11_Thread";
    thread_attr.stack_size = 1024;
    thread_attr.priority = osPriorityNormal;
    osThreadId_t tid = osThreadNew(dht11_thread, HI_NULL, &thread_attr);
    if (HI_NULL == tid)
    {
        perror("[osThreadNew] create DHT11_Thread Failed!");
    }
}
APP_FEATURE_INIT(dht11_testTask);
void dht11_thread(void *arg)
{
    hi_u32 tem = 0;
    hi_u32 tem_dec = 0;
    hi_u32 hum = 0;
    hi_u32 hum_dec = 0;
// 初始化完成
    dht11_init();
    while (1)
    {
        // MCU 发送起始信号到 DHT11
        dht11_start_signal();
        // 判断 DHT11 响应结果
        if (dht11_response())
        {
            printf("DHT11 Response Failed!\n");
            osDelay(500);
            continue;
        }
        // DHT11 响应正常,读取 DHT11 数据反馈
        if (dht11_read_data(&tem, &tem_dec, &hum, &hum_dec))
        {
            printf("DHT11 Read Data Failed!\n");
        }
        else
        {

            printf("Temperature : %d.%d, Humidity : %d.%d\n",
            tem, tem_dec, hum, hum_dec);
        }
        osDelay(500);
    }
}

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐