CH32V203操作DHT11的应用

发布时间 2023-08-30 17:37:18作者: ZaiLi

1、关于DHT11产品介绍

DHT11是一款数字温湿度传感器,单线制串行接口,信号传输距离可达20米以上。湿度测量范围为20—90%RH,精度为±5%RH;温度测量范围为0-50℃,精度为±2℃。通常应用在湿度调节器、暖通空调、测试及检测设备等应用领域。

 

2、关于DHT11应用连接

DHT11具有4个引脚,分别为VDD引脚,单串行总线引脚DATA引脚,NC引脚,GND引脚。其中关于DATA引脚,建议连接长度小于20米时接5K上拉电阻,大于20米时适当降低上拉电阻的阻值。VDD引脚供电范围为3—5.5V,注意传感器上电后需要等待1s度过不稳定状态时间,且在此期间不需要发送任何指令,因此VDD引脚需要接一个100nF的电容,用以去耦滤波。

 

3、关于DHT11应用程序

DHT11信号线为单线双向串行接口总线,用于与DHT11与MCU之间的通讯和同步。程序中仅需配置一个引脚作为DHT11信号线引脚即可,此处选择PB0引脚。

DHT11一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据” 所得结果的末8位。

DHT11的通讯过程如下图所示:

DHT11空闲状态下总线电平为高电平,因此在DHT11初始化时将该总线引脚配置为输出并拉高,具体程序如下:

 //DHT11 初始化函数
void DHT11_Init ( void )
{
    DHT11_GPIO_Config ();
    //DHT11总线空闲状态为高电平,因此在DHT11初始化时将其拉高
    DHT11_Dout_1;               // 拉高PB0
}

//DHT11_GPIO_Config
static void DHT11_GPIO_Config ( void )
{
    /*定义一个GPIO_InitTypeDef类型的结构体*/
    GPIO_InitTypeDef GPIO_InitStructure={0};

    /*开启DHT11_Dout_GPIO_PORT的外设时钟*/
    RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOB, ENABLE );

    /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

    /*设置引脚模式为通用推挽输出*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    /*设置引脚速率为50MHz */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
    GPIO_Init ( GPIOB, &GPIO_InitStructure );
}

 

当开始进行通讯获取DHT11温湿度时,MCU作为主机把DHT11模块总线引脚拉低且拉低时间必须大于等于18ms,方能保证DHT11模块能检测到起始信号并进行响应,起始信号具体程序如下:

    /***********************开始信号**************************/
    /*输出模式*/
    DHT11_Mode_Out_PP();
    /*主机拉低*/
    DHT11_Dout_0;
    /*延时18ms*/
    Delay_Ms(18); //延时时间需要大于等于18ms
    /***********************开始信号**************************/

 

DHT11接收到主机的开始信号后,等待主机开始信号结束 ,此时需要将信号线拉高并延时等待一段时间,大约20-40us,具体程序如下:

    /******************等待开始信号结束**********************/
    /*总线拉高 主机延时30us*/
    DHT11_Dout_1;
    Delay_Us(30);   //延时30us
    /******************等待开始信号结束**********************/

 

DHT11接收到主机的开始信号后,会向主机发送80us低电平响应信号,此时需要将信号线切换为输入模式接收响应信号,当接收到高电平时标志响应信号结束,具体程序如下:

    /*主机设为输入 判断从机响应信号*/
    DHT11_Mode_IPU();
    /*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
    if(DHT11_Dout_IN()==Bit_RESET)
    {
        /*轮询直到从机发出 的80us 低电平 响应信号结束*/
        while(DHT11_Dout_IN()==Bit_RESET);

        /*轮询直到从机发出的 80us 高电平 标置信号结束*/
        while(DHT11_Dout_IN()==Bit_SET);

 

由上图通讯过程波形图可以看出,当检测到响应信号高电平结束标志时,开始接收数据,根据上述数据格式,依次是湿度数据、温度数据、校验数据,具体程序如下:

        /*开始接收数据*/
        DHT11_Data->humi_int= DHT11_ReadByte();

        DHT11_Data->humi_deci= DHT11_ReadByte();

        DHT11_Data->temp_int= DHT11_ReadByte();

        DHT11_Data->temp_deci= DHT11_ReadByte();

        DHT11_Data->check_sum= DHT11_ReadByte();

 

接收数据完成之后,信号总线由输入切换为输出,主机将信号线拉高,表示一次通讯结束,最后根据得到的数据进行校验,具体程序如下:

        /*读取结束,引脚改为输出模式*/
        DHT11_Mode_Out_PP();
        /*主机拉高*/
        DHT11_Dout_1;

        /*检查读取的数据是否正确*/
        if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
            return 1;
        else
            return 0;

 

以上就是DHT11获取数据的一个通讯过程,关于DHT11_ReadByte函数,温湿度以及校验和的数据都是由8位组成的,因此各获取一个字节即可。关于DHT11_ReadByte函数,具体程序如下:

//从DHT11读取一个字节,MSB先行
static uint8_t DHT11_ReadByte ( void )
{
    uint8_t i, temp=0;

    for(i=0;i<8;i++)
    {
        /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/
        while(DHT11_Dout_IN()==Bit_RESET);

        /*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
                         *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时
         */
        Delay_Us(40); //延时x us 这个延时需要大于数据0持续的时间即可

        if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
        {
            /* 等待数据1的高电平结束 */
            while(DHT11_Dout_IN()==Bit_SET);

            temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1,MSB先行
        }
        else     // x us后为低电平表示数据“0”
        {
            temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
        }
    }
    return temp;
}

DHT11发送响应信号结束后,会把总线拉高80us之后发送数据,如上述程序while查询注释。当开始发送数据之后,每一位数据都要以50us低电平间隙表示开始,因此在读取字节的函数中开始位置有一个while查询,查询总线电平是否为低电平。此外DHT11数据的高低(即0、1)是由高电平的持续时间决定的:

当50us的低电平之后,高电平的持续时间为26或28us,则表示数据0;

当50us的低电平之后,高电平的持续时间为70us,则表示数据1,但一般这个时间只要大于程序中定义的延时时间(大于数据0的时间)即可。

如程序中就是定义延时40us,当40us过后还是高电平,则表示1,否则表示0。

以上就是DHT11通讯的一个整个的流程。

 

4、下载运行结果以及整体代码:

dht11.h文件

#ifndef __DHT11_H
#define __DHT11_H

#include "ch32v20x.h"

/************************** DHT11 数据类型定义********************************/
typedef struct
{
    uint8_t  humi_int;      //湿度的整数部分
    uint8_t  humi_deci;     //湿度的小数部分
    uint8_t  temp_int;      //温度的整数部分
    uint8_t  temp_deci;     //温度的小数部分
    uint8_t  check_sum;     //校验和
} DHT11_Data_TypeDef;

/************************** DHT11 函数宏定义********************************/
#define      DHT11_Dout_0                 GPIO_ResetBits ( GPIOB, GPIO_Pin_0 )
#define      DHT11_Dout_1                 GPIO_SetBits ( GPIOB, GPIO_Pin_0 )
#define      DHT11_Dout_IN()              GPIO_ReadInputDataBit ( GPIOB, GPIO_Pin_0 )

/************************** DHT11 函数声明 ********************************/
void DHT11_Init( void );
uint8_t DHT11_Read_TempAndHumidity( DHT11_Data_TypeDef * DHT11_Data );

#endif

dht11.c文件

#include "dht11.h"
#include "debug.h"

static void DHT11_GPIO_Config( void );
static void DHT11_Mode_IPU( void );
static void DHT11_Mode_Out_PP( void );
static uint8_t DHT11_ReadByte( void );

 //DHT11 初始化函数
void DHT11_Init ( void )
{
    DHT11_GPIO_Config ();
    //DHT11总线空闲状态为高电平,因此在DHT11初始化时将其拉高
    DHT11_Dout_1;               // 拉高PB0
}

//DHT11_GPIO_Config
static void DHT11_GPIO_Config ( void )
{
    /*定义一个GPIO_InitTypeDef类型的结构体*/
    GPIO_InitTypeDef GPIO_InitStructure={0};

    /*开启DHT11_Dout_GPIO_PORT的外设时钟*/
    RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOB, ENABLE );

    /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

    /*设置引脚模式为通用推挽输出*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    /*设置引脚速率为50MHz */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
    GPIO_Init ( GPIOB, &GPIO_InitStructure );
}

//使DHT11-DATA引脚变为上拉输入模式
static void DHT11_Mode_IPU(void)
{
    GPIO_InitTypeDef GPIO_InitStructure={0};

    /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

    /*设置引脚模式为浮空输入模式*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;

    /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

//使DHT11-DATA引脚变为推挽输出模式
static void DHT11_Mode_Out_PP(void)
{
    GPIO_InitTypeDef GPIO_InitStructure={0};

    /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

    /*设置引脚模式为通用推挽输出*/
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    /*设置引脚速率为50MHz */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

//从DHT11读取一个字节,MSB先行
static uint8_t DHT11_ReadByte ( void )
{
    uint8_t i, temp=0;

    for(i=0;i<8;i++)
    {
        /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/
        while(DHT11_Dout_IN()==Bit_RESET);

        /*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
                         *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时
         */
        Delay_Us(40); //延时x us 这个延时需要大于数据0持续的时间即可

        if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
        {
            /* 等待数据1的高电平结束 */
            while(DHT11_Dout_IN()==Bit_SET);

            temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1,MSB先行
        }
        else     // x us后为低电平表示数据“0”
        {
            temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
        }
    }
    return temp;
}


/*
   * 一次完整的数据传输为40bit,高位先出
 * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和
 */
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{
    /***********************开始信号**************************/
    /*输出模式*/
    DHT11_Mode_Out_PP();
    /*主机拉低*/
    DHT11_Dout_0;
    /*延时18ms*/
    Delay_Ms(18); //延时时间需要大于等于18ms
    /***********************开始信号**************************/

    /******************等待开始信号结束**********************/
    /*总线拉高 主机延时30us*/
    DHT11_Dout_1;
    Delay_Us(30);   //延时30us
    /******************等待开始信号结束**********************/

    /*主机设为输入 判断从机响应信号*/
    DHT11_Mode_IPU();
    /*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
    if(DHT11_Dout_IN()==Bit_RESET)
    {
        /*轮询直到从机发出 的80us 低电平 响应信号结束*/
        while(DHT11_Dout_IN()==Bit_RESET);

        /*轮询直到从机发出的 80us 高电平 标置信号结束*/
        while(DHT11_Dout_IN()==Bit_SET);

        /*开始接收数据*/
        DHT11_Data->humi_int= DHT11_ReadByte();

        DHT11_Data->humi_deci= DHT11_ReadByte();

        DHT11_Data->temp_int= DHT11_ReadByte();

        DHT11_Data->temp_deci= DHT11_ReadByte();

        DHT11_Data->check_sum= DHT11_ReadByte();


        /*读取结束,引脚改为输出模式*/
        DHT11_Mode_Out_PP();
        /*主机拉高*/
        DHT11_Dout_1;

        /*检查读取的数据是否正确*/
        if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
            return 1;
        else
            return 0;
    }

    else
        return 0;

}

main.c文件

/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.0.0
 * Date               : 2021/06/06
 * Description        : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for 
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/

/*
 *@Note
 USART Print debugging routine:
 USART1_Tx(PA9).
 This example demonstrates using USART1(PA9) as a print debug port output.

*/

#include "debug.h"
#include "dht11.h"

/* Global typedef */

/* Global define */

/* Global Variable */

/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
int main(void)
{
    DHT11_Data_TypeDef DHT11_Data;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n", SystemCoreClock);

    printf("This is printf example\r\n");

    /*初始化DTT11的引脚*/
    DHT11_Init ();

    while(1)
    {
        /*调用DHT11_Read_TempAndHumidity读取温湿度,若成功则输出该信息*/
        if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == 1)
        {
            printf("\r\n读取DHT11成功!\r\n\r\n湿度为%d.%d %RH ,温度为 %d.%d℃ \r\n",\
            DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci);
        }
        else
        {
            printf("Read DHT11 ERROR!\r\n");
        }

        Delay_Ms(2000);
    }

}