hub75-framebuffer
DMA-friendly framebuffer implementations for driving HUB75 RGB LED matrix panels with Rust. The crate focuses on performance, correct timing, and ergonomic drawing by integrating tightly with the embedded-graphics ecosystem.
How HUB75 LED panels work (very short recap)
A HUB75 panel behaves like a long daisy-chained shift-register:
- Color data for one pair of rows is shifted in serially on every cycle of
CLK. - After the last pixel of the row pair has been clocked, the controller blanks the LEDs (
OEHIGH), sets the address lines A–E, and produces a short pulse onLATto latch the freshly-shifted data into the LED drivers. OEgoes LOW again and the row pair lights up while the next one is already being shifted.
Color depth is achieved with Binary/Bit-Angle Code Modulation (BCM): lower bit-planes are shown for shorter times, higher ones for longer, yielding 2^n intensity levels per channel while keeping peak currents low.
If you want a deeper explanation, have a look inside src/lib.rs — the crate documentation contains an extensive primer.
Two framebuffer flavors
| Module | Extra hardware | Word size | Memory use | Pros / Cons |
|---|---|---|---|---|
plain |
none | 16 bit | high | Simplest, wires exactly like a standard HUB75 matrix. |
latched |
external latch gate (see below) | 8 bit | ×½ of plain |
Lower memory footprint, but needs a tiny glue-logic board. |
The latch circuit
The latched implementation assumes a small external circuit that holds the row address while gating the pixel clock. A typical solution uses a 74xx373 latch along with a few NAND gates:

The latch IC stores the address bits whilst one NAND gate blocks the CLK signal during the latch interval. The remaining spare gate can be employed to combine a global PWM signal with OE for fine-grained brightness control as shown.
Getting started
Add the dependency to your Cargo.toml:
[]
= "0.1.0"
= "0.8"
Choose your parameters
use ;
use DmaFrameBuffer; // or ::plain::DmaFrameBuffer
const ROWS: usize = 32; // panel height
const COLS: usize = 64; // panel width
const BITS: u8 = 3; // colour depth ⇒ 7 BCM frames
const NROWS: usize = compute_rows; // 16
const FRAME_COUNT:usize = compute_frame_count; // (1<<BITS)-1 = 7
// Create & clear a framebuffer
let mut fb = new;
fb.clear;
You can now draw using any embedded-graphics primitive:
use *;
use ;
use Color;
new
.into_styled
.draw?;
new
.into_styled
.draw?;
Finally hand the raw DMA buffer off to your MCU's parallel peripheral.
Crate features
doc-images– embed documentation images when building docs.esp-dma– enable if your usingesp-hal.esp32– adjust byte ordering required by the ESP32 quirky I²S peripheral.
Enable them in your Cargo.toml or with --features.
Running tests
cargo test
All logic including bitfields, address mapping, brightness modulation and the embedded-graphics integration is covered by a comprehensive test-suite (≈ 300 tests).
License
Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.