Need more control over PWM for led fade.

Hi, i am doing a 3 RGB led lamp, the lamp fades up in a random color when the light in the room goes out, then shifts to a second random color, and after a prior of time it fades to off.
All the 3 leds shows the same color.
This is all working well, no problems :smiley:

The problem is that in the lowest range of the fade out, form (i guess) 20 to 0, the steps are clearly visible and disturbing, in particular the "brightness 1" is too bright, so when it switches to 0 the step in the dark is highly noticeable.
Part of the problem is that i use a HSB color library i found that uses a number from 0 to 99 for brightness, so i have less steps than direct PWM control, however 100 steps could be good.

Sure i can add a resistor on the Leds to have a dimmer "brightness 1", but this would also cut off on the maximum brightness.
Someone have an idea of how can i fix this?

Tnx

You could put in a 100-byte mapping table to get the taper you like.

with a mapping table (if i'm correct) i could change the linearity of the first 20-30 steps, that may be an improvement, but the biggest problem is the on/off effect of the steps 0/1, because 1 is too bright...

It looks like the 8 bit resoloution on the PWM is a bit low for you. The only soloution is to go for more bits of resoloution on the PWM and that will involve using an external chip to provide it. The TLC5940 is a popular choice, that shives you 12 bit resoloution.

Grumpy_Mike:
It looks like the 8 bit resoloution on the PWM is a bit low for you. The only soloution is to go for more bits of resoloution on the PWM and that will involve using an external chip to provide it. The TLC5940 is a popular choice, that shives you 12 bit resoloution.

Even 12 bits isn't really enough. 16 bits is noticeably better. You can do it in software.

You can do it in software.

What! 16 bit PWM in software? That is like 65k refreshes in 20 mS.

Is it possible to do 16bit PWM in software?
I planned to make the final project on a 328 with internal 8mhz clock, maybe i should get back to the external clock XD
The minimum duty cycle of 12/16 bit PWM is less than 8bit PWM?

TLC5940 seems interesting, it seems to be also a 16bit resolution version, but the package is not trough hole :frowning:

mactsk:
TLC5940 seems interesting, it seems to be also a 16bit resolution version, but the package is not trough hole :frowning:

The chips I have are through hole versions ??

You might also want to consider the PCA9685 - Adafruit make a nice board, albeit a bit expensive Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface [PCA9685] : ID 815 : Adafruit Industries, Unique & fun DIY electronics and kits

mactsk:
Is it possible to do 16bit PWM in software?

Of course!

mactsk:
TLC5940 seems interesting, it seems to be also a 16bit resolution version, but the package is not trough hole :frowning:

TI does make a 16 bit PWM chip. I don't remember the number but it's not a TLC5940.

Grumpy_Mike:
What! 16 bit PWM in software? That is like 65k refreshes in 20 mS.

Well, no, it isn't, because you would change the timer value on the fly rather than polling for it.

Actually, the point is that you only need this resolution for the dimmest part of the cycle, so what you actually do, is to use PWM for 1 mS, and then turn the LED off for 19 - or adjust these proportions as necessary.

If you only need 1 or 2 channels of 16-bit PWM you can use Timer1. At 16 MHz your PWM can go at 244 Hz. I guess at 8 MHz it would be 122 Hz which should still be plenty.

I need 3 PWM's since is a RGB lamp.
Is there a sample code or library to use higher frequency PWM?
it can be done on every pin?

mactsk:
Is there a sample code or library to use higher frequency PWM?
It can be done on every pin?

No, it can only be done on on pins associated with an Output Compare Register. Each of the three Timers has two OCR's so that is why the Arduino UNO only has six PWM pins. And only one of those timers is a 16-bit timer so four of those outputs are limited to 8-bit PWM which you say is inadequate. You might want to switch to an Arduino that has a processor more 16-bit PWM outputs. The Arduino Mega should almost certainly do the job.

The Arduino Leonardo chip (ATmega32u4) has two 16-bit timers and four PWM outputs with up to 16-bit resolution. It is possible that some of those outputs are not connected to Arduino pins or share a pin with something you need but it might be worth a look. It also has six PWM channels capable of 11-bit resolution so that might be enough.

mactsk:
Part of the problem is that i use a HSB color library i found that uses a number from 0 to 99 for brightness, so i have less steps than direct PWM control, however 100 steps could be good.

There are some good suggestions here, but have you tried changing this to 0-255 first to get smaller steps? How about not dimming to 0 at all, but stopping at 1 instead?

Just my thoughts, a temporary solution if you'ld like.

Paul__B:

Grumpy_Mike:
What! 16 bit PWM in software? That is like 65k refreshes in 20 mS.

Well, no, it isn't, because you would change the timer value on the fly rather than polling for it.

Actually, the point is that you only need this resolution for the dimmest part of the cycle, so what you actually do, is to use PWM for 1 mS, and then turn the LED off for 19 - or adjust these proportions as necessary.

That is my point. You can not have true 16 bit software PWM only a fudge.

mactsk:
I need 3 PWM's since is a RGB lamp.
Is there a sample code or library to use higher frequency PWM?
it can be done on every pin?

It's not rocket science, all you need to do is count to 65535 and turn the LEDs off when the counter passes their PWM value:

void PWM()
{
  cli();   // Don't interrupt me
  if (redVal >  0) digitalWrite(redLED,HIGH);
  if (greenVal > 0) digitalWrite(greenLED,HIGH);
  if (blueVal > 0) digitalWrite(blueLED,HIGH);
  unsigned int count = 0;
  while (++count < 65535) {
    if (count == redVal) digitalWrite(redLED,LOW);
    if (count == greenVal) digitalWrite(greenLED,LOW);
    if (count == blueVal) digitalWrite(blueLED,LOW);
  }
  sei();  // Allow interrupts again
}

Just call that function from your loop()

fungus:
It's not rocket science, all you need to do is count to 65535 and turn the LEDs off when the counter passes their PWM value:

Unfortunately doing it in software gives you a PWM cycle rate of about 8 Hz. Far too slow. That is why the PWM hardware is so useful.

const int redLED = 13;
const int greenLED = 2;
const int blueLED = 3;

unsigned int redVal, greenVal, blueVal;

void setup() {
  pinMode(redLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(blueLED, OUTPUT);
  redVal = 32767;
  greenVal = 32767;
  blueVal = 32767;
}

void loop() {PWM();}

johnwasser:

fungus:
It's not rocket science, all you need to do is count to 65535 and turn the LEDs off when the counter passes their PWM value:

Unfortunately doing it in software gives you a PWM cycle rate of about 8 Hz. Far too slow. That is why the PWM hardware is so useful.

I hadn't run the numbers before I posted that. I just looked at the disassembly and it takes about 24 clock cycles to go around the main loop. 64k loops is about 1.5 million clock cycles so 8-10Hz sounds about right.

You can probably tweak it a bit but it's not going to do 100Hz. :frowning:

But ... you can use it to do 13 or 14 bits. That's better than the 12 of a TLC5940! :slight_smile:

Another trick is to alternate the bottom bits on each PWM cycle. If you alternate between PWM=0 and PWM=1 you get PWM=0.5, it's like having an extra bit. Combine the two and you can get something that looks like 15-bit PWM in software. (This also works with a TLC5940, I've done it...)

As Mike pointed out earlier, brightness is logarithmic. 16-bit PWM will only give about 512 really different brightnesses. There's no reason your loop has to have equal sized steps as it counts. You only need high resolution when you're very close to zero.

I think there's room for a bit of creativity/research in software LED control.

Going back to the original problem, mood lamps usually have saturated colors so there's always at least one LED at full brightness, this helps hide the chunky steps of the other LEDs when they're close to zero.

I think 12 bits is enough to give the appearance of smooth changes for saturated colors. A TLC5940 can do that.

Yes also my mood lamp is saturated, but when that saturated color goes to black you have chunky steps...

The problem is really that this project is a "good night lamp" it senses when you turn off the main light in the bedroom and activates itself, and it needs to fade to absolute black after some time (and gradually) because in total darkness also a PWM of 1 in my case is really visible.

However i'll try with a TLC5940 and sacrificing a little bit of maximum output with a resistor the lower part of the fade should become adequate!

Creating a PWM of 0.5 is a good solution, but it requires some knowledge and skills that i really don't have at the moment, modifying the library to support 256 steps in intensity is probably easier, but it depends on how the library is written, my programming knowledge has stopped decades ago, with C on Amiga XD