Module plain

Source
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§

DmaFrameBuffer
DMA-compatible framebuffer for HUB75 LED panels.