Author Topic: How To: Prevent from ghosting on seven-segment display  (Read 1532 times)

0 Members and 1 Guest are viewing this topic.

Offline danchouzhouTopic starter

  • Contributor
  • Posts: 18
  • Country: tw
How To: Prevent from ghosting on seven-segment display
« on: April 09, 2025, 12:54:51 pm »
Many people encounter the issue of "ghosting" when implementing multiplexed scanning to drive seven-segment displays. Recently, I had a project that used multiplexed scanning, so I decided to write an article documenting the process for others to reference in the future.

To explain the specifics of "ghosting," you can refer to the comparison images below. The left image shows each digit with a "ghost" of the adjacent right digit, whereas the right image demonstrates a properly handled multiplexed scanning process.

This project's requirements were not complicated: the client wanted a square wave generator with an adjustable output frequency of 1~6 kHz, along with a power-off memory feature. After restoring power, the device should output the last adjusted frequency. Of course, the frequency adjustment should be visible to the user, so I opted for a four-digit seven-segment display.

For this project, I chose the Nuvoton MG51FB9AE because it operates at 24 MHz, whereas the commonly used N76S003 runs at 16 MHz. These two chips are pin-to-pin compatible. The higher clock speed is needed not for CPU performance but for output frequency resolution. The higher the input frequency for PWM or Timer, the greater the division steps for the output, and consequently, the higher the frequency resolution. Readers who require precise output frequency or duty cycle should consider this aspect in their selection criteria.


What is multiplexed scanning?

The schematic below is part of the project's circuit. It has an Everlight ELF-511 - a standard four-digit common cathode seven-segment display. The segments a~g and dot of each digit are directly connected inside the display, while the common cathode for each digit is separate.

The signal from P0 will only activate one of Q1~Q4 at a time, and while active, P1 sends corresponding data for segments a~g and dot. In short, this process lights up each digit in turn. When the switching speed exceeds the persistence of vision, all digits appear continuously illuminated.

Advantages of multiplexed scanning

If each digit of the four-digit seven-segment display were independently controlled, its common points would directly connect to the power supply, and requiring 32 pins to control (4 × segments a~g and dot).

With multiplexed scanning, only 12 pins are needed: 8 for segments a~g and dot and 4 for scanning lines. This reduction is significant for design, benefiting BOM cost, circuit size, and layout complexity. For example, with just 12 pins, the project can be completed using the MG51FB9AE in TSSOP20 package. If 32 pins were required, an MCU with at least a 48-pin package would be necessary.

Firmware implementation

First, an array `u8segments[16]` is created to map 0~F (hexadecimal 0x0~0xF). Through modulo operations and a switch-case statement, the units, tens, hundreds, and thousands digits of a number `u32num` are extracted, looked up in `u8segments[]`, and sent to P1. The switch-case selects the current digit based on `u8digit`, which is simultaneously output to P0 as the scanning line signal. The variable `u8digit` shifts left one position with each iteration (e.g., 0x10 -> 0x20 -> 0x40 -> 0x80) before cycling back to 0x10. This process synchronizes the active digit with the number being output to P1. The entire routine is wrapped into a `showNum()` function, which is repeatedly called every 1 ms to complete the scanning process.

Common mistakes: Incorrect sequence causing ghosting

Switching scanning lines before updating the segment data can lead to ghosting. Since the program executes sequentially, so when the scanning line switches to the next digit, the previous digit's data is still being output, causing the previous digit to briefly appear. Observing the implementation, the scan moves from right to left, which explains the shadowing from the adjacent right digit.

Code: [Select]
void showNum(uint32_t u32num)
{
    static uint8_t u8digit = 0x10;

    P0 = u8digit;

    switch (u8digit) {
        case 0x10:
            P1 = u8segments[u32num % 10];
            break;

        case 0x20:
            P1 = u8segments[u32num / 10 % 10];
            break;

        case 0x40:
            P1 = u8segments[u32num / 100 % 10];
            break;

        case 0x80:
            P1 = u8segments[u32num / 1000];
            break;
       
        default:
            u8digit = 0x10;
    }

    if (u8digit < 0x80)
        u8digit = u8digit << 1;
    else
        u8digit = 0x10;
}

Correct implementation

To resolve this, the output must be briefly disabled before switching to the next digit to prevent the previous digit from momentarily displaying. In this case, I first set `P0 = 0;` to disable the scanning line output, then update `P1` with the segment data for the next digit. Finally, I set `P0 = u8digit;` to activate the scanning line for the new digit. Alternatively, the output can be briefly disabled by turning off the data lines. A possible sequence is as follows:
Disable scanning lines > Output new data > Update scanning lines
Disable data lines > Update scanning lines > Output new data

Code: [Select]
void showNum(uint32_t u32num)
{
    static uint8_t u8digit = 0x10;

    P0 = 0;

    switch (u8digit) {
        case 0x10:
            P1 = u8segments[u32num % 10];
            break;

        case 0x20:
            P1 = u8segments[u32num / 10 % 10];
            break;

        case 0x40:
            P1 = u8segments[u32num / 100 % 10];
            break;

        case 0x80:
            P1 = u8segments[u32num / 1000];
            break;
       
        default:
            u8digit = 0x10;
    }

    P0 = u8digit;

    if (u8digit < 0x80)
        u8digit = u8digit << 1;
    else
        u8digit = 0x10;
}

Full code:
Code: [Select]
#include "numicro_8051.h"

__code const uint8_t u8segments[16] = {
//    abcdefg.
    0b11111100, 0b01100000, 0b11011010, 0b11110010, 0b01100110, 0b10110110, 0b00111110, 0b11100000,
    0b11111110, 0b11100110, 0b11101110, 0b00111110, 0b10011100, 0b01111010, 0b10011110, 0b10001110
};

void showNum(uint32_t u32num)
{
    static uint8_t u8digit = 0x10;

    P0 = 0;

    switch (u8digit) {
        case 0x10:
            P1 = u8segments[u32num % 10];
            break;

        case 0x20:
            P1 = u8segments[u32num / 10 % 10];
            break;

        case 0x40:
            P1 = u8segments[u32num / 100 % 10];
            break;

        case 0x80:
            P1 = u8segments[u32num / 1000];
            break;
       
        default:
            u8digit = 0x10;
    }

    P0 = u8digit;

    if (u8digit < 0x80)
        u8digit = u8digit << 1;
    else
        u8digit = 0x10;
}

void main(void)
{
    uint32_t u32num = 1234;

    TA = 0xAA;
    TA = 0x55;
    CKEN |= 0xC0; // EXTEN
    while(!(CKSWT & 0x08)); // ECLKST
    TA = 0xAA;
    TA = 0x55;
    CKSWT |= 0x02; // Switch to external clock source
    while(CKEN & 0x01); // CKSWTF
    TA = 0xAA;
    TA = 0x55;
    CKEN &= 0xDF; // Disable HIRC

    P0M1 &= 0x0F;
    P0M2 |= 0xF0;
    /* P1 as push-pull output mode */
    P1M1 = 0x00;
    P1M2 = 0xFF;

    CKCON |= 0x08; // Timer 0 source from Fsys directly
    TH0 = (uint8_t)(41536 >> 8); // 65536 - 24000
    TL0 = (uint8_t)(41536 & 0xFF);
    TMOD |= 0x01; // Timer 0 mode 1
    TCON |= 0x10; // Timer 0 run

    while(1)
    {
        showNum(u32num);

        /* 1 ms delay */
        while (!(TCON & 0x20)); // Wait until timer 0 overflow
        TH0 = (uint8_t)(41536 >> 8); // 65536 - 24000
        TL0 = (uint8_t)(41536 & 0xFF);
        TCON &= 0xDF; // Clear TF0
    }
}
« Last Edit: April 09, 2025, 01:09:49 pm by danchouzhou »
If you are using Nuvoton chips, visit NuForum to get faster, more professional advice and technical support from their experts.
https://forum.nuvoton.com/
 
The following users thanked this post: DavidAlfa, Picuino

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 16388
  • Country: fr
Re: How To: Prevent from ghosting on seven-segment display
« Reply #1 on: April 09, 2025, 04:34:32 pm »
A small detail but that indeed many will overlook. Similarly when multiplexing any kind of signal:
- open the previous channel
- change the signal
- close the new channel

Here you have 4 distinctly controllable switches for multiplexing, but when the multiplexer is "always on" (meaning one path is always closed whatever the configuration), then you can't avoid ghosting, so that's something to keep in mind.
 
The following users thanked this post: danchouzhou

Offline xvr

  • Frequent Contributor
  • **
  • Posts: 656
  • Country: ie
    • LinkedIn
Re: How To: Prevent from ghosting on seven-segment display
« Reply #2 on: April 09, 2025, 07:28:17 pm »
Quote
Here you have 4 distinctly controllable switches for multiplexing, but when the multiplexer is "always on" (meaning one path is always closed whatever the configuration), then you can't avoid ghosting, so that's something to keep in mind.
Then you can turn off segments (P1) instead of digits (P0). Ghosting will be avoided
 
The following users thanked this post: danchouzhou

Online PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 2368
  • Country: au
Re: How To: Prevent from ghosting on seven-segment display
« Reply #3 on: April 09, 2025, 10:23:15 pm »
Then you can turn off segments (P1) instead of digits (P0). Ghosting will be avoided

Yes, the main issue here is the storage time of the BJT - it just needs some small dead time of no-drive to avoid.
Designers might choose to omit the base resistors and use NMOS instead, or they might use NMOS with RFI reducing gate resistors, to soften the switching edges, and use no-drive delays.
   
Some MCUs have enough port capability to drive directly.
 
The following users thanked this post: danchouzhou

Online PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 2368
  • Country: au
Re: How To: Prevent from ghosting on seven-segment display
« Reply #4 on: April 09, 2025, 11:20:12 pm »
For this project, I chose the Nuvoton MG51FB9AE because it operates at 24 MHz, whereas the commonly used N76S003 runs at 16 MHz. These two chips are pin-to-pin compatible.

I notice Nuvoton has recently expanded their MG51 series, with a new die having more ADC and more PWM and more RAM/FLASH.
Nuvoton MG51 Brief 2.0

It is unclear if the new PWM can run at a higher CLK than the MCU Core? 
That faster PWM capability is becoming more common on MCUs.
 
The following users thanked this post: danchouzhou

Offline danchouzhouTopic starter

  • Contributor
  • Posts: 18
  • Country: tw
Re: How To: Prevent from ghosting on seven-segment display
« Reply #5 on: April 10, 2025, 01:47:17 am »
I notice Nuvoton has recently expanded their MG51 series, with a new die having more ADC and more PWM and more RAM/FLASH.
Nuvoton MG51 Brief 2.0

It is unclear if the new PWM can run at a higher CLK than the MCU Core? 
That faster PWM capability is becoming more common on MCUs.
For MG51, the maximum clock rate is 24 MHz (same as core clock).

I see some other models support higher frequency. For example, NUC1263 the maximum clock rate for PWM is 144 MHz which is higher than 72 MHz core clock.
If you are using Nuvoton chips, visit NuForum to get faster, more professional advice and technical support from their experts.
https://forum.nuvoton.com/
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 16388
  • Country: fr
Re: How To: Prevent from ghosting on seven-segment display
« Reply #6 on: April 10, 2025, 12:01:12 pm »
Not to start a flamewar at all, but just curious about what made you pick a MCU with a 8051 core when there are now thousands of very cheap 32-bit MCUs, much more capable and supported by mainstream toolchains.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6587
  • Country: es
Re: How To: Prevent from ghosting on seven-segment display
« Reply #7 on: April 10, 2025, 12:39:23 pm »
Yes, the main issue here is the storage time of the BJT - it just needs some small dead time of no-drive to avoid.
Not here, just bad programming.
Code: [Select]
void showNum(uint32_t u32num){
    static uint8_t u8digit = 0x10;
    P0 = u8digit;                              // <----- Enable the segment first, before the new data is loaded
    switch (u8digit) {                         // <----- Load segment data

Do we need a full article for this? What's next? How to not burn leds with a novel technique, using current limiting resistors? :)
« Last Edit: April 10, 2025, 12:41:23 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: tooki, danchouzhou

Offline danchouzhouTopic starter

  • Contributor
  • Posts: 18
  • Country: tw
Re: How To: Prevent from ghosting on seven-segment display
« Reply #8 on: April 10, 2025, 01:51:52 pm »
Not to start a flamewar at all, but just curious about what made you pick a MCU with a 8051 core when there are now thousands of very cheap 32-bit MCUs, much more capable and supported by mainstream toolchains.
I have been using Nuvoton's MCU for more than 10 years. Each series has similar peripherals, and I am already very familiar with them.
Although I know there are many cheap MCUs on the market, the Nuvoton works fine and I have no motivation to replace it.

Do we need a full article for this? What's next? How to not burn leds with a novel technique, using current limiting resistors? :)
Many people have asked me the same question, and even some inexperienced think that ghosting is an inevitable phenomenon :palm:. This lead me to write an article to explain it. Of course this article is not for a pro like you.
If you are using Nuvoton chips, visit NuForum to get faster, more professional advice and technical support from their experts.
https://forum.nuvoton.com/
 

Offline RoGeorge

  • Super Contributor
  • ***
  • Posts: 7367
  • Country: ro
Re: How To: Prevent from ghosting on seven-segment display
« Reply #9 on: April 10, 2025, 02:00:48 pm »
Many people encounter the issue of "ghosting" when implementing multiplexed scanning to drive seven-segment displays.

Many people?  More like nobody.  Looks like a disguised commercial.
Addressing a non existing problem with a long and tedious first post.  :-//

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6587
  • Country: es
Re: How To: Prevent from ghosting on seven-segment display
« Reply #10 on: April 10, 2025, 02:37:26 pm »
@danchouzhou you're absolutely right, sorry, sometimes I take things for granted, or think they're obvious, but it wasn't always the case!
Everyone had to start from the very bottom, the forum is and should be place for everyone, from rookie to pros.
BTW I'm just a rookie "plus", nowhere close to pro!

I remember the time where I struggled with delays in assembler, so I used a potentiometer to adjust the speed of the PIC RC oscillator and thus the motor speed  :).

« Last Edit: April 10, 2025, 02:40:20 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: danchouzhou

Offline danchouzhouTopic starter

  • Contributor
  • Posts: 18
  • Country: tw
Re: How To: Prevent from ghosting on seven-segment display
« Reply #11 on: April 10, 2025, 04:34:12 pm »
@danchouzhou you're absolutely right, sorry, sometimes I take things for granted, or think they're obvious, but it wasn't always the case!
Everyone had to start from the very bottom, the forum is and should be place for everyone, from rookie to pros.
BTW I'm just a rookie "plus", nowhere close to pro!

I remember the time where I struggled with delays in assembler, so I used a potentiometer to adjust the speed of the PIC RC oscillator and thus the motor speed  :).


Thank you David! Don't be so modest, I saw your project it's amazing.  :)
If you are using Nuvoton chips, visit NuForum to get faster, more professional advice and technical support from their experts.
https://forum.nuvoton.com/
 

Offline jnk0le

  • Regular Contributor
  • *
  • Posts: 137
  • Country: pl
Re: How To: Prevent from ghosting on seven-segment display
« Reply #12 on: April 10, 2025, 05:29:34 pm »
Then you can turn off segments (P1) instead of digits (P0). Ghosting will be avoided

Yes, the main issue here is the storage time of the BJT - it just needs some small dead time of no-drive to avoid.
In this case issue was switching columns before segments, but those division/modulo ops provided enough dead time.

Even with the proper method, common emitter will ghost if segments come from precalculated buffer.
 
The following users thanked this post: danchouzhou

Offline peter-h

  • Super Contributor
  • ***
  • Posts: 4639
  • Country: gb
  • Doing electronics since the 1960s...
Re: How To: Prevent from ghosting on seven-segment display
« Reply #13 on: April 10, 2025, 05:54:37 pm »
LEDs produce a visible output even with a miniscule % of the "normal" current, so proper off slots during muxing are important.

Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 
The following users thanked this post: danchouzhou

Offline prosper

  • Regular Contributor
  • *
  • Posts: 120
  • Country: ca
Re: How To: Prevent from ghosting on seven-segment display
« Reply #14 on: April 16, 2025, 09:00:16 pm »

Yes, the main issue here is the storage time of the BJT - it just needs some small dead time of no-drive to avoid.
Designers might choose to omit the base resistors and use NMOS instead, or they might use NMOS with RFI reducing gate resistors, to soften the switching edges, and use no-drive delays.
   
Some MCUs have enough port capability to drive directly.


I recently had the same issue driving those 8x8 matrix LEDs. Indeed, the holding time of the BJT is the culprit. I found a few solutions. In the end, I went with a combination of #1 and #4 for my first revision, and #2 for my second rev boards.

1. Slow down your multiplexing. Cycle through the common pins more slowly. Reduces ghosting, but won't eliminate it entirely (but maybe enough to be un-noticeable)
2. Use FETs intead of BJTs
3. Add a delay between shutting off your BJT, and updating your segments. Hold time is usually only a few hundred nanoseconds depending on base drive, so it doesn't take much
4. Add a speedup capacitor in series (edit: parallel) with the base resistor (100pF is a good place to start)
5. use an emitter follower configuration (though this will cost you a few hundred millivolts, and potentially lower your brightness)
6. Or maybe an anti-saturation diode
« Last Edit: April 16, 2025, 11:30:58 pm by prosper »
 

Offline pqass

  • Super Contributor
  • ***
  • Posts: 1024
  • Country: ca
Re: How To: Prevent from ghosting on seven-segment display
« Reply #15 on: April 16, 2025, 09:59:00 pm »
"...storage time of the BJT"  Duh!  Of course!

I'd been prototyping a couple of large DIY 4" LED displays for an arcade game; each segment driven by a single white LED @20mA indirectly lighting the cavity.
And I was noticing that sometimes an LED was lit by a tiny dot when it should be off completely.
I'm using two cascaded 74HC595 - one for the NPN-driven segments and the other for PNP-driven common anodes - for a total of 8, 7-seg. displays.
The '595s are latched at the same time so there shouldn't be misdirected segment info with the wrong digit.
It's a tiny dot so it isn't noticeable with the red diffuser sheet so there wasn't any urgency to change my multiplex algorithm.

For others, where even the tiny amount of light is noticeable, you can add a small cap (100pf?) across the base resistor of the digit driver (see this link) to remove this off delay.


 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf