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.
A HUB75 panel behaves like a long daisy-chained shift-register:
CLK
.OE
HIGH), sets the address lines A–E, and produces a short pulse on LAT
to latch the freshly-shifted data into the LED drivers.OE
goes 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.
plain
none 16 bit (14 used) 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.
tiling::TiledFrameBuffer
to drive several HUB75 panels as one large display.ChainTopRightDown
and any of the framebuffers above (plain or latched).embedded-graphics
canvas, so a 3 × 3 stack of 64 × 32 panels simply looks like a 192 × 96 screen while all coordinate translation happens transparently.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.
Add the dependency to your Cargo.toml
:
[dependencies] hub75-framebuffer = "0.4.2"
use hub75_framebuffer::{compute_frame_count, compute_rows}; use hub75_framebuffer::latched::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(ROWS); // 16 const FRAME_COUNT:usize = compute_frame_count(BITS); // (1<<BITS)-1 = 7 // Create a framebuffer (already initialized/cleared) let mut framebuffer = DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT> ::new();
You can now draw using any embedded-graphics
primitive:
use embedded_graphics::prelude::*; use embedded_graphics::primitives::{Circle, Rectangle, PrimitiveStyle}; use hub75_framebuffer::Color; Rectangle::new(Point::new(0, 0), Size::new(COLS as u32, ROWS as u32)) .into_styled(PrimitiveStyle::with_fill(Color::BLACK)) .draw(&mut framebuffer) .unwrap(); Circle::new(Point::new(20, 10), 8) .into_styled(PrimitiveStyle::with_fill(Color::GREEN)) .draw(&mut framebuffer) .unwrap();
Finally hand the raw DMA buffer off to your MCU's parallel peripheral.
esp-hal-dma
(required when using esp-hal
)
Required when using the esp-hal
crate for ESP32 development. This feature switches the ReadBuffer
trait implementation from embedded-dma
to esp-hal::dma
. If you're targeting ESP32 devices with esp-hal
, you must enable this feature for DMA compatibility.
[dependencies] hub75-framebuffer = { version = "0.4.2", features = ["esp-hal-dma"] }
esp32-ordering
(required for original ESP32 only)
Required when targeting the original ESP32 chip (not ESP32-S3 or other variants). This feature adjusts byte ordering to accommodate the quirky requirements of the ESP32's I²S peripheral in 8-bit and 16-bit modes. Other ESP32 variants (S2, S3, C3, etc.) do not need this feature.
[dependencies] hub75-framebuffer = { version = "0.4.2", features = ["esp32-ordering"] }
Skip drawing black pixels for performance boost in UI applications. When enabled, calls to set_pixel()
with Color::BLACK
return early without writing to the framebuffer, assuming the framebuffer was already cleared.
Implement the defmt::Format
trait so framebuffer types can be logged with the defmt
ecosystem.
Embed documentation images when building docs on docs.rs. Not needed for normal usage.
Enable features in your Cargo.toml
:
[dependencies] hub75-framebuffer = { version = "0.4.2", features = ["esp-hal-dma", "esp32-ordering"] }
All logic including bitfields, address mapping, brightness modulation and the embedded-graphics
integration is covered by a comprehensive test-suite (≈ 300 tests).
Licensed under either of
at your option.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4