Arduino Case-study: 7-segment LED Display (Part I)

Nguyễn Bá Anh
12 min readSep 8, 2018

--

The very first story in a series which I will introduce to you who want to get a full-detail tutorial to deal with Arduino UNO and 7-segment LED Display.

Live Demo and Full Documented Source Code HERE.

Notice: This link should be placed at the end of the story but I put it here for your convenience. Please read the instruction first if you are new-comers.

I’ve read many posts on the Internet about this topic but I didn’t satisfy yet, or lack of explanation in detail or source code was too long, full of boilerplates. So in this story you will learn how to display decimal digits from 0 to 9 on a single LED Display, step-by-step to build-up the project, learn to code and share you some optimization tricks to make it work effectively: Long explain, short code but run fast and reusable!

Preparation

+-------------------+----------+----------+
| Component | Value | Quantity |
+-------------------+----------+----------+
| Arduino | UNO R3 | 01 |
| 7-seg LED Display | CC | 01 | *
| Breadboard | - | 01 |
| Resistor | 220 Ohm | 02 |
| Jumper | Male | -- |
+-------------------+----------+----------+

YOu have nothing but still interesting? Don’t worry you can use a Simulator, goto tinkercad.com, signup then login and goto Circuit / Create new Circuit. You will find enough stuff to start immediately. Of course I prefer using Tinkercad too, it saves me a lot of time to provide you nice visual examples.

And it’s always better if you have basic programming skills in C or C++.
You have to know how to build and upload the program to Arduino board or how to use a Simulator also.

7-segment LED Display structure *

Now is the time you have to select which kind of display you want to do: Common Cathode or Common Anode. If you knew it, skip this and jump to the next section.

F0: 7-Segment Display Layout

Because the display is made from 8 single LEDs for every segment, from a to g and a decimal point. And a LED is a Diode, while a diode allows the current to travel through it from Anode to Cathode only.

F1: Visualization of a single Diode

There is no problem for a single diode, but 8 diodes together always have 2 methods to connect: share the same Anode or Cathode, they are reflected!

F2: Common Anode vs. Common Cathode circuits

I prefer using Common Cathode display because it’s quite easy to imagine for beginners: connect Cathode to GND and provide a positive current to the other side, it lights up! I calculated a table with ready-to-use values:

    comm        SYMBOL  BINARY-MASK DECIMAL HEXA
g f | a b 0 11111100 252 0xFC
| | | | | 1 01100000 96 0x60
--------- 2 11011010 218 0xDA
10 9 8 7 6| 3 11110010 242 0xF2
| -A- | 4 01100110 102 0x66
| F| |B | 5 10110110 182 0xB6
| -G- | 6 10111110 190 0xBE
| E| |C | 7 11100000 224 0xE0
| -D- * | 8 11111110 254 0xFE
|1 2 3 4 5| 9 11110110 246 0xF6
|_________|
| | | | | Common Cathode, pin order: a,b,c,d,e,f,g,dp
e d | c dp
comm ngbaanh@medium.com

The easiest digit is 8, just turn on all segments from a to g

Of course! You can choose Common Anode, no problem! Because I will show you how to deal with both types just using the same source code.

Wiring up

There are many ways to wire up the components together but if you care a little bit, it will be easier for you to code and maintain the project later.

Take up your Arduino and examine the digital pins which marked from D0 to D13. In my opinion, you should start from Pin#13 down to lower pins. You shouldn’t start from D0 because D0 and D1 are for serial ports which you may see them in the future. Here is my suggestion, you should draw a table, no need to remember everything in your brain, the table below will be reused later, remember to take care of it:

+-------+---------+-----------------+-----------------+
| Index | Segment | LED Display Pin | Arduino Pin |
+-------+---------+-----------------+-----------------+
| 0 | a | 7 | 12 |
| 1 | b | 6 | 13 |
| 2 | c | 4 | 7 |
| 3 | d | 2 | 8 |
| 4 | e | 1 | 9 |
| 5 | f | 9 | 11 |
| 6 | g | 10 | 10 |
| 7 | dp | 5 | 6 |
+-------+---------+-----------------+-----------------+
| - | - | 3 and 8 | GND(CC), 5V(CA) | *
+-------+---------+-----------------+-----------------+

Look at the table and let’s go.
(*) Pay attention to Common Pin #3 and #8. Connect them to the ground pin (GND) if you are using CC Display; connect them to pin 5V if you are using CA Display.

Left: Common Cathode. Right: Common Anode.

Notice: Remember to protect your LED by wiring a resistor to it, this is very important, or your display may be damaged before the project goes to the end. Your display has 2 common pins, so you must prepare 2 resistors for them.

Program it!

Open Arduino IDE and create a new project. If you are using TinkerCad, click Code button and change the editor mode to Text. You will see a starter program similar to this:

// Run once on start
void setup() {
}
// Infinite loop after setup()
void loop() {
}

If you see some junk code inside, you can clear them. Before you can code something, you must prepare enough input information for your program. Let’s declare some info about pin values and their working mode.

Declaration

First step, we MUST give a convention to agree that which value is ON/ACTIVE or OFF/NEGATIVE. As I said before, I chose using Common Cathode Display so I would like to apply this convention:

const bool ON = HIGH; // HIGH value as ON state for CC display. 
const bool OFF = !ON; // Reflect it, do not use LOW here.

In the whole project we use ON, OFF as the replacement of HIGH, LOW. If you prefer using Common Anode display, replace the first line by const bool ON = LOW; , that’s enough.

Next, declare an array of your display pins connected to Arduino digital pins, which its elements are 7 segments from a to g and the last is dp. You can leave them separated but it’s more convenient if you use an array for faster tracing the data. Look into the table we prepared above, and make sure the pins are in order as below:

// LED Pin Array:       a,  b, c, d, e,  f,  g, dp
byte LED_DISPLAY[] = { 12, 13, 7, 8, 9, 11, 10, 6 };

You can prepare the order following your choice, but you should keep the dp at the last, keep reading till the end you’ll know why ;-)

This array indices (of the table) are equivalent to the table we draw above. So LED_DISPLAY[0] → a, LED_DISPLAY[2] → c, etc.

We need to display digits from 0 to 9, so we have bit mask for each digit, which 1represents ON and 0represents OFFfor Common Cathode (CC) display, converted to hexa values as a new array:

// LED Digit Value Array: 0 to 9
byte DIGIT_CC[] = { 0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6 };

Feel confused? Just follow it. Remember that in C programming language, array index always starts at 0, so at its position represents itself value. This is great, we easily access the value of digits just by their array indices. E.g: digit5 → DIGIT_CC[5], etc.

Initial Setup

You have to specify pin mode as OUTPUT for the arduino, then it can control the led via our program. Travel through the LED_DISPLAY array by a while loop, put this code inside the function setup():

byte i = 0;
while (i < 8) { pinMode(LED_DISPLAY[i++], OUTPUT); }

Notice: setup() function is called once at the time the arduino starts, so don’t process your job here. You should use loop() function.

Implementation

Before going to do the main task, there’s an optional task for beginners to understand how 7-segment LED Display works, is to do a small task to display something on the display. We provide the current to every pin of the segment inputs. Like we want to turn on segments {a,b,f,g} to make a small symbol ° in the image:

digitalWrite(LED_DISPLAY[0], ON); // a
digitalWrite(LED_DISPLAY[1], ON); // b
digitalWrite(LED_DISPLAY[5], ON); // f
digitalWrite(LED_DISPLAY[6], ON); // g

Then turn off the others:

digitalWrite(LED_DISPLAY[2], OFF); // c
digitalWrite(LED_DISPLAY[3], OFF); // d
digitalWrite(LED_DISPLAY[4], OFF); // e
digitalWrite(LED_DISPLAY[7], OFF); // dp

Delay the display state to 500ms to improve performance.

delay(500);

Now we sort the pin follow the indices of LED_DISPLAY array from a to dp, we have these states:

{ ON, ON, OFF, OFF, OFF, ON, ON, OFF }{ 1,1,0,0,0,1,1,0 }

If you choose data type byte to store that states, you loose 8 bytes just for one symbol. The space is doubled to 16 bytes if you choose to use int.

But do you still remember that 8 bits made of 1 byte, the display has 8 control pins. So the binary number 11000110 (1 byte) is equal to the decimal value 198, and it is also equal to hexa value C6. Instead of to write 8 lines of code for the symbol which is not able to reuse, you can use a while loop to make it applies the value automatically:

byte value = 0xC6; // equals 0b11000110 in Binary
byte i = 0, mask = 0b10000000;
while (i < 8) {
digitalWrite(LED_DISPLAY[i++], mask & value);
mask >>= 1;
// delay(200);
}

We use bitwise operator to sequently extract the bits of value using logic operator &, every while loop the bit 1 extract the mask shifts the bit 1 to the right and continue to filter the next bit, the while loop explained below:

i = 0 — segment a:
mask: 10000000
value: 11000110
seg a: 1....... — ON (1 & 1 = 1)
i = 1 — segment b:
mask: 01000000
value: 11000110
seg b: .1...... — ON (1 & 1 = 1)
i = 2 — segment c:
mask: 00100000 — mask
value: 11000110 — value
seg c: ..0..... — OFF (1 & 0 = 0)
...

Now, it’s time to work with our digits. You want to display digit 5? Just replace the value above by DIGIT_CC[5] , quite easy right? Given hexa values are the binary masks for digit values from 0 to 9, without the decimal point.

Hence you don’t need to use 2-dimension array to represent digits anymore. This old way spends too much memory and you have to write a lot of code. Finally I bring to you the whole function:

void displayDigit(byte digit) {
if (digit < 0 || digit > 9) { return; } // skip on invalid
byte value = DIGIT_CC[digit]; // [!!!]
byte i = 0, mask = 0b10000000;
while (i < 8) {
digitalWrite(LED_DISPLAY[i++], mask & value);
mask >>= 1;
}
}

Call the function inside the loop(): displayDigit(5); We get the result:

I’m using CC Display, so focus on the left one. The right one (CA) is reflected from the left one.

At the 2nd line in the function above, reflect the value if you are choosing to use CA Display:
byte value = ON ? DIGIT_CC[digit] : ~DIGIT_CC[digit];

If you want to make the right one (CA) display the digit properly, fix the fisrt line to:
const bool ON = LOW;

We get new result:

When ON = LOW, the CA Display works!

Okey, now we want it to display digit from 0 to 9, let’s make a loop and travel the DIGIT_CC array values:

void runDigits() {
int i = 0;
while (i <= 9) {
displayDigit(i++);
delay(1000); // ms
}
}

Done? Yes, it’s done. Here is the full source code, it’s really short and beautiful:

const bool ON = HIGH; // Common Cathode: ON = HIGH;
const bool OFF = !ON;
byte LED_DISPLAY[] = { 12, 13, 7, 8, 9, 11, 10, 6 };
byte DIGIT_CC[] = { 0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6 };
void setup() {
byte i = 0;
while (i < 8) { pinMode(LED_DISPLAY[i++], OUTPUT); }
}
void loop() {
runDigits();
}
void displayDigit(byte digit) {
if (digit < 0 || digit > 9) { return; } // skip on invalid
byte value = ON ? DIGIT_CC[digit] : ~DIGIT_CC[digit];
byte i = 0, mask = 0b10000000;
while (i < 8) {
digitalWrite(LED_DISPLAY[i++], mask & value);
mask >>= 1;
}
}
void runDigits() {
int i = 0;
while (i <= 9) {
displayDigit(i++);
delay(1000); // ms
}
}

Adjustment for Common Anode Display

The source code above is for Common Cathode display, how about the remaining Common Anode one? You don’t need to write a new code, let’s change only-one-line at the top:

const bool ON = LOW; // Common Anode: ON = LOW;

That’s enough. I mentioned you about this in the previous section but it’s better to make a new section for the equality.

Tricky with Decimal Point

As I noticed you before, you should put the dp pin value at the last position in the array. Why? Because at the last position, we just add 1 unit to the digit value to make to turn on the DP segment. E.g: Value of digit ‘5’ is 0xB6 , you want to display ‘5.’ then you need to add 1, 0xB6 + 1 = 0xB7 , apply this new value to the display and you’ll get the final result without any complex task. let’s write a new function for it:

void displayDigitWithDot(byte digit, bool showDP) {
if (digit < 0 || digit > 9) { return; } // skip on invalid
byte value = DIGIT_CC[digit] + (showDP ? 1 : 0); // HERE
byte i = 0, mask = 0b10000000;
value = ON ? value : ~value;
while (i < 8) {
digitalWrite(LED_DISPLAY[i++], mask & value);
mask >>= 1;
}
}

But you can make it shorter by using available functions we made before:

void displayDigitWithDot(byte digit, bool showDP) {
displayDigit(digit); // Display the digit without the dot
digitalWrite(LED_DISPLAY[7], showDP); // turn on if showDP == ON
}

This function also works with both CC and CA display, please don’t use true or false in the parameter, let’s use ON or OFF as we defined instead. For the digit ‘5.’, let’s call: displayDigitWithDot(5, ON);

Performance improvement

The function loop() is called during the life time of Arduino board, so it has to re-calculate every thing as the speed of the processor on it. We are displaying digits for a study-case and that can make the performance decreased. So please use delay(int) function at the right time. In my recommendation 500 or 1000 milliseconds interval is acceptable. You can write new functions:

void displayDigitWithDelay(byte digit, unsigned int interval) {
displayDigit(digit);
delay(interval);
}
void displayDigitWithDotAndDelay(byte digit, bool showDP, unsigned int interval) {
displayDigitWithDot(digit, showDP);
delay(interval);
}

Expansion

This source code is designed not for display digits only, you can calculate the binary mask to display a lot of symbols you may interested in: A, B, C, D, E, F, G, H, I, J, L, N, P, S, U, b, o, a, e, n, r,… You can expand it yourself.

Long enough! The first part ended here. Hope it’s useful for you, especially you guys who are beginner in this. You can go to the top of this post for link to source code and schematic layout.

In the next story in this series, I will introduce you about BCD encoder, how to build it and explain how it works with 7-segment LED display and Arduino.

Thanks for your notice, leave a comment if I made any mistake in this story.

--

--