基于STM32的DHT11传感器驱动,代码开源!!!

目录

一、概要

1.1 引脚说明

1.2 注意

二、串行通信说明

2.1 单总线说明

2.2 单总线传送数据位定义

2.3 校验位数据定义

 2.4 数据时序图

三、外设读取步骤 

 3.1 DHT11上电

3.2 起始信号

3.3 从机响应信号

3.4 数据读取

3.5 读取一次温度

四、总结


开发环境

MCUSTM32F103C8T6
传感器DHT11
软件keil MDK 
标准库

一、概要

        DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有枀高的可靠性与卓越的长期稳定性。传感器包括一个电容式感湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接。

1.1 引脚说明

  • VDD 供电 3.3~ 5.5V DC
  • DATA 串行数据,单总线
  • GND 接地,电源负极

1.2 注意

  • 典型应用电路中建议连接线长度短于 5m 时用 4.7K 上拉电阻,大于 5m 时根据实际情况降低上拉电阻的阻值。
  • 使用 3.3V 电压供电时连接线尽量短,接线过长会导致传感器供电不足,造成测量偏差。
  • 每次读出的温湿度数值是上一次测量的结果,欲获取实时数据,需连续读取 2 次,但不建议连续多次读取传感器,每次读取传感器间隔大于 2 秒即可获得准确的数据。
  • 电源部分如有波动,会影响到温度。如使用开关电源纹波过大,温度会出现跳动。

二、串行通信说明

2.1 单总线说明

        DHT11 器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总线完成。设备(主机或从机)通过一个漏枀开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其它设备使用总线;单总线通常要求外接一个约 4.7kΩ 的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结极,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱,器件将不响应主机。

2.2 单总线传送数据位定义

        DATA 用于微处理器与 DHT11 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。

数据格式:
8bit 湿度整数数据 + 8bit 湿度小数数据 + 8bit 温度整数数据 + 8bit 温度小数数据 + 8bit 校验位。
注:其中湿度小数部分为 0。

2.3 校验位数据定义

8bit 湿度整数数据 + 8bit 湿度小数数据 + 8bit 温度整数数据 + 8bit 温度小数数据,8bit 校验位等于
所得结果的末 8 位。
示例一:接收到的 40 位数据为:

0011 01010000 00000001 10000000 01000101 0001
湿度高 8 位湿度低 8 位温度高 8 位温度低 8 位校验位

计算:
0011 0101+0000 0000+0001 1000+0000 0100= 0101 0001
接收数据正确:
湿度: 0011 0101(整数)=35H=53%RH    0000 0000 (小数)=00H = 0.0%RH  

温度:0001 1000(整数)=18H = 24℃   0000 0100(小数)=04H = 0.4℃

当温度低于 0 ℃ 时温度数据的低 8 位的最高位置为 1。
示例: -10.1 ℃ 表示为 0000 1010 1000 0001
温度: 0000 1010(整数)=0AH=10℃, 0000 0001(小数)=01H=0.1℃ =>-(10℃+0.1℃)= -10.1℃

 2.4 数据时序图

         用户主机( MCU)发送一次开始信号后, DHT11 从低功耗模式转换到高速模式,待主机开始信号结束后, DHT11 发送响应信号,送出 40bit 的数据,并触发一次信采集。信号发送如图所示:

注:主机从 DHT11 读取的温湿度数据总是前一次的测量值,如两次测间隔时间很长,请连续读两次以第二次获得的值为实时温湿度值。

三、外设读取步骤 

 3.1 DHT11上电

        DHT11 上电后( DHT11 上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令),
同时 DHT11 的 DATA 数据线由上拉电阻拉高一直保持高电平;此时 DHT11 的DATA 引脚处于输入状态,时刻检测外部信号。程序如下:

/*初始化传感器数据引脚,引脚可自定义*/
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOG, &GPIO_InitStructure);

 	GPIO_SetBits(GPIOG,GPIO_Pin_11);   /* 数据引脚输出高电平 */
} 

3.2 起始信号

        STM32的 I/O 设置为输出同时输出低电平,且低电平保持时间不能小于 18ms(最大不得超过 30ms),然后STM32的 I/O 设置为输入状态,由于上拉电阻,微处理器的 I/O 即 DHT11 的 DATA 数据线也随之变高,等待 DHT11 作出回答信号,时序图如下:

程序如下:

//设置IO为输入
#define DHT11_IO_IN()  {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=8<<12;}
//设置IO为输出
#define DHT11_IO_OUT() {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=3<<12;}

//IO输出										   
#define	DHT11_DQ_OUT PGout(11)
//IO输入
#define	DHT11_DQ_IN  PGin(11)

void DHT11_Rst(void)	   
{                 
	DHT11_IO_OUT();
    DHT11_DQ_OUT=0;
    delay_ms(20);      //根据时序图可知,需要至少拉低18ms
    DHT11_DQ_OUT=1;
	delay_us(30);
}

3.3 从机响应信号

        DHT11 的 DATA 引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后 DHT11 的 DATA引脚处于输出状态,输出 83 微秒的低电平作为应答信号,紧接着输出 87 微秒的高电平通知外设准备接收数据,微处理器的 I/O 此时处于输入状态,检测到 I/O 有低电平( DHT11 回应信号)后,等待 87 微秒的高电平后的数据接收,时序图如下:

程序如下:

u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();                        /* 检测外部电平,就需要将IO设置为输入 */ 
    while (DHT11_DQ_IN&&retry<100)        /* DHT11数据线先拉低,再拉高 */
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    while (!DHT11_DQ_IN&&retry<100)       /* DHT11数据线拉高 */
	{
		retry++;
		delay_us(1);
	};
	if(retry>=100)return 1;	    
	return 0;                             /* 返回0代表有回应,DHT11在线 */
}

3.4 数据读取

        由 DHT11 的 DATA 引脚输出 40 位数据,微处理器根据 I/O 电平的变化接收 40 位数据,位数据“0”的格式为: 54 微秒的低电平和 23-27 微秒的高电平,位数据“1”的格式为: 54 微秒的低电平加 68-74微秒的高电平。时序图如下:

程序如下:

//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);//等待40us
	if(DHT11_DQ_IN)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|=DHT11_Read_Bit();
    }						    
    return dat;
}

3.5 读取一次温度

程序如下:

u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();
	if(DHT11_Check()==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];
			*temp=buf[2];
		}
	}else return 1;
	return 0;	    
}

四、总结

        本章为传感器数据手册与正点原子例程相结合,时序图与程序一一对应,仅仅为学习笔记。

        根据以上程序,稍作修改,即可获取一次温湿度数据。也可定时实现一次数据获取。比如,开个定时器,定时1s,在定时时间到之后获取一次数据并通过串口打印出来。程序完全开源,也可参考正点原子例程。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于STM32的DHT11温湿度传感器的示例代码,使用STM32CubeIDE进行开发。 ```c #include "main.h" #include "dht11.h" #include <stdio.h> UART_HandleTypeDef huart2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART2_UART_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); uint8_t data[5]; while (1) { if (DHT11_ReadData(data) == DHT11_OK) { float temperature = data[2] + ((float)data[3] / 10.0f); float humidity = data[0] + ((float)data[1] / 10.0f); char message[50]; sprintf(message, "Temperature: %.1f C, Humidity: %.1f %%\r\n", temperature, humidity); HAL_UART_Transmit(&huart2, (uint8_t *)message, strlen(message), HAL_MAX_DELAY); } HAL_Delay(2000); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin : PA1 */ GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void HAL_Delay(uint32_t Delay) { uint32_t tickstart = HAL_GetTick(); uint32_t wait = Delay; if (wait < HAL_MAX_DELAY) { wait += (uint32_t)(uwTickFreq); } while ((HAL_GetTick() - tickstart) < wait) { } } void Error_Handler(void) { __disable_irq(); while (1) { } } ``` 注意:这是一个仅供参考的示例代码,使用前请根据具体情况进行适当修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值