7 Segment LED Tutorial for Raspberry Pi

I received a request to make a tutorial on how to use 7 segment LED displays with a Raspberry Pi. The following video demonstrates how to use pulse width modulation (PWM) via the Pi’s DMA hardware to drive 7 segment displays.

For this video, I’m using a Kingbright DC04-11 blue dual digit display.  Please note that the pinouts of LED displays vary greatly (always check your datasheets).  To simplify the programming, segments A through G are wired to GPIO pins 10 to 16 in series with appropriate resistors.  I start with 750 Ω resistors, then lower the resistance to get the correct brightness being careful not to exceed the Pi’s 50 mA limit.  220 Ω resistors provide good brightness and only use 12.8 mA.  The common cathodes are controlled by 2 NTE2361 high speed NPN transistors connected to GPIO pins 20 and 21.  You could omit the transistors and connect the common cathodes directly GPIO pins 20 and 21 as long as the total current does not exceed 16 mA.  You would also have to swap the start times of the pulses because the pulses would be driving the cathodes high instead of low.
Wiring for 7 segment LED display using PWM

 

The DMA multiplexing requires the RPIO library.  First make sure that the python-dev is installed:

sudo apt-get install python-dev

Blinky The RPIO python library was recently updated to support newer model Pis.  You will need to manually install the v.2 beta if you are using a Raspberry Pi 2 or newer.

I recommend that you manually install RPIO to get the latest version which as of 6/4/2016 is 2.0.0-beta1:

cd ~
git clone https://github.com/metachris/RPIO.git --branch v2 --single-branch
cd RPIO
sudo python setup.py install

Here is the Python code from the video:

from time import sleep
from RPIO import PWM

PWM.setup()
PWM.set_loglevel(PWM.LOG_LEVEL_ERRORS)
PWM.init_channel(0)

# Dictionary translating numbers to segments byte
num = {
    0:(1,1,1,1,1,1,0),
    1:(0,1,1,0,0,0,0),
    2:(1,1,0,1,1,0,1),
    3:(1,1,1,1,0,0,1),
    4:(0,1,1,0,0,1,1),
    5:(1,0,1,1,0,1,1),
    6:(1,0,1,1,1,1,1),
    7:(1,1,1,0,0,0,0),
    8:(1,1,1,1,1,1,1),
    9:(1,1,1,1,0,1,1)}

# Pulse off and on (minimum of 4 for off state = 40 μs)
pulse = {
    0:4,
    1:999}

# Start alternating 10k μs pulses for the cathodes
PWM.add_channel_pulse(0, 20, 0, 999)
PWM.add_channel_pulse(0, 21, 1000, 999)

def SetDual7Seg( value ):
    # Split passed value into separate digit integer list
    digits = map(int, "%02d" % value)
    
    # Set pulses for segments A-G (both digits) 
    for i in range(7):
        PWM.add_channel_pulse(0, 10 + i, 0, pulse[num[digits[0]][i]])
        PWM.add_channel_pulse(0, 10 + i, 1000, pulse[num[digits[1]][i]])

for i in range(100):
    SetDual7Seg(i)
    sleep(1)

# Stop PWM for channel 0
PWM.clear_channel(0)

# Shutdown all PWM and DMA activity
PWM.cleanup()

RaceCar I was asked how to add additional digits. Here is an example with 6 digits. The pulse width is reduced from 999 to 332 to fit the 4 additional sets of pulses into the same 20K μs sub-cycle.

# Pulse off and on (minimum of 4 for off state = 40 μs)
pulse = {
    0:4,
    1:332}

# Start cathode pulses for each digit (GPIO 20 - 25)
PWM.add_channel_pulse(0, 20, 0, 332)
PWM.add_channel_pulse(0, 21, 333, 332)
PWM.add_channel_pulse(0, 22, 666, 332)
PWM.add_channel_pulse(0, 23, 1000, 332)
PWM.add_channel_pulse(0, 24, 1333, 332)
PWM.add_channel_pulse(0, 25, 1666, 332)

def SetDual7Seg( value ):
    # Split passed value into separate digit integer list
    digits = map(int, "%06d" % value)
    print(digits)
    # Set pulses for segments A-G (all digits) 
    for i in range(7):
        PWM.add_channel_pulse(0, 10 + i, 0, pulse[num[digits[0]][i]])
        PWM.add_channel_pulse(0, 10 + i, 333, pulse[num[digits[1]][i]])
        PWM.add_channel_pulse(0, 10 + i, 666, pulse[num[digits[2]][i]])
        PWM.add_channel_pulse(0, 10 + i, 1000, pulse[num[digits[3]][i]])
        PWM.add_channel_pulse(0, 10 + i, 1333, pulse[num[digits[4]][i]])
        PWM.add_channel_pulse(0, 10 + i, 1666, pulse[num[digits[5]][i]])

Tank UPDATE: It looks like the RPIO library has been abandoned. Therefore, I created a Python example using PiGPIO. It requires the Wave PWM 2 library.

from time import sleep
import pigpio
from wavePWM import PWM

pi = pigpio.pi() # Start PiGPIO
pwm = PWM(pi)    # Use default frequency

# Dictionary translating numbers to segments byte
num = {
    0:(1,1,1,1,1,1,0),
    1:(0,1,1,0,0,0,0),
    2:(1,1,0,1,1,0,1),
    3:(1,1,1,1,0,0,1),
    4:(0,1,1,0,0,1,1),
    5:(1,0,1,1,0,1,1),
    6:(1,0,1,1,1,1,1),
    7:(1,1,1,0,0,0,0),
    8:(1,1,1,1,1,1,1),
    9:(1,1,1,1,0,1,1)}

# Start alternating 10k μs pulses for the cathodes
pwm.set_pulse_start_and_length_in_fraction(20, 0.0, 0.5)
pwm.set_pulse_start_and_length_in_fraction(21, 0.5, 0.5)

pulses = {
    0: (0.0, 0.0),
    1: (0.0, 0.5),
    2: (0.5, 0.5),
    3: (0.0, 0.999)
    }

def SetDual7Seg( value ):
    # Split passed value into separate digit integer list
    digits = list(map(int, "%02d" % value))
    
    # Set pulses for segments A-G (both digits)
    for i in range(7):
        pulse = pulses[num[digits[0]][i] << 1 | num[digits[1]][i]]
        pwm.set_pulse_start_and_length_in_fraction(10 + i, *pulse)
    pwm.update()

for i in range(100):
    SetDual7Seg(i)
    sleep(1)

# Clean up
for i in range(7):
    pwm.set_pulse_start_and_length_in_fraction(10 + i, 0.0, 0.0)
pwm.update()
pwm.cancel()
pi.stop()