Flickering Issue with 7-Segment Displays in Daisy Chain Configuration

Hi everyone,

I'm working on a project that involves six 4-digit 7-segment displays, each controlled by CD4094B shift registers in a daisy chain configuration. All the displays share the same data, clock, and latch pins (connected to an Arduino). The displays are showing numbers correctly, but I'm facing a flickering issue whenever the numbers are updated. I've made some changes to the code, which have significantly reduced the flicker, but it’s still noticeable and I would like to eliminate it completely.

Here are the details of my setup:

  • The displays are updated via the shiftOut() function.
  • I'm using a single set of clock, data, and latch pins for all displays.
  • I buffer the data and update all displays in one go, latching the data afterward.
  • Despite these optimizations, there’s still some flickering during updates.

I would like to avoid using separate clock or latch pins for each display, as I want to conserve I/O pins.

Has anyone experienced similar issues or have any suggestions on how to achieve a flicker-free display update?

#include <Arduino.h>

// Define shared pins for all shift registers
const int latchPin = 10;
const int clockPin = 13;
const int dataPin = 2;

// Segment patterns for digits 0-9
const byte segmentData[] = {
  0b00111111, // 0
  0b00000110, // 1
  0b01011011, // 2
  0b01001111, // 3
  0b01100110, // 4
  0b01101101, // 5
  0b01111101, // 6
  0b00000111, // 7
  0b01111111, // 8
  0b01101111  // 9
};

// Number of digits per display
const int numDigits = 4;

// Number of displays
const int numDisplays = 6;

// Array to hold the data to be displayed
byte displayBuffer[numDigits * numDisplays] = {0};

void setup() {
  // Set shared pins as output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  // Update the buffer with the numbers to be displayed
  updateDisplayBuffer(3030, 6);  // Update sixth display
  updateDisplayBuffer(2021, 5);  // Update fifth display
  updateDisplayBuffer(1015, 4);  // Update fourth display
  updateDisplayBuffer(7893, 3);  // Update third display
  updateDisplayBuffer(4564, 2);  // Update second display
  updateDisplayBuffer(1238, 1);  // Update first display

  // Send the buffered data to the displays
  updateDisplays();

  delay(1000);  // Wait 1 second before updating again
}

// Function to update the buffer for a specific display
void updateDisplayBuffer(int number, int display) {
  // Calculate the starting position in the buffer for the specific display
  int startShiftRegister = (display - 1) * numDigits;

  // Flag to indicate if a non-zero digit has been encountered
  bool hasNonZeroDigit = false;

  // Store the digits in the buffer from most significant to least significant
  for (int i = numDigits - 1; i >= 0; i--) {
    int digit = number / pow(10, i);
    digit %= 10;
    // Suppress leading zeros
    if (digit != 0 || hasNonZeroDigit || i == 0) {
      hasNonZeroDigit = true;
      displayBuffer[startShiftRegister + (numDigits - 1 - i)] = segmentData[digit];
    } else {
      // If it's a leading zero and hasn't encountered a non-zero digit, turn off the segments
      displayBuffer[startShiftRegister + (numDigits - 1 - i)] = 0b00000000;  // Turn off all segments
    }
  }
}

// Function to update all displays at once
void updateDisplays() {
  // Prepare to send data to all shift registers
  digitalWrite(latchPin, LOW);

  // Shift out all data from the buffer in reverse order
  for (int i = (numDigits * numDisplays) - 1; i >= 0; i--) {
    shiftOut(dataPin, clockPin, MSBFIRST, displayBuffer[i]);
  }

  // Latch the data to display the updated numbers
  digitalWrite(latchPin, HIGH);
}

Any help would be greatly appreciated!

This is the video of the display flickering

This is the display I am using

  1. NSK ELECTRONICS: 7SEG 4 Digit Display 2.2" with SIPO based 3 Wire Control

All segments light (one has DP)... can you turn the common pin off, shiftOut, common pin on?

Hi @khalifa_007,
I had similar dificulties when I developed the library I use to deal with 74HC595 shift registers Four Bit Led Digital Tube library, including some flickering, some ghost chars and some other glitches.
Altough I never had to deal with so many daisy chained displays some things came out:

  • In some cases I replaced the shiftOut() with some simple code simply handling the 3 output lines needed, using my own code, to make my own fastSend() method.
  • As the time employed in refreshing the individual characters is much less than the time the display stays waiting for the next refresh, the last port refreshed is the one that keeps more time the leds powered, so my refresh routine starts each refreshing cycle from the "next in the row" port, and so it ends each time in the next port, and when it reaches the end it starts over from the first.
  • Test different refreshing speeds: low speeds produce evident flickering, to high speeds produce ghostings.

If you're interested, in my repository you'll find the abovementioned library and two more advanced that separate the code into "data processing" and "data displaying" to use different chipset driven displays.
Good Luck!
Gaby.//

I don't know exactly how shiftOut() works. Does it finish shifting completely before it returns? If it doesn't you could be seeing the last byte shifting by. Anyway, try adding a little delay before latching the new data.

Traditionally, this kind of thing is dealt with by turning off the output-enable pin before latching, and turning it back on after. That way any glitching in the latching process doesn't get through to the output, so you don't see it.

@xfpd The Display which I am using has only these 10 pins
Data,
CLK,
STRB,
12v,
GND

other 5 is
QS1,
CLK,
STRB,
12v,
GND

So I am not able to get which common pin you are talking about

Then post the data sheet to the display, or second best paste a link to where you bought it.

You should be able to speed it up considerably by using the hardware SPI peripheral of your Arduino board. This is faster than shiftOut() but you can't then use arbitrarily chosen pins to connect to your shift register chain.

Hi, @Grumpy_Mike I have already provided the link from where I bought this display in my question but unfortunately, they have not offered me the datasheet of the display.

Sorry I missed that. Yes it is a useless link that just takes you round in circles of the same set of pages. A very poor situation.
But it is interesting that you have rejected the advice given to you, it is almost as if you do not want to fix your problem.

It is summered in the lines

the simple fix is not to open the latch before the shifting, but to briefly (and not too briefly as the old CMOS is a slow device) open and close the latch after shifting.

I think I got the same PM as you have quoted.

Anyway, now the problem has been made clear, here is a project complete with code which uses a daisy chain of these CD4094 museum pieces. Appropriately enough it is a "vintage" display project. The strobe (latch) is pulsed at the end of the data delivery. ESP32-C3 Supermini clock with VFD display

Hi @khalifa_007 ,

You're describing the classic shift register module pins, the first the input side, the other the output side:
CLK: clock pin, you set the signal that marks the "pace" of the execution.
Data: data pin where you have to put the bit value to be input in serial sequence.
STRB: strobe pin. latches the data received serially in the buffer and presents the value in parallel in the Q1~Q8 pins connected to the 7 segments display module.
QS1: the value output to be passed to the next shift register to daisy chain, connected to the data in the next module.

Nothing in there seems to be a problem to you, as you mentioned you have the data correct, but for some artifacts. so let's figure you're just missing the point in the timely activation of the corresponding lines.
Downloading the datasheet from the net will take you 5 seconds, but the sequence logic is pretty simple: depending on the specific shift register you must change the data value in different stages of the CLK, wait for the data value to be shifted into the shift register buffer, repeata as many times as bits and then activate the latch mechamism... while in the correct level of the CLK. In your case it will be after you've correctly feeded the 24 data bits.
Once again, the key is in the timming diagrams of the datasheet.

Good Luck!
Gaby.//

Have you got a link for that. The seller's website doesn't mention a model number so how do you know what to search for?

Hi @Grumpy_Mike ,

The original post states:

each controlled by CD4094B shift registers in a daisy chain configuration

That's the part we're looking the datasheet for... let me see as I downloaded some lame low res scans from TI...
This one has the graphics pretty clear:

It's that one ok for you?

Good Luck!
Gaby.//

Just incase it is not clear, the correction to the OP's code to handle the CD4094 shift registers is as follows:

// Function to update all displays at once
void updateDisplays() {
  // Prepare to send data to all shift registers
  // digitalWrite(latchPin, LOW);   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  // Shift out all data from the buffer in reverse order
  for (int i = (numDigits * numDisplays) - 1; i >= 0; i--) {
    shiftOut(dataPin, clockPin, MSBFIRST, displayBuffer[i]);
  }

  // pulse latch  to display the updated numbers
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(100) ;                     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  digitalWrite(latchPin, LOW);   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}

Credit to: anonymous

Well, the presentation of the schematic (https://www.onsemi.com/pdf/datasheet/mc74hc4094a-d.pdf) is indeed better than the scanned and blurred TI version but it is not the same device, for example:
image

From TI (https://www.ti.com/lit/ds/symlink/cd4094b.pdf)

image

Some special use cases can profit from this relatively high voltage, for example, VFD displays, which I have mentioned earlier in the thread.

Hi @6v6gt ,

My mistake, thank you for noting it. My wrong just checking for the schematic!

Thanks.
Gaby.//

I have tried this but it is still showing flickering

Can you show your latest code and maybe a picture of the modules you are using also where the numbering on the integrated circuits is visible.

Hi @6v6gt this are the pics of display which I am having



this is the updated code according to your suggestion

#include <Arduino.h>

// Define shared pins for all shift registers
const int latchPin = 10;
const int clockPin = 13;
const int dataPin = 2;

// Segment patterns for digits 0-9
const byte segmentData[] = {
  0b00111111, // 0
  0b00000110, // 1
  0b01011011, // 2
  0b01001111, // 3
  0b01100110, // 4
  0b01101101, // 5
  0b01111101, // 6
  0b00000111, // 7
  0b01111111, // 8
  0b01101111  // 9
};

// Number of digits per display
const int numDigits = 4;

// Number of displays
const int numDisplays = 6;

// Array to hold the data to be displayed
byte displayBuffer[numDigits * numDisplays] = {0};

void setup() {
  // Set shared pins as output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  // Update the buffer with the numbers to be displayed
  updateDisplayBuffer(3030, 6);  // Update sixth display
  updateDisplayBuffer(2021, 5);  // Update fifth display
  updateDisplayBuffer(1015, 4);  // Update fourth display
  updateDisplayBuffer(7893, 3);  // Update third display
  updateDisplayBuffer(4564, 2);  // Update second display
  updateDisplayBuffer(1238, 1);  // Update first display

  // Send the buffered data to the displays
  updateDisplays();

  delay(1000);  // Wait 1 second before updating again
}

// Function to update the buffer for a specific display
void updateDisplayBuffer(int number, int display) {
  // Calculate the starting position in the buffer for the specific display
  int startShiftRegister = (display - 1) * numDigits;

  // Flag to indicate if a non-zero digit has been encountered
  bool hasNonZeroDigit = false;

  // Store the digits in the buffer from most significant to least significant
  for (int i = numDigits - 1; i >= 0; i--) {
    int digit = number / pow(10, i);
    digit %= 10;
    // Suppress leading zeros
    if (digit != 0 || hasNonZeroDigit || i == 0) {
      hasNonZeroDigit = true;
      displayBuffer[startShiftRegister + (numDigits - 1 - i)] = segmentData[digit];
    } else {
      // If it's a leading zero and hasn't encountered a non-zero digit, turn off the segments
      displayBuffer[startShiftRegister + (numDigits - 1 - i)] = 0b00000000;  // Turn off all segments
    }
  }
}

// Function to update all displays at once
void updateDisplays() {
  // Prepare to send data to all shift registers
  //digitalWrite(latchPin, LOW);

  // Shift out all data from the buffer in reverse order
  for (int i = (numDigits * numDisplays) - 1; i >= 0; i--) {
    shiftOut(dataPin, clockPin, MSBFIRST, displayBuffer[i]);
  }

  // Latch the data to display the updated numbers
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(100) ;                     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  digitalWrite(latchPin, LOW);
}

Which Arduino are you using ?
Which header pin block is connected to the Arduino J2 or J6 ?
Can you measure the voltage across C10 (that equals the logic voltage of the CD4094)

Try an experiment.
Comment out the following block:

  // Latch the data to display the updated numbers
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(100) ;                     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  digitalWrite(latchPin, LOW);

then in setup() :

  1. digitalWrite(latchPin, HIGH); to see if the flickering is worse or more or less the same.
    then later
  2. digitalWrite(latchPin, LOW); to see if the displays show nothing.