18F PIC Example - Red Green Blue (RGB)
LED's
RGB
LED's are
a marvel to watch in action, and I have got to say, they are worth
the effort to play around with. The programming side of things does
take a little thought, primarily because you need to control 3
different Pulse Width Modulated (PWM) signals to control the
brightness of each
LED
(Red Green and Blue).
RGB LED's have
4 pins, 3 of them are for a specific colour (either Red Green or
Blue), and the forth is a common anode or cathode. I prefer to use
the common anode devices, as I like to use the
ULN2003 to drive the LED's. This way I can hook up many LED's in
parallel to get some very powerful and amazing lighting effects.
The
LED's I am
using are Common Anode so that I can use the
ULN2003 to drive many in parallel. I brought them from
www.core-electronics.com.au and I'm very impressed by the vivid
colour and extreme brightness of these LED's! Below is the pin-out;
Note the PIC's
power supply/oscillator are not shown
The above
diagram utilises the
ULN2003 to drive much higher current devices then what the PIC
can handle so with this design,
you could put a lot of RGB LED's in parallel for some really
stunning lighting effects.
The
RGB LED's from
Core Electronics have a forward voltage drop of about 2 Volts
for the Red LED, and 3.5 Volts for both the Green and Blue LED's. To
ensure that I am running them at full brightness (20mA for each colour), I can calculate the resistance required for
each
resistor by the following equation;
R = (V -
Vf - Vce) /
I
Where R =
Resistance in ohms, V = Supply Voltage,
Vf = LED Forward Bias
Voltage,
Vce = ULN2003 voltage drop, I = Current
To get the maximum
brightness from your LED's, replace the resistors with the following
(increase the current if more LED's are
used in parallel);
Red
resistor = (5 - 2 - 0.9) / 0.020 =
105 ohm or greater
Green
resistor = (5 - 3.5 -
0.9) /
0.020 =
30 ohm or greater
Blue
resistor = (5 - 3.5 -
0.9) /
0.020 =
30 ohm or greater
The Program
Because the program is interrupt driven, I
want to take as little time as possible to service the interrupt.
One way is to minimise the code, another is to maximise the clock
speed, so I chose a little of both.
The reason why
I chose an Interrupt driven routine, is so that once the registers
and interrupt is setup, you can do
anything you want in the main program, and not worry about
the performance of the RGB LED. In the circuit above, I use the
18F4550 with a 20Mhz Crystal Oscillator and PLL enabled (a couple of config settings) to achieve 48Mhz CPU Clock speed,
handy.
The program has
to incorporate the control of three different
PWM
signals (one for each colour). I wanted to maintain as much
brightness control as possible, and decided to make the Duty Cycle
editable from 0 t0 100% by simply editing a register from 0 to 255.
In the example
below, I am using the 18F4550 with a 20Mhz external oscillator, and
with a few PLL config settings, the CPU will operate at
48Mhz
(12 million instructions per second). The Timer 2 interrupt will be
serviced every
50uS,
that is 20Khz. With this in mind, as each complete PWM signal takes
256 timer cycles, this means that a PWM pulse will be driven 20000 /
256 =
78.125 Hz.
Anything
above
25Hz will look fluent to the human eye!
This might not
be the most efficient way to create three PWM signals, but it allows
complete diversity in the fact that you can control the brightness
of each LED colour component
on the fly while doing anything
else! The program also uses the custom library file called
RandGen.bas, to create random numbers from 0 to 255.
The 19mS delay between
increments/decrements of the colours is to control the time it takes
going from one extreme to the other, in this case, 19mS ensures it
takes just under 5 seconds for
all
256 brightness levels to change.
Device = 18F4550
Clock = 48
Config PLLDIV = 5,
CPUDIV = OSC1_PLL2,
USBDIV = 2,
FOSC = HSPLL_HS
Include "RandGen.bas"
Dim TMR2IE As PIE1.1, // TMR2 interrupt enable
TMR2IF As PIR1.1, // TMR2 overflow flag
TMR2ON As T2CON.2, // Enables TMR2 to begin incrementing
Signal_Pin As PORTB.0 // Signal output to frequency meter
Dim Red_Pin As PORTC.0,
Green_Pin As PORTC.1,
Blue_Pin As PORTC.2,
Red_Duty As Byte,
Green_Duty As Byte,
Blue_Duty As Byte,
Red_DutyVal As Byte,
Green_DutyVal As Byte,
Blue_DutyVal As Byte,
RandomVal As Byte
Dim uS As Word,
mS As Word
Interrupt TMR2_Interrupt()
High(Signal_Pin)
Save(0) // Back up system variables
If TMR2IF = 1 Then // Check if the interrupt was from TMR2
TMR2IF = 0 // Clear the TMR2 interrupt flag
uS = uS + 50
If uS >= 1000 Then
uS = uS - 1000
Inc(mS)
EndIf
Inc(Red_DutyVal)
Inc(Green_DutyVal)
Inc(Blue_DutyVal)
If Red_DutyVal > Red_Duty or red_Duty = 0 Then
Red_Pin = 0
Else
Red_Pin = 1
EndIf
If Green_DutyVal > Green_Duty or Green_Duty = 0 Then
Green_Pin = 0
Else
Green_Pin = 1
EndIf
If Blue_DutyVal > Blue_Duty or Blue_Duty = 0 Then
Blue_Pin = 0
Else
Blue_Pin = 1
EndIf
EndIf //
Restore // Restore system variables
Low(Signal_Pin)
End Interrupt
Private Sub TMR2_Initialize()
TMR2ON = 0 // Disable TMR2
TMR2IE = 0 // Turn off TMR2 interrupts
PR2 = 149 // TMR2 Period register PR2
T2CON = %00000001 // T2CON 0:1 = Prescale
// 00 = Prescale is 1:1
// 01 = Prescale is 1:4
// 1x = Prescale is 1:16
// 3:6 = Postscale
// 0000 = 1:1 postscale
// 0001 = 1:2 postscale
// 0010 = 1:3 postscale...
// 1111 = 1:16 postscale
TMR2 = 0 // Reset TMR2 Value
TMR2IE = 1 // Enable TMR2 interrupts
TMR2ON = 1 // Enable TMR2 to increment
Enable(TMR2_Interrupt)
End Sub
// Start Of Program...
Low(Red_Pin)
Low(Green_Pin)
Low(Blue_Pin)
Red_Duty = 0
Green_Duty = 0
Blue_Duty = 0
Red_DutyVal = 0
Green_DutyVal = 0
Blue_DutyVal = 0
uS = 0
mS = 0
RandGen.Initialize(128) // Initialize the Random Number generator
TMR2_Initialize // Setup and enable TMR2
While True // Create an infinite loop
RandomVal = RandGen.Rand // Grab a random number from 0 to 255
Select RandomVal // Find out what colour to increase/decrease
Case 0 To 42
While Red_duty < 255
mS = 0
Repeat
Until mS = 19
inc(Red_Duty)
Wend
Case 43 To 84
While Red_duty > 0
mS = 0
Repeat
Until mS = 19
dec(Red_Duty)
Wend
Case 84 To 127
While Green_duty < 255
mS = 0
Repeat
Until mS = 19
inc(Green_Duty)
Wend
Case 128 To 170
While Green_duty > 0
mS = 0
Repeat
Until mS = 19
dec(Green_Duty)
Wend
Case 170 To 212
While Blue_duty < 255
mS = 0
Repeat
Until mS = 19
inc(Blue_Duty)
Wend
Case 212 To 255
While Blue_duty > 0
mS = 0
Repeat
Until mS = 19
dec(Blue_Duty)
Wend
End Select
Wend
Video Tutorials:
-
RGB Program and Circuit Simulation
- In depth explanation of
the program/code
- I show the program being
simulated and cover the control of the PWM signals
-
RGB Practical Demonstration
- Have a look at the
program operating in real life, keep in mind the video does not give
the RGB LED's the justice they deserve!
Download RandGen
Library as seen in the program
Link:
What is a Swordfish Library?
Where you can get the components;

 | Site Tutorial Index |
|  | 16F PIC Examples |
|  | 18F PIC Examples |
|  | Handy Tips |