RGB LED SX/B Example
This is something I have been wanting to do for some time now. Mixing colors with an RGB LED.
So lets start at the beginning, The object is to vary the color of each red, green and blue LED to produce a different color. You can think of it as a standard 8 bit color palette.
So lets say you wanted to produce the color purple. That would be full red, full blue and no green. Easy enough. But what if you wanted to produce a dark purple. This can be done by reducing the intensity of the blue LED.
But how?
The answer is in PWM (pulse width modulation).
If you are new to PWM, then lets get one thing straight. You DO NOT control the color of an LED by blinking it at different speeds. Blinking is a byproduct of PWM. Controlling the color is a function of reducing the voltage that the LED sees. By reducing the voltage, you decrease the brightness.
I will provide two examples. One I will just type at the end of this paragraph, the other is an attached file that will also be pasted to the end of this post.
A simple example.
The Red LED is connected to RB.0. Green connected to RB.1 and Blue connected to RB.2.
Place the OUTPUT and PWM commands below into your SX/B Template:
Start:
' initialization code here
OUTPUT RB.0
OUTPUT RB.1
OUTPUT RB.2
Main:
pwm RB.0, 255, 1
pwm RB.1, 0, 1
pwm RB.2, 255, 1
GOTO Main
Please note that the color intensity comes from changing the DUTY cycle, not the duration.
To create a dark purple you would set RB.2 (blue) to something like 180.
There are a couple issues with the above approach. The first is that the duration of the PWM is too long. This destroys the POV (persistence of vision) effect. You can see this by looking at the LED and the quickly look up, or down or anywhere for that matter. You will see red, green and blue streaks coming from the LED. Very distracting to say the least. Bump up the duration from 1 to 5 to see the full effect.
Another issue is that you are applying PWM to each LED one after the other. In other words the above code takes 3 milliseconds to PWM all three LEDs. You really need to PWM all three at the same time.
The final issue is that pausing at a certain color is very problematic with the above method.
The sample code provided solves all these issues. The code is pretty heavily commented so I will not reproduce it here.
Basically it uses an INTERRUPT and some helper functions to produce some really pleasing results.
Enjoy and have some fun this weekend.
A more advanced method:
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- - - PLJack - - -
Perfection in design is not achieved when there is nothing left to add.
It is achieved when there is nothing left to take away.
Post Edited (PLJack) : 11/24/2006 9:09:35 PM GMT
So lets start at the beginning, The object is to vary the color of each red, green and blue LED to produce a different color. You can think of it as a standard 8 bit color palette.
So lets say you wanted to produce the color purple. That would be full red, full blue and no green. Easy enough. But what if you wanted to produce a dark purple. This can be done by reducing the intensity of the blue LED.
But how?
The answer is in PWM (pulse width modulation).
If you are new to PWM, then lets get one thing straight. You DO NOT control the color of an LED by blinking it at different speeds. Blinking is a byproduct of PWM. Controlling the color is a function of reducing the voltage that the LED sees. By reducing the voltage, you decrease the brightness.
I will provide two examples. One I will just type at the end of this paragraph, the other is an attached file that will also be pasted to the end of this post.
A simple example.
The Red LED is connected to RB.0. Green connected to RB.1 and Blue connected to RB.2.
Place the OUTPUT and PWM commands below into your SX/B Template:
Start:
' initialization code here
OUTPUT RB.0
OUTPUT RB.1
OUTPUT RB.2
Main:
pwm RB.0, 255, 1
pwm RB.1, 0, 1
pwm RB.2, 255, 1
GOTO Main
Please note that the color intensity comes from changing the DUTY cycle, not the duration.
To create a dark purple you would set RB.2 (blue) to something like 180.
There are a couple issues with the above approach. The first is that the duration of the PWM is too long. This destroys the POV (persistence of vision) effect. You can see this by looking at the LED and the quickly look up, or down or anywhere for that matter. You will see red, green and blue streaks coming from the LED. Very distracting to say the least. Bump up the duration from 1 to 5 to see the full effect.
Another issue is that you are applying PWM to each LED one after the other. In other words the above code takes 3 milliseconds to PWM all three LEDs. You really need to PWM all three at the same time.
The final issue is that pausing at a certain color is very problematic with the above method.
The sample code provided solves all these issues. The code is pretty heavily commented so I will not reproduce it here.
Basically it uses an INTERRUPT and some helper functions to produce some really pleasing results.
Enjoy and have some fun this weekend.
A more advanced method:
' ========================================================================= ' ' Purpose... RBG LED Example ' Author.... PLJack ' E-mail.... kellylabs@fastmail.fm ' ' ========================================================================= ' ------------------------------------------------------------------------- ' Program Description: ' ' ------------------------------------------------------------------------- '--- Don't Forget: Zero is full brightness and 255 is off. '--- This example demonstrates the mixing of colors '--- using a RGB LED. '--- CAUTION: Be careful with bright LEDs while using '--- this code, staring at a bright LED for most of '--- the day can have undesirable results for your '--- eyes. '--- I find that surface mount LEDs work best for '--- color mixing. Most 5mm LEDs seem to have the '--- three LEDs too far apart for proper convergence '--- (mixing). '--- '--- General: '--- Basically there are several components to this '--- example. The first is an INTERRUPT that is '--- constantly monitoring the PWM lines for RGB. '--- There are three variables for the LED, RedVal, '--- GreenVal and BlueVal. You can change these '--- variables at any time. The interrupt will '--- automatically change the LED to the new value. '--- Because we are using an interrupt you can use the '--- PAUSE command and the LED will continue to emit '--- light. This makes it very easy to display a color '--- for a certain amount of time. '--- '--- Operation: '--- '--- Not much to it really. There are three main '--- functions for you to use. '--- '--- Diag: '--- '--- Used at startup. It will display the red, green '--- and blue LEDs in that order. This is useful to '--- determine that your LED is correctly connected to '--- the SX. '--- '--- '--- TransitColor [noparse][[/noparse]Num]: '--- '--- This function creates a random color. Then makes '--- a transition from the current LED color over to '--- the new color. It performs this transition in '--- 255 steps. You can provide and optional parameter '--- to pause for [noparse][[/noparse]Num] '--- milliseconds per step. '--- The max transition time is 255 steps X 255ms = 65 '--- seconds. '--- '--- '--- RandomPulse [noparse][[/noparse]Num]: '--- '--- This function actually creates some beautiful '--- results. It turns off the LED, creates a random '--- color then slowly raises the led to full '--- brightness and finally fades to off. '--- The great results come from the fact that it '--- increments the RGB colors by one. So if the color '--- has a lot of blue in it (strongest color) as it '--- fades blue will be the last led to fade. Thus '--- mixing with the red and green as those colors are '--- reduced. '--- This also has an optional parameter to pause for '--- [noparse][[/noparse]Num] milliseconds per step. Although the total '--- number of steps is unknown. It depends on the '--- value of the strongest color. '--- '--- And remember to have fun. '--- Please forgive my breaking of the Uppercase keyword convention. '--- After years of coding in PC languages, I just can't take '--- all those uppercase words. ' ------------------------------------------------------------------------- ' Device Settings ' ------------------------------------------------------------------------- DEVICE SX28, OSC4MHZ, TURBO, STACKX, OPTIONX FREQ 4_000_000 ID "SXB 1.50" ' ------------------------------------------------------------------------- ' IO Pins ' ------------------------------------------------------------------------- RedLED var RB.0 GreenLED var RB.1 BlueLED var RB.2 ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- '--- LED TYPE '--- Common anode - SX pins sink to ground. '--- Common Cathode - SX pins source the voltage. '--- Un-comment the correct type for your LED. '--- Common Anode 'LEDOn con 0 'LEDOff con 1 '--- Common Cathode LEDOn con 1 LEDOff con 0 '--- Color Clip '--- If your LED has a color that is over powering the others you can '--- clip its brightness here. Keep in mind this is not a global clip '--- because the color reduction is not proportional to the others. '--- But it will prevent a color from becoming too bright. '--- Zero is full strength. Increase number to reduce total brightness. '--- To do this properly you would need a variable resistor '--- on each LED. Run them at full brightness, then adjust '--- each VR for a perfect white. '--- You might have some luck performing a proportional adjust on '--- the fly via code, but I think rounding errors might cause more '--- trouble than it is worth. C_RedClip con 0 C_GreenClip con 0 C_BlueClip con 0 '--- Defaults if no parameter is provided C_DefaultTransitSpeed con 5 C_DefaultPulseSpeed con 15 '--- Just substitutes for 0 and 255 incase you should '--- ever want to change them. C_PwmMin con 0 C_PwmMax con 255 ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- '--- This is the value (duty cycle) given to each LED at each INTERRUPT. RedVal var Byte GreenVal var Byte BlueVal var Byte '--- Sometimes handy to store the above variables. RedValCopy var Byte GreenValCopy var Byte BlueValCopy var Byte '--- Used for the duty cycle inside the INTERRUPT. RpwmCounter var Byte GpwmCounter var Byte BpwmCounter var Byte '--- For use constructing loops. LoopIdx var Byte '--- Used to increase the time a loop takes to finish. FadeSpeed var Byte '--- For functions that return a Byte variable. ReturnVar var Byte '--- Always nice to have a few around. Always assume as volatile. UtilVar1 var Byte UtilVar2 var Byte UtilVar3 var Byte ' ------------------------------------------------------------------------- INTERRUPT 30000 ISR_Start: '--- Add one to pwm counters. '--- Note they will roll over to zero after 255. Inc RpwmCounter Inc GpwmCounter Inc BpwmCounter '--- Red LED PWM if RpwmCounter > RedVal then RedLED = LEDOn else RedLED = LEDOff endif '--- Green LED PWM if GpwmCounter > GreenVal then GreenLED = LEDOn else GreenLED = LEDOff endif '--- Blue LED PWM if BpwmCounter > BlueVal then BlueLED = LEDOn else BlueLED = LEDOff endif ISR_Exit: RETURNINT ' ========================================================================= PROGRAM Start ' ========================================================================= ' ------------------------------------------------------------------------- ' Subroutine Declarations ' ------------------------------------------------------------------------- TransitColor sub 0, 1 RandomPulse sub 0, 1 Diag sub 0 SeedVars sub 0 RandomColor sub 0 RandomUtilVar sub 0 RaiseStrongest sub 1 FadeStrongest sub 1 StoreColor sub 0 RestoreColor sub 0 GetStrongestColor sub 0 ' ------------------------------------------------------------------------- ' Program Code ' ------------------------------------------------------------------------- Start: '--- Set LEDs as outputs. OUTPUT RedLED OUTPUT GreenLED OUTPUT BlueLED '--- Un-comment to perform a quick diagnostic at start up. Diag 'Seed anything that the RANDOM command will use. SeedVars Main: TransitColor ' RandomPulse Goto Main ' ------------------------------------------------------------------------- ' Subroutine Code ' ------------------------------------------------------------------------- ' ------------------------------------------------------------------------- RandomPulse: '--- Too few parameters, set to default. if __PARAMCNT <> 1 then FadeSpeed = C_DefaultPulseSpeed else FadeSpeed = __PARAM1 endif RandomColor '--- Bring color to full brightness. RaiseStrongest FadeSpeed '--- Now fade until off. FadeStrongest FadeSpeed return ' ------------------------------------------------------------------------- TransitColor: '--- Too few parameters, set to default. if __PARAMCNT <> 1 then FadeSpeed = C_DefaultTransitSpeed else FadeSpeed = __PARAM1 endif '--- Create a random color, only with the util vars this time. RandomUtilVar '--- Now bring the current LED values towards the new values. for loopIdx = C_PwmMin to C_PwmMax if RedVal <> UtilVar1 then if RedVal < UtilVar1 then inc RedVal else dec RedVal endif endif if GreenVal <> UtilVar2 then if GreenVal < UtilVar2 then inc GreenVal else dec GreenVal endif endif if BlueVal <> UtilVar3 then if BlueVal < UtilVar3 then inc BlueVal else dec BlueVal endif endif pause FadeSpeed next return ' ------------------------------------------------------------------------- FadeStrongest: FadeSpeed = __PARAM1 StoreColor UtilVar1 = GetStrongestColor '--- Find number of loops required to turn off color UtilVar2 = C_PwmMax - UtilVar1 '--- Bring down all colors. for loopIdx = C_PwmMin to UtilVar2 if RedVal < C_PwmMax then inc RedVal endif if GreenVal < C_PwmMax then inc GreenVal endif if BlueVal < C_PwmMax then inc BlueVal endif pause FadeSpeed next RestoreColor return ' ------------------------------------------------------------------------- RaiseStrongest: FadeSpeed = __PARAM1 UtilVar1 = GetStrongestColor UtilVar2 = C_PwmMax - UtilVar1 StoreColor '--- Set LEDs to off. RedVal = C_PwmMax GreenVal = C_PwmMax BlueVal = C_PwmMax '--- Bring up all colors. for loopIdx = C_PwmMin to UtilVar2 if RedVal > RedValCopy then dec RedVal endif if GreenVal > GreenValCopy then dec GreenVal endif if BlueVal > BlueValCopy then dec BlueVal endif pause FadeSpeed next RestoreColor return ' ------------------------------------------------------------------------- StoreColor: RedValCopy = RedVal GreenValCopy = GreenVal BlueValCopy = BlueVal return ' ------------------------------------------------------------------------- RestoreColor: RedVal = RedValCopy GreenVal = GreenValCopy BlueVal = BlueValCopy return ' ------------------------------------------------------------------------- RandomColor: random RedVal random GreenVal random BlueVal '--- If a color clip is set, honor it. if RedVal < C_RedClip then RedVal = C_RedClip endif if GreenVal < C_GreenClip then GreenVal = C_GreenClip endif if BlueVal < C_BlueClip then BlueVal = C_BlueClip endif return ' ------------------------------------------------------------------------- RandomUtilVar: '--- This is a bit of an odd ball function. '--- To perform the transition for the TransitColor function '--- we need a few random numbers. Here we just use the UtilVars '--- instead of wasting RAM for three one off variables. '--- random UtilVar1 random UtilVAr2 random UtilVar3 '--- If a color clip is set, honor it. if UtilVar1 < C_RedClip then UtilVar1 = C_RedClip endif if UtilVar2 < C_GreenClip then UtilVar2 = C_GreenClip endif if UtilVar3 < C_BlueClip then UtilVar3 = C_BlueClip endif return ' ------------------------------------------------------------------------- GetStrongestColor: '--- Assign strongest (low val) color to ReturnVal. if RedVal < GreenVal then ReturnVar = RedVal else ReturnVar = GreenVal endif if ReturnVar > BlueVal then ReturnVar = BlueVal endif return ReturnVar ' ------------------------------------------------------------------------- SeedVars: 'Seed anything that the RANDOM command will use. RedVal = 50 GreenVal = 100 BlueVal = 150 UtilVar1 = 150 UtilVar2 = 100 UtilVar3 = 50 return ' ------------------------------------------------------------------------- Diag: '--- A simple diagnostic to confirm that the LED is '--- connected to the SX correctly and is working properly. RedVal = C_PwmMax GreenVal = C_PwmMax BlueVal = C_PwmMax RedVal = C_PwmMin Pause 500 RedVal = C_PwmMax GreenVal = C_PwmMin Pause 500 GreenVal = C_PwmMax BlueVal = C_PwmMin Pause 500 BlueVal = C_PwmMax for loopIdx = C_PwmMin to C_PwmMax RedVal = LoopIdx pause 2 next for loopIdx = C_PwmMin to C_PwmMax GreenVal = LoopIdx pause 2 next for loopIdx = C_PwmMin to C_PwmMax BlueVal = LoopIdx pause 2 next for loopIdx = C_PwmMin to C_PwmMax RedVal = LoopIdx BlueVal = LoopIdx GreenVal = LoopIdx pause 4 next return ' ------------------------------------------------------------------------- ' ========================================================================= ' User Data ' ========================================================================= Pgm_ID: DATA "SX/B RGB LED Example", 0 '
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- - - PLJack - - -
Perfection in design is not achieved when there is nothing left to add.
It is achieved when there is nothing left to take away.
Post Edited (PLJack) : 11/24/2006 9:09:35 PM GMT
SXB
![](/plugins/FileUpload/images/file.png)
13K
Comments
Very nice.
Just a couple notes though:
· You only need 1 PWMCounter because all three will always be the same value anyway.
· Check the assembly listing, and if it doesn't use any __PARAMx variables, then add NOPRESERVE to the INTERRUPT line. That will save a bunch of cycles in the interrupt.
· 30000 interrupts per second is a refresh rate of 117Hz. 60Hz is plenty fast, which would be 15360.
· I would add the second parameter to FREQ (to account for time spent in the interrupt).·You can use SX-Sim·to see how many clocks are spent in the interrupt.
·
When you are ready for a challenge, make the interrupt routine adjust the brightness (like your TransitColor routine does).
Bean.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheap used 4-digit LED display with driver IC·www.hc4led.com
Low power SD Data Logger www.sddatalogger.com
SX-Video Display Modules www.sxvm.com
Stuff I'm selling on ebay http://search.ebay.com/_W0QQsassZhittconsultingQQhtZ-1
"People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
Post Edited (Bean (Hitt Consulting)) : 11/25/2006 12:21:38 AM GMT
Yes there are optimizations that can be had, wanted to make it more of an example.
Got me on the PWMCounter though. Doh!
I found 30000 gave the best results. I meant to get back to that number, forgot.
Thanks for the feedback.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- - - PLJack - - -
Perfection in design is not achieved when there is nothing left to add.
It is achieved when there is nothing left to take away.
I hope you still remember this project after 2.5 years.
I worked on it last night and enjoyed it. Need to experiment more on the different options on PAUSES.
On a simpler note, can you give some guidelines if I just want to simply do the following:
A fade time (preferably with speed adjust) and a scene time (LEDOn for a certain amount of time via a PAUSE) for each color where I can control the time for each fade time and scene time.
Also a transistion time where one LED fades out and the next LED fades in so that there is no complete blackout at transition but a slight mixing of the two interacting colors.
Thanks.
Danny
2 1/2 years! Holy C##P.
I'm really happy you enjoyed it. That's exactly why I wrote it.
Actually I remember this article well.
I just re-read it. Pretty rough code.
Since then I have done allot of RGB code. I've made night lights, counter lights.
Currently I have a white globe attached to the wall that pulses different colors depending on what is in my email inbox.
If you don't mind being patient I will fire up the SX computer and see what I have for code that can help.
Patient being the key word there. Could take a handful of days.
BTW, I did another write up on RGB color in these forums here:
Link: RGB Color Models
BTW again, although I have not logged into the forums in a long time I still read them a few times a week.
Hi Guys!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- - - PLJack - - -
Perfection in design is not achieved when there is nothing left to add.
It is achieved when there is nothing left to take away.
Post Edited (PLJack) : 3/27/2009 11:41:36 PM GMT
Good to hear from you. You've not post anything but looks like you''ve been busy working other projects.
I have gone through your older posts as well.
I will patiently wait.
Thanks.
Danny
There are ways to create the presence of black, I use white plastic and while other leds are on that one spot is not is of coarse the absence of light and looks dark. This also works for gray scales. The other way is using smoked black acylic the same as what they use for non glare applications. This also improves the richiness of colors and allows·intensities of white light to create gray.
Kevin
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔