Raspberry Pi Pico
MAX7219 LED Matrix

The MAX7219 integrated circuit is an LED driver that can be used with LED matrices to drive the LEDs at constant current. There are i2c and SPI versions of the chip. This page uses the SPI version. The one I am using here cost me £5 and came with the headers already soldered on.

Pico Circuit

To connect the circuit here, you need to connect the VCC and GND to power and ground. An external power supply can be used for the VCC as long as you connect its GND to the GND on the Pico. There are three more connections to make. You need one pin for chip select, which can be any GPIO pin. Then you need to connect to two specialist SPI pins which share their ID (either 0 or 1). SCLK needs to go to an SCK pin. DIN goes to a MOSI pin (SPI tx). I used GP15 for my CS pin, GP18 for SCLK and GP19 for MOSI.

The following is my library for controlling the matrix. It needs to be saved to the Pico as matrix.py. I had to add line breaks to the font definition to make it fit on this page. You need to remove them so that this is defined in a single line.

from machine import Pin, SPI
from time import sleep

font = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x5f\x00\x00\x00\x07\x00\x07\x00\x14\x7f\x14\x7f\x14\x24\x2a
\x7f\x2a\x12\x23\x13\x08\x64\x62\x36\x49\x55\x22\x50\x00\x05\x03\x00\x00\x00\x1c\x22\x41\x00\x00\x41\x22\x1c\x00
\x14\x08\x3e\x08\x14\x08\x08\x3e\x08\x08\x00\x50\x30\x00\x00\x08\x08\x08\x08\x08\x00\x60\x60\x00\x00\x20\x10\x08
\x04\x02\x3e\x51\x49\x45\x3e\x00\x42\x7f\x40\x00\x42\x61\x51\x49\x46\x21\x41\x45\x4b\x31\x18\x14\x12\x7f\x10\x27
\x45\x45\x45\x39\x3c\x4a\x49\x49\x30\x01\x71\x09\x05\x03\x36\x49\x49\x49\x36\x06\x49\x49\x29\x1e\x00\x36\x36\x00
\x00\x00\x56\x36\x00\x00\x08\x14\x22\x41\x00\x14\x14\x14\x14\x14\x00\x41\x22\x14\x08\x02\x01\x51\x09\x06\x32\x49
\x79\x41\x3e\x7e\x11\x11\x11\x7e\x7f\x49\x49\x49\x36\x3e\x41\x41\x41\x22\x7f\x41\x41\x22\x1c\x7f\x49\x49\x49\x41
\x7f\x09\x09\x09\x01\x3e\x41\x49\x49\x7a\x7f\x08\x08\x08\x7f\x00\x41\x7f\x41\x00\x20\x40\x41\x3f\x01\x7f\x08\x14
\x22\x41\x7f\x40\x40\x40\x40\x7f\x02\x0c\x02\x7f\x7f\x04\x08\x10\x7f\x3e\x41\x41\x41\x3e\x7f\x09\x09\x09\x06\x3e
\x41\x51\x21\x5e\x7f\x09\x19\x29\x46\x46\x49\x49\x49\x31\x01\x01\x7f\x01\x01\x3f\x40\x40\x40\x3f\x1f\x20\x40\x20
\x1f\x3f\x40\x38\x40\x3f\x63\x14\x08\x14\x63\x07\x08\x70\x08\x07\x61\x51\x49\x45\x43\x00\x7f\x41\x41\x00\x02\x04
\x08\x10\x20\x00\x41\x41\x7f\x00\x04\x02\x01\x02\x04\x40\x40\x40\x40\x40\x00\x01\x02\x04\x00\x20\x54\x54\x54\x78
\x7f\x48\x44\x44\x38\x38\x44\x44\x44\x20\x38\x44\x44\x48\x7f\x38\x54\x54\x54\x18\x08\x7e\x09\x01\x02\x0c\x52\x52
\x52\x3e\x7f\x08\x04\x04\x78\x00\x44\x7d\x40\x00\x20\x40\x44\x3d\x00\x7f\x10\x28\x44\x00\x00\x41\x7f\x40\x00\x7c
\x04\x18\x04\x78\x7c\x08\x04\x04\x78\x38\x44\x44\x44\x38\x7c\x14\x14\x14\x08\x08\x14\x14\x18\x7c\x7c\x08\x04\x04
\x08\x48\x54\x54\x54\x20\x04\x3f\x44\x40\x20\x3c\x40\x40\x20\x7c\x1c\x20\x40\x20\x1c\x3c\x40\x30\x40\x3c\x44\x28
\x10\x28\x44\x0c\x50\x50\x50\x3c\x44\x64\x54\x4c\x44\x00\x08\x36\x41\x00\x00\x00\x7f\x00\x00\x00\x41\x36\x08\x00
\x10\x08\x08\x10\x08\x00\x00\x00\x00\x00')

class Matrix:
    def __init__(self, c, clk, din, spinum):
        self.cs = Pin(c, Pin.OUT)
        self.spi = SPI(spinum, 1000000, sck=Pin(clk), mosi=Pin(din))
        self.buffer = bytearray(8)       
        self.init()

    def _register(self, command, data):       
        self.cs.value(0)
        self.spi.write(bytes([command, data]))
        self.cs.value(1)

    def init(self):
        for command, data in (
            (12, 0),
            (15, 0),
            (11, 7),
            (9, 0),
            (12, 1),
        ):
            self._register(command, data)

    def brightness(self, value):
        self._register(10, value)

    def fill(self, color):       
        for y in range(8):
            self.buffer[y] = color

    def pixel(self, x, y, color=None):
        if color is None:
            return bool(self.buffer[x] & 1 << y)
        elif color:
            self.buffer[x] |= 1 << y
        else:
            self.buffer[x] &= ~(1 << y)

    def show(self):
        for y in range(8):
            self._register(1 + y, self.buffer[y])
            
    def scroll(self, txt, delay):
        msg = bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
        for c in txt:
            msg += bytearray(b'\x00')
            msg += font[(ord(c)-32)*5:(ord(c)-32)*5+5]
            msg += bytearray(b'\x00')
        msg += bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00')
        for i in range(len(msg)-8):
            self.buffer = msg[i:i+8]
            self.show()
            sleep(delay)

Here is some test code to put in the main. It shows that you can add a dot, one at time, can set the whole matrix to on or off and can scroll a message across the matrix.

from matrix import Matrix
from time import sleep


d = Matrix(15,18,19,0)
d.brightness(15)

d.show()

for y in range(8):
    for x in range(8):
        d.pixel(x,y,True)
        d.show()
        sleep(0.05)

sleep(1)
d.fill(0)
d.show()
d.scroll("Hello World! Testing the scroll feature.", 0.1)