How to fix ghosting in 3 boards of 2-digit 7-seg displays?

Hello,

I'm working on doing multiplexing between 3 boards, 2-digits, 7-segment displays.

I'm succeeded to do the multiplexing but I get some ghosting.

Any ideas?!

This is the code:

// Multiplexing 2 digit 7-segment with 74LS47
unsigned long delay_start;
unsigned long delay_current;
unsigned long delay_period=100;


void functions_init(void){
    delay_start = micros();
}

void multitasking_manager(void)
{
    display_set_traffic_system();
    display_refresh_traffic_system();
}


void display_set_traffic_system(void){
    counter1 = 10;
    counter2 = 20;
    counter3 = 70;

    tens1 = counter1 / 10;
    units1 = counter1 % 10;  
    tens2 = counter2 / 10;
    units2 = counter2 % 10;  
    tens3 = counter3 / 10;
    units3 = counter3 % 10; 
}

void display_refresh_traffic_system(void){
  /////////////////////////////////////////////////////////////////// 1
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    IOEXP8_WRITE(0xFE);
    PORTB = (units1%10);
    delay_start = micros();
  }
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    IOEXP8_WRITE(0xFD);
    PORTB = (tens1%10);
    delay_start = micros();
  }
  /////////////////////////////////////////////////////////////////// 2
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    IOEXP8_WRITE(0xFB);
    PORTB = (units2%10);
    delay_start = micros();
  }
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    IOEXP8_WRITE(0xF7);
    PORTB = (tens2%10);
    delay_start = micros();
  }    
  /////////////////////////////////////////////////////////////////// 3
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    IOEXP8_WRITE(0xEF);
    PORTB = (units3%10);
    delay_start = micros();
  }
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    IOEXP8_WRITE(0xDF);
    PORTB = (tens3%10);
    delay_start = micros();
  }
}

It looks like the 'magic numbers' are enabling the various digits. You might try something like turning all digits off before you enable a new one.

Don

floresta: It looks like the 'magic numbers' are enabling the various digits. You might try something like turning all digits off before you enable a new one.

Don

Tried this one, but still not working perfectly.

void display_refresh_traffic_system(void){
  /////////////////////////////////////////////////////////////////// 1
  ioexp8_write(0xFF);
    delay_current = micros();
    if(delay_current - delay_start >= delay_period){     
    ioexp8_write(0xFE);
    PORTB = (units1%10);
    delay_start = micros();
  }
  ioexp8_write(0xFF);
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xFD);
    PORTB = (tens1%10);
    delay_start = micros();
  }
  /////////////////////////////////////////////////////////////////// 2
  ioexp8_write(0xFF);
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xFB);
    PORTB = (units2%10);
    delay_start = micros();
  }
  ioexp8_write(0xFF);
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xF7);
    PORTB = (tens2%10);
    delay_start = micros();
  }    
  /////////////////////////////////////////////////////////////////// 3
  ioexp8_write(0xFF);
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xEF);
    PORTB = (units3%10);
    delay_start = micros();
  }
  ioexp8_write(0xFF);
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xDF);
    PORTB = (tens3%10);
    delay_start = micros();
  }
}

Why don't you check for ideas in the LEDs and Multiplexing section of the forum.

Make sure you reference this thread so you don't get criticized for cross-posting.

Don

floresta: Why don't you check for ideas in the LEDs and Multiplexing section of the forum.

Make sure you reference this thread so you don't get criticized for cross-posting.

Don

How? Do you mean that I have to ask in other posts or comment in them?

Are you calling display_refresh_traffic_system() from loop? At a minimum, these calls:

“ioexp8_write(0xFF);”

Needs to be inside the if statements when the time expires, not outside since it is unconditionally called 4 times for every invocation of display_refresh_traffic_system(). I also think you need some additional code to know which digit position is currently active and just one timer test, not four.

WattsThat: Are you calling display_refresh_traffic_system() from loop?

Yes

At a minimum, these calls: “ioexp8_write(0xFF);”

Needs to be inside the if statements when the time expires, not outside since it is unconditionally called 4 times for every invocation of display_refresh_traffic_system(). I also think you need some additional code to know which digit position is currently active and just one timer test, not four.

Tried this one

void display_refresh_traffic_system(void){
  /////////////////////////////////////////////////////////////////// 1
    delay_current = micros();
    if(delay_current - delay_start >= delay_period){     
    ioexp8_write(0xFF);
    ioexp8_write(0xFE);
    PORTB = (units1%10);
    delay_start = micros();
  }
  
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xFF);
    ioexp8_write(0xFD);
    PORTB = (tens1%10);
    delay_start = micros();
  }
  /////////////////////////////////////////////////////////////////// 2
  
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xFF);
    ioexp8_write(0xFB);
    PORTB = (units2%10);
    delay_start = micros();
  }
  
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xFF);
    ioexp8_write(0xF7);
    PORTB = (tens2%10);
    delay_start = micros();
  }    
  /////////////////////////////////////////////////////////////////// 3
  
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xFF);
    ioexp8_write(0xEF);
    PORTB = (units3%10);
    delay_start = micros();
  }
  
  delay_current = micros();
  if(delay_current - delay_start >= delay_period){
    ioexp8_write(0xFF);
    ioexp8_write(0xDF);
    PORTB = (tens3%10);
    delay_start = micros();
  }
}

And this:

void display_refresh_traffic_system(void){
  /////////////////////////////////////////////////////////////////// 1
    delay_current = micros();

    if((delay_current - delay_start >= delay_period) && digit_switch==0){
        ioexp8_write(0xFF);
        ioexp8_write(0xFE);
        PORTB = (units1%10);

        ioexp8_write(0xFF);
        ioexp8_write(0xFD);
        PORTB = (tens1%10);
        delay_start = micros();
        digit_switch=1;
    }

    if((delay_current - delay_start >= delay_period) && digit_switch==1){
        ioexp8_write(0xFF);
        ioexp8_write(0xFB);
        PORTB = (units2%10);
      
        ioexp8_write(0xFF);
        ioexp8_write(0xF7);
        PORTB = (tens2%10);
        delay_start = micros();
        digit_switch=2;
    }

    if((delay_current - delay_start >= delay_period) && digit_switch==2){
        ioexp8_write(0xFF);
        ioexp8_write(0xEF);
        PORTB = (units2%10);
      
        ioexp8_write(0xFF);
        ioexp8_write(0xDF);
        PORTB = (tens2%10);
        delay_start = micros();
        digit_switch=0;
    }
}

OK, I was able to minimize the ghosting with this one:

void display_refresh_traffic_system(void){
  /////////////////////////////////////////////////////////////////// 1
    delay_current = micros();

    if((delay_current - delay_start >= delay_period) && digit_switch == 0){
        ioexp8_write(0xFF);
        ioexp8_write(0xFE);
        PORTB = (units1%10);
        delay_start = micros();
        digit_switch = 1;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 1){     
        ioexp8_write(0xFF);
        ioexp8_write(0xFD);
        PORTB = (tens1%10);
        delay_start = micros();
        digit_switch = 2;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 2){
        ioexp8_write(0xFF);
        ioexp8_write(0xFB);
        PORTB = (units2%10);
        delay_start = micros();
        digit_switch = 3;       
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 3){
        ioexp8_write(0xFF);
        ioexp8_write(0xF7);
        PORTB = (tens2%10);
        delay_start = micros();
        digit_switch = 4;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 4){
        ioexp8_write(0xFF);
        ioexp8_write(0xEF);
        PORTB = (units3%10);
        delay_start = micros();
        digit_switch = 5;
    }       

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 5){ 
        ioexp8_write(0xFF);
        ioexp8_write(0xDF);
        PORTB = (tens3%10);
        delay_start = micros();
        digit_switch=0;
    }
}

This one was like 80-90% ghosting free, not perfect!

void display_refresh_traffic_system(void){
  /////////////////////////////////////////////////////////////////// 1
    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 0){
        ioexp8_write(0xFE);
        PORTB = (units1%10);
        delay_start = micros();
        digit_switch = 1;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 1){
        ioexp8_write(0xFF);
        digit_switch = 2;
    }       


    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 2){     
        ioexp8_write(0xFD);
        PORTB = (tens1%10);
        delay_start = micros();
        digit_switch = 3;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 3){
        ioexp8_write(0xFF);
        digit_switch = 4;
    }   

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 4){
        ioexp8_write(0xFB);
        PORTB = (units2%10);
        delay_start = micros();
        digit_switch = 5;       
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 5){
        ioexp8_write(0xFF);
        digit_switch = 6;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 6){
        ioexp8_write(0xF7);
        PORTB = (tens2%10);
        delay_start = micros();
        digit_switch = 7;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 7){
        ioexp8_write(0xFF);
        digit_switch = 8;
    }   

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 8){
        ioexp8_write(0xEF);
        PORTB = (units3%10);
        delay_start = micros();
        digit_switch = 9;
    }       

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 9){
        ioexp8_write(0xFF);
        digit_switch = 10;
    }   

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 10){    
        ioexp8_write(0xDF);
        PORTB = (tens3%10);
        delay_start = micros();
        digit_switch = 11;
    }

    delay_current = micros();
    if((delay_current - delay_start >= delay_period) && digit_switch == 11){
        ioexp8_write(0xFF);
        digit_switch = 0;
    }   
}

Your >= compares need to be less than for the way you’re switching digits.

You can drop the multiple “delay_current = micros()” and set once at the top of the subroutine. This assumes you’re using a reasonable delay time (> 100 micro seconds). Too small a delay value will cause brightness variations due to loop() timing variations.

This one was %90 ghosting free, still not perfect! But better, shorter and more compact.

void display_refresh_traffic_system(void){
    delay_current = micros();
    if(delay_current - delay_start >= delay_period){
        ioexp8_write(0xFF);     // turn all digits off
        PORTB = (units1%10);    // upload data to portb
        ioexp8_write(0xFE);     // enable board1 - digit1
        ioexp8_write(0xFF);     // turn all digits off
        PORTB = (tens1%10);     // upload data to portb
        ioexp8_write(0xFD);     // enable board1 - digit2
        ioexp8_write(0xFF);
        PORTB = (units2%10);
        ioexp8_write(0xFB);     // enable board2 - digit1
        ioexp8_write(0xFF);
        PORTB = (tens2%10);
        ioexp8_write(0xF7);     // enable board2 - digit2
        ioexp8_write(0xFF);
        PORTB = (units3%10);
        ioexp8_write(0xEF);     // enable board3 - digit1
        ioexp8_write(0xFF);
        PORTB = (tens3%10);
        ioexp8_write(0xDF);     // enable board3 - digit2
        delay_start = micros();
    }   
}

Then I removed the delay and it was the same result! How about that?

        ioexp8_write(0xFF);     // turn all digits off
        PORTB = (units1%10);    // upload data to portb
        ioexp8_write(0xFE);     // enable board1 - digit1
        ioexp8_write(0xFF);     // turn all digits off
        PORTB = (tens1%10);     // upload data to portb
        ioexp8_write(0xFD);     // enable board1 - digit2
        ioexp8_write(0xFF);
        PORTB = (units2%10);
        ioexp8_write(0xFB);     // enable board2 - digit1
        ioexp8_write(0xFF);
        PORTB = (tens2%10);
        ioexp8_write(0xF7);     // enable board2 - digit2
        ioexp8_write(0xFF);
        PORTB = (units3%10);
        ioexp8_write(0xEF);     // enable board3 - digit1
        ioexp8_write(0xFF);
        PORTB = (tens3%10);
        ioexp8_write(0xDF);     // enable board3 - digit2

While I give you karma for persistence, I have to assume you do not understand how display multiplexing works. The rough outline:

Turn off display
Set segment pattern in digit 1
Turn on the digit
Leave it on for a fixed period of time

Turn off display
Set segment pattern in digit 2
Turn on the digit
Leave it on for a fixed period of time

Here it is extended to a 4 digit display on an Uno displaying a fixed string of "1234". Assume a common anode display where the four anodes are on PORTB and the cathodes are on PORTD.

void setup() {
  DDRD = 0xff;
  DDRB = 0x0f;
}

void loop() {
  PORTB = 0xff;   // all off
  PORTD = 0x99;   // #1 in LED segments
  PORTB = 0xff-1; // turn on digit 1  
  delayMicroseconds(100);    // and leave it on for a time
  
  PORTB = 0xff;   // all off
  PORTD = 0xb0;   // #2 in LED segments
  PORTB = 0xff-2; // turn on digit 2  
  delayMicroseconds(100);    // and leave it on for a time

  PORTB = 0xff;   // all off
  PORTD = 0xa4;   // #3 in LED segments
  PORTB = 0xff-4; // turn on digit 3  
  delayMicroseconds(100);    // and leave it on for a time

  PORTB = 0xff;   // all off
  PORTD = 0xf9;   // #4 in LED segments
  PORTB = 0xff-8; // turn on digit 4  
  delayMicroseconds(100);    // and leave it on for a time
}

WattsThat: While I give you karma for persistence, I have to assume you do not understand how display multiplexing works.

That was pretty clear from the start, and why I did not buy into it (apart from not having too much time to spare).

But I have been thinking all the time - what is this "delayMicroseconds(100);" business? Considering you only need to refresh the whole display about fifty times per second, 1 ms would be way more than short enough and 5 ms for each of four digits or 3 ms for six would fulfil the criterion perfectly.

Okay, so my example was a blocking program. Now that I’ve had a few minutes in front of a real computer, here it is as a non-blocking version.

const unsigned long onTime = 100UL;
unsigned long startTime;

byte dataTable[] = {0x99, 0xB0, 0xA4, 0xF9};

void setup() {
  DDRD = 0xff;    // port D all outputs
  DDRB = 0x0f;    // port B bottom four bits are outputs
}

void loop() {

  displayNow();
}

void displayNow() {

  static byte digit;

  if (micros() - startTime > onTime) {
    if ( digit > 3)
      digit = 0;
    PORTB = B1111;
    PORTD = dataTable[digit];
    PORTB = ~(1 << digit);
    digit = digit + 1;
    startTime = micros();
  }
}