Expand description
DMA-friendly framebuffer implementation for HUB75 LED panels.
This module provides a framebuffer implementation with memory layout optimized for efficient transfer to HUB75 LED panels. The data is structured for direct signal mapping, making it ideal for DMA transfers but also suitable for programmatic transfer. It supports RGB color and brightness control through multiple frames using Binary Code Modulation (BCM).
§Hardware Requirements
This implementation can be used by any microcontroller that has a peripheral capable of outputting a clock signal and 16 bits (though only 14 bits are actually needed) in parallel. The data is structured to directly match the HUB75 connector signals, making it suitable for various parallel output peripherals.
§Features
- Memory layout optimized for efficient data transfer
- Direct signal mapping to HUB75 connector signals
- Support for RGB color with brightness control
- Multiple frame buffers for Binary Code Modulation (BCM)
- Integration with embedded-graphics for easy drawing
§Brightness Control
Brightness is controlled through Binary Code Modulation (BCM):
- The number of brightness levels is determined by the
BITS
parameter - Each additional bit doubles the number of brightness levels
- More bits provide better brightness resolution but require more memory
- Memory usage grows exponentially with the number of bits:
(2^BITS)-1
frames - Example: 8 bits = 256 levels, 4 bits = 16 levels
§Memory Usage
The framebuffer’s memory usage is determined by:
- Panel size (ROWS × COLS)
- Number of brightness bits (BITS)
- Memory grows exponentially with bits:
(2^BITS)-1
frames - 16-bit entries provide direct signal mapping but use more memory
§Example
use embedded_graphics::pixelcolor::RgbColor;
use embedded_graphics::prelude::*;
use embedded_graphics::primitives::Circle;
use embedded_graphics::primitives::Rectangle;
use hub75_framebuffer::compute_frame_count;
use hub75_framebuffer::compute_rows;
use hub75_framebuffer::Color;
use hub75_framebuffer::plain::DmaFrameBuffer;
use embedded_graphics::primitives::PrimitiveStyle;
// Create a framebuffer for a 64x32 panel with 3-bit color depth
const ROWS: usize = 32;
const COLS: usize = 64;
const BITS: u8 = 3; // Color depth (8 brightness levels, 7 frames)
const NROWS: usize = compute_rows(ROWS); // Number of rows per scan
const FRAME_COUNT: usize = compute_frame_count(BITS); // Number of frames for BCM
let mut framebuffer = DmaFrameBuffer::<ROWS, COLS, NROWS, BITS, FRAME_COUNT>::new();
// Draw a red rectangle
Rectangle::new(Point::new(10, 10), Size::new(20, 20))
.into_styled(PrimitiveStyle::with_fill(Color::RED))
.draw(&mut framebuffer)
.unwrap();
// Draw a blue circle
Circle::new(Point::new(40, 20), 10)
.into_styled(PrimitiveStyle::with_fill(Color::BLUE))
.draw(&mut framebuffer)
.unwrap();
§Implementation Details
The framebuffer is organized to directly match the HUB75 connector signals:
- Each 16-bit word maps directly to the HUB75 control signals
- Color data (R, G, B) for two sub-pixels is stored in dedicated bits
- Control signals (output enable, latch, address) are mapped to specific bits
- Multiple frames are used to achieve Binary Code Modulation (BCM)
- DMA transfers the data directly to the panel without any transformation
§HUB75 Signal Bit Mapping
Each 16-bit Entry
represents the logic levels that will be driven onto the HUB75
bus during a single pixel-clock cycle. Every panel signal—apart from the clock
(CLK
) itself—occupies a dedicated bit, allowing the word to be streamed via DMA
with zero run-time manipulation.
15 ─ Dummy2 (spare)
14 ─ B2 Blue – lower half of the panel
13 ─ G2 Green – lower half of the panel
12 ─ R2 Red – lower half of the panel
11 ─ B1 Blue – upper half of the panel
10 ─ G1 Green – upper half of the panel
9 ─ R1 Red – upper half of the panel
8 ─ OE Output-Enable / Blank
7 ─ Dummy1 (spare)
6 ─ Dummy0 (spare)
5 ─ LAT Latch / STB
4-0 ─ A..E Row address lines
The pixel clock is generated by the peripheral that owns the DMA stream and is therefore not part of the 16-bit word stored in the framebuffer.
§Binary Code Modulation (BCM) Frames
Brightness is achieved with Binary-Code-Modulation as outlined in
https://www.batsocks.co.uk/readme/art_bcm_1.htm. For a colour depth of
BITS
, the driver allocates FRAME_COUNT = 2^BITS - 1
frames. Frame n
(0-based) is displayed for a duration proportional to 2^n
, mirroring the
weight of that bit in a binary number.
When a pixel is written its 8-bit RGB components are compared against a per-frame threshold:
brightness_step = 256 / 2^BITS
threshold_n = (n + 1) * brightness_step
If a channel’s value is greater than or equal to threshold_n
the
corresponding colour bit is set in frame n. Streaming the frames from the
least-significant (shortest) to the most-significant (longest) time-slot
produces the correct 8-bit brightness while keeping the refresh routine
trivial.
§Safety
This implementation uses unsafe code for DMA operations. The framebuffer must be properly aligned in memory and the DMA configuration must match the buffer layout.
Structs§
- DmaFrame
Buffer - DMA-compatible framebuffer for HUB75 LED panels.