MakerSpace Amiens · Microcontrôleurs

Entrées et sorties

CM2 : GPIO, ADC et PWM

Alban Petit

Au programme

  1. Zéro ou un
  2. Une broche en sortie
  3. Une broche en entrée
  4. Lire le monde analogique
  5. Simuler une tension
2UniLaSalle Amiens

Ce que vous saurez faire

À la fin de ce cours, vous saurez :

  • distinguer un signal numérique d’un signal analogique
  • trouver une valeur dans la datasheet
  • configurer une broche, et calculer la résistance série d’une LED
  • commander une charge de puissance par un transistor
  • expliquer l’état flottant, et choisir entre pull-up et pull-down
  • filtrer les rebonds d’un bouton, par scrutation ou par interruption
  • lire une tension avec l’ADC, et la mettre à l’échelle
  • produire une tension moyenne avec le PWM

GPIO et monde numérique ADC et PWM

3UniLaSalle Amiens

Quiz : ce qu’il reste du CM1

  1. Où est rangé le programme quand la carte est éteinte ?
  2. Où vit une variable locale d’une fonction ?
  3. Que fait REG |= (1 << 5) ?
  4. Pourquoi ne pas mettre de delay() dans loop() ?
  5. Le C++ de l’ESP32 est-il compilé ou interprété ?
  6. Comment le matériel prévient-il le programme d’un événement ?
  7. Quelle tension au plus sur une broche de l’ESP32-S3 ?
4UniLaSalle Amiens

Quiz : correction

  Réponse
1 En mémoire flash, qui ne s’efface pas à la coupure
2 Sur la pile, en RAM, libérée à la sortie de la fonction
3 Met le bit 5 du registre à 1, sans toucher aux autres
4 delay() bloque tout ; on regarde l’heure avec millis()
5 Compilé, une fois pour toutes, avant le téléversement
6 Par une interruption
7 3,3 V, jamais 5 V

Ces quatre notions reviennent aujourd’hui, appliquées aux broches.

Architecture d’un microcontrôleur Du matériel au logiciel

5UniLaSalle Amiens

Zéro ou un

Les niveaux logiques

Deux natures de signal

À gauche, un signal numérique en créneaux qui ne prend que deux niveaux, 0 V et 3,3 V ; à droite, un signal analogique qui varie en continu entre les deux

  Numérique Analogique
Valeurs possibles 2 (LOW / HIGH) continues, de 0 à 3,3 V
Fonction Arduino digitalRead / digitalWrite analogRead (ADC)
Dans votre montage les boutons les axes du joystick

GPIO et monde numérique ADC et PWM

7UniLaSalle Amiens

Les seuils de l’ESP32-S3

Seuil Valeur Interprétation
$V_{IL}$ (LOW max) ≤ 0,25 × VDD ≈ 0,8 V lu comme LOW
Zone indéterminée ~0,8 V à ~2,5 V lecture imprévisible
$V_{IH}$ (HIGH min) ≥ 0,75 × VDD ≈ 2,5 V lu comme HIGH

En sortie, la puce garantit à l’inverse des niveaux francs : proche de 0 V, ou proche de 3,3 V.

Cette zone grise au milieu explique tout ce qui suit sur les entrées flottantes. Retenez-la.

GPIO et monde numérique

8UniLaSalle Amiens

Les seuils, sur une échelle

LOW sous 0,8 V, zone indéterminée entre 0,8 et 2,5 V, HIGH au-dessus de 2,5 V

Une entrée flottante traîne dans la zone rouge.

GPIO et monde numérique

8.1UniLaSalle Amiens

La datasheet, page « DC Characteristics »

Symbole Ce que c’est Valeur
$V_{IH}$ Tension minimale lue comme HIGH 0,75 × VDD
$V_{IL}$ Tension maximale lue comme LOW 0,25 × VDD
$V_{OH}$ Tension minimale garantie en sortie HIGH 0,8 × VDD
$V_{OL}$ Tension maximale garantie en sortie LOW 0,1 × VDD
$I_{OH}$ Courant que peut fournir une sortie HIGH 40 mA
$R_{PU}$, $R_{PD}$ Résistances de tirage internes 45 kΩ

Extrait de la datasheet de l’ESP32-S3 (VDD = 3,3 V, 25 °C). Juste avant, le tableau des valeurs maximales absolues : 3,6 V au plus sur une broche.

ESP32-S3 : datasheet GPIO et monde numérique

9UniLaSalle Amiens

Une broche en sortie

D’une LED à un moteur

Configurer, puis écrire

pinMode(LED_PIN, OUTPUT);     // la broche devient une sortie

digitalWrite(LED_PIN, HIGH);  // 3,3 V : LED allumée
digitalWrite(LED_PIN, LOW);   // 0 V   : LED éteinte

GPIO veut dire General Purpose Input/Output : la même broche peut servir d’entrée ou de sortie, et c’est le logiciel qui décide, à l’exécution.

Vu au CM1 : derrière pinMode, un registre de configuration ; derrière digitalWrite, un bit d’un registre de sortie.

GPIO et monde numérique Architecture d’un microcontrôleur

11UniLaSalle Amiens

Toujours une résistance série

Une LED n’a quasiment pas de résistance propre : branchée seule, elle tire tout le courant que la broche peut fournir, et l’une des deux grille.

$$ R = \frac{V_{alim} - V_f}{I} = \frac{3{,}3 - 2}{0{,}01} = 130\ \Omega $$

Pour une LED rouge ($V_f \approx 2$ V) à 10 mA : 150 Ω en valeur normalisée E12.

Courant maximal par broche : ~40 mA. En pratique, rester sous 12 mA. ↓ pour le calculateur en ligne.

GPIO et monde numérique

12UniLaSalle Amiens

Le calculateur de résistance

12.1UniLaSalle Amiens

Combien de LED sur une broche ?

12 mA

par broche, en pratique. Soit une LED correctement calibrée.

40 mA

le maximum de la datasheet. Plus, et la broche grille.

Pour piloter plus de courant (un moteur, un ruban de LED, un relais), la broche ne fournit pas la puissance : elle commande un transistor, qui lui la fournit.

GPIO et monde numérique

13UniLaSalle Amiens

Commander une charge : le MOSFET

Montage côté bas : la broche GPIO de la XIAO commande la grille d'un MOSFET N par une résistance de 100 ohms, avec une résistance de 10 kilo-ohms vers la masse ; le moteur est relié entre le +12 V et le drain, une diode de roue libre à ses bornes, la source à la masse commune

La broche n’impose qu’une tension sur la grille : le courant du moteur vient de sa propre alimentation. La 10 kΩ garde le moteur arrêté au démarrage ; la diode, deux slides plus loin.

GPIO et monde numérique

14UniLaSalle Amiens

Choisir le MOSFET

Paramètre Ce que c’est À viser
$V_{GS(th)}$ Tension de grille où il commence à conduire Bien sous 3,3 V
$R_{DS(on)}$ Résistance une fois passant : elle chauffe Donnée à 2,5 V, quelques dizaines de mΩ
$I_D$ Courant maximal Au moins 2 × celui de la charge
$V_{DS}$ Tension maximale Au-dessus de l’alimentation de la charge

Un MOSFET « logic level » conduit franchement avec 3,3 V : AO3400 (CMS), IRLB8721 (traversant).

Un IRF540 classique attend 10 V sur sa grille : à 3,3 V, il chauffe sans conduire.

GPIO et monde numérique

15UniLaSalle Amiens

La diode de roue libre

Moteur, relais, électro-aimant : une bobine s’oppose à toute variation brutale de courant. Quand le MOSFET coupe, elle produit une pointe de plusieurs dizaines de volts, qui détruit le transistor.

La parade : une diode aux bornes de la charge, cathode côté +. Elle offre un chemin au courant le temps qu’il s’éteigne.

Charge inductive : une diode en parallèle, sans exception.

1N4007 pour un relais, une Schottky (1N5819) pour un moteur en PWM rapide.

GPIO et monde numérique

16UniLaSalle Amiens

Push-pull ou open-drain

Push-pull

Le mode par défaut d’OUTPUT. Deux transistors tirent activement la broche vers HIGH et vers LOW.

Open-drain

La broche ne peut que tirer vers LOW, ou relâcher. Indispensable sur un bus partagé comme l’I2C.

Deux composants en push-pull sur le même fil, l’un imposant 0 et l’autre 1 : c’est un court-circuit. L’open-drain règle ce problème, on le retrouvera au CM3 avec l’I2C.

GPIO et monde numérique Les bus de communication

17UniLaSalle Amiens

Push-pull et open-drain, côte à côte

Push-pull : un interrupteur vers 3,3 V et un vers 0 V ; open-drain : un seul vers 0 V, et une résistance de pull-up

En open-drain, personne n’impose le HIGH : on peut partager le fil.

GPIO et monde numérique Les bus de communication

17.1UniLaSalle Amiens

Une broche en entrée

Lire un bouton, vraiment

Activité : le programme le plus simple

pinMode(BTN_PIN, INPUT);

void loop() {
  if (digitalRead(BTN_PIN) == HIGH)
    Serial.println("Appui !");
}

Montage : la broche 3V3 de la XIAO ESP32S3 est reliée à un bouton poussoir, dont l'autre borne va sur D5 (BTN_PIN) ; rien d'autre n'est branché

Le programme est-il correct ?

19UniLaSalle Amiens

Le piège de l’entrée nue

Une broche en entrée non connectée est en haute impédance : presque aucun courant ne la traverse, et rien ne fixe sa tension.

Elle capte alors le bruit ambiant (un câble voisin, le secteur à 50 Hz, votre main qui s’approche) et sa lecture devient aléatoire. C’est l’état flottant.

Le symptôme est reconnaissable : le programme « détecte des appuis » que personne ne fait.

GPIO et monde numérique

20UniLaSalle Amiens

Pourquoi ça flotte

flowchart LR
  A["INPUT<br/>sans rappel"] --> B["Haute impédance"]
  B --> C{"Une tension<br/>est-elle imposée ?"}
  C -->|"Non"| D["Flottant :<br/>lecture imprévisible"]
  C -->|"Oui"| F["Niveau garanti<br/>au repos"]

GPIO et monde numérique

21UniLaSalle Amiens

La solution : une résistance de rappel

Pull-up : repos à HIGH

Pull-up : une résistance de 10 kilo-ohms relie D5 au 3V3, le bouton relie D5 à GND ; au repos D5 lit HIGH, bouton pressé LOW

Pull-down : repos à LOW

Pull-down : le bouton relie D5 au 3V3, une résistance de 10 kilo-ohms relie D5 à GND ; au repos D5 lit LOW, bouton pressé HIGH

La résistance fixe un niveau au repos, sans court-circuit quand le bouton se ferme.

GPIO et monde numérique

22UniLaSalle Amiens

Le rappel est déjà dans la puce

Mode Au repos Câblage requis
INPUT flottant résistance externe
INPUT_PULLUP HIGH, tiré en interne bouton vers GND
INPUT_PULLDOWN LOW, tiré en interne bouton vers 3,3 V
pinMode(BTN_PIN, INPUT_PULLUP);                // pull-up interne (~45 kΩ)
bool presse = (digitalRead(BTN_PIN) == LOW);   // logique inversée

La logique est inversée : LOW veut dire « pressé ».

GPIO et monde numérique Les modules de la mallette

23UniLaSalle Amiens

Un bouton rebondit

Les lamelles métalliques d’un bouton mécanique rebondissent pendant quelques millisecondes : une rafale de HIGH/LOW avant stabilisation, parfaitement visible à l’oscilloscope.

Un digitalRead() naïf compte alors plusieurs appuis là où vous n’en avez fait qu’un. Dans un menu de jeu, cela se traduit par une sélection qui saute deux lignes.

La parade : après un changement d’état, ignorer les lectures pendant 20 à 50 ms.

GPIO et monde numérique

24UniLaSalle Amiens

Un appui, deux boutons

Chronogramme d'un appui : le signal du bouton idéal passe une seule fois de 3,3 V à 0 V ; celui du bouton réel bascule plusieurs fois pendant environ 1 ms, soit 5 fronts descendants, avant de rester à 0 V

Même appui : le bouton idéal descend une fois, le vrai rebondit. Un digitalRead() naïf compte chaque front descendant.

GPIO et monde numérique

25UniLaSalle Amiens

L’anti-rebond, sans bloquer


const unsigned long ANTIREBOND = 30;                            // ms
int dernierEtat = HIGH;  unsigned long dernierChangement = 0;
void loop() {
  int etat = digitalRead(BTN_PIN);     // INPUT_PULLUP : LOW = pressé
  if (etat != dernierEtat) {           // le signal vient de bouger
    dernierChangement = millis();
    dernierEtat = etat;
  }
  if (millis() - dernierChangement > ANTIREBOND && etat == LOW) {
    // enfoncé et stable depuis 30 ms (vrai tant qu'il reste enfoncé)
  }
}

Même motif qu’au CM1 : comparer millis() à une date mémorisée.

GPIO et monde numérique

26UniLaSalle Amiens

La fenêtre de 30 ms

Le signal rebondit environ 1 ms, l'appui n'est retenu qu'une fois le signal stable depuis 30 ms

Une seule détection par appui, sans jamais bloquer la boucle.

GPIO et monde numérique

26.1UniLaSalle Amiens
  Et un simple delay(20) ?

On le voit souvent : attendre 20 ms après la première lecture. Il fige tout le programme pendant 20 ms à chaque appui, et ne règle pas la répétition quand le bouton reste enfoncé. Avec un écran à rafraîchir 60 fois par seconde, ces 20 ms se voient : prenez tout de suite l’habitude de la version millis().

Les modules de la mallette

27UniLaSalle Amiens

L’interruption, face à un bouton qui rebondit

L’interruption du CM1 se déclenche sur chaque front : un bouton qui rebondit la déclenche plusieurs fois pour un seul appui.

  • L’interruption lève un drapeau ; loop() filtre les rebonds avec millis()
  • volatile : la variable change hors de loop(), le compilateur la relit à chaque fois

Pour un bouton lu à chaque image d’un jeu, la scrutation avec anti-rebond suffit : c’est la solution du TD2.

GPIO et monde numérique

28UniLaSalle Amiens

attachInterrupt() en pratique

volatile bool appui = false;                 // écrite par l'ISR, lue par loop()
void IRAM_ATTR surAppui() {                  // la routine d'interruption (ISR)
  appui = true;                              // lever le drapeau, rien de plus
}
void setup() {
  pinMode(BTN_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BTN_PIN), surAppui, FALLING);
}
void loop() {
  if (appui) { appui = false; /* traiter l'appui */ }
}

FALLING : front descendant, donc l’appui en INPUT_PULLUP (RISING et CHANGE existent aussi). IRAM_ATTR range l’ISR en RAM, toujours accessible.

Architecture d’un microcontrôleur

29UniLaSalle Amiens

Dans une interruption, jamais

  • de delay() ni d’attente : tout le reste est figé pendant l’ISR
  • de Serial.print() ni de dessin à l’écran : bien trop lents
  • de calcul long : quelques microsecondes, pas plus
  • La règle : l’ISR note, loop() traite

Une ISR trop longue retarde les autres interruptions, dont celles du Wi-Fi : sur l’ESP32, un chien de garde finit par redémarrer la carte.

Architecture d’un microcontrôleur

30UniLaSalle Amiens

Scrutation ou interruption ?

  Scrutation Interruption
Réaction À la prochaine lecture (une image, 16 ms) Quelques microsecondes
Événement très bref Peut être manqué Jamais manqué
Rebonds Filtrés au passage Déclenchent plusieurs fois
Code Simple volatile, ISR courte
Exemples Boutons et joystick d’un jeu Encodeur rotatif, capteur de vitesse

Un encodeur rotatif produit des impulsions trop brèves pour la scrutation : là, l’interruption s’impose.

GPIO et monde numérique Architecture d’un microcontrôleur

31UniLaSalle Amiens

Une broche n’est pas figée

La GPIO matrix route UART, SPI, I2C et PWM vers presque n’importe quelle broche. Sur un Arduino Uno, le SPI est câblé en dur.

Deux exceptions à connaître :

  • l’ADC est câblé en dur : seules certaines broches mesurent
  • des broches sont réservées : strapping, Flash SPI, USB

Vérifiez le brochage de la carte avant de câbler, pas après.

GPIO et monde numérique XIAO ESP32S3

32UniLaSalle Amiens

Exemple : le schéma de la carte

Extrait du schéma KiCad de la carte : le symbole de la XIAO ESP32S3, avec SDA sur D3, SCL sur D4, SCK sur D9 et MOSI sur D10

SDA et SCL sur D3 et D4, SCK et MOSI sur D9 et D10 : un choix de routage, pas une obligation. La GPIO matrix permet de déplacer ces signaux ; seul le code change, comme SPI.begin(D9, -1, D10).

GPIO et monde numérique Concevoir la carte de la console

33UniLaSalle Amiens

Le câblage de la mallette

Module Broche Mode
Joystick : axes X et Y D0, D1 (ADC1) analogRead
Joystick : appui sur le stick D2 INPUT_PULLUP
Bouton D5 INPUT_PULLUP
Buzzer D3 PWM (tone)
Écran (SPI, vu au CM3) D7 à D10 Bibliothèque

C’est le brochage de la future carte (TD3), plus le buzzer, présent seulement sur breadboard. Un programme écrit au TD2 tournera tel quel sur la carte soudée.

Les modules de la mallette

34UniLaSalle Amiens

Le câblage, module par module

Bouton : VCC sur 3V3, GND sur GND, S sur D5

Buzzer : VCC sur 3V3, GND sur GND, S sur D3

Joystick : GND sur GND, +5V sur 3V3, VRx sur D0, VRy sur D1, SW sur D2

Le câblage de l’écran est au CM3, avec le SPI.

Les modules de la mallette

34.1UniLaSalle Amiens

Connecter un composant 5 V

  • Signal 5 V vers le µC : un pont diviseur de deux résistances ramène le niveau sous 3,3 V
  • Bus bidirectionnel (I2C par exemple) : un level shifter dédié
  • Module alimenté en 5 V : vérifier la tension de ses sorties ; si elles montent à 5 V, pont diviseur ou level shifter
  • Jamais 5 V directement sur un GPIO

Le sens inverse est sans danger pour la puce : un composant 5 V lit souvent 3,3 V comme un HIGH, mais pas toujours (vérifier son seuil $V_{IH}$).

GPIO et monde numérique

35UniLaSalle Amiens

Le pont diviseur pour un signal 5 V

Le signal 5 V traverse R1 de 10 kilo-ohms et R2 de 20 kilo-ohms vers la masse ; le point milieu à 3,3 V va sur l'entrée

Deux résistances suffisent dans le sens 5 V vers la puce.

GPIO et monde numérique

35.1UniLaSalle Amiens

Lire le monde analogique

L’ADC

Du continu vers l’entier

Une température, une position de joystick, une luminosité produisent des tensions qui varient en continu entre 0 et 3,3 V. Le microcontrôleur, lui, ne manipule que des entiers.

Le convertisseur analogique-numérique (ADC) fait la traduction.

int valeur = analogRead(PIN_JOYSTICK_X);   // 0 à 4095

Pas besoin de pinMode : analogRead() configure la broche lui-même.

ADC et PWM

37UniLaSalle Amiens

12 bits, soit 4096 valeurs

$$ \text{valeur} = \frac{V_{\text{entrée}}}{3{,}3} \times 4095 $$
Position du stick Tension Valeur ADC
Butée gauche ou bas 0 V 0
Centre, au repos 1,65 V ≈ 2048
Butée droite ou haut 3,3 V 4095

Cette formule suppose un ADC idéal, qui mesure exactement de 0 à 3,3 V. Les slides suivantes montrent le vrai.

ADC et PWM

38UniLaSalle Amiens

Du continu aux marches

Une tension continue convertie en valeur en marches d'escalier, 8 marches pour la lisibilité

Ici 8 marches pour les voir ; l’ESP32-S3 en a 4096.

ADC et PWM

38.1UniLaSalle Amiens

La plage réelle : l’atténuation

Le cœur de l’ADC ne mesure qu’environ 1 V : un atténuateur placé devant élargit la plage, en perdant un peu de précision.

Atténuation Plage mesurable (ESP32-S3)
0 dB 0 à ~0,95 V
2,5 dB 0 à ~1,25 V
6 dB 0 à ~1,75 V
11 dB (défaut d’Arduino-ESP32) 0 à ~3,1 V

Au-delà de ~3,1 V, la lecture sature à 4095.

ADC et PWM

39UniLaSalle Amiens

Une mesure juste : analogReadMilliVolts()

int brut = analogRead(PIN);            // 0 à 4095, courbe imparfaite
int mv   = analogReadMilliVolts(PIN);  // tension corrigée, en mV

La réponse de l’ADC n’est pas une droite parfaite : elle s’écrase près de 0 V et près de la saturation, et varie d’une puce à l’autre. Chaque ESP32-S3 sort d’usine avec des coefficients de calibration gravés dans la puce : analogReadMilliVolts() les applique.

Joystick : analogRead() suffit, on calibre le centre soi-même. Tension d’une batterie ou d’un capteur : analogReadMilliVolts().

ADC et PWM

40UniLaSalle Amiens

L’ADC réel

La courbe réelle reste à 0 sous environ 0,1 V et sature dès environ 3,1 V

La calibration d’usine redresse cette courbe.

ADC et PWM

40.1UniLaSalle Amiens

Mesurer plus de 3,3 V : le pont diviseur

Pont diviseur : la batterie LiPo (4,2 V au plus) alimente R1 puis R2, deux résistances de 100 kilo-ohms en série vers la masse ; le point milieu (2,1 V au plus) va sur une entrée ADC1 de la XIAO, avec un condensateur de 100 nF vers la masse

$$ V_{ADC} = V_{in} \times \frac{R_2}{R_1 + R_2} $$

Avec $R_1 = R_2 = 100$ kΩ, la LiPo (4,2 V au plus) donne 2,1 V au plus sur l’ADC : dans la plage, avec de la marge.

float vbat = analogReadMilliVolts(VBAT_PIN) * 2 / 1000.0;   // en volts

De grandes résistances consomment peu, mais l’ADC lit mal une source aussi « molle » : un condensateur de 100 nF sur l’entrée stabilise la mesure.

ADC et PWM

41UniLaSalle Amiens
  ADC2 est inutilisable avec le Wi-Fi

Sur ESP32, le bloc ADC2 partage du matériel avec la radio : dès que le Wi-Fi est actif, ses lectures échouent. Utilisez uniquement les broches ADC1, GPIO1 à GPIO10 : sur la XIAO, toutes les broches analogiques le sont. Le symptôme est déroutant : le montage marche parfaitement pendant des semaines, jusqu’au jour où vous activez le Wi-Fi pour le projet.

ADC et PWM Les modules de la mallette

42UniLaSalle Amiens

Un joystick, c’est deux potentiomètres

Carte ESP32-S3 sur breadboard, reliée par des fils de couleur à un module joystick

Un module joystick, ici sur une autre carte ESP32-S3

int x = analogRead(PIN_JOYSTICK_X);
int y = analogRead(PIN_JOYSTICK_Y);
bool pressed =
  digitalRead(PIN_JOYSTICK_SELECT) == LOW;

Deux axes lus par l’ADC, un bouton sous le stick en INPUT_PULLUP.

Cinq fils : VCC, GND, VRx, VRy, SW.

Les modules de la mallette

43UniLaSalle Amiens

Des valeurs brutes aux directions

enum Direction { CENTRE, HAUT, BAS, GAUCHE, DROITE };
Direction lireDirection() {
  int x = analogRead(PIN_JOYSTICK_X);
  int y = analogRead(PIN_JOYSTICK_Y);
  if (x < 1500) return GAUCHE;
  if (x > 2500) return DROITE;
  if (y < 1500) return BAS;
  if (y > 2500) return HAUT;
  return CENTRE;
}

Le centre n’est jamais à 2048 : les seuils laissent une large zone morte.

enum crée un type qui ne peut prendre que les valeurs listées : plus lisible et plus sûr qu’un entier de 0 à 4. Il revient au CM3 avec la machine à états.

ADC et PWM

44UniLaSalle Amiens

Les zones du joystick

GAUCHE sous 1500 et DROITE au-dessus de 2500, HAUT et BAS dans la colonne centrale, CENTRE dans la zone morte

X est testé avant Y : en diagonale, GAUCHE et DROITE l’emportent.

ADC et PWM

44.1UniLaSalle Amiens

Un ADC qui tremble

Une lecture analogique bruite toujours un peu : deux analogRead() consécutifs sur un stick immobile ne rendent pas la même valeur.

long somme = 0;
for (int i = 0; i < 16; i++) somme += analogRead(PIN);
int lisse = somme / 16;

Seize lectures coûtent quelques centaines de microsecondes : négligeable face aux 16 ms d’une image, et l’affichage arrête de sautiller.

ADC et PWM

45UniLaSalle Amiens

Calibrer et normaliser

calibrateJoystick();   // au démarrage, stick relâché : moyenne de 16 lectures

float normalizeJoystick(int raw, int center) {
  int offset = raw - center;
  if (abs(offset) < JOYSTICK_DEADZONE) return 0.0f;   // zone morte (300)
  int range = (offset > 0) ? (4095 - center) : center;
  return constrain((float)offset / range, -1.0f, 1.0f);
}

De -1 à +1, 0 au repos : la raquette avance d’autant plus vite que le stick est incliné.

ADC et PWM Les modules de la mallette

46UniLaSalle Amiens

Changer d’échelle : map()

map(x, a, b, c, d) passe x de [a, b] à [c, d] par une règle de trois :

$$ \text{résultat} = c + (x - a) \times \frac{d - c}{b - a} $$
int lecture = analogRead(POT_PIN);             // 0 à 4095
int angle   = map(lecture, 0, 4095, 0, 180);   // servo : 0 à 180°
int pwm     = map(lecture, 0, 4095, 0, 255);   // analogWrite : 0 à 255

Deux pièges : map() calcule en entiers (les décimales sont perdues), et ne borne pas son résultat. map(3800, 500, 3500, 0, 255) donne 280, hors de la plage d’analogWrite : constrain(pwm, 0, 255) le ramène à 255.

Exemple : potentiomètre à mi-course, lecture 2048, donc angle 90° et pwm 127.

ADC et PWM

47UniLaSalle Amiens

map(), en image

La plage 0 à 4095 projetée sur 0 à 255 : 2048 donne 127

Une règle de trois, puis les décimales sont tronquées.

ADC et PWM

47.1UniLaSalle Amiens

Simuler une tension

Le PWM

Le problème

Un GPIO ne produit que 0 V ou 3,3 V. Et l’ESP32-S3 n’a pas de DAC matériel.

Comment régler une demi-luminosité, une vitesse de moteur, la position d’un servo ?

En alternant très vite.

ADC et PWM

49UniLaSalle Amiens

Le rapport cyclique

Le PWM (Pulse Width Modulation) bascule entre HIGH et LOW à haute fréquence. La proportion de temps passée à HIGH est le rapport cyclique.

Duty cycle Effet perçu
0 % Toujours éteint
50 % Mi-luminosité
100 % Plein éclat
$$ V_{moyen} = V_{max} \times \frac{\text{duty cycle}}{100} $$

Entre 1 et 50 kHz, ni l’œil ni un moteur ne perçoivent le clignotement : seulement la moyenne. ↓ pour le démonstrateur.

ADC et PWM

50UniLaSalle Amiens

Le démonstrateur PWM

50.1UniLaSalle Amiens

En pratique

analogWrite(LED_PIN, 0);     // éteint
analogWrite(LED_PIN, 128);   // ~50 % : demi-luminosité
analogWrite(LED_PIN, 255);   // plein éclat

Derrière, Arduino-ESP32 utilise le périphérique LEDC : 8 canaux matériels indépendants, fréquence et résolution configurables (jusqu’à 14 bits).

Le CPU n’intervient pas. Une fois le canal réglé, le signal est généré par le matériel, même si la loop() est occupée ailleurs. C’est tout l’intérêt d’un périphérique dédié.

ADC et PWM Architecture d’un microcontrôleur

51UniLaSalle Amiens

Ce que le PWM permet

Application Principe
LED dimmable Duty cycle proportionnel à la luminosité
Moteur à courant continu Duty cycle proportionnel à la vitesse
Servomoteur Impulsion de 1 à 2 ms toutes les 20 ms
Buzzer (celui de la mallette) La fréquence devient un ton

Un servomoteur n’écoute pas le rapport cyclique mais la largeur de l’impulsion : c’est le même périphérique, lu autrement.

ADC et PWM

52UniLaSalle Amiens

Le signal d’un servomoteur

Une impulsion toutes les 20 ms : 1 ms pour 0°, 1,5 ms pour 90°, 2 ms pour 180°

Le servo lit la largeur de l’impulsion, pas la moyenne.

ADC et PWM

52.1UniLaSalle Amiens

Activité : de quoi avez-vous besoin ?

Pour chacun, dites quel type de broche et quelle fonction Arduino :

  1. Détecter qu’un capot est fermé (contact mécanique)
  2. Mesurer la luminosité de la pièce avec une photorésistance
  3. Faire varier la vitesse d’un ventilateur
  4. Allumer un voyant rouge de défaut
53UniLaSalle Amiens

Correction

Besoin Broche Fonction
Capot fermé GPIO en INPUT_PULLUP digitalRead
Luminosité broche ADC1 analogRead
Vitesse d’un ventilateur PWM, via un MOSFET et sa diode analogWrite
Voyant de défaut GPIO en OUTPUT + résistance digitalWrite

Quatre lignes, et vous avez déjà couvert l’essentiel de ce que fait un microcontrôleur dans l’industrie.

GPIO et monde numérique ADC et PWM

54UniLaSalle Amiens

Activité : la datasheet répond

Avec le tableau « DC Characteristics » vu en début de cours (↓ pour le revoir) :

  1. Un capteur signale un HIGH par 2,2 V. L’ESP32-S3 le lit-il ?
  2. Que vaut la résistance activée par INPUT_PULLUP ?
  3. Une broche à HIGH doit fournir 20 mA. C’est permis ?
  4. Un module sort 5 V. Peut-on le brancher directement ?
55UniLaSalle Amiens

Le tableau, pour répondre

Symbole Ce que c’est Valeur
$V_{IH}$ Tension minimale lue comme HIGH 0,75 × VDD
$V_{IL}$ Tension maximale lue comme LOW 0,25 × VDD
$V_{OH}$ Tension minimale garantie en sortie HIGH 0,8 × VDD
$V_{OL}$ Tension maximale garantie en sortie LOW 0,1 × VDD
$I_{OH}$ Courant que peut fournir une sortie HIGH 40 mA
$R_{PU}$, $R_{PD}$ Résistances de tirage internes 45 kΩ

3,6 V au plus sur une broche (valeurs maximales absolues).

ESP32-S3 : datasheet GPIO et monde numérique

55.1UniLaSalle Amiens

Correction : la datasheet

  Réponse
1 Non : 2,2 V < $V_{IH}$ ≈ 2,5 V, zone indéterminée
2 45 kΩ : un rappel « faible »
3 Oui, $I_{OH}$ = 40 mA ; en pratique, rester sous 12 mA
4 Non : 3,6 V au plus, il faut un pont diviseur

Le réflexe d’ingénieur : chercher la réponse dans la datasheet avant de la chercher sur un forum.

ESP32-S3 : datasheet

56UniLaSalle Amiens

À retenir

  • LOW sous ~0,8 V, HIGH au-dessus de ~2,5 V, rien entre les deux
  • Une entrée sans rappel flotte : INPUT_PULLUP, logique inversée
  • Un bouton mécanique rebondit : filtrer sur 20 à 50 ms
  • Une interruption : l’ISR note, loop() traite
  • L’ADC rend 0 à 4095 ; ADC1 seulement si le Wi-Fi tourne
  • map() change d’échelle, constrain() borne le résultat
  • Le PWM simule une tension moyenne : analogWrite(pin, 0..255)
  • Une charge : un MOSFET « logic level », plus une diode si inductive
  • Une LED se pilote toujours avec sa résistance série

GPIO et monde numérique ADC et PWM

57UniLaSalle Amiens

Les quatre réflexes à garder

  1. Une sortie qui pilote une LED : toujours une résistance série
  2. Une entrée qui lit un bouton : INPUT_PULLUP, bouton vers GND, logique inversée
  3. Un appui qui compte double : c’est un rebond, filtrez sur 30 ms
  4. Une lecture analogique : ADC1 uniquement, et une zone morte large au centre

Ces quatre réflexes couvrent la quasi-totalité des bugs de câblage et de lecture d’entrées.

GPIO et monde numérique ADC et PWM Les modules de la mallette

58UniLaSalle Amiens

Au prochain cours

CM3 : bus série, game loop et machine à états

  • Les bus UART, I2C et SPI, pour parler aux composants
  • Les résistances de tirage reviennent avec l’I2C, en open-drain
  • L’écran de la mallette dialogue en SPI
  • La game loop et la machine à états structurent le programme

D’ici là, relisez les concepts GPIO et ADC & PWM : ils servent dès le TD2.

Les bus de communication Machine à états finis

59UniLaSalle Amiens
UniLaSalle Amiens, Demain commence ici