玩转传感器——DHT11温湿度传感器(STM32版)

玩转传感器——DHT11温湿度传感器(STM32版)



前言

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。
在这里插入图片描述


一、接口说明

建议连接线长度短于20米时用5K上拉电阻,大于20米时根据实际情况使用合适的上拉电阻。

1 接线图

在这里插入图片描述
在这里插入图片描述

2 电源引脚

DHT11的供电电压为3~5.5 V。传感器上电后,要等待 1s 以越过不稳定状态,在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF 的电容,用以去耦滤波。

3 串行接口(单线双向)

DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零.操作流程如下:

(1)一次完整的数据传输为40bit,高位先出。

(2)数据格式:
8bit湿度整数数据 + 8bit湿度小数数据 + 8bi温度整数数据 + 8bit温度小数数据 + 8bit校验和

(3)数据传送正确时,校验和数据等于
“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位

二、通信过程

(1)如下图所示,用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据。
在这里插入图片描述
从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集。采集数据后转换到低速模式。

(2)如下图所示,总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号。主机发送开始信号结束后,延时等待20-40us后,读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可,总线由上拉电阻拉高。
在这里插入图片描述
(3)如上图所示,总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据。

(4)如下图所示每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1。如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常。当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
数字0信号表示方法如下图所示:
在这里插入图片描述
数字1信号表示方法如下图所示:
在这里插入图片描述
数字0信号与数字1信号的不同之处在于高电平的时间不同,利用这点,我么们可以通过设置电平时间阈值来判断信号的种类。

三、测量分辨率与电气特性

测量分辨率分别为 8bit(温度)、8bit(湿度)。

VDD=5V,T = 25℃,除非特殊标注
在这里插入图片描述
注:最新版厂家升级,采样周期为2秒,老版为1秒。

四、使用注意事项

1 工作与贮存条件

超出建议的工作范围可能导致高达3%RH的临时性漂移信号。返回正常工作条后,传感器会缓慢地向校准状态恢复。要加速恢复进程/可参阅6.3小节的“恢复处理”。在非正常工作条件下长时间使用会加速产品的老化过程。

2 暴露在化学物质中

电阻式湿度传感器的感应层会受到化学蒸汽的干扰,化学物质在感应层中的扩散可能导致测量值漂移和灵敏度下降。在一个纯净的环境中,污染物质会缓慢地释放出去。下文所述的恢复处理将加速实现这一过程。高浓度的化学污染会导致传感器感应层的彻底损坏。

3 恢复处理

置于极限工作条件下或化学蒸汽中的传感器,通过如下处理程序,可使其恢复到校准时的状态。在50-60℃和< 10%RH的湿度条件下保持2 小时(烘干);随后在20-30℃和>70%RH的湿度条件下保持 5小时以上。

4 温度影响

气体的相对湿度,在很大程度上依赖于温度。因此在测量湿度时,应尽可能保证湿度传感器在同一温度下工作。如果与释放热量的电子元件共用一个印刷线路板,在安装时应尽可能将DHT11远离电子元件,并安装在热源下方,同时保持外壳的良好通风。为降低热传导,DHT11与印刷电路板其它部分的铜镀层应尽可能最小,并在两者之间留出一道缝隙。

5 光线

长时间暴露在太阳光下或强烈的紫外线辐射中,会使性能降低。

6 配线注意事项

DATA信号线材质量会影响通讯距离和通讯质量,推荐使用高质量屏蔽线。

五、DHT11驱动程序

1 DHT11.c

1.1 配置输入输出GPIO

在这里插入图片描述

1.2 复位DHT11

复位DHT11就是发送DHT11起始信号,告诉传感器通讯开始。
在这里插入图片描述
在这里插入图片描述

1.3 检查DHT11是否正常

检查DHT11是否正常,正常的话会在单片机发送起始信号完成后,传感器返回80us低电平,然后发送80us高电平。即证明DHT11工作正常,该函数工作正常返回0,否则返回1,该函数中利用了while循环检测在一定时间内的电平变化,此类用法在后面也会经常用到。
在这里插入图片描述
在这里插入图片描述

1.4 DHT11初始化

在上电后,对IO端口初始化,和检查DHT11状态。需要特别指出的是,在上电后到对DHT11初始化前应有1s的稳定期,等待传感器稳定。可以在单片机上电后采用1s延时处理。
在这里插入图片描述

1.5 读取一位数据(返回值0/1)

该函数采用两个while循环是等待每个周期的电平变化,先等待低电平到来,后等待高电平到来,延时40us后判断引脚电平,来判断该位数据为1或0。之所以是40微秒是因为传感器数字0的信号持续时间为26-28us,数字1的信号持续时间为70us,选择一个中值来区分两种信号,当然也可以选择其他值,但最好在40us附近,在while循环中选择循环100次也就是100us,是因为防止当单片机由于某些原因迟迟收不到传感器电平信号,造成死机。
在这里插入图片描述
在这里插入图片描述

1.6 读取一个字节(返回值:读到的数据)

循环读入一个字节的数据,并将每一步新加入的数据放置在最低位。
在这里插入图片描述

1.7 读取DHT11数据(读取成功返回0,失败返回1)

读取数据将数据存入数组,这里仅保留了温度数据的整数位,注意数据较验方法,校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位
在这里插入图片描述

1.8 完成版.c文件

#include "stm32f10x.h"                  // Device header
#include "DHT11.h"
#include "Delay.h"

void DHT11_IO_OUT (void){ //温湿度模块输出函数
	
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    GPIO_InitStructure.GPIO_Pin = DHT11_IO; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

void DHT11_IO_IN (void){ //温湿度模块输入函数
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    GPIO_InitStructure.GPIO_Pin = DHT11_IO; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入      
	GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

void DHT11_RST (void){ 						//DHT11端口复位,发出起始信号(IO发送)
	DHT11_IO_OUT();							//端口为输出
	GPIO_ResetBits(DHT11_PORT,DHT11_IO); 	//使总线为低电平
	Delay_ms(20); 							//拉低至少18ms						
	GPIO_SetBits(DHT11_PORT,DHT11_IO); 		//使总线为高电平							
	Delay_us(30); 							//主机拉高20~40us
}

u8 DHT11_Check(void){ 	//等待DHT11回应,返回1:未检测到DHT11,返回0:成功(IO接收)	   
    u8 retry=0;			//定义临时变量
    DHT11_IO_IN();		//IO到输入状态	 
//GPIO端口输入时,配置为上拉输入或者浮空输入,因为外接上拉电阻,所以默认为高电平
//如果DHT11的数据线输入为高电平,且 retry 小于100,则将 retry 加1,并延时1微秒,重复这个过程直到 retry 大于等于100 或者DHT11的数据线输入变成低电平。如果 retry 大于等于100,表示检测失败,返回1;否则,将 retry 重置为0。
	while ((GPIO_ReadInputDataBit(DHT11_PORT,DHT11_IO) == 1) && retry<100)	//DHT11会拉低40~80us
	{
		retry++;
        Delay_us(1);
    }
    if(retry>=100)return 1; 	
	else retry=0;
//如果DHT11的数据线输入为低电平,且 retry 小于100,则将 retry 加1,并延时1微秒,重复这个过程直到 retry 大于等于100 或者DHT11的数据线输入变成高电平。如果 retry 大于等于100,表示检测失败,返回1;否则,返回0,表示检测成功。
    while ((GPIO_ReadInputDataBit(DHT11_PORT,DHT11_IO) == 0) && retry<100)  //DHT11拉低后会再次拉高40~80us
	{  
        retry++;
        Delay_us(1);
    }
    if(retry>=100)return 1;	    
    return 0;
}

u8 DHT11_Init (void){	//DHT11初始化
	RCC_APB2PeriphClockCmd(DHT11_RCC,ENABLE);	//开始DHT11的时钟
	DHT11_RST();								//DHT11端口复位,发出起始信号
	return DHT11_Check(); 						//等待DHT11回应
}

//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)
{
    u8 retry = 0;
    while((GPIO_ReadInputDataBit(DHT11_PORT,DHT11_IO) == 1) && retry < 100) //等待变为低电平
    {
        retry++;
        Delay_us(1);
    }
    retry = 0;
    while((GPIO_ReadInputDataBit(DHT11_PORT,DHT11_IO) == 0) && retry < 100) //等待变高电平
    {
        retry++;
        Delay_us(1);
    }
    Delay_us(40);//等待40us
    if(GPIO_ReadInputDataBit(DHT11_PORT,DHT11_IO) == 1)       //用于判断高低电平,即数据1或0
        return 1;
    else
        return 0;
}

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)
{
    u8 i, dat;
    dat = 0;
    for (i = 0; i < 8; i++)
    {
        dat <<= 1;					//左移运算符,dat左移1位
        dat |= DHT11_Read_Bit();	//"|"表示按位或等于
    }
    return dat;
}

//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp, u8 *humi)
{
    u8 buf[5];
    u8 i;
    DHT11_RST();						//DHT11端口复位,发出起始信号
    if(DHT11_Check() == 0)				//等待DHT11回应,0为成功回应
    {
        for(i = 0; i < 5; i++) 			//读取40位数据
        {
            buf[i] = DHT11_Read_Byte();	//读出数据
        }
        if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])	//数据校验
        {
            *humi = buf[0];				//将湿度值放入指针humi
            *temp = buf[2];				//将温度值放入指针temp
        }
    }
    else return 1;
    return 0;
}


2 DHT11.h

定义了以下几个函数和IO口宏定义,标注了各自的功能。
在这里插入图片描述


总结

本文介绍DHT11温湿度传感器,及STM32版本驱动函数的编写

  • 407
    点赞
  • 2806
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 40
    评论
以下是使用STM32的HAL库编写DHT11湿度传感器程序的示例代码: ```c #include "stm32f4xx_hal.h" #define DHT11_PORT GPIOA #define DHT11_PIN GPIO_PIN_0 TIM_HandleTypeDef htim2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); while (1) { // 等待 1s HAL_Delay(1000); // 发送开始信号 HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); HAL_Delay(30); // 接收数据 uint8_t buffer[5] = {0}; uint8_t bit_count = 0; uint8_t byte_count = 0; uint8_t data = 0; for (int i = 0; i < 40; i++) { // 等待下一位数据的到来 while (!HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)); // 计时,以确定数据位是 0 还是 1 TIM2->CNT = 0; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)); uint16_t time = TIM2->CNT; if (time > 40) { // 数据位为 1 data |= (uint8_t)(1 << (7 - bit_count)); } bit_count++; if (bit_count == 8) { buffer[byte_count] = data; byte_count++; data = 0; bit_count = 0; } } // 验证校验和 if (buffer[4] == (buffer[0] + buffer[1] + buffer[2] + buffer[3])) { // 计算度和湿度 uint16_t humidity = ((uint16_t)buffer[0] << 8) | buffer[1]; uint16_t temperature = ((uint16_t)buffer[2] << 8) | buffer[3]; // 输出度和湿度 float h = (float)humidity / 10.0f; float t = (float)temperature / 10.0f; printf("Humidity: %.1f%% Temperature: %.1fC\r\n", h, t); } else { printf("Checksum error!\r\n"); } } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = DHT11_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct); } static void MX_TIM2_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; __HAL_RCC_TIM2_CLK_ENABLE(); htim2.Instance = TIM2; htim2.Init.Prescaler = 100 - 1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 65535; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } } ``` 请注意,此代码仅供参考,您应该根据您的具体硬件和需求进行修改。在编写自己的程序之前,请确保您已经正确地连接了DHT11传感器

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZRob

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值