Go Down

Topic: Need more control over PWM for led fade. (Read 10580 times) previous topic - next topic

fungus


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:

Code: [Select]

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()
Advanced Arduino

johnwasser


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.

Code: [Select]

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();}


Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp
See who has no social life: https://forum.arduino.cc/index.php?action=stats :)

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. :-(

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

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.
Advanced Arduino

fungus

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.

Advanced Arduino

mactsk

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

Headroom

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!

Headroom

Another thing that came to  ind is that if actually do want to use 16 bit, the Teensy3 http://www.pjrc.com/teensy/index.html has 10 pins that can do 16-bit PWM http://www.pjrc.com/teensy/td_pulse.html. Not bad for $20!

fungus


Another thing that came to  ind is that if actually do want to use 16 bit, the Teensy3 http://www.pjrc.com/teensy/index.html has 10 pins that can do 16-bit PWM http://www.pjrc.com/teensy/td_pulse.html. 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.
Advanced Arduino

fungus



Another thing that came to  ind is that if actually do want to use 16 bit, the Teensy3 http://www.pjrc.com/teensy/index.html has 10 pins that can do 16-bit PWM http://www.pjrc.com/teensy/td_pulse.html. 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.

Advanced Arduino

fungus


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: https://www.adafruit.com/products/1455

That looks ideal for a mood lamp.

Advanced Arduino

Headroom

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!


fungus


... 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.
Advanced Arduino

mactsk

#27
Nov 11, 2013, 07:27 pm Last Edit: Nov 11, 2013, 07:36 pm by mactsk Reason: 1
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!

fungus

Advanced Arduino

mactsk

I managed to create some frames, or PWM steps, the code is probably a bit messy and inelegant, but i don't know how to make it look better, however it works quite well, effectively creating a half stepping for led PWM

it defines a time frame, in the first half it simply loops with the same PWM output, in the second half of the frame it flickers between a PWM value and PWM-1 (if it does not get to -1)

here is that part of the code.
suggestions and comments are welcome!

Code: [Select]
void fadeDown(int values[]) //values[] is an array of int H,S,B
{

  int pause=30;  //"substeps" lasting time
  unsigned long time_delay=400; //duration of each macro PWM step
  unsigned long target; // clock time to reach for each PWM step
  while(values[2]>=0) //values[] is H,S,B values[2] is the brightness, it starts at 99 so do the loop untill it's black(0)
  {

    target=millis()+time_delay; //calculate when the current macro PWM step ends
   
    while(millis()<target) //this is the main loop for the PWM step
    {
      while(millis()<target-(time_delay/2)) //while we are in the first half of the macro PWM step...
      {
        H2R_HSBtoRGB( values[0],  values[1], values[2], color); //set the rgb values for that color
        ledsOn(); //write the rgb values to the leds
        Serial.println(values[2]); //print for debugging
        delay(pause);
      }
     
      // if we are here, the first half of the frame was done, now we flicker between the current brightness and the next (-1)
     
      H2R_HSBtoRGB( values[0],  values[1],  values[2],  color); //set the rgb values for that color
      ledsOn(); //write the rgb values to the leds
      Serial.print(values[2]); //print for debugging
      Serial.print(" "); //print for debugging
      delay(pause);
      if(values[2]>0) //if the brightness is more than 0 we can continue
      {
        H2R_HSBtoRGB( values[0],  values[1],  values[2]-1,  color); // temporary set "values[2]-1" and calculate color so it dims
        ledsOn();//write the rgb values to the leds
        Serial.println(values[2]-1); //print for debugging
        delay(pause);
      }
    }
    --values[2]; //the loop ended so we can subtract 1 and continue the loop
  }
}

void ledsOn()
{
  analogWrite(ledr, color[0]);
  analogWrite(ledg, color[1]);
  analogWrite(ledb, color[2]);
  ledstatus=1;
}

Go Up