STM32F4 · ADC — Du Zéro à l'Expert
Cours Complet · Systèmes Embarqués · STM32F4xx

// ADC · Convertisseur Analogique-Numérique Du Zéro à l'Expert

Architecture SAR, registres bas niveau, drivers HAL, DMA circulaire et 5 problèmes réels avec code C complet. Chaque bit expliqué. Zéro hypothèse.

12Modules
5Problèmes
12-bitRésolution
2.4MSPS Max
4096Niveaux
Module 00

Fondements de la Conversion Numérique

Quatre piliers théoriques universels — ils s'appliquent à tout ADC. Maîtrisez ces bases avant de toucher un registre.

Pilier 01
Échantillonnage
Prélever des instantanés du signal continu à intervalle régulier Ts.
Pilier 02
Sample & Hold
Geler la tension d'entrée pendant toute la durée de la conversion.
Pilier 03
Shannon-Nyquist
fs ≥ 2·fmax pour reconstruire le signal sans aliasing.
Pilier 04
SAR
Approximations successives : N bits = N cycles de recherche binaire.
Vin = (Code / (2N − 1)) × VREF
ÉCHANTILLONNAGE Prélever x(nTs) BLOCAGE S&H Vout = const SHANNON-NYQUIST fs ≥ 2 · fmax APPROX. SUCCESSIVES N bits → N cycles SAR
FIG. 0.1 — Pipeline complet d'une conversion ADC
Module 01

Échantillonnage

Un signal analogique est continu dans le temps. Un MCU ne peut pas traiter l'infini — il prélève des mesures à des instants discrets nTs.

Définitions fondamentales

SymboleNomUnité
TsPériode d'échantillonnagesecondes
fsFréquence d'échantillonnageHz = 1/Ts
x(t)Signal analogique continuVolts
x[n]x(n·Ts) — séquence discrèten = 0,1,2…

Physique de l'opération

À chaque instant nTs, un MOSFET interne se ferme quelques nanosecondes. La tension est capturée sur le condensateur interne (~4 pF). Le MOSFET se rouvre et la conversion commence.

ℹ️
STM32F4 — registre SMPR

Durée du S/H : 3 / 15 / 28 / 56 / 84 / 112 / 144 / 480 cycles d'horloge CAN.

0Ts2Ts3Ts4Ts5Ts6Ts Signal continu x(t) ← Échantillon
FIG. 1.1 — Points ambre = échantillons prélevés à fréquence fs = 1/Ts
Module 02

② Blocage — Sample & Hold

La conversion SAR dure plusieurs cycles. Pendant ce temps, l'entrée ne doit pas changer. Le S&H gèle la tension grâce à un condensateur isolé.

Phase 01
TRACK
S1 fermé. Condensateur se charge à Vin. Vout = Vin en temps réel.
Phase 02
HOLD
S1 ouvert. Charge isolée. Vout constant même si Vin change. SAR commence.
Phase 03
CONVERT
SAR compare Vout (bloqué) bit par bit. 12 cycles pour 12 bits.
Phase 04
ACQUIRE
S1 se referme. Condensateur se recharge. Durée minimale = t_acq.
⚠️
Temps d'acquisition et impédance source

Pour une thermistance 10 kΩ utiliser ADC_SAMPLETIME_480CYCLES. Pour un potentiomètre ≤ 1 kΩ, 3 cycles suffisent.

t_acq ≥ 9 · R_source · C_ADC  =  9 × 10 000 × 4×10⁻¹²  ≈  0,36 µs
Module 03

③ Théorème de Shannon-Nyquist

Trop lentement → repliement spectral (aliasing). Shannon démontre la condition minimale absolue pour une reconstruction parfaite.

Théorème de Shannon-Nyquist — 1949
fs  ≥  2 · fmax

Pour reconstruire parfaitement un signal de fréquence max f_max, la fréquence d'échantillonnage f_s doit être au moins le double.

f_Nyquist = fs / 2
Limite max représentable
STM32F4 : fs max = 2,4 MSPS
→ Signaux jusqu'à 1,2 MHz
Applicationf_max signalfs minimumfs pratique
🌡️ Température NTC~0,1 Hz0,2 Hz1 Hz
🔋 Tension batterie~10 Hz20 Hz100 Hz
🕹️ Joystick~50 Hz100 Hz1 kHz
🎙️ Son / voix~4 kHz8 kHz8–16 kHz
🎵 Audio hi-fi~20 kHz40 kHz44,1 / 48 kHz
⚡ Courant DC/DC~100 kHz200 kHz1 MHz+
Module 04

④ Approximations Successives (SAR)

Comment transformer une tension en binaire ? Le SAR applique la recherche binaire. N bits = N cycles. MSB d'abord, LSB en dernier.

CAN 4 BITS — Vin = 2,2V / VREF = 3,3V → Résultat attendu : 1010 CYCLE BIT REGISTRE SAR V_DAC COMPARAISON DÉCISION 1 B3 1 ? ? ? 1,65 V Vin 2,2V > 1,65V ✅ B3 = 1 — GARDER 2 B2 1 1 ? ? 2,42 V Vin 2,2V < 2,42V ❌ B2 = 0 — EFFACER 3 B1 1 0 1 ? 2,09 V Vin 2,2V > 2,09V ✅ B1 = 1 — GARDER 4 B0 1 0 1 1 2,20 V Vin 2,2V ≈ 2,20V ❌ B0 = 0 — EFFACER 🏁 RÉSULTAT : 1010 = 10 → V = 10/15 × 3,3 = 2,20 V ✅
FIG. 4.1 — Trace complète SAR 4 bits · STM32F4 : 12 cycles → résolution 0,806 mV/LSB @ 3,3V

📊 Résolution & LSB

1 LSB = V_REF / (2N − 1)
RésolutionNiveauxLSB @ 3,3V
6 bits6451,6 mV
8 bits25612,9 mV
10 bits1 0243,22 mV
12 bits4 0960,806 mV

⏱️ Temps de Conversion

t_total = (t_S/H + 12,5) / f_ADC
S/H@ 21 MHzDébit max
3 cycles738 ns1,35 MSPS
84 cycles4,60 µs217 kSPS
480 cycles23,4 µs42,7 kSPS
Module 05

📝 Exercices de Vérification

Quatre questions clés avec réponses directes — testez votre compréhension des bases.

Q1 — Théorème de Shannon

Capteur de vibrations, fréquence maximale 8 kHz. Fréquence d'échantillonnage minimale ?

✅ 16 000 Hz — fs ≥ 2 × 8 000 = 16 000 Hz

Q2 — Résolution & LSB

CAN 12 bits, V_REF = 3,3 V, valeur brute = 1638. Tension d'entrée ?

✅ 1,320 V — V = (1638 / 4095) × 3,3 = 1,320 V

Q3 — Cycles SAR

CAN SAR 12 bits — combien de cycles pour la conversion seule (sans S/H) ?

12,5 cycles — 12 décisions + 0,5 cycle de synchronisation

Q4 — Durée totale

APB2 = 84 MHz, prescaler /4 → f_ADC = 21 MHz, S/H = 84 cycles. Durée totale ?

4,595 µs — t = (84 + 12,5) / 21 000 000
Module 06

Architecture du CAN STM32F4

Jusqu'à trois CAN 12 bits (ADC1/2/3), jusqu'à 19 canaux, capteurs internes. Horloge dérivée de l'APB2.

Architecture SAR

Recherche binaire : compare l'entrée bit par bit à une tension CNA interne. 12 cycles = résultat 12 bits. Jusqu'à 2,4 MSPS.

🕐

Horloge CAN

Dérivée de l'APB2 (max 84 MHz) divisée par 2, 4, 6 ou 8. Horloge CAN max = 36 MHz. Typiquement 84/4 = 21 MHz.

📦

Trois instances

ADC1, ADC2, ADC3 — indépendants ou couplés. Seul ADC1 accède au capteur de température interne et à VREFINT.

🎯

Modes de conversion

  • Unique : une conversion, puis stop
  • Continu : boucle automatique
  • Balayage : séquence de canaux
  • Discontinu : N canaux / déclenchement
🔴
Plage de tension d'entrée

Toujours entre V_REF− (GND) et V_REF+. Sur Nucleo/Discovery : V_REF+ = VDD = 3,3 V. N'appliquez jamais plus de 3,3 V — destruction immédiate.

Module 07

Registres Clés

Même avec HAL, comprendre les registres est fondamental. Chaque appel HAL finit par écrire dans ces bits.

ADC_CR1 — Contrôle 1

31
26
OVRIE
25–24
RES[1:0]
23
AWDEN
13–11
DISCNUM
10
DISCEN
8
SCAN
5
EOCIE
4–0
AWDCH

ADC_CR2 — Contrôle 2

31
SWSTART
29–28
EXTEN
11
ALIGN
10
EOCS
8
DMA
1
CONT
0
ADON
RegistreBit(s)DescriptionHAL équivalent
ADC_CR1RES[1:0]Résolution : 00=12b 01=10b 10=8b 11=6bhadc.Init.Resolution
ADC_CR1SCANMode balayage multi-canauxhadc.Init.ScanConvMode
ADC_CR2ADONMise sous tension du CANHAL_ADC_Start()
ADC_CR2CONTMode continu automatiquehadc.Init.ContinuousConvMode
ADC_CR2DMAActive requêtes DMAHAL_ADC_Start_DMA()
ADC_CR2SWSTARTDéclenchement logicielHAL_ADC_Start()
ADC_DR[11:0]Registre de données (lecture efface EOC)HAL_ADC_GetValue()
Module 08

Configuration CubeMX + HAL

Chaque option de configuration HAL expliquée — pourquoi elle existe et ce qu'elle fait au niveau registres.

01

Sélectionner le MCU

STM32CubeMX → "Start from MCU" → STM32F407VGTx → toolchain STM32CubeIDE.

02

Activer le périphérique ADC

Analogique → ADC1 → activer IN0 → PA0 passe automatiquement en mode Analogique.

03

Paramètres clés

Prescaler /4 (21 MHz), Résolution 12b, Alignement Droit, Balayage OFF, Mode Continu OFF.

f_ADC = f_APB2 / Prescaler  ≤  36 MHz
04

Code généré par CubeMX

MX_ADC1_Init — C / HAL
static void MX_ADC1_Init(void) {
  ADC_ChannelConfTypeDef sConfig = {0};
  hadc1.Instance              = ADC1;
  hadc1.Init.ClockPrescaler   = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution       = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode     = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign        = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion  = 1;
  if (HAL_ADC_Init(&hadc1) != HAL_OK) Error_Handler();
  sConfig.Channel      = ADC_CHANNEL_0;
  sConfig.Rank         = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}

Mode Scrutation (Polling)

main.c — Polling
uint32_t adc_raw; float voltage; char buf[64];
while (1) {
  HAL_ADC_Start(&hadc1);
  HAL_ADC_PollForConversion(&hadc1, 100);
  adc_raw = HAL_ADC_GetValue(&hadc1);
  HAL_ADC_Stop(&hadc1);
  voltage = ((float)adc_raw / 4095.0f) * 3.3f;
  sprintf(buf, "Raw: %lu | V: %.3f V\r\n", adc_raw, voltage);
  HAL_UART_Transmit(&huart2, (uint8_t*)buf, strlen(buf), 100);
  HAL_Delay(500);
}
Module 09

CAN avec DMA — Charge CPU Nulle

Le DMA transfère chaque résultat directement en RAM sans intervention CPU. Indispensable pour l'acquisition multi-canaux haute vitesse.

ℹ️
Mapping DMA fixe en matériel

ADC1 → DMA2, Stream 0, Channel 0. Non configurable. Consultez le tableau "DMA2 request mapping" du Reference Manual RM0090.

main.c — ADC + DMA Circulaire
/* Buffer GLOBAL — jamais sur la pile ! */
uint32_t dma_buf[4];
volatile uint8_t dma_ready = 0;

HAL_ADC_Start_DMA(&hadc1, dma_buf, 4);

while (1) {
  if (dma_ready) {
    dma_ready = 0;
    float ch0 = (dma_buf[0] / 4095.0f) * 3.3f;
    float ch1 = (dma_buf[1] / 4095.0f) * 3.3f;
    display_update(ch0, ch1);
  }
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
  if (hadc->Instance == ADC1) dma_ready = 1;
}
Scan Mode — 4 canaux + capteur température
hadc1.Init.ScanConvMode          = ENABLE;
hadc1.Init.NbrOfConversion       = 4;
hadc1.Init.ContinuousConvMode    = ENABLE;
hadc1.Init.DMAContinuousRequests = ENABLE;

ADC_ChannelConfTypeDef sConfig;
sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_84CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);

sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = 2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);

/* Capteur Température — 480 cycles obligatoires */
sConfig.Channel      = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank         = 4;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);

uint32_t buf[4];
HAL_ADC_Start_DMA(&hadc1, buf, 4);
Module 10 — Exercices Pratiques

5 Problèmes Réels

Situations terrain avec code C complet — toutes les solutions sont affichées directement.

🎛️
Problème 01 · Débutant
Lecture d'un Potentiomètre
Canal unique · Scrutation · ⭐ Débutant
📋 Énoncé

Potentiomètre 10 kΩ entre 3,3 V et GND, curseur sur PA0. Lire en scrutation toutes les 500 ms, convertir en tension et pourcentage, envoyer via UART2 à 115 200 bps.

Q1
Formule : Brut = 3072, VREF = 3,3V, 12 bits → Tension = 2,476 V
💡 V = (brut / 4095) × 3,3
Q2
Séquence HAL : StartPollForConversionGetValueStop
Q3
Durée : 3 cycles S/H @ 21 MHz → 0,738 µs
✅ Solution Complète
Solution P1 — Potentiomètre
uint32_t raw; float v, pct; char buf[80];
while (1) {
  HAL_ADC_Start(&hadc1);
  HAL_ADC_PollForConversion(&hadc1, 100);
  raw = HAL_ADC_GetValue(&hadc1);
  HAL_ADC_Stop(&hadc1);
  v   = ((float)raw / 4095.0f) * 3.3f;
  pct = ((float)raw / 4095.0f) * 100.0f;
  sprintf(buf, "%.1f%%  %.3f V\r\n", pct, v);
  HAL_UART_Transmit(&huart2, (uint8_t*)buf, strlen(buf), 100);
  HAL_Delay(500);
}
🌡️
Problème 02 · Intermédiaire
Capteur de Température Interne
Canal interne · Calibration usine · ⭐⭐
📋 Énoncé

STM32F407 intègre un capteur sur ADC1_IN16. Données usine : TS_CAL1 @ 0x1FFF7A2C (30°C), TS_CAL2 @ 0x1FFF7A2E (110°C). Afficher température °C chaque seconde.

✅ Solution Complète
Clé

Activer le bit TSVREFE (bit 23) dans ADC_CCR avant toute lecture. Désactivé par défaut.

Solution P2 — Température interne
float ReadTemperature(void) {
  ADC->CCR |= ADC_CCR_TSVREFE;
  ADC_ChannelConfTypeDef sConfig = {0};
  sConfig.Channel      = ADC_CHANNEL_TEMPSENSOR;
  sConfig.Rank         = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
  HAL_ADC_Start(&hadc1);
  HAL_ADC_PollForConversion(&hadc1, 100);
  uint32_t raw = HAL_ADC_GetValue(&hadc1);
  HAL_ADC_Stop(&hadc1);
  uint16_t *cal1 = (uint16_t*)0x1FFF7A2C;
  uint16_t *cal2 = (uint16_t*)0x1FFF7A2E;
  return (110.0f-30.0f) / ((float)(*cal2-*cal1))
         * ((float)raw - *cal1) + 30.0f;
}
💡
Problème 03 · Intermédiaire
Contrôle Automatique de Luminosité (LDR)
Hystérésis · ⭐⭐
📋 Énoncé

LDR en diviseur de tension → PA1. Allumer LED sur PD12 si sombre (ADC > 3000), éteindre si clair (ADC < 1000). Implémenter une zone d'hystérésis.

✅ Solution Complète
Solution P3 — LDR + Hystérésis
#define THR_DARK   3000
#define THR_BRIGHT 1000
static uint8_t led = 0;

HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
uint32_t raw = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);

if (!led && raw > THR_DARK) {
  led = 1;
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
} else if (led && raw < THR_BRIGHT) {
  led = 0;
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
}
🔋
Problème 04 · Avancé
Moniteur de Tension Batterie LiPo
Diviseur · Suréchantillonnage · ⭐⭐⭐
📋 Énoncé

LiPo 1S : 3,0V–4,2V. Diviseur R1=R2=100kΩ → PA4. Suréchantillonnage ×16. Afficher tension réelle, SoC (0–100%), alarme si V < 3,2V.

✅ Solution Complète
Solution P4 — Batterie LiPo
#define N_OS 16
uint32_t sum = 0;
for (int i=0; i<N_OS; i++) {
  HAL_ADC_Start(&hadc1);
  HAL_ADC_PollForConversion(&hadc1, 10);
  sum += HAL_ADC_GetValue(&hadc1);
  HAL_ADC_Stop(&hadc1);
}
float v_adc = ((float)(sum/N_OS) / 4095.0f) * 3.3f;
float v_bat = v_adc * 2.0f;
float soc   = (v_bat - 3.0f) / (4.2f - 3.0f) * 100.0f;
if(soc < 0) soc = 0;
if(soc > 100) soc = 100;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14,
  v_bat < 3.2f ? GPIO_PIN_SET : GPIO_PIN_RESET);
🕹️
Problème 05 · Avancé
Joystick Double Axe avec DMA
2 canaux · DMA Balayage · Zone morte · ⭐⭐⭐
📋 Énoncé

Joystick : axe X sur PA0, axe Y sur PA1. DMA balayage continu. Normaliser −1,0 à +1,0. Zone morte ±150 pts autour du centre (2048). Sortie PWM moteur @ 50 Hz.

✅ Solution Complète
Solution P5 — Joystick DMA
uint32_t joy[2];
#define CENTER 2048
#define DZ     150

float deadzone(uint32_t raw) {
  int32_t e = (int32_t)raw - CENTER;
  if (e > -DZ && e < DZ) return 0.0f;
  float v = ((float)e - (e > 0 ? DZ : -DZ)) / (float)(CENTER - DZ);
  return v > 1.0f ? 1.0f : v < -1.0f ? -1.0f : v;
}

HAL_ADC_Start_DMA(&hadc1, joy, 2);
while(1) {
  float x = deadzone(joy[0]);
  float y = deadzone(joy[1]);
  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, (uint32_t)(500 + x * 500));
  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, (uint32_t)(500 + y * 500));
  HAL_Delay(20);
}
Module 11

Conseils Pro & Erreurs Courantes

Ce qui distingue un débutant d'un ingénieur embarqué professionnel — condensé de leçons terrain.

Guide Temps S/H

ImpédanceS/H recommandé
< 1 kΩ3 – 15 cycles
1 – 10 kΩ28 – 56 cycles
> 10 kΩ84 – 480 cycles
Temp. interne480 cycles
🚫

Ne Jamais Faire

  • ❌ Appliquer >3,3V sur broche ADC
  • ❌ Broche GPIO flottante
  • ❌ Oublier volatile sur buffer DMA
  • ❌ TSVREFE non activé
  • ❌ Buffer DMA sur la pile
  • ❌ Horloge ADC > 36 MHz
🔌

Filtre Anti-Repliement

R = 1 kΩ en série + C = 100 nF vers GND → f_coupure ≈ 1,6 kHz. Placer le plus proche possible de la broche MCU.

🧮

Quel Mode Choisir ?

ModeUsage
ScrutationDébutant, <1 Hz
InterruptÉvénementiel
DMA Single1 canal rapide
DMA ScanMulti-capteurs

🧮 Calculateur de Temps de Conversion — Exemple

f_ADC = 84 MHz / 4 = 21 MHz
t_conv = (84 + 12,5) / 21 000 000 = 4,595 µs
Débit max ≈ 217 600 SPS par canal

📚 Référence Rapide HAL

Fonction HALAction registreUsage
HAL_ADC_Init()Écrit CR1, CR2, SMPRInit obligatoire
HAL_ADC_ConfigChannel()Configure SQR, SMPRSéquenceur
HAL_ADC_Start()SWSTART ← 1 dans CR2Scrutation
HAL_ADC_PollForConversion()Scrute EOC dans SRAttente bloquante
HAL_ADC_GetValue()Lit ADC_DRRécupère résultat
HAL_ADC_Stop()ADON ← 0 dans CR2Arrêt propre
HAL_ADC_Start_DMA()DMA + CONT bitsMode DMA circulaire
HAL_ADC_Stop_DMA()DMA ← 0, ADON ← 0Arrêt DMA
STM32F4 ADC 12-bit HAL DMA CubeMX C Embarqué SAR Shannon
RM0090 Rev 19  ·  HAL STM32F4xx UM1725  ·  Datasheet STM32F407  ·  ADC1/2/3 · 12-bit SAR · 2,4 MSPS