HUB75-style RGB panels have no framebuffer of their own. The controller must continuously shift color bits, latch them, select a row address, and control output enable. An FPGA is a strong match because those deadlines can be expressed as a deterministic state machine. This tutorial explains the interface and builds the safe row-scan sequence.
The signal groups
- R0/G0/B0 and R1/G1/B1: two RGB pixels shifted in parallel, usually for an upper and lower row.
- CLK: shifts one column of color data.
- LAT or STB: transfers the shift register into the output latch.
- OE: output enable, commonly active-low.
- A–E: binary row address; the count depends on the panel scan ratio.
Panel naming is not a complete electrical specification. Verify the connector, logic voltage, scan ratio, channel order, and timing on the exact unit. Some panels swap green and blue or use a nonstandard address arrangement.
One safe row transaction
- Drive OE inactive to blank the outputs.
- Shift one bit per color channel for every column.
- Pulse LAT while outputs remain blanked.
- Set the row address and allow it to settle.
- Drive OE active for the desired dwell time.
- Blank again before the next latch or row transition.
Blanking around latch and address changes is the main defense against ghosting. The interval can be short, but it must be explicit and repeatable.
A row-scan state machine
typedef enum logic [2:0] {
BLANK, SHIFT_LOW, SHIFT_HIGH, LATCH, SET_ROW, DISPLAY
} state_t;
always_ff @(posedge clk) begin
case (state)
BLANK: begin
oe_n <= 1'b1;
col <= '0;
state <= SHIFT_LOW;
end
SHIFT_LOW: begin
hub_clk <= 1'b0;
{r1,g1,b1,r0,g0,b0} <= pixel_bits;
state <= SHIFT_HIGH;
end
SHIFT_HIGH: begin
hub_clk <= 1'b1;
if (col == COLUMNS-1) state <= LATCH;
else begin col <= col + 1'b1; state <= SHIFT_LOW; end
end
LATCH: begin
hub_clk <= 1'b0;
lat <= 1'b1;
state <= SET_ROW;
end
SET_ROW: begin
lat <= 1'b0;
row_addr <= row;
state <= DISPLAY;
end
DISPLAY: begin
oe_n <= 1'b0;
if (dwell_done) begin oe_n <= 1'b1; state <= BLANK; end
end
endcase
end
Production logic should register memory output ahead of the shift edge and constrain the external clock and data relationship. The conceptual FSM above makes signal ownership and blanking easy to review.
First-light test pattern
Start with one row and solid primary colors. Then display vertical stripes that change every column, followed by a single moving pixel. These patterns reveal channel swaps, column direction, row-address errors, and off-by-one clock counts much faster than a photograph.
Power and logic levels
A panel can draw several amperes at 5 V. Power it through its intended connector with appropriate wiring and ground return; do not route LED current through an FPGA board header. Many controller boards use level shifting between 3.3 V FPGA I/O and 5 V panel logic. Confirm the exact panel requirements before connection.
Panel references
Timing diagram: blank before changing state

A controller that follows this ownership sequence makes ghosting much easier to reason about. The output latch never changes while LEDs are visible, and the address bus receives a defined settling interval before output enable becomes active.
Self-check the transaction
The reference testbench asserts that LAT never pulses while outputs are enabled, counts exactly one shift-clock rising edge per column before each latch, and observes the row address wrapping after the configured number of groups.
unzip fpga-led-controller-examples.zip
cd fpga-led-controller-examples
make hub75
Calculate the refresh budget before adding color
The row transaction is repeated for every bitplane and every row group. Use the interactive HUB75 timing calculator to see how panel width, scan ratio, pixel clock, overhead, and BCM depth change the maximum full-frame refresh rate.
FPGA LED controller tutorial series
This article is part of the FPGA LED Controller Tutorials learning path. Continue with PWM vs. BCM, the HUB75 timing calculator, or SystemVerilog verification.
Leave a Reply