19 juin 2007

PWM logicielle sur Atmel AVR Attiny2313

Tags : , ,

posté dans Electronique |

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.

Article posté on Mardi, 19 juin 2007 à 1:00 dans Electronique. Vous pouvez suivre les commentaires sur cet article via un feed RSS 2.0. Vous pouvez laisser un commentaire. Le Ping n'est actuellement pas autorisé.

Il y a actuellement 4 réponses pour “PWM logicielle sur Atmel AVR Attiny2313”

  1. 1 Le 3 septembre 2007, leucos à écrit :

    Bon sang, "DenisBodor" c’est une association avec plusieurs personnes non ? Comment un humain peut trouver le temps de faire tout ça à la fois ? La moitié des articles de GLMF, 90% des hors séries, et ça trouve le temps de jouer en assembleur sur les AtTiny, de gutser une fonera, de faire du POV, etc…. Impossible. C’est un bot.

  2. 2 Le 4 septembre 2007, Lefinnois à écrit :

    “La moitié des articles de GLMF, 90% des hors séries” c’est très exagéré :)

    Mais merci de ce commentaire.

  3. 3 Le 3 janvier 2008, Nicolas à écrit :

    Oui d’après certaines thèses, “DenisBodor” serait constitué d’un collectif d’une centaine d’érudits ( à qui l’on devrait également L’Illiade et l’Odyssée ).

    La communication entre les membres de ce collectif n’est, du reste, pas toujours parfaite, ce qui explique la récurrence effroyable d’articles sur les Tours de Hanoi…. ;-)
    Argh, non, pitié, pas de hors série spécial Tours de Hanoi !!!!

    Nicolas

  4. 4 Le 3 janvier 2008, Lefinnois à écrit :

    Le collectif DenisBodor… toute résistance est futile… ;)
    Non sérieusement, c’est juste que j’adore faire ce que je fais et que j’accepte de payer le tribut d’une telle passion.

Laisser un commentaire

*
Pour prouver que vous n'êtes pas un bot, recopiez le code ci-dessous
Anti-Spam Image