Prop C PWM of simple RGB LED
John Kauffman
Posts: 653
I’m having trouble controlling an RGB LED with C and I haven’t gotten a search hit in fora on “RGB Propeller C”
This is not the smart/programmable kind of LED. It is just 3 LEDs in one case with 4 pins (common cathode). I checked the device without Prop control and no problem.
In the library reference I don't understand the purpose of the channel argument: pwm_set(int pin, int channel, int tHigh)
I tried the following code and got result of: blue > bright blue > nothing.
- Thanks.
This is not the smart/programmable kind of LED. It is just 3 LEDs in one case with 4 pins (common cathode). I checked the device without Prop control and no problem.
In the library reference I don't understand the purpose of the channel argument: pwm_set(int pin, int channel, int tHigh)
I tried the following code and got result of: blue > bright blue > nothing.
- Thanks.
/*RGB LED: use PWM to turn on red, green, blue in order 0.5 sec at 50%. what is “channel”?*/
#include "simpletools.h"
int pinRed = 0;int pinGreen = 1;int pinBlue = 2;
int main(void){
pwm_start(1000);
pwm_set(pinRed,1,500) ;
pwm_set(pinGreen,1,000) ;
pwm_set(pinBlue,1,000) ;
pause(500);
pwm_set(pinRed,1,000) ;
pwm_set(pinGreen,1,500) ;
pwm_set(pinBlue,1,100) ;
pause(500);
pwm_set(pinRed,1,000) ;
pwm_set(pinGreen,1,000) ;
pwm_set(pinBlue,1,500) ;
pause(500);
pwm_stop();
return 0;
}

Comments
Now it makes sense. The channels are the two timers in the cog.
Changing pinGreen to channel 2 gave me another color.
Can I start two PWM modules to get greater than 2 pins controlled? Perhaps in different cogs?
That being said, IIRC there was something in the Learn forum re: multiple channels ... have to check. Remembered, would the servo library be fast enough/suitable? At least it supports multiple pins but is limited in frequency.
Result is red, off, green, off, blue stays on.
I expected it to be red then ADD green then ADD blue to make white. Any guesses? Thanks.
/** This is the main PWM on RGB LED program file. turn on red then add green then add blue */ #include "simpletools.h" void displayRed(); void displayGreen(); void displayBlue(); int main(void){ int *cog1=cog_run(&displayRed,20); pause(500); int *cog2=cog_run(&displayGreen,20); pause(500); int *cog3=cog_run(&displayBlue,20); } void displayRed(void){ pwm_start(1000); pwm_set(0,0,500) ; } void displayGreen(void){ pwm_start(1000); pwm_set(1,0,500) ; } void displayBlue(){ pwm_start(1000); pwm_set(2,0,500) ; }My next try will be 2 LEDs on D/A pins and third on PWM
I have used Forth (pfth) to control the 3 inputs of an RGB Led using one PWM counter. I turn each assign each Led (R, B, G) pin in turn to the counter with a 3 millisecond delay between each and repeat the loop without any other delays. The 3 ms delay between colors is enough to blend the colors w/o flickering. It works in forth, but you may have to play with that number to get what you want.
I do have to assign different values to the PWM value (the 3rd value in your PWM statement) for each color to balance them. I am using a common anode LED which turns a color on by setting its cathode to Low. That means I have to turn off each color before moving to the next by changing its pin from output to input. In forth I have to interact with each parameter of the counters, but the C library should take care of that for you. I've attached the looping section of my code (there's more code to set the counter to pwm mode, but that's not needed to illustrate how to blend the colors.) Even though it's in forth, you should be able to follow the comments. The 'load intensity' and 'set ctra' commands essentially do the pwm function. The dirasetbit and diraclrbit commands are used to change the respective pin to input (turn off) and output (turn on). You probably don't have to worry about that. rctra, bctra, and gctra are variables that contain everything the counter needs to set the PWM function to the pin. Again, this is taken care of by C's PWM library function.
In my code the routine below is started in a new cog and I adjust the color intensities (rval, bval, and gval on a scale of 0 to 10) in the original cog. That code and the code to start the new cog are not shown. The initial values for rval, bval, and gval are shown in hex (just because it was easier to debug and balance the colors). I don't expect those values (even in decimal) would work in the PWM library function.
Hope this helps
Tom
: rbgledtest ( starts 1 cog and 1 ctr to run red, blue, and green ) 88800000 rval ! 48000000 bval ! 68000000 gval ! begin ( start endless loop for timesharing each color on 1 ctr ) \ --------------- Turn on red gdira diraclrbit \ set green pin to input -- turn green off rval @ frqa! \ load red intensity setting into frqa rctra ctra! \ set ctra to red pin rdira dirasetbit \ turn on red - set red pin to output 3 ms \ keep on for 3ms \ --------------- Turn on blue rdira diraclrbit \ set red pin to input -- turn red off bval @ frqa! \ load blue setting in frqa bctra ctra! \ set ctra to blue pin bdira dirasetbit \ turn on blue - set blue pin to output 3 ms \ --------------- Turn on green -- same as above bdira diraclrbit gval @ frqa! gctra ctra! gdira dirasetbit 3 ms again ;I tried it in C. The following code works. It just turns on all 3 leds using one PWM channel. It's just an endless loop with each color set to 500 which gives a white light. You should be able to add controls to vary each intensity and stop the loop.
Tom
/* RGBLedTest.C Use PWM to turn on red, green, blue Leds and blend colors at 50%. This is written for a common anode rbg led -- low = on, so need to use pin directions (input/output) to turn colors off when other is lit or they will go full brightness. */ #include "simpletools.h" // Include simple tools int pinRed = 12; int pinGreen = 14; int pinBlue = 13; int main() // Main function { set_directions(pinGreen, pinRed, 0); // set to input -- all colors off pwm_start(1000); while(1) // endless loop { set_direction(pinGreen, 0); // turn off green pwm_set(pinRed,1,500) ; set_direction(pinRed, 1); // turn on red pause(3); set_direction(pinRed, 0); // turn off red pwm_set(pinBlue,1,500) ; set_direction(pinBlue, 1); // turn on blue pause(3); set_direction(pinBlue, 0); // turn off blue pwm_set(pinGreen,1,500) ; set_direction(pinGreen, 1); // turn on green pause(3); } }In the meantime I had tried the low road DIY with 8 possible pulse widths:
while(1){ //total pause < 21 to keep within persistence of vision high(pinRed); pause(1); low(pinRed); high(pinGreen); pause(7); low(pinGreen); high(pinBlue); pause(7); low(pinBlue); }I am confused. When I tried my code to cycle separate color (R,B,G) on by itself for a given time by changing the pause to a long time (e.g. 1/2sec), it didn't work. The colors blended (each color did not turn off when I set the pin direction to input). I tried a number of things, for example just switching pin numbers after the half second delay. Since the documentation for pwm says that pins numbers can switched on the fly I expected it to work. It didn't. The colors still blended.
That is an issue with using library functions, it's not always clear what the library function is doing at the lower level - I assume it is setting pin directions.
I finally used a brute force method, stopping pwm and restarting it for each color. That worked. The code below works to cycle R, B, and G at 50% for times given by ton and toff. I think the code I wrote above could be simplified to get rid of the direction statements in the loop.
Tom
/* RGBLedTest no blend.C Use PWM to turn on red, green, blue Leds without blending colors at 50%. This is written for a common anode rbg led -- low = on, */ #include "simpletools.h" // Include simple tools int pinRed = 12; int pinGreen = 14; int pinBlue = 13; int ton = 500; int toff = 500; int main() // Main function { pwm_start(1000); while(1) // endless loop { pwm_set(pinRed,1,500) ; pause(ton); pwm_stop(); pause(toff); pwm_start(1000); pwm_set(pinBlue,1,500) ; pause(ton); pwm_stop(); pause(toff); pwm_start(1000); pwm_set(pinGreen,1,500) ; pause(ton); pwm_stop(); pause(toff); pwm_start(1000); } }It does not mix to make, e.g., purple, right? I don't see a way to do that with that code.
But if you set 'ton' = 6, 'toof' = 1, and play with the 3rd value in the pwm_set function you can blend colors. For example with my common anode Led (low = on, high = off), if I set the 3rd value to 1000 for green (i.e. off), and 0 for both red and blue, I get magenta. With a common cathode Led set green = 0, red and blue to a high value to get magenta.
You do have to play with the values. If I set 'ton' to 10ms or more, I see flickering, but 8ms makes the led brighter. Changing the values in the R,B,G calls to pwm_set will adjust the colors. Subtle changes in values don't have much effect. For example, with my LED if I turn Blue off and set both Green and Red to 0, I get a greenish yellow. To get a pure yellow, I need to increase the green value (make it dimmer) to about 500.
I'm not very happy using the method in that post, because the Led is not as bright as it could be and the range of color adjustments is touchy. By directly manipulating the counters (like in the forth program), I get a much greater range of brightness and color adjustments. However, even with that, it took a lot of fooling around changing the values that contributed to changed intensity to get the constants set so I can get 10 different intensities for each prime color.
Your brute force method may be a better start.
Tom
I wish I knew how to link PASM into C. If I did, I'd use this simple code for rgb handling.
dat org 0 rgbcog mov r1, par ' address of parameters add r1, #4 ' skip color (in par) rdlong r2, r1 ' read read pin mov rmask, #1 ' convert to mask shl rmask, r2 or dira, rmask ' make output add r1, #4 rdlong r2, r1 mov gmask, #1 shl gmask, r2 or dira, gmask add r1, #4 rdlong r2, r1 mov bmask, #1 shl bmask, r2 or dira, bmask rdlong ltix, #0 shr ltix, #7 ' tix for 128Hz shr ltix, #8 ' tix for 256 steps mov ltimer, ltix add ltimer, cnt ' start loop timer mov cycle, #0 rgbloop rdlong r1, par ' get color, 0x00_RR_GG_BB do_red mov r2, r1 ' make copy shr r2, #16 ' isolate red and r2, #$FF wz if_z andn outa, rmask if_z jmp #do_green cmp cycle, r2 wc, wz ' check value against cycle if_be or outa, rmask if_a andn outa, rmask do_green mov r2, r1 shr r2, #8 ' isolate green and r2, #$FF wz if_z andn outa, gmask if_z jmp #do_blue cmp cycle, r2 wc, wz if_be or outa, gmask if_a andn outa, gmask do_blue mov r2, r1 and r2, #$FF wz ' isolate blue if_z andn outa, bmask if_z jmp #next_cycle cmp cycle, r2 wc, wz if_be or outa, bmask if_a andn outa, bmask next_cycle add cycle, #1 ' bump cycle and cycle, #$FF ' keep 0..255 waitcnt ltimer, ltix ' let loop timing expire jmp #rgbloop ' --------------------------------------------------------------------------------------- rmask res 1 ' output masks gmask res 1 bmask res 1 rgbhub res 1 ' hub address or RGB value ltix res 1 ' loop tix for ~1kHz freq ltimer res 1 ' loop timer cycle res 1 ' pwm cycle position r1 res 1 ' working registers r2 res 1 fit 496Bad JonnyMac! The code I originally posted was out of my head and not tested -- the code posted now does in fact work (in Spin), and I'm doing as Steve suggests and trying to incorporate it into a C demo.
The RGB LED is a nice little way for students to see results of PWM (and reminds me of my college years spent in discos).
I don't have the skills to write it but am glad to test.
If you include the .spin file in a SimpleIDE project, your spin PASM code will automatically be linked (as binary_filename_dat_start).
This is demonstrated in the Documents/SimpleIDE/Propeller GCC Demos/toggle/pasm_toggle project.
All you need is an interface to the PASM similar to what you would write in SPIN. It's not really complicated. Many demo projects do this.
That limit exists only when using counters; with software PWM quite a lot more is possible.
10 % 4 SETPWM
and that will set P4 to 10% duty cycle, or:
20 % 4 8 SETPWMS
will set P4 through to P8 to 20 %
Jazzed,
1. Where can I find: Documents/SimpleIDE/Propeller GCC Demos/toggle/pasm_toggle project
2. Are you saying that if I write a C program using SimpleIDE (the IDE located at:
https://sites.google.com/site/propellergcc/documentation/simpleide ) that I can include a spin file and that it will automatically be linked? Like I would with #include simpletools.h
Thanks
Tom
I played a bit more with the code and found that for blending colors, the pause(toff) isn't needed, and without it there is more control over the individual colors.
Tom
1. If you have SimpleIDE installed the demo would be in the "Documents" area assuming you have one of those.
If you don't have that, look here: https://code.google.com/p/propsideworkspace/source/browse/#hg%2FPropeller GCC Demos%2Ftoggle%2Fpasm_toggle
2. SimpleIDE (the spinside branch) will link the PASM, that's all. It's been like that since the beginning.
Now that David added the ability to automatically generate and call SPIN from C/C++, the rest of the linking story can be added when I have time.
I've also go to look at Peter's PWM code.
Thanks, I've been using the tutorials on the C learning page and never realized this was in the SimpleIDE. Now I got some more studying to do.
Thanks again
Tom
can you help me understand that note? I'm thinking of PWM_set and PWM_start as the software solution. I'm thinking the D/A chip as the hardware solution. What do you mean by software in your note? Maybe working with ASM instead of a C library?
Thanks.
I've been working through jm_rgb.c and the spin program you posted. I can follow the process (writing one is another story), but I'm not sure about the 'start' function in the C program and have a question.
int start(unsigned int *color) // start RGB cog { extern unsigned int binary_rgbled_dat_start[]; return cognew(&binary_rgbled_dat_start, color); }Is the format of 'binary_rgbled_dat_start[]' specified? (start a binary in the dat section of rgbled.spin ?) or is it your method of keeping things straight. If it's specified, where can I find the explanation? If it is not specified, please explain what is happening.
I appreciate your help.
Tom
I followed Steve's guidance and opened one of the SimpleIDE demos to get the basic structure. Using the PASM Toggle demo as guidance, I muddled through and got things working (in Spin in about 5 minutes, in C about an hour).
My understanding is (please jump in and save me, Steve), that adding rgbled.spin to the project causes the compiler to create an executable module from it; the module is called binary_FILENAME_dat_start. All I did was copy, paste, and modify the start function from the C demo. Note that the Spin file has a start method in it -- but I don't think this is used (by C that is; I did use it to test the code with Spin).
The only reason I jumped into this fracas was to point out that with very little effort (on the PASM side), one could have multiple PWM outputs without resorting to the counters and being limited to two.
Jon,
Thanks. I'm glad you jumped in because having an actual example helps a lot for those of us who are not very experienced with this.
The C Learning tutorials are very detailed in their explanations and how-tos. Extending concepts taught to different sensors and devices is relatively straightforward, but going beyond the specific subjects covered, such as combining C with Spin or PASM, there are not any details on the how tos.
I assume that as time goes on there will be tutorials on more advanced concepts. But for now, asking questions and getting help from you experts (even when you have to use trial and error) are very helpful, particularly when there are examples to study.
My issue is that the Propellor is such an interesting device, that I keep jumping out of sequence with the C and Spin tutorials (and then there's all the different Forths and PASM). For example in the Prop Education Manual there is a section on A-D conversion using a potentiometer. So now, before I've done that section, I thinking about interfacing 3 pots to control the intensity of the 3 Leds in the rgb LED (and maybe use a couple of Xbees to do it remotely). I probably should learn to crawl before entering a marathon.
Thanks again
Tom
Again, I think if you stick to one language until it's very comfortable, translating code from other places will be less daunting.