Need more control over PWM for led fade.

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

The steps come from a nonlinear response of the human eye to changes in brightness.
If an LEDs light output would be by some strange coincidence matched to that, 256 steps or 8-bit per color channel would provide you with smooth dimming.
The article here http://neuroelec.com/2011/04/led-brightness-to-your-eye-gamma-correction-no/ explains this fairly well. In order to linearize it is fairly common to use a lookup table that maps the 256 steps onto a 12-bit map.

I use that approach in my lighting systems ( see signature for links) and it provides very smooth fading using 265 steps per color channel. Only when fading very, very slowly at ranges vevery close to zero have I been able to detect steps. So in essence the speed which with you fade also has to do with the ability to detect steps. Having 12-bit dimming available I can still use a 10-bit lookup table to correct that behavior.

16 bits are nice if you want to use arbitrary scaling, say if you want to be able to only use only a quarter of the dimming range of your LED but still want to be able to smoothly fade, however is not really necessary at all in my experience. More is not necessarily better!

Another thing that came to ind is that if actually do want to use 16 bit, the Teensy3 Teensy USB Development Board has 10 pins that can do 16-bit PWM Pulse Width and Tone on Teensy with Arduino. Not bad for $20!

Headroom:
Another thing that came to ind is that if actually do want to use 16 bit, the Teensy3 Teensy USB Development Board has 10 pins that can do 16-bit PWM Pulse Width and Tone on Teensy with Arduino. Not bad for $20!

Arduino has two 16-bit pins so another way is to use two Arduinos. You can get Pro-Minis for under $4.

fungus:

Headroom:
Another thing that came to ind is that if actually do want to use 16 bit, the Teensy3 Teensy USB Development Board has 10 pins that can do 16-bit PWM Pulse Width and Tone on Teensy with Arduino. Not bad for $20!

Arduino has two 16-bit PWM pins so another way is to use more Arduinos to get extra pins. You can get Pro-Minis for well under $4 each.

Headroom:
The steps come from a nonlinear response of the human eye to changes in brightness.

The final brightness step between 'pwm=1' and 'pwm=0' is very hard to eliminate. With 12 bit resolution it's still very easy to see.

I just looked on Adafruit and they do TI's 16-bit PWM chip on a little board: Adafruit 12-Channel 16-bit PWM LED Driver - SPI Interface [TLC59711] : ID 1455 : $7.50 : Adafruit Industries, Unique & fun DIY electronics and kits

That looks ideal for a mood lamp.

I re-read the OPs original post, and in that application when you fade in all color channels from zero that step is detectable, I agree. For the OPs intended purpose I'd agree that the little breakout from Adafruit seems ideal. Had not seen that one before, so thanks for posting it!

Headroom:
... the little breakout from Adafruit seems ideal. Had not seen that one before, so thanks for posting it!

They seem to have something new every time I look on there.

I had an idea, i have put three big capacitors (1000uf) in each color channel (r,g,b) now the effect is much better than before since they act as a buffer for the PWM, the only little downside is that there is an additional fade out when stepping from PWM 1 to 0, since the capacitors have to discharge, but i can probably match the fade out and capacitor size to obtain an even better effect, however right now it's quite good!

good idea!