SÉRIE DE TRAVAUX PRATIQUES — STM32F4 BLACK PILL
7 TPs STM32F411CEU6
de Zéro à Système Intégré
// STM32CubeIDE · HAL Library · C · GPIO · Timer · ADC · I2C · UART
SIMULATION LED PC13
État : OFF · Cycle : 0
HAL_Delay(500ms) → Toggle → HAL_Delay(500ms)
01
Configuration CubeMX — PC13 en GPIO_Output
Pinout & Configuration
- Créer projet : File▶New STM32 Project → sélectionner
STM32F411CEU6 - Dans CubeMX → cliquer sur la broche PC13 → choisir GPIO_Output
- Clic droit sur PC13 → Enter User Label → taper
LED - Générer le code : Project▶Generate Code
PC13 ACTIF BAS
Sur la Black Pill, PC13 commande la LED intégrée en logique inverse : GPIO_PIN_RESET = LED allumée, GPIO_PIN_SET = éteinte.02
Code HAL — Clignotement 1 Hz
main.c → while(1)
C — TP1 : LED Blink 1 Hz
/* TP1 — LED Clignotante sur PC13 · STM32F411CEU6 Black Pill */ #include "main.h" #define BLINK_DELAY 500 /* ms — modifier pour changer la fréquence */ /* 500 ms ON + 500 ms OFF = période 1 s = fréquence 1 Hz */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { /* Méthode 1 : Toggle (recommandée) */ HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(BLINK_DELAY); /* Méthode 2 : Write explicite (équivalent) */ // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // ON // HAL_Delay(BLINK_DELAY); // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // OFF // HAL_Delay(BLINK_DELAY); } }
Variantes de clignotement
SOS (· · · — — — · · ·)
void blink(uint32_t ms){ HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13,GPIO_PIN_RESET); HAL_Delay(ms); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13,GPIO_PIN_SET); HAL_Delay(100); } /* S: 3 courts */ for(int i=0;i<3;i++) blink(200); /* O: 3 longs */ for(int i=0;i<3;i++) blink(600); /* S: 3 courts */ for(int i=0;i<3;i++) blink(200); HAL_Delay(2000);
Fréquence variable
uint32_t freq[] = { 100,200,500,1000}; for(int f=0;f<4;f++){ for(int i=0;i<5;i++){ HAL_GPIO_TogglePin( GPIOC,GPIO_PIN_13); HAL_Delay(freq[f]); } }
Schéma câblage
FIG. PC13 → 220Ω → LED → GND
RÉSULTAT ATTENDU
LED clignote à 1 Hz (500ms ON / 500ms OFF). Modifier BLINK_DELAY pour changer la fréquence.Phase : STOP
–
Appuyer sur Start
01
Configuration GPIO — 3 LEDs
PA0=ROUGE · PA1=ORANGE · PA2=VERT
| Broche | Couleur LED | Mode | Label |
|---|---|---|---|
| PA0 | ● ROUGE | GPIO_Output | LED_R |
| PA1 | ● ORANGE | GPIO_Output | LED_O |
| PA2 | ● VERT | GPIO_Output | LED_G |
FIG. PA0/PA1/PA2 → 220Ω → LED → GND
02
Code HAL — Séquenceur Feux Tricolores
C — TP2 : Feux Tricolores
/* TP2 — Feux Tricolores · PA0=Rouge PA1=Orange PA2=Vert */ #include "main.h" /* ── Durées de chaque phase (ms) — personnalisables ── */ #define T_ROUGE 4000 /* Rouge seul : 4 secondes */ #define T_ORANGE 1500 /* Orange seul : 1.5 secondes */ #define T_VERT 3000 /* Vert seul : 3 secondes */ /* ── Macros : éteindre toutes les LEDs ── */ #define ALL_OFF() do { \ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET); \ } while(0) int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { /* PHASE 1 : ROUGE — Stop */ ALL_OFF(); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); HAL_Delay(T_ROUGE); /* PHASE 2 : ORANGE — Attention */ ALL_OFF(); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); HAL_Delay(T_ORANGE); /* PHASE 3 : VERT — Passage autorisé */ ALL_OFF(); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); HAL_Delay(T_VERT); /* PHASE 4 : ORANGE — Avant rouge */ ALL_OFF(); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); HAL_Delay(T_ORANGE); } }
APPUYER
BTN
COMPTEUR APPUIS
0
LED OFF
01
Configuration : PB0 = Bouton, PC13 = LED
GPIO_Input + EXTI
- Broche PB0 → GPIO_Input · Label :
BTN - GPIO Pull-up/Pull-down : Pull-up (bouton câblé vers GND)
- Pour le mode EXTI : PB0 → GPIO_EXTI0 · Trigger : Falling Edge
- Activer l'IRQ : NVIC▶ cocher EXTI line0 interrupt · Priority : 0
ANTI-REBOND
Un bouton physique génère des rebonds (oscillations rapides de quelques ms). Solution logicielle : ignorer les appuis à moins de 50ms d'intervalle (debounce).02
Code Polling + EXTI avec Debounce
C — TP3 : Bouton Polling + EXTI
/* TP3 — Bouton PB0 (Pull-up) + LED PC13 */ #include "main.h" #define DEBOUNCE_MS 50 /* Délai anti-rebond */ /* ════ MÉTHODE 1 : POLLING ════ */ int main_polling(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); uint32_t last_press = 0; while (1) { /* Bouton actif bas (Pull-up) : RESET = appuyé */ if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET) { /* Anti-rebond */ if (HAL_GetTick() - last_press > DEBOUNCE_MS) { last_press = HAL_GetTick(); HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); } /* Attendre relâchement */ while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET); } } } /* ════ MÉTHODE 2 : INTERRUPTION EXTI ════ */ volatile uint8_t btn_flag = 0; volatile uint32_t last_irq = 0; volatile uint32_t press_count = 0; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { if (btn_flag) { btn_flag = 0; press_count++; HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); } } } /* Callback EXTI — appelé automatiquement par l'IRQ */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { /* Anti-rebond dans l'IRQ */ if (HAL_GetTick() - last_irq > DEBOUNCE_MS) { last_irq = HAL_GetTick(); btn_flag = 1; /* Signaler au main() */ } } }
SIMULATEUR HC-SR04
50 cm
Echo : 2941 µs · TOF : 1471 µs
2 cm120 cm400 cm
◀ Déplacer l'obstacle ▶
01
Principe HC-SR04 + Configuration Timer
PA8=TRIG · PA9=ECHO · TIM1 @ 1MHz
FIG. 4 — Protocole HC-SR04 : impulsion TRIG 10µs → mesure ECHO → calcul distance
d (cm) = t_echo(µs) × 0.017
Vitesse son = 340 m/s · Aller-retour ÷ 2 · Conversion µs→cm
Configuration Timer TIM1 pour délais µs
- CubeMX → Timers▶TIM1 → Clock Source : Internal Clock
- Prescaler =
99· Counter Period =65535
→ Tick TIM1 = 100MHz / (99+1) = 1 MHz → 1 tick = 1 µs - PA8 → GPIO_Output (TRIG) · PA9 → GPIO_Input (ECHO)
02
Code HAL — Lecture distance HC-SR04
C — TP4 : HC-SR04 Ultrason
/* TP4 — HC-SR04 : TRIG=PA8 ECHO=PA9 · TIM1 @ 1 MHz */ #include "main.h" #include <stdio.h> TIM_HandleTypeDef htim1; UART_HandleTypeDef huart1; /* ── Délai précis en µs via TIM1 ── */ void delay_us(uint16_t us) { __HAL_TIM_SET_COUNTER(&htim1, 0); while (__HAL_TIM_GET_COUNTER(&htim1) < us); } /* ── Lecture distance HC-SR04 (en cm) ── */ float hcsr04_read_cm(void) { uint32_t t_start, t_echo, timeout; /* 1. Impulsion TRIG : 10 µs */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); delay_us(10); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); /* 2. Attendre montée ECHO (timeout 30 ms) */ timeout = HAL_GetTick() + 30; while (!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)) if (HAL_GetTick() > timeout) return -1; /* 3. Mesurer durée ECHO */ __HAL_TIM_SET_COUNTER(&htim1, 0); while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)); t_echo = __HAL_TIM_GET_COUNTER(&htim1); /* en µs */ /* 4. Calculer distance (vitesse son = 340 m/s) */ return (float)t_echo * 0.017f; /* cm */ } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); MX_USART1_UART_Init(); HAL_TIM_Base_Start(&htim1); /* Démarrer TIM1 */ while (1) { float dist = hcsr04_read_cm(); if (dist > 0) printf("Distance : %.1f cm\r\n", dist); else printf("Hors portée !\r\n"); HAL_Delay(100); } }
PORTÉE HC-SR04
Distance mesurable : 2 cm à 400 cm. Au-delà, le signal ECHO ne revient pas dans le timeout → retourne -1.SIMULATEUR SERVO SG90
90°
PWM Pulse : 1500 µs · CCR : 3000
0° ◀────────── 90° ──────────▶ 180°
01
Théorie PWM Servo + Configuration TIM2
50 Hz · Pulse 1ms–2ms → 0°–180°
CCR = Pulse_µs × (ARR+1) / Période_µs
ARR=19999 · Période=20ms=20000µs · Ex: 90° → 1500µs → CCR=1500×20000/20000=1500
Angle 0°
Pulse : 1000 µs
CCR : 1000
Angle 90°
Pulse : 1500 µs
CCR : 1500
Angle 180°
Pulse : 2000 µs
CCR : 2000
- CubeMX → Timers▶TIM2 → Channel1 : PWM Generation CH1 (PA0)
- Prescaler =
99→ TIM2 clock = 1 MHz - Counter Period (ARR) =
19999→ Période = 20 ms = 50 Hz ✓ - Pulse =
1500(90° par défaut)
02
Code HAL — Servo SG90
C — TP5 : Servo Motor SG90
/* TP5 — Servo SG90 sur PA0 (TIM2_CH1) · 50Hz PWM */ #include "main.h" TIM_HandleTypeDef htim2; /* ── Convertir angle (0–180°) en valeur CCR ── */ uint32_t angle_to_ccr(uint8_t angle) { /* Interpolation linéaire : 0° → 500µs, 180° → 2500µs */ if (angle > 180) angle = 180; return (uint32_t)(500 + ((float)angle / 180.0f) * 2000); } void servo_set_angle(uint8_t angle) { __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, angle_to_ccr(angle)); } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); /* Démarrer le PWM */ HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); while (1) { /* Balayage 0° → 180° → 0° (sweep) */ for (uint8_t a = 0; a <= 180; a += 5) { servo_set_angle(a); HAL_Delay(30); } for (int16_t a = 180; a >= 0; a -= 5) { servo_set_angle((uint8_t)a); HAL_Delay(30); } /* Positions fixes */ servo_set_angle(0); HAL_Delay(1000); servo_set_angle(90); HAL_Delay(1000); servo_set_angle(180); HAL_Delay(1000); } }
CÂBLAGE SG90
Fil Rouge → 5V · Fil Marron/Noir → GND · Fil Orange/Jaune → PA0 (signal PWM). Alimenter le servo sur 5V externe si possible (fort courant au démarrage).SIMULATEUR DHT11
25
°C · Température
60
% · Humidité
OFF
LED Alarme (T > 30°C)
Température : 25°C
Humidité : 60%
Trame 40 bits : 0001100100111100 00011001 00111100 00110101
01
Protocole DHT11 — Séquence 1-Wire
Trame 40 bits : 16b Humidité + 16b Température + 8b Checksum
FIG. 6 — Protocole DHT11 : start 18ms, réponse 80µs, 40 bits (bit0=26µs, bit1=70µs)
RÉSISTANCE PULL-UP OBLIGATOIRE
La ligne DATA nécessite une résistance pull-up de 4.7 kΩ entre DATA et VCC (3.3V ou 5V). Sans elle, la communication échoue systématiquement.02
Code HAL — Driver DHT11 complet
PA5=DATA · TIM1 @ 1MHz · GPIO bidirectionnel
C — TP6 : DHT11 Driver complet
/* TP6 — DHT11 sur PA5 · TIM1 @ 1MHz · PC13 = LED alarme */ #include "main.h" #include <stdio.h> TIM_HandleTypeDef htim1; UART_HandleTypeDef huart1; #define DHT_PORT GPIOA #define DHT_PIN GPIO_PIN_5 #define TEMP_ALARM 30 /* °C — modifier le seuil ici */ void delay_us(uint16_t us){ __HAL_TIM_SET_COUNTER(&htim1,0); while(__HAL_TIM_GET_COUNTER(&htim1)<us); } /* ── Basculer PA5 en Output ── */ void dht_set_output(void){ GPIO_InitTypeDef g={0}; g.Pin=DHT_PIN; g.Mode=GPIO_MODE_OUTPUT_PP; g.Pull=GPIO_NOPULL; g.Speed=GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(DHT_PORT,&g); } /* ── Basculer PA5 en Input ── */ void dht_set_input(void){ GPIO_InitTypeDef g={0}; g.Pin=DHT_PIN; g.Mode=GPIO_MODE_INPUT; g.Pull=GPIO_PULLUP; HAL_GPIO_Init(DHT_PORT,&g); } /* ── Structure résultat DHT11 ── */ typedef struct { uint8_t temperature; uint8_t humidity; uint8_t valid; } DHT11_Data; DHT11_Data dht11_read(void) { DHT11_Data result = {0,0,0}; uint8_t data[5]={0}; /* STEP 1 : Signal de démarrage (18 ms LOW) */ dht_set_output(); HAL_GPIO_WritePin(DHT_PORT,DHT_PIN,GPIO_PIN_RESET); HAL_Delay(18); HAL_GPIO_WritePin(DHT_PORT,DHT_PIN,GPIO_PIN_SET); delay_us(30); dht_set_input(); /* STEP 2 : Attendre réponse DHT11 (80µs LOW + 80µs HIGH) */ uint32_t t=HAL_GetTick()+2; while(!HAL_GPIO_ReadPin(DHT_PORT,DHT_PIN) && HAL_GetTick()<t); while(HAL_GPIO_ReadPin(DHT_PORT,DHT_PIN) && HAL_GetTick()<t+2); /* STEP 3 : Lire 40 bits */ for (int i=0;i<40;i++) { while(!HAL_GPIO_ReadPin(DHT_PORT,DHT_PIN)); /* attendre HIGH */ delay_us(40); /* échantillonner après 40µs */ if(HAL_GPIO_ReadPin(DHT_PORT,DHT_PIN)) data[i/8] |= (1 << (7-(i%8))); /* BIT = 1 */ while(HAL_GPIO_ReadPin(DHT_PORT,DHT_PIN)); /* attendre LOW */ } /* STEP 4 : Vérifier le checksum */ if(data[4]==((data[0]+data[1]+data[2]+data[3])&0xFF)){ result.humidity = data[0]; result.temperature = data[2]; result.valid = 1; } return result; } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); MX_USART1_UART_Init(); HAL_TIM_Base_Start(&htim1); while(1){ DHT11_Data d = dht11_read(); if(d.valid){ printf("T=%d°C H=%d%% %s\r\n", d.temperature, d.humidity, d.temperature>TEMP_ALARM?"[ALARME!]":"[OK]"); /* LED alarme */ HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13, d.temperature>TEMP_ALARM?GPIO_PIN_RESET:GPIO_PIN_SET); } HAL_Delay(2000); /* DHT11 : lecture max 1×/2s */ } }
SYS
Architecture du Système
Machine à états — 3 modes de fonctionnement
FIG. 7 — Architecture système intégré : 3 capteurs → STM32F411 → 3 actionneurs + UART
🟢 MODE NORMAL
- Lecture DHT11 toutes les 2s
- Distance HC-SR04 toutes 100ms
- LED verte allumée
- Servo en position 90°
🔴 MODE ALERTE
- Temp > 30°C → LED rouge
- Distance < 20cm → Alarme
- Servo se déplace à 0°
- UART : message d'alerte
🔵 MODE SCAN
- Bouton → change de mode
- Servo balaye 0→180°
- Mesure distance à chaque angle
- Cartographie ultrasonique
01
Tableau des Broches — Système Complet
| Broche | Signal | Composant | Mode | Périphérique HAL |
|---|---|---|---|---|
| PA0 | PWM | Servo SG90 | TIM2_CH1 | htim2 |
| PA5 | DATA | DHT11 | GPIO_IO (bidirectionnel) | GPIO |
| PA6 | TRIG | HC-SR04 | GPIO_Output | GPIO |
| PA7 | ECHO | HC-SR04 | GPIO_Input | GPIO |
| PB0 | BTN | Bouton mode | GPIO_EXTI0 | NVIC |
| PB4 | LED VERTE | LED état normal | GPIO_Output | GPIO |
| PC13 | LED ROUGE | LED alarme | GPIO_Output | GPIO |
| PA9 | UART1_TX | Debug PC | USART1 | huart1 |
| TIM1 | Base 1MHz | DHT11+HC-SR04 | Timer Base | htim1 |
02
Code Principal — Machine à États
C — TP7 : Système Intégré Complet
/* TP7 — Système intégré : DHT11 + HC-SR04 + Servo + LEDs */ #include "main.h" #include <stdio.h> /* ─── Seuils personnalisables ─── */ #define TEMP_ALARM_C 30 /* °C */ #define DIST_ALARM_CM 20 /* cm */ /* ─── Machine à états ─── */ typedef enum { MODE_NORMAL = 0, MODE_ALERT = 1, MODE_SCAN = 2 } SystemMode; volatile SystemMode sys_mode = MODE_NORMAL; volatile uint8_t btn_pressed = 0; volatile uint32_t last_btn = 0; /* ─── Fonctions capteurs (voir TP4 et TP6) ─── */ extern float hcsr04_read_cm(void); extern DHT11_Data dht11_read(void); extern void servo_set_angle(uint8_t angle); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_USART1_UART_Init(); HAL_TIM_Base_Start(&htim1); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); uint32_t t_dht = 0, t_dist = 0; DHT11_Data dht = {0}; float dist = 0; while (1) { /* ── Changer de mode via bouton ── */ if (btn_pressed) { btn_pressed = 0; sys_mode = (SystemMode)((sys_mode + 1) % 3); printf("Mode: %d\r\n", sys_mode); } /* ── Lecture DHT11 toutes les 2 secondes ── */ if (HAL_GetTick() - t_dht > 2000) { t_dht = HAL_GetTick(); dht = dht11_read(); } /* ── Lecture distance toutes les 200ms ── */ if (HAL_GetTick() - t_dist > 200) { t_dist = HAL_GetTick(); dist = hcsr04_read_cm(); } /* ── Logique selon le mode ── */ switch (sys_mode) { case MODE_NORMAL: servo_set_angle(90); HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET); /* LED verte */ HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET); /* LED rouge OFF */ break; case MODE_ALERT: /* Alarme température */ if (dht.valid && dht.temperature > TEMP_ALARM_C) { HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET); servo_set_angle(0); /* Servo en butée */ printf("ALERTE TEMP! T=%d°C\r\n", dht.temperature); } /* Alarme distance */ if (dist > 0 && dist < DIST_ALARM_CM) { HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13); /* Clignotement */ printf("ALERTE DIST! D=%.1fcm\r\n", dist); } break; case MODE_SCAN: /* Balayage servo + cartographie distance */ for (uint8_t a=0;a<=180;a+=10) { servo_set_angle(a); HAL_Delay(200); float d = hcsr04_read_cm(); printf("SCAN angle=%d dist=%.1f\r\n", a, d); } break; default: break; } /* ── Affichage UART régulier ── */ if (dht.valid) printf("T=%d°C H=%d%% D=%.1fcm Mode=%d\r\n", dht.temperature, dht.humidity, dist, sys_mode); HAL_Delay(50); } } /* ── IRQ Bouton ── */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin==GPIO_PIN_0 && HAL_GetTick()-last_btn>50) { last_btn=HAL_GetTick(); btn_pressed=1; } }
SYSTÈME OPÉRATIONNEL
Appuyer sur BTN (PB0) pour cycler : NORMAL → ALERTE → SCAN → NORMALTerminal UART à 115200 baud pour voir toutes les mesures en temps réel.
EXTENSIONS POSSIBLES
→ Ajouter un écran OLED 0.96" (I2C) pour afficher T°, humidité, distance→ Sauvegarder les mesures en Flash interne (HAL_FLASH)
→ Envoyer les données via Bluetooth (HC-05) ou WiFi (ESP8266 AT commands)