How to fix the ghosting 7 segment LED display

I have built a count up code for a 7 segment LED display like this.
However, it has terrible ghosting but I couldn't fix it.

I want to build a code without delay() but millis() for the combination of some function.
Would you give me some advice?

//sample source
/*https://books.google.co.jp/books?id=xJOBDwAAQBAJ&pg=PA130&lpg=PA130&dq=arduino+7+segment+LED+millis++millis++ghosting&source=bl&ots=cY-duNyZQj&sig=ACfU3U3Z7iC69TV9xWq5jmc-mdw-ushFYA&hl=ja&sa=X&ved=2ahUKEwibn_eo6ovhAhWJHHAKHQspCRcQ6AEwAXoECAkQAQ#v=onepage&q=ghotsting&f=false
*/

#define DATA 11
#define LATCH 10
#define CLOCK 9

//  0b11111110   DP 
//  0b11111101   A
//  0b11111011   B
//  0b11110111   C
//  0b11101111   D
//  0b11011111   E
//  0b10111111   F
//  0b01111111   G

static const int num[] {
  0b10000001,   //ZERO
  0b11110011,   //ONE
  0b01001001,   //TWO
  0b01100001,   //THREE
  0b00110011,   //FOUR
  0b00100101,   //FIVE
  0b00000101,   //SIX
  0b11110001,   //SEVEN
  0b00000001,   //EIGHT
  0b00100001,   //NINE
  0b10000001,   //ZERO
  
  0b11110010,   //ONE DOT
  0b01001000,   //TWO DOT
  0b01100000,   //THREE DOT
  0b00110010,   //FOUR DOT
  0b00100100,   //FIVE DOT
  0b00000100,   //SIX DOT
  0b11110000,   //SEVEN DOT
  0b00000000,   //EIGHT DOT
  0b00100000,   //NINE DOT
};
int digits[] = {2,3,4};
int start;
int timer;
int interval = 5000;
int buzzer_time = 20;
int blinkingtime = 10;
int del = 10;

void setup()
{
  pinMode(DATA, OUTPUT);
  pinMode(LATCH, OUTPUT);
  pinMode(CLOCK, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(12,OUTPUT);
  analogRead(A0);
  start = millis();
}

void loop()
{
  start = millis();
  while  (millis() - start < interval)
  {
    timer = millis();                      //time in elapsed seconds
    digit(0,((timer)%100000)/10000,0);                //digit D1 for thousands
    digit(1,((timer)%1000000)/100000,0);          //digit D2 for hundreds
    digit(2,((timer)%10000)/1000,0);
  }
}

void digit(int d, int n, int DP)
{                                                             //turn all digits off, digit states are HIGH
   for (int i = 0; i<3; i++) digitalWrite(digits[i],HIGH);
   digitalWrite(LATCH, LOW);                                //add 128 for decimal point
   shiftOut(DATA, CLOCK, MSBFIRST, num[n]);
   digitalWrite(LATCH,HIGH);                                //change display pattern
   digitalWrite(digits[d],LOW);                                  //turn digit on, digit state LOW
   delay(del);
}
static const int num[] {
  0b10000001,   //ZERO
  0b11110011,   //ONE
  0b01001001,   //TWO
  0b01100001,   //THREE
  0b00110011,   //FOUR
  0b00100101,   //FIVE
  0b00000101,   //SIX
  0b11110001,   //SEVEN
  0b00000001,   //EIGHT
  0b00100001,   //NINE
  0b10000001,   //ZERO
 
  0b11110010,   //ONE DOT
  0b01001000,   //TWO DOT
  0b01100000,   //THREE DOT
  0b00110010,   //FOUR DOT
  0b00100100,   //FIVE DOT
  0b00000100,   //SIX DOT
  0b11110000,   //SEVEN DOT
  0b00000000,   //EIGHT DOT
  0b00100000,   //NINE DOT
};
int digits[] = {2,3,4};
int start;
int timer;
int interval = 5000;
int buzzer_time = 20;
int blinkingtime = 10;
int del = 10;

Paying attention to variable types IS important. You are wasting memory.

  analogRead(A0);

Useless.

  while  (millis() - start < interval)
  {
    timer = millis();                      //time in elapsed seconds
    digit(0,((timer)%100000)/10000,0);                //digit D1 for thousands
    digit(1,((timer)%1000000)/100000,0);          //digit D2 for hundreds
    digit(2,((timer)%10000)/1000,0);
  }

Why do you need to update the digits more than once a second?

Hmm, its odd, your code is correctly turning all the digits off before shifting more segment data,
which ought to eliminate ghosting. Is there anything in the circuit that might slow down the switching off
of digits (such as using darlingtons)?

MarkT:
Hmm, its odd, your code is correctly turning all the digits off before shifting more segment data,
which ought to eliminate ghosting. Is there anything in the circuit that might slow down the switching off
of digits (such as using darlingtons)?

The display is updating every time through the loop, because the delay using millis is not implemented properly. Not at a computer where I can type out the details right now.

Yes, I think the problem is trying to display the actual millis value in that way. I don't think it's ghosting at all. Have you tried setting up your own counter variable and displaying that?

I'm pretty sure it isn't ghosting, but that you are updating the display too fast.
The statement:

while (millis() - start < interval)

will execute the code continuously until the value of interval is reached.
The usual way of doing timing with millis() would use:

if (millis() - start >= interval)

so that the code would only be executed after the interval has elapsed.

I was about to comment that timer and start should be declared as unsigned long, but in the video I notice you are using an Arduino DUE, so an integer is already the normal 4-byte length of a long, although it should be declared unsigned int.

The equations you use to get the individual digits do work, but look a bit cumbersome.

((timer) % 1000000) / 100000
((timer) % 100000) / 10000
((timer) % 10000) / 1000

can be written as

((timer / 100000) % 10)
((timer / 10000) % 10)
((timer / 1000) % 10)

Try this code and see if it works, I have commented the lines that I changed.
(not sure if you have the digits in the correct order when you display them, I didn't change that part)

//sample source
/*https://books.google.co.jp/books?id=xJOBDwAAQBAJ&pg=PA130&lpg=PA130&dq=arduino+7+segment+LED+millis++millis++ghosting&source=bl&ots=cY-duNyZQj&sig=ACfU3U3Z7iC69TV9xWq5jmc-mdw-ushFYA&hl=ja&sa=X&ved=2ahUKEwibn_eo6ovhAhWJHHAKHQspCRcQ6AEwAXoECAkQAQ#v=onepage&q=ghotsting&f=false
*/

#define DATA 11
#define LATCH 10
#define CLOCK 9

//  0b11111110   DP
//  0b11111101   A
//  0b11111011   B
//  0b11110111   C
//  0b11101111   D
//  0b11011111   E
//  0b10111111   F
//  0b01111111   G

static const int num[] {
  0b10000001,   //ZERO
  0b11110011,   //ONE
  0b01001001,   //TWO
  0b01100001,   //THREE
  0b00110011,   //FOUR
  0b00100101,   //FIVE
  0b00000101,   //SIX
  0b11110001,   //SEVEN
  0b00000001,   //EIGHT
  0b00100001,   //NINE
  0b10000001,   //ZERO

  0b11110010,   //ONE DOT
  0b01001000,   //TWO DOT
  0b01100000,   //THREE DOT
  0b00110010,   //FOUR DOT
  0b00100100,   //FIVE DOT
  0b00000100,   //SIX DOT
  0b11110000,   //SEVEN DOT
  0b00000000,   //EIGHT DOT
  0b00100000,   //NINE DOT
};
int digits[] = {2, 3, 4};
unsigned int start;  //changed from int to unsigned int
unsigned int timer; //changed from int to unsigned int
//int interval = 5000;
int interval = 1000; //update the display once each second
int buzzer_time = 20;
int blinkingtime = 10;
int del = 10;

void setup()
{
  pinMode(DATA, OUTPUT);
  pinMode(LATCH, OUTPUT);
  pinMode(CLOCK, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(12, OUTPUT);
  analogRead(A0);
  //start = millis();
  timer = millis(); //start time for the timer
}

void loop()
{
  start = millis();
  //while  (millis() - start < interval)  //this will loop on the code constantly for the duration of interval
  if ((start - timer) >= interval)  //this will execute the code once when interval in reached
  {
    timer = start; //reset the timer     //time in elapsed seconds
    digit(0, ((timer) % 100000) / 10000, 0); //digit D1 for thousands
    digit(1, ((timer) % 1000000) / 100000, 0); //digit D2 for hundreds
    digit(2, ((timer) % 10000) / 1000, 0);
    //looks like you may have the digits out of order
  }
}

void digit(int d, int n, int DP)
{ //turn all digits off, digit states are HIGH
  for (int i = 0; i < 3; i++) digitalWrite(digits[i], HIGH);
  digitalWrite(LATCH, LOW);                                //add 128 for decimal point
  shiftOut(DATA, CLOCK, MSBFIRST, num[n]);
  digitalWrite(LATCH, HIGH);                               //change display pattern
  digitalWrite(digits[d], LOW);                                 //turn digit on, digit state LOW
  delay(del);
}

Thank you for your kind advise.

I have copied and built the modified code in my Arduino Due.
However, the code is still wrong to be fixed.

It maybe that.

    digit(0, ((timer) % 100000) / 10000, 0); //digit D1 for thousands
    digit(1, ((timer) % 1000000) / 100000, 0); //digit D2 for hundreds
    digit(2, ((timer) % 10000) / 1000, 0);

I'm going to fix it right now.

I forgot all about having to scan the display, but I'm surprised you have two digits showing instead of just one. The way it is written, the code should very briefly show the two upper digits, then display the least significant digit for a second. In the digit function, are you sure that outputting a HIGH turns the digit OFF? If you have that reversed, and HIGH turns it ON, that might cause what you are seeing.

I added some code for scanning the display, and swapped the HIGH and LOW in the digit function, see if this works:

//sample source
/*https://books.google.co.jp/books?id=xJOBDwAAQBAJ&pg=PA130&lpg=PA130&dq=arduino+7+segment+LED+millis++millis++ghosting&source=bl&ots=cY-duNyZQj&sig=ACfU3U3Z7iC69TV9xWq5jmc-mdw-ushFYA&hl=ja&sa=X&ved=2ahUKEwibn_eo6ovhAhWJHHAKHQspCRcQ6AEwAXoECAkQAQ#v=onepage&q=ghotsting&f=false
*/

#define DATA 11
#define LATCH 10
#define CLOCK 9

//  0b11111110   DP
//  0b11111101   A
//  0b11111011   B
//  0b11110111   C
//  0b11101111   D
//  0b11011111   E
//  0b10111111   F
//  0b01111111   G

static const int num[] {
  0b10000001,   //ZERO
  0b11110011,   //ONE
  0b01001001,   //TWO
  0b01100001,   //THREE
  0b00110011,   //FOUR
  0b00100101,   //FIVE
  0b00000101,   //SIX
  0b11110001,   //SEVEN
  0b00000001,   //EIGHT
  0b00100001,   //NINE
  0b10000001,   //ZERO

  0b11110010,   //ONE DOT
  0b01001000,   //TWO DOT
  0b01100000,   //THREE DOT
  0b00110010,   //FOUR DOT
  0b00100100,   //FIVE DOT
  0b00000100,   //SIX DOT
  0b11110000,   //SEVEN DOT
  0b00000000,   //EIGHT DOT
  0b00100000,   //NINE DOT
};
int digits[] = {2, 3, 4};
int LEDdigit[] = {0, 0, 0}; //storage for digits to be displayed
int LEDdecimal[] = {0, 0, 0}; //storage for decimal indicators
byte scanDigit = 0; //tells which digit to display
unsigned int start;  //changed from int to unsigned int
unsigned int timer; //changed from int to unsigned int
unsigned int scanTimer; //timer for display scanning
//int interval = 5000;
int interval = 1000; //update the display once each second
int buzzer_time = 20;
int blinkingtime = 10;
int del = 10;

void setup()
{
  Serial.begin(9600);
  pinMode(DATA, OUTPUT);
  pinMode(LATCH, OUTPUT);
  pinMode(CLOCK, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(12, OUTPUT);
  analogRead(A0);
  //start = millis();
  timer = millis(); //start time for the timer
  scanTimer = millis(); //start the display scan timer
}

void loop()
{
  start = millis();

  if ((start - scanTimer) >= del) //digit scanning timer
  {
    scanTimer = start; //reset the timer
    scanDigit++;       //display the next digit
    if (scanDigit >= 3) {
      scanDigit = 0;
    }
    digit(scanDigit, LEDdigit[scanDigit], LEDdecimal[scanDigit]);
  }

  //while  (millis() - start < interval)  //this will loop on the code constantly for the duration of interval
  if ((start - timer) >= interval)  //this will execute the code once when interval in reached
  {
    timer = start; //reset the timer     //time in elapsed seconds
    //digit(0, ((timer) % 100000) / 10000, 0); //digit D1 for thousands
    //digit(1, ((timer) % 1000000) / 100000, 0); //digit D2 for hundreds
    //digit(2, ((timer) % 10000) / 1000, 0);
    LEDdigit[0] = (timer % 1000000) / 100000;
    LEDdigit[1] = (timer % 100000) / 10000;
    LEDdigit[2] = (timer % 10000) / 1000;
    //looks like you may have the digits out of order
  }
}

void digit(int d, int n, int DP)
{ //turn all digits off, digit states are HIGH
  for (int i = 0; i < 3; i++) digitalWrite(digits[i], LOW); //changed from HIGH to LOW to disable the digits
  digitalWrite(LATCH, LOW);                                //add 128 for decimal point
  shiftOut(DATA, CLOCK, MSBFIRST, num[n]);
  digitalWrite(LATCH, HIGH);                               //change display pattern
  digitalWrite(digits[d], HIGH);                           //changed from LOW to HIGH here  //turn digit on, digit state LOW
  //delay(del);
}

Thank you for your advise.
I have fixed the problem.

The order of digits was reversed, so I changed the code like this.

from

int digits[] = {2, 3, 4};

to

int digits[] = {4, 3, 2};

The combination of HIGH and LOW in the digit function was copied from the Arduino text book, but it made a problem.
I have no idea what is the cause of this problem exactly, but it may be my shift resister "HC595N" on the circuit instead of a "HC595" in the text book as reference, so the output of high and low on the code may be reversed from HC595. For example, num[0b01111110] displays "0" usually in common text books with HC595, however I should build num[0b10000001] to display "0" with HC959N.

Glad you got it working. The displays come in two types, common anode and common cathode, you probably have the common anode type, where the common connection has the (+) power connection (in your case from an output pin), and the segment is turned on by a LOW low. Common cathode is the opposite, with the common connection going to ground and the segments are turned on by a HIGH level.

But what is it supposed to be counting ?
Does say 463 in the display represent 4 seconds and 63 hundredths of a second form the system start ?

Your original logic, albeit with errors, appeared as if 463 would be four hundred and sixty three (463) seconds since the system start.

I'd like you to watch the modified version.

This is a practice for me to build a code using millis instead of delay in order to make a cymatics devise which indicates the frequency with 7 digit LED display.