Interfacing a 7 segment display with a PIC16F microcontroller

One of the key elements in embedded systems is the user interface. A good user interface will feature visual elements to communicate with the user, such as LEDs or a screen for example. Seven segment displays are widely used in electronic devices, as they can display information in an easy way. We find 7 segment displays everywhere in our homes: our alarm clocks, microwave ovens or even the screens of some devices. Let’s see how to use this component in an embedded system! We’ll start simple, with a single 7 segment display.

7 segment display

Bill of Material

First of all, we’ll need the basic components required for the PIC to run. I’ll use a PIC16F88, but you’re free to use any microcontroller you prefer. Check this tutorial if you forgot how to get started with PIC microcontrollers.

We’ll also need a 7 segment display, like this one from Newark.

Finally, we’ll use 8 resistors to protect the LEDs of the 7 segment display. 330Ω is a good value here.

A bit of theory…

Before we can build a circuit and program the PIC, let’s take a look at the datasheet of our 7 segment display. The reference of the one I use is HDSP-303G, and it is a common cathode circuit. The difference with a common anode circuit is simple:

  • In a common cathode circuit, all the LEDs cathodes are tied together, and connected to the ground. The state of the LEDs (ON/OFF) is controlled by applying a signal to their anode. If you apply 0 to the anode, the LED will be OFF, and if you apply 1, it will be ON.
  • In a common anode circuit, the anodes of the LEDs are tied together and connected to the power supply of the circuit. In this case we’ll control the LEDs by applying a signal to their cathodes. A 0V signal on the cathode will turn the LED ON, and a high level signal will turn it OFF.

Each segment of the display is controlled individually, which means a 7 segment display can be seen as the following circuit (oh yeah, if you wonder why there are 8 LEDs, it’s because there is also a decimal point!):

Decomposition of a 7 segment display
Decomposition of a 7 segment display

And the names of the segments are the following:

Decomposition of a 7 segment display
Names of segments in a 7 segment display

For the rest of this tutorial we’ll consider a common cathode circuit.

Hardware: build the circuit

Base circuit for the PIC16F88

First let’s prepare the PIC16F88 with the components it needs to run properly.

  • Connect Vss to the ground (0V)
  • Connect Vdd to your 5V supply, with a 0.1uF decoupling capacitor
  • Connect a crystal on the OSC1 and OSC2 pins (pins 15 and 16), with two 22pF capacitors connected to the ground
  • Connect MCLR pin (pin 4) to your 5V supply to prevent unwanted resets. Add a 10kΩ resistor and a 1N5817 Schottky diode in series. The resistor will protect the pin against high currents, and the diode will prevent the high voltage of the programmer to get in the 5V rail. Indeed, sometimes the programmer will apply a voltage higher than Vdd to MCLR (about 10V in some cases) to program the chip. The diode will allow your 5V voltage on the MCLR pin, but will prevent the high voltage from the programmer to get in the 5V rail.

Here is a schematic of the circuit:

Schematic of the PIC16F88 base circuit
Schematic of the PIC16F88 base circuit

Wiring the 7 segment display

As with any LED circuit, the first step is to protect the LED with a series resistor. We’ve seen that every segment of the display is actually a LED, so we need to add a resistor on every input of the 7 segment display. I’ve seen plenty of people not using any resistor, and in some cases it may be safe, but it’s always better to add them and be sure you won’t burn your display.

Series resistors protecting the LEDs
Series resistors protecting the LEDs

If we take the individual LEDs representation, we have the following circuit:

Decomposition of the series resistors
Decomposition of the series resistors

And the circuit looks like the following on a breadboard:

7 segment display and resistors on a breadboard
7 segment display and resistors on a breadboard

Connecting the display to the PIC

We need 8 digital outputs to control each segment of the display. Let’s take a look at the pin diagram of the PIC16F88.

Pin diagram of the PIC16F88 microcontroller
Pin diagram of the PIC16F88 microcontroller

We have the choice of either PORTA or PORTB, as both offer 8 GPIO pins. But, we are already using RA5 (pin 4) as a reset pin that we tie to Vdd, and RA6 and RA7 (pins 15 and 16) are used to connect the crystal. On the other hand, we use RB3, RB6 and RB7 (pins 9, 12 and 13) to connect the programmer, but those are only used during programmation. Once the chip is programmed, the signals PGC, PGD and PGM are not used anymore and the pins can be used as normal GPIO pins. So PORTB is unused during runtime. For this reason we’ll use PORTB to connect our 7 segment display.

Here is the full circuit with the 7 segment display connected to the PIC:

PIC16F88 microcontroller controlling a 7 segment display
PIC16F88 microcontroller controlling a 7 segment display

And here is what it looks like on a breadboard. As you can see, breadboard circuits can become messy very fast!

PIC16F88 microcontroller controlling a 7 segment display on a breadboard
PIC16F88 microcontroller controlling a 7 segment display on a breadboard

Software: write the program

Configuration bits

First let’s configure the PIC to operate with an external crystal as a clock source. The other bits are not very important for this program, but it’s always better to configure everything and not have any surprise.

Display a digit

To display a digit, we just have to apply 0V to the segments we want to be OFF, and 5V (which is a logical 1) to the ones we want ON. For example, let’s display the digit “4”. This means we want the segments B, C, F and G on. Considering the way we wired the 7 segment display to the port B of the PIC, it means we want RB6, RB5, RB2 and RB1 to be ON, and the rest will be OFF.

We can write this value directly to the output port, or we can also write this value in a single 8-bit integer segValue, and then write the value of this integer to port B. This is what we do in the following code, where you can see the value applied to port B is 0b01100110 .

This way of doing things is easier to modify in the future, because we’ll only have to change the value of segValue, without having to search into the code to change it. It’s especially useful in bigger programs, where you don’t want to look into hundreds of lines of code to find the value you want to change.

Here is the result on the circuit.

Digit 4 displayed on a 7 segment display
Digit 4 displayed on a 7 segment display

Display successive digits using a counter

Now let’s do something a bit more advanced. We’re going to successively display digits from 0 to 15 in hexadecimal format.

First, we’ll need a list of all the segment states for each digit. We’ll use an array of 8-bit integers that we’ll call segValues , like shown below. We’ll declare and initialize this array before the main()  function, and mark it as a constant using the  const keayword. It means the values of the array will never be written to, they’ll be read-only.

This is very convenient, because each value corresponds to its index in the array. It means that if we want the segment values for the digit “8”, we can look into the 8th element of the array segValues[8].

Then, we need to initialize a counter n at the beginning of the main function, and increment it at every iteration of the while(1) loop. We also need to update the output signal applied to the 7 segment display in this loop.

Now if you compile and program your PIC, you’ll see that the 7 segment is not displaying the numbers correctly. It’s displaying a constant set of segments, some being brighter than others.

The reason is very simple: our program is counting too fast. Why? Because it’s counting at the speed of the instruction clock, which is 2MHz if you have a 8MHz crystal as a clock source. As a side note, the instruction cycle on PIC16F microcontrollers is equal to the clock frequency divided by 4.

So we need to add a delay between each iteration of the loop in order to see the each digit. The good thing is, it’s easy. The XC8 compiler offers a function __delay_ms(x) that will wait for x milliseconds. There are three things to know about this function:

  • There are two underscores at the beginning of the name, and only one between delay and ms.
  • It will keep the processor busy doing nothing, which means you can’t do anything else during its execution. This is not a problem here, but in some more elaborate cases you may want to use a timer interruption instead.
  • We need to tell the compiler what is the system clock frequency. This is done by defining the _XTAL_FREQ constant after including xc.h, and before the main() function.
So if we put it all together, here is the final code for our program. Note that I use a 500ms delay in the loop, but you can use any delay you want.

Result

If you compile and program your microcontroller, you’ll see that the 7 segment is displaying each number from 0 to F (0 to 15).

Congratulations! You can now use 7 segment displays in your projects!

In the next tutorial we’ll see how to print a 2-digit number, using two 7 segment displays. Be sure to subscribe to get new tutorials delivered to your mailbox!

[wysija_form id=”2″]

 

And feel free to ask questions in the comment if anything is unclear!

Digit 1 displayed on a 7 segment display
Digit 1 displayed on a 7 segment display
Digit 4 displayed on a 7 segment display
Digit 4 displayed on a 7 segment display
Digit e (14) displayed on a 7 segment display
Digit e (14) displayed on a 7 segment display

Be the first to comment

%d bloggers like this: