Hi, my name's Louis, and I designed the SmartMatrix Shield for Teensy
and wrote the SmartMatrix Library
which was originally designed to make it easy to add a HUB75 matrix panel to a Teensy Arduino sketch
. I've been wanting to add support for a platform with WiFi capability to the SmartMatrix Library for years, and was excited to find this forum thread and example code that makes it possible using the ESP32.
The way of refreshing the panels in Sprite_tm's example code makes a lot of sense for the ESP32's architecture, but highlights some problems with these HUB75 panels. After charging up a row by setting the address lines and then releasing that row by moving to a new row, the row drivers needs to be discharged through the column drivers and LEDs. This lights up pixels that may not have been intended to be lit on the current row. This shows up every time the address line changes, and the method of refreshing the panels in the example code is switching rows as frequently as possible, leading to the maximum number of ghost pixels showing up.
Here's a photo of the example code refreshing a panel with CLK set to 20MHz, and a test pattern displayed. The diagonal line maximizes the ghost pixels as each row has only a single unique column lit, forcing previous rows to discharge through a pixel that wasn't intended to be lit.
Here's some other references describing the issue and solutions.
https://www.maximintegrated.com/en/app- ... vp/id/4111
The best solution - that doesn't involve modifying the matrix panels themselves - seems to be to minimize the number of row switches when refreshing, so shift out all the color bits for a given row at once, before moving to the next row.
I modified the example code to do just that. Here's the same panel and test pattern refreshed with CLK set to 20MHz.
It looks much better, at a distance you may not even notice the ghost pixels. They are still there, but dimmer and limited to the row above each lit column. (There's another even dimmer green pixel lit below the row, but I think this is an unrelated bug I haven't yet tracked down)
This method of refreshing has some downsides. Because the binary time division is applied to each row, not each frame, it requires more linked list entries, using up more RAM. The simplest way of applying the technique would require 16x the RAM for the linked lists (32 rows / 2 pixels refreshed in parallel per row). I'm reducing the RAM required in two ways.
First, I sweep from color LSB to MSB, instead of a color bit at a time, which divides the RAM required in half.
Second, I added a new LSBMSB_TRANSITION_BIT definition, which defines the color bit that is refreshed once per frame, with the same brightness as the bits above it. Every color bit below it is refreshed only once, with fractional brightness. If LSBMSB_TRANSITION_BIT is 2 and brightness is 50%, color bit 1 will be refreshed only once with brightness 25%, and color bit 0 refreshed only once with brightness 12.5%. The color bits from LSBMSB_TRANSITION_BIT to the MSB are refreshed with normal brightness 1x, 2x, 4, … 2^n. This divides the RAM required for linked lists in half each time LSBMSB_TRANSITION_BIT increases by one. It also speeds up the refresh rate as there is less data to shift out per row. The cost is less resolution in brightness, decreased maximum brightness, and at dimmer brightness levels, some lower color bits will not be visible.
I made the above changes to the code, and cleaned some of it up (fewer magic numbers), and fixed a few bugs. The modified example code is up on GitHub. (If you want to change the CLK speed, you'll need to edit that in i2s_parallel.c, the code using clkspeed_hz is broken and I haven't tried fixing it yet.)
https://github.com/pixelmatix/esp32_I2s ... aLedMatrix