Go Down

Topic: RGB led: can I put only 1 resistor at ground? (Read 14141 times) previous topic - next topic

oric_dan

#15
Nov 19, 2014, 09:34 pm Last Edit: Nov 19, 2014, 09:36 pm by oric_dan
Quote
I answered gilperon it is possible to use only one resistor if he/she multiplex the RGB LEDs.
That's a nice clever hack, but I figure new people need grounding in the basics first. Secondly, you're trading off using two 1-cent Rs against 2 days or so of coding, especially for a new programmer.

Thirdly, multiplexing makes much more sense if you're using more Leds than you have I/O pins available. Fourthly, it takes quite a lot of cpu cycles to execute proper multiplexing [ie at rates above the flicker-fusion frequency of human vision], and that bites into resources available to do everything else - especially if interrupts are used.

Fifthly, it's easy to simultaneously PWM the 3 channels of the RGB, if separated by 3 Rs, and get 256 hues and 256 or so brightnesses, which is a major reason people use RGBs in the first place. Trying to do this via any multiplexing scheme with only one R would be a real dog to code, and hardly worth the effort [except maybe for bragging rights].

All these things should be explained to a beginner, so they understand the issues on each side.

rlogiacco

#16
Nov 20, 2014, 12:50 am Last Edit: Nov 20, 2014, 12:52 am by rlogiacco
I consider myself a beginner and while I do understand it might be simpler to wire 3 resistors I do understand Pelleplut point.
On top of that having a software background for me might be easier to code the PWM rather than walk to the local shop and pay 0.50 for 3 resistors (that's the price I get for low volumes locally).

Now, being a beginner I say the answers "no, you can't" are not making a beginner's life easier, especially when, later on, you start understanding you can do it! If you want to make life simpler to the guy because you think he is a beginner then just say "you can, but it's a lot more complicated than just using 3 resistors and involves some complicated coding we advise against", but when someone else comes in and says "it can be done" then you shouldn't state that he, or his suggestion, is stupid, simply because it's not and it's answering the exact question!

I'm here to learn and I believe Pelletplutt gave his perspective to the question: it's a valid and working solution. The OP never asked for the cheapest solution, and even if he did, it would be a lot cheaper for me to write down the code and use 1 resistor... not everybody has a shelf full of all kind of passives you pros certainly have. And because I've already spent 20$ on my Arduino board and it's sitting there doing nothing I might prefer to have it's loop function doing something better than just running delays over and over.

Now, I'm not saying I would go for Pelleplut's solution, but certainly I do not consider what he said neither stupid nor wrong. It was actually wrong and pretentious to say it was stupid, as much as it is wrong to keep saying it's a less valid solution because of OP knowledge, resource usage and such... the question never mentioned that.

Please, do not send me personal messages containing forum related questions: I will not answer.

I share my discoveries and thoughts at http://rlogiacco.wordpress.com

oric_dan

Well, cool. You can settle one point. See how long it takes to actually code the multiplexing scheme, and I mean actually get multiple LEDs to work in some reasonable way, and not simply code up a simple  for...loop, and say it works.

Easy enough. Now, inject that routine into another program that is doing something non-trivial, that has some other timing constraints, and things start to get more interesting. You're essentially writing your own simple multitasker just to keep your LEDs from flickering.

Now try to change the RGB hues and intensities to given values using your multiplexing scheme, and then report back how much work the 3 programs took to accomplish. Now, you're learning quite a lot by this time.

polymorph

Keeping in mind that, due to all LEDs sharing a single resistor, you cannot have any two LEDs on at the same time. So if you want each color to have a full range of brightness regardless of the other colors states, each can only have a maximum of 1/3 duty cycle.

Which means you will probably need to boost the peak current. As even 40mA is too much to draw from an Arduino pin (never use the full ratings, always derate by 2x or more), you'll need driver transistors. But those require current limiting resistors on their bases.
Steve Greenfield AE7HD
Drawing Schematics: tinyurl.com/23mo9pf - tinyurl.com/o97ysyx - https://tinyurl.com/Technote8
Multitasking: forum.arduino.cc/index.php?topic=223286.0
gammon.com.au/blink - gammon.com.au/serial - gammon.com.au/interrupts

oric_dan

#19
Nov 20, 2014, 01:57 am Last Edit: Nov 20, 2014, 02:00 am by oric_dan
This multiplexing thing all sounds pretty darn simple .... until you actually try to get it to work, and work well. That's the real proof of the pudding.

dlloyd

RGB LED PWM Example (R=25, G=100, B=250):

If using 3 resistors:

Code: [Select]
int pinR = 9;   // LED R
int pinG = 10;  // LED G
int pinB = 11;  // LED B

byte valR = 25;
byte valG = 50;
byte valB = 250;

void setup() {
}

void loop() {
  analogWrite(pinR, valR);
  analogWrite(pinG, valG);
  analogWrite(pinB, valB);
}


If using 1 resistor on GND terminal, RGB multiplexed at 125Hz, 1 extra line of code:

Code: [Select]
int pinR = 9;   // LED R
int pinG = 10;  // LED G
int pinB = 11;  // LED B

byte valR = 25;
byte valG = 50;
byte valB = 250;

void setup() {
}

void loop() {
  byte x = (millis() / 8) % 3;
  analogWrite(pinR, valR * ((x == 0) ? 1 : 0));
  analogWrite(pinG, valG * ((x == 1) ? 1 : 0));
  analogWrite(pinB, valB * ((x == 2) ? 1 : 0));
}

oric_dan

That's actually pretty good, finally an example of something to look at. You've created the makings of a simple multitasker. Now, try interjecting your code into a real program that does something real and useful, as described in reply #18. [or at least trigger this thing off a Timer interrupt].


dlloyd

#22
Nov 20, 2014, 07:15 am Last Edit: Nov 20, 2014, 08:18 am by dlloyd
Quote
That's actually pretty good, finally an example of something to look at. You've created the makings of a simple multitasker. Now, try interjecting your code into a real program that does something real and useful, as described in reply #18. [or at least trigger this thing off a Timer interrupt].
Points made in previous replies (re multiplexing) are valid.

  • Normal hardware PWM with RGB LED and 3 resistors is immune to blocking code and delays.
  • Multiplexed PWM with RGB LED and 1 GND resistor relies on the iteration speed of the loop. Luckily the human eye will smooth out anything over 30Hz, so if the loop speed is resonably faster than about 100Hz, your eye shouldn't detect any flicker or jitter in color changes.

Wish I had an RGB led to test ... I just used 3 red LEDs.

The main loop speed of the multiplex code was 97ms for 10,000 iterations on the Arduino Due.

  • If interjecting the code in the "blink" sketch, the multiplexing stops for the delay interval.
  • If interjecting the code in the "blink without delay" sketch, there is no observable flicker or pauses on the external LEDs (RGB).  The board's LED on pin13 blinks as normal, unaffected by the RGB multitasking code.

Quote
[or at least trigger this thing off a Timer interrupt].
Would the purpose here be to make the multiplexing immune to blocking code? If so, at this point it's beyond the scope of my experience (looking to correct that when I get the time).


Taking it further, I guess more LEDs could be added like this, but not sure what the practical limit would be:




oric_dan

Yeah, BlinkWithoutDelay only keeps proper timing if the main loop is never bogged down or delayed by any other code. So, any routines that hog the loop for longer than your led update rate will boggle the led timing, and cause flicker. And as your program grows more complex, the problem will only get worse. You have to scrutinize everything you add to the program.

You can get around this by using a Timer interrupt. Not hard to do, go look in the Arduino reference notes. It's around somewheres. And try here,
http://www.gammon.com.au/forum/?id=11504
Just be sure to declare all variables accessed within the interrupt routine as 'volatile'.

The big problem, however, is that every 2 PWM channels also co-opt one timer, and a UNO ATmega328 only has 3 timers. Timer0 is used by millis(). And your 3 RGB analogWrite() calls already use the last 2 timers. So, with a UNO, you're already toast for adding timer interrupts.

For reference: Timer0 PWM on D5,D6. Timer1 PWM on D9,D10. Timer2 PWM on D3,D11.

And also unfortunately, the PWM pins D10,D11 are also used for the SPI peripheral, so that's toast too. Anymore, SPI is one of the most popular peripherals; I use it in every application. And horror of horrors, PWM on pin D10 co-opts Timer1, while PWM on D11 co-opts Timer2. If you want both millis() and also SPI, you lose "all" PWM capability for the 328. [IOW, Atmel REALLY screwed up with their PWM pin assignments on the 328. What a dog].

Other than that, good thinking. It's just that all this is like trying to stuff 25# in a 5# box.



Docedison

@ oric_dan, well said.
Until you have to go in the field and back up your talk/code you're still playing with paper doilies.
The proof of the pudding, is the way it really works after the first code revision/patch.

Doc
--> WA7EMS <--
"The solution of every problem is another problem." -Johann Wolfgang von Goethe
I do answer technical questions PM'd to me with whatever is in my clipboard

dlloyd

#25
Nov 22, 2014, 09:06 am Last Edit: Nov 22, 2014, 09:29 am by dlloyd
I've tried this method in four or five examples, and I think a very useful application for this would be a "heartbeat" led for Arduino boards or compatibles. For this application, no timer interrupts required ... we need the RGB to respond to iteration speed.

For fast and smoothly performing code, the led glows white. As the code slows down, some flickering occurs and color changes start becoming apparent. If there's substantial delays, the led will hesitate on a random color.

The user immediately gets a visual picture of the performance of their code.

Here, I've suggested the Arduino board's LED, however an RGB LED gives a much better visual range for main loop speed:
http://forum.arduino.cc/index.php?topic=280201.0

oric_dan

#26
Nov 22, 2014, 08:38 pm Last Edit: Nov 22, 2014, 08:40 pm by oric_dan
Yeah, it occurred to me the other day, your scheme would make a reasonable indicator for how much time the rest of the program code takes. The more cycles burned, the worse the Led flicker.

But I was hoping you'd come back after 2 days, and have made some progress on getting the multiplexing scheme to actually work. The other guys who were also pushing its merits just disappeared in a puff of smoke.

Basically, it's exactly what I said. People weren't realizing they'd be spending a couple of days getting s.w. to work, just in order to save 2-cents for extra resistors. But it would be a good challenge as a "learning" experience.

dlloyd

Quote
But I was hoping you'd come back after 2 days, and have made some progress on getting the multiplexing scheme to actually work. The other guys who were also pushing its merits just disappeared in a puff of smoke.
The multiplexing works, but I think you mean "multitasking" (see below)

Quote
Basically, it's exactly what I said. People weren't realizing they'd be spending a couple of days getting s.w. to work, just in order to save 2-cents for extra resistors. But it would be a good challenge as a "learning" experience.
With my experience level at C++ ... could I have more than 2 days please?
I'm on par with this guy who took a lifetime to come up with this (you know, for kids).



oric_dan

#28
Nov 23, 2014, 04:26 am Last Edit: Nov 23, 2014, 04:28 am by oric_dan
Quote
The multiplexing works, but I think you mean "multitasking" (see below)
Well, I don't consider a toy demo to really mean "working". In fact, far too many of the examples that come with Ardunio libraries are just toy demos, and are barely useful for anything.

Quote
With my experience level at C++ ... could I have more than 2 days please?
Well, I would go with the timer interrupts. You just have to make some tradeoffs. Cannot do it all on a UNO-328 as mentioned, so decide what to give up (ie, 1 or 2 pwm channels), and (a) get it to work first. Later on, you can always (b) change the tradeoffs to best advantage.

It would take some wrangling, but you could actually (c) do s.w. pwm on 1 or more channels by varying the timer interrupt occurrence times, but I would just get it to basically work first. Also, you might want to slow down the interrupt rate from every 8-msec. That is hogging a lot of processor time. Eg, a 40-Hz rate is probably adequate to not see flickering, as the human flicker-fusion frequency is about 20-30 hz.

ph77

Multiplexed PWM for driving a RGB led? Challenge accepted!

Took about 15 minutes to write :)

Code: [Select]

// Multiplexed PWM RGB Led Demo
// By Petri Hakkinen
// 24th November 2014
//
// Arduino driving a single RGB led with only one resistor.
//
// Make the following connections:
// * Connect Arduino pins D2,D3,D3 to anodes of a RGB led.
// * Connect cathode of the RGB led to 330 ohm resistor.
// * Connect the other end of the resistor to ground.
//
// A multiplexed PWM signal is generated by a timer interrupt routine.
//
// Uses TimerOne library, download from here:
// http://playground.arduino.cc/Code/Timer1

#include <TimerOne.h>

volatile uint8_t color[3]; // R,G,B led intensities in range [0,255]
volatile uint8_t phase = 0; // phase of the PWM signal, counts from 0 to 255
volatile uint8_t led = 0; // which led to update, counts from 0 to 2

// Look up table of sin values for mega demo effect.
uint8_t sintab[64];

// The interrupt routine that updates the states of leds.
void interruptRoutine()
{
// turn all leds off
PORTD &= B11100011;

// sample pwm and turn on one led
if(phase < color[led])
PORTD |= 1<<(led+2);

led = (led + 1) & 3;
phase++;
}

void setup()
{
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);

// attach interrupt routine
Timer1.initialize(50);
Timer1.attachInterrupt(interruptRoutine);

// init sin table for mega demo effect
for(uint8_t i = 0; i < 64; i++)
sintab[i] = (uint8_t)(sin(i * M_PI * 2 / 64) * 127.0f + 128.0f);
}

void loop()
{
const uint8_t speed = 13;

color[0] = 0;
color[1] = 0;
color[2] = 0;

// fade red
for(int i = 0; i < 256; i++) {
color[0] = i;
delay(speed);
}

// fade green
for(int i = 0; i < 256; i++) {
color[1] = i;
delay(speed);
}

// fade blue
for(int i = 0; i < 256; i++) {
color[2] = i;
delay(speed);
}

// mega demo effect
for(int i = 0; i < 500; i++) {
color[0] = sintab[i & 63];
color[1] = sintab[(i*2 + 123) & 63];
color[2] = sintab[(i*2/3 + 35) & 63];
delay(speed);
}
}

Go Up