Générer des tensions variables grâce au PWM en MicroPython

Par tensions variables, -> tension constante dont on peut faire varier la valeur pas un DAC !!

Le PWM, une astuce pour générer des tensions variables sur des broches numériques

Le PWM est une technique qui permet de générer une tension comprise entre 0 et 3.3V en utilisant uniquement des sorties numériques . PWM est l’acronyme de Pulse With Modulation , en français c’est appelé MLI pour Modulation de Largeur d’Impulsion . En effet, cette astuce repose sur la proportion temporelle d’un signal logique à son état haut (3.3V) et à son état bas (0V) : le PWM consiste à faire varier la largeur d’une impulsion électrique.

fonctionnement pwm micropython

Principe du PWM

La succession d’impulsion avec une largeur donnée est vue en moyenne comme une tension constante comprise entre 0V et 3.3V dont sa valeur est déterminée par :

\[V_{sortie} = V_{entree} \times \alpha\]

avec α la largeur d’impulsion ( duty cycle en anglais)

Un signal PWM se configure via la largeur d’impulsion et la fréquence de l’impulsion. On pourra modifier ces 2 paramètres en MicroPython.

En pratique, le PWM est utilisé pour :
  • Contrôler la vitesse d’un moteur

  • Contrôler la luminosité de LED

  • Générer des signaux carrés (avec α=0.5)

  • Générer des notes de musiques (son similaire à celui des consoles rétro)

Performance du PWM sur la Raspberry Pi Pico et l’ESP32 :

Les signaux PWM sont non pas générés en permanence par le microprocesseur mais par des blocs matériels dédiés. Il suffit ainsi de configurer une seule fois les blocs PWM dans le script pour que le signal soit généré en continu en tache de fond. On associe une sortie d’un bloc PWM à une broche de notre carte. Les ressources du processeur seront libres pour exécuter d’autres tâches.

Chaque bloc PWM peut avoir une fréquence indépendante. Les caractéristiques techniques des blocs PWM sont différentes entre l’ESP32 et la Raspberry Pi Pico.

Comparaison du PWM entre l’ESP32 et la Pi Pico

Pi Pico

ESP32

Plage de fréquence du signal PWM

7 Hz à 125 Mhz

1Hz à 40 Mhz

Fréquence PWM indépendante

8

8

Sortie PWM

16

16

Résolution de la largeur d’impulsion

16 bits

10 bits

Note

En pratique, dans la majorité des cas, une fréquence PWM de 1000 Hz sera suffisante.

PWM sur MicroPython

L’utilisation du PWM avec MicroPython est très simple. MicroPython se charge de sélectionner automatiquement un bloc PWM disponible : il n’est pas nécessaire d’indiquer celui que l’on compte utiliser. La partie hardware décrite au-dessus est complétement masquée.

La configuration consiste à associer un objet PWM à un objet Pin et de choisir la fréquence PWM.

from machine import Pin, PWM

pin = Pin(25, mode=Pin.OUT)
pin_with_pwm = PWM(pin) # Attach PWM object on a pin

Note

Puisque l’objet PWM est aussi dans le module machine, on peut réunir les 2 imports sur une seule ligne :

from machine import Pin
from machine import PWM

est équivalent à

from machine import Pin, PWM

Une fois que l’on a lié une broche de la carte à notre objet PWM, toutes les fonctions se font directement sur l’objet PWM (Contrairement au code Arduino ou l’on précise le numéro de pin avec la fonction analogWrite(pin_number)

Indication

En python, on peut insérer des underscores _ pour rendre les nombres plus facile à lire (et ainsi éviter de compter les zéros sur des grands nombres)

Par exemple, pour écrire 1 MHz , au lieu d’avoir 1000000 ou peut mettre 1_000_000

On utilise la fonction .duty_u16() pour choisir la largeur d’impulsion avec une valeur comprise entre 0 et 2 16 (0-65535). La tension est imposée directement en sortie du pin sélectionné précédemment.

Avertissement

Sur l’ESP32, il faut utiliser la fonction .duty() au lieu de .duty_u16() . _u16 précise le fait que la fonction attend une valeur sur 16 bits en entrée. Puisque la largeur d’impulsion n’est que sur 10 bits sur ESP32, on utilise la fonction .duty() à la place

Pour se rendre compte de la variation de la tension, on peut utiliser la LED intégrée de votre carte avec le script suivant :

from machine import Pin, PWM

LED_BUITLTIN = 25 # For Raspberry Pi Pico
# LED_BUITLTIN = 2 # For ESP32

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin

# Settings
pwm_led.freq(1_000)

while True:
    for duty in range(0,65025):
        pwm_led.duty_u16(duty) # For Pi Pico
        #pwm_led.duty(duty) # For ESP32

Pour que ce soit plus parlant, on pourrait préciser juste le pourcentage du duty cycle puis appliquer une formule.

from machine import Pin, PWM

LED_BUITLTIN = 25 # For Raspberry Pi Pico
# LED_BUITLTIN = 2 # For ESP32

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin
pwm_led.freq(1_000)

duty_cycle = 50 # Between 0 - 100 %
pwm_led.duty_u16(int((duty_cycle/100)*65535))

On peut fait varier la luminosité de le LED en faisant varier entre 0 et 100% la largeur d’impulsion.

from machine import Pin, PWM
import time

LED_BUITLTIN = 25 # For Raspberry Pi Pico
# LED_BUITLTIN = 2 # For ESP32

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin
pwm_led.freq(1_000)

while True:
    for duty in range(100): # Duty from 0 to 100 %
        pwm_led.duty_u16(int((duty/100)*65535))
        time.sleep_ms(10)

Mini-Projet : Faire dimmer la LED intégrée de sa carte

Avec tout ce que l’on a vu, le script est très facile à faire :

from machine import Pin, PWM

LED_BUITLTIN = 25 # For Raspberry Pi Pico
# LED_BUITLTIN = 2 # For ESP32

pwm_led = PWM(Pin(LED_BUITLTIN, mode=Pin.OUT)) # Attach PWM object on the LED pin

# Settings
pwm_led.freq(1_000)

while True:
    for duty in range(0,65025, 5):
        pwm_led.duty_u16(duty)
    for duty in range(65025,0, -5):
        pwm_led.duty_u16(duty)

Note

Pour que la tension varie plus vite, on fait varier le duty de 5 en 5 avec range(0,65025, 5) . Pour décrémenter une valeur, on met un pas de -5 : range(65025,0, -5) .