22 janvier 2025

Timers Atmega328p

Un Timer dans un microcontrôleur est un élément essentiel, il permet de faire fonctionner multiples choses, comme des délais, générer des signaux, faire de la comparaison.
Un Timer est aussi appelé « Counter » car celui-ci compte !

Pour expliquer le fonctionnement d’un Timer, il est mieux d’employer une image, voici l’image provenant de la datasheet d’un Atmega328p (même microcontrôleur qu’un Arduino Uno ou Nano).

Nous voyons ici plusieurs choses, nous sommes dans le mode CTC (Clear Timer on Compare Match) de ce Timer, ce qui signifie que nous pourrions faire changer la vitesse de fonctionnement de celui-ci par une comparaison, nous revenons dessus plus tard.

Notons tout de suite TCNT (Timer Counter), un registre qui contient la valeur de notre compteur, un Timer va incrémenter celle-ci, dans l’image ci-dessus, le Timer va partir de 0 et monter jusqu’à une certaine valeur. Dans un Timer en mode normal, celui-ci montera jusqu’à la valeur maximale jusqu’à retourner à 0. La valeur maximale est imposée par le nombre de bits du Timer utilisé.

Au-dessus, nous sommes en mode CTC, le TCNT repassera à 0 donc quand il y aura comparaison avec la valeur d’un registre de comparaison (OCR, Output Compare Register).

Nous avons aussi OCn (Output Compare), qui est une sortie de notre Timer qui fonctionne avec un jeu de comparaison, en particulier avec le mode CTC ou PWM.

Prescaler

Avant d’aller plus loin, il est important de savoir comment fonctionne le prescaler.

Un Prescaler est un bloc fonctionnel placé en amont d’une horloge pour diviser celle-ci par un certain nombre. Utilisés ici pour les Timers, nous pouvons retrouver des prescalers partout dans un microcontrôleur pour modifier la fréquence. Modifier la fréquence peut pour certains microcontrôleurs être nécessaire pour des raisons techniques.

Nous avons « clk_tn » (Timer/Counter clock), il s’agit de la vitesse d’horloge de notre Timer, celle-ci peut fonctionner de deux façons, soit à l’aide d’un détecteur de front « Edge Detector » soit depuis le Prescaler.

Le Prescaler ici utilisera la fréquence de notre processeur ou plus précisément la fréquence I/O.

Sur ce tableau, nous voyons les différentes horloges utilisables pour notre Timer, nous pouvons utiliser une horloge externe (les deux derniers), ou utiliser l’horloge I/O de notre microcontrôleur.

Pour plus de précision, voici à quoi correspond l’horloge I/O :

Les fonctions du microcontrôleur (ADC/CPU Core/Ram/…) utilisent des horloges différentes, elles portent des noms différents car dans certaines situations, ces dernières peuvent ne pas être égale à l’horloge source du µC (Microcontrôleur) représentée par les flèches entrantes dans l’AVR Clock Control Unit.

Nous avons parfois besoin de Prescaler en amont des fonctions pour limiter la fréquence pour des raisons techniques, ici nous n’en avons pas besoin, donc toutes nos horloges en sortie de l’AVR Clock Control Unit, sont égales.

Si nous avons une horloge de 16MHz pour faire fonctionner notre Atmega328p, alors la fréquence CLK_IO sera identique.

Les différents Prescalers permettent de faire varier l’évolution de notre Timer/Counter, par exemple, divisons par 64 notre fréquence CLK_IO.
Nous étions à 16MHz, le Timer/Counter s’incrémentera (ou inversement) alors à une vitesse de 250kHz.

Dans le mode « normal » du Timer, il n’y a pas de comparaison, ce qui signifie qu’avec notre Prescaler de 64, nous augmentons TCNT de 1 à une fréquence de 250kHz.

16bits, 10 bits, 8 bits

Chaque Timer peut fonctionner sur une certaine quantité de données. En particulier ce qui va nous intéresser, cela va être notre TCTN, car en fonction sur combien de bits sera notre Timer, cela indiquera la valeur maximale que pourra avoir notre TCTN.

Dans la table des matières de notre Atmega 328p, nous voyons directement ces informations.

Le Timer0 fonctionne sur 8bits et Timer1 sur 16bits.
Cela signifie que dans l’un les registres seront sur 8bits et dans l’autre sur 16 bits.

Dans la réalité, il s’agit de deux registres de 8bits pour un 16bits avec un « haut » et un « bas ».

(Nous avons ici un registre (appelons-le ainsi plutôt que Variable) TCNT qui monte jusqu’à 16 bits, mais qui est enfaite deux registres de 8bits).

La différence entre 8 et 16bits est que pour le premier le compteur pourra compter jusqu’à 255 (256 possibilités – 1) et l’autre 65 536.

Mode CTC (Clear Timer on Compare Match)

Reprenons la première image :

Le mode CTC va fonctionner en comparant la valeur du registre TCNT avec un registre de comparaison. Dans le Timer0 voici ce que nous avons :

Nous avons un registre OCR (Output Compare Register) qui sera comparé avec le registre TCNT, les deux sont sur 8bits.

Lorsque OCR = TCNT, alors le compteur repasse à 0 (TCNT =0). Dans le schéma au-dessus le mode « toggle » est activé (tableau ci-dessous).

De cette façon, à chaque comparaison, la sortie OC0A sera à l’état haut, puis bas. Nous pouvons ainsi générer un signal à une fréquence bien précise grâce au choix de nos valeurs de Prescaler et OCR.

La datasheet nous fournit même la formule qui permet de calculer la fréquence en fonction de nos paramètres

Un formulaire est disponible ici pour calculer la valeur d’OCR rapidement : https://geoffrey-lecoq.fr/?cff-form=6

Mode PWM (Pulse Width Modulation, ou MLI)

Le mode PWM est très utile pour contrôler de nombreuses choses, telles que des moteurs, transistors, etc.

Pour ne pas nous perdre, nous allons ici utiliser le mode Fast PWM, qui fonctionnera toujours avec une comparaison, mais celle-ci ne remettra pas à 0 le compteur.

Ci-dessus, nous avons le fonctionnement du mode FastPWM de notre Timer0, celui-ci utilise une seule comparaison, en fonction de comment nous avons configuré notre FastPWM (OCR et les sorties). La fréquence PWM sera directement liée à celle du Prescaler pour le Timer0.

Pour mieux comprendre, voici l’un des fonctionnements possibles :
– Nous mettons à l’état bas à chaque overflow de notre TCNT (quand la valeur de TCNT a atteint sa valeur maximale)
– Nous mettons à l’état haut quand TCNT = OCR
De cette façon, notre rapport cyclique sera proportionnel à la valeur d’OCR.
Nous pouvons voir ce que j’ai décrit sur le graphique plus haut pour l’inverse d’OCnx, en particulier sur la 3ème période.

Sachant tout cela, nous pouvons alors générer 2 signaux PWM à fréquence égale, mais à rapport cyclique différent !

Le Timer0 possède deux registres de comparaisons, OCR0A et OCR0B qui peuvent chacun activer la sortie OC0A et OC0B.

Ici nous avons COM0A1 et COM0A0, qui permet de configurer la sortie OC0A.

Mais nous avons aussi COM0B1 et COMB0 qui permettent de configurer la sortie 0C0B. De ce fait, nous avons deux sorties et aussi deux signaux PWM différents !

FastPWM avec Timer1

Le mode FastPWM du Timer1 fonctionne différemment

Nous avons vu qu’avec le mode CTC, nous pouvions définir la fréquence de sortie OC. Nous allons utiliser ce même principe, mais avec une comparaison supplémentaire.

Une réflexion sur « Timers Atmega328p »

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *