La PWM : Qu’est-ce que c’est ? (3)

Changer la fréquence de la PWM

. Par : Jean-Luc. URL : https://www.locoduino.org/spip.php?article202

Dans l’article précédent, « La PWM : Qu’est-ce que c’est ? (2) », nous avons vu comment piloter un moteur et nous avions noté que la PWM par défaut proposée par le logiciel Arduino était à une fréquence relativement basse, environ 490 Hz. Une telle fréquence présente deux inconvénients. Tout d’abord, le moteur émet un son correspondant à sa fréquence. Or, entre 20Hz et 20000Hz, nous somme dans les fréquences audibles. Ensuite, comme exposé dans une série d’article sur l’alimentation par PWM, une fréquence trop basse engendre des pertes par effet joule et fait chauffer le moteur. Nous allons donc voir comment modifier la fréquence de la PWM de l’Arduino.

Les PWM sur les Arduino à base d’AVR 328 (Uno, Nano, Pro Mini, ...) sont au nombre de 6 et sont disponibles sur les broches 3, 5, 6, 9, 10, et 11. À l’intérieur du micro-contrôleur, les PWM sont associées, par paire, à un des 3 timers dont dispose le micro-contrôleur. L’association est résumée dans la table suivante :

TimerSorties du module Uno
timer0 5 et 6
timer1 9 et 10
timer2 3 et 11

Toutes le informations relatives à ces timers peuvent être trouvées dans la série d’articles de Christian en commençant par le premier « Les Timers (I) ».

À quelles fréquences sont les PWM

Les timers comptent au rythme de l’horloge du module Arduino. Cette horloge de base est la plupart du temps à 16MHz [1]. À cela vient s’ajouter la possibilité de diviser l’horloge pour ralentir le timer, voire de la couper purement et simplement. Comme expliqué dans « Les Timers (I) », les bits de contrôle du diviseurs sont situés dans les 3 bits de poids faible du registre TCCRxB ou x est le numéro du timer. Ces bits sont également appelés CSx2 (bit 2), CSx1 (bit 1) et CSx0 (bit 0), CS signifie Clock Select, sélection d’horloge en français. Les combinaisons suivantes de ces bits permettent de régler le diviseur :

Configuration de l’horloge des timers 0 et 1
CSx2CSx1CSx0Diviseur / fonction spéciale
0 0 0 Pas d’horloge
0 0 1 division par 1 (pas de division donc)
0 1 0 division par 8
0 1 1 division par 64
1 0 0 division par 256
1 0 1 division par 1024
1 1 0 source d’horloge sur la broche Tx [2], front descendant
1 1 1 source d’horloge sur la broche Tx, front montant
Configuration de l’horloge du timer 2
CS22CS21CS20Diviseur / fonction spéciale
0 0 0 Pas d’horloge
0 0 1 division par 1 (pas de division donc)
0 1 0 division par 8
0 1 1 division par 32
1 0 0 division par 64
1 0 1 division par 128
1 1 0 division par 256
1 1 1 division par 1024

Le logiciel Arduino configure ces timers de la manière suivante :

  • Le timer 0 est configuré avec une division par 64 et la PWM est en Fast PWM Mode ;
  • Les timers 1 et 2 sont configurés avec une division par 64 et la PWM est en Phase Correct PWM Mode.

Ces deux modes sont décrits dans « Les Timers (IV) ».

Tous les timers comptent donc à un rythme de 16MHz/64 = 250kHz, 250000 ticks par seconde. En Fast PWM Mode, un cycle de la PWM correspond à 256 ticks. Les PWM du timer 0 sont donc par défaut à 250kHz/256 = 976,5625Hz. En Phase Correct PWM Mode le timer fait un aller-retour : de 0 à 255 puis de 255 à 0, soit 510 ticks. Le cycle de la PWM est donc égal à 250kHz/510 et les PWM des timers 1 et 2 sont donc à 490,2Hz.

Peut-on changer la fréquence sans risque

Bien évidemment les timers sont utilisés pour de nombreuses fonctions dans le logiciel Arduino. Changer la fréquence des PWM n’est donc pas sans risque pour le bon fonctionnement du logiciel.

Le timer 0

Le timer 0 est employé pour fournir le temps au logiciel Arduino. Quand vous employez les fonctions delay(...), delayMicroseconds(...) ou bien encore millis() et micros(), c’est en fait le comptage de temps engendré par l’interruption du timer 0 qui est employé. Cette interruption va survenir à une fréquence de 976,5625Hz ce qui correspond à une période de 1,024 ms. Elle incrémente un compteur en l’ajustant de manière à compter des milli-secondes.

On comprend donc que changer la fréquence de comptage du timer 0 va également changer le comptage du temps, ce qui risquent de faire dérailler de nombreuses fonctions et de nombreuses bibliothèques. Il vaut donc mieux utiliser les deux autres timers si on veut changer la fréquence de la PWM.

Le timer 1

Ce timer est utilisé par la bibliothèque Servo. Si vous n’utilisez pas cette bibliothèque dans votre application, le timer 1 est libre pour vos PWM « customisées ».

Le timer 2

Enfin, le timer 2 est utilisé par la bibliothèque Tone, destinée à produire des sons. Ici aussi si cette bibliothèque ne vous est pas utile, usez librement du timer 2.

Comment changer la fréquence en pratique

Pour modifier la fréquence des PWM liées à ces timers, il suffit d’écrire les valeurs adéquates dans les 3 bits de poids faible des registres TCCR1B pour le timer 1 et TCCR2B pour le timer 2.

Si on reprend la table ci-dessus en substituant la fréquence de la PWM à la division, on a ceci :

Fréquences arrondies de PWM possibles pour les PWM liées au timer 1
CS12CS11CS10Fréquence en Hz
0 0 1 31373
0 1 0 3922
0 1 1 490
1 0 0 123
1 0 1 31
Fréquences arrondies de PWM possibles pour les PWM liées au timer 2
CS22CS21CS20Fréquence en Hz
0 0 1 31373
0 1 0 3922
0 1 1 980
1 0 0 490
1 0 1 245
1 1 0 123
1 1 1 31

Pour les moteurs des engins en N et pour les moteurs à rotor sans fer, on préférera la fréquence de 31373Hz. Pour les autres la fréquence de 3922 est utilisable. Voir à ce propos la série d’article « Tension hachée et pertes par effet Joule »

Une fonction de configuration

Pour configurer la fréquence de la PWM, je vous propose une fonction de configuration : setPWMFrequency. Cette fonction prend 2 arguments. Le premier sert à désigner la paire de broches concernée, la seconde la fréquence. Comme tout n’est pas permis, nous allons d’abord déclarer quelques constantes. Tout d’abord 2 constantes permettant de désigner la paire de broche :

const byte PINS_9_10 = 0;
const byte PINS_3_11 = 1;

Ensuite, 5 constantes, une pour chaque fréquence permise. Nous allons arrondir les noms à des valeurs arrondies aux dizaines des fréquences des PWM : 32k, 4k, 1k, 500, 250, 125 et 30, afin de nous en souvenir plus facilement même si en réalité les valeurs seront un peu différentes, nous ne sommes pas à quelques % près :

const byte PWM_32k = 0;
const byte PWM_4k  = 1;
const byte PWM_1k  = 2;
const byte PWM_500 = 3;
const byte PWM_250 = 4;
const byte PWM_125 = 5;
const byte PWM_30  = 6;

Ces fréquences sont toutes possibles pour les broches 3 et 11 mais pas pour les broches 9 et 10 car le timer 1 ne permet pas la division par 32 (1k) et par 128 (250).

Comme le codage est différent entre le timer 1 et le timer 2, nous allons utiliser deux tables de constantes indexé par les constantes définies ci-dessus. La première table va donner les codes de CS pour le timer 1 et la seconde pour le timer 2. Les fréquences manquantes pour le timer 1 seront remplacées par le code 0, qui coupe l’horloge.

const byte codeForTimer1[] PROGMEM = {
  B00000001,
  B00000010,
  B00000000, /* no clock if config not allowed */
  B00000011, 
  B00000000, /* no clock if config not allowed */
  B00000100,
  B00000101,
};

const byte codeForTimer2[] PROGMEM = {
  B00000001,
  B00000010,
  B00000011,
  B00000100, 
  B00000101,
  B00000110,
  B00000111,
};

Enfin la fonction qui met en place ces codes dans le registre TCCR1B ou TCCR2B selon la paire de broche. Nous allons faire particulièrement attention aux arguments passées à la fonction et vérifier qu’il s’agit de valeurs autorisées. La fonction échouera silencieusement si les arguments sont hors de la gamme permise. Concernant le code de la fréquence, il suffira de tester que l’argument est inférieur à 7.

void setPWMFrequency(byte pins, byte frequency)
{
  if (frequency < 7) {
    if (pins == PINS_9_10) {
      byte csCode = pgm_read_byte_near(codeForTimer1 + frequency);
      TCCR1B &= B11111000; /* aucune horloge */
      TCCR1B |= csCode;
    }
    else if (pins == PINS_3_11) {
      byte csCode = pgm_read_byte_near(codeForTimer2 + frequency);
      TCCR2B &= B11111000; /* aucune horloge */
      TCCR2B |= csCode;
    }
  }
}

L’utilisation de la fonction est simple, dans setup(), appelez là avec les arguments adéquats. Par exemple, si vous souhaitez configurer la fréquence des broches de PWM 9 et 10 à 31373Hz, il suffit d’écrire :

setPWMFrequency(PINS_9_10, PWM_32k);

Voici le tout encapsulé dans une bibliothèque qu’il vous suffira d’installer sur votre machine.

Bibliothèque PWMFreq

Bon pilotage de moteurs.

[1Mais sur certains modèles et sur certains clones chinois, elle peut être plus basse, 8MHz ou 12MHz.

[2Sur l’Arduino, il s’agit de la broche 4 pour le timer 0, la 5 pour le timer 1