vendredi 16 août 2019

Modulation par largeur d'impulsion (PWM) sur ESP32

Que ce soit pour contrôler l'intensité lumineuse d'une LED, la vitesse de rotation d'un moteur ou la position angulaire d'un servomoteur, il est souvent utile de créer un signal modulé en largeur d'impulsion (MLI, ou PWM pour Pulse Width Modulation).

Un signal PWM alterne entre 0 V et 3,3 V. Son rapport cyclique est le pourcentage du temps total pendant lequel il se trouve à 3,3 V.

Voici donc un petit tuto sur la création et le contrôle d'un signal PWM avec l'EPS32.

Avant d'aller plus loin, rappelons que j'utilise l'IDE Arduino pour programmer l'ESP32.

"analogWrite" ne fonctionne pas

Si vous avez l'habitude de produire des signaux PWM avec une carte Arduino, vous supposerez probablement que l'ESP32 se débrouillera avec une commande du genre "analogWrite(16,100)".

Hé bien non! Toute tentative d'utiliser la fonction analogWrite avec l'ESP32 cause une erreur de compilation: "'analogWrite' was not declared in this scope".


C'est peut-être une bonne chose, remarquez: puisque l'ESP32 comporte un DAC (convertisseur numérique/analogique), il est possible de générer un véritable signal de sortie analogique grâce à la fonction DACWrite, alors que le signal PWM généré par une carte Arduino avec la fonction analogWrite n'est pas, à proprement parler, un véritable signal analogique.

21 broches peuvent être utilisées pour du PWM

En ce qui concerne les broches GPIO, nous avons l'embarras du choix: n'importe quelle broche capable d'être configurée comme sortie peut être utilisée pour produire un signal PWM. Nous excluons les broches GPIO 34 à 39 (qui ne peuvent être configurées qu'en entrée), ce qui laisse tout de même, sur la carte que j'utilise, 21 broches pouvant produire un signal PWM.
16 canaux PWM

Doit-on en déduire qu'il est possible de générer 21 signaux PWM indépendants? Pas tout à fait: l'ESP32 comporte 16 canaux PWM.

Ces canaux sont numérotés de 0 à 15. Comme nous le verrons plus loin, lorsqu'on désire produire un signal PWM sur une broche, on doit associer cette broche à un des 16 canaux au moyen de la fonction ledcAttachPin().

3 fonctions essentielles

Pour générer et contrôler notre signal PWM, notre sketch dispose de 3 fonctions LEDc (pour "LED control" puisqu'elles sont prévues avant tout pour contrôler l'intensité lumineuse d'une LED): ledcAttachPin, ledcSetup et ledcWrite.

  • ledcAttachPin(broche GPIO, canal)
Cette fonction sert à associer une broche GPIO à l'un des 16 canaux GPIO, qui sont numérotés de 0 à 15. Par exemple, si je désire produire un signal PWM sur la broche GPIO 18,  "ledcAttachPin(18, 0)" fera en sorte que le signal généré par le canal 0 sera acheminé à la broche 18. Si j'ai besoin d'un deuxième signal PWM différent du premier, j'utilise pour ce deuxième signal un numéro de broche et un numéro de canal différents. En général, cette fonction est placée dans la partie setup() du programme, puisqu'il est rarement utile de modifier cette assignation en cours d'exécution.

  • ledcSetup(canal, fréquence, résolution)
Cette fonction permet de régler la fréquence et la résolution d'un canal PWM. Le canal continue d'être un entier situé entre 0 et 15. La fréquence est en hertz; elle est typiquement de l'ordre du kilohertz lorsqu'on contrôle la luminosité d'une LED. La résolution, qui peut prendre n'importe quelle valeur entière située entre 1 et 13, détermine le nombre valeurs distinctes pouvant être prises par le rapport cyclique.

Par exemple: avec une résolution de 12 bits, le rapport cyclique pourra prendre 212 = 4096 valeurs différentes, alors qu'avec une résolution de 8 bits, le nombre de valeurs possibles ne sera que de 28= 256.

Par exemple, l'expression "ledcSetup(0, 5000, 12)" règle le canal PWM numéro 0 à une fréquence de 5000 Hz, avec une résolution de 12 bits. Si vous n'avez pas besoin de modifier la fréquence pendant l'exécution du programme, vous placerez probablement cette fonction dans la partie setup().

Quel est l'avantage d'utiliser une résolution plus faible que le maximum permis? Atteindre une fréquence plus élevée! La fréquence maximale permise pour un signal PWM généré par l'ESP32 est de 40 megaHertz! Mais à cette fréquence, la seule résolution possible est de 1 bit (permettant un rapport cyclique de 50% et rien d'autre). Plus la fréquence est grande, plus la résolution maximale possible est petite.

  • ledcWrite(canal, rapport cyclique)
Cette fonction permet de régler le rapport cyclique du signal PWM du canal spécifié.  Par exemple,  "ledcWrite(0, 250)" règle à 250 le rapport cyclique du canal 0.

Attention: le résultat de cette commande dépend de la résolution préalablement réglée avec la fonction ledcSetup: si la résolution est de 12 bits, 250/212 correspond à un rapport cyclique d'environ 6%; si la résolution est de 10 bits, 250/210 correspond plutôt à un rapport cyclique de 24%, et le rapport cyclique sera de 98%  (250/28 ) à une résolution de 8 bits.

Un sketch minimaliste

Le sketch ci-dessous constitue le strict minimum pour générer un signal PWM (qui, dans ce cas, demeure identique pendant toute l'exécution du programme).

Dans ce sketch, j'ai d'abord lié la broche GPIO 18 au canal PWM 0, puis j'ai réglé le canal 0 à une fréquence de 5000 Hz et une résolution de 12 bits. Finalement, j'ai réglé le rapport cyclique à 50% (2048 est la moitié de 212 ).


Observations

J'ai ensuite branché un oscilloscope à l'ESP32 afin d'observer le signal PWM généré.

Tout d'abord, le résultat de mon sketch sans modification:  fréquence de 5000 Hz, résolution de 12 bits,  rapport cyclique à 50%,


On obtient un signal dont le rapport cyclique est de 50%: la moitié du temps à 0 V, et la moitié du temps à 3,3 V:

La fonction statistique de mon oscilloscope confirme la fréquence de 5 kHz et le rapport cyclique de 50%.



Deuxième essai: cette fois, je modifie le rapport cyclique pour qu'il devienne 25%:


Résultat: la tension est de 3,3 V pendant un quart de cycle, et nulle ensuite.




J'ai ensuite doublé la fréquence (pour qu'elle soit de 10 kHz), et réglé le rapport cyclique à 75%.






Finalement, j'ai réglé la résolution à 8 bits. Pour un rapport cyclique de 50%, il faut maintenant utiliser 128.





Un potentiomètre pour régler le rapport cyclique

Terminons avec un sketch classique qui permet de modifier le rapport cyclique d'un signal PWM en tournant un potentiomètre. Ce sketch peut servir, par exemple, à varier la luminosité d'une LED.


Le circuit est constitué d'un potentiomètre relié à la broche GPIO 15, et d'une LED (accompagnée d'une résistance de protection) branchée à la broche GPIO 18.


Rien de bien compliqué ici: nous réglons le rapport cyclique de la broche GPIO 18 à la valeur lue par la broche GPIO 15. En tournant le potentiomètre, l'intensité lumineuse de la LED change. Puisque la résolution du canal PWM et celle de l'ADC sont toutes les deux de 12 bits, aucune conversion n'est nécessaire; les deux résultats prennent des valeurs situées entre 0 et 4095.



Yves Pelletier   (TwitterFacebook)

9 commentaires:

  1. Merci, les mots de suffisent pas!!!

    RépondreSupprimer
  2. Bravo.et Merci.
    En tout cas Arduino a compilé sans problème.
    Demain, je ferai un essai de variateur de vitesse sur un moteur 230.Je souhaite réussir;je ferai un commentaire.

    RépondreSupprimer
  3. Plus que parfait! Continue!

    RépondreSupprimer
  4. Remarquablement bien fait, bravo

    RépondreSupprimer
  5. Bonjour Mr Pelletier,
    Je cherche depuis un bout de temps quelque chose de concret pour utiliser du PWM sur un M5 STAMP-PICO-D4 afin de contrôler un moteur de modelé réduit.
    Merci pour ce Tutoriel
    Henri Gueraud Pinet

    RépondreSupprimer
  6. bonjour ,
    la tension maxi délivré par un esp_32 est de 3.3v .
    j'utilise un convertisseur de niveau GT1167 pour avoir 5v .
    que penser vous de ce montage .

    d'avance merci .

    RépondreSupprimer