一、DHT11简介

        DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则

二、DHT11引脚

       

        这里需要说明,由于DHT11的种类有很多,有的是没有NC,有的S数据线在右边,不在中间,所以要找到自己对应设备的引脚图。

三、通讯方式

        DHT11 采用简化的单总线通信。单总线即仅有一根数据线(SDA),通信所进行的数据交换、 挂在单总线上的所有设备之间进行信号交换与传递均在一条通讯线上实现。单总线上必须有一个上拉 电阻(Rp)以实现单总线闲置时,其处于高电平状态。同时所有单总线上的设备包含微处理器和 DHT11 必须通过一个具有并设置为开漏或三态的 IO 端口连至单总线,以实现在进行单总线通信时设 备间交替控制单总线。在单总线中,微处理器与传感器是主从结构,只有微处理器呼叫传感器时,传感器才会应答。微处理器访问传感器必须严格遵循单总线时序要求,否则传感器将不响应主机。        

四、通讯流程

        

        总线空闲状态为高电平,首先单片机要发送起始信号给DHT11,因此DHT11的数据线对应的GPIO口要设置成通用推挽输出,并将数据位设为低电平,拉低的时间必须大于18ms,当起始信号发送完毕后,需要将总线拉高,时间为20~40us。当发送起始信号后,DHT11就要进行响应,首先DHT11将数据位设为浮空输入模式,然后将数据位拉低80us,然后拉高80us,如果成功先拉低再拉高,代表响应成功。

        接着当总线由DHT11拉高后,接着DHT11就开始发送40bit的数据,每一bit数据都以50us低电平时隙开始,高电平的时间长短定了数据位是0还是1,当最后1bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。

数字0信号表示方法

数字1信号表示方法

        如何检测数据是否正确呢?数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和。数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。

五、代码

        我们使用DHT11与STM32F103C8T6连接,来使用DHT11测试温湿度以及将数据提供串口发送器,发送到电脑。

        首先配置DHT11,使用DHT11.c模块,由于需要不断切换数据位的GPIO状态,因此需要使用Mode函数来进行切换。

DHT11.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "DHT11.h"

/*
	开始时序
*/

void DHT11_Start(void)
{
	DHT11_Mode(1);
	GPIO_ResetBits(GPIOA, GPIO_Pin_11);
	Delay_ms(20);
	GPIO_SetBits(GPIOA, GPIO_Pin_11);
	Delay_us(13);
}

/*
	DHT11响应
*/

uint8_t DHT11_Response(void)
{
	uint8_t i = 0;
	DHT11_Mode(0);
	while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) && i < 100)		//	超时保护
	{
		i++;
		Delay_us(1);
	}
	if(i >= 100)
	{
		return 0;										//	代表响应失败
	}
	else
	{
		i = 0;
	}
	while(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) && i < 100)
	{
		i++;
		Delay_us(1);
	}
	if(i >= 100)
	{
		return 0;										//	代表响应失败
	}
	else
	{
		return 1;										//	代表响应成功
	}
}

/*
	读取1位
*/

uint8_t DHT11_ReadBit(void)
{
	uint8_t i;
	while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) && i < 100)		//	超时保护,等待低电平
	{
		i++;
		Delay_us(1);
	}
	i = 0;
	while(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) && i < 100)	//	超时保护,等待高电平
	{
		i++;
		Delay_us(1);
	}
	Delay_us(40);
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11) == 1)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/*
	读取1Byte
*/

uint8_t DHT11_ReadByte(void)
{
	uint8_t i, dat;
	dat = 0;
	for(i = 0; i < 8; i++)
	{
		dat <<= 1;
		dat |= DHT11_ReadBit();
	}
	return dat;
}

/*
	读取Data
*/

uint8_t DHT11_ReadData(uint8_t *temp, uint8_t *humi)
{
	uint8_t i, Array[5];
	DHT11_Start();
	if(DHT11_Response() == 1)
	{
		for(i = 0; i < 5; i++)
		{
			Array[i] = DHT11_ReadByte();
		}
		if(Array[0] + Array[1] + Array[2] + Array[3] == Array[4])
		{
			*humi = Array[0];
			*temp = Array[2];
		}
	}
	else
	{
		return 0;
	}
	return 1;
}

/*
	DHT11初始化
*/

uint8_t DHT11_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;					//	数据引脚
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_11);
	
	DHT11_Start();
	
	return DHT11_Response();
	
}

/*
	由于是单总线传输
	将PA11引脚的工作模式进行切换(通用推挽输出,以及浮空输入)
*/

void DHT11_Mode(uint8_t Mode)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	if(Mode == 1)
	{
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;	
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStruct);
	}
	else
	{
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;	
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStruct);
	}
	GPIO_Init(GPIOA, &GPIO_InitStruct);
}

        在串口模块,我们只需要将单片机读取到的信号发送出去,因此只需要TX就可以完成。

Serial.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;	//	TX引脚
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate = 115200;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode = USART_Mode_Tx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStruct);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for(i=0; String[i]!=0; i++)
	{
		Serial_SendByte(String[i]);
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

        在主函数中,我们使用OLED读取数据,以及使用串口中重定义的printf来打印输出信息。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "DHT11.h"
#include "USART.h"

uint8_t temp;
uint8_t humi;
int main(void)
{
	OLED_Init();
	Serial_Init();
	LED_Init();
	DHT11_Init();
	LED_ON();
	
	printf("Start \n");
	Delay_ms(1000);
	if (DHT11_Init() == 0)  // 初始化失败
	{
		while(1)  // 死循环,一直显示“初始化失败”
		{
			OLED_Clear();
			OLED_ShowChinese(0, 40, "初始化失败");
			OLED_Update();
			Delay_ms(1000);
		}
	}
	
	OLED_ShowChinese(0, 0, "温度:");
	OLED_ShowChinese(0, 20, "湿度:");
	OLED_ShowChinese(0, 60, "C");
	OLED_ShowChinese(0, 60, "%");
	OLED_ShowString(60, 0, "C", OLED_8X16);
	OLED_ShowString(60, 20, "%", OLED_8X16);
	OLED_Update();
	
	while(1)
	{
		DHT11_ReadData(&temp,&humi);
		printf("temp %d ,humi %d\r\n",temp,humi);
		LED_Turn();
		Delay_ms(1000);
		OLED_ShowNum(40, 0, temp, 2, OLED_8X16);
		OLED_ShowNum(40, 20, humi, 2, OLED_8X16);
		OLED_Update();
	}	
}

        当程序下载完成后,OLED显示DHT11采集的数据,并将该数据通过串口调试助手发送到接收区。在主函数加入了1s延时,因此OLED数据的改变会在LED每一次闪烁时改变。

六、总结

        DHT11是一款常用的低成本数字温湿度一体化传感器,由专用的温湿度采集模块和一个高性能8位微处理器组成,能够快速、稳定地采集周围环境的温度和相对湿度数据。 在测量范围上,温度可测范围为0-50℃,测量精度为±2℃;相对湿度的测量范围是20%-90%RH,精度为±5%RH,适用于对温湿度精度要求不高的场景,比如家庭温湿度监测、小型温室、简易气象站等。 它采用单总线通信协议,只需一根数据线即可实现与单片机等微控制器的双向通信,接线简单,电路设计便捷,大大降低了硬件连接的复杂度。同时,传感器内部集成了温湿度校准电路,无需用户进行复杂的校准操作,上电后即可直接使用。 不过,DHT11的测量响应速度相对较慢,两次测量之间需要间隔至少1秒,且在低温或低湿度环境下,测量误差可能会略有增大。但综合其低成本、易使用的特点,仍是入门级温湿度检测项目中的常用选择。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐