IntervalTimer and LED fading

Status
Not open for further replies.

stephanschulz

Well-known member
i am porting code that used to run on a maple mini.

in it i set a timer overflow which called a function that caused 16 LEDs to be set to a specific brightness

i am now trying to use the teensy3 IntervalTimer but the LEDs flicker.

in the maple code i used a library called digitalWriteFaster, maybe that helped?
http://forums.leaflabs.com/topic.php?id=737&page=2#post-25151

but maybe i am just doing some wrong ???

thanks, stephan.
maple code:
Code:
setup(){
Timer3.setPrescaleFactor(1);
  Timer3.setOverflow(255); //2000); //500); //1079);
  Timer3.attachCompare1Interrupt(timer3_dimming_handler);
}

void timer3_dimming_handler(void) {
  for(int i=0; i<DMX_NUM_CHANNELS; i++){

    if(dmx_data[i] > counter){
      dimStates[i] = true;
    }
    else{
      dimStates[i] = false;
    }
    
    digitalWriteFaster(ledPin[i],dimStates[i]);
  }

  counter++;
  if(counter > counterLimit) counter = 0;
}

teensy code:
Code:
void setup(){
  for(int i=0; i<DMX_NUM_CHANNELS; i++){
    pinMode(ledPin[i],OUTPUT);
  }

  dimmingTimer.begin(dimming_handler, 100);  
}

void dimming_handler(void) {
  for(int i=0; i<DMX_NUM_CHANNELS; i++){

    if(dmx_data[i] > counter){
      dimStates[i] = true;
    }
    else{
      dimStates[i] = false;
    }
    
    digitalWrite(ledPin[i],dimStates[i]);
  }
  counter++;
  if(counter > counterLimit) counter = 0;
}
 
I ran your code just now. Yup, it flickers.

You've got the interval timer set to 200 us, which is 5000 Hz. But you're counting to 255 in the handler for a single PWM cycle. So your PWM carrier frequency is only 19.6 Hz.

Just changing the interval timer to 40 us makes the flicker go way, because that's about 98 Hz.

Unfortunately it also makes the interrupt handler consume almost 50% of the CPU time. Nearly all that time is spent inside digitalWrite, which is being called 16 times on every interrupt. The LED states are computed and stored too. But each LED only changes state twice every 255 interrupts. Here's a minor edit to the code which reduces the interrupt to approx 6% of the CPU, while still running at flicker-free 98 Hz PWM waveforms.

Code:
// Create an IntervalTimer object 
IntervalTimer dimmingTimer;

unsigned int counter = 0;

//int ledAmt = 16;
const int counterLimit = 255; //160;

//volatile boolean dimStates [] = {
//  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

volatile  int dimDir [] = {
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
volatile int dimValue [] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

const char ledPin[] = {
  1,2,3,4,5,6,7,8,9,10,11,14,15,16,17,18};

//15 ,16 ,17 ,18 ,19 ,20  ,21  ,22  ,27 ,28  ,29  ,30  ,31  ,0   ,1   ,6
//b7 ,b6 ,b5 ,b4 ,b3 ,a15 ,a14 ,a13 ,a8 ,b15 ,b14 ,b13 ,b12 ,b11 ,b10 ,a5 

void dimming_handler(void) {
  digitalWriteFast(0, HIGH);

  for(int i=0; i<DMX_NUM_CHANNELS; i++) {
    if (dmx_data[i] == counter) {
      digitalWrite(ledPin[i], LOW);
    }
  }

  counter++;
  if(counter > counterLimit) {
    for(int i=0; i<DMX_NUM_CHANNELS; i++) {
      digitalWrite(ledPin[i], HIGH);
    }
    counter = 0;
  }
  digitalWriteFast(0, LOW);
}

void setup_dimming(){
  pinMode(0, OUTPUT);
  for(int i=0; i<DMX_NUM_CHANNELS; i++){
    pinMode(ledPin[i],OUTPUT);
    // dimValue[i] = counterLimit/DMX_NUM_CHANNELS * i;
  }

  dimmingTimer.begin(dimming_handler, 40);  
  // 150000 = blinkLED to run every 0.15 seconds


//  Timer3.setPrescaleFactor(1);
//  Timer3.setOverflow(255); //2000); //500); //1079);
//  Timer3.attachCompare1Interrupt(timer3_dimming_handler);
}

This could be optimized even more by using the digitalPinToPort and similar macros and then doing direct I/O, instead of calling digitalWrite(). But hopefully this at least gives you a good start to making this run fast enough?
 
thank you very much for this.

it does remove the flicker.

i also edited the softPWM library and changed in softPWM.cpp this line #define SOFTPWM_FREQ 90UL //60UL
this also removed the flicker. but i am not sure what this means for the cpu load.
how did you calculate the cpu % ?

i am guessing it would be best to use regular analogWrite for those pins that allow for it and softPWM for the digital pins. right?

the next step is to find a way to see the lights flicker free through a camera.
adjusting the intervalTimer certainly changes how the camera sees the led flicker, but it never removes the flicker completely.
would i have to use a proper DAC with analog voltage to properly feed my led driver?
 
Last edited:
i am noticing that your code produce sometimes a flicker when the counter reaches one of it's limits.

i modified my code to only write to the digital pin when there needs to be a change.
would this be also less load on the cpu ?

Code:
void dimming_handler2(void) {
  for(int i=0; i<DMX_NUM_CHANNELS; i++){

    if(dmx_data[i] > counter){
      dimStates[i] = true;
    }
    else{
      dimStates[i] = false;
    }


    if(dimStates[i] != old_dimStates[i]) {
      old_dimStates[i] = dimStates[i];
      digitalWriteFast(ledPin[i],dimStates[i]);
    }

  }

  counter++;
  if(counter > counterLimit) counter = 0;
}
 
Last edited:
how did you calculate the cpu % ?

I put a digitalWriteFast() at the beginning and end of the handler, and then used a DC voltmeter to measure the average voltage on a pin.

the next step is to find a way to see the lights flicker free through a camera.

That's easy, you simply need the camera's shutter speed to be approximate in the range of a human's persistence of vision, like about 1/10th of a second. Most cameras have a shutter priority mode, but if you have a lot of light shining directly into the lens, there are limits to how slow it can set the shutter speed.

To photograph really bright stuff, they sell neutral density filters to reduce the light so your camera doesn't need to use extremely small aperture settings... which apparently have some disadvantages, though that's getting beyond my limited photography knowledge. I do know I had to buy one of those little filters for my camcorder when I shot the OctoWS2811 video, where 1920 LEDs were pointing right at my poor little camcorder.
 
thanks.

for the project i am working i have no access to the camera, since it will be people's smart phone cams.
sure the smart phone app could lock the camera's shutter speed. which i will try for sure.

but it would be great if there isn't any flicker at all from the start.

i did some tests today and noticed if i use a WS2801 and feed it serial data, it outputs PWM to dim LEDs. This PWM seems to be faster then what my teensy can output, since i do not see any flicker through the camera.

i attached the WS2801 to this high bright LED driver https://www.sparkfun.com/products/9748.

this makes me think i should build something similar to LED light strips and use your great OctoWS2811 LED Library.
in my case i need to address 16 leds per fixture and each fixture is 3 meters apart, with a total of 255 fixtures.
not sure how well the WS2801 and similar devices behave with longer cable lengths. do you :) ?
 
Last edited:
Perhaps it's time for you to disclose the full content of what you are trying to achieve. I am not sue why you are even using the softPWM library. The Teensy3 has 10 hardware PWM pins with a default PWM frequency of 488Hz these can dim up to 16 bit. That would, however, unlikely work with Several LED fixtures that are eAch 3m apart from each other.
 
All PWM-driven LEDs flicker at some frequency, because the LED rapidly is turning on and off. That's how pulse width modulation works.

The only way to get absolutely zero flicker is the drive the LED with an analog circuit. Unfortunately, that usually involves using a pretty substantial number of parts, though they can be very cheap, like a LMV358 opamp.

analogled.jpg
(click for larger)
 
@PaulStoffregen
i will give this a try too.
but it was surprising to not see a flicker when i used teensy -> WS2801 -> rebel led driver -> high amp led.
that why i thought the WS2801 must work at a higher frequency, so the on/off happens faster then visible to eye and camera.
but i do like the idea of controlling the rebel led driver with an analog voltage.


@Headroom
i am working on updating this artwork with a new arrangement of the leds and more spaced out placement. as mentioned the new version needs to display no flicker when someone views it via their camera.
currently we are using dmx - rs485 ic->maple mini->rebel led driver-> high amp
 
here is the schematic.
led:Cree - XPCWHT-L1-0000-008E7
led driver: ZXLD1350ET5TA

led_circuit.jpg

it is similar to the rebel driver

it's interesting to hear from paul that his led strips flickered when seen through a camera. which disproves my theory of these drivers having a super high frequency pwm.
i will do some more tests and see.
 
i am still pursuing this quest of getting flicker/band free LED dimming.

at this point i am trying to use teensy -> TLC59711 -> ZXLD1350.

connecting the tlc output directly to the adjust pin on the ZXLD1350 did not do anything.

so i thought using an optocoupler could work.
but using this optocoupler 6N137 did not work. http://www.fairchildsemi.com/ds/6N/6N137.pdf

the tlc works fine, because as a test i connected a LED to the opto's output Vo, which dimmed just fine.

i also took the tlc out of the equation and feed a PWM from the teensy in to the opto's Vf, which dimmed the ZXLD1350 circuit fine.

this is really strange.
maybe there is a problem with the frequency the TLC bangs the opto ?

thanks.

Screen Shot 2013-12-20 at 4.42.08 PM.png
 
I do not understand why you're using a 16 bit PWM chip. That makes absolutely no sense to me. With digitally generated PWM, higher resolution means dividing the main clock by a bigger number, which is 65536 for 16 bit PWM.

On a Teensy3, try using analogWriteFrequency() to set the PWM output to a higher frequency. There is a fundamental trade-off between frequency and resolution. This page has a table:

http://www.pjrc.com/teensy/td_pulse.html

I know you probably want many more PWM pins, but at least you can use Teensy 3.0 for testing. If a higher frequency PWM (with less resolution, like 8 to 10 bits) works, at least you'll know that part of the problem is solved.

Who knows, maybe for this installation a workable solution might be to use the 12 PWM outputs from a Teensy 3.1 and create a chain of them using I2C or Serial1, Serial2, etc.
 
for sure i will try analogWriteFrequency(), which i did not know about.

it all started when i came across the TLC5940 and noticed that my LEDs did not display any flicker/bands. that's why i am still using it, since using the PWM from the teensy did display bands.
but maybe altering the frequency will do the trick. honestly, i thought i was already doing that by adjusting the intervalTimer when doing the manual PWM.

still have to figure out how to make the teensy do open collector/drain PWM.
thanks.
 
i finally had a chance to test analogWriteFrequency. and true enough my camera does not see any flicker during the dimming curve.
BUT now the leds using the ZXLD led driver turn off earlier then before; i.e. the dimming in to the darker region becomes not available. with a regular LED it works fine.
it must have something to do with the new frequency and how the LED driver reads it.

but using this LED driver based on the AL8805W5 does dimming fro 0 - full brightness and has no flicker: https://www.sparkfun.com/products/11850

i.e. more research. and i know now that the teensy is great and not the problem :)
 
Last edited:
It is fairly normal for LED drivers to have a reduced dimming range and reduced duty cycle range when the PWM frequency is raised.
The two chips you are using already don't have a particularly high dimming range. If you're not afraid to do some soldering this board from Tindie is probably a much better fit as the PT4115 has a 5000:1 dimming ratio, however at 120Hz PWM that is. Here also, if you use 500Hz the dimming ratio will go down very sidnificantly.
 
Just looked at the ZXLD driver spec sheet. It's got a max of 100:1 dimming range (I am assuming at 100Hz). The AL8805W5 is a much better chip with a 10bit resolution when a PWM frequency under 500Hz is used ( without really mentioning how much under).

I am not making much sense out of your statement "with a normal LED it works fine" what is a "normal" LED ?
What PWM frequency did you use ?

That little bit of code that you did not post would help ;-)

Also, having read through this thread again, keep the LEDs close to the drivers. Several inches is better than several feet away.
 
thanks for taking a look at this.

by normal LED i meant something like this https://www.sparkfun.com/products/9590 without a dedicated led driver, connected straight to the PWM pin on the teensy.

i need to set analogWriteFrequency to at least 3000 (Hz) otherwise i see bands/flicker when looking at the LEDs through a cellphone camera.

that's why the ZXLD stops responding during the darker phases of the dimming curve. since everything according to the datasheet:
"Adjustment range 25% to 100% of IOUTnom for f>10kHz and 1% to 100% of IOUTnom for f<500Hz"
With 3000 Hz i am somewhere in between.

so, you think the PT4115 will be better then the AL8805W5 ?

also. that's why i am also trying to use the TLC to do the PWM and have it's output drive the led-drivers.

Code:
uint8_t pwmLeds[4] = {
  5,6,9,10};

unsigned long fadeTimer;
int fadeValue = 0;
int fadeDir = 10;

unsigned int maxres[15] ={
  3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
int bits[15] = {
  2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
long feq[15] = {
  12000000,6000000,3000000,1500000,750000,375000,187500,93750,46875,23437,11718,5859,2929,1464,732};

int pick = 10; // 10/11 no flicker
int pwmMax = 255; //8191; //maxres[pick]; //511; //1023; //127;
int pwmResBit = 8; //13; //bits[pick]; //9;//10; //7;
long pwmFeq =  3000; //feq[pick]; //93750; //46875; //375000;

void setup(){
  Serial.begin(9600);

  analogWriteResolution(pwmResBit);

  for (int i = 0; i < 4; i++){
    //375000 = 375kHz
   analogWriteFrequency(pwmLeds[i],pwmFeq);
  }
}


void loop(){

  if(millis() - fadeTimer > 5){
    fadeTimer = millis();
    fadeValue = fadeValue + fadeDir;
    Serial.println(fadeValue);
    if(fadeValue >= pwmMax){
      Serial.print("max ");
      Serial.println(fadeValue);
      fadeValue = pwmMax;
      fadeDir = -1;


    }

    if(fadeValue <= 0){
      Serial.print("min ");
      Serial.println(fadeValue);
      fadeValue = 0;
      fadeDir = 1;
    }

    for (int i = 0; i < 4; i++)
      analogWrite(pwmLeds[i], fadeValue);
  }


}
 
when looking through a cell phone camera, you'll see a heterodyne or beat frequency - same effect with a video/film camera and wagon wheels.
And more commonly, the frame rate difference between standard movie film and video cameras. To solve this, they have sync cables and schemes to keep the two running in an integral multiple of each other, ideally 1:1, but often not.
The frame rate of a cell phone cam is likely different when actually recording than when previewing on the viewfinder.

The laws of physics at work!
 
At 3000Hz any LED driver is going to have very reduced dimming range and will often have issues with a limited duty cycle range. As Steve has already mentioned, the laws of physics are at play here :)

Your only bet to avoid any flickering problems is to use Analog Dimming. However, as that is exactly not the most common ly used dimming method for color fading you will have problems finding LED driver chips with much of a dimming range.

The LM3409Q/LM3409QHV has an Analog dimming range of 250:1 and you could control that for example with an MCP4278 DAC. But then we are not talking about the cheap ( and somewhat overpriced) driver stuff from Sparkfun anymore but about a fully custom designed driver, allthough there is a open source design available for that chip.
 
Status
Not open for further replies.
Back
Top