Raspberry Pi LED Matrix
An LED matrix lights up dozens of LEDs using only a handful of GPIO pins by rapidly scanning rows and columns, and this lesson shows how that trick works and how to drive one for real with a MAX7219 chip.
Why You Can't Wire 64 LEDs to 64 Pins
An 8x8 LED matrix contains 64 individual LEDs, but it does not have 64 separate wires coming out of it. Instead, the LEDs are wired in a grid: every LED in the same row shares one wire, and every LED in the same column shares another. That means an 8x8 matrix needs only 8 row connections plus 8 column connections - 16 pins total - to reach all 64 LEDs. The tradeoff is that you can no longer turn on a single LED in isolation by simply setting one pin, because each wire touches eight LEDs at once.
Multiplexing: Faking "All On at Once"
The shared-wire wiring is made usable through a technique called multiplexing. Instead of trying to light the whole grid simultaneously, the controller lights only one row at a time, setting the column wires to match whatever pattern that row needs, then immediately moves on to the next row, and the next, cycling through all of them many times per second. At any single instant only one row is actually lit - but because the scan happens far faster than the human eye can perceive (well over 60 full cycles per second), persistence of vision makes the whole grid appear to glow steadily, exactly as if every LED were powered continuously.
- Turn on the wiring for row 1 and set every column pin to match that row's desired pattern.
- Hold that state for a fraction of a millisecond - just long enough for the LEDs to visibly light.
- Turn row 1 off before turning row 2 on, so current never lights two rows through the same column wire at the same time.
- Repeat for every row, then loop back to row 1, cycling fast enough that the eye perceives one steady image instead of a scan.
The MAX7219 Driver Chip
Doing the row-by-row scanning yourself in Python works for a tiny demo, but it wastes CPU time and does not scale well to a full 8x8 grid or to chaining several matrices together. In real projects, that scanning job - along with current-limiting for the LEDs - is handed off to a dedicated driver chip, the MAX7219. The Pi talks to it over a simple three-wire serial interface (data, clock, and chip-select, the same wiring style as SPI), sends it the pattern you want displayed, and the chip handles the high-speed multiplexing entirely in hardware. Multiple MAX7219 modules can be chained together to build larger displays, such as a scrolling text sign built from four 8x8 modules in a row.
A Tiny Manual Multiplexing Demo
from gpiozero import OutputDevice
from time import sleep
# A miniature 2-row x 3-column grid to illustrate the scanning technique
rows = [OutputDevice(5), OutputDevice(6)]
cols = [OutputDevice(13), OutputDevice(19), OutputDevice(26)]
# True = LED on, arranged as pattern[row][col]
pattern = [
[True, False, True],
[False, True, False],
]
def refresh_once():
for r, row_pin in enumerate(rows):
row_pin.on()
for c, col_pin in enumerate(cols):
col_pin.value = pattern[r][c]
sleep(0.002) # brief flash - too fast for the eye to notice
row_pin.off() # off before the next row lights, to avoid ghosting
for _ in range(500):
refresh_once()Displaying Text on a Real MAX7219 Matrix
from luma.led_matrix.device import max7219
from luma.core.interface.serial import spi, noop
from luma.core.render import canvas
from luma.core.legacy import text
from luma.core.legacy.font import proportional, CP437_FONT
serial = spi(port=0, device=0, gpio=noop())
device = max7219(serial, cascaded=1, block_orientation=-90)
device.contrast(16) # brightness, 0-255
with canvas(device) as draw:
text(draw, (0, 0), 'HI', fill='white', font=proportional(CP437_FONT))