Sensore di temperatura DHT11 con scheda STM32F401RE Nucleo
Il progetto si pone come obiettivo quello di implementare un sensore di temperatura DHT11 con la scheda di sviluppo STM32F401RE Nucleo.
Per il progetto si ringrazia Alexandro Brinoveanu, per gli amici Alex, che mi ha stimolato alla scrittura di un firmware per questa applicazione.
Il sensore DHT11
Si tratta di un sensore digitale di temperatura e umidità, che rende i dati in uscita in modo seriale attraverso un singolo BUS. Vi sono tre terminali: VCC e GND, dedicati all'alimentazione del sensore, e Data, dedicato allo scambio di informazioni tra il sensore e il microcontrollore.
Se non si dispone del modulo, è necessario connettere il sensore come mostrato in figura 1: il pin 1, VDD, alla tensione di alimentazione (3.3 V o 5 V), il pin 2, GND, a massa e il pin 4, Data, a un ingresso della scheda. E' necessario inserire una resistenza di Pull-Up tra VDD e Data da 5 kOhm. Il pin 3 non è connesso (NC).
Datasheet del sensore a questo link
![]() |
Figura 1: schema di collegamento. |
Dopo aver creato un progetto nell'ide di sviluppo CUBE, apparirà la schermata iniziale del file .ioc, nella quale vi è la creazione grafica del progetto. Andare nella schermata Clock Configuration. Settare PLL Source Mux su HSE e System Clock Mux su PLLCLK (osservare figura 2). Cliccare sul pulsante in alto Resolve clock issues ogni volta.
![]() |
Figura 2: configurazione clock. |
Si rientra nella schermata Pinout & Configuration. Nella sezione System Core si eseguono le seguenti operazioni:
- DMA -> Add -> Select -> USART2_TX per la configurazione di un accesso diretto alla memoria per la trasmissione UART.
Figura 3: configurazione DMA - RCC -> High Speed Clock -> Crystal/Ceramic Resonator che definisce la sorgente del clock. Attivare un interrupt in Configuration -> NVIC Settings -> RCC Global Interrupt -> Enable
Figura 4: configurazione clock
Spostandosi nella categoria Timers, attivare il timer TIM1 con Clock Source -.> Internal Clock. Il timer avrà un prescaler sulla frequenza di sistema di 84. Verrà utile per la configurazione di un timer con un periodo di 1 microsecondo, il quale serve per l'implementazione di alcune funzioni per il DHT11.
Sull'interfaccia principale pinout view, infine, si configura un pin come GPIO_OUTPUT.
E' possibile quindi generare il codice con l'apposito pulsante sulla barra superiore, mostrato in figura 7:
![]() |
Figura 5: implementazione del timer TIM1. |
Sull'interfaccia principale pinout view, infine, si configura un pin come GPIO_OUTPUT.
![]() |
Figura 6: pin di output. |
E' possibile quindi generare il codice con l'apposito pulsante sulla barra superiore, mostrato in figura 7:
![]() |
Figura 7: pulsante per la generazione del codice |
Prima di tutto, si osserva che il sensore DHT11, prima di comunicare i dati letti in modo seriale, deve essere azionato dal microcontrollore tramite un segnale di Start Communication: dopo aver impostato l'uscita del pin sul valore alto, occorre immediatamente impostarlo sul valore basso, attendere un tempo di 18 millisecondi e impostare di nuovo a valore alto. Si attendono tra i 20 e i 40 microsecondi, e si imposta il pin come GPIO_INPUT (via codice).
Il sensore risponde tenendo basso il pin per circa 80 microsecondi e lo pone ad alto successivamente per altri 80 microsecondi. Se l'operazione ha successo, allora il sensore comunica i dati.
Prima di iniziare, è necessario scrivere una libreria per l'azionamento e la lettura dei dati del sensore DHT11.
Creare un file header dht11.h con il seguente contenuto:
#ifndef DHT11_H_
#define DHT11_H_
#include "stm32f4xx_hal.h"
extern TIM_HandleTypeDef htim1;
#define DHT11_PORT GPIOB
#define DHT11_PIN GPIO_PIN_1
typedef struct{
float temperature;
float humidity;
}temphum;
void DHT11_Data(temphum* DHT_Data);
#endif /* DHT11_H_ */
Nel quale file occorre modificare il pin e la porta. Per esempio, se collego il sensore al pin PA2, sarà da dichiarare:
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_PIN_2
Nello stesso file è dichiarata la funzione di lettura dei dati DHT11_Data(...) e una struttura dati che possa contenere i valori di temperatura e umidità.
Creare poi un file dht11.c e dichiarare le seguenti funzioni:
#include "dht11.h"
void delay(uint16_t time){
__HAL_TIM_SET_COUNTER(&htim1, 0);
while((__HAL_TIM_GET_COUNTER(&htim1)) < time);
}
necessaria all'implementazione di un timer da 1 microsecondo;
void setPinOutput(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin){
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOx, &GPIO_InitStruct);
}
void setPinInput(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin){
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOx, &GPIO_InitStruct);
}
Le prime due funzioni preliminari serviranno a impostare il pin come input o output.
Si può ora definire una funzione che attivi il sensore.
void DHT11_Start(void){
setPinOutput(DHT11_PORT, DHT11_PIN);
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET);
HAL_Delay(18);
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET);
delay(40);
setPinInput(DHT11_PORT, DHT11_PIN);
}
Si crea ora la funzione che riconosca la risposta del sensore:
int8_t DHT11_Response(void){
uint8_t R = 0;
delay (40);
if (!(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN))){
delay (50);
if ((HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN))) R = 1;
else R = -1;
}
while ((HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)));
return R;
}
La lettura dei dati del sensore deve rispettare lo standard del sensore: il sensore tiene a livello basso il pin Data per 50 microsecondi, così da avvertire il microcontrollore che sta per inviare un bit. Dopodiché, il pin va a livello alto. Se dura tra i 26 e 28 microsecondi, significa che il bit vale 0; nel caso in cui dura circa 70 microsecondi, allora il bit vale 1.
La funzione di lettura è la seguente:
uint8_t DHT11_Read (void){
uint8_t i,j;
for (j=0;j<8;j++){
while (!(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)));
delay (30);
if (!(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN))){
i&= ~(1<<(7-j));
}else{
i|= (1<<(7-j));
}
while ((HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)));
}
return i;
}
è una funzione in grado di leggere fino a un byte (8 bit).
Arrivati a questo punto, è possibile fare una lettura totale che restituisca sia temperatura che umidità, ricordandosi che:
- il byte 1 contiene la parte intera dell'umidità
- il byte 2 contiene la parte decimale dell'umidità
- il byte 3 contiene la parte intera della temperatura
- il byte 4 contiene la parte decimale della temperatura
- il byte 5 è la somma dei quattro precedenti
Dichiarare in alto al file dht11.c le seguenti variabili globali:
uint8_t Rh_byte1, Rh_byte2, Temp_byte1, Temp_byte2;
uint16_t SUM;
Infine, si scrive la funzione di lettura di tutti i dati:
void DHT11_Data (temphum *DHT_Data){
DHT11_Start ();
if(DHT11_Response() > 0){
Rh_byte1 = DHT11_Read ();
Rh_byte2 = DHT11_Read ();
Temp_byte1 = DHT11_Read ();
Temp_byte2 = DHT11_Read ();
SUM = DHT11_Read();
}
if (SUM == (Rh_byte1+Rh_byte2+Temp_byte1+Temp_byte2)){
DHT_Data->temperature = Temp_byte1+Temp_byte2;
DHT_Data->humidity = Rh_byte1+Rh_byte2;
}
}
Il file è completo. Ora è possibile scrivere il firmware vero e proprio del microcontrollore. Dopo aver creato i due file dht11.c e dht11.h , entrare nel file main.c nella directory Core -> Src
Includere tre file sorgente importanti:
#include <string.h>
#include <stdio.h>
#include "dht11.h"
![]() |
Figura 8: inclusioni nel main. |
Nella funzione int main(void), come mostrato in figura 9, dichiarare un buffer stringa char info[24] e una struttura temphum mydata nella sezione USER CODE BEGIN 1
![]() |
Figura 9: dichiarazioni iniziali. |
Nella medesima funzione, attivare il timer con la funzione HAL_TIM_Base_Start(&htim1) nella zona USER CODE BEGIN 2.
![]() |
Figura 10: dichiarazione della funzione di azionamento del timer |
Ora, nel ciclo del programma while(1)si esegue la lettura dei dati, la scrittura su una stringa di testo e la trasmissione dal microcontrollore al terminale tramite UART2. Osserva la figura 11
![]() |
Figura 11: Ciclo principale del programma. |
La temperatura viene mostrata su terminale.
Commenti
Posta un commento