接口:建议连接线长度短于20米时用5K上拉电阻,大于20米时根据实际情况使用合适的上拉电阻。
型号 | 测量范围 | 测湿精度 | 测温精度 | 分辨力 | 封装 |
DHT11 | 20-90%RH 0-50℃ | ±5%RH | ±2℃ | 1 | 4 针单排直插 |
DHT11的供电电压为3-5.5V。传感器上电后,要等待1s 以越过不稳定状态,在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF 的电容,用以去耦滤波。
2.5.1 单总线工作原理:DATA 输出线用于单片机与DHT11之间的通讯,一次通讯时间4ms左右;单片机发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取数据,采集数据后转换到低速模式;如果没有接收到主机发送的开始信号,DHT11不会主动进行温湿度采集。
2.5.2 数据格式:
2.5.3 开始信号与响应信号
开始信号:由于上拉电阻,总线空闲状态为高电平,主机发出开始信号,把总线拉低大于18ms,拉高电平延时等待20-40us后, 读取DHT11的响应信号;
响应信号:DHT11接收到主机的开始信号后,等待主机开始信号结束,发送80us低电平响应信号,再把总线拉高80us;主机发送开始信号结束后,可以切换到输入模式,总线由上拉电阻拉高或者输出高电平均可。
2.5.4 数据信号与结束信号
数据信号:每一bit数据都以50us低电平开始,高电平的长短决定数据位是0还是1,如下图所示:
结束信号:当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
//主函数
#include
#include "stm32f10x.h"
//#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_rccclkconfig.h"
#include "dht11.h"
#include "bsp_systick.h"
extern uint8_t dat[5];
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
USART_Config();
HSE_SetSysClk( RCC_PLLMul_6 );
printf( "串口printf函数测试\n" );
while (1)
{
if(DHT_Read())
printf("湿度:%d%%,温度:%d度\r\n",dat[0],dat[2]);
SysTick_Delay_ms(3000);
}
}
/*********************************************END OF FILE**********************/
//dht11.c程序
#include "dht11.h"
#include "bsp_systick.h"
uint8_t dat[5]={0x00,0x00,0x00,0x00,0x00};
uint8_t sum=0;
//初始化为输出
void DHT11_GPIO_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//初始化为输入
void DHT11_GPIO_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//读一个字节
uint8_t DHT_Read_Byte(void)
{
uint8_t temp;
uint8_t ReadDat=0;
uint8_t retry = 0;
uint8_t i;
for(i=0;i<8;i++)
{
//数据信号低电平50us
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0&&retry<100)
{
SysTick_Delay_us(1);
retry++;
}
retry=0;
SysTick_Delay_us(30);
temp=0;//数字信号0,temp=0
//数字0信号高电平持续28us,数字1信号高电平70us,延时30us以确认数字0或1
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1) temp=1;
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1&&retry<100)//数字1信号高电平剩余40us
{
SysTick_Delay_us(1);
retry++;
}
retry=0;
ReadDat<<=1;
ReadDat|=temp;
}
return ReadDat;
}
uint8_t DHT_Read(void)
{
uint8_t i;
uint8_t retry = 0;
//主机设置为输出,发送开始信号低电平18ms,高电平40us
DHT11_GPIO_OUT();
GPIO_ResetBits(GPIOB,GPIO_Pin_14);
SysTick_Delay_ms(18);
GPIO_SetBits(GPIOB,GPIO_Pin_14);
SysTick_Delay_us(40);
//主机设置为输入,检查并接收响应信号低电平80us,高电平80us
DHT11_GPIO_IN();
SysTick_Delay_us(20);
//延时20us,低电平80us,还剩60us,检查是否是低电平以确定是否有响应信号
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0)
{
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0&&retry<100)//接收响应信号低电平剩余60us
{
SysTick_Delay_us(1);
retry++;
}
retry=0;//超过100us自动向下运行,以免卡死
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==1&&retry<100)//接收响应信号高电平80us
{
SysTick_Delay_us(1);
retry++;
}
retry=0;
//接收8字节数据
for(i=0;i<5;i++)
{
dat[i]=DHT_Read_Byte();
}
SysTick_Delay_us(50);//DHT11拉低总线50us作为结束信号,或者使用以下语句
// while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0&&retry<100)//接收响应信号低电平剩余60us
// {
// SysTick_Delay_us(1);
// retry++;
// }
// retry=0;
}
sum=dat[0]+dat[1]+dat[2]+dat[3];
if(dat[4]==sum)
{
return 1;
}
else
return 0;
}
//dth11.h程序
#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f10x.h"
void DHT11_GPIO_OUT(void);
void DHT11_GPIO_IN(void);
uint8_t DHT_Read_Byte(void);
uint8_t DHT_Read(void);
#endif
//延时.c函数
#include "bsp_systick.h"
#if 0
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
// 判断 tick 的值是否大于 2^24,如果大于,则不符合规则
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);
// 初始化reload寄存器的值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
// 配置中断优先级,配置为15,默认为最低的优先级
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
// 初始化counter的值为0
SysTick->VAL = 0;
// 配置 systick 的时钟为 72M
// 使能中断
// 使能systick
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0);
}
#endif
void SysTick_Delay_us(uint32_t us)
{
uint32_t i;
SysTick_Config(72);
for(i=0; iCTRL) & (1<<16)) );
}
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
void SysTick_Delay_ms(uint32_t ms)
{
uint32_t i;
SysTick_Config(72000);
for(i=0; iCTRL) & (1<<16)) );
}
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
//延时.h函数
#ifndef __BSP_SYSTICK_H
#define __BSP_SYSTICK_H
#include "stm32f10x.h"
#include "core_cm3.h"
void SysTick_Delay_us(uint32_t us);
void SysTick_Delay_ms(uint32_t ms);
#endif /* __BSP_SYSTICK_H */
//12M晶振替换8M,RCC.C程序配置频率
#include "bsp_rccclkconfig.h"
void HSE_SetSysClk( uint32_t RCC_PLLMul_x )
{
ErrorStatus HSEStatus;
// 把RCC 寄存器复位成复位值
RCC_DeInit();
// 使能 HSE
RCC_HSEConfig(RCC_HSE_ON);
HSEStatus = RCC_WaitForHSEStartUp();
if( HSEStatus == SUCCESS )
{
// 使能预取指
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
// 配置 PLLCLK = HSE * RCC_PLLMul_x
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
// 使能PLL
RCC_PLLCmd(ENABLE);
// 等待PLL稳定
while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
// 选择系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while( RCC_GetSYSCLKSource() != 0x08 );
}
else
{
/* 如果HSE 启动失败,用户可以在这里添加处理错误的代码 */
}
}
void HSI_SetSysClk( uint32_t RCC_PLLMul_x )
{
__IO uint32_t HSIStatus = 0;
// 把RCC 寄存器复位成复位值
RCC_DeInit();
// 使能 HSI
RCC_HSICmd(ENABLE);
HSIStatus = RCC->CR & RCC_CR_HSIRDY;
if( HSIStatus == RCC_CR_HSIRDY )
{
// 使能预取指
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
// 配置 PLLCLK = HSE * RCC_PLLMul_x
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x);
// 使能PLL
RCC_PLLCmd(ENABLE);
// 等待PLL稳定
while( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET );
// 选择系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while( RCC_GetSYSCLKSource() != 0x08 );
}
else
{
/* 如果HSI 启动失败,用户可以在这里添加处理错误的代码 */
}
}
void MCO_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 选择GPIO8引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
//设置为复用功能推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//设置IO的翻转速率为50M
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA8
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//12M晶振替换8M晶振,重新设置频率.H程序
#ifndef __BSP_RCCCLKCONFIG_H
#define __BSP_RCCCLKCONFIG_H
#include "stm32f10x.h"
void HSE_SetSysClk( uint32_t RCC_PLLMul_x );
void MCO_GPIO_Config(void);
void HSI_SetSysClk( uint32_t RCC_PLLMul_x );
#endif /*__BSP_RCCCLKCONFIG_H */