Controlling A Quad 7 Segment Display Using A Pi Pico, Two Ways

Intro

I have recently been toying with some new…toys I got for my workbench. One of them being quadruple 7-segment display. I had mistakenly purchased the version without a “backpack” that contains an LED driver, so I had to figure how how to drive the device manually.

Here is the data-sheet: https://cdn-shop.adafruit.com/datasheets/1001datasheet.pdf

How this device works is rather simple:

  1. All LEDs are tied to the same pins. This means the top LED for each number is the same pin to be powered.
  2. Each panel will only operate if its appropriate COM pin is given a “0” or a LOW signal.
  3. The center colon has its own separate LED and COM pin.

This means that if you give the COM pin for digit 1 and 2 a LOW signal, they will both display whatever LED pins you pass a HIGH signal to.

The pinout to the quadruple seven-segment display, displayed to showcase how each LED is tied together

Once you have all of the pins connected to an appropriate one on the Pico it is pretty easy to make each pin say the same thing: turn all digit COM pins to 0 and then turn on some LED pins!

The LED display showcasing the number 7 on each display digit

For detailed wiring diagrams for how to do this yourself, see the links at the bottom of the post. You can find all of the code used in this project here: https://gist.github.com/turnipsoup/c5ca00fc8157414484f2cbf142032a6b.

Via Direct GPIO

I ran into an issue where I could not get different numbers to properly show on individual displays, only properly if the numbers were all the same.

GIF of my LED display keeping around “old” numbers in quick flashes. Watch the first digit. Individual LEDs slowed down for troubleshooting.

The issue was that they were still using the “old” data from the pins when switching over and this caused some digits to display a mix of two or more numbers, with some more faded than others. The solution was the clear the cell immediately after drawing it

# Draw each digit
    for i in range(4):
        draw_number_on_digit(number[i], i)
        clear_digit(i)

Next I wanted to count up and display the numbers 0000 through 9999 as a proof of concept that this works. Just letting it run is kind of fast, so I wanted to slow down the counting to see more numbers – but this lead to more flashing! I then realized that I could use one core of the Pi Pico CPU to draw the display and the other to count up.


import _thread

def draw_four_thread() -> None:
    """
    Takes a four digit integer and draws it on the display
    """
    from machine import Pin

    while True:
        draw_number_four_digits(display_digit)

if __name__ == "__main__":
    #[.....]

    # Start the display thread
    second_thread = _thread.start_new_thread(draw_four_thread, ())

A fully working, multiplexed quadruple 7-segment LED display, controlled by the Pi Pico

Not only did this work and allow me to change the pace of the number counting, it also increased the rate at which the CPU was counting, since core_0 could just focus entirely on the variables and core_1 can just focus on displaying the numbers stored in the global variable of display_digit.

Via Shift Registers

As you may have noticed these “7-segment” displays actually have 8 LEDs, one is the dot. With exactly 8 LEDs to control these seems like the perfect candidate for driving with a shift register. I also wanted to drive the COM pins with a shift register to make it a tad easier (see: cooler) as well.

It turns out that doing it this way is actually conceptually much simpler code-wise, but a tad bit more complex circuit wise. What advantage could this give us, then? We could have a separate circuit for the display that takes fewer pins from the microcontroller, thus leaving us with more GPIO access for other activities.

For this project I used two SN54HC595N shift registers, whose data-sheet is located here: https://www.ti.com/lit/ds/symlink/sn54hc595-sp.pdf

In a similar fashion to the previous stage of the project, I created some maps/arrays of which pins needed to be on to draw each number and iterated over these:


#SNIP
...

numbers = {
  "0": "11111100",
  "1": "01100000",
  "2": "11011010",
  "3": "11110010",
  "4": "01100110",
  "5": "10110110",
  "6": "10111110",
  "7": "11100000",
  "8": "11111110",
  "9": "11100110"
}

coms = ["01111111", "10111111", "11011111", "11101111", "11110111"]

#SNIP
...

if __name__ == "__main__":

  # Start the display thread
  second_thread = _thread.start_new_thread(display_number_thread, ())

  count = 0
  while True:
    display_number = count
    count += 1
    if count > 9999:
      count = 0

    time.sleep(0.1)

After some troubleshooting (fiddling with cables) everything ended up working as expected!

Image of the LED display being driven by shift registers

Things I Could Have Done Differently (Lessons Learned)

Sources Referenced/Used

  1. https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/advanced-driving-seven-segment-displays-with-background-writes
  2. https://learn.adafruit.com/intro-to-rp2040-pio-with-circuitpython/advanced-driving-seven-segment-displays-with-background-writes
  3. https://forums.raspberrypi.com/viewtopic.php?t=307851
  4. https://raspi.tv/2015/how-to-drive-a-7-segment-display-directly-on-raspberry-pi-in-python

An image of both breadboards next to each other