DHT11温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性和卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。
DHT11器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总线完成。单总线通常要求外接一个约5.1kΩ的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结构,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱,器件将不响应主机。
4根引脚,名称与功能如下:
DHT11:异步(无同步时钟)、串行通信(只有一根线),半双工(一根线双向通信),电平信号发送
用户主机(STM32单片机)发送一次开始信号后,DHT11从低功耗模式转换到高速模式,待主机开始信号结束后,DHT11发送响应信号,送出40bit的采集数据,然后结束本次采集任务。
注:主机从DHT11读取的温湿度数据总是前一次的测量值,如两次测间隔时间很长,请连续读两次以第二次获得的值为实时温湿度值。
首先单片机将连接DHT11 DATA引脚的GPIO口输出低电平,且低电平保持时间不能小于18ms (t1)最大不能超多30ms,然后拉高数据线20~40us (t2) ,等待读取DHT11的响应信号。
DHT11的DATA引脚检测到外部信号有低电平(t1),并等待外部低电平信号结束(t2),延迟后DHT11的DATA引脚处于输出状态,之后DHT11开始输出80 us (t3)的低电平作为应答信号,紧接着输出80us(t4)的高电平通知主机准备接收数据。
主机的I/O此时处于输入状态,检测I/O有低电平(DHT11应答信号)后,等待80us的高电平后接受数据。
由DHT11的DATA引脚输出40位数据,采用高位优先方式(MSB),微处理器根据I/0电平的变化接收40位数据。
位数据“0”的格式为:50微秒的低电平和26-28us的高电平。
位数据“1”的格式为:50微秒的低电平加70us的高电平。
单片机在处理数据接收时可以先等待低电平过去,即等待数据线拉高,再延时40us (因为40us大于28us且小于70us) ,再检测此时数据线是否为高,如果为高,则数据判定为1,否则为0。
DHT11在传输数据时,一次传输4字节温湿度值数据和1字节数据校验(总共40bit)。其数据格式为:
1B湿度整数数据+1B湿度小数数据+1B温度整数数据+ 1B温度小数数据+1B校验位。
接收到的40位数据为:
计算:
0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001(校验位)
说明接收数据正确:
湿度:
00110101(整数)=35H(16进制)=53%RH(相对湿度)
00000000(小数)=00H(16进制)=0.0%RH(相对湿度)
最终得出湿度为:53.0%RH
温度:
00011000(整数)=18H(16进制)=24℃
00000100(小数)=04H(16进制)=0.4℃
最终得出温度为:24.4℃
当温度低于0℃时温度数据的低8位的最高位置为1。
-10.1℃表示为 0000 1010 1000 0001 看到1代表为负数,算的时候还是按照0来算,最后加上负号。
温度:
00001010(整数)=0AH(16进制)=10℃
00000001(小数)=01H(16进制)=0.1℃
最终得出温度为:-10.1℃
说明接收数据正确:
计算:
0011 0101 + 0000 0000 + 00011000 + 0000 0100 = 0101 0001不等于校验位,接收错误。本次不接收,重新接收数据。
/*
* dht11.c
*
* Created on: Oct 28, 2022
* Author: Administrator
*/
#include "tim.h"
#include "gpio.h"
#include "main.h"
#include "usart.h"
typedef struct w1_gpio_s
{
GPIO_TypeDef *group;
uint16_t pin;
}w1_gpio_t;
static w1_gpio_t W1Dat =
{
.group = GPIOA,
.pin = GPIO_PIN_5,
};
#define W1DQ_Input() \
{\
GPIO_InitTypeDef GPIO_InitStruct = {0};\
GPIO_InitStruct.Pin = W1Dat.pin;\
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;\
GPIO_InitStruct.Pull = GPIO_PULLUP;\
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;\
HAL_GPIO_Init(W1Dat.group, &GPIO_InitStruct);\
}
#define W1DQ_Output() \
{\
GPIO_InitTypeDef GPIO_InitStruct = {0};\
GPIO_InitStruct.Pin = W1Dat.pin;\
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;\
GPIO_InitStruct.Pull = GPIO_NOPULL;\
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;\
HAL_GPIO_Init(W1Dat.group, &GPIO_InitStruct);\
}
#define W1DQ_Write(x) HAL_GPIO_WritePin(W1Dat.group, W1Dat.pin, (x==1)?GPIO_PIN_SET:GPIO_PIN_RESET)
#define W1DQ_Read(x) HAL_GPIO_ReadPin(W1Dat.group, W1Dat.pin)
static void DHT11_StartSignal(void)
{
W1DQ_Output();
/*主机拉低 >= 18ms*/
W1DQ_Write(0);
HAL_Delay(20);
/*主机拉高 >= 20-40us*/
W1DQ_Write(1);
delay_us(30);
W1DQ_Input();
}
uint8_t DHT11_RespondSignal(void)
{
uint8_t retry = 0;
/*总线变成低电平说明设备发送了响应信号 80us*/
while( W1DQ_Read() && retry < 100)
{
retry++;
delay_us(1);
}
/*超时设备没有收到响应信号*/
if( retry >= 100)
{
printf("1 Timeout No response received\r\n");
return 1;
}
/*从设备再把总线拉高表示从设备要发送数据了:80us*/
retry = 0;
while( !W1DQ_Read() && retry < 100 )
{
retry++;
delay_us(1);
}
if( retry >= 100)
{
printf("2 Timeout No response received\r\n");
return 1;
}
return 0;
}
/*读取一个位*/
uint8_t DHT11_ReadBit(void)
{
uint8_t retry = 0;
/*从设备回复的每个数据以低电平标志开始:50uw*/
while( W1DQ_Read() && retry < 100 )
{
retry++;
delay_us(1);
}
/*数据位都用高电平表示,但是高电平的长短决定数据是1还是0*/
while( !W1DQ_Read() && retry < 100 )
{
retry++;
delay_us(1);
}
/*判断数据位是1(70us)还是0(26-28us)*/
delay_us(40);
if( W1DQ_Read() )
{
return 1;
}
else
{
return 0;
}
}
/*读取一个字节 MSB*/
uint8_t DHT11_ReadByte(void)
{
uint8_t i,dat;
dat = 0;
for(i=0; i<8; i++)
{
dat <<= 1;
dat |= DHT11_ReadBit();//每读取到一个位就当在最后一位
}
return dat;
}
int DHT11_SampleData(float *temperature, float *humidity)
{
uint8_t humi_H8bit;
uint8_t humi_L8bit;
uint8_t temp_H8bit;
uint8_t temp_L8bit;
uint8_t check_sum;
if( !temperature || !humidity)
{
//printf("1\r\n");
return -1;
}
/*主机发送起始信号并且等待设备的响应信号*/
DHT11_StartSignal();
if(0 != DHT11_RespondSignal())
{
//printf("2\r\n");
return -2;
}
humi_H8bit = DHT11_ReadByte();
humi_L8bit = DHT11_ReadByte();
temp_H8bit = DHT11_ReadByte();
temp_L8bit = DHT11_ReadByte();
check_sum = DHT11_ReadByte();
if((humi_H8bit + humi_L8bit + temp_H8bit + temp_L8bit) != check_sum )
{
//printf("3\r\n");
return -3;
}
*humidity = (humi_H8bit*100 + humi_L8bit) / 100.00;
*temperature = (temp_H8bit*100 + temp_L8bit) / 100.00;
return 0;
}
/*
* dht11.h
*
* Created on: Oct 28, 2022
* Author: Administrator
*/
#ifndef INC_DHT11_H_
#define INC_DHT11_H_
#endif /* INC_DHT11_H_ */
int DHT11_SampleData(float *temperature, float *humidity);
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "dht11.h"
/* Private includes ----------------------------------------------------------*/
...
int main(void)
{
/* USER CODE BEGIN 1 */
float temperature, humidity;
/* USER CODE END 1 */
...
printf("Welcome to the DHT11 project\r\n");
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if( DHT11_SampleData(&temperature, &humidity) < 0)
{
printf("ERROR: DHT11 sample data failure\r\n");
}
else
{
printf("DHT11 sample Tempeature: %.3f Relative Humidity: %.3f\r\n", temperature, humidity);
}
HAL_Delay(3000);
}
}
保存烧录然后就可以看见我们获取到的温湿度了;用手捂住可以看见温湿度正在慢慢升高。
代码主要是看着实验室的资料敲出来的,但是都能够理解,看着datasheet敲代码可能还有一些困难,慢慢努力加油吧!
如有错误还请指出~