文章目录
一、DHT11传感器介绍
1.1DHT11简介
DHT11温湿度传感器是一款含有已校准的数字信号输出的温湿度符合传感器,传感器包括一个电阻式感湿元件和一个NTC感温元件,并于一个高性能8位单片机相连接。像如下图这种都是内置了上拉电阻的,使用的时候不需要自己再接上拉电阻。
1.2DHT11工作原理
DHT11次啊用单总线通信。单总线即只有一根数据线,即为上图out引脚,主机对DHT11的数据交换和控制均由单总线完成。单总线通常要求外接一个约5.1k欧姆的上拉电阻,使得总线闲置时,其状态为高电平。由于它们是主从结构,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果序列混乱,器件将不响应主机。其原理图如下:注意NC引脚是不接出来的。DHT11的供电电压为3~5.5V。传感器上电后,要等待1s以越过不稳定状态在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF的电容,用以去耦滤波。
1.3DHT11通信时序
数据总时序
STM32单片机发送一次开始信号后,DHT11从低功耗模式转换到告诉模式,待主机开始信号结束后,DHT11发送响应信号,并发送出5个字节的采样数据,之后结束本次采集任务。
主机发送起始信号
DHT11的out(data)引脚连接单片机GPIO引脚,总线空闲状态是高电平,主机GPIO把总线拉低(>=18ms),以保证DHT11能检测到起始信号,继续延时等待20~40us,切换到输入模式,等待并读取DHT11响应。
从机应答信号
DHT11接收到主机的开始信号后,等待主机发送开始信号到结束,然后向主机发送80us低电平响应信号,之后DHT11再把总线拉高80us,告诉主机准备发送数据。
高低电平的表示
我们要知道的是主机和从机的通信最基本的单位是位(bit),0和1,8个位为一字节,一个字节可以表示一个字符。那如何表示数字0和1呢?在这里,每一bit数据都以50us低电平时隙开始,而高电平的长短决定了数据位是0还是1,格式见下图。如果主机读取响应信号为高电平,则说明DHT11没有响应,检查线路是否正常。当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
数字0信号表示方法:50us低电平+26~28us高电平
数字1信号表示方法:50us低电平+70us高电平
数据传输
DHT11在传输数据时,一次传输4个字节温湿度值数据和1字节的数据校验。其数据格式为:1B湿度整数数据+1B湿度小数数据+1B温度整数数据+1B温度小数数据+1B校验。不过目前温湿度的小数都还没用到。
一次性完整的数据传输为40bit,高位先出:MSB,即大端字节序,高位数存于低地址。
数据传送正确时校验和数据等于“1B湿度整数数据+1B湿度小数数据+1B温度整数数据+1B温度小数数据”所得结果的末8位。
二、DHT11温湿度采样实现
2.1接口说明:
DHT11传感器的工作电压范围为3~5.5V,因此接3.3V和5V都可以,此外将DHT11的I/O口接到单片机的某个GPIO管脚的上(我这里时PA5) 。对于没有内置上拉电阻的DHT11传感器,建议连接线长度短于20米时用5k上拉电阻,大于20米时根据实际情况使用合适上拉电阻。注意:如果传感器上有引脚为S,则表示信号引脚。
硬件连接如下
在这里插入图片描述
2.2编写DHT11源文件
创建DHT11温湿度传感器的驱动源文件dht11.c
dht11.c文件
/*
* dht11.c
*
* Created on: 2022年6月17日
* Author: 28980
*/
#include "dht11.h"
/*创建一个结构体,用来存引脚的组别和引脚号*/
typedef struct w1_gpio_S
{
GPIO_TypeDef *group;
uint16_t pin;
}w1_gpio_t;
/*结构体变量*/
static w1_gpio_t W1Dat =
{
.group = GPIOA,//组别A
.pin = GPIO_PIN_5,//引脚5,即PA5
};
//定义宏,将引脚设置为特定的输入模式,至于为什么没有封装成函数,是因为调用函数过程中的变量等数据需要进栈出栈,消耗大
#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);\
}
/*运用了三目运算符,如果x为1,则HAL_GPIO_WritePin的第三个参数level为GPIO_PIN_SET(1) ,否则为GPIO_PIN_RESET(0),*/
#define W1DQ_Write(x) HAL_GPIO_WritePin(W1Dat.group, W1Dat.pin,\
(x==1)?GPIO_PIN_SET : GPIO_PIN_RESET)
/*用于读取引脚的电平*/
#define W1DQ_Read() HAL_GPIO_ReadPin(W1Dat.group, W1Dat.pin)
/*向主机发送起始信号*/
static void DHT11_StartSignal(void)
{
W1DQ_Output();//设置引脚为输出模式
W1DQ_Write(0);//输出20ms低电平
HAL_Delay(20);
W1DQ_Write(1);//输出30us高电平
delay_us(30);
W1DQ_Input();//设置引脚为输入模式
}
/*主机检测从机应答信号*/
uint8_t DHT11_RespondSignal(void)
{
uint8_t retry = 0;//用于判断是否超市
/*读取引脚状态,如果是高电平,则说明从机DHT11还没有发送低电平信号给主机,将一直循环,直到retry==100*/
while(W1DQ_Read() && retry < 100)
{
retry++;
delay_us(1);
}
/*retry>=100时,说明从机DHT11没有响应主机*/
if(retry >= 100)
{
printf("The host receives no response signal!\r\n");
return 1;
}
retry = 0;
while(!W1DQ_Read() && retry < 100)
{
retry++;
delay_us(1);
}
if(retry >= 100)
{
printf("DHT11 has not received a start signal!\r\n");
return 1;
}
return 0;
}
/*读取一个位的数据,即为高电平或低电平*/
uint8_t DHT11_ReadBit(void)
{
uint8_t retry = 0;
/*如果是低电平,则跳过这个while循环*/
while(W1DQ_Read() && (retry < 100))
{
retry++;
delay_us(1);
}
retry = 0;
while( !W1DQ_Read() && retry < 100)
{
retry++;
delay_us(1);
}
delay_us(40);
if(W1DQ_Read())
{
return 1;
}
else
{
return 0;
}
}
/*读取一个字节的数据*/
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)
return -1;
DHT11_StartSignal();
if(0 != DHT11_RespondSignal())
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)
return -3;
*humidity = (humi_H8bit*1000 + humi_L8bit)/1000;//湿度,整数+小数(保留三位小数)
*temperature = (temp_H8bit*1000 + temp_L8bit)/1000;
return 0;
}
dht11.h文件
/*
* dht11.h
*
* Created on: 2022年6月17日
* Author: 28980
*/
#ifndef INC_DHT11_H_
#define INC_DHT11_H_
#include "main.h"
extern int DHT11_SampleData(float *temperature, float *humidity);
#endif /* INC_DHT11_H_ */
main.h文件需要在main()里面添加的代码
while(1)
{
int flag = DHT11_SampleData(&temperature, &humidity);
if( flag < 0 )
{
printf("ERROR: DHT11 Sample Data failure:%d\r\n", flag);
}
else
{
printf("DHT11 Sample Temperature: %.3f Relative Humidity: %.3f\r\n", temperature,humidity);
}
HAL_Delay(3000);
}