
Seven Segment Display with 8051 Microcontroller

Hello friends! Today, we will interface a seven-segment display with the 8051 microcontroller and use it to show the digits zero through nine. This is a useful first display project because you can trace every visible segment back to one output bit in your program.
We will use the AT89C51 and examine the original Proteus circuit with its 74LS245 buffer. Before writing the code, we will identify the display type, build a digit table and calculate the timing. We will also correct an important issue in the historical schematic: LED segments need individual current-limiting resistors.
The original screenshots and project download remain available below. They are useful references for the simulation, but some details need care when moving to hardware. In particular, the displayed circuit and active-high digit codes correspond to a common-cathode arrangement, despite the earlier description calling it common-anode.
What Is a Seven-Segment Display?
A seven-segment display contains seven illuminated bars, normally identified as a through g. Different combinations form decimal digits. Many devices also include a decimal point, called dp, which gives us an eighth controllable LED section.
The conventional positions are a at the top, b at the upper right, c at the lower right, d at the bottom, e at the lower left, f at the upper left and g across the middle. A digit such as 1 uses b and c. A digit such as 8 uses all seven main segments.
The display does not understand the number stored in a C variable. Your program must translate that number into the required LED pattern. This is the purpose of the lookup table we will build shortly. It also explains why sending the ASCII character '3' to the port does not automatically display the numeral three.
Common Cathode and Common Anode
In a common-cathode display, the LED cathodes share a connection that normally goes to ground. A segment lights when its anode is driven sufficiently positive through a resistor. With a noninverting driver, a logic one therefore selects a segment.
In a common-anode display, the shared anodes connect to the positive supply. A segment lights when its cathode is pulled low through its resistor and driver. With the same noninverting signal path, a logic zero selects a segment. The common terminal must be wired correctly as well as changing the software polarity.
| Property | Common cathode | Common anode |
|---|---|---|
| Common connection | Ground | Positive LED supply |
| Active level with a noninverting driver | High | Low |
| Blank pattern for eight connected segments | 0x00 | 0xFF |
| Pattern for zero with our bit mapping | 0x3F | 0xC0 |
Do not identify the common pin from its position alone. Physical pin numbering varies between display packages, and some packages provide two common pins. Read the exact display datasheet and connect the pins according to that device, rather than copying the visual order of a simulator symbol.
Understand the Original 8051 Circuit
The original schematic places Port 2 between the AT89C51 and the A side of a 74LS245. The corresponding B outputs feed the display. Its common terminal returns to ground through a single 47-ohm resistor, showing why the active-high digit table belongs to a common-cathode arrangement.
| Part | Role in the project |
|---|---|
| AT89C51 | Writes a new segment pattern at each counting interval |
| Seven-segment LED display | Shows one decimal digit |
| 74LS245 | Buffers eight digital signals without inverting them |
| Segment resistors | Limit each illuminated segment's current |
| Crystal and associated components | Provide the controller's timing reference |
| Reset and supply components | Establish startup conditions and stable operation |
The drawing labels its crystal as 11.0592 MHz, while the earlier instructions mentioned 16 MHz. Those settings cannot be treated as equivalent. The revised example below uses 11.0592 MHz, matching the drawing. Set the Proteus processor clock to the same value, and recalculate the timer reload if you intentionally use another frequency.
For the physical controller, check supply, ground, oscillator and reset connections against the AT89C51 datasheet. Its reset input is active high. A manual reset circuit must generate the required high pulse, rather than merely grounding RST. Keep EA high for execution from internal program memory and add local supply decoupling.
What Does the 74LS245 Do?
The 74LS245 is an octal bus transceiver. With output enable active and direction set from A to B, each B output follows its corresponding A input. It is not a constant-current LED driver, and it does not convert a segment pattern into a different numeral.
For the standard 20-pin package, VCC is pin 20, ground is pin 10, DIR is pin 1 and active-low output enable is pin 19. DIR high and output enable low select A-to-B operation. The Texas Instruments SN74LS245 datasheet provides the package mapping and electrical limits.
Do not assume its high output equals the five-volt supply under load. TI specifies a minimum high-output voltage of 2.4 V at a 3 mA source-current test condition, and a lower guaranteed high voltage at heavier specified loading. That limited voltage headroom matters when sourcing current into LEDs.
For a reliable physical build, select a driver and display combination with sufficient voltage and current margin, then check total package loading. A dedicated LED driver can simplify a larger design. The original buffer remains useful for understanding the simulation's digital signal path.
Give Each Segment Its Own Resistor
The original drawing's single resistor in the common lead is not a substitute for one resistor per segment. The number of lit LEDs changes from digit to digit. With one shared resistor, the total current and its division among the LEDs depend on the pattern and device variations.
For example, digit 1 lights two segments while digit 8 lights seven. A shared current path can make their individual segment brightness differ. Use a separate resistor in each segment path, including the decimal point when it is connected, and connect the common terminal directly to its appropriate return or supply.
Calculate a Starting Resistance
For a sourcing driver and common-cathode display, the approximate relationship is R = (Vdrive - VF) / Isegment. For a sinking driver and common-anode display, use R = (Vsupply - VF - Vsink) / Isegment. All voltages must correspond to the intended operating current.
As a separate illustrative design calculation, suppose a suitable driver supplies 5.0 V, the red LED drops 2.0 V and the target segment current is 5 mA. Then R = (5.0 - 2.0) / 0.005 = 600 ohms. Choosing 620 ohms gives approximately 4.84 mA under those assumptions.
This example does not assume a loaded 74LS245 output supplies 5.0 V. Substitute the actual driver characteristics before selecting a resistor for that circuit. Check the highest supply, lowest plausible LED forward voltage and resistor tolerance when calculating maximum current; check the opposite extremes for sufficient brightness.
At 5 mA through 620 ohms, resistor dissipation is I²R = 0.005² × 620 = 0.0155 W. Seven segments would draw roughly 35 mA in total, excluding the decimal point and other circuitry. Check the display, driver, common connection and power supply together rather than considering only one output pin.
Map Port 2 Bits to Segments
We will use P2.0 for segment a and continue in order through P2.6 for g. P2.7 controls the decimal point when present. If your wiring uses another order, the following hexadecimal values must be rearranged to match it.
| Port bit | Segment | Bit weight |
|---|---|---|
| P2.0 | a | 0x01 |
| P2.1 | b | 0x02 |
| P2.2 | c | 0x04 |
| P2.3 | d | 0x08 |
| P2.4 | e | 0x10 |
| P2.5 | f | 0x20 |
| P2.6 | g | 0x40 |
| P2.7 | dp | 0x80 |
To show zero, turn on a, b, c, d, e and f while leaving g and dp off. Adding the corresponding weights gives 1 + 2 + 4 + 8 + 16 + 32 = 63, or 0x3F. For one, only b and c are needed, giving 2 + 4 = 6, or 0x06.
| Digit | Segments on | Common-cathode code | Common-anode code |
|---|---|---|---|
| 0 | a b c d e f | 0x3F | 0xC0 |
| 1 | b c | 0x06 | 0xF9 |
| 2 | a b d e g | 0x5B | 0xA4 |
| 3 | a b c d g | 0x4F | 0xB0 |
| 4 | b c f g | 0x66 | 0x99 |
| 5 | a c d f g | 0x6D | 0x92 |
| 6 | a c d e f g | 0x7D | 0x82 |
| 7 | a b c | 0x07 | 0xF8 |
| 8 | a b c d e f g | 0x7F | 0x80 |
| 9 | a b c d f g | 0x6F | 0x90 |
The common-anode column is the eight-bit complement of the common-cathode column. The decimal point stays off because its bit changes from zero to one. If an additional inverting driver is introduced, account for that inversion instead of applying this table blindly.
Calculate the Counting Delay
A software loop containing empty instructions depends on the compiler and optimization settings. We will instead use Timer 0 in 16-bit mode to create nominal ten-millisecond intervals, then repeat that interval one hundred times between digits.
For the classic twelve-clock 8051 timing used here, the timer frequency is fosc / 12. At 11.0592 MHz, that is 921,600 timer increments per second. A ten-millisecond interval therefore requires 921,600 × 0.010 = 9,216 increments.
A 16-bit timer overflows after reaching 65,536. Its starting value should therefore be 65,536 - 9,216 = 56,320, which is 0xDC00. Load TH0 with 0xDC and TL0 with 0x00 before starting each interval.
One hundred such intervals produce a nominal second, plus the instructions used to stop, reload and restart the timer. That is suitable for a visible counting demonstration. It is not a precision clock. For accurate periodic timing, use an interrupt-driven schedule that accounts for reload behavior and the oscillator's error.
If you use 16 MHz on the same twelve-clock architecture, ten milliseconds needs approximately 13,333 timer increments and a reload of 0xCBEB. Changing only the crystal while retaining 0xDC00 would make the count run too quickly. Faster 8051 derivatives may also use different timer clock divisors.
C51 Program for Digits Zero Through Nine
The example below is intended for Keil C51 with the classic AT89C51 register definitions. It uses the stated port mapping, an 11.0592 MHz clock and Timer 0 dedicated to this delay. It does not require an LCD, keypad or serial terminal.
#include <REG51.H>
/* 0: common cathode; 1: common anode.
Change the physical common connection as well. */
#define COMMON_ANODE 0
unsigned char code digit_pattern[10] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
void show_digit(unsigned char number)
{
unsigned char pattern = 0x00;
if (number < 10)
pattern = digit_pattern[number];
#if COMMON_ANODE
pattern = (unsigned char)(~pattern);
#endif
P2 = pattern;
}
void delay_10ms(void)
{
TR0 = 0;
TF0 = 0;
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
while (TF0 == 0) {
/* Wait for the hardware timer overflow. */
}
TR0 = 0;
TF0 = 0;
}
void delay_one_second(void)
{
unsigned char count;
for (count = 0; count < 100; ++count)
delay_10ms();
}
void main(void)
{
unsigned char digit;
show_digit(10); /* Out-of-range value blanks the display. */
ET0 = 0; /* Timer 0 is polled in this example. */
TR0 = 0;
TMOD = (TMOD & 0xF0) | 0x01;
while (1) {
for (digit = 0; digit < 10; ++digit) {
show_digit(digit);
delay_one_second();
}
}
}
The lookup table is stored in program memory using C51's code qualifier. The bounds check prevents an invalid index from reading beyond its ten entries. Values outside zero through nine blank the display, giving the application a deliberate fallback behavior.
The TMOD assignment changes Timer 0's lower nibble while preserving Timer 1's configuration. ET0 is cleared because this example polls the overflow flag instead of servicing a Timer 0 interrupt. Do not combine it unchanged with another module that already owns Timer 0.
The delay blocks the main program. That is acceptable for this single-purpose demonstration, but a keypad, communication link or multiplexed display may require regular service. In a larger application, replace the blocking delay with a timer tick and update the displayed digit when its scheduled interval has elapsed.
Load and Run the Proteus Simulation
- Select the intended AT89C51 device in your C51 project and add the source file.
- Enable HEX-file generation, compile and resolve any build errors.
- Set the controller's Program File property to the generated application HEX.
- Set its simulated clock to 11.0592 MHz for the code shown above.
- Check that the display model, common connection and COMMON_ANODE setting agree.
- Verify the buffer direction and output-enable connections.
- Run the simulation and observe zero through nine before the sequence repeats.
The retained image below illustrates the original project. Before reproducing it as hardware, replace its shared common resistor with correctly calculated individual segment resistors and review the driver and reset circuitry as discussed above.
Download the Original Project
The historical project archive is attached below. Its source, timing choices and schematic may differ from the revised example. Keep a separate copy when experimenting so you can compare the original implementation with your changes.
Download Simulation and CodeAfter compiling a modified program, select the newly generated HEX file explicitly. A correct source listing on the screen does not mean Proteus has loaded that build. Check the output file location and modification time when a simulation continues showing older behavior.
Test the Mapping Before Testing the Counter
First blank the display. Then activate one segment at a time using the bit weights in the mapping table. For common cathode, write 0x01, 0x02, 0x04 and so on. For common anode with the same noninverting path, use the complemented values.
Confirm each physical segment against its intended letter. This short exercise separates a wiring mistake from a lookup-table mistake. Testing only the number eight cannot reveal swapped segment wires because all seven segments are illuminated together.
Next compare digits 1 and 8. On hardware, check segment current and brightness under both patterns. Large brightness changes suggest an unsuitable shared resistor, supply droop or driver loading. Measure current within the circuit's ratings rather than increasing the supply to make a dim display brighter.
Check startup too. Classic 8051 ports reset high, which can briefly enable every segment in an active-high circuit. If startup blanking matters, design a defined output-enable arrangement that keeps the driver disabled until valid data is ready. Do not leave the enable input floating.
Troubleshooting Common Problems
| Problem | What to inspect |
|---|---|
| No segments illuminate | Common connection, supply, buffer enable, direction and program execution |
| Digits look like inverted patterns | Common-anode/common-cathode selection and any driver inversion |
| Only particular digits look wrong | Bit-to-segment wiring and table entries |
| Decimal point stays on | Bit 7 polarity and decimal-point connection |
| Counter runs at the wrong speed | Processor clock, timer divisor and reload value |
| Brightness changes with the number | Individual resistors, output-voltage headroom and aggregate current |
| Display remains on the previous program's output | Selected HEX path and whether the build completed |
Project Review and Extending the Design
This interface makes binary encoding visible: one byte becomes a recognizable numeral. It also introduces an important distinction between a correct logic diagram and an electrically complete LED circuit. A simulation that displays the intended digit does not establish resistor sizing, brightness or driver temperature on a real board.
For several digits, you can multiplex the display by sharing segment lines and selecting one common connection at a time through suitable drivers. Blank the active digit before changing the segment pattern, then enable the next digit. This sequencing helps prevent unwanted segments appearing during transitions.
As a scheduling example, four digits with one millisecond allocated to each produce a four-millisecond frame, or 250 frames per second. Each digit has approximately 25 percent duty cycle before blanking overhead. Do not automatically multiply LED current by four: allowable pulse current depends on the exact LED, pulse duration, duty cycle and driver ratings.
A timer-driven multiplex routine should refresh the display independently of slower application tasks. For a counter requiring only one digit, the direct interface here remains simpler. Choose the additional complexity only when the application needs more displayed information.
Frequently Asked Questions
Why does writing 3 to Port 2 not show the digit three?
The numeric value 3 sets only bits zero and one. Under our mapping, that lights segments a and b on a common-cathode display. The numeral three requires the pattern 0x4F.
Can I remove the 74LS245?
Only after designing an alternative drive arrangement that respects the controller's electrical limits and the required LED current. Do not assume a microcontroller port can directly supply every illuminated segment just because a simulator accepts the connection.
Can this program display hexadecimal digits A through F?
The provided table contains only decimal digits zero through nine. Hexadecimal display requires six additional patterns and corresponding bounds changes. Some letters need stylized forms because seven bars cannot represent every character clearly.
Does changing COMMON_ANODE also fix the hardware?
No. It changes the output polarity only. The display type, common terminal connection, driver arrangement and resistor paths must all match the selected configuration.
Is the one-second interval exact?
No. It includes software overhead and oscillator error. The calculation explains the nominal timer interval; a clock or measurement instrument needs a more precise timing design and suitable verification.
























Comments
0