Driver de LED RGB à 8 canaux.

Version imprimableVersion imprimableSend by emailSend by email


Driver de LED RGB à 8 canaux (8-CHANNEL RGB Led Driver)

common cathod

Led RGB

Luminous intensity vs Forward current

Led spectrum vs Forward current
Introduction
Cette article présente une carte qui permet de contrôler 8 canaux de leds RGB. Chaque canal peut piloter jusqu'à 5 led RGB.
Les 8 canaux sont commandés de manière à réaliser un effet arc-en-ciel, se déplaçant d'un canal à l'autre.

Ce projet est en cours de développement. Son état d'avancement est bien avancé. La version du programme actuelle fonctionne correctement. Il existe néanmoins un petit "bug" au niveau des leds qui n'a pas encore été résolu. Ce bug n'est cependant pas très génant.

Pour rappel, les LEDs RGB contiennent 3 LEDs au sein d'un même package.
On retrouve 1 led verte, 1 led rouge et 1 led bleue.

Principe de fonctionnement
Pour faire varier la couleur d'une LED RGB, il faut jouer sur l'intensité lumineuse de chaque qui la compose. Pour faire du rouge, on règle l'intensité lumineune de la LED rouge à 100 % (de son intensité lumineuse nominale).

L'illustration à droite "Led RGB" montre les 3 couleurs fondamentales activée successivement.

Pour obtenir les autres couleurs, nous allons jouer sur les intensités. Par exemple, pour obtenir de l'orange, on règle le rouge à 100 % et le vert à 50%.


Variation de l'intensité

Pour faire varier cette intensité lumineuse, il y a plusieurs méthodes. On peut par exemple, moduler le courant entre 0 et le courant nominal à l'aide de par exemple, d'un transistor, en fonctionnement linéaire.

Cette méthode présente plusieurs inconvénients:

Difficile à mettre en oeuvre avec un microcontrolleur qui ne dispose que de sortie Tout ou rien.
Solution encombrante (trop de composants)
Rendement faible
La courbe "Intensité lumineuse en fonction du courant dans la led" n'est pas linéaire (cfr illustration à droite).

Une solution beaucoup plus simple est d'employer une modulation à largeur d'impulsion (PWM = pulse width modulation).
Le courant peut prendre 2 valeurs: le courant nominal ou 0 mA. En fonction du rapport cyclique, l'intensité lumineuse. Etant donné du nombre de canaux à géré (8 canaux x 3 led = 24 rapports cyclique à générer), la fréquence du signal ne sera pas trop élevée, de l'ordre de quelques dizaines de Hertz.

Le temps de réponse de l'oeil fait que l'on ne perçoit pas les variations instantannée de la lumière (on ne voit pas les led clignotée). L'oeil aperçoit l'intensité lumineuse moyenne. Ce phénomène est connu sous le nom de persistance rétinienne.

Il faut également savoir que le spectre lumineux émis par chaque led varie légèrement en fonction du rapport cyclique. Le pic de spectre se déplace souvent légèrement. Mais normalement, il se déplace dans une moindre mesure avec la commande PWM qu'avec une commande linéaire. A droite est mis à titre d'exemple le spectre d'une LED qui doit être celui d'une LED UV.

 

 

Schema

Feuille 1/2 - la logique de commande et circuit de puissance

 

 

Schema (cliquer pour agrandir)

 

Il n'y a pas grand chose à dire sur le schéma. Il est assez simple.

La pièce centrale est un microcontrolleur ATmega 88 qui convient très bien pour cette fonction. Il est cadencé par un quartz à 20 MHz, ce qui donne une bonne vitesse d'éxecution. Il faut savoir que le processeur ne fait rien 70 % du temps. On pourrait penser qu'un quartz à 5 MHz pourrait suffire. En réalité, il faut maintenir cette vitesse, car après chaque période d'échantillonage les programmes se mettent à jour et le processeur peut passer à 100 % d'occupation pendant quelques ms. Avec un processeur cadencé plus lentement, on risque de voir des discontinuité dans le programme.

La programmation se fait par un connecteur ISP 10 pin standard (IDC10). J'utilise pour le programmer le programmateur USBprog. Si votre PC dispose encore d'un port série, vous pouvez vous construire un programmateur très simple avec très peu de composants (voir ici).

Le port D est multiplexé par 3 latchs de type 74HCT573. Chaque latch commande les 8 canaux d'une couleur. Par exemple, IC4 commande les 8 leds vertes. Chaque latch est activé en mettant à 1 sa ligne C(ontrol?) ou aussi nommé LE (Latch Enable).
Vous pouvez également utilisez des latchs de type 74HCT574. La différence est qu'ils sont triggés au front montant la ligne CP (Clock), équivalent de la ligne LE du 573.
Il faut apporter quelques modifications minimes au programmes.

La puissance des sorties des latchs est évidemment insuffisante pour commander directement plusieurs LED (la somme des courants en sorties des buffers peut être au maximum de 70 mA). Des transistors PNP (BC516) montés en émetteur commun font office de "circuit de puissance". Ici, c'est la solution la moins cher qui a été préfèré pour la puissance. Ma première idée était d'utilisé un IC qui contient 8 paires de darlingtons PNP (du type UDN2981) mais pour une raisons que je ne connais pas ceux-ci sont difficile à trouver, et coûtent environ 5 € la pièce, contrairement à son homologue NPN ULN2803. De plus, le routage est plus simple pour un circuit imprimé simple face.

A titre d'exemple, une connexion d'une led RGB est montré (encadré en gris). Les valeurs des résistances pour la led RGB sont celles que j'ai utilisé pour les leds dont je dispose. Je vous conseille de les redimensionner en fonction de la tension Vforward@Inom de vos leds et en tenant compte de Vec_sat des transistors PNP.

Le circuit dispose d'une horloge temps réel (RTC) disposant d'une interface I2C. Celle-ci peut vous être utile si vous souhaitez programmer des heures et/ou des dates de fonctionnement pour le montage. Cette fonction n'est pas encore implémenté dans le programme.

Le circuit dispose d'une connexion (via un bornier) pour une LDR. De nouveau, si vous installez les leds dehors, vous pouvez choisir d'activer (ou de désactiver) le driver s'il fait jour ou nuit. Cette fonction n'est pas implanté en H.W.. Pour l'implanté, il suffit de faire une simple conversion analogique numérique, et selon la tension, activer ou désactiver le driver.


Feuille 2/2 - Alimentation

 


Schema Power Supply(cliquer pour agrandir)

La consommation du montage n'est pas négligeable.

Voici un rapide calcul de la consommation dans le pire des cas.

Dans chaque led RGB, il y a 3 leds. J'utilise pour ce montage 4 leds par canal. Il y a 8 canaux. Chaque led consomme environ 20 mA, ce qui nous donne une consommation de:

3.20 mA. 4 . 8 = 1920 mA sous 5 V

Bien sûr, il s'agit du plus mauvais cas, avec le programme qui est dans le µc cela ne se produit jamais (le courant est au max environ 66 % de celui calculé).

L'utilisation d'une alimentation linéaire n'est pas concevable en raison de la forte consommation et du faible rendement de cette dernière. J'ai donc opté pour une alimentation à découpage. Ces circuits sont presque aussi facile à implanter qu'un classique 7805. Le régulateur utilisé est un LT1076 (qui peut sortir jusqu'à 2 A).

En théorie on peut donc mettre un total de 6 leds/canal (soit un total de 50 leds) pour une alimentation de 2 A. Si vous désirez utilisé plus de led, vous pouvez utiliser un LT1074 (qui peut sortir jusqu'à 5 A). Il faut alors redimensionner certains composants (la self: Rdc et Isat), la capacité d'entrée et de sortie (Capacité, Zmax@Fdécoupage,...) ainsi que la diode shottky. Linear Technology a publié sur son site un application note (AN) pour le dimensionnement des composants, que j'ai suivi pour dimensionner les composants. Il faudra probablement modifier le circuit imprimé pour ces nouveaux composants. Le circuit à découpage garantit un rendement compris entre 75 et 80 %. La carte alimenté peut être alimentée en DC ou en AC grâce au pont de graetz. La tension minimale d'entrée est d'environ 10..12 Vdc ou environ 10 Vac. La tension maximale d'entrée est limitée par la tension max que supporte la capacité d'entrée (ici 24 Vdc) et par le régulateur (40 Vdc max). Il va de soi que c'est pas plus petite des 2 valeurs qui détermine Vin_max.


Photos de la carte (proto)
 


Améliorations à apporter


Horloge temps réel

Une RTC est présente sur la carte mais cette fonction n'est pas encore implanté dans le H.W.Il y a plusieurs problèmes à résoudre qui sont: le réglage de l'heure et le réglage des heures de fonctionnement.
La solution qui me parait la plus simple est d'utiliser la liaison SPI. Il n'est pas possible d'utiliser l'usart car celui-ci est occuppé par le bus.

On pourrait également envisager d'activer l'usart au démarrage pendant quelques secondes. Le uC envoit alors une demande de connexion. Si un PC est connecté celui-ci détecte la demande et envoit un acquittement. Le µc reste dans ce mode jusqu'au prochain reset. Si le µc ne reçoit pas d'acquitement (fonctionnement normal, sans pc connecté), il retourne dans le mode normal (driver de led).


Code source

Le programme est écris en assembleur et occuppe moins de 600 bytes (sur les 8K disponible). L'assembleur s'impose, car le programme doit être très optimisés pour que les routines soit suffisamment rapide (et donc avoir un foncionnement "continu" du programme, sans discontinuité. L'assembleur a permit d'avoir un code très compacte et rapide à l'éxecution.

Toute suggestion d'amélioration est la bienvenue ...


.include "m88def.inc"

.def prescalar_counter = r14
.def counter = r25 ; compteur pour le PWM mode
.def temp = r16
.def argument = R18
.def red_latch = r19
.def green_latch = r20
.def blue_latch = r21

 


.equ PBUS = PORTD ; port ou est connecte le bus
.equ PLATCH = PORTB ; port ou on connecte les Latch Enable
.equ BRedLatch = 0
.equ BGreenLatch = 1
.equ BBlueLatch = 2


.eseg
.db High(0x1200),low(0x1200)

 

 

.dseg
duty_cycle_Rled:
.byte 8
duty_cycle_Gled:
.byte 8
duty_cycle_Bled:
.byte 8
degrade_counter:
.byte 8
degrade_status:
.byte 8

 


FLAG:
.byte 1

.cseg

.org 0x000

rjmp reset


.org 0x010
rjmp TIM0_OVF ; Timer0 Overflow Handler

.org 0x012 ; Receive complete
;rjmp USART_RXD

.org 0x021

 


reset:

ldi r16,0xFF ; configure portB as output
out DDRD,r16

; (0010 | 0100) & 0111 // |(1<<RS485_TXEN)
ldi r16,((1<<BRedLatch)|(1<<BGreenLatch)|(1<<BBlueLatch)) ; configure portD as input
out DDRB,r16

 

// counter0 initialisation (8 bit)

ldi r16,0x00 ; Normal mode, No Output
out TCCR0A,r16
ldi r16,(0x02<<CS00) ; clock source = clock / 8 (prescalar)
out TCCR0B,r16
ldi r16,(1<<TOIE0) ; Overflow Interrupt Enabled
sts TIMSK0,r16

ldi r16,0x00
STS TWSR,r16 ;STATUS register, prescaler 1
ldi r16,32
STS TWBR,r16 ; Bit rate register = 100 kHz
ldi r16,(1<<TWEN)|(1<<TWINT)
STS TWCR,r16 ; control register, enable IIC


cbi PLatch,BBluelatch
cbi PLatch,BRedlatch
cbi PLatch,BGreenlatch
ser r16
out PBUS,r16
sbi PLatch,BBluelatch
sbi PLatch,BRedlatch
sbi PLatch,BGreenlatch
 

ldi red_latch,0xFE
led_test_red:
out PBUS,red_latch
cbi PLatch,BRedlatch
sbi PLatch,BRedlatch
rcall tempo
sec
rol red_latch
brcs led_test_red
out PBUS,red_latch
cbi PLatch,BRedlatch
sbi PLatch,BRedlatch


ldi green_latch,0xFE
led_test_green:
out PBUS,green_latch
cbi PLatch,BGreenlatch
sbi PLatch,BGreenlatch
rcall tempo
sec
rol green_latch
brcs led_test_green
out PBUS,green_latch
cbi PLatch,BGreenlatch
sbi PLatch,BGreenlatch

 

ldi blue_latch,0xFE
led_test_blue:
out PBUS,blue_latch
cbi PLatch,BBLuelatch
sbi PLatch,BBluelatch

rcall tempo
sec
rol blue_latch
brcs led_test_blue
out PBUS,blue_latch
cbi PLatch,BBluelatch
sbi PLatch,BBluelatch

 

 

ldi r17,16
ldi ZH,high(2*program_0_degrade_counter)
ldi ZL,low(2*program_0_degrade_counter)
ldi YH,high(degrade_counter)
ldi YL,low(degrade_counter)

set_program_0:
lpm r16,Z+ ; contient la data de la mémoire program
st Y+,r16 ; on stock dans la sram
dec r17
brne set_program_0
 

 

SEI ; on active les interruptions

rjmp main

 

 

 

;*********************************************************************
;
;
; Procedure principale
;
;
;*********************************************************************

 


main:

; ldi ZL,low(degrade_counter)
; ldi ZH,high(degrade_counter)
; ldi r16,252
; st Z+,r16
; st Z+,r16
; ldi ZL,low(degrade_status)
; ldi ZH,high(degrade_status)
; ldi r16,6
; st Z+,r16
; st Z+,r16
 

;main1:
; rcall inc_counters
; rcall update_duty_cycle
 


; rjmp main1


brtc main_no_1_Tdecoupage ; on test si le flag T est à 1 (=1 une période de découpage)
; s'ést écoulé
clt ; si oui on le clear
inc prescalar_counter ; on
;bst prescalar_counter,0
mov argument,prescalar_counter
andi argument,1 ; on fait un & avec le prescalar

breq main_even
rcall update_duty_cycle
rjmp main
main_even:
rcall inc_counters
 

main_no_1_Tdecoupage:

rjmp main

 

 

;*********************************************************************
;
;
; inc_counters
;
; incremente les compteurs de tout les channels
; lorsqu'un counter fait overflow, on incremente le status
;
;*********************************************************************

inc_counters:
ldi Zl,low(degrade_counter) ;on charge l'adresse qui contient les 8 états
ldi Zh,high(degrade_counter) ; des compteurs canaux
ldi r17,8 ; compteur du nombre de canaux à effectuer (compteur boucle)

inc_counters_0:
ld r16,Z ;on charge la valeur du compteur d'un canaux
inc r16 ;on l'incremente
brne inc_counters_no_ovf_counters ; si pas d'overflow (=0) pour le counter on skip
adiw Z,8 ; si ovf on met le pointeur Z 8 bytes plus loin, pour pointer sur le
; byte de status du canal correspondant. (cfr organisation mémoire)
ld argument,Z ; On charge la valeur du status (RAM) dans un registre de travail
inc argument ; on incremente pour passer au status suivant
cpi argument,7 ; on regarde si on a pas dépasser le n° du dernier programme
brlo inc_counters_no_ovf_status
clr argument ; si oui on remet à zéro


inc_counters_no_ovf_status:
st Z,argument ; on remet la valeur dans la RAM
sbiw Z,8 ; on remet le pointeur sur le byte du counteur du canal

inc_counters_no_ovf_counters:
st Z+,r16 ; on passe au channel suivant (incrementation du pointeur)
dec r17 ; on décremente le compteur (boucle)
brne inc_counters_0
inc_counters_2:


ret

 

 


;*********************************************************************
;
;
; update_duty_cycle
;
; met à jour les duty cycle de toute les leds en fonction du statut et du compteur
;
;*********************************************************************

update_duty_cycle:
ldi YL,low(degrade_counter)
ldi YH,high(degrade_counter)
ldi r17,8 ; compteur

update_duty_cycle_0:
ld r16,Y
adiw Y,8 ; on va chercher la valeur du status
ld argument,Y ; on incremente de 8 avant car pointeur Y est 8 byte en arrière

; bst argument,0 ;on stocke le bit de parity dans T
; brtc update_duty_cycle_1 ; pour étapes paires = incrementation, on skip
; com r16 ; les etapes impaires = decrementation
; r16 = 0xFF - r16
update_duty_cycle_1:
; ce bloc de code, va calculer l'adresse ou l'on va sauter en fonction du status du programme
; chaque status doit être calculé d'une manière différentes.
; chaque status est précéder d'un label (adresse) nommé update_duty_cycle_status_x (avec x le n° du statut)
; le programme a été écrit de telle sorte à ce que le code pour chaque statut fasse 10 instructions
; (ceci justifie l'utilisation de nop, pour augmenter la taille si trop court).
; Il est donc facile de calculer l'adresse ou l'on doit jumper :

;@ (update_duty_cycle_status_0)+ 10 x n° statut

; clt

ldi r22,10 ; ici on calcule l'offset de l'addresse
mul argument,r22 ; en faisant 10 x statut
ldi ZH,high(update_duty_cycle_status_0) ; on pointe sur l'adresse du statut 0
ldi ZL,low(update_duty_cycle_status_0)
add ZL,R0 ; et on ajoute le résultat de la multiplication
adc ZH,R1
ijmp


update_duty_cycle_status_0:
sbiw Y,32 // red led
st Y,r16
adiw Y,8 // green led
ser argument
st Y,argument
adiw Y,8 // blue led
clr argument
st Y,argument
nop
rjmp update_duty_cycle_end_status

update_duty_cycle_status_1:
sbiw Y,32 // red led
ser argument
st Y,argument
adiw Y,8 // green led
com r16 ;les etapes impaires = decrementation r16 = 0xFF - r16
st Y,r16
adiw Y,8 // blue led
clr argument
st Y,argument
rjmp update_duty_cycle_end_status

update_duty_cycle_status_1_bis_red_continious:
sbiw Y,32 // red led
ser argument
st Y,argument
adiw Y,8 // green led
clr argument
st Y,argument
adiw Y,8 // blue led
clr argument
st Y,argument
rjmp update_duty_cycle_end_status

update_duty_cycle_status_2:
sbiw Y,32 // red led
ser argument
st Y,argument
adiw Y,8 // green led
clr argument
st Y,argument
adiw Y,8 // blue led
st Y,r16
nop
rjmp update_duty_cycle_end_status

update_duty_cycle_status_3:
sbiw Y,32 // red led
com r16 ;les etapes impaires = decrementation r16 = 0xFF - r16
st Y,r16
adiw Y,8 // green led
clr argument
st Y,argument
adiw Y,8 // blue led
ser argument
st Y,argument
rjmp update_duty_cycle_end_status

update_duty_cycle_status_4:
sbiw Y,32 // red led
clr argument
st Y,argument
adiw Y,8 // green led
st Y,r16
adiw Y,8 // blue led
ser argument
st Y,argument
nop
rjmp update_duty_cycle_end_status

update_duty_cycle_status_5:
sbiw Y,32 // red led
clr argument
st Y,argument
adiw Y,8 // green led
ser argument
st Y,argument
adiw Y,8 // blue led
com r16 ;les etapes impaires = decrementation r16 = 0xFF - r16
st Y,r16
rjmp update_duty_cycle_end_status

update_duty_cycle_end_status:
adiw Y,9
dec r17
breq update_duty_cycle_end
rjmp update_duty_cycle_0

update_duty_cycle_end:
ret

 

;*********************************************************************
;
;
; write_duty_cycle
;
; met à jour les duty cycle de toute les leds
;
; Input: r16 = counter
; argument = statut
;
;*********************************************************************

write_duty_cycle:
bst argument,0 ;on stocke le bit de parity dans T
brtc write_duty_cycle_1 ; pour étapes paires = incrementation, on skip
com r16 ; les etapes impaires = decrementation
; r16 = 0xFF - r16
write_duty_cycle_1:

 

 

 

 

write_duty_cycle_end:
clt

 

 

 


tempo:
;LDS r31,countH
;LDS r30,countL
ldi r17,37 ; 30*20/8 = 1 s
tempo_0:
CLR r31
CLR r30
tempo_1:
ADIW Z,1
BRNE tempo_1
DEC r17
BRNE tempo_0
ret

 

 


/*


;*********************************************************************
;
;
; Procedure TIM0_OVF
; called when TIMER overflow occurs
; input: les 8x duty_cycle des 3 couleurs: duty_cycle_Rled, duty_cycle_Gled
; duty_cycle_Bled
; output: T (indique que l'on vient de terminer une période de découpage)
;
;
; Cette procédure est executé à chaque interruption
; et génère le signal PWM de chaque led en fonction des duty_cycle
;
; appellé à Foverflow=Fcpu/8/(timer0=256)=9665 Hz = 102us = 2048i
; Fdécoupage = Foverflow/256 = 38.15 Hz = 26.2 ms
;
; Ninstructions:
; Pour balayer une input: 10i (x 24)
; pour éteindre une led + 7 (x24)
; pour passer d'un latch à l'autre: 2 i
; push et pop: 24
; init: 6
;
; Total: 442max - min 274
;
; Scanner chaque led : 10i x 24 = 240 i
; Push/Pop : 24 i
; init (sans OVF) : 6 i
;------------------------------
; Total : 276 i
; Total cycle (x256) : 70.656 i
;
; Pour éteindre les 24 leds (pour un cycle)
; : 7 i x 24 = 168 i
;-------------------------------
; Total cycle : 70.824 i (ancienne procédure = 70 824) (+ 38 %)

;*********************************************************************

TIM0_OVF:
push r16
push argument
push Zl
push Zh
push YL
push YH


;cbi TIFR0,TOV0 ; effacement du flag Overflow

inc counter ; registre 8bit

brne TIM0_OVF_no_counter_ovf ;s'il y a eu un overflow (counter=0),
; il faut allumer toute les leds

cbi PLatch,BBluelatch
cbi PLatch,BGreenlatch
cbi PLatch,BRedlatch
clr red_latch ; on clear tout les registres images des latchs
out PBUS,red_latch ; on met 0 sur le bus
clr green_latch ; qui commande les leds
clr blue_latch ; si clear(=0V) => led ON car Transistor ON
sbi PLatch,BBluelatch ; les latchs sont "rafraichit" au front montant
sbi PLatch,BGreenlatch ; sur l'entrée clock
sbi PLatch,BRedlatch ;

set ; on set le flag T
; pour indiquer au main que l'on a fait un cycle complet
; de découpage

TIM0_OVF_no_counter_ovf:

ldi YH,high(duty_cycle_bled+8) ; Y, pointeur sur le byte du duty cycle
ldi YL,low(duty_cycle_bled+8) ; que l'on traite en cours
;on fait plus 8 car on decremente le pointeur. => +8 = une adresse
; au dessusdernier byte. Une au-dessus car prédecremenation avant de charger (ld r16,-Y)

; latch BLEU


TIM0_OVF_boucle_B:
ld r16,-Y ; pre-dec & load dans r16 ; ;
; N.B.: il faut que la 1er adresse soit à un multiple de 8 pour que cela fonctionne

mov argument,YL ; on récupère le byte d'adresse de poids faible
andi argument,0x07 ; en faisant un &, argument varie entre 0 et 7 et on déduit
; ainsi le canal en cours de traitement.
; (nécessaire pour savoir quelle led il faudra éteindre et test boucle)

cp counter,r16 ; on compare la valeur extraite (dutycycle) et la valeur
; en cours du compteur pour le signal PWM
brne TIM0_OVF_boucle_B_1 ; si c égal, on éteint la led, sinon on skip

ldi ZH,high(2*powerof2com) ; chargement de l'adresse du tableau powerof2com dans
ldi ZL,low(2*powerof2com) ; dans le pointeur Z
add ZL,argument ; on additionne le canal en cours de traitement (0..7)
lpm r16,Z ; on charge la valeur pointée par r16
; on en extrait 2^argument (on met bit n° argument à 1)
or blue_latch,r16 ; on fait un OR, ce qui met à 1 le bit concerné sans effacé


TIM0_OVF_boucle_B_1:

tst argument ; argument = 0 ?
brne TIM0_OVF_boucle_B ; on tourne en rond jusque qd on a fait toute les bleus

cbi PLatch,BBluelatch ; on met à jour le latch avec la nouvelle valeur
out PBUS,blue_latch ; on met à jour les latchs
sbi PLatch,BBluelatch

; on passe ensuite au verte


TIM0_OVF_boucle_G:

ld r16,-Y
mov argument,YL
andi argument,0x07

cp counter,r16
brne TIM0_OVF_boucle_G_1

; si c égal, on éteint la led

ldi ZH,high(2*powerof2com)
ldi ZL,low(2*powerof2com)
add ZL,argument
lpm r16,Z

or green_latch,r16



TIM0_OVF_boucle_G_1:
tst argument
brne TIM0_OVF_boucle_G; on tourne en rond jusque qd on a fait toute les vertes
cbi PLatch,BGreenlatch
out PBUS,green_latch
sbi PLatch,BGreenlatch


; on passe au bleu

TIM0_OVF_boucle_R:

ld r16,-Y
mov argument,YL
andi argument,0x07

cp counter,r16
brne TIM0_OVF_boucle_R_1

; si c égal, on éteint la led

ldi ZH,high(2*powerof2com)
ldi ZL,low(2*powerof2com)
add ZL,argument
lpm r16,Z

or red_latch,r16



TIM0_OVF_boucle_R_1:
tst argument
brne TIM0_OVF_boucle_R ; on tourne en rond jusque qd on a fait toute les rouges

cbi PLatch,BRedlatch
out PBUS,red_latch
sbi PLatch,BRedlatch

 

TIM0_OVF_end:
pop YH
pop YL
pop Zh
pop Zl
pop argument
pop r16


reti

*/
 


;*********************************************************************
;
;
; Procedure TIM0_OVF
; called when TIMER overflow occurs
; input: les 8x duty_cycle des 3 couleurs: duty_cycle_Rled, duty_cycle_Gled
; duty_cycle_Bled
; output: T (indique que l'on vient de terminer une période de découpage)
;
;
; Cette procédure est executé à chaque interruption
; et génère le signal PWM de chaque led en fonction des duty_cycle
;
; appellé à Foverflow=Fcpu/8/(timer0=256)=9665 Hz = 102us = 2048i
; Fdécoupage = Foverflow/256 = 38.15 Hz = 26.2 ms
;
; Ninstructions:
; Pour balayer une led: 7i (x 24)
; pour éteindre une led + 24 (x24) instructions supplémentaires
; push et pop: 24
; init: sans OVF : 5
; avec OVF : 13
;
; Total: 442max - min 197
;
;
; Scanner chaque led : 7i x 24 = 168 i
; Push/Pop : 24 i
; init (sans OVF) : 5 i
;------------------------------
; Total : 197 i
; Total cycle (x256) : 50.432 i
;
; Pour éteindre les 32 leds (pour un cycle)
; : 24 i x 24 = 576 i
;-------------------------------
; Total cycle : 51.008 i (ancienne procédure = 70 824) (+ 38 %)
;*********************************************************************

 

 

TIM0_OVF:
push r16
push argument
push Zl
push Zh
push YL
push YH

 

 

TIM0_OVF_1:
inc counter ; registre 8bit
 

 

brne TIM0_OVF_no_counter_ovf ;s'il y a eu un overflow (counter=0),
; il faut allumer toute les leds

cbi PLatch,BBluelatch
cbi PLatch,BGreenlatch
cbi PLatch,BRedlatch
clr red_latch ; on clear tout les registres images des latchs
out PBUS,red_latch ; on met 0 sur le bus
clr green_latch ; qui commande les leds
clr blue_latch ; si clear(=0V) => led ON car Transistor ON
sbi PLatch,BBluelatch ; les latchs sont "rafraichient" au front montant
sbi PLatch,BGreenlatch ; sur l'entrée clock
sbi PLatch,BRedlatch ;
 

set ; on set le flag T
; pour indiquer au main que l'on a fait un cycle complet
; de découpage


TIM0_OVF_no_counter_ovf:

ldi YH,high(duty_cycle_bled+8) ; Y, pointeur sur le byte du duty cycle
ldi YL,low(duty_cycle_bled+8) ; que l'on traite en cours
;on fait plus 8 car on decremente le pointeur. => +8 = une adresse
; au dessusdernier byte. Une au-dessus car prédecremenation avant de charger (ld r16,-Y)

; latch BLEU


TIM0_OVF_loop_0:
tst YL ; on teste si on est pas à l'adresse de duty_cycle_red (YL= 0x00)
breq TIM0_OVF_end_loop ; si oui on quitte

ld r16,-Y ; pre-dec & load dans r16 ; ;
; N.B.: il faut que la 1er adresse soit à un multiple de 8 pour que cela fonctionne

cp counter,r16
brne TIM0_OVF_loop_0

TIM0_OVF_set_led:
mov r16,YL
andi r16,0x07

ldi ZH,high(2*powerof2com) ; chargement de l'adresse du tableau powerof2com dans
ldi ZL,low(2*powerof2com) ; dans le pointeur Z
add ZL,r16 ; on additionne le canal en cours de traitement (0..7)
lpm r16,Z ; on charge la valeur pointée par r16
; on en extrait 2^argument (on met le bit n° argument à 1)
mov argument,YL ; on extrait ici la couleur en cours de traitement
andi argument,0x18 ; pour déterminer le latch à activer

ldi ZH,high(TIM_OVF_set_red_latch) ; on pointe sur l'adresse du red latch
ldi ZL,low(TIM_OVF_set_red_latch)
add ZL,argument ; et on ajoute le n° de la couleur x4
ijmp // 15

TIM_OVF_set_red_latch:
cbi PLatch,BRedlatch
or red_latch,r16
out PBUS,red_latch
sbi PLatch,BRedlatch
nop
nop
nop
rjmp TIM0_OVF_loop_0

TIM_OVF_set_green_latch:
cbi PLatch,BGreenlatch
or green_latch,r16
out PBUS,green_latch
sbi PLatch,BGreenlatch
nop
nop
nop
rjmp TIM0_OVF_loop_0

TIM_OVF_set_blue_latch:
cbi PLatch,BBluelatch
or blue_latch,r16
out PBUS,blue_latch
sbi PLatch,BBluelatch
rjmp TIM0_OVF_loop_0


TIM0_OVF_end_loop:

 


TIM0_OVF_end:
pop YH
pop YL
pop Zh
pop Zl
pop argument
pop r16


reti

 


.org 0xC00
.cseg
powerof2com:
.db 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80

; les programmes
program_0_degrade_counter:
.db 000, 036, 072, 108, 144, 180, 216, 000
program_0_degrade_status:
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01


; program_0_degrade_counter:
; .db 000, 065, 128, 192, 000, 064, 128, 192
; program_0_degrade_status:
; .db 0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01

 

 

powerof2com_inv:
.db 0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F
 

 

 

 

 

Ressources / Download

Description Lien/Fichier(s)
Circuit imprimé (PCB) et schema.

Fichier au format Eagle (+pdf) PCB & layout v1.0

PCB (pdf)

Implantation composants (PDF)
Code source

Projet AVR Studio Projet AVR studio (zip) contenant tout les fichiers (code source asm & projet aps, programme assemblé (.hex))

Fichier assembler (.asm)
 

Commentaires

Poster un nouveau commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.
Refresh Type the characters you see in this picture.
Type the characters you see in the picture; if you can't read them, submit the form and a new image will be generated. Not case sensitive.  Switch to audio verification.

Commentaires récents