12. 7-Segment Display (Multiplexing)
Now we show human-readable digits. A 7-segment uses 7 bar LEDs (a~g) + a dot to draw 0–9. The keys: ① a lookup table mapping a digit to "which bars to light", and ② multiplexing (dynamic drive) that shows multiple digits while saving pins. We light "1234" one digit at a time, very fast, so persistence of vision makes all four appear lit at once.
What you'll learn
- 1Explain segment layout (a~g·dp) and common cathode/anode
- 2Build a lookup table mapping 0–9 to segment bit patterns
- 3Drive multiple digits by separating digit-select from segment output
- 4Show 4 digits at once via multiplexing (time-division + persistence)
- 5Avoid ghosting with the off→change→on order
Introduction
On top of lessons 6–7's port output we add a lookup table and time-division. This lesson assumes common cathode (bit=1 is ON). (μVision's simulator has no 7-segment graphic, so we verify the P0/P2 value cycling in the Port windows/Logic Analyzer, and the digit→code conversion in pc_test.)
Key concepts
1) 0–9 lookup table (common cathode)
Bit layout: a=0 b=1 c=2 d=3 e=4 f=5 g=6 dp=7. Each digit's segments are fixed, so precompute a table and fetch SEG_CODE[n].
static const unsigned char SEG_CODE[10] = {
0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F
}; /* 0–9 */2) Digit select — saving pins
Wiring 4 digits with 7 pins each needs 28 pins. Instead, the 4 digits share the segment pins (a~g) and a digit-select pin picks "which digit". That's 7+4=11 pins. But since segment lines are shared, only one digit can be lit at any instant.
3) Multiplexing and ghost prevention
1) turn off all digits → 2) write the new segment pattern → 3) turn on only that digitChanging the pattern while a digit is on briefly shows a wrong pattern there (ghosting). Always follow off → write → on.
Core example
#include <reg52.h>
#define SEG_PORT P2 /* segments */
#define DIG_PORT P0 /* digit select */
static const unsigned char SEG_CODE[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
static void delay(unsigned int n){ unsigned int i; for(i=0;i<n;i++){} }
void main(void){
unsigned char digits[4] = {1,2,3,4};
unsigned char d;
while(1){
for(d=0; d<4; d++){
DIG_PORT = 0x00; /* 1) off (ghost prevention) */
SEG_PORT = SEG_CODE[digits[d]]; /* 2) write pattern */
DIG_PORT = (unsigned char)(1u << d); /* 3) turn on only this digit */
delay(150);
}
}
}PC check: draw the lit segments as letters — 0→ABCDEF-, 1→-BC----, 8→ABCDEFG — to verify the lookup table.
Common mistakes
Q. Digits overlap or show faint ghosting.
A. You changed the pattern before turning the digit off. Always follow off (DIG_PORT=0) → write pattern → turn on digit.
Q. One digit is bright, others are dim.
A. The per-digit hold time is too long, or it stalls on one digit. Cycle the 4 digits fast enough (1–5ms each, ≥50Hz overall) for equal brightness.
Q. Common anode lights all or nothing.
A. This code assumes common cathode (bit=1 ON). For common anode, ON/OFF are reversed — invert (~) the segment code.
Summary
- 7-segment draws digits with a~g(+dp); common cathode is ON at bit=1
- Map digit→pattern fast with the lookup table SEG_CODE[n]
- Sharing segment lines saves pins as you add digits (one digit at a time)
- Multiplexing: cycle digits fast so persistence shows them all lit
- Prevent ghosting: off → write pattern → turn on digit
Exercises
- Invert SEG_CODE (~) to drive a common-anode display
- Use a timer (lesson 9) to make a counter that increments 0000→0001… every second
- Extend the lookup table to show hex digits (A–F)
All lecture materials and example code (with homework and answers) are openly available on GitHub.
View on GitHub ↗