DHT11 简介
DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。DHT11 有 3 脚和 4 脚两款,在使用上没有差别,接线都一样,主要接三根,四脚的款式有一脚悬空。
典型接线线路如下图:
引脚:
Pin |
名称 |
注释 |
1 |
VDD |
供电 3-5.5V |
2 |
GND |
接地,电源负极 |
3 |
DATA |
串行数据,单总线 |
4 |
NC |
空脚,请悬空 |
DHT11 采样频率为 1HZ,建议平均 1~2s 读取一次数据。
单总线协议
DHT11 采用单总线协议与单片机通信,也就是使用一根 DATA 线进行数据的收发。单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。
数据格式(共 40bit):
- 8bit 湿度整数数据
- 8bit 湿度小数数据
- 8bit 温度整数数据
- 8bit 温度小数数据
- 8bit 校验和
DHT11 的 DATA 传输一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。
DHT11 只有在接收到开始信号后才触发一次温湿度采集,如果没有接收到主机发送复位信号,DHT11 不主动进行温湿度采集。当数据采集完毕且无开始信号后,DHT11 自动切换到低速模式。
初始化
DHT11 初始化过程分为:
- 主机发送复位信号:首先主机拉低总线至少 18ms,然后再拉高总线,延时 20~40us,取中间值 30us,此时复位信号发送完毕。
- DHT11 发送响应信号:DHT11 检测到复位信号后,触发一次采样,并拉低总线 80us 表示响应信号,告诉主机数据已经准备好了;然后 DHT11 拉高总线 80us,之后开始传输数据。
注意,主机一开始拉低总线是 18 毫秒,不是 18 微秒。
从模式下,DHT11 接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11 不会主动进行温湿度采集。采集数据后转换到低速模式。
实际上 DHT11 的响应时间并不是标准的 80us,往往存在误差,当响应时间处于 20~100us 之间时就可以认定响应成功。
DHT11 传输数据
DHT11 在拉高总线 80us 后开始传输数据。每 1bit 数据都以 50us 低电平时隙开始,告诉主机开始传输一位数据了。DHT11 以高电平的长短定义数据位是 0 还是 1,当 50us 低电平时隙过后拉高总线,高电平持续 26~28us 表示数据“0”;持续 70us 表示数据“1”。
当 最后 1bit 数据传送完毕后,DHT11 拉低总线 50us,表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。
区分 0 和 1 的方法:当数据位之前的 50us 低电平时隙过后,总线肯定会拉高,此时延时 40us 后检测总线状态,如果为高,说明此时处于 70us 的时隙,则数据为“1”;如果为低,说明此时处于下一位数据 50us 的开始时隙,那么上一位数据肯定是“0”。
驱动实现
标准库
1 2 3 4 5 6 7 8 9 10 11 12
| #ifndef _DHT11_H #define _DHT11_H #include "stm32f10x.h"
void DHT11_GPIO_Init_OUT(void); void DHT11_GPIO_Init_IN(void); void DHT11_Start(void); unsigned char DHT11_REC_Byte(void); void DHT11_REC_Data(void);
#endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| #include "stm32f10x.h" #include "dht11.h" #include "SysTick.h"
unsigned int rec_data[4];
#define DHT11_GPIO_Pin_x GPIO_Pin_11
#define dht11_high GPIO_SetBits(GPIOB, DHT11_GPIO_Pin_x) #define dht11_low GPIO_ResetBits(GPIOB, DHT11_GPIO_Pin_x) #define Read_Data GPIO_ReadInputDataBit(GPIOB, DHT11_GPIO_Pin_x)
void DH11_GPIO_Init_OUT(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_Pin_x; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void DH11_GPIO_Init_IN(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_Pin_x; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void DHT11_Start(void) { DH11_GPIO_Init_OUT(); dht11_high; Delay_us(30); dht11_low; Delay_ms(20); dht11_high; Delay_us(30); DH11_GPIO_Init_IN(); }
char DHT11_Rec_Byte(void) { unsigned char i = 0; unsigned char data; for(i=0;i<8;i++) { while( Read_Data == 0); Delay_us(30); data <<= 1; if( Read_Data == 1 ) { data |= 1; } while( Read_Data == 1 ); } return data; }
void DHT11_REC_Data(void) { unsigned int R_H,R_L,T_H,T_L; unsigned char RH,RL,TH,TL,CHECK; DHT11_Start(); dht11_high; if( Read_Data == 0 ) { while( Read_Data == 0); while( Read_Data == 1); R_H = DHT11_Rec_Byte(); R_L = DHT11_Rec_Byte(); T_H = DHT11_Rec_Byte(); T_L = DHT11_Rec_Byte(); CHECK = DHT11_Rec_Byte(); dht11_low; Delay_us(55); dht11_high; if(R_H + R_L + T_H + T_L == CHECK) { RH = R_H; RL = R_L; TH = T_H; TL = T_L; } } rec_data[0] = RH; rec_data[1] = RL; rec_data[2] = TH; rec_data[3] = TL; }
|
1 2 3 4 5 6 7 8 9
| #include "dht11.h" extern unsigned int rec_data[4]; int main_task(void) { while(1) { } }
|
HAL
DHT11 对时序要求严格,需要微妙级延时,我们常用的 HAL_Delay 是毫秒级时延。由于 FreeRTOS 是不支持微秒级延时的,需要自己定义微秒级别的延时函数(详见:STM32 延时函数(FreeRTOS)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #ifndef __dht11_h #define __dht11_h
extern uint8_t temperature; extern uint8_t humidity;
void DHT11_REST(void); uint8_t DHT11_Check(void); uint8_t DHT11_Read_Bit(void); uint8_t DHT11_Read_Byte(void); uint8_t DHT11_Read_Data(uint8_t* humi,uint8_t* temp);
#endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| #include "stm32f1xx_hal.h" #include "delay.h" # us级延时函数
#define DHT11_GPIOx GPIOB #define DHT11_GPIO_PIN GPIO_PIN_11
uint8_t temperature; uint8_t humidity;
void DHT11_OUT(void ) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = DHT11_GPIO_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(DHT11_GPIOx, &GPIO_InitStruct); } void DHT11_IN(void ) { GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = DHT11_GPIO_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT ; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(DHT11_GPIOx, &GPIO_InitStruct); }
void DHT11_REST(void ) { DHT11_OUT(); HAL_GPIO_WritePin (DHT11_GPIOx ,DHT11_GPIO_PIN ,GPIO_PIN_RESET ); HAL_Delay (20); HAL_GPIO_WritePin (DHT11_GPIOx ,DHT11_GPIO_PIN ,GPIO_PIN_SET ); delay_us (30); }
uint8_t DHT11_Check(void) { uint8_t retry = 0;
DHT11_IN();
while (HAL_GPIO_ReadPin(DHT11_GPIOx, DHT11_GPIO_PIN) == 1 && retry < 80) { retry++; delay_us(1); }
if (retry >= 80) return 1; else retry = 0;
while (HAL_GPIO_ReadPin(DHT11_GPIOx, DHT11_GPIO_PIN) == 0 && retry < 80) { retry++; delay_us(1); }
if (retry >= 80) return 1;
return 0; }
uint8_t DHT11_Read_Bit(void) { uint8_t retry=0; while((HAL_GPIO_ReadPin (DHT11_GPIOx ,DHT11_GPIO_PIN)==1) && (retry <100)) { retry ++; delay_us (1); } retry=0; while((HAL_GPIO_ReadPin(DHT11_GPIOx ,DHT11_GPIO_PIN)==0) && (retry <100)) { retry ++; delay_us (1); } delay_us (40); if(HAL_GPIO_ReadPin (DHT11_GPIOx ,DHT11_GPIO_PIN )==1) return 1; else return 0; }
uint8_t DHT11_Read_Byte(void) { uint8_t dat=0; for(uint8_t i=0;i<8;i++) { dat <<= 1; dat |= DHT11_Read_Bit(); } return dat;
}
uint8_t DHT11_Read_Data(uint8_t* humi, uint8_t* temp) { uint8_t buf[5];
DHT11_REST();
if (DHT11_Check() == 0) { for (uint8_t i = 0; i < 5; i++) buf[i] = DHT11_Read_Byte();
if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) { *humi = buf[0]; *temp = buf[2]; } } else { return 1; }
return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include "dht11.h"
int main_task(void) { DHT11_REST(); while(DHT11_Check()) { OLED_ShowString(1,1,"DHT11 No Connect!"); HAL_Delay(500); } while(1) { DHT11_Read_Data(&humidity,&temperature); } }
|
本文参考