ATMega328 (Arduino uno) TimerCounter et PWM Ă  62,5kHz

Timer/Counter et PWM pour des fréquences élevés.

Cette article traite de l’utilisation de certaines fonctionnalitĂ©s de l’ATMega328P qui peuvent s’avĂ©rer utiles pour le traitement des signaux audio (par exemple, rĂ©aliser une pĂ©dale d’effet), jouer des samples de musique (Ă©chantillonnĂ©s Ă  44100Hz ou moins), ou encore rĂ©aliser de la synthĂšse sonore.

Il est question d’aborder ici le stricte minimum pour utiliser le PWM (Pulse Width Modulation) Ă  une frĂ©quence suffisamment Ă©levĂ© affin de pouvoir gĂ©nĂ©rer un signale analogique et de contrĂŽler son amplitude. En bonus, nous pourrons exĂ©cuter une interruption toute les 16uS.

Je recommande fortement au gens aillant un peut d’expĂ©rience de lire directement la documentation relative aux Timers/Counters, PWM, et aux registres DDRx/PORTx.

Timer et PWM

L’ATMega328P comprend 3 Timers, c’est Ă  dire trois compteurs qui sont incrĂ©mentĂ© par le microcontroleur tout les N ticks d’horloges (K peut valoir 1, 8, 32, 64, 256, 1024 celons vos envies). Ces timers se noment “Timer0”, “Timer1” et “Timer2”.  Le Timer0 est utilisĂ© par la bibliothĂšque arduino pour Ă©valuer l’écoulement du temps (delay, microsecond) et pour les sorties PWM associĂ©s Ă  ce timer. Le Timer1 dispose d’un compteur 16bits, c’est Ă  dire de 0 Ă  0xFFFF, alors que les timers 2 et 1 sont en 8bits (de 0 Ă  0xFF). L’idĂ©e est que nous voulons compter vite, trĂšs vite, donc nous avons intĂ©ret Ă  utiliser un compteur 8bits. Notre choix se port donc tout naturellement sur le timer2.

Le PWM correspond Ă  la mise sous tension d’une patte de l’ATMega durant un certain laps de temps, puis de sa mise Ă  0 durant un autre instant, et le tout rĂ©pĂ©tĂ© trĂšs rapidement. Cela permet de simuler un signale analogique de tension comprise entre 5v et 0v (Moins si vous n’utilisez pas l’alimentation 5V). Le principe est trĂšs bien dĂ©crit par le schĂ©ma suivant provenant du site d’arduino(http://arduino.cc/en/Tutorial/PWM) :

PWM on Arduino

Il faut savoir que chaque Timer controle deux sorties PWM. Les pattes correspondant Ă  ces sorties sont indiquĂ© sur la page 2 de la documentation atmel (OCnA et OCnB, oĂč n est le numĂ©ro du timer). Dans notre cas nous utiliseront la sortie OC2A, qui correspond au pin 11 d’une carte arduino.

Le fonctionnement du PWM est extrĂȘmement simple ; Vous rĂ©glez le PWM avec une valeur comprise entre 0 et 255, disons C. Alors, tant que le compteur est plus petit que C, il y auras une tension de 5V sur la sortie OC2A. Quand le compteur dĂ©passe C, la tension passe Ă  0V. Pour accĂ©lĂ©rer une sortie PWM, il faut donc accĂ©lĂ©rer le timer qui est associĂ©.

Le mode FastPWM (ATMega328P datasheet p152-153)

Il existe plusieurs configurations possible pour la sortie PWM, la fréquence des timers, etc. Je vous conseille trÚs fortement de lire la documentation atmel à ce sujet. Dans notre cas, nous cherchons la plus rapide, et il y en a une qui correspond tout à fait à nos besoins ; La configuration Fast PWM.

Le compteur que nous avons Ă©voquĂ©, associĂ© au Timer2, se nome “TCNT2”(Timer Counter 2), et peut ĂȘtre lut et Ă©crit Ă  tout moment. La valeur Ă  partir de la quel le signal bascule de 5V Ă  0V est stockĂ© dans OCR2A(Output Compare Register 2 A). Un second registre de comparaison est disponible, OCR2B, et correspond au pin OC2B.

Dans ce mode, le comTCNT2 part de 0, puis atteint progressivement OCR2A. Alors, la tension de sortie de OC2A s’inverse. À cette instant, une interruption peut ĂȘtre dĂ©clenchĂ©e. C’est Ă  dire, si vous n’ĂȘtes pas familier avec ce mĂ©canisme, qu’une fonction que vous aurez prĂ©alablement Ă©crite, compilĂ©, et associĂ© Ă  ce mĂ©canisme, seras exĂ©cutĂ©. De mĂȘme pour OCR2B/OC2B.

Le compteur continus sa montĂ©e, et atteint alors 0xFF. Il y a alors overflow : le compteur repasse Ă  0x00 Ă  la prochaine incrĂ©mentation, et enclenche l’interruption TIMER2_OVF (Timer 2 Overflow), si elle est activĂ©e.

Sachez enfin que vous pouvez configurer si la tension de sortie avant que le compteur atteigne OCR2A doit ĂȘtre haute ou basse. Nous choisirons qu’elle est haute, puis basse. De cette façon, si OCR2A = 0, la tension sera 0 sur une pĂ©riode (16us, pour mĂ©moire), et si OCR2A = 0xFF, elle sera de 5V (toujours sur une pĂ©riode).

Le schĂ©ma suivant, issu de la documentation, dĂ©crit une succession d’interventions sur le registre OCR2A, l’évolution du compteur, le dĂ©clenchement des interruptions, et les tensions de sorties(haut pour 5V et bas pour 0V) celons la configuration des deux bits COM2A1 et COM2A0 (Pour les valeur 2( = 1 0 en binaire) et 3 = (1 1 en binaire))

FastPWM Atmel ATMega328p

La frĂ©quence Ă  la quel l’interruption TOV2(TIMER2OVF) est appelĂ© se calcul aisĂ©ment Ă  partir de celle de l’horloge de l’ATMega. Si vous utilisez un quartz externe Ă  16MHz (c’est le cas des cartes arduino) la frĂ©quence est alors $\frac{f{clock}}{N . 256}$ oĂč $N$ est le facteur d’échelle (prescale factor), dont nous parlerons un peu plus loin.

L’activation du mode FastPWM (p160 de la documentation) se fait en fixant les bits du “Timer Counter Control Register”  A et B (TCCR2A et TCCR2B).

Le premier contient le type de sortie COM2A1:0 et COM2B1:0, ainsi que les bits WGM21 WMGM20 contrĂŽlant le mode de l’horloge.  Le second (p163)  contient le dernier bit de configuration du mode, WGM22 et les trois bits permettant de sĂ©lectionner le facteur d’échelle (dans notre cas, nous choisirons 1, pour que la frĂ©quence soit la plus Ă©levĂ©) ; CS22, CS21, CS20.

Ainsi, nous pouvons déjà configurer le mode Fast PWM :

TTCCR2A = 1 << COM2A1 | 1 << WGM01 | 1 << WGM00;
TTCCR2B = 0 << WGM02 | 0 << CS22 | 0 << CS21 | 1 << CS20;

Nb : Sachez que pour chacun des modes, vous trouverez un tel schéma dans la documentation :)

Facteur d’échelle

Les bits CS22, CS21 et CS20 permettent de configurer le facteur d’échelle. Le concept est simple (p155 et p156 de la documentation pour des schĂ©ma extrĂȘmement claires) ; le compteur TCNT2 seras incrĂ©mentĂ© de 1 tout les N ticks d’horloge, oĂč la valeur de n dĂ©pend du facteur d’échelle choisit, d’aprĂšs le tableau suivant (p164 de la documentation) :

Clock Select Bit Description

Activer la sortie PWM

Nous avons donc configurer le compteur, et la sortie PWM peut maintenant ĂȘtre utilisĂ©e. Mais encore faut-il l’activer. Pour cela, il faut sĂ©lectionner la “direction” du pin associĂ© au comparateur A (OC2A) comme “OUTPUT”. Cela se fait via le Data Direction Register B (car la sortie OC2A est la troisiĂšme patte du port B, c’est Ă  dire la sortie 11 de la carte arduino).

DDRB |= 1 << DDB3;

Pour désactiver la sortie PWM lorsque le pin est toujours en mode output, il suffit de modifier la valeur de COM2A.

Utiliser les interruptions

Le dernier outil, pour pouvoir par exemple effectuer une acquisition audio à  $31,25kHz$, est interruption OVF, dĂ©clenchĂ©e dĂšs que le compteur passe de 0xFF Ă  0x00.  Pour ce faire, il faut informer le compilateur que votre fonction doit ĂȘtre associĂ© Ă  une interruption, et activer l’interruption grĂące au masque d’interruption TIMSK2.

// Configuration
TIMSK2 = 1 << TOIE2;

// ...

ISR(TIMER2_OVF_vect)
{
  // Code de l'interruption
}

La bibliothĂšque C AVR

Si nous pouvons utiliser toute ces macros, c’est bien quelles sont dĂ©finit quelques part. Il s’agit d’une bibliothĂšque C permettant de dĂ©velopper sur les microcontroleurs atmel en C. Cette bibliothĂšque contient une documentation dont je recommande chaudement la lecture, notamment pour ceux qui souhaiterais diminuer le temps perdu en entĂȘte C dans l’interruption OVF (environs 20 Ă  ticks d’horloge Ă  16MHz, ce qui reprĂ©sente tout de mĂȘme 20 Ă  30 256iĂšme du temps d’exĂ©cution de votre microcontroleur!)

Sachez aussi qu’une façon plus agrĂ©able d’écrire (1 « ABCD) est _BV(ABCD).

Références: