Electronics Software Development

Interfacing A 7-Segment Display To An Arduino Uno

Arduino 7-Segment Graphic
Written by John Woolsey

Skill Level: Intermediate

Introduction

This tutorial will teach you how to interface a 7-segment display to an Arduino Uno. A basic understanding of electronics and programming is expected along with some familiarity with the Arduino platform.

If you are new to the Arduino platform or would just like to refresh your knowledge, please see our Blink: Making An LED Blink On An Arduino Uno tutorial before proceeding with this one. In addition, this tutorial will use a solderless breadboard to build a circuit from a schematic (circuit) diagram. The How to Use Breadboards, How to Use a Breadboard, and How to Read a Schematic are some good guides for learning how to translate a schematic to a breadboard.

The resources created for this tutorial are available on GitHub for your reference.

What Is Needed

Background Information

My development system consists of the Arduino Uno WiFi Rev2 development board connected to a macOS based computer with the desktop Arduino IDE. If you are using a different Arduino model or computer setup, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.

In this tutorial, I will be connecting a common anode 7-segment display to an Arduino through a 74LS47 BCD To 7-Segment Decoder/Driver integrated circuit (IC). This chip decodes binary coded decimal (BCD), which is just basic binary numbers for the 0-9 decimal digits, into the individual LED segments of a 7-segment display. The 4 BCD inputs of the chip are denoted as A through D, while the individual LED segment outputs are denoted as a through g. See the 74LS47 datasheet for more information. While usage of the IC is not strictly necessary, it does allow us to use fewer Arduino GPIO pins and write a simpler program. If you prefer to use a common cathode display instead, replace the 74LS47 decoder IC with a 74LS48 chip and make sure to connect the common terminals of the display to ground instead of power.

If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.

Building The Circuit

Before connecting any circuitry to your Arduino board, disconnect the Arduino from power and your computer. This avoids accidental damage during wiring.

Place the components and wire up the circuit on a breadboard according to the schematic diagram shown below and then connect it to your Arduino.

Arduino Uno BCD 7-Segment Display Schematic

Note, you may come across other 7-segment display circuits using only one resistor at the common terminal(s), but using resistors across all the segments, as done here, ensures a constant level of brightness regardless of how many segments are lit.

The circuit should look similar to the one shown below once completed. Note, I used a two digit display (that I had available) in my circuit, but I only connected one digit.

Arduino Uno BCD 7-Segment Display Circuit

Once the circuit is built, you can connect your Arduino to your computer with the USB cable.

Writing The Software

Open the Arduino IDE and create a new sketch named BCD7SegmentDisplay with the code shown below.

// BCD7SegmentDisplay - BCD7SegmentDisplay.ino
//
// Description:
// Implements a counter that is displayed on a common anode 7-segment display
// driven through a BCD To 7-Segment Decoder/Driver (74LS47) IC.  The count is
// reset to 0 when a button is pressed.
//
// Circuit:
// Momentary push button connected to pin D2.
// Common anode 7-segment display connected through 74LS47 IC to pins D3-D6.
//
// Created by John Woolsey on 01/30/2019.
// Copyright © 2019 Woolsey Workshop.  All rights reserved.


// Pin Mapping
const byte button = 2;  // momentary push button, fires interrupt service routine
const byte bcdA   = 3;  // binary coded decimal (BCD) least significant bit (LSB) for 74LS47 A input pin
const byte bcdB   = 4;
const byte bcdC   = 5;
const byte bcdD   = 6;  // binary coded decimal (BCD) most significant bit (MSB) for 74LS47 D input pin


// Global Variables
unsigned long lastTimeButtonPressed = 0;  // used for debouncing button
byte count = 0;                           // display counter

void setup() {
   // Pin configuration
   pinMode(button, INPUT_PULLUP);  // utilize microprocessor's internal pull-up resistor
   pinMode(bcdA, OUTPUT);
   pinMode(bcdB, OUTPUT);
   pinMode(bcdC, OUTPUT);
   pinMode(bcdD, OUTPUT);

   // Initialize interrupt service routine
   // Calls resetCount() function when button is pressed,
   // i.e., the button pin value falls from high to low.
#ifdef ARDUINO_AVR_UNO_WIFI_REV2
   attachInterrupt(button, resetCount, FALLING);
#else
   attachInterrupt(digitalPinToInterrupt(button), resetCount, FALLING);
#endif
}


void loop() {
   displayWrite(count);         // update display
   count++;                     // increase counter
   if (count == 10) count = 0;  // reset to 0 if count exceeds 9
   delay(1000);                 // wait one second
}


// Resets counter and display to 0
void resetCount() {
   unsigned long currentTimeButtonPressed = millis();  // get current time
   if (currentTimeButtonPressed - lastTimeButtonPressed > 250) {  // allow for button debounce time of 250 ms
      lastTimeButtonPressed = currentTimeButtonPressed;
      count = 0;        // reset counter
      displayWrite(0);  // reset display
   }
}


// Writes value to display using binary coded decimal
void displayWrite(byte value) {
#ifdef ARDUINO_AVR_UNO_WIFI_REV2
   digitalWrite(bcdA, PinStatus(bitRead(value, 0)));  // BCD LSB
   digitalWrite(bcdB, PinStatus(bitRead(value, 1)));
   digitalWrite(bcdC, PinStatus(bitRead(value, 2)));
   digitalWrite(bcdD, PinStatus(bitRead(value, 3)));  // BCD MSB
#else
   digitalWrite(bcdA, bitRead(value, 0));  // BCD LSB
   digitalWrite(bcdB, bitRead(value, 1));
   digitalWrite(bcdC, bitRead(value, 2));
   digitalWrite(bcdD, bitRead(value, 3));  // BCD MSB
#endif
}

The code should be fairly straightforward to understand, but I would like to mention a few items of interest.

The bcdA through bcdD pins represent the A through D BCD inputs of the 74LS47 chip, which itself subsequently drives the a through g segment inputs of the 7-segment display.

The pin mode of the button is specified as INPUT_PULLUP, which uses the internal pull-up resistor for the pin within the microprocessor on the Arduino board, instead of just INPUT since we did not use an external pull-up resistor in the circuit.

Since I am using the new Arduino Uno WiFi Rev2 board, I came across a few differences in how it works versus the standard Uno board. For this reason, I am using preprocessor directives to check if I am on the ARDUINO_AVR_UNO_WIFI_REV2 board or another one.

The first instance is on line 40 where I am setting up the interrupt service routine that triggers a call to the resetCount() function when the button is pressed. Typically we would use the digitalPinToInterrupt() function to translate the pin number to the interrupt number. However, that function does not work as expected for the WiFi Rev2 board. Luckily, no translation is required for the WiFi Rev2, so we can just simply use button.

The second usage is on line 69 and deals with a change made to the argument types of the digitalWrite() function. For the WiFi Rev2, we need to add a PinStatus cast since the underlying type is no longer an integer as is returned by the bitRead() function.

Speaking of bitRead(), this function just translates a number into its individual binary bits. For example bitRead(5, 0) will return 1 since the least significant bit in the number 5, 00000101 in binary, is 1. Likewise, bitRead(5, 1) will return 0. This is how we are sending our values to the BCD decoder chip.

Buttons used within circuits are notoriously noisy, meaning once pressed they can take a little while for their contacts to settle down to a known state. For instance, the interrupt service routine we are using can actually get called multiple times for a single button press. For this reason, a technique called debouncing is usually applied to register a button’s state. In software we can handle this by waiting a short amount of time, usually around 100 ms or so. In our program this is handled within the resetCount() function and I have conservatively set a debounce wait time of 250 ms.

If there is something you don’t quite understand or needs further explanation, please let me know in the comment section and I will try to answer your question.

Running And Testing The System

Now that our circuit is built and our software is written, it is time to run and test our creation.

Upload (Sketch > Upload) the sketch to the board and you should see the 7-segment display counting up from 0. When it reaches 9, it should reset back to 0. Press the pushbutton on the breadboard at anytime and the display should reset back to 0.

Summary

In this tutorial, we learned how to interface a common anode 7-segment display to an Arduino Uno using a 74LS47 BCD To 7-Segment Decoder/Driver integrated circuit. We also learned how to take into account some of the subtle differences between the new Arduino Uno WiFi Rev2 board and the standard Arduino Uno Rev3.

The final source code and schematic used for this tutorial is located on GitHub.

Thank you for joining me in this journey and I hope you enjoyed the experience. Please feel free to share your thoughts in the comments section below.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.
 
He knew he wanted to work with electronics from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).
 
He put himself through college (The University of Texas at Austin) by working at Motorola where he worked for many years afterward in the Semiconductor Products Sector in Research and Development.
 
John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.
 
Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.

15 Comments

  • Hello John,
    This is one of the most recent posts I’ve stumbled across searching for information. At the beginning of the tutorial you stated to contact you if we were using a different set up. Which I am, and could use some assistance.
    First, (for some background) I’m currently attending the University of Advancing Technology for a BS in robotics and embedded systems. The current course I’m in the professor has given a choice in projects to reflect concepts in RSA. I’ve chosen to create a single digit 7 segment Arduino clock. The project doesn’t necessarily have to work, but I would like to turn in a fully functional product if possible. I currently have around 10 days to complete this project so hopefully this reaches you fairly quickly.
    I’m using a Arduino Uno REV3, with a Adafruit Datalogger/RTC shield. The shield is using a PCF8523 RTC IC, and I’m attempting to get the time from the RTC to the seven segment via a 74LS48N decoder IC. Another difference here is that I am running a common cathode 7 seg.
    I’ve inspected your code example but I’m having a hard time applying it to my use case. I’d be more than happy to supply a diagram if need be if you would be willing to assist. I just need someone to point me in the right direction here. I have some code written, but it’s only running a count up, and it’s certainly not very readable at the moment.

    • Sure, I can try to help. Where are you getting stuck? I would suggest that you first get the base display circuit and code working before attaching the shield, then we can see what changes are necessary once the shield is attached.

  • I’ll have to remove the shield to proceed, but I’m just unclear as to 1.) which outputs on the Arduino I need to utilize, and 2.) how the code would be constructed to retrieve the Hours and Minutes and send it to the seven segment. Currently, the code I have sends the information through the serial via the serial monitor in the IDE. I do have all the components functioning from my initial assessment, but I’m just not getting the proper output at the seven seg. Initially, I attempted to send directly to the seven seg via binary on digital pins 2-9 for a count-up. Which was successful. But I recently understood that I would need a BCD decoder in the circuit to get the time from the RTC as the PCF8523 sends out all code in BCD format. This is where I’m having difficulty as I’m unsure of the written code I would need to accomplish this. At the same time I reached out to you I also posted the issue in Quora and have had some given answers that I will explore this evening. But certainly I do appreciate the response and any assistance you could provide. Cheers~ J. Rusch

    • I sounds like you are having less difficulty with the content of this tutorial and more difficulty with interfacing the Adafruit Datalogger/RTC shield to your Arduino. I do not have any experience with this shield. Have you checked out the Adafruit Data Logger Shield learning guide? It provides library and code examples for accessing the RTC. Also, how are you trying to display the clock data on a single 7-segment digit as most clock values would require two digits?

  • That’s likely the case. Yes, I have looked at that guide and unfortunately it isn’t very much help to me. And that is something I may have failed to mention. What I want to do is display one digit at a time. My intended configuration I want to use an RGB LED output to 4 different colors. The color of the LED will indicate the place of the digit on the seven seg. So format would be HH:MM. For example, Magenta = 1st digit hour, Green = 2nd digit hour, Red= 1st digit minute, etc. I’m not concerning myself with seconds in this project, although if I can get the format right it’d be nice to use the DP output to show a seconds indicator.

    • If you are using the libraries mentioned in the Adafruit guide, you should be able to get the hour and minute values (in integers), break out the individual digits, and then send them to the 7-segment display. For instance the A pin for minutes would be

      int digit_10s = minutes / 10; // get 10s digit for minutes
      int digit_1s = minutes % 10; // get 1s digit for minutes
      digitalWrite(bcdA, bitRead(digit_10s, 0));
      // some time later
      digitalWrite(bcdA, bitRead(digit_1s, 0));

      Is this what you are looking for?

  • Its certainly a step in the right direction, I will try to implement and see if I can get some results. Thanks a ton, l know its difficult to try and help with such limited information, I really wish I had more time with this. But as I stated, its not a requirement that the project is functional. Its more about the process itself.

  • So, I attempted a quick experimental sketch. I tried to make some modifications for this specific case. But I’m getting errors.

    #include

    void setup() {
    // Pin mapping _setup code here, to run once:
    const byte bcdA = 2; //LSB
    const byte bcdB = 3;
    const byte bcdC = 4;
    const byte bcdD = 5; //MSB

    // Pin configuration
    pinMode(bcdA, OUTPUT);
    pinMode(bcdB, OUTPUT);
    pinMode(bcdC, OUTPUT);
    pinMode(bcdD, OUTPUT);

    RTC_PCF8523 rtc;
    DateTime now = rtc.now();

    int digit_10s = (now.minute() /10); //will get the 10s place for digit:mintue
    int digit_1s = (now.minute() % 10); //will get the 1s place for digit:minute
    }

    void loop() {

    //main code here, to run repeatedly:
    digitalWrite(bcdA, (bitRead(digit_10s, 0)));
    delay (1000);
    digitalWrite(bcdA, (bitRead(digit_1s, 0)));
    delay (1000);
    }

    /////////////////////////
    I get the following error when compiling:

    variable or field “loop” declared void
    exit status 1

    • I see a few issues with your code.

      • The #include line does not contain the rest of the statement including the library.
      • The pin mapping lines are global constants and need to be outside of any function.
      • The rtc instance should also be outside of any function.
      • The now, digit_10s, and digit_1s variable definitions should be at the beginning of your loop function.
      • You are only driving one input of the decoder IC.

      Without knowing your exact setup, the following appears to me to be what you are looking for.

      
      #include "RTClib.h"
      
      const byte bcdA = 2;  // LSB
      const byte bcdB = 3;
      const byte bcdC = 4;
      const byte bcdD = 5;  // MSB
      
      RTC_PCF8523 rtc;
      
      void setup() {
         pinMode(bcdA, OUTPUT);
         pinMode(bcdB, OUTPUT);
         pinMode(bcdC, OUTPUT);
         pinMode(bcdD, OUTPUT);
      }
      
      void loop() {
         DateTime now = rtc.now();
         int digit_10s = (now.minute() / 10);  // will get the 10s place for digit:mintue
         int digit_1s  = (now.minute() % 10);  // will get the 1s place for digit:minute
      
         digitalWrite(bcdA, (bitRead(digit_10s, 0)));
         digitalWrite(bcdB, (bitRead(digit_10s, 1)));
         digitalWrite(bcdC, (bitRead(digit_10s, 2)));
         digitalWrite(bcdD, (bitRead(digit_10s, 3)));
         delay(1000);
         digitalWrite(bcdA, (bitRead(digit_1s, 0)));
         digitalWrite(bcdB, (bitRead(digit_1s, 1)));
         digitalWrite(bcdC, (bitRead(digit_1s, 2)));
         digitalWrite(bcdD, (bitRead(digit_1s, 3)));
         delay(1000);
      }
      
  • Greetings John,
    I made numerous modifications to the code. My results are in the right direction, but I’m only getting an 8 digit, and a 5 digit on the display. The sequence appears to be working, it’s just the output isn’t correct.
    Below is the current code:

    #include “RTClib.h”

    // Pin mapping _setup code here, to run once:
    const byte bcdA = 2; //LSB
    const byte bcdB = 3;
    const byte bcdC = 4;
    const byte bcdD = 5; //MSB
    const int red = 7;
    const int blue = 8;
    const int green = 9;

    RTC_PCF8523 rtc;

    // Pin configuration
    void setup() {
    pinMode(red, OUTPUT);
    pinMode(green, OUTPUT);
    pinMode(blue, OUTPUT);
    pinMode(bcdA, OUTPUT);
    pinMode(bcdB, OUTPUT);
    pinMode(bcdC, OUTPUT);
    pinMode(bcdD, OUTPUT);
    }

    void loop() {
    DateTime now = rtc.now();
    int digit_hourFirstplc =(now.hour()/10); //get 10s place for hours
    int digit_hourSecondplc =(now.hour()%10); //get 1s place for hours
    int digit_minuteFirstplc = (now.minute()/10); //will get the 10s place for digit:minute
    int digit_minuteSecondplc = (now.minute()% 10); //will get the 1s place for digit:minute

    digitalWrite(red, HIGH);//lights red
    digitalWrite(blue, LOW);
    digitalWrite(green, LOW);
    digitalWrite(bcdA, (bitRead(digit_hourFirstplc, 0)));
    digitalWrite(bcdB, (bitRead(digit_hourFirstplc, 1)));
    digitalWrite(bcdC, (bitRead(digit_hourFirstplc, 2)));
    digitalWrite(bcdD, (bitRead(digit_hourFirstplc, 3)));
    delay (2000);
    digitalWrite(red, HIGH);//lights magenta
    digitalWrite(blue, LOW);
    digitalWrite(green, HIGH);
    digitalWrite(bcdA, (bitRead(digit_hourSecondplc, 0)));
    digitalWrite(bcdB, (bitRead(digit_hourSecondplc, 1)));
    digitalWrite(bcdC, (bitRead(digit_hourSecondplc, 2)));
    digitalWrite(bcdD, (bitRead(digit_hourSecondplc, 3)));
    delay (2000);
    digitalWrite(red, LOW);//lights blue
    digitalWrite(blue, LOW);
    digitalWrite(green, HIGH);
    digitalWrite(bcdA, (bitRead(digit_minuteFirstplc, 0)));
    digitalWrite(bcdB, (bitRead(digit_minuteFirstplc, 1)));
    digitalWrite(bcdC, (bitRead(digit_minuteFirstplc, 2)));
    digitalWrite(bcdD, (bitRead(digit_minuteFirstplc, 3)));
    delay (2000);
    digitalWrite(red, LOW);//lights green
    digitalWrite(blue, HIGH);
    digitalWrite(green, LOW);
    digitalWrite(bcdA, (bitRead(digit_minuteSecondplc, 0)));
    digitalWrite(bcdB, (bitRead(digit_minuteSecondplc, 1)));
    digitalWrite(bcdC, (bitRead(digit_minuteSecondplc, 2)));
    digitalWrite(bcdD, (bitRead(digit_minuteSecondplc, 3)));
    delay (2000);
    }

    • The code looks okay to me. If you have not done so already, I would suggest verifying the digit_hour… and digit_minute… values by printing them to the Serial Monitor. If that looks good, verify your decoder/display circuit and A/B/C/D pin connections. You were able to get the original tutorial sketch working (counting) without any issues, right?

  • I was able to get the counter working except for the number 6 wasn’t displaying properly, I never determined why. I’ll review everything once again and see if there is something amiss. When you state, “I would suggest verifying the digit_hour… and digit_minute… values by printing them to the Serial Monitor”…meaning, just send outputs to serial instead of digitalWrite?

    • Correct.
      Add the following two lines somewhere in your setup() function.

      Serial.begin(9600);
      while (!Serial);

      Then add something like Serial.print("First place digit of hour: "); Serial.println(digit_HourFirstplc); within the loop() function after you define the digit variables. Open the Serial Monitor window (Main Menu > Tools > Serial Monitor) and then upload the sketch. You should see the value of the digit_HourFirstplc variable being printed in the Serial Monitor.

  • Happy Days John!!!!!!
    Got a break through last night after playing with it for awhile. It appears this shield is finicky. I had to re-flash the RTC to get the time closer to current. It’s still about 5 minutes off, but I can live with it. Additionally, the Adafruit shield doesn’t account for daylight savings . So re-flashing it, I was able to get more accurate. I implemented the code from your last message and see the serial output now along with the functioning display. It’s in 24 hour format, but working like a clock (pun intended)! I can’t thank you enough for your help and appreciate the patience you’ve shown. If there is anything I can do to help you I’d be more than happy to assist. Thanks a million!!

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.