STM32 | DHT11 传感器

发布于:2025-07-21 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

Overview

引脚说明:

 典型应用电路:

串行接口 (单线双向)

数据格式:

校验和

通信流程:

时序问题:

“0”和“1”的表示

代码实现

GPIO口初始化:

配置GPIO输入输出的接口

开始信号

检测DHT11对开始信号的响应

读取一个比特数据

读取一个字节数据

 读取数据的接口函数

主函数中调用接口函数


本博客使用的STM32芯片是 F103

Overview

DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。

特点是:

  • 单线制串行接口,
  • 体积小、功耗低
  • 信号传输距离可达 20 米以上
  • 测湿精度:±5%RH  (测量范围 20-90%RH)
  • 测温精度:±2℃ (测量范围 0-50℃)

引脚说明:

DHT11的供电电压为 3-5.5V。

传感器上电后,要等待 1s 以越过不稳定状态,在此期间无需发送任何指令

 

 典型应用电路:

串行接口 (单线双向)

DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分。一次完整的数据传输为40bit (5个字节),高位先出。

数据格式:

[8bit湿度整数数据] + [8bit湿度小数数据] + [8bi温度整数数据] + [8bit温度小数数据] + [8bit校验和]

校验和

可以通过校验和判断数据是否传送正确

校验和数据 =

“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据” 结果的末8位

通信流程:

  1. 用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式。
  2. 等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据。

如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集,采集数据后转换到低速模式。

时序问题:

总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。

DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号。

主机发送开始信号结束后,延时等待20-40us后,可以切换到输入模式,进行读取DHT11的响应信号,或者输出高电平均可, 总线由上拉电阻拉高。

“0”和“1”的表示

==============

一开始都是低电平,看高电平持续时间,判断是“0”还是“1”

“0“:高电平持续 26us ~ 28us

“1“:高电平持续 70 us

代码实现

GPIO口初始化:

初始化时先配置为输出,配置为开漏和推挽都可以

void DHT11_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;


    // PE0
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    // 先设置为输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOE, &GPIO_InitStructure);

    GPIO_SetBits(GPIOE, GPIO_Pin_0);

}

配置GPIO输入输出的接口

输入配置为带上拉


void SetDataIO_Output()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    // 设置为输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOE, &GPIO_InitStructure);
}

void SetDataIO_Input()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    // 设置为输入
    GPIO_Init(GPIOE, &GPIO_InitStructure);
}

开始信号

  1. 配置DATA引脚为输出
  2. 拉低引脚超过18ms
  3. 然后拉高引脚,结束开始信号

void DHT11_Reset(void)
{
    SetDataIO_Output();

    // 拉低引脚超过18ms
    GPIO_ResetBits(GPIOE, GPIO_Pin_0);
    delay_ms(20);
    GPIO_SetBits(GPIOE, GPIO_Pin_0);
    delay_us(30);
}

检测DHT11对开始信号的响应

  1. 配置为输入
  2. 等待DHT11把引脚拉低
  3. 再等待DHT11把引脚拉高
  4. 后续可以准备接收数据了

uint8_t DHT11_Check()
{
    uint8_t retry = 0;
    SetDataIO_Input();

    // 等待DHT11响应信号的第一个下降沿(从高到低)
    while(DHT11_DataIO_IN)
    {
        retry++;
        delay_us(1);

        if (retry > 100)
        {
            printf("[Error] DHT11_Check: 111 timeout \r\n");
            break;
        }
    }

    retry = 0;

    // 等待DHT11响应信号的上升沿(从低到高)
    while(!DHT11_DataIO_IN)
    {
        retry++;
        delay_us(1);

        if (retry > 100)
        {
            printf("[Error] DHT11_Check: 222 timeout \r\n");
            break;
        }
    }

    // 最终检查是否超时,并返回结果
    if (retry > 100)
    {
        return 1;
    }
    return 0;
}

读取一个比特数据

在等待对开始信号的响应时,已经把GPIO切换为输入,所以这里不再操作方向。

每个有效数据来临之前都是低电平,然后通过后面的高电平持续时间判断是0还是1

  1. 先等待低电平(有效数据之前都是拉低)
  2. 检测到低电平后,再变成等待高电平
  3. 一旦高电平来了,可以使用定时器,也可以使用简单延时。(或者可以使用从高电平开始,到低电平到来时的时间差,比较时间差也可以得出结果)
  4. 40us后再次查看电平状态,如果还是高电平,那么就是传输1,否则是0
uint8_t DHT11_ReadBit()
{
    uint8_t retry = 0;

    // 等待变成低电平
    while(DHT11_DataIO_IN)
    {
        retry++;
        delay_us(1);

        if (retry > 100)
        {
            printf("[Error] DHT11_ReadBit: 111 timeout \r\n");
            break;
        }
    }

    retry = 0;

    // 等待变成高电平
    while(!DHT11_DataIO_IN)
    {
        retry++;
        delay_us(1);

        if (retry > 100)
        {
            printf("[Error] DHT11_ReadBit: 222 timeout \r\n");
            break;
        }
    }

    // 等待40us,表示0的高电平持续26us ~ 28us
    delay_us(40);

    // 在判断一次数据
    if (DHT11_DataIO_IN)
    {
        return 1;
    }

    return 0;
}

读取一个字节数据

  • 因为是高位先出,所以一位一位左移
uint8_t DHT11_ReadByte()
{
    uint8_t i = 0;
    uint8_t data = 0;

    for (i = 0; i < 8; i++)
    {
        data <<= 1;
        data |= DHT11_ReadBit();
    }

    return data;
}

 读取数据的接口函数

  1. 先发送开始信号
  2. 检测DHT11对开始信号的响应
  3. 读取5个字节数据
  4. 对校验和进行校验
  5. 通过参数传出结果
uint8_t DHT11_Read_AllData(uint8_t *humiInt, uint8_t *humiDec, uint8_t *tempInt, uint8_t *tempDec)
{
    uint8_t i = 0;
    uint8_t buf[5];
    uint8_t checksum = 0;

    DHT11_Reset();
    if (DHT11_Check() == 0)
    {
        for (i = 0; i < 5; i++)
        {
            buf[i] = DHT11_ReadByte();
        }

        checksum = buf[0] + buf[1] + buf[2] + buf[3];
        if (checksum == buf[4])
        {
            *humiInt = buf[0];
            *humiDec = buf[1];
            *tempInt = buf[2];
            *tempDec = buf[3];

            return 0;
        }
    }

    return 1;
}

主函数中调用接口函数

void task1_task(void *pvParameters)
{
	uint8_t tempInt = 0;
	uint8_t tempDec = 0;
	uint8_t humiInt = 0;
	uint8_t humiDec = 0;
	while(1)
	{
		if (DHT11_Read_AllData(&humiInt, &humiDec, &tempInt, &tempDec) == 0)
		{
			printf("\r\n==========================\r\n");
			printf("temp = %d.%d \r\n",tempInt,tempDec);
			printf("humi = %d.%d \r\n",humiInt,humiDec);
		}
		else
		{
			printf("DHT11_Read_AllData error \r\n");
		}
		delay_ms(2000);
	}

}


网站公告

今日签到

点亮在社区的每一天
去签到