GD32F330系列单片机之DHT11温湿度传感器

发布于:2022-11-09 ⋅ 阅读:(12) ⋅ 点赞:(0) ⋅ 评论:(0)

一、DHT11温湿度传感器

1、DHT11温湿度传感器简介

温湿度数据采集使用DHT11数字温湿度传感器。DHT11是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O口。传感器内部湿度和温度数据40Bit的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11功耗很低,5V电源电压下,工作平均最大电流0.5mA。DHT11实物图如下。

2、DHT11湿温度传感器数据传输

DHT11数字湿温度传感器采用单总线数据格式。单个数据引脚端口完成输入输出双向传输。其数据包由5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。

DHT11的数据格式为:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和。其中校验和数据为前四个字节相加。如图所示。

传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。

图 DHT11的数据格式

由以上数据就可得到湿度和温度的值,计算方法:

湿度= byte4 . byte3=45.0 (%RH)

温度= byte2 . byte1=28.0 ( ℃)

校验= byte4+ byte3+ byte2+ byte1=73(校验正确)

3、DHT11数据发送流程

首先主机发送开始信号,即:拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t2(20~40us)时间,然后读取DHT11的响应。正常的话,DHT11会拉低数据线,保持t3(40~50us)时间,作为响应信号,然后DHT11拉高数据线,保持t4(40~50us)时间后,开始输出数据。DHT11数据发送流程如图所示。

图 DHT11数据发送流程

DHT11输出数字‘0’的时序,如图所示。

图 DHT11输出数字‘0’的时序

DHT11输出数字‘1’的时序,如图所示。

图 DHT11输出数字‘1’的时序

二、程序实现

1、DHT11.h文件

在开发过程使用了GD32F330RB芯片,下面是DHT11的使用代码,已验证完全有效。

包含函数名和宏定义。需要使用的同学直接复制粘贴就可以。

#ifndef __DHT11_H
#define __DHT11_H 

#include "gd32f3x0.h"
#include "systick.h"

//宏定义,DHT11的GPIO口和引脚
#define DHT11_Pin    GPIO_PIN_4
#define DHT11_GPIO    GPIOA

//DHT11作为从机发送给主机信号,所以是读取DHT11的GPIO口和引脚的输入状态
#define DHT11_Input    gpio_input_bit_get(DHT11_GPIO,DHT11_Pin)

//主机发送开始信号。拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t2(20~40us)时间,然后读取DHT11的响应。
#define DHT11_Low    gpio_bit_reset(DHT11_GPIO,DHT11_Pin)
#define DHT11_High    gpio_bit_set(DHT11_GPIO,DHT11_Pin)

void DHT11_Init(void);
void DHT11_Read_Out_Input(uint8_t cmd);
uint8_t DHT11_Read_Byte(void);
uint8_t DHT11_Read_Humi_Temp(uint8_t *HuimH,uint8_t *HuimL,uint8_t *TempH,uint8_t *TempL);

#endif

2、DHT11.c文件

包含函数的具体实现。需要使用的同学直接复制粘贴就可以。

#include "gd32f3x0.h"
#include "DHT11.h"

uint16_t Time = 0;    //防止硬件出错,加个计时条件

//初始化DHT11的IO口        
void DHT11_Init(void)
{     
    rcu_periph_clock_enable(RCU_GPIOA);
    gpio_mode_set(DHT11_GPIO,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,DHT11_Pin);
    gpio_output_options_set(DHT11_GPIO,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,DHT11_Pin);

//主机需要发送开始信号,然后读取数据。
//这是配置DHT11的输入输出模式
//cmd = 1,配置为输出模式
//cmd = 0,配置为输入模式
void DHT11_Read_Out_Input(uint8_t cmd)
{    
    if(cmd == 1)
    {
        gpio_mode_set(DHT11_GPIO,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,DHT11_Pin);
        gpio_output_options_set(DHT11_GPIO,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,DHT11_Pin);
    }
    else
    {
        gpio_mode_set(DHT11_GPIO,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,DHT11_Pin);
    }
}

//读取一个字节数据
//DHT11输出数字‘0’,高电平持续时间是26-28us。DHT11输出数字‘1’,高电平持续时间是70us。
//判断40us后的电平状态,假如40us后,输出仍为高电平,则DHT11输出数字‘1’; 
//假如40us后,输出为低电平,则DHT11输出数字‘0’。
uint8_t DHT11_Read_Byte(void)
{
    uint8_t Data = 0;
    uint8_t i;

    //一个字节Byte为8个位,这里循环8次,读取8位数据。也就是读取一个字节
    for(i=0;i<8;i++)
    {
        //DHT11作为从机发送给主机信号,所以是读取DHT11的GPIO口和引脚的输入状态。这里配置为输入模式
        DHT11_Read_Out_Input(0);
        //低电平持续时间是10-50us
        //读出来的数据不等于0,就跳出循环。
        //防止硬件出错,加个计时条件,超过也跳出循环。
        //这样不会导致程序陷入这里的死循环
        while((DHT11_Input == RESET) && (++Time <1000) );
        Time = 0;
        Data <<= 1;
        delay_1us(40);
        if(DHT11_Input == SET)
        {
            //40us后,DHT11输出高电平,则DHT11输出数字‘1’
            Data |= 0x01;
            //高电平持续时间是70us。等待一个高电平结束。防止硬件出错,加个计时条件,超过也跳出循环。
            while((DHT11_Input == SET) && (++Time <1000) );
        }
    }
    return Data;
}

//读取温湿度数据
//返回1,数据错误;//返回0,工作正常
//主机发送开始信号。拉低数据线,保持至少18ms时间,然后拉高数据线20~40us时间,然后读取DHT11的响应。
//正常DHT11会拉低数据线,保持40~50us时间,作为响应信号。然后DHT11拉高数据线,保持40~50us时间后,开始输出数据。
uint8_t DHT11_Read_Humi_Temp(uint8_t *HuimH,uint8_t *HuimL,uint8_t *TempH,uint8_t *TempL)
{
    uint8_t i;
    uint8_t Data[5];        //存放5个字节的数据,40bit位

    //主机发送开始信号
    DHT11_Read_Out_Input(1);
    DHT11_Low;
    delay_1ms(20);//20ms
    DHT11_High;
    delay_1us(40);
    
    //DHT11响应信号
    DHT11_Read_Out_Input(0);
    //读出来的数据不等于0,就跳出循环。说明DHT11已经跳出拉低数据线的80us时间。
    while((DHT11_Input == RESET) && (++Time <1000) );    //80us 低电平是否过去
    Time = 0;
    //读出来的数据不等于1,就跳出循环。说明DHT11已经跳出拉高数据线的80us时间。
    while((DHT11_Input == SET) && (++Time <1000) );        //80us 高电平是否过去
    Time = 0;
    
    //读取5个字节的数据
    for(i=0;i<5;i++)
    {
        Data[i] = DHT11_Read_Byte();
    }
    
    //校验。校验正确,返回数据。
    delay_1ms(3);//延时,等待数据稳定

    if((Data[0]+Data[1]+Data[2]+Data[3]) == Data[4])
    {
        *HuimH = Data[0];
        *HuimL = Data[1];
        *TempH = Data[2];
        *TempL = Data[3];
    }
    else
    {
        return 1;//返回1,数据错误
    }
    return 0;//返回0,数据正确
}

3、main主程序调用