认识模块
DTH11内部集成了温湿度传感器模块和数字式信号处理电路,它可以将温度和湿度值转换为数字信号输出。数字信号输出格式为二进制数,包括40位数据,其中包含8位的湿度整数数据、8位的湿度小数数据、8位的温度整数数据、8位的温度小数数据以及8位校验和。STM32通过读取DTH11输出的数字信号,就可以得到温度和湿度的精确值,进而对环境进行控制。
STM32单片机通过引脚与dth11传感器进行连接,包括VCC、GND、DATA三个引脚。单片机向dth11传感器发送开始信号,即拉低DATA引脚,持续至少18ms,然后再将DATA引脚拉高,等待dth11传感器的响应。此时dth11传感器会发送一个80us的低电平响应信号,然后再发送一个80us的高电平信号,表示已经准备好发送数据。接着,dth11传感器开始向单片机发送40位二进制数据,包括湿度值、温度值、校验位等信息。每一位数据都有固定的发送方式,即:首先,dth11传感器会将DATA引脚拉低50us,表示开始发送一位数据;其次,dth11传感器会根据实际数值将DATA引脚拉高26~28us表示0,或者拉高70us表示1;最后,单片机需要在每一位数据发送完毕后,将DATA引脚拉高,等待下一位数据的发送。当单片机接收到40位数据后,需要对数据进行解析,获取温度和湿度数值,以及校验位信息。
通过查阅资料,可知dht11的DATA引脚与PG9相连。如下图所示:
代码实现
DHT11.c
#include "DHT11.h"
void dht11_outputmode(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//初始化DHT11对应的引脚 PG9 的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
//2、通过结构体初始化DHT11引脚
/* 配置PG9 引脚为输出模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //配置的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度为100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //上下拉电阻:无上下拉电阻
GPIO_Init(GPIOG, &GPIO_InitStructure);
}
void dht11_inputmode(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//初始化DHT11对应的引脚 PG9 的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
//2、通过结构体初始化DHT11引脚
/* 配置PG9 引脚为输出模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //配置的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度为100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //上下拉电阻:无上下拉电阻
GPIO_Init(GPIOG, &GPIO_InitStructure);
}
//发送开始信号和接收响应信号
uint32_t dht11_start(void)
{
uint32_t i;
/* 发送开始信号 */
//配置PG9位输出模式
dht11_outputmode();
//引脚输出低电平
PGout(9) = 0;
delay_ms(20);
//引脚输出高电平
PGout(9) = 1;
delay_us(30);
/* 接收响应信号 */
//配置PG9位输入模式
dht11_inputmode();
//检测低电平,最多持续100us
i=0;
while(i<100)
{
//检测到低电平跳出循环
if(PGin(9) == 0)
break;
i++;
delay_us(1);
}
if(i>=100) //上面的循环是超时跳出,对方无响应
return 1;
//检测高电平,最多持续100us
i=0;
while(i<100)
{
//检测到高电平跳出循环
if(PGin(9))
break;
i++;
delay_us(1);
}
if(i>=100) //上面的循环是超时跳出,对方无响应
return 2;
return 0;
}
uint8_t dht11_readbyte(void)
{
int i;
uint8_t d=0;
//跳过前面的高电平
while(PGin(9));
for(i=0; i<8; i++)
{
//跳过前面50us低电平
while(PGin(9)==0);
delay_us(40);
if(PGin(9)) //引脚为高电平,说明数据为1
{
d |= 1<<(7-i);//保存数据
while(PGin(9)); //跳过剩余的高电平时间
}
}
return d;
}
int dht11_read_data(uint8_t *buf)
{
//让DHT11启动
int i=0;
while(i<100)
{
//启动成功,跳出循环
if(dht11_start() == 0)
break;
i++;
delay_us(1);
}
if(i>=100)//上面循环是由于超时退出,DHT11没有启动
return -1;
//读取5字节数据
for(i=0; i<5; i++)
{
buf[i] = dht11_readbyte();
}
//计算检验和
if(buf[4] != buf[0]+buf[1]+buf[2]+buf[3])
return -2;
return 0;
}
DHT11.h
#ifndef _DHT11_H
#define _DHT11_H
//C文件中需要的其他的头文件
#include <stm32f4xx.h>
#include "sys.h"
#include "delay.h"
//C文件中定义的函数的声明
int dht11_read_data(uint8_t *buf);
#endif
主函数部分
main.c
#include <stm32f4xx.h>
#include "sys.h"
#include <stdio.h>
#include "delay.h"
#include "uart.h"
#include "DHT11.h"
//重定向fputc函数
int fputc(int ch, FILE *F)
{
//通过串口1发送数据到PC
USART_SendData(USART1, ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//等待发送数据完毕
return ch;
}
u8 Temperature_yu = 40;//温度的阈值
int main(void)
{
uint8_t buf[5];//保存温湿度数值的数组
//确定系统定时器的工作频率 内核的工作频率/8 = 168MHz/8 = 21MHz
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
UART1_Config();//配置串口
while(1)
{
if(dht11_read_data(buf)==0)
{
printf("湿度:%d.%d 温度:%d.%d\n",buf[0], buf[1],buf[2],buf[3]);
}
else
{
printf("采集校验错误\n");
}
}
return 0;
}