Files
formation-arduino/schemas/m03_pwm_adc.py
T
ArtemiaandClaude Opus 5.5 d933e348c0 Retire les .odt et l'insertion XML, désormais remplacés par Quarto
Les supports .odt restent récupérables via le tag avant-quarto. Les figures ne déclarent plus que leur identifiant et leur fonction de dessin (légende et emplacement dans le .qmd). Génération SVG reproductible (sans date, identifiants stables) pour des diffs git propres. README : mode d'emploi Quarto et schémas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:04:25 +02:00

146 lines
6.4 KiB
Python

"""Module 3 — Signaux analogiques : PWM & ADC."""
import numpy as np
import matplotlib.pyplot as plt
import schemdraw
import schemdraw.elements as elm
from style import BLEU, ORANGE, GRIS, ENCRE, VERT, CM, Figure
schemdraw.use("matplotlib")
def _signal_carre(rapport, periodes=3, points=2000):
t = np.linspace(0, periodes, points, endpoint=False)
return t, np.where((t % 1) < rapport, 3.3, 0.0)
def pwm_rapport_cyclique():
fig, axes = plt.subplots(3, 1, figsize=(16 * CM, 11 * CM), sharex=True)
for ax, rapport in zip(axes, (0.25, 0.50, 0.75)):
t, v = _signal_carre(rapport)
moyenne = 3.3 * rapport
ax.fill_between(t, v, step=None, color=BLEU, alpha=0.12)
ax.plot(t, v, color=BLEU)
ax.axhline(moyenne, color=ORANGE, ls="--", lw=1.8)
ax.text(3.04, moyenne, f"moyenne\n{round(moyenne + 1e-9, 2):.2f} V".replace(".", ","),
color=ORANGE, va="center", fontsize=9, fontweight="bold")
ax.set_ylim(-0.3, 4.1)
ax.set_yticks([0, 3.3], ["0 V", "3,3 V"])
ax.set_title(f"Rapport cyclique {int(rapport * 100)} % → analogWrite(pin, {round(rapport * 255)})",
loc="left", fontsize=10, pad=16 if rapport == 0.25 else 6)
ax.grid(axis="x")
# Cotes T et t_on sur le premier graphe
ax = axes[0]
ax.annotate("", xy=(1, 3.75), xytext=(2, 3.75),
arrowprops=dict(arrowstyle="<->", color=ENCRE, lw=1))
ax.text(1.5, 3.85, "période T", ha="center", va="bottom", fontsize=8.5, color=ENCRE)
ax.annotate("", xy=(0, 3.75), xytext=(0.25, 3.75),
arrowprops=dict(arrowstyle="<->", color=ENCRE, lw=1))
ax.text(0.125, 3.85, "t$_{on}$", ha="center", va="bottom", fontsize=8.5, color=ENCRE)
axes[-1].set_xticks([0, 1, 2, 3], ["0", "T", "2T", "3T"])
axes[-1].set_xlabel("temps (ex. T = 0,2 ms à 5 kHz)")
fig.tight_layout()
return fig
def servo_50hz():
fig, axes = plt.subplots(3, 1, figsize=(16 * CM, 9.5 * CM), sharex=True)
cas = ((1.0, "0°"), (1.5, "90°"), (2.0, "180°"))
t = np.linspace(0, 40, 4000, endpoint=False)
for ax, (largeur, angle) in zip(axes, cas):
v = np.where((t % 20) < largeur, 3.3, 0.0)
ax.plot(t, v, color=BLEU, lw=1.8)
ax.fill_between(t, v, color=BLEU, alpha=0.12)
ax.set_ylim(-0.3, 4.3)
ax.set_yticks([0, 3.3], ["0 V", "3,3 V"])
ax.text(22.8, 2.0, f"impulsion {str(largeur).replace('.', ',')} ms → bras à {angle}",
fontsize=9, color=ENCRE, va="center")
ax.grid(axis="x")
ax = axes[0]
ax.annotate("", xy=(0, 3.85), xytext=(20, 3.85),
arrowprops=dict(arrowstyle="<->", color=ORANGE, lw=1.2))
ax.text(10, 3.95, "période = 20 ms (50 Hz)", ha="center", va="bottom",
fontsize=9, color=ORANGE, fontweight="bold")
axes[-1].set_xticks([0, 5, 10, 15, 20, 25, 30, 35, 40])
axes[-1].set_xlabel("temps (ms)")
fig.tight_layout()
return fig
def montage_variateur():
d = schemdraw.Drawing(show=False)
d.config(fontsize=11, unit=2.5, color=ENCRE)
ic = elm.Ic(
pins=[
# schemdraw empile les broches de gauche de bas en haut
elm.IcPin(name="GND", side="left", anchorname="GNDL"),
elm.IcPin(name="GPIO1 (ADC1)", side="left", anchorname="ADC"),
elm.IcPin(name="3V3", side="left", anchorname="V33"),
elm.IcPin(name="GPIO4 (PWM)", side="right", anchorname="PWM", slot="1/3"),
],
size=(6.5, 4.2), pinfontsize=10, lblsize=13,
).label("ESP32-S3", loc="top", fontsize=14).color(BLEU)
d += ic
# Potentiomètre 10 kΩ entre 3V3 et GND, curseur vers GPIO1
haut = ic.V33
bas = ic.GNDL
d += elm.Line().at(haut).left().length(2.2)
p_haut = d.here
d += elm.Line().at(bas).left().length(2.2)
p_bas = d.here
d += (pot := elm.Potentiometer().at(p_haut).down().toy(p_bas).label("10 kΩ", loc="top").color(VERT))
d += elm.Wire("-").at(pot.tap).to((ic.ADC[0], pot.tap[1])).color(VERT)
d += elm.Ground().at(p_bas)
# Sortie PWM : résistance 220 Ω puis LED vers GND
d += elm.Line().at(ic.PWM).right().length(1)
d += elm.Resistor().right().label("220 Ω").color(ORANGE)
d += elm.LED().down().label("LED", loc="bottom").color(ORANGE)
d += elm.Ground()
return d.draw(show=False)
def adc_quantification():
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16 * CM, 7.5 * CM),
gridspec_kw={"width_ratios": [1, 1.25]})
# (a) principe de la quantification, illustré sur 3 bits
v = np.linspace(0, 3.3, 1000)
niveaux = 8
code = np.minimum(np.floor(v / 3.3 * niveaux), niveaux - 1)
ax1.plot(v, v / 3.3 * (niveaux - 1), color=GRIS, ls=":", lw=1.3, label="tension réelle")
ax1.step(v, code, where="post", color=BLEU, label="valeur numérique")
ax1.set_xlim(0, 3.3)
ax1.set_yticks(range(niveaux))
ax1.set_xlabel("tension d'entrée (V)")
ax1.set_ylabel("valeur lue")
ax1.set_title("(a) Principe : la tension est\ndécoupée en paliers", loc="left", fontsize=10)
ax1.text(2.35, 0.6, "illustré avec 8 paliers (3 bits)\nl'ESP32-S3 en a 4096 (12 bits)",
fontsize=7.5, color=GRIS, ha="center")
ax1.legend(fontsize=7.5, loc="upper left", frameon=False)
# (b) chaîne de conversion utilisée dans l'exercice
ax2.plot([0, 3.3], [0, 4095], color=BLEU)
ax2.set_xlim(0, 3.3)
ax2.set_ylim(0, 4095)
ax2.set_yticks([0, 1024, 2048, 3072, 4095])
ax2.set_xlabel("tension d'entrée (V)")
ax2.set_ylabel("analogRead()", color=BLEU)
sec = ax2.secondary_yaxis("right", functions=(lambda x: x * 255 / 4095, lambda y: y * 4095 / 255))
sec.set_ylabel("map() → PWM", color=ORANGE)
sec.set_yticks([0, 85, 170, 255])
sec.tick_params(colors=ORANGE)
sec.spines["right"].set_visible(True)
sec.spines["right"].set_color(ORANGE)
ax2.plot([1.65, 1.65, 3.3], [0, 2048, 2048], color=ORANGE, ls="--", lw=1.2)
ax2.plot(1.65, 2048, "o", color=ORANGE)
ax2.text(1.75, 1150, "1,65 V → 2048\nmap() → 127", color=ORANGE, fontsize=8.5, fontweight="bold")
ax2.set_title("(b) Lecture puis mise à l'échelle", loc="left", fontsize=10)
fig.tight_layout()
return fig
FIGURES = [
Figure("m03-pwm-rapport-cyclique", pwm_rapport_cyclique),
Figure("m03-servo-50hz", servo_50hz),
Figure("m03-montage-variateur", montage_variateur),
Figure("m03-adc-quantification", adc_quantification),
]