Table of Contents
1. Introduction to DHT11
2. Code Implementation
3. Summary & Precautions
01
—
Introduction to DHT11
The DHT11 is a composite sensor for temperature and humidity that features a calibrated digital signal output. It utilizes dedicated digital module acquisition technology and temperature and humidity sensing technology, ensuring high reliability and excellent long-term stability. It employs a single-wire communication protocol, requiring only one GPIO pin for data exchange with the microcontroller, making it cost-effective and resistant to interference. Its parameters are as follows:
Operating Voltage: 3-5.5V, Operating Current: 1mA, Measurement Resolution: 8 bit
Humidity Range: 20 – 90 %RH, Humidity Accuracy: ±5 %RH, Temperature Range: 0 – 50 ℃
Temperature Accuracy: ±2 ℃, Communication Protocol: Single Wire The DHT11 transmits 40 bits of data in one go, formatted as 8 bits of humidity integer data + 8 bits of humidity decimal data (usually 0); 8 bits of temperature integer data + 8 bits of temperature decimal data; 8 bits of checksum (the last 8 bits of the sum of the previous four 8-bit data);
The high bits of the data are sent first, and the checksum is used to verify the correctness of the data.
Hardware connection of DHT11 with STM32:
| DHT11 Pin | STM32 Pin | Description |
|---|---|---|
| VCC | 3.3V | Power positive |
| GND | GND | Power negative |
| DATA | PA5 | Single wire data line, preferably with a pull-up resistor |
02
—
Code Implementation
The system clock is set to 72MHz, using HSE clock, configuring APB1 and APB2, enabling the serial port and PA5 pin. Call the DHT11_Read_Data function to blockingly obtain temperature and humidity data. dht11.h
#ifndef __DHT11_H
#define __DHT11_H
#include "main.h"
// Pin definitions
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_PIN_5
#define DHT11_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
// Pin operation macros
#define DHT11_DQ_OUT(x) do{ \
if(x) HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); \
else HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); \
}while(0)
#define DHT11_DQ_IN() HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)
// Function declarations
void delay_init(uint8_t SYSCLK);
void delay_us(uint32_t nus);
void DHT11_GPIO_Input(void);
void DHT11_GPIO_Output(void);
uint8_t DHT11_Init(void);
void DHT11_Start(void);
uint8_t DHT11_Read_Bit(void);
uint8_t DHT11_Read_Byte(void);
uint8_t DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity);
#endif
dht11.c
// dht11.c
#include "dht11.h"
static uint32_t fac_us = 0;
void delay_init(uint8_t SYSCLK)
{
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
fac_us = SYSCLK;
}
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
ticks = nus * fac_us;
told = SysTick->VAL;
while(1)
{
tnow = SysTick->VAL;
if(tnow != told)
{
if(tnow < told)
tcnt += told - tnow;
else
tcnt += reload - tnow + told;
told = tnow;
if(tcnt >= ticks)
break;
}
}
}
// GPIO input mode setting
void DHT11_GPIO_Input(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
DHT11_CLK_ENABLE();
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
}
// GPIO output mode setting
void DHT11_GPIO_Output(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
DHT11_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_HIGH;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
}
// DHT11 initialization
uint8_t DHT11_Init(void)
{
DHT11_GPIO_Output();
DHT11_DQ_OUT(1);
HAL_Delay(1000); // Wait for DHT11 to stabilize
return 0;
}
// Start signal
void DHT11_Start(void)
{
// Set to output mode
DHT11_GPIO_Output();
// Pull down for at least 18ms
DHT11_DQ_OUT(0);
delay_us(18000);
// Release the bus, pull high for 20-40μs
DHT11_DQ_OUT(1);
delay_us(30);
// Set to input mode, wait for DHT11 response
DHT11_GPIO_Input();
}
// Read a bit
uint8_t DHT11_Read_Bit(void)
{
uint32_t timeout = 0;
// Wait for low level start (about 50μs)
while(DHT11_DQ_IN() == 1)
{
timeout++;
if(timeout > 100) return 0;
delay_us(1);
}
// Wait for low level end (about 54μs)
timeout = 0;
while(DHT11_DQ_IN() == 0)
{
timeout++;
if(timeout > 100) return 0;
delay_us(1);
}
// Delay 40μs to determine high level duration
delay_us(40);
// Read current level
if(DHT11_DQ_IN() == 1)
{
// Long high level duration (about 70μs), indicates data 1
timeout = 0;
while(DHT11_DQ_IN() == 1)
{
timeout++;
if(timeout > 100) return 1;
delay_us(1);
}
return 1;
}
else
{
// Short high level duration (about 26μs), indicates data 0
return 0;
}
}
// Read a byte
uint8_t DHT11_Read_Byte(void)
{
uint8_t i, data = 0;
for(i = 0; i < 8; i++)
{
data <<= 1;
data |= DHT11_Read_Bit();
}
return data;
}
// Read temperature and humidity data
uint8_t DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity)
{
uint8_t data[5] = {0};
uint8_t i;
// Send start signal
DHT11_Start();
// Wait for DHT11 response
uint32_t timeout = 0;
while(DHT11_DQ_IN() == 1)
{
timeout++;
if(timeout > 100) return 0;
delay_us(1);
}
timeout = 0;
while(DHT11_DQ_IN() == 0)
{
timeout++;
if(timeout > 100) return 0;
delay_us(1);
}
timeout = 0;
while(DHT11_DQ_IN() == 1)
{
timeout++;
if(timeout > 100) return 0;
delay_us(1);
}
// Read 40 bits of data
for(i = 0; i < 5; i++)
{
data[i] = DHT11_Read_Byte();
}
// Set to output mode, pull high
DHT11_GPIO_Output();
DHT11_DQ_OUT(1);
// Verify data
if(data[4] == (data[0] + data[1] + data[2] + data[3]))
{
*humidity = data[0]; // Humidity integer
*temperature = data[2]; // Temperature integer
return 1; // Read successful
}
return 0; // Checksum failed
}
main.c
// main.c
#include "main.h"
#include "dht11.h"
#include "stdio.h"
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
// Initialize delay function
delay_init(72);
// Initialize DHT11
DHT11_Init();
uint8_t temperature = 0;
uint8_t humidity = 0;
char buffer[50];
while(1)
{
if(DHT11_Read_Data(&temperature, &humidity) == 1)
{
// Output temperature and humidity data via serial
sprintf(buffer, "Humidity: %d%%, Temperature: %d℃\r\n", humidity, temperature);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), 1000);
}
else
{
HAL_UART_Transmit(&huart1, (uint8_t*)"Read failed\r\n", 10, 1000);
}
HAL_Delay(2500); // At least 2 seconds interval between readings
}
}
// System clock configuration (adjust according to actual situation)
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// Configure HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// Configure system clock
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;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
// USART1 initialization (for debugging output)
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
// GPIO initialization
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
}
03
—
Summary & Precautions
The single-wire protocol is sensitive to delays, requiring precise calibration of microsecond-level delay functions; there should be at least a 2-second interval between two readings. The pull-up effect of STM32’s GPIO is relatively weak, so it is advisable to add a pull-up resistor between the GPIO and the DHT11 data pin. If data verification consistently fails, it is recommended to reduce the interval for reading GPIO data.