Adafruit_Protomatter.git
RGB matrix library for Arduino
rad:z3UBK8SJPHj5fKK7UN4Mzs8rkNng5
Visibility
public
Delegates
did:key:z6MkrmvEJY7gDDQBso3T38xS6UXrS88dG47Ezfp1naw8XuMr
Default branch
master → ffc2ec1d028a7244a79ae8667fcd09a02a58d9ed (Sat Mar 14 00:58:57 2020)
Threshold
1
README.md
# Adafruit_Protomatter

"I used protomatter in the Genesis matrix." - David Marcus, Star Trek III

Code for driving HUB75-style RGB LED matrices, targeted at 32-bit MCUs
using brute-force GPIO (that is, not relying on DMA or other specialized
peripherals beyond a timer interrupt, goal being portability).

Name might change as it's nondescriptive and tedious to type in code.

# Matrix Concepts and Jargon

HUB75 RGB LED matrices are basically a set of six concurrent shift register
chains, each with one output bit per column, the six chains being red, green
and blue bits for two non-adjacent rows, plus a set of row drivers (each
driving the aforementioned two rows) selected by a combination of address
lines. The number of address lines determines the overall matrix height
(3 to 5 bits is common...as an example, 3 address lines = 2^3 = 8 distinct
address line combinations, each driving two rows = 16 pixels high). Address
0 enables rows 0 and height/2, address 1 enables rows 1 and height/2+1, etc.
Shift register chain length determines matrix width...32 and 64 pixels are
common...matrices can be chained to increase width, a 64-pixel wide matrix
is equivalent to two 32-pixel chained matrices, and so forth.

These matrices render only ONE BIT each for red, green and blue, they DO NOT
natively display full color and must be quickly refreshed by the driving
microcontroller, basically PWM-ing the intermediate shades (this in addition
to the row scanning that must be performed).

There are a few peculiar RGB LED matrices that have the same physical
connection but work a bit differently -- they might have only have three
shift register chains rather than six, or might use a shift register for
the row selection rather than a set of address lines. The code presented
here DOES NOT support these matrix variants. Aim is to provide support for
all HUB75 matrices in the Adafruit shop. Please don't submit pull requests
for these other matrices as we have no means to test them. If you require
this functionality, it's OK to create a fork of the code, which Git can
help keep up-to-date with any future changes here!

# Hardware Requirements and Jargon

The common ground for architectures to support this library:

* 32-bit device (e.g. ARM core, but potentially ESP32 and others in future)
* One or more 32-bit GPIO PORTs with atomic (single-cycle, not
  read-modify-write) bitmask SET and CLEAR registers. A bitmask TOGGLE
  register, if present, may improve performance but is NOT required.
* Tolerate 8-bit or word-aligned 16-bit accesses within the 32-bit PORT
  registers (e.g. writing just one of four bytes, rather than the whole
  32 bits). The library does not use any unaligned accesses (i.e. the
  "middle word" of a 32-bit register), even if a device tolerates such.

# Software Components

This repository currently consists of:

* An Arduino C++ library (files Adafruit_Protomatter.cpp and
  Adafruit_Protomatter.h, plus the "examples" directory). The Arduino code
  is dependent on the Adafruit_GFX library.

* An underlying C library (files core.c, core.h and arch.h) that might be
  adaptable to other runtime environments (e.g. CircuitPython).

# Arduino Library

This *might* supersede the RGBmatrixPanel library on non-AVR devices, as the
older library has painted itself into a few corners. The newer library uses
a single constructor for all matrix setups, potentially handling parallel
chains (not yet fully implemented), various matrix sizes and chain lengths,
and variable bit depths from 1 to 6 (refresh rate is a function of all of
these). Note however that it is NOT A DROP-IN REPLACEMENT for RGBmatrixPanel.
The constructor is entirely different, and there are several changes in the
available functions. Also, all colors in the new library are specified as
5/6/5-bit RGB (as this is what the GFX library GFXcanvas16 type uses, being
aimed at low-cost color LCD displays), even if the matrix is configured for
a lower bit depth (colors will be decimated/quantized in this case).

It does have some new limitations, mostly significant RAM overhead (hence
no plans for AVR port) and that all RGB data pins and the clock pin MUST be
on the same PORT register (e.g. all PORTA or PORTB, can't intermix). RAM
overhead is somewhat reduced (but still large) if those pins are all in a
single 8-bit byte within the PORT (they do not need to be contiguous or
sequential within this byte, if for instance it makes PCB routing easier,
but they should all aim for a single byte). Other pins (matrix address lines,
latch and output enable) can reside on any PORT or bit.

# C Library

The underlying C library is focused on *driving* the matrix and does not
provide any drawing operations of its own. That must be handled by
higher-level code, as in the Arduino wrapper which uses the Adafruit_GFX
drawing functions.

The C code has the same limitations as the Arduino library: all RGB data
pins and the clock pin MUST be on the same PORT register, and it's most
memory efficient (though still a bit gluttonous) if those pins are all
within the same 8-bit byte within the PORT (they do not need to be
contiguous or sequential within that byte). Other pins (matrix address lines,
latch and output enable) can reside on any PORT or bit.

When adapting this code to new devices (e.g. nRF52, ESP32) or new runtime
environments (e.g. CircuitPython), goal is to put all the device- or
platform-specific code into the arch.h file (or completely separate source
files, as in the Arduino library .cpp and .h). core.c contains only the
device-neutral bitbang code and should not have any "#ifdef DEVICE"- or
"#ifdef ENVIRONMENT"-like lines. Macros for things like getting a PORT
register address from a pin, or setting up a timer peripheral, all occur
in arch.h, which is ONLY #included by core.c (to prevent problems like
multiple instances of ISR functions, which must be singularly declared at
compile-time).

Most macros and functions begin with the prefix **\_PM\_** in order to
avoid naming collisions with other code (exception being static functions,
which can't be seen outside their source file).