TP 3 : Timers, Modulation de Largeur d'Impulsion (PWM)
Chapitres utiles du Datasheet ATmega324P
-
- Configuration des broches – section 1.1, page 15
-
- Timer/Compteur 8 bits 0 avec PWM – section 16.9, page 140
-
- Timer/Compteur 16 bits 1 avec PWM – section 17.14, page 173
-
- Timer/Compteur 8 bits 2 avec PWM et fonctionnement asynchrone – section 19.11, page 202
1. Modulation de Largeur d'Impulsion (PWM)
La PWM (Pulse Width Modulation) est une technique permettant de contrôler la tension appliquée à un dispositif électronique en le commutant rapidement entre les états ON et OFF.
Ce basculement rapide génère une tension moyenne déterminée par le temps pendant lequel le signal reste à l'état ON par rapport au temps total du cycle — ce rapport s'appelle le cycle de travail (duty cycle).
La PWM permet un contrôle numérique de signaux analogiques, par exemple :
- Variation de l'intensité d'une LED
- Changement de couleur des LEDs RGB
- Réglage de la fréquence d’un buzzer
- Contrôle de la vitesse d’un moteur
1.1. Principe de fonctionnement
Le cycle de travail s’exprime en pourcentage du temps ON par rapport au cycle total :
La tension moyenne reçue par un dispositif est :
Les signaux PWM sont généralement générés par des circuits numériques et des microcontrôleurs. L’ATmega324 utilise un compteur qui se réinitialise périodiquement et compare sa valeur avec une référence (OCRn
). Lorsque le compteur dépasse la valeur de référence, la sortie PWM change d’état.
🛠️ PWM par logiciel ?
La PWM peut également être implémentée en logiciel avec des méthodes comme :
- Bit-banging
- Timers avec routines d’interruption (ISR)
Mais pour une PWM à haute fréquence (par exemple, dans les kHz pour les moteurs), la PWM matérielle est bien plus efficace, car elle évite la surcharge du CPU causée par des interruptions fréquentes.
2. PWM sur les Microcontrôleurs AVR
Dans le laboratoire précédent, nous avons vu que l’ATmega324 possède trois timers :
Timer0
(8 bits)Timer1
(16 bits)Timer2
(8 bits)
Chaque timer peut être configuré via les registres de contrôle TCCRnA
et TCCRnB
, en utilisant les bits WGMnx
pour sélectionner le mode :
- Normal
- CTC (Clear Timer on Compare Match)
- Fast PWM (utilisé aujourd’hui !)
- Phase Correct PWM, etc.
Chaque timer dispose de deux canaux de comparaison de sortie : OCnA
et OCnB
. Ceux-ci sont liés à des broches physiques spécifiques. Consultez le chapitre Pin Configurations de la fiche technique pour les correspondances exactes :
Aujourd’hui, nous nous concentrons sur le mode Fast PWM, un mode courant adapté à des applications générales comme la variation de luminosité de LED ou le contrôle de vitesse d’un moteur.
Fast PWM
En mode Fast PWM, le timer compte uniquement sur le front montant du signal d’horloge. Les changements de cycle de travail (duty cycle) prennent effet immédiatement, mais le signal n’est pas centré — un décalage ou un artefact peut apparaître lors de changements brusques du rapport cyclique.
Plusieurs variantes du mode Fast PWM sont disponibles selon les timers. Pour Timer1, on trouve :
- Fast PWM 8 bits : TOP = 0x00FF
- Fast PWM 9 bits : TOP = 0x01FF
- Fast PWM 10 bits : TOP = 0x03FF
- Fast PWM avec TOP dans
ICR
- Fast PWM avec TOP dans
OCRnA
Dans ce TP, nous utiliserons uniquement le mode Fast PWM 8 bits. Reportez-vous à la fiche technique pour découvrir les autres modes spécifiques aux timers.
Supposons que le Timer1 soit configuré en Fast PWM. Ce mode possède une fréquence fixe et permet de modifier le cycle de travail pendant l’exécution.
- Avec le mode
10
pourCOM1A1:COM1A0
, le signal sur la brocheOC1A
reste à l’état HAUT pendant la montée jusqu’au seuil, puis passe à l’état BAS jusqu’à la fin du cycle. - Pour obtenir un cycle de travail de
x%
, on définit :
OCR1A = x * TOP / 100
oùTOP
vaut 255 (8 bits), 511 (9 bits), ou 1023 (10 bits), selon la configuration.
Exemple : Timer1 en Fast PWM 8 bits, mode non-inversé avec prédiviseur de 1024
Fréquence PWM ≈ 12 MHz / 1024 / 255 ≈ 45 Hz
/* OC1A est PD5, doit être configurée en sortie */
DDRB |= (1 << PD5);
/* Sélectionner le mode Fast PWM 8 bits : WGM[3:0] = 0b0101 */
/* WGM10 et WGM11 -> TCCR1A ; WGM12 et WGM13 -> TCCR1B */
TCCR1A = (1 << WGM10);
TCCR1B = (1 << WGM12);
/* Mode non-inversé sur OC1A : COM1A[1:0] = 0b10 */
TCCR1A |= (1 << COM1A1);
/* Définir le prédiviseur à 1024 : CS1[2:0] = 0b101 */
TCCR1B |= (1 << CS12) | (1 << CS10);
/* Définir un cycle de travail à 50% (TOP = 255, donc OCR1A = 127) */
OCR1A = 127;
Phase Correct PWM
Bien que nous n'utilisions pas ce mode dans le TP, il est utile de comprendre comment Phase Correct PWM diffère du Fast PWM — il offre une meilleure précision, souvent requise pour les moteurs BLDC ou l’audio.
Différence clé : le compteur compte de BOTTOM jusqu’à TOP, puis redescend jusqu’à BOTTOM.
- La sortie est définie par comparaison avec
OCRnx
, comme en Fast PWM. - Cependant,
OCRnx
reste constant pendant tout le cycle, ce qui donne une durée ON plus stable et un signal plus propre. - Cela évite les variations de fréquence (jitter) — crucial pour les moteurs, qui peuvent perdre en efficacité avec une PWM irrégulière.
Comparaison : Fast PWM vs Phase Correct PWM
3. Exercices
L’objectif de ces exercices est de contrôler la couleur d’une LED RGB à l’aide de la PWM. En ajustant indépendamment la luminosité de chaque diode (Rouge, Vert, Bleu), vous pouvez générer n’importe quelle couleur !
Les broches de la LED RGB sont connectées comme suit :
- Rouge : broche
PD5
, fonctionOC1A
(liée au Timer1) - Vert : broche
PD7
, fonctionOC2A
(liée au Timer2) - Bleu : broche
PB3
, fonctionOC0A
(liée au Timer0)
La LED RGB est câblée avec une anode commune / configuration "active-bas" — la LED s’allume lorsque la broche est à l’état BAS (LOW), et s’éteint lorsqu’elle est à l’état HAUT (HIGH).
Tâche 0
- Téléchargez et exécutez le projet de départ (ZIP).
- Que remarquez-vous sur la carte ?
- Consultez le moniteur série — vous souvenez-vous comment la temporisation était gérée dans le TP précédent ?
- Comment se comporte la LED ? Quel timer est utilisé et dans quel mode ?
- Où l’intensité est-elle mise à jour ? Quelle est l’entrée de la formule, et où est appliquée la sortie ?
Tâche 1
- Faisons en sorte que la LED bleue se comporte de la même manière.
- Utilisez le Timer0 (
OC0A
surPB3
) en mode Fast PWM. - Consultez la Section 12.9 du Datasheet ATmega324P pour une bonne utilisation des registres — les registres du Timer0 sont différents de ceux du Timer1 !
- Faites pulser la LED bleue plus rapidement que la rouge, et en parallèle.
- Que remarquez-vous ?
- Utilisez le Timer0 (
Tâche 2
- C’est au tour de la LED verte !
- Problème :
PD7
(OC2A) est utilisé par le Timer2, déjà utilisé pour les ticks système demillis()
... - Solution : nous allons générer la PWM manuellement en utilisant les interruptions :
- Le Timer2 compte de 0 à 188 et déclenche une interruption via
COMPA
(pour incrémentersysticks
). - Le deuxième comparateur
COMPB
du Timer2 déclenche une interruption à une valeur entre 0–188. - Dans l’ISR de
COMPA
: allumer la LED verte → mettre le GPIO à LOW. - Dans l’ISR de
COMPB
: éteindre la LED verte → mettre le GPIO à HIGH. - Le cycle de travail est défini par
OCR2B
.
- Le Timer2 compte de 0 à 188 et déclenche une interruption via
- Problème :
Gardez OCR2A = 188
pour conserver le bon fonctionnement de millis()
.
Tâche 3
- Parcourez toutes les couleurs à l’aide de l’espace colorimétrique HSV :
- Convertissez HSV → RGB en utilisant la fonction
convert_HSV_to_RGB()
fournie. - Faites varier la Teinte (Hue) dans le temps, gardez Saturation = 1, Valeur = 1 (luminosité maximale).
- Utilisez les ticks système pour animer la couleur de manière fluide, comme pour la pulsation des LEDs.
- Assurez-vous de désactiver tout code en conflit qui modifierait l’intensité des LEDs.
- Convertissez HSV → RGB en utilisant la fonction
Tâche 4 (Bonus)
- Utilisez le haut-parleur connecté à
PD4
(égalementOC1B
) pour jouer une mélodie depuis le modulesound.c
.surprise_notes[]
contient les fréquences.durations[]
contient les durées des notes.- Dans
update_notes()
, reconfigurez le Timer1 à chaque nouvelle note :- Utilisez le mode CTC (
OCR1A
définit la valeur TOP → contrôle la fréquence). - Réglez
OCR1B = OCR1A / 2
pour un cycle de travail de 50% (onde carrée). - Appelez
update_notes()
toutes les 25 ms viasysticks
. - Remettez
TCNT1 = 0
après chaque modification deOCR1A
pour éviter les décalages temporels.
- Utilisez le mode CTC (
Bonus 2
Combinez :
- L’animation RGB (cycle HSV) et (and)
- La lecture de la mélodie (via le haut-parleur)
— les deux fonctionnant simultanément et de manière fluide.