Sorties PWM

Ce que l'on va faire ici

Ici, nous allons voir comment utiliser les broches GPIO en sortie en mode PWM (=largeur d'impulsion modulée) avec MicroPython

Je rappelle à toutes fins utiles que la PWM consiste à utiliser une fréquence de base fixe (500 à 1000 Hz typiquement) dont on va contrôler la proportion de niveau HAUT/BAS au sein de la période. Le résultat obtenu est une "émulation" analogique, la tension étant en moyenne égale à 3.3V x % niveau HAUT.

Liens utiles :

Les broches GPIO de la Pyboard

La Pyboard dispose de 30 broches GPIO utilisables en entrée ou en sortie.

Ces broches sont dénommées :

  • X1 à X22
  • Y1 à Y8

Infos techniques utiles

  • Les broches GPIO sont au niveau 3.3V.

  • La PWM ne sera disponible que sur les broches disposant d'un canal Timer. La pyboard dispose de 13 timers (rien que çà !) et chaque timer dispose de plusieurs canaux (1 à 4 selon).

Pour info :

  • T2 et T3 sont utilisés par PWM des LEDS onboard
  • T5 est utilisé pour les servomoteurs
  • T6 est utilisé pour les conversion ADC/DAC
  • les timers T1, T4, T8, T9, T10, T11, T12, T13, T14 utilisables sans soucis à priori.

Quelques uns des timers disponibles / broches :

  • sur X1 : T2C1, T5C1
  • sur X2 : T2C2, T5C2
  • sur X3 : T2C3, T5C3, T9C1
  • sur X4 : T2C4, T5C4, T9C2

Le plus simple est de se baser sur le brochage de la pyboard.

Instructions Micropython utiles

Le principe général de la PWM avec micropython est la configuration d'un canal à partir du timer source et de la broche utilisée.

Définition de la broche utilisée

Pin(id,[mode], [pull=Pin.PULL_NONE])

La broche GPIO est instanciée sous forme d'un objet Pin.

La broche, au moment de sa déclaration peut être dénomée au choix par :

  • son nom cpu : pyb.cpu.Name
  • son nom board : pyb.Pin.board.X1
  • par une chaîne texte : 'X1'

Il est également possible de créer une dénomination personnalisée des broches. Voir la doc.

Pour une utilisation en PWM, on ne précise aucun mode particulier pour la broche.

Définition du timer utilisé

Timer(id, [freq], ... )

  • id : le numéro du timer utilisé de 1 à 13
  • freq : la fréquence en Hz souhaitée

Définition du canal

Timer.channel(channel, mode, pin=Pin)

  • channel : numéro du canal utilisé
  • mode : le mode utilisé pour le timer : ici, mode PWM : Timer.PWM
  • pin : la broche utilisée. IMPORTANT : Le canal / Timer utilisé doit être disponible sur la broche !!

Les timers sont des objets très polyvalents et puissants : nous les verrons en détail séparément.

L'objet channel obtenu dispose lui-même de la fonction suivante :

ch.pulse_width_percent(value)

  • value : % de niveau HAUT

Utilisation

La définition type se fera sous la forme :

p = pyb.Pin('X1') # X1 has TIM2, CH1
tim = pyb.Timer(2, freq=1000)
ch = tim.channel(1, pyb.Timer.PWM, pin=p)
ch.pulse_width_percent(50)

Exemple : Générer un PWM symétrique (50%) sur la broche X1

Le circuit à réaliser est le suivant :

Le code est le suivant :

import pyb

p = pyb.Pin('X1') # X1 has TIM2, CH1
tim = pyb.Timer(2, freq=1000)
ch = tim.channel(1, pyb.Timer.PWM, pin=p)
ch.pulse_width_percent(50)

La LED s'allume avec une luminosité intermédiaire. L'oscilloscope mis sur la broche confirme un signal symétrique :

Exemple : Variation de l'intensité d'une LED de 0 à 100%

On reprend le même montage que précédemment : une LED sur X1 via une résistance de 270 Ohms :

Le code est le suivant :

import pyb

# définition du timer utilisé pour le PWM
p = pyb.Pin('X1') # X1 has TIM2, CH1
tim = pyb.Timer(2, freq=1000)
ch = tim.channel(1, pyb.Timer.PWM, pin=p)

while True :
    for i in range(100): # pwm croissant
        ch.pulse_width_percent(i)
        pyb.delay(50)

    for i in range(100,0,-1): # pwm décroissante
        ch.pulse_width_percent(i)
        pyb.delay(50)

On obtient bien une largeur d'impulsion variable qui se traduit par variation de la luminosité de la LED et qui est confirmée par l'oscilloscope.

PWM et servomoteurs :

import pyb

timer = pyb.Timer(5, freq=50)
#timer=pyb.Timer(2, prescaler=0, period=20) 
# period est le nombre de pulse utilisés
# period [0-0xffff] for timers 1, 3, 4, and 6-15. [0-0x3fffffff] for timers 2 & 5
ch1 = timer.channel(1, pyb.Timer.PWM, pin=pyb.Pin.board.X1, pulse_width=210000) # T2C1 sur X1

# à 50 hz = 20ms de période = impulsion servomoteurs : le pulse 100% est à 1 680 000 sur timer 2 ou 5, 50% à 840000
# 20ms = 20000µs du coup, le pulse 500µs(0°) est à 42000, le pulse de 2500(180°) est à 210000