( lien vers la version français )
For some time now, I think the commercially available garlands, are too boring, too repetitive with their two or three alternations of colored bulbs.
I wanted to create a truly personalized one without ruin me either. Two ideas immediately came:
- a microprocessor to control all the lights, but at the cost of a relatively large strand wire, even though the use of crossing wire and diodes, can reduce the number of son, this is not very satisfactory,
- place a microprocessor per lamp, but until the price of each processor exploded the price of the most basic garlands.
For a short time, Microchip sells very small processors, a few tens of cents the unit, CMS and low number of legs.
Diagram:
The master processor sends control commands over a serial single wire bus. These commands are interpreted and transmitted by each slave unit.
The processor used is the PIC12F609-I/SN for several reasons:
- Its price 0.56 including VAT per pack of 100 on tmicrochipDIRECT
- Its case: 8-pin SOIC
- The presence of an external interrupt pin and an hardware timer
The serial bus, as opposed to a parallel connection of all processors, was chosen for two reasons:
- This allows the self-enumeration of the slave units: starting the master sends a byte with a value of zero. This value is stored by the first unit, then the value "1" is sent to the next unit. And so on: each unit is taking to address, the value received, and sends that value incremented to the next unit. This avoids to program each unit specifically with a different address.
- On long cable, the resistance of the copper becomes significant. While each unit consumes about 50mA (LED above), after 50 diodes, the total consumption is close to 2.5A. In this case, the voltage reference to the mass change, and the interpretation of 1 or 0 become difficult. With chaining, the signal is regenerated at each hop unit.
Always the problem of current drawn on a string of any length, it is not guaranteed to have the same voltage at the beginning and the end of the garland. The principle is to feed the entire wreath with 7V and one regulator will stabilize to 5V on each unit.
The diode actually consists of three LEDs (red, green, blue) to create all the visible colours. It is supplied with common anode, because the microprocessor has drain capacity to ground greater than to provide +5 V on each bit output.
The intensity control is software (pulse width modulation). Each colour is controlled internally on 64 levels, all refreshed 100 Hz.
The printed circuit board is looking like :
- On the left the two power connectors and serial bus around the 5V regulator,
- The processor,
- The resistors for the LEDs,
- The connector of the LED.
(dimensions in milimeters )
The transmitted frames are made of 25 bits : 7 address bits + 6 bits red bits + 6 green bits+ 6 blue bits.
Unlike a conventional serial communications, bits are encoded as pulse width, not by voltage level. The objective is to work with interruption, instead of pooling.
The software configures the trigger interrupt on rising edge. When this change occurs, a hardware timer is started and next interrupt is configured on falling edge. When this new event arrives, the timer value is tested to determine the value of the encoded bit. (60μS to "0", 30μS to "1"). At each interruption, the state is reported as output for the next unit.
The total duration of a bit is 100μS. With few hundred microseconds between each command, this mean 300 commands per second.
Extremely simple:
- 8F252 I / SP with its 20MHz quartz,
- Resistor on the MCLR pin,
- A programming connector,
- the serial output on PORTBbits.RB1,
- A 7805 regulator and its capacitor
I used a ready-to-use testing wafer, instead of creating at printed circuit board.
Master processor PIC18F252
LED processor PIC12F609
5V smd regulator 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
;--------------------------------
The serial protocol used is not compatible with the PIC18F252s UART.
It is therefore necessary to code software-protocol.
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
}
//************************************
Then you need to encode the colours sequences.
Two examples: random colours on all LED and alternating red/green/blue all over the garland.
//************************************
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++){}
}
}
//************************************
Components before soldering
Circuits manufacturing
Final appearance
Chrono diagrams
Vidéo
If you have questions or suggestions : hamatum.lcdfree.fr