PWM logicielle sur Atmel AVR Attiny2313
Travaillant sur un projet de présentoire lumineux, j'ai eu besoin de disposer de plus que les quelques canaux de PWM que n'en proposent généralement les microcontrôleurs. En particulier les petits AVR comme l'Attiny2313. La seule solution restante était donc de se passer de ces fonctionnalités et d'implémenter la PWM en logiciel de manière à pouvoir piloter un minimum de 6 leds via les port E/S standards.
Le principe de fonctionnement est simple. En réglant l'un des timers de manière à obtenir le maximum de tops d'horloge possible, la plus haute fréquence donc, on peut implémenter, sous la forme d'un compteur et d'une poignée de comparaisons une PWM acceptable. Le début du code est tout ce qu'il y a de plus classique :
.device attiny2313
.def T1 = r1
.def T2 = r2
.def temp = r16
.def truc = r17
.def cpt = r18
.def savestatus = r20
.def zlow = r21
.def zhigh = r22
.def pwm1 = r19
.def pwm2 = r23
.def pwm3 = r24
.def pwm4 = r25
.def pwm5 = r26
.def pwm6 = r27
.org 0x0000
rjmp RESET
.org 0x000D
rjmp TIM0A_CMP
.org 0x0013
Vient ensuite la routine d'interruption correspondant à la comparaison de timer. Ici on trouve cpt, le compteur. Il est incrémenté à chaque interruption puis sa valeur est comparée aux variables réglant le PWM pour chaque sortie. Tant que le compteur est en dessus de la valeur d'une variable la sortie correspondante est active. Si le compteur est au delà, la sortie est à la masse. Notez qu'il faut penser à sauvegarder le registre d'état car les comparaisons successives risquent de perturber les drapeaux et donc de nuire au bon fonctionnement du reste du code :
TIM0A_CMP:
in savestatus,SREG
inc cpt
cp cpt,pwm1
brlo lowrouge
cbi PORTB,PB4
rjmp vert
lowrouge:
sbi PORTB,PB4
vert:
cp cpt,pwm2
brlo lowvert
cbi PORTB,PB3
rjmp bleu
lowvert:
sbi PORTB,PB3
bleu:
cp cpt,pwm3
brlo lowbleu
cbi PORTB,PB2
rjmp uv
lowbleu:
sbi PORTB,PB2
uv:
cp cpt,pwm4
brlo lowuv
cbi PORTB,PB1
rjmp blanc
lowuv:
sbi PORTB,PB1
blanc:
cp cpt,pwm5
brlo lowblanc
cbi PORTB,PB0
rjmp jaune
lowblanc:
sbi PORTB,PB0
jaune:
cp cpt,pwm6
brlo lowjaune
cbi PORTB,PB5
rjmp fin
lowjaune:
sbi PORTB,PB5
sbi PORTB,PB5
fin:
out SREG,savestatus
reti
On définit également deux petites routines de retardement :
mini:
clr T1
hop:
dec T1
brne hop
dec temp
brne hop
ret
micro:
dec temp
brne micro
ret
Enfin, on arrive au reste où il faut, dans l'ordre, définir la pile, utiliser l'octet de calibration propre à chaque AVR, placer les ports en sortie, définir l'effacement du timer en comparaison, le prescaling, le niveau de référence pour la comparaison et le déclenchement d'une interruption en comparaison :
RESET:
ldi temp, 0x70
out SPL, temp
ldi temp,0x52
out OSCCAL,temp
ldi temp,0xf0
rcall mini
sbi DDRB,PB0
sbi DDRB,PB1
sbi DDRB,PB2
sbi DDRB,PB3
sbi DDRB,PB4
sbi DDRB,PB5
ldi temp,(1<<WGM01)
out TCCR0A,temp
ldi temp,(1<<CS00)
out TCCR0B,temp
ldi temp,0x02
out OCR0A,temp
ldi temp,(1<<OCIE0A)
out TIMSK,temp
sei
Arrive ensuite la boucle principale où l'on pioche dans la mémoire flash les valeurs des variables pwm*. Cette boucle sera interrompue par les interruptions du timer. Cela manque de précision mais est largement suffisant pour faire pulser des leds proprement :
loop: ldi ZH, high(tbpwm1*2)
ldi ZL, low(tbpwm1*2)
add ZL, zlow
adc ZH, zhigh
lpm
mov pwm1,r0
ldi ZH, high(tbpwm2*2)
ldi ZL, low(tbpwm2*2)
add ZL, zlow
adc ZH, zhigh
lpm
mov pwm2,r0
ldi ZH, high(tbpwm3*2)
ldi ZL, low(tbpwm3*2)
add ZL, zlow
adc ZH, zhigh
lpm
mov pwm3,r0
ldi ZH, high(tbpwm4*2)
ldi ZL, low(tbpwm4*2)
add ZL, zlow
adc ZH, zhigh
lpm
mov pwm4,r0
ldi ZH, high(tbpwm5*2)
ldi ZL, low(tbpwm5*2)
add ZL, zlow
adc ZH, zhigh
lpm
mov pwm5,r0
ldi ZH, high(tbpwm6*2)
ldi ZL, low(tbpwm6*2)
add ZL, zlow
adc ZH, zhigh
lpm
mov pwm6,r0
inc zlow
inc zlow
ldi temp,0x50
rcall micro
rcall mini
rjmp LOOP
Enfin, pour terminer, nous avons les valeurs stockées. Ici 6 sinus identiques légèrement décalés les uns par rapport aux autres :
tbpwm1: .db 128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174 .db 177,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216 .db 218,220,222,224,226,228,230,232,234,235,237,239,240,241,243,244 .db 245,246,248,249,250,250,251,252,253,253,254,254,254,254,254,254 .db 254,254,254,254,254,254,254,253,253,252,251,250,250,249,248,246 .db 245,244,243,241,240,239,237,235,234,232,230,228,226,224,222,220 .db 218,216,213,211,209,206,204,201,199,196,193,191,188,185,182,179 .db 177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131 .db 128,125,122,119,116,112,109,106,103,100,97,94,91,88,85,82 .db 79,77,74,71,68,65,63,60,57,55,52,50,47,45,43,40 .db 38,36,34,32,30,28,26,24,22,21,19,17,16,15,13,12 .db 11,10,8,7,6,6,5,4,4,4,4,4,4,4,4,4 .db 4,4,4,4,4,4,4,4,4,4,5,6,6,7,8,10 .db 11,12,13,15,16,17,19,21,22,24,26,28,30,32,34,36 .db 38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77 .db 79,82,85,88,91,94,97,100,103,106,109,112,116,119,122,125 tbpwm2: .db 11,12,13,15,16,17,19,21,22,24,26,28,30,32,34,36 .db 38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77 .db 79,82,85,88,91,94,97,100,103,106,109,112,116,119,122,125 .db 128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174 .db 177,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216 .db 218,220,222,224,226,228,230,232,234,235,237,239,240,241,243,244 .db 245,246,248,249,250,250,251,252,253,253,254,254,254,254,254,254 .db 254,254,254,254,254,254,254,253,253,252,251,250,250,249,248,246 .db 245,244,243,241,240,239,237,235,234,232,230,228,226,224,222,220 .db 218,216,213,211,209,206,204,201,199,196,193,191,188,185,182,179 .db 177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131 .db 128,125,122,119,116,112,109,106,103,100,97,94,91,88,85,82 .db 79,77,74,71,68,65,63,60,57,55,52,50,47,45,43,40 .db 38,36,34,32,30,28,26,24,22,21,19,17,16,15,13,12 .db 11,10,8,7,6,6,5,4,4,4,4,4,4,4,4,4 .db 4,4,4,4,4,4,4,4,4,4,5,6,6,7,8,10 tbpwm3: .db 38,36,34,32,30,28,26,24,22,21,19,17,16,15,13,12 .db 11,10,8,7,6,6,5,4,4,4,4,4,4,4,4,4 .db 4,4,4,4,4,4,4,4,4,4,5,6,6,7,8,10 .db 11,12,13,15,16,17,19,21,22,24,26,28,30,32,34,36 .db 38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77 .db 79,82,85,88,91,94,97,100,103,106,109,112,116,119,122,125 .db 128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174 .db 177,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216 .db 218,220,222,224,226,228,230,232,234,235,237,239,240,241,243,244 .db 245,246,248,249,250,250,251,252,253,253,254,254,254,254,254,254 .db 254,254,254,254,254,254,254,253,253,252,251,250,250,249,248,246 .db 245,244,243,241,240,239,237,235,234,232,230,228,226,224,222,220 .db 218,216,213,211,209,206,204,201,199,196,193,191,188,185,182,179 .db 177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131 .db 128,125,122,119,116,112,109,106,103,100,97,94,91,88,85,82 .db 79,77,74,71,68,65,63,60,57,55,52,50,47,45,43,40 tbpwm4: .db 177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131 .db 128,125,122,119,116,112,109,106,103,100,97,94,91,88,85,82 .db 79,77,74,71,68,65,63,60,57,55,52,50,47,45,43,40 .db 38,36,34,32,30,28,26,24,22,21,19,17,16,15,13,12 .db 11,10,8,7,6,6,5,4,4,4,4,4,4,4,4,4 .db 4,4,4,4,4,4,4,4,4,4,5,6,6,7,8,10 .db 11,12,13,15,16,17,19,21,22,24,26,28,30,32,34,36 .db 38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77 .db 79,82,85,88,91,94,97,100,103,106,109,112,116,119,122,125 .db 128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174 .db 177,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216 .db 218,220,222,224,226,228,230,232,234,235,237,239,240,241,243,244 .db 245,246,248,249,250,250,251,252,253,253,254,254,254,254,254,254 .db 254,254,254,254,254,254,254,253,253,252,251,250,250,249,248,246 .db 245,244,243,241,240,239,237,235,234,232,230,228,226,224,222,220 .db 218,216,213,211,209,206,204,201,199,196,193,191,188,185,182,179 tbpwm5: .db 245,244,243,241,240,239,237,235,234,232,230,228,226,224,222,220 .db 218,216,213,211,209,206,204,201,199,196,193,191,188,185,182,179 .db 177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131 .db 128,125,122,119,116,112,109,106,103,100,97,94,91,88,85,82 .db 79,77,74,71,68,65,63,60,57,55,52,50,47,45,43,40 .db 38,36,34,32,30,28,26,24,22,21,19,17,16,15,13,12 .db 11,10,8,7,6,6,5,4,4,4,4,4,4,4,4,4 .db 4,4,4,4,4,4,4,4,4,4,5,6,6,7,8,10 .db 11,12,13,15,16,17,19,21,22,24,26,28,30,32,34,36 .db 38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77 .db 79,82,85,88,91,94,97,100,103,106,109,112,116,119,122,125 .db 128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174 .db 177,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216 .db 218,220,222,224,226,228,230,232,234,235,237,239,240,241,243,244 .db 245,246,248,249,250,250,251,252,253,253,254,254,254,254,254,254 .db 254,254,254,254,254,254,254,253,253,252,251,250,250,249,248,246 tbpwm6: .db 218,220,222,224,226,228,230,232,234,235,237,239,240,241,243,244 .db 245,246,248,249,250,250,251,252,253,253,254,254,254,254,254,254 .db 254,254,254,254,254,254,254,253,253,252,251,250,250,249,248,246 .db 245,244,243,241,240,239,237,235,234,232,230,228,226,224,222,220 .db 218,216,213,211,209,206,204,201,199,196,193,191,188,185,182,179 .db 177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131 .db 128,125,122,119,116,112,109,106,103,100,97,94,91,88,85,82 .db 79,77,74,71,68,65,63,60,57,55,52,50,47,45,43,40 .db 38,36,34,32,30,28,26,24,22,21,19,17,16,15,13,12 .db 11,10,8,7,6,6,5,4,4,4,4,4,4,4,4,4 .db 4,4,4,4,4,4,4,4,4,4,5,6,6,7,8,10 .db 11,12,13,15,16,17,19,21,22,24,26,28,30,32,34,36 .db 38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77 .db 79,82,85,88,91,94,97,100,103,106,109,112,116,119,122,125 .db 128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174 .db 177,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216
Bien entendu, ce code n'est qu'une première ébauche. Il est possible de condenser le code de l'interruption et de la boucle principale pour économiser du temps et de la mémoire. De plus il faut prendre en considération que ce code occupe déjà quelques 85% de la mémoire flash d'un Attiny2313. En voulant utiliser plus de motifs ou plus de sorties, on se heurte à un problème d'espace. Il me faudra donc trouver un autre support de stockage, comme une carte MMC, pour pousser plus loin.