Flickering while controlling a lot of LEDs

I have a boost gauge project that converts a voltage signal into a number. This number gets displayed on one of two 3-digit-7-segment displays. The other display will be a temporary peak reading.

I am using two MAX7219s to control all of this. There are 28 bicolor LEDs (so technically 56 leds there) and the 6 total 7-segment digits. ( 48 LEDs because there are DPs)

Anyway, the numbers displayed are flawless. But I have an annoying problem with the remaining LEDs. They flicker. Pretty bad. They are constantly lit (perceptively) but it is an eyesore and doesnt look professional.

I am pretty sure it is my coding. I have given up for a while because I have ran out of ideas to fix this. If I manually light all the LEDs without using the rest of the code, they do not flicker.

I hope I am not maxing out the “processing power” of the MAX7219s. This project was designed around them.

Pseudo version of my code:

For loop at the beginning cycles through all the values that would be converted from the sensor

  1. Value is received. Sent to the 7-segment boost display function. The integer is broken down and displayed.

  2. Based on what the signal integer was, I mapped it in a looonnggg sequence of if statements. This lights up the appropriate LEDs

  3. Not added: A peak boost function I wrote for another working project.

I think this is very inefficient and my inexperience leads me to no better route. I even added a delay thinking the entire loop was the problem. Nope.

Please help me kill this flickering by fixing the long “if sequence”. Or finding what is really wrong.

Video of said flickering…

//This is the std needle that sweeps from one end to the other.  Sweep is in red.  MAX boost changes color to green
void needle(int X) {
  
  if(abs(Xprev-X)>10){
  lc.clearDisplay(0);
  if (X <  -270){ lc.clearDisplay(0); }
  if (X >= -270){ lc.setLed(0,0,2,true); }
  if (X >= -240){ lc.setLed(0,0,4,true); }
  if (X >= -210){ lc.setLed(0,0,6,true); }
  if (X >= -180){ lc.setLed(0,0,0,true); }
  if (X >= -150){ lc.setLed(0,1,2,true); }
  if (X >= -120){ lc.setLed(0,1,4,true); }
  if (X >=  -90){ lc.setLed(0,1,6,true); }
  if (X >=  -60){ lc.setLed(0,1,0,true); }
  if (X >=  -30){ lc.setLed(0,2,2,true); }
  if (X >=  0)  { lc.setLed(0,2,4,true); }
  if (X >=  20) { lc.setLed(0,2,6,true); }
  if (X >=  40) { lc.setLed(0,2,0,true); }
  if (X >=  60) { lc.setLed(0,3,2,true); }
  if (X >=  80) { lc.setLed(0,3,4,true); }
  if (X >= 100) { lc.setLed(0,3,6,true); }
  if (X >= 120) { lc.setLed(0,3,0,true); }
  if (X >= 140) { lc.setLed(0,4,2,true); }
  if (X >= 160) { lc.setLed(0,4,4,true); }
  if (X >= 180) { lc.setLed(0,4,6,true); }
  if (X >= 200) { lc.setLed(0,4,0,true); }
  if (X >= 220) { lc.setLed(0,5,2,true); }
  if (X >= 240) { lc.setLed(0,5,4,true); }
  if (X >= 260) { lc.setLed(0,5,6,true); }
  if (X >= 280) { lc.setLed(0,5,0,true); }
  if (X >= 300) { lc.setLed(0,6,2,true); }
  if (X >= 320) { lc.setLed(0,6,4,true); }
  if (X >= 340) { lc.setLed(0,6,6,true); }
  if (X >= 360) { lc.setLed(0,6,0,true); }
  if (X >= 380) { }  //idk what to put here...
  
  }

}

Hi. Please post the full sketch, not just the parts you think might be the problem.

Looks like you are using the Arduino "led control" library? That's great for beginners and smaller displays with occasional updates. But I have seen it cause flickering/slow update problems before with larger displays and when fast updated are needed.

Paul

Got any supply decoupling on those chips?

Those if statements bothered me so much, I did the map function to prepare for a switch case. Then realized I could finish now with a simple for loop. Had to get creative with mapping but it wasn’t so bad. Thanks number theory class…
The clearDisplay function is what was killing me. I turned that into this:

//This is the std needle that sweeps from one end to the other.  Sweep is in red.  MAX boost 36+changes color to green
void needle(int X) {
  
  int hiLED = map(X,-240,400,-1,28);
  int a,z;
  if(hiLED>24){    //This displays all leds green to indicate the upper boost levels have been reached
     for(a=0;a<(hiLED+1);a++){                 
       lc.setLed(0,(floor(a*.25)),((2*(a+1))%8),false);    //Clears the previously RED LEDs Because that color overrides all
       lc.setLed(0,(floor(a*.25)),((2*a+1)%8),true);       //Lights all leds up to HiLED GREEN
     } 
     for(z=(hiLED+1); z < 28; z++){
       lc.setLed(0,(floor(z*.25)),((2*(z+1))%8),false);   //clears the remaining RED LEDs 
       lc.setLed(0,(floor(z*.25)),((2*z+1)%8),false);     //clears the remaining GREEN LEDs 
     }
  } else {                  //Displays RED LEDs for 
      for(a=0;a<(hiLED+1);a++){
        lc.setLed(0,(floor(a*.25)),((2*(a+1))%8),true);  //Lights all leds up to HiLED RED
      } 
      for(z=(hiLED+1); z < 28; z++){
        lc.setLed(0,(floor(z*.25)),((2*z+1)%8),false);   //clears the remaining GREEN LEDs
        lc.setLed(0,(floor(z*.25)),((2*(z+1))%8),false); //clears the remaining RED LEDs 
      }
    }
}

No flickering whatsoever.
It would be a little shorter if not for utilizing the dual color LEDs

Grumpy_Mike:
Got any supply decoupling on those chips?

I used the recommended wiring diagram from the manufacturer. I have seen what incorrect powering does. These are set up perfectly. No "ghosting" or interference here.

This is really inefficient:

floor(a*.25)

Remember, Arduino is not a quad-core 3GHz Intel/AMD processor with floating-point hardware!

Just use:

a>>2

Also:

(2*(a+1))%8

would be slightly more efficient as:

((a+1)<<1) & 7

If in the future, you start to run out of time in your update loop,
you can always increase the rate you sweep the lights and only update
the lights at a slower rate.
It is a trick used on movie projectors. They show the same image
twice wit two flashes ( it may be more, I forget ).
As long as the flash rate is fast enough no one notices.
Dwight

PaulRB:
Just use:

a>>2

Not familiar with this operation/function. I would google it, but that is a pretty tough thing to accurately google. Thanks!

Oh and I know the arduino is severely limited. My main loop takes ~50-100ms to iterate. I will try my best to clean all this up, as I have a little more functionality I want to add.

dwightthinker:
If in the future, you start to run out of time in your update loop,
you can always increase the rate you sweep the lights and only update
the lights at a slower rate.
It is a trick used on movie projectors. They show the same image
twice wit two flashes ( it may be more, I forget ).
As long as the flash rate is fast enough no one notices.
Dwight

I really hesitate to run a "delay" on the LED update to speed things. I know there are several optimizers such as if the LED is not going to change, don't update. Also, I am not a pro at using the timing to "run multiple things at once" but I am slowly getting comfortable with it. It sure as hell beats a delay function any day on the arduino. Baby steps.

for 2>>x;
don't google. Select Learning at the top of the page and then
reference.
Scroll down until you see >>
1>>x is the same as divide by 2
2>>x '' by 4
3>>x " by 8
4>>x " by 16
with no rounding, just truncation.
Dwight

Gslenk:
Not familiar with this operation/function. I would google it, but that is a pretty tough thing to accurately google.

Its here. Here is the page listing all the "bitwise" functions, about half way down on the left.

dwightthinker:
for 2>>x;
don't google. Select Learning at the top of the page and then
reference.
Scroll down until you see >>
1>>x is the same as divide by 2
2>>x '' by 4
3>>x " by 8
4>>x " by 16
with no rounding, just truncation.
Dwight

Dwight, you got all that the wrong way around.

dwightthinker:
for 2>>x;
don't google. Select Learning at the top of the page and then
reference.
Scroll down until you see >>
1>>x is the same as divide by 2
2>>x '' by 4
3>>x " by 8
4>>x " by 16
with no rounding, just truncation.
Dwight

Oops, Sorry about that. I'm new to C but not to programming in general.
it should be:
x>>1 is the same as divide by 2
x>>2 '' by 4
x>>3 " by 8
x>>4 " by 16

I should reread better before posting.
Dwight

I've gone back and looked at your code.
First, some general rules for real time programming.

  1. Do not use your code to calculate intermediate values that
    are convenience for you but have no function in the program.
    Use the compiler to do calculator stuff for you.
    In your case, it looks like you've used map() to create some
    nice numbers that are obviously for your convenience and are
    not only a waste of time to calculate but create dangerous
    sense of offset and scale that does not exist in the values
    you got from the A/D.
  2. I suspect you really wanted to do a IF..ELSE tree and not
    a string of IF's. You don't need to be doing so many updates
    to your serial chips. Even the IF ...ELSE structure is poor
    for your program.
  3. If you have a lot of IFs to look at, find a way to reduce
    it to a simple operation. You only have one value from the A/D.
    In your case, you have two spans. You have negative values
    and positive values.
    take the raw A/D values and separate them for values above and values
    below 0.
    Don't transform them to signed values. That makes the calculations
    later round/truncate towards 0 when you really want to keep it as floored math.
    Convert that number into indexs to a table ( or in your case, 3 tables one for
    each spans of LEDs in your serial chips ). Your program is perfect for
    a table lookup.
    4.Look up the values and send them to the display, only once. Writing to the display
    is a slow process. Only do it when needed to change a value.
    As much as possible, let the compiler be a calculator, not the running code.
    At worst, if you have calibration values such as offsets or scale values,
    reduce them to fixed variable to be used in your program. Set them up in the init
    if they need changing in the program.
    The tables for the negative values should only be indexed in steps of 30, using
    the bogus intermediate values you have and the positive by steps of 20.
    Don't forget to put to use MAX and MIN someplace to keep safe array access.
  4. Avoid using floating point. It has to be truly justified. I've done coding for servo
    loops, temperature control system and what not. I've not come on a case were I
    could justify floating point.
    Dwight

dwightthinker:
4.Look up the values and send them to the display, only once. Writing to the display
is a slow process. Only do it when needed to change a value.

This is very important technique to always keep in mind.
i.e. it is faster/better to do nothing when nothing changes rather than blindly continually update the display.
It is amazing how often this is overlooked.
In this case the code should look at the X value and do nothing if it is the same as the last one since nothing needs to be updated.

i.e.

void needle(int X) {
static int lastX;

    if (X == lastX)
        return;
    lastX = X;

    ..... remainder of your code

Note:
You may want to pre-initialize the lastX value with a value that never occurs to ensure that the initial update occurs should the initial X value be zero.

Thanks a ton for all the pointers so far! Definitely gave me some great ideas to think about :slight_smile:

PaulRB:
Also:

(2*(a+1))%8

would be slightly more efficient as:

((a+1)<<1) & 7

Wow. That is interesting (more so the floor function replacement...) . never thought there was any point in using base 2 numbers. My math bug will be itching over resolving this for some time. Also not used to the structure of base 2 numbers but if I play around with it... nice.

Is the difference in efficiency significant for the above operations? I would prefer the original as it is much closer to the language I speak and in terms of readability, I would feel more comfortable with it like that. If it is a worthwhile change, I will just add more comments to my code. =]

Gslenk:
Is the difference in efficiency significant for the above operations?

As I said, only slightly. You probably won't notice any difference in your application.

But you might notice the other change I suggested.

Compare this:

void setup() {
  Serial.begin(115200);
}

void loop() {
  int a = 123;
  Serial.println(floor(a*.25));
}
Sketch uses 3,688 bytes (11%) of program storage space.

To this:

void setup() {
  Serial.begin(115200);
}

void loop() {
  int a = 123;
  Serial.println(a>>2);
}
Sketch uses 2,140 bytes (6%) of program storage space.

So your original formula uses 5% more of your program space.

Its not the floor() function that is the problem as such, its using floating-point maths at all. My suggestion uses only integer maths. The Arduino does not have floating-point hardware, so it has to be done in software, which makes it very much sower and requires extra library functions to be included, which increases the program size.

dwightthinker:

  1. Do not use your code to calculate intermediate values that
    are convenient for you but have no function in the program.
    Use the compiler to do calculator stuff for you.
    In your case, it looks like you've used map() to create some
    nice numbers that are obviously for your convenience and are
    not only a waste of time to calculate but create dangerous
    sense of offset and scale that does not exist in the values
    you got from the A/D.

What does A/D mean exactly? Analog/digital read?

Using the map() function was most certainly me being lazy. Would I be better off using a formula to map the numbers where I want them? I thought this code was a little more clever than the first dump. But I am open to more improvement.

Also, the needle is just an additional visual indicator. It does not have to be perfectly accurate (there are not printed numbers for each LED on the gauge face) And the boost pressure of my car is a pretty volatile variable, changing rapidly. As I go through and clean up code, and calibrate stuff, I may fix that.

PaulRB:
Its not the floor() function that is the problem as such, its using floating-point maths at all.

Was the floor() function creating the floating point math? Does using (2*(z+1))%8 and z>>2, keep me away from float?