If we previously made one LED blink, this time we display numbers that people can read. A 7-segment display uses 7 bar-shaped LEDs (ag) and a decimal point (dp) to draw 09 and some letters. The key points are twofold: ① a lookup table that converts numbers into bit patterns for "which segments to light up," ② multiplexing (dynamic driving) to light multiple digits while saving pins.
In this session, on top of the port output from parts 6~7, we add new tools: a lookup table and time division. By turning on the 4-digit number "1234" one digit at a time very quickly, the persistence of vision effect makes it appear as if all four digits are lit simultaneously.
Simulator Note: μVision simulator does not have a graphic 7-segment display. Therefore, in this session, we use the Port Window/Logic Analyzer to check if "segment patterns (P2) and digit selections (P0) cycle through the correct values," and the number → segment conversion itself is verified on a PC using
pc_test.c.
Learning Objectives
- Explain the segment layout (a~g, dp) of the 7-segment display and the difference in common Cathode/Anode.
- Create a lookup table that converts 0~9 to segment bit patterns.
- Separate digit selection and segment output to drive multiple digits.
- Make 4 digits appear simultaneously using multiplexing (time division + persistence of vision).
- Apply the sequence "turn off → change → turn on" to prevent ghosting.
Target Environment
- Compiler: Keil C51.
- Device: AT89C52 (μVision simulator).
- Verification: μVision simulator (Port 0 / Port 2 window, Logic Analyzer). Conversion from number to code is done on PC
gcc(pc_test.c).
Key Concepts
1) 7-segment structure and wiring
A digit is made up of 7 segments (a~g) and a decimal point (dp). One side of all segments is commonly connected, and depending on the polarity of the connection, there are two types.
| Type | Common pin | Segment ON condition | Note |
|---|---|---|---|
| Common cathode | Connect all LEDs' (−) to GND | Segment pin = 1 | Assumed in this text |
| Common anode | Connect all LEDs' (+) to VCC | Segment pin = 0 | Covered in Assignment 1 |
The story of 7 active segments in High/Low-and is the same. Here we assume common cathode (bit = 1 is ON).
2) Segment bit mapping
Map the segments to port bits as follows (this text convention).
aaa Bits: a=0 b=1 c=2 d=3 e=4 f=5 g=6 dp=7
f b Number 0 = a,b,c,d,e,f ON, g OFF
f b = 0b00111111 = 0x3F
ggg
e c
e c
ddd (dp)
3) 0~9 lookup table
Since the segments to be lit are fixed for each number, make a table in advance and use SEG_CODE[n] to retrieve it. This is faster and easier to read than calculating the bits every time.
| Number | Segments to light | Code | Number | Segments to light | Code |
|---|---|---|---|---|---|
| 0 | a b c d e f | 0x3F |
5 | a c d f g | 0x6D |
| 1 | b c | 0x06 |
6 | a c d e f g | 0x7D |
| 2 | a b d e g | 0x5B |
7 | a b c | 0x07 |
| 3 | a b c d g | 0x4F |
8 | a b c d e f g | 0x7F |
| 4 | b c f g | 0x66 |
9 | a b c d f g | 0x6F |
4) Digit selection — Reason for saving pins
If you connect each of the 4 digits separately with 7 pins each, you need 28 pins. Instead, the segment pins (a~g) are shared among the 4 digits, and you use the digit select pins to choose "which digit to turn on." Then 7 (segments) + 4 (digits) = 11 pins are sufficient. However, since the segment lines are shared, only one digit can be lit at a time.
5) Multiplexing and Afterimage
If only one digit can be lit, how do we display all four digits? Turn them on alternately very quickly. Digit 0 → Digit 1 → Digit 2 → Digit 3 → back to Digit 0 … by repeating this at intervals of hundreds of µs, the eye perceives all four digits as being on simultaneously due to afterimage.
The order is important. If you change the segment pattern while the digit is on, an incorrect pattern will briefly appear on that digit at the moment of change, causing ghosting. Therefore, always:
1) Turn off all digits → 2) Write new segment pattern → 3) Turn on only that digit
Creating a μVision Project
Project → New µVision Project…→ name itc51_seg7.- Device: Atmel → AT89C52.
- Include STARTUP.A51 (Yes).
- After adding
main.c, copy and paste the code below. Options for Target → Debug → Use Simulator.- Rebuild(F7) → Start Debug(Ctrl+F5).
- Observation: Display
Peripherals → I/O-Ports → Port 0(digit select) andPort 2(segments). Or addP0.0~P0.3andP2.0~P2.6toView → Analysis Windows → Logic Analyzer.
Examples
Example 1 — main.c: 4-Digit Multiplexing
Turn on "1234" one digit at a time quickly alternately. To prevent afterimage, turn off → write → turn on for each digit.
#include <reg52.h>
/*
*Lesson 12 Example — 4-digit 7-segment dynamic driving (multiplexing).
*Target: AT89C52, Compiler: Keil C51.
*
*Wiring assumptions (common cathode):
*- Segments a~g, dp connected to P2. Segment turns ON when bit=1.
* a=P2.0 b=P2.1 c=P2.2 d=P2.3 e=P2.4 f=P2.5 g=P2.6 dp=P2.7
*- Digit selection via lower 4 bits of P0. Turns the digit ON when bit=1 (1 bit per digit).
* digit0=P0.0 digit1=P0.1 digit2=P0.2 digit3=P0.3
*
*Multiplexing: only one digit is ON at a time, cycling through the 4 digits very quickly.
*Thanks to human eye persistence of vision, it appears as if all 4 digits are ON simultaneously.
*/
#define SEG_PORT P2 /*Segment pattern output*/
#define DIG_PORT P0 /*Digit selection*/
/*Common cathode 0~9 segment codes (bit=1 → segment ON).
*Bit order: a(0) b(1) c(2) d(3) e(4) f(5) g(6) dp(7)*/
static const unsigned char SEG_CODE[10] = {
0x3F, /* 0 */ 0x06, /* 1 */ 0x5B, /* 2 */ 0x4F, /* 3 */ 0x66, /* 4 */
0x6D, /* 5 */ 0x7D, /* 6 */ 0x07, /* 7 */ 0x7F, /* 8 */ 0x6F /* 9 */
};
/*Short software delay (digit hold time). For exact timing, refer to Lesson 9 timer.*/
static void delay(unsigned int count)
{
unsigned int i;
for (i = 0; i < count; i++) {
/*Sending time*/
}
}
void main(void)
{
/*4 digits to display. digits[0] is the leftmost (digit 0). Here: "1234".*/
unsigned char digits[4] = { 1, 2, 3, 4 };
unsigned char d;
while (1) {
for (d = 0; d < 4; d++) {
DIG_PORT = 0x00; /*1) Turn off all digits (to prevent ghosting)*/
SEG_PORT = SEG_CODE[digits[d]]; /*2) Segment pattern for this digit*/
DIG_PORT = (unsigned char)(1u << d); /*3) Turn only this digit ON*/
delay(150); /*4) Hold briefly then move to the next digit*/
}
}
}
Example 2 — pc_test.c: Lookup Table Verification
Without hardware, draw which segments light up for 0~9 in letters (capital letters = ON, - = OFF).
#include <stdio.h>
/*
*Lecture 12 PC Verification — Outputs the common cathode segment codes for 0~9 and the segments that light up.
*Checks only the accuracy of the lookup table without hardware.
*/
static const unsigned char SEG_CODE[10] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
/*Lit segments are indicated by the letters a~g, and off segments are indicated by '-'.*/
static void print_seg(unsigned char code)
{
static const char names[7] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
int i;
for (i = 0; i < 7; i++) {
putchar((code & (1u << i)) ? names[i] : '-');
}
}
int main(void)
{
int n;
for (n = 0; n <= 9; n++) {
printf("%d -> 0x%02X ", n, SEG_CODE[n]);
print_seg(SEG_CODE[n]);
putchar('\n');
}
return 0;
}
Checking with Simulator
- Port 2 (segments) changes in the order of digits:
0x06(1) →0x5B(2) →0x4F(3) →0x66(4) ... - Port 0 (digit selection) shifts one bit at a time as
0x01→0x02→0x04→0x08, synchronized with the pattern and timing of P2. - Looking at it with a Logic Analyzer, at any given moment only one P0 pin is High, and the number pattern for that digit appears on P2.
- Expected Behavior: (If it were a real display) "1234" appears stably across 4 digits. In simulation, it can be checked through synchronized cycles of P0/P2 values.
Verification on a PC
gcc pc_test.c -o pc_test && ./pc_test
Expected output (stdout):
0 -> 0x3F ABCDEF-
1 -> 0x06 -BC----
2 -> 0x5B AB-DE-G
3 -> 0x4F ABCD--G
4 -> 0x66 -BC--FG
5 -> 0x6D A-CD-FG
6 -> 0x7D A-CDEFG
7 -> 0x07 ABC----
8 -> 0x7F ABCDEFG
9 -> 0x6F ABCD-FG
ABCDEF- (digit 0) has segments a~f lit and g off, meaning the middle bar is missing, forming the "0" shape.
Common Mistakes
Q. The digits appear overlapping or there is a faint afterimage.
A. This happens because the digits are not turned off before changing the segment pattern. If the pattern changes while a digit is on, the incorrect number may briefly appear in that position. Always follow the order: Turn off (DIG_PORT=0) → Write pattern → Turn on the digit.
Q. Only one digit is bright while the others are dim.
A. The hold time per digit (delay) is too long, or it stops on only one digit. All 4 digits need to cycle fast enough (usually 1~5 ms per digit, total refresh rate over 50Hz) to appear at the same brightness. In simulation, check whether P0 cycles through all 4 bits.
Q. The display is reversed (4321).
A. This occurs if the order of digits[] or the mapping of digit selection bits (1<<d) is reversed. Decide which physical digit digits[0] connects to and maintain consistency.
Q. I bought a common anode display, but all the segments turn on or all turn off.
A. This code assumes a common cathode (bit=1 is ON). For a common anode, it is the opposite of ON/OFF, so you need to invert the segment code (~). Try creating the conversion yourself in Exercise 1.
Q. I want to place the lookup table in ROM instead of RAM.
A. In Keil C51, if you add keywords like code similar to unsigned char code SEG_CODE[10] = {...};, it will be placed in program memory (ROM) to save RAM. This track used the regular const for portability, but if RAM is tight on the 8051, code is useful.
Summary
- 7-segment displays draw numbers with a~g (+dp), and for common cathode, bit=1 is ON.
- Number→pattern is quickly converted using the lookup table
SEG_CODE[n]. - Even when increasing the number of digits, sharing segment lines saves pins (only one digit active at a time).
- Multiplexing: quickly alternating the active digits makes it appear that all are on due to persistence of vision.
- Preventing ghosting: follow the sequence of off → write pattern → turn on digit.
Try it yourself
Solve the assignment below. The reference solution is shown below.