Skip to main content

Module latched

Module latched 

Source
Expand description

DMA-friendly framebuffer implementation for HUB75 LED panels with external latch circuit support.

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 8 bits in parallel. A latch circuit similar to the one shown below can be used to hold the row address. The clock is gated so it does not reach the HUB75 interface when the latch is open. Since there is typically 4 2 input nand gates on a chip the 4th is used to allow PWM to gate the output enable providing much finer grained overall brightness control.

Latch Circuit

§Key Differences from Plain Implementation

  • Uses an external latch circuit to hold the row address and gate the pixel clock, reducing memory usage
  • 8-bit entries instead of 16-bit, halving memory requirements
  • Separate address and data words for better control
  • Requires an external latch circuit; not compatible with plain HUB75 wiring

§Features

  • Support for RGB color with brightness control
  • Multiple frame buffers for Binary Code Modulation (BCM)
  • Integration with embedded-graphics for easy drawing
  • Memory-efficient 8-bit format

§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
  • 8-bit entries reduce memory usage compared to 16-bit implementations

§Example

use embedded_graphics::pixelcolor::RgbColor;
use embedded_graphics::prelude::*;
use embedded_graphics::primitives::Circle;
use embedded_graphics::primitives::Rectangle;
use embedded_graphics::primitives::PrimitiveStyle;
use hub75_framebuffer::compute_frame_count;
use hub75_framebuffer::compute_rows;
use hub75_framebuffer::Color;
use hub75_framebuffer::latched::DmaFrameBuffer;

// 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 efficiently use memory while maintaining HUB75 compatibility:

  • Each row contains both data and address words
  • 8-bit entries store RGB data for two sub-pixels
  • Separate address words control row selection and timing
  • Multiple frames are used to achieve Binary Code Modulation (BCM)
  • DMA transfers the data directly to the panel without transformation

§HUB75 Signal Bit Mapping (8-bit words)

Two distinct 8-bit words are streamed to the panel:

  1. Address / Timing (Address) – row-select and latch control.
  2. Pixel Data (Entry) – RGB bits for two sub-pixels plus OE/LAT shadow bits.

The bit layouts intentionally overlap so that the very same GPIO lines can transmit either word without any run-time bit twiddling:

Address word (row select & timing)
┌──7─┬──6──┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─┐
│ OE │ LAT │   │ E │ D │ C │ B │ A │
└────┴─────┴───┴───┴───┴───┴───┴───┘
       ^                ^
       |                └── Row-address lines (LSB = A)
       └── Latch pulse – when HIGH the current address is latched and
           external glue logic gates the pixel clock (`CLK`).
Entry word (pixel data for two sub-pixels)
┌──7─┬──6──┬─5──┬─4──┬─3──┬─2──┬─1──┬─0──┐
│ OE │ LAT │ B2 │ G2 │ R2 │ B1 │ G1 │ R1 │
└────┴─────┴────┴────┴────┴────┴────┴────┘

Bits 7–6 (OE/LAT) mirror those in the Address word so the control lines remain valid throughout the entire DMA stream.

§External Latch Timing Sequence

  1. Pixel data for row N is clocked out while OE is LOW.
  2. OE is raised HIGH – LEDs blank.
  3. An Address word with the new row index is transmitted while LAT is HIGH; the CPLD/logic also blocks CLK during this period.
  4. LAT returns LOW and OE is driven LOW again.

This keeps visual artefacts to a minimum while allowing the framebuffer to use just 8 data bits.

§Binary Code Modulation (BCM) Frames

Brightness is realised with Binary-Code-Modulation just like the plain implementation—see https://www.batsocks.co.uk/readme/art_bcm_1.htm. With a colour depth of BITS the driver allocates FRAME_COUNT = 2^BITS − 1 frames. Frame n (0-based) is displayed for a time slice proportional to 2^n.

For each channel the driver compares the 8-bit colour value against a per-frame threshold:

brightness_step = 256 / 2^BITS
threshold_n     = (n + 1) * brightness_step

The channel bit is set in frame n iff value >= threshold_n. Streaming the frames from LSB to MSB therefore reproduces the intended 8-bit intensity without extra processing.

§Memory Layout

Each row consists of:

  • 4 address words (8 bits each) for row selection and timing
  • COLS data words (8 bits each) for pixel data

§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 with external latch circuit support.