STM32基于STM32-HAL工程读取DHT11数据

STM32基于STM32-HAL工程读取DHT11数据


  • ✨申明:本文章仅发表在CSDN网站,任何其他网站,未注明来源,见此内容均为盗链和爬取,请多多尊重和支持原创!
  • 对于文中所提供的相关资源链接将作不定期更换。
  • 相关篇《STM32基于STM32-HAL工程读取DHT11/DHT22/AM2302/AM2301》
  • 本工程经实物验证没有问题。
  • 本驱动案例需要占用一个STM32一个定时器。也就是说在STM32CUBEMX配置的时候要配置一个定时器。

在调试该输出数据时,掉坑里面去了。刚开始以为是驱动代码的问题,没有读到数据,实际上是由于printf输出时,数据类型转换错误或者栈溢出问题导致的。

在使用printf函数输出时注意事项

在使用printf函数打印结构体成员时,需要先通过指针访问结构体成员,然后再将访问结果传递给printf函数。如果直接使用结构体变量,在某些情况下会导致数据类型转换错误或者栈溢出等问题,因此最好使用指针来访问结构体成员。

  • ❌错误示范;
//定义了一个结构体
typedef struct
{
	uint8_t humi_int;				// 湿度的整数部分
	uint8_t humi_deci;	 		// 湿度的小数部分
	uint8_t temp_int;	 			// 温度的整数部分
	uint8_t temp_deci;	 		// 温度的小数部分
		                 
} DHT11_Data_TypeDef;

//定义了一个形参为:指向该结构体的指针函数
uint8_t DHT11_Read(DHT11_Data_TypeDef *DHT11_Data)
{
    uint8_t buf[5];
    uint8_t 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])
        {
		DHT11_Data->humi_int  = buf[0];
        DHT11_Data->humi_deci = buf[1];
        DHT11_Data->temp_int  = buf[2];
        DHT11_Data->temp_deci = buf[3];
        }
    }
    else return 1;
    return 0;
}

DHT11_Data_TypeDef *dt	//创建一个结构体指针变量
DHT11_Read(dt);//将dt结构体指针变量传给进去
/*****以上都没有问题******/
printf("Temp=%d.%d°C,Humi=%d.%d%% \r\n", dt->temp_int, dt->temp_deci, dt->humi_int, dt->humi_deci);
//问题在这一句调试输出上。

在使用printf函数打印结构体成员时,需要先通过指针访问结构体成员,然后再将访问结果传递给printf函数。如果直接使用结构体变量,在某些情况下会导致数据类型转换错误或者栈溢出等问题,因此最好使用指针来访问结构体成员。需要理解的是指针传值操作本身是没有问题的,在printf调试信息输出时最好不要这么用

  • ✅正确示范:
typedef struct
{
	uint8_t humi_int;				// 湿度的整数部分
	uint8_t humi_deci;	 		// 湿度的小数部分
	uint8_t temp_int;	 			// 温度的整数部分
	uint8_t temp_deci;	 		// 温度的小数部分
		                 
} DHT11_Data_TypeDef;

//定义了一个形参为:指向该结构体的指针函数
uint8_t DHT11_Read(DHT11_Data_TypeDef *DHT11_Data)
{
    uint8_t buf[5];
    uint8_t 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])
        {
				DHT11_Data->humi_int  = buf[0];
        DHT11_Data->humi_deci = buf[1];
        DHT11_Data->temp_int  = buf[2];
        DHT11_Data->temp_deci = buf[3];
        }
    }
    else return 1;
    return 0;
}
DHT11_Data_TypeDef dht{0};//定义一个结构体成品变量并初始化为0;
DHT11_Data_TypeDef *dt=&dht;	//创建一个结构体指针变量并将dht的地址赋值给指针变量。
DHT11_Read(dt);//将dt结构体指针变量传给进去
printf("Temp=%d.%d°C,Humi=%d.%d%% \r\n", dht.temp_int, dht.temp_deci, dht.humi_int, dht.humi_deci);
//这样输出就没有问题。

也可以理解未进行未初始化的结构体指针变量的地址不是固定的,在调用完之后再去访问可能不确定了。

  • 串口打印DHT11读取到的数据:
    STM32基于STM32-HAL工程读取DHT11数据_第1张图片

STM32CubeMX工程配置

  • 定时器配置一个。可以根据芯片自行选择一个定时器。
    STM32基于STM32-HAL工程读取DHT11数据_第2张图片

72-1对于STM32F103也就是1MHz,1us周期。

  • 配置DHT11数据引脚
    STM32基于STM32-HAL工程读取DHT11数据_第3张图片
  • 配置一个串口用于调试信息输出。
    STM32基于STM32-HAL工程读取DHT11数据_第4张图片

DHT驱动代码

  • dht11.h文件
#ifndef _DHT11_H_
#define _DHT11_H_

#include "stm32f1xx.h"
#include "tim.h"

#define	_DHT11_TIMER	htim6 //移植项目都需要关注这里的定时器句柄

#define DHT11_Pin 	GPIO_PIN_8	//自定义引脚需要和配置一致

#define GPIO_Port 	GPIOA	//自定义引脚需要和配置一致

#define DHT11_DQ_IN HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_SET)
typedef struct
{
	uint8_t humi_int;				// 湿度的整数部分
	uint8_t humi_deci;	 		// 湿度的小数部分
	uint8_t temp_int;	 			// 温度的整数部分
	uint8_t temp_deci;	 		// 温度的小数部分
		                 
} DHT11_Data_TypeDef;

void DHT11_Rst(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,uint8_t *tem,uint8_t *hum);
uint8_t DHT11_Init(void);
void DHT11_IO_IN(void);
void DHT11_IO_OUT(void);
uint8_t DHT11_Read(DHT11_Data_TypeDef *DHT11_Data);
#endif



  • DHT11.c文件
#include "DHT11.h"

void Delay_us(uint16_t us)      //微秒延时
{
    HAL_TIM_Base_Stop(&htim6);
    __HAL_TIM_SET_COUNTER(&htim6, 0);
    HAL_TIM_Base_Start(&htim6);
    while (__HAL_TIM_GET_COUNTER(&htim6) <= us);
    HAL_TIM_Base_Stop(&htim6);
}


uint8_t DHT11_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOA_CLK_ENABLE();

    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIO_Port, DHT11_Pin, GPIO_PIN_SET);

    /*Configure GPIO pin : PtPin */
    GPIO_InitStruct.Pin = DHT11_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIO_Port, &GPIO_InitStruct);

    DHT11_Rst();
    return DHT11_Check();
}
//复位DHT11
void DHT11_Rst(void)
{
    DHT11_IO_OUT();     //SET OUTPUT
    HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_RESET);    //拉低
    HAL_Delay(20);      //拉低延时至少18ms
    HAL_GPIO_WritePin(GPIOA, DHT11_Pin, GPIO_PIN_SET);  //DQ=1,拉高
    Delay_us(30);       //拉高延时至少20~40us
}

//检测回应
//返回1:检测错误
//返回0:检测成功
uint8_t DHT11_Check(void)
{
    uint8_t retry = 0;
    DHT11_IO_IN();//SET INPUT
    while (HAL_GPIO_ReadPin(GPIO_Port, DHT11_Pin) && retry < 100) //DHT11拉低40~80us
    {
        retry++;
        Delay_us(1);
    };
    if (retry >= 100)return 1;
    else retry = 0;
    while (!HAL_GPIO_ReadPin(GPIO_Port, DHT11_Pin) && retry < 100) //DHT11再次拉高40~80us
    {
        retry++;
        Delay_us(1);
    };
    if (retry >= 100)return 1;
    return 0;
}

//读取一个位Bit
//返回1或0
uint8_t DHT11_Read_Bit(void)
{
    uint8_t retry = 0;
    while (HAL_GPIO_ReadPin(GPIO_Port, DHT11_Pin) && retry < 100) //等待变低电平
    {
        retry++;
        Delay_us(1);
    }
    retry = 0;
    while (!HAL_GPIO_ReadPin(GPIO_Port, GPIO_PIN_8) && retry < 100) //等待变高电平
    {
        retry++;
        Delay_us(1);
    }
    Delay_us(40);//等待40us
    if (HAL_GPIO_ReadPin(GPIO_Port, GPIO_PIN_8))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;
}

//DHT11读取一次数据
//temp:温度(范围:0~50°)
//humi:湿度(范围:20%~90%)
//tem:温度小数位
//hum:湿度小数位
说明:Data[0]湿度, Data[2]温度。Data[1]和Data[3]分别为0和2的小数位。Data[4]用于校验。
/*
uint8_t DHT11_Read_Data(uint8_t *temp, uint8_t *humi, uint8_t *tem, uint8_t *hum)
{
    uint8_t buf[5];
    uint8_t 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];
            *hum = buf[1];
            *temp = buf[2];
            *tem = buf[3];
        }
    }
    else return 1;
    return 0;
}
*/

//DHT11输出模式配置
void DHT11_IO_OUT()
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    /* GPIO Ports Clock Enable */
//    __HAL_RCC_GPIOA_CLK_ENABLE();
    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(GPIO_Port, DHT11_Pin, GPIO_PIN_SET);

    /*Configure GPIO pin : PtPin */
    GPIO_InitStruct.Pin = DHT11_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIO_Port, &GPIO_InitStruct);
}

//DHT11输入模式配置
void DHT11_IO_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOA_CLK_ENABLE();;
    /*Configure  DHT11_Pin */
    GPIO_InitStruct.Pin = DHT11_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIO_Port, &GPIO_InitStruct);
}

uint8_t DHT11_Read(DHT11_Data_TypeDef *DHT11_Data)
{
    uint8_t buf[5];
    uint8_t 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])
        {
				DHT11_Data->humi_int  = buf[0];
        DHT11_Data->humi_deci = buf[1];
        DHT11_Data->temp_int  = buf[2];
        DHT11_Data->temp_deci = buf[3];
        }
    }
    else return 1;
    return 0;
}

主程序代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* 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 "DHT11.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* 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);
/* 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 */
    DHT11_Data_TypeDef dht = {0};
    DHT11_Data_TypeDef *dt=&dht;

    /* 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();
    printf("Hello world! \r\n");
    MX_TIM6_Init();
    DHT11_Init();
    /* USER CODE BEGIN 2 */
    uint32_t TimerUART = HAL_GetTick();

//    static DHT_sensor livingRoom = {GPIOA, GPIO_PIN_8, DHT11, GPIO_NOPULL};//指定DHT数据引脚
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        if ((HAL_GetTick() - TimerUART) > 2500)
        {

            if (DHT11_Read(dt) == 0)
            {

               printf("Temp=%d.%d°C,Humi=%d.%d%% \r\n", dht.temp_int, dht.temp_deci, dht.humi_int, dht.humi_deci);
                TimerUART = HAL_GetTick();
                HAL_GPIO_TogglePin(GPIOE, LED_Pin);
            }
            else printf("Read error \r\n");
        }
    }
    /* 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 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.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 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_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        Error_Handler();
    }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1)
    {
    }
    /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
       ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

工程源码

  • ✨申明:本文章仅发表在CSDN网站,任何其他网站,未注明来源,见此内容均为盗链和爬取,请多多尊重和支持原创!
  • 对于文中所提供的相关资源链接将作不定期更换。
链接: https://pan.baidu.com/s/1CLG5mUmLziSaz2cV8O5_dA
提取码: g4um

你可能感兴趣的:(stm32,STM32CubeMX,DHT11)