使用STM32CubeMX和HAL库读取DHT11温湿度传感器数据

目录

所用的工具:

知识概括:

DHT11介绍:

DHT11性能说明:

接口说明

 电源引脚

串行接口  

工程创建

1、设置RCC

2、时钟设置

3、项目文件设置

4、设置IO口(DATA)

5、TIM时钟配置

6、USART配置

  程序编写:

         1、TIM3实现微秒(us)级延时函数:

2、IO口配置

3、DHT11检测起始信号(这里的DHT11_LOW,DHT11_HIGH 在下面的的第8点)

4、DHT11检测响应信号(这里的DHT11_IO_IN在下面的第8点)

5、DHT11读取一bit数据

6、DHT11读取一个Byte数据

7、DHT11读取湿度和温度的数据

8、IO口输出高、低电平以及读取IO口电平定义和需要添加的头文件

9、温度、湿度和数据缓存数组的声明,函数的声明

10、主循环,(DHT11数据的读取和串口发送)

效果展示

1、将程序下载到开发板

2、串口助手查看数据​编辑

总结 

附录(完整程序)


所用的工具:

1、芯片:STM32F103C8T6

2、软件:STM32CubeMX软件

3、IDE :   MDK_Keil软件

知识概括:

DHT11基本知识和原理

DHT11通讯方式

STM32CubeTIM和USART的配置

DHT11数据采集实现

DHT11介绍:

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传

感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高

的可靠性与卓越的长期稳定性。

DHT11性能说明:

接口说明

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

 电源引脚

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

串行接口  

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

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

         数据格式: 8bit湿度整数数据+8bit湿度小数数据

                          +8bi温度整数数据+8bit温度小数数据

                          +8bit校验和

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

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

*接下来就是重点中的重点了!!

DHT11的通讯过程:

        

        总线空闲状态为高电平,主机把总线拉低等待DHT11响应,
主机
把总线拉低必须
大于18毫秒
,保证DHT11能检测到起始信号。当DHT11接收到主机的起始信号之后,会拉高总线并且等待。开始信号的结束后,将DATA切换成输入模式(这里的DATA指的是那个模块上的IO口切换成输入模式),随后总线为低电平,说明
DHT11
发出
80us低电平
的响应信号,发出响应信号之后 ,
DHT11
再次把总线
拉高80us
,准备开始发送数据,
数据是以一bit位的方式传输
,当最后一bit数据传送完毕后

DHT11
拉低总线50us,随后总线由上拉电阻拉高进入空闲态。

下面这张图的延迟时长非常重要,需要在程序中用到

         总线空闲状态为高电平,
主机
把总线拉低等待DHT11响应,主机把总线拉低必 须
大于18毫秒
,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后, 拉高总线
20us~40us
,等待开始信号结束后,
DHT11
发出
80us低电平
的响应信号,发出响应信号之后 ,
DHT11
再次把总线
拉高80us
并且准备输出数据。

        下面这张是DHT11数据传输的时序图:

        数字0信号表示方法如图所示:

     数字1信号表示方法如图所示:

        

         总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉 高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定 了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有 响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线 50us,随后总线由上拉电阻拉高进入空闲状态。

工程创建

1、设置RCC

2、时钟设置

 3、项目文件设置

  • 1 设置项目名称
  • 2 设置存储路径
  • 3 选择所用IDE
  • 1、 复制所用文件的.C和.H

    2、每个功能生成独立的.C和.H文件

     4、设置IO口(DATA)

    第二步的Moed设置GPIO_OUTPUT和GPIO_INPUT都可以,因为DHT11是单线双向的 ,所以在编写程序时我们需要改写的它IO模式。

     5、TIM时钟配置

           

    第二是选择时钟源,第三步是PSC预分频,第四步是 设置自动重装载值。(这里是设置1us的时间)

    想要具体了解TIM的可以 https://blog.csdn.net/as480133937/article/details/99201209 转到这里看 看又详细又全面。

    6、USART配置

    第二步 Mode设置为异步通信 

      程序编写:

       首先来编写函数部分:

        1、TIM3实现微秒(us)级延时函数:

    /**
      * @brief  TIM3  us级延时函数
      * @param  delay 控制延时的时长
      * @retval 无
      */
    void Delay_us(uint16_t delay)
    {
    	__HAL_TIM_DISABLE(&htim3);
    	__HAL_TIM_SET_COUNTER(&htim3,0);
    	__HAL_TIM_ENABLE(&htim3);
    	uint16_t curCnt=0;
    	while(1)
    	{
    		curCnt=__HAL_TIM_GET_COUNTER(&htim3);
    		if(curCnt>=delay)
    			break;
    	}
    	__HAL_TIM_DISABLE(&htim3);
    }

    2、IO口配置

    /**
      * @brief  DATA引脚(PA7)设置为输出模式
      * @param  无
      * @retval 无
      */
    void DHT11_OUT(void)
    {
    	GPIO_InitTypeDef  GPIO_InitStruct = {0};
    
    	GPIO_InitStruct.Pin = GPIO_PIN_7;
    	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    	GPIO_InitStruct.Pull = GPIO_PULLUP;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    /**
      * @brief  DATA引脚(PA7)设置为输入模式
      * @param  无
      * @retval	无 
      */
    void DHT11_IN(void)
    {
    	GPIO_InitTypeDef  GPIO_InitStruct = {0};
    
    	GPIO_InitStruct.Pin  = GPIO_PIN_7;
    	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    	GPIO_InitStruct.Pull = GPIO_NOPULL;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }

    3、DHT11检测起始信号(这里的DHT11_LOW,DHT11_HIGH 在下面的的第8点)

    /**
      * @brief  DHT11检测起始信号
      * @param  无
      * @retval 无
      */
    void DHT11_Strat(void)
    {
    	DHT11_OUT();   //PA7设置为输出模式
    	DHT11_LOW;     //主机拉低总线
    	HAL_Delay(20); //延迟必须大于18ms ; 
    	DHT11_HIGH;    //主机拉高总线等待DHT11响应
    	Delay_us(30);   
    }

    4、DHT11检测响应信号(这里的DHT11_IO_IN在下面的第8点)

    /**
      * @brief  DHT11发送响应信号
      * @param  无
      * @retval 返回值0/1  0:响应成功 1:响应失败
      */
    uint8_t DHT11_Check(void)
    {
    	uint8_t retry = 0 ;
    	DHT11_IN();
    	//采用while循环的方式检测响应信号
    	while(DHT11_IO_IN && retry <100) // DHT11会拉低 40us ~80us
    	{
    		retry++;
    		Delay_us(1);//1us
    	}
    	if(retry>=100) //判断当DHT11延迟超过80us时return 1 , 说明响应失败
    	{return  1;}
    	else retry =  0 ;
    		
    	while(!DHT11_IO_IN && retry<100)// // DHT11拉低之后会拉高 40us ~80us
    	{
    		retry++;
    		Delay_us(1);//1us
    	}
    		
    	if(retry>=100)
    	{return 1;}
    	return 0 ;
    }

    5、DHT11读取一bit数据

    /**
      * @brief  DHT11读取一位数据
      * @param  无
      * @retval 返回值0/1  1:读取成功 0:读取失败
      */
    uint8_t DHT11_Read_Bit(void)
    {
    	uint8_t retry = 0 ;
    	while(DHT11_IO_IN && retry <100)//同上采用while循环的方式去采集数据
    	{
    		retry++;
    		Delay_us(1);
    	}
    	retry = 0 ;
    	while(!DHT11_IO_IN && retry<100)
    	{
    		retry++;
    		Delay_us(1);
    	}
    
    	Delay_us(40);              //结束信号,延时40us 
    	if(DHT11_IO_IN) return 1;  //结束信号后,总线会被拉高 则返回1表示读取成功
    	else 
    	return 0 ;
    }

    6、DHT11读取一个Byte数据

    /**
      * @brief  DHT11读取一个字节数据
      * @param  无
      * @retval 返回值:dat 将采集到的一个字节的数据返回
      */
    uint8_t DHT11_Read_Byte(void)
    {
    	uint8_t i , dat ;
    	dat = 0 ;
    	for(i=0; i<8; i++)
    	{
    		dat <<= 1; //通过左移存储数据
    		dat |= DHT11_Read_Bit();
    	}
    	return dat ; 
    }

    7、DHT11读取湿度和温度的数据

    /**
      * @brief  DHT11读取数据
      * @param  temp:温度值 humi :湿度值
      * @retval 返回值0/1 0:读取数据成功 1:读取数据失败
      */
    uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi)
    {
    	uint8_t buf[5];        //储存五位数据
        uint8_t i;    
    	DHT11_Strat();         //起始信号
    	if(DHT11_Check() == 0) //响应信号
        {
    		for(i=0; i<5; i++)
    		{
    			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 ;
    }

    以上就是需要用到的函数部分的编写,接下来是一些声明和定义:

    8、IO口输出高、低电平以及读取IO口电平定义和需要添加的头文件

    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    #define DHT11_HIGH     HAL_GPIO_WritePin(GPIOA, DHT11_Pin,	GPIO_PIN_SET) //输出高电平
    #define DHT11_LOW      HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_RESET)//输出低电平
    
    #define DHT11_IO_IN      HAL_GPIO_ReadPin(GPIOA, DHT11_Pin)//读取IO口电平
    
    /* USER CODE END PD */
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include	"stdio.h"
    #include  "string.h"
    
    /* USER CODE END Includes */

    9、温度、湿度和数据缓存数组的声明,函数的声明

    
      /* USER CODE BEGIN 1 */
         uint8_t temperature = 1;  //温度
    
    	 uint8_t humidity = 1;     //湿度
    
    	 uint8_t aTXbuf[32];       //串口发送缓存数组
      /* USER CODE END 1 */
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    
    void Delay_us(uint16_t delay);                //us级延时函数
    
    //DHT11_DATA ,IO口设置为输出模式(这里的IO口指的是STM32CubeMX中配置的IO口)
    void DHT11_OUT(void);	
    												
    void DHT11_IN(void);                          //DHT11_Data IO设置为输入模式
    void DHT11_Strat(void);				          //主机发出起始信号
    uint8_t DHT11_Check(void);                    //DHT11发送响应信号
    uint8_t DHT11_Read_Bit(void);                 //读取DHT11一个BIT的数据
    uint8_t DHT11_Read_Byte(void);                //读取DHT11一个Byte的数据
    uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi);  //读取DHT11湿度和温度的数据
    
    /* USER CODE BEGIN PFP */

    10、主循环,(DHT11数据的读取和串口发送)

      while (1)
      {
    	//接收DHT11的温度和湿度的值
    	DHT11_Read_Data(&temperature , &humidity);  
        //将数据存放到aTXbuf这个数组当中去。 不了解"sprintf"用法的可以去查一下...       
    	sprintf((char*)aTXbuf,"温度:%d℃,湿度: %d %%\r\n" ,temperature ,humidity);
        //将数据通过串口发送到主机上的串口助手
    	HAL_UART_Transmit(&huart1, aTXbuf, strlen((const char*)aTXbuf), 200); 
        //延时500ms
    	HAL_Delay(500);   
    
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }

    到这里DHT11的程序就编写完了 ,下面就来看看效果吧 

    效果展示

    1、将程序下载到开发板

     2、串口助手查看数据

    总结 

    通过本次DHT11温湿度传感器的学习,掌握了单线双向的数据通信方式DHT11和主机之间数据通讯过程,而且在此过程中要注意延时函数的把控精度,在编写程序中DHT11数据读取方式,数据传输方式和数据存储方式,以及微秒级延迟函数的编写,本设计参考正点原子探索者开发板教程STM32Cube高效开发指南(高级篇)

    附录(完整程序)

    /* USER CODE END Header */
    
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "tim.h"
    #include "usart.h"
    #include "gpio.h"
    
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include	"stdio.h"
    #include  "string.h"
    
    /* USER CODE END Includes */
    
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
    
    /* USER CODE END PTD */
    
    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    
    #define DHT11_HIGH     HAL_GPIO_WritePin(GPIOA, DHT11_Pin,	GPIO_PIN_SET)
    #define DHT11_LOW      HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_RESET)
    
    #define DHT11_IO_IN      HAL_GPIO_ReadPin(GPIOA, DHT11_Pin)
    
    /* USER CODE END PD */
    
    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
    
    /* USER CODE END PM */
    
    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    
    /* USER CODE END PV */
    
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    
    void Delay_us(uint16_t delay);              
    void DHT11_OUT(void);													
    void DHT11_IN(void);                          
    void DHT11_Strat(void);				                
    uint8_t DHT11_Check(void);                   
    uint8_t DHT11_Read_Bit(void);                 
    uint8_t DHT11_Read_Byte(void);                
    uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi);        
    
    /* USER CODE BEGIN PFP */
    
    /* USER CODE END PFP */
    
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    
    /* USER CODE END 0 */
    
    /**
      * @brief  The application entry point.
      * @retval int
      */
    int main(void)
    {
      /* USER CODE BEGIN 1 */
    
        uint8_t temperature = 1 ; 
    	uint8_t humidity = 1;
    	uint8_t aTXbuf[32] ;
    
      /* USER CODE END 1 */
      
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_USART1_UART_Init();
      MX_TIM3_Init();
      /* USER CODE BEGIN 2 */
    	
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
    		
    	 DHT11_Read_Data(&temperature , &humidity);
    	 sprintf((char*)aTXbuf,"温度:%d℃ , 湿度:%d %% \r\n" ,temperature ,humidity);
    	 HAL_UART_Transmit(&huart1, aTXbuf, strlen((const char*)aTXbuf), 200);
    	 HAL_Delay(500);
    
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
      /* USER CODE END 3 */
    }
    
    /**
      * @brief System Clock Configuration
      * @retval None
      */
    void SystemClock_Config(void)
    {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};
      RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
      /** Initializes the CPU, AHB and APB busses clocks 
      */
      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
      RCC_OscInitStruct.HSEState = RCC_HSE_ON;
      RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
      RCC_OscInitStruct.HSIState = RCC_HSI_ON;
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
      RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
      RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
        Error_Handler();
      }
      /** Initializes the CPU, AHB and APB busses 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_DIV2;
      RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    
      if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    /* USER CODE BEGIN 4 */
    
    void Delay_us(uint16_t delay)
    {
    	__HAL_TIM_DISABLE(&htim3);
    	__HAL_TIM_SET_COUNTER(&htim3,0);
    	__HAL_TIM_ENABLE(&htim3);
    	uint16_t curCnt=0;
    	while(1)
    	{
    		curCnt=__HAL_TIM_GET_COUNTER(&htim3);
    		if(curCnt>=delay)
    			break;
    	}
    	__HAL_TIM_DISABLE(&htim3);
    }
    
    void DHT11_OUT(void)
    {
    	GPIO_InitTypeDef  GPIO_InitStruct = {0};
    
    	GPIO_InitStruct.Pin = GPIO_PIN_7;
    	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    	GPIO_InitStruct.Pull = GPIO_PULLUP;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    
    void DHT11_IN(void)
    {
    	GPIO_InitTypeDef  GPIO_InitStruct = {0};
    
    	GPIO_InitStruct.Pin  = GPIO_PIN_7;
    	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    	GPIO_InitStruct.Pull = GPIO_NOPULL;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    
    
    void DHT11_Strat(void)
    {
    	DHT11_OUT();
    	DHT11_LOW;
    	HAL_Delay(20);
    	DHT11_HIGH;
    	Delay_us(30);
    }
    
    
    uint8_t DHT11_Check(void)
    {
    	uint8_t retry = 0 ;
    	DHT11_IN();
    		
    	while(DHT11_IO_IN && retry <100) 
    	{
    		retry++;
    		Delay_us(1);//1us
    	}
    	if(retry>=100) 
    	{return  1;}
    	 else retry =  0 ;
    		
    	while(!DHT11_IO_IN && retry<100)
    	{
    		retry++;
    		Delay_us(1);//1us
    	}
    		
    	if(retry>=100)
    	{return 1;}
    	return 0 ;
    }
    
    
    uint8_t DHT11_Read_Bit(void)
    {
    	uint8_t retry = 0 ;
    	while(DHT11_IO_IN && retry <100)
    	{
    		retry++;
    		Delay_us(1);
    	}
    		retry = 0 ;
    	while(!DHT11_IO_IN && retry<100)
    	{
    		retry++;
    		Delay_us(1);
    	}
    	Delay_us(40);
    	if(DHT11_IO_IN) return 1;
    	else 
    	return 0 ;
    }
    
    
    uint8_t DHT11_Read_Byte(void)
    {
    	uint8_t i , dat ;
    	dat = 0 ;
    	for(i=0; i<8; i++)
    	{
    		dat <<= 1;
    		dat |= DHT11_Read_Bit();
    	}
    	return dat ; 
    }
    
    
    uint8_t DHT11_Read_Data(uint8_t* temp , uint8_t* humi)
    {
    	uint8_t buf[5];
    	uint8_t i;
    	DHT11_Strat();
        if(DHT11_Check() == 0)
    	{
    		for(i=0; i<5; i++)
    		{
    			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 ;
    }
    
    /* USER CODE END 4 */

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32CubeMX和HAL库读取DHT11温湿度传感器数据

    发表评论