Drive a 7 segment display with an Arduino
- 192parts on the bench
- 25boards running now
- 1.00xreal time, on every board
A seven-segment display looks like a component with some intelligence in it, and it has none. It is eight ordinary LEDs in one package (seven bars and a decimal point) with one shared connection. The circuit above drives one directly from seven Uno pins and turns it into an electronic die: press the button and the digit tumbles, slows down and settles on a number from 1 to 6.
Press the button on the breadboard now. The simulated Uno is running the same sketch you can read below.
What you need
- An Arduino Uno
- A common cathode 7 segment display
- Resistors: one 220 ohm here, seven for the better wiring described below
- One pushbutton
- A breadboard and jumper wires
Wiring
Common cathode or common anode
The shared connection is either all the LEDs’ cathodes (common cathode) or all their anodes (common anode). With a common cathode display, the common pin goes to ground and a segment lights when its pin goes HIGH. With a common anode, the common goes to 5 volts and a segment lights when its pin goes LOW, so every digit pattern in the code is inverted. Buying one type and writing code for the other is the single most common reason a display shows nonsense.
The segments are named a to g, clockwise from the top, with g across the middle. Any datasheet or the part’s pin table maps those letters to physical pins.
The code, explained
Each digit is stored as one byte, one bit per segment, with bit 0 as segment a. The number 1 lights only b and c, so it is 0x06. The number 8 lights all seven, so it is 0x7F. The show() function walks the seven bits and writes each one to its pin. That table of bytes is what people mean by a seven-segment font, and it is the same in almost every sketch.
The tumble is the roll repeated fourteen times with a delay that grows a little each time, which reads as the die slowing down. The final face comes from random(1, 7), which returns 1 to 6. The sketch then waits for the button to be released, so holding it down does not roll again.
const int SEG_PINS[] = {2, 3, 4, 5, 6, 7, 8};
// bit 0 a, 1 b, 2 c, 3 d, 4 e, 5 f, 6 g
const uint8_t DIGITS[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D,
};
static void show(uint8_t mask) {
for (int i = 0; i < 7; i++) {
digitalWrite(SEG_PINS[i], (mask >> i) & 1 ? HIGH : LOW);
}
}One resistor or seven
This circuit takes the cheap shortcut: a single resistor on the common cathode. It works, and you can see its flaw if you look closely. All the lit segments share that one resistor’s current, so the more segments are on, the less each one gets. An 8 is visibly dimmer than a 1. The simulator shows this because it solves the LEDs as real diodes rather than as lamps that are simply on or off.
The right way is seven resistors, one in series with each segment, and the common pin straight to ground. Then every segment gets the same current whatever digit is showing. Try it in the editor: move the resistor and add six more, and the brightness evens out.
Try it in the editor
Open the circuit in the editor and select the display. Change its common property from cathode to anode, and rewire the common pin to 5 volts instead of ground. Every digit now shows as its own negative, because the sketch still drives segments HIGH to light them. Invert the output in show() with a single exclamation mark and the digits come back.
Then fix the brightness. Move the single resistor off the common pin, wire the common straight to ground, and put a 220 ohm resistor in series with each segment instead. Roll an 8 and a 1 and compare: with seven resistors they are equally bright.
Finally, add randomSeed(micros()) after the first button press, so the rolls are not the same sequence after every reset.
Common mistakes
Using a common anode display with common cathode code, or the reverse. If every digit looks like its own negative, invert the pattern or swap the display type in the part’s properties.
Wiring segments by position on the package rather than by letter. The pins are not in segment order. Check the pin table before wiring.
No current limit at all. Each segment is an LED and needs a resistor somewhere in its path.
Running out of pins for more digits. Four digits wired this way would need 32 pins. Multi-digit displays share the segment lines and switch the digits in turn, or use a driver chip such as a 74HC595, a MAX7219 or the TM1637, all of which are on the bench.
Questions
How do I show the decimal point?
It is an eighth LED with its own pin, usually called DP. Wire it to another pin through its own resistor and write it HIGH when you want the point lit.
How do I drive a 4-digit 7 segment display?
With a driver chip or by multiplexing. The desk clock example drives a four-digit TM1637 module on two wires, and the shift register page covers driving LEDs through a 74HC595.
Why is random() giving the same sequence every time?
Because the generator starts from the same seed after every reset. Call randomSeed() in setup() with something that varies, such as the time of the first button press in microseconds.
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.