https://github.com/jupfu/hub75
Hub75 LED matrix panel driver for Raspberry Pi Pico
https://github.com/jupfu/hub75
c c-sdk dma hub75 led-matrix-displays pico pio raspberry-pi-pico
Last synced: 12 months ago
JSON representation
Hub75 LED matrix panel driver for Raspberry Pi Pico
- Host: GitHub
- URL: https://github.com/jupfu/hub75
- Owner: JuPfu
- License: mit
- Created: 2025-03-30T20:42:11.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-05-24T17:36:52.000Z (about 1 year ago)
- Last Synced: 2025-05-24T18:35:35.753Z (about 1 year ago)
- Topics: c, c-sdk, dma, hub75, led-matrix-displays, pico, pio, raspberry-pi-pico
- Language: C++
- Homepage:
- Size: 5.12 MB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# HUB75 DMA-Based Driver
https://github.com/user-attachments/assets/7c41193c-c724-4fae-8823-af36d70fcedd
*Demo video: Colours are much brighter and more brilliant in reality*
## Documentation and References
This project is based on:
- [Raspberry Pi's pico-examples/pio/hub75](https://github.com/raspberrypi/pico-examples)
- [Pimoroni's HUB75 driver](https://github.com/pimoroni/pimoroni-pico/tree/main/drivers/hub75)
To understand how RGB matrix panels work, refer to the article **[Everything You Didn't Want to Know About RGB Matrix Panels](https://learn.adafruit.com/adafruit-gfx-graphics-library/what-is-the-gfx-library)**.
For details on Binary Coded Modulation (BCM), see **[LED Dimming Using Binary Code Modulation](https://www.ti.com/lit/an/slva377a/slva377a.pdf)**.
---
## Achievements of the Revised Driver
The modifications to the Pimoroni HUB75 driver result in the following improvements:
- **Offloading Work**: Moves processing from the CPU to DMA and PIO co-processors.
- **Performance Boost**: Implements self-paced, interlinked DMA and PIO processes.
- **Eliminates Synchronization Delays**: No need for `hub75_wait_tx_stall`, removing blocking synchronization.
- **Optimized Interrupt Handling**: Reduces code complexity in the interrupt handler.
These enhancements lead to significant performance improvements. In tests up to a **250 MHz system clock**, no ghost images were observed.
---
## Motivation
As part of a private project, I sought to gain deeper knowledge of the Raspberry Pi Pico microcontroller. I highly recommend **[Raspberry Pi Pico Lectures 2022 by Hunter Adams](https://youtu.be/CAMTBzPd-WI?feature=shared)**βthey provide excellent insights!
If you are specifically interested in **PIO (Programmable Input/Output)**, start with [Lecture 14: Introducing PIO](https://youtu.be/BVdaw56Ln8s?feature=shared) and [Lecture 15: PIO Overview and Examples](https://youtu.be/wet9CYpKZOQ).
Inspired by Adams' discussion on **[DMA](https://youtu.be/TGjUHChO1kM?feature=shared&t=1475) and PIO co-processors**, I optimized the HUB75 driver as a self-assigned challenge.
π **[Raspberry Pi Pico Lectures 2025 by Hunter Adams](https://youtu.be/a4uLrfqHZQU?feature=shared")** is available now!
---
## Evolution of Pico HUB75 Drivers
### Raspberry Pi Pico HUB75 Example
The **Pico HUB75 example** demonstrates connecting an **HUB75 LED matrix panel** using PIO. This educational example prioritizes clarity and ease of understanding.
- The color palette is generated by modulating the **Output Enable (OE)** signal.
- **Binary Coded Modulation (BCM)** is applied row-by-row, modulating all color bits before advancing to the next row.
- Synchronization depends on `hub75_wait_tx_stall`.
- **No DMA is used**, leading to lower performance.
### Pimoroni HUB75 Driver
The **Pimoroni HUB75 driver** improves performance by:
- Switching from **row-wise** to **plane-wise** modulation handling.
- Using **DMA** to transfer pixel data to the PIO state machine.
- Still relying on `hub75_wait_tx_stall` for synchronization.

*Picture 1: Pimoroni's Hub75 Driver DMA Section*
---
## Eliminating `hub75_wait_tx_stall`
Both the **Raspberry Pi and Pimoroni** implementations use `hub75_wait_tx_stall`, which ensures:
- The state machine **stalls** on an empty TX FIFO.
- The system **waits** until the OEn pulse has finished.
However, this blocking method **prevents an efficient DMA-based approach**.
### Original `hub75_wait_tx_stall` Implementation
```c
static inline void hub75_wait_tx_stall(PIO pio, uint sm) {
uint32_t txstall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + sm);
pio->fdebug = txstall_mask;
while (!(pio->fdebug & txstall_mask)) {
tight_loop_contents();
}
}
```
### Alternative Approach
Instead of waiting for TX FIFO stalling, we can:
1. Modify the **PIO program** to emit a signal once the OEn pulse completes.
2. Set up a **DMA channel** to listen for this signal.
3. Establish an **interrupt handler** to trigger once the signal is received.
This approach allows fully **chained DMA execution** without CPU intervention.

*Picture 2: Modified hub75_row Program*
---
## DMA Chains and PIO State Machines in the Revised HUB75 Driver
### Overview
The following diagram illustrates the interactions between **DMA channels** and **PIO state machines**:
```
[ Pixel Data DMA ] -> [ hub75_data_rgb888 PIO ]
|
|--> [ Dummy Pixel Data DMA ] -> [ hub75_data_rgb888 PIO ]
|
|--> [ OEn Data DMA ] -> [ hub75_row PIO ]
|
|--> [ OEn Finished DMA ] (Triggers interrupt)
```
### Step-by-Step Breakdown
1. **Pixel Data Transfer**
- Pixel data is streamed via **DMA** to the **hub75_rdata_gb888** PIO state machine.
- This handles shifting pixel data into the LED matrix.
2. **Dummy Pixel Handling**
- A secondary **dummy pixel DMA channel** adds additional pixel data.
- This ensures correct clocking of the final piece of genuine data.
3. **OEn Pulse Generation**
- The **OEn data DMA channel** sends 32-bit words - 5 bit address information (row select) and 27 bit puls width - to the **hub75_row** PIO state machine.
- This output enable signal switches on those LEDs in the current row with bit set in the current bitplane for the specified number of cycles.
4. **Interrupt-Driven Synchronization**
- A final **OEn finished DMA channel** listens for the end of the pulse.
- An **interrupt handler** (`oen_finished_handler`) resets DMA for the next cycle.

*Picture 3: Chained DMA Channels and assigned PIOs*
---
### Refresh Rate Performance
With a **bit-depth of 10**, the HUB75 driver achieves the following refresh rates for a 64 x 64 matrix depending on the system clock:
| System Clock | Refresh Rate |
|--------------|---------------|
| 100 MHz | 179 Hz |
| 150 MHz | 268 Hz |
| 200 MHz | 358 Hz |
| 250 MHz | 448 Hz |
These results demonstrate stable operation and high-performance display rendering across a wide range of system clocks.
### Key Benefits of this Approach
β
Fully **automated** data transfer using **chained DMA channels**.
β
Eliminates **CPU-intensive** busy-waiting (`hub75_wait_tx_stall`).
β
Ensures **precise timing** without unnecessary stalling.
---
## Conclusion
By offloading tasks to **DMA and PIO**, the revised HUB75 driver achieves **higher performance**, **simpler interrupt handling**, and **better synchronization**. This approach significantly reduces CPU overhead while eliminating artifacts like **ghosting** at high clock speeds.
If you're interested in optimizing **RGB matrix panel drivers**, this implementation serves as a valuable reference for efficient DMA-based rendering.
---
## Demo Effects
β οΈ The examples contained in hub75_lvgl.cpp have been tested with a Raspberry Pi Pico 2 microcontroller (RP2350). For a RP2040 processor you might have to comment out some demo effects due to minor memory capabilities. Ask if you need support π.
## How to Use This Project in VSCode
You can easily use this project with VSCode, especially with the **Raspberry Pi Pico plugin** installed. Follow these steps:
1. **Open VSCode and start a new window**.
2. **Clone the repository**:
- Press `Ctrl+Shift+P` and select `Git: Clone`.
- Paste the URL: `https://github.com/JuPfu/hub75`

- Choose a local directory to clone the repository into.
3. **Project Import Prompt**:
- Consent to open the project.
- When prompted, "Do you want to import this project as Raspberry Pi Pico project?", click **Yes** or wait a few seconds until the dialog prompt disappears by itself.
4. **Configure Pico SDK Settings**:
- A settings page will open automatically.
- Use the default settings unless you have a specific setup.
- Click **Import** to finalize project setup.
- Switch the board-type to your Pico model.
5. **Wait for Setup Completion**:
- VSCode will download required tools, the Pico SDK, and any plugins.
6. **Connect the Hardware**:
- Make sure the HUB75 LED matrix is properly connected to the Raspberry Pi Pico.
- Attach the Rasberry Pi Pico USB cable to your computer
7. **Build and Upload**:
- Compiling the project can be done without a Pico attached to the computer.
- Click the **Run** button in the bottom taskbar.
- VSCode will compile and upload the firmware to your Pico board.
> π‘ If everything is set up correctly, your matrix should come to life with the updated HUB75 DMA driver.
---
## Next Steps
- **Add another chained DMA channel** to further reduce calls to the oen_finished_handler, trading memory for reduced CPU load.
- **Investigate removing the hub75_data_rgb888_set_shift method**, potentially achieving a completely DMA- and PIO-based solution with no CPU involvement.
For any questions or discussions, feel free to contribute or open an issue!