Depuis quelques temps, déjà, je trouve les guirlandes disponibles dans le commerce, trop ennuyeuse, trop répétitive avec leurs deux ou trois alternances d’ampoules colorées.
J’ai eu envie de créer une guirlande réellement personnalisable, sans me ruiner non plus. Tout de suite deux idées sont arrivées :
- un microprocesseur pour piloter toutes les lampes, mais au prix d’un relativement gros toron de fils, même si l’utilisation de croisement de fils et de diodes, permet de diminuer le nombre de fils, cela n’est pas très satisfaisant,
- placer un microprocesseur par lampe, mais jusqu’alors le prix de chaque processeur faisait exploser le tarif de la plus basique des guirlandes.
Depuis peu de temps, Microchip vend de tout petits processeurs, a quelque dizaines de centimes d’euro l’unité, en CMS et a faible nombre de pattes. Financièrement le projet devient alors réalisable.
Le schéma de principe est le suivant :
Un processeur maitre envoie des ordres de pilotage sur un bus série mono-fils. Ces ordres sont interprétés et retransmis par chaque unité esclave.
Le processeur retenu est le PIC12F609-I/SN pour plusieurs raisons :
- son prix 0.56 € TTC par lot de 100 sur le site MicrochipDirect
- son boitier : 8 broches SOIC
- la présence d’une broche externe d’interruption et d’un timer hardware
Le bus en série, par opposition avec un branchement en parallèle de tous les processeurs, a été choisi pour deux raisons :
Ceci permet l’auto-énumération des unités esclaves : au démarrage le maitre envoie un octet ayant pour valeur zéro. Cette valeur est mémorisée par la première unité, puis la valeur « 1 » est envoyée à la suivant. Et ainsi de suite : chaque unité prend pour son adresse, la valeur reçue, et envoie cette valeur incrémentée à l’unité suivante. Ceci évite de devoir programmer chaque unité spécifiquement avec une adresse différente.
Sur un câble de grande longueur, la résistance du cuivre devient significative. Même si chaque unité consomme environ 50mA (la diode surtout), au bout de 50 diodes, la consommation totale approche les 2.5A. Dans ce cas, la référence de tension à la masse change, et le processeur ne sait plus interpréter correctement les valeurs 1 ou 0. Avec le chainage, le signal est régénéré à chaque saut d’unité.
Toujours pour le problème du courant tiré sur un fil relativement long, il n’est pas garanti d’avoir la même tension au début et à la fin de la guirlande. Le principe est donc d’alimenter l’ensemble de la guirlande avec 7V, et un régulateur stabilise à 5V la tension interne de chaque unité.
La diode, en fait constitué de trois diodes (rouge, vert, bleu), permet de créer toutes les couleurs de l’arc en ciel. Celle-ci est alimentée en anode commune, car le microprocesseur a une capacité de drainage vers la masse supérieure à celle de sa capacité à fournir du +5V sur chaque bit en sortie.
Le contrôle de l’intensité se fait logiciellement (modulation en largeur d’impulsion). Chaque couleur est pilotée en interne sur 64 niveaux, le tout rafraichi environ 100 fois par secondes.
Après routage du circuit nous avons :
- A gauche les deux connecteurs d’alimentation et bus série, autour du régulateur 5V,
- Le processeur,
- Les résistances pour la LED,
- Le connecteur de la LED.
(dimensions en mm )
Les trames transmises sont constituées de 25 bits : 7 bits d’adresse + 6 bits pour le couleur rouge + 6 bits pour le couleur verte + 6 bits pour la couleur bleue.
Contrairement à une communication série classique, les bits sont codés en largeur d’impulsion, et non par des états de tension. L’objectif est de faire travailler le pic sur interruption et non en pooling. Ceci allège la charge du logiciel dans le pic12F609.
Le logiciel configure le déclenchement d’interruption sur flanc montant. Lorsque ce changement survient, un timer hardware est lancé, ainsi que la configuration pour une interruption sur flanc descendant. Quand ce nouvel événement arrive, la valeur du timer est testée pour déterminer la valeur du bit ainsi codé. (60µS pour « 0 », 30µS pour « 1 »). A chaque interruption, l’état adéquat est reporté en sortie, pour l’unité suivante, avec une latence de 7µS.
Un bit dure 100µS au total, avec une pause de quelque centaines de microsecondes entre chaque commande. On atteint 300 commandes par seconde.
Extrêmement simple :
- un pic 18F252 I/SP avec son quartz à 20MHz,
- une résistance sur la broche MCLR,
- un connecteur pour sa programmation,
- un fil de sortie série sur le PORTBbits.RB1,
- un régulateur 7805 et son condensateur de stabilisation,
Tellement simple que je n’ai pas pris la peine de faire un circuit imprimé. Une plaque d’expérimentation à pastille est suffisante.
Processeur maitre PIC18F252
Processeur des modules à LED PIC12F609
Régulateur 5V cms MCP1702
; *****************************************************************************
; ** Module LED pour guirlande
; **
; ** CPU @8MHz : 0.5µS/instruction
; ** message : 7 adr + 6 rouge + 6 vert + 6 bleu @ 10Kbps -> 400 ordres/s
; ** le bits sont codés par des largeurs d'impulsions : 1 = 30µS , 0 = 60µS
; *****************************************************************************
processor 12F609
#include <p12F609.INC>
radix DEC
__CONFIG _BOD_OFF & _IOSCFS_8MHZ & _CP_OFF & _MCLRE_ON & _PWRTE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT
#define inW 0
#define inF 1
#define led_verte GP0
#define led_bleue GP1
#define bus_in GP2
#define mclr GP3
#define bus_out GP4
#define led_rouge GP5
serbuf1 EQU .64
serbuf2 EQU .65
serbuf3 EQU .66
serbuf4 EQU .67
replica EQU .68
own_adr EQU .69
rougeB EQU .70
vertB EQU .71
bleuB EQU .72
rouge EQU .73
vert EQU .74
bleu EQU .75
adr EQU .76
stack_w EQU .77
stack_s EQU .78
pwm EQU .79
tmp1 EQU .80
tmp2 EQU .81
ORG 0x0000 ; Reset
NOP
NOP
GOTO Start
;-------Routine d'interruption exterieur-----
ORG 0x0004
INT: MOVWF stack_w ; push w & status
SWAPF STATUS,w
MOVWF stack_s
BCF INTCON,INTF
BSF STATUS,RP0 ; BANK1
BTFSS OPTION_REG,INTEDG ; si flanc montant de l'IT
GOTO fld
NOP ; pour corriger le délai de montée par rapport à la descente
BCF OPTION_REG,INTEDG ; config pour attente flanc descendant
BCF STATUS,RP0 ; BANK0
BSF GPIO,bus_out
GOTO sorti
fld: BSF OPTION_REG,INTEDG ; config pour attente flanc ascendant
BCF STATUS,RP0 ; BANK0
BCF GPIO,bus_out
BTFSS INTCON,T0IF ; si overflow du timer (au delà de 128µS)
GOTO fle
BCF INTCON,T0IF
CLRF serbuf1 ; reset buffer d'entrée
INCF serbuf1,inF ; bit indicateur en début de chaine
CLRF serbuf2
CLRF serbuf3
CLRF serbuf4
fle: MOVF TMR0,inW ; detecte le temps passé depuis flanc montant (mesuré 60µS=110 / 30µS=56 )
SUBLW .85
RLF serbuf1,inF ; utilise la retenue pour injecter nouveau bit
RLF serbuf2,inF
RLF serbuf3,inF
RLF serbuf4,inF
sorti: MOVLW .0 ; lancer le timer 0
MOVWF TMR0
SWAPF stack_s,w ; pop w & status
SWAPF stack_w,f
SWAPF stack_w,w
RETFIE
;----------------------------------DEMARRAGE
Start CLRF rouge ; init des variables
CLRF vert
CLRF bleu
CLRF serbuf1 ; reset buffer d'entrée
INCF serbuf1,inF ; bit indicateur en début de chaine
CLRF serbuf2
CLRF serbuf3
CLRF serbuf4
MOVLW 0xff ; init replica port
MOVWF replica
BCF replica,bus_out ; bus de sortie a zero
MOVWF GPIO
BSF STATUS,RP0 ; BANK1
CLRF ANSEL
MOVLW b'00001100' ; port en entrée pour MCLR et INT, le reste en sortie
MOVWF TRISIO
MOVLW 0x3f ; init pull-up des ports
MOVWF WPU
MOVLW 0x48 ; init pull-up + div sur wdt
MOVWF OPTION_REG
BCF STATUS,RP0 ; BANK0
MOVLW 0xd0 ; init gie peie inte
MOVWF INTCON
MOVLW 49 ; config adresse en attendant autoénumération
MOVWF own_adr
;----------------------------------BOUCLE PRINCIPALE
main: BTFSS serbuf4,1 ; test le 25 ème bit pour savoir si on a tt récupéré en entrée série
GOTO atten
;----------------------------------TRAITE DONNEES REçUES 20µS
MOVF serbuf1, inW ; récupère bleu
ANDLW b'00111111'
MOVWF bleuB
RLF serbuf1,inF ; récupère vert avec 2 shift gauche d'abord
RLF serbuf2,inF
RLF serbuf3,inF
RLF serbuf4,inF
RLF serbuf1,inF
RLF serbuf2,inF
RLF serbuf3,inF
RLF serbuf4,inF
MOVF serbuf2, inW
ANDLW b'00111111'
MOVWF vertB
RLF serbuf1,inF ; récupère rouge avec 2 shift gauche d'abord
RLF serbuf2,inF
RLF serbuf3,inF
RLF serbuf4,inF
RLF serbuf1,inF
RLF serbuf2,inF
RLF serbuf3,inF
RLF serbuf4,inF
MOVF serbuf3, inW
ANDLW b'00111111'
MOVWF rougeB
RLF serbuf1,inF ; récupère adr avec 2 shift gauche d'abord
RLF serbuf2,inF
RLF serbuf3,inF
RLF serbuf4,inF
RLF serbuf1,inF
RLF serbuf2,inF
RLF serbuf3,inF
RLF serbuf4,inF
MOVF serbuf4, inW
ANDLW b'01111111'
MOVWF adr
CLRF serbuf1 ; reset buffer d'entrée
INCF serbuf1,inF ; bit indicateur en début de chaine
CLRF serbuf2
CLRF serbuf3
CLRF serbuf4
;----------------------------------APPLICATION DES DONNEES 5µS
MOVF own_adr,inW ; si adr=adresse
SUBWF adr,inW
BTFSC STATUS,Z
GOTO main3
MOVLW .127 ; si adr=broadcast
SUBWF adr,inW
BTFSS STATUS,Z
GOTO atten
main3: MOVF rougeB,inW
MOVWF rouge
MOVF vertB,inW
MOVWF vert
MOVF bleuB,inW
MOVWF bleu
;----------------------------------TRAITEMENT PWM LED #9µS/11µS
atten MOVLW .63
MOVWF pwm
Lred MOVF rouge,inW ; si pwm=rouge
SUBWF pwm,inW
BTFSS STATUS,Z
GOTO Lvert
BCF GPIO,led_rouge
Lvert MOVF vert,inW ; si pwm=vert
SUBWF pwm,inW
BTFSS STATUS,Z
GOTO Lbleu
BCF GPIO,led_verte
Lbleu MOVF bleu,inW ; si pwm=bleu
SUBWF pwm,inW
BTFSS STATUS,Z
GOTO Lred2
BCF GPIO,led_bleue
Lred2 DECFSZ pwm,inF
GOTO Lred
BSF GPIO,led_rouge
BSF GPIO,led_verte
BSF GPIO,led_bleue
GOTO main
END
;--------------------------------
Le protocole série utilisé n’est pas compatible avec l’UART du PIC18F252.
Il est donc nécessaire de coder logiciellement ce protocole.
void setled( unsigned char led, unsigned char tx_rouge, unsigned char tx_vert, unsigned char tx_bleu )
{
unsigned char d,i;
int j;
for(d=0; d<=6; d++) // Address (7bits)
if (led & (0x40>>d))
{
PORTBbits.RB1=1;
for (i=0; i<12; i++){}
PORTBbits.RB1=0;
for (i=0; i<25; i++){}
}
else
{
PORTBbits.RB1=1;
for (i=0; i<24; i++){}
PORTBbits.RB1=0;
for (i=0; i<14; i++){}
}
for(d=0; d<=5; d++) // Rouge (6bits)
if (tx_rouge & (0x20>>d))
{
PORTBbits.RB1=1;
for (i=0; i<12; i++){}
PORTBbits.RB1=0;
for (i=0; i<25; i++){}
}
else
{
PORTBbits.RB1=1;
for (i=0; i<24; i++){}
PORTBbits.RB1=0;
for (i=0; i<14; i++){}
}
for(d=0; d<=5; d++) // Vert (6bits)
if (tx_vert & (0x20>>d))
{
PORTBbits.RB1=1;
for (i=0; i<12; i++){}
PORTBbits.RB1=0;
for (i=0; i<25; i++){}
}
else
{
PORTBbits.RB1=1;
for (i=0; i<24; i++){}
PORTBbits.RB1=0;
for (i=0; i<14; i++){}
}
for(d=0; d<=5; d++) // Bleu (6bits)
if (tx_bleu & (0x20>>d))
{
PORTBbits.RB1=1;
for (i=0; i<12; i++){}
PORTBbits.RB1=0;
for (i=0; i<25; i++){}
}
else
{
PORTBbits.RB1=1;
for (i=0; i<24; i++){}
PORTBbits.RB1=0;
for (i=0; i<14; i++){}
}
for (j=0; j<180; j++){} // Petite pause entre chaque commande
}
//************************************
Ensuite il faut coder les séquences de couleurs.
Deux exemples : couleurs aléatoires sur toutes les LED et alternance rouge vert bleu sur toute la guirlande.
//************************************
void fete_foraine()
{
int i;
long j;
while(counter)
{
for (i=1; i<nbled; i+=2 )
setled ( i , rand()&0x7, rand()&0x7, rand()&0x7 );
for (i=2; i<nbled; i+=2 )
setled ( i , rand()&0x7, rand()&0x7, rand()&0x7 );
for (j=0; j<35000; j++){}
}
}
//************************************
void alternance_rouge_vert_bleu()
{
int i;
long j;
while(counter)
{
for (i=1; i<nbled; i+=3 )
{
setled( i, 4,0,0 );
setled( i+1, 0,4,0 );
setled( i+2, 0,0,4 );
}
for (j=0; j<32700; j++){}
for (j=0; j<32000; j++){}
for (i=1; i<nbled; i+=3 )
{
setled( i, 0,0,4 );
setled( i+1, 4,0,0 );
setled( i+2, 0,4,0 );
}
for (j=0; j<32700; j++){}
for (j=0; j<32000; j++){}
for (i=1; i<nbled; i+=3 )
{
setled( i, 0,4,0 );
setled( i+1, 0,0,4 );
setled( i+2, 4,0,0 );
}
for (j=0; j<32700; j++){}
for (j=0; j<32000; j++){}
}
}
//************************************
Les composants avant soudure
La fabrication des circuits
Aspect final
Chrono diagrammes
Vidéo
Si vous avez des questions ou des suggestions : hamatum.lcdfree.fr