How to drive 4096 RGB LEDs (each 24Bit RGB brightness) with one STM32 Microcontroller (without much CPU load)

A while ago I ordered some chinese LED modules (the very cheap ones) on Aliexpress and now they arrived very quickly.
I ordered also a cheap chinese LED controller but I was not realy surprised about the functionality and the software was ugly.
So i decided to drive the LED matrices with my own controller.
The choose which microcontroller or to say which development board was quite easy because it was clear that these LED modules need a powerfull controller because they are usually driven by FPGAs.
Moreover I had some STM32F429I-DISCOs laying around without LCD, perfect choice for the project.
First i got some informations about driving this LED modules. The LED modules are labelled with Scan 1/8 or Scan 1/16 mode. This means that the rows are multiplexed. To reduce the costs of the modules the rows are multiplexed and the Scan means how many rows need to be multiplexed.
I ordered some red modules with 32x64 pixel resolution and they used the HUB08 data interface and used 1/16 Scan mode.

Here is the specification of the LED module interface and what pins are connected to which microcontroller pin:


As you can see in the table above, the interface have 4 row select lines (A,B,C,D). To drive a row the data is simply shifted into and after that latched.
The output enable (labelled EN, or OE) is used to enable the setting (high or low) of the output driver at the shift registers, which then light up the inidividual LED or not.
Dependent on the scan mode the interface specifies also which data lines need to be driven. So for the red module I need to drive 2 (32/16, 32 module height * scan mode) red data lines, 4 row select lines, a clock (for the data lines), a latch (to apply the shifted data) and a output enable.
To light up one LED red I have to shift 64 Bits for the width and then latch/set output enable and shift the next width data and set next row. This process is repeated for the half of the height (because the module is splitted at the middle regarding to the scan mode).
With this method it is possible to get 1 bit resolution color for the whole pixel module.
To get more brightness adjustments the process of driving one row is repeated.
I want at minimum 5 or more Bits, usually the higher the better and the images you want to display are nicer.
So traditionally this is PWM, means you repeat the set row process two to the power of the bits you want to have.
This results in much data to transmit to the module and also CPU time.
Can we do better?
Yes, there is a method called BAM, where you use the bit position as an inidicator how long you set the LED to bright or to dark.
So for the LSB you light it up to say 1 time and for the next bit you light it up the double time and for the next bit you light it up the double time (overall 4 times) and so on.
This is no problem to implement for one or a bunch of LEDs with a timer and interrupt on a traditional microcontroller.
My solution was to drive the LED module interface with the poor GPIO peripheral but with the DMA.
So now I have my array of data which I want to send to the LED module as fast as possible.
The process is now calculate the data for to the interface of the LED module and then send it with the DMA in the so called circular mode (to repeat the data sending process) and a timer is used to let the DMA now that he need to transfer data to the GPIO.
This works quite nice, one drawback is of course that you have also to calculate the clock and so it doubles the led module data size. I use the timer output channel as the CLK pin, so I need only the data for the color channels, latch and output enable. At the first test i got something around 20MHz (toggling speed at clock) at 168MHz CPU speed. Maybe it is possible to get higher frequency but I had too much jitter, so other LEDs flickered.
But we save much data if we use also BAM and not PWM, because to get 8 Bit color resolution per individual color pixel the led module data array is 255 times bigger and with BAM only 8 times.
How can we get the change of the light time regarding to the bit position? I used also a DMA to drive the prescaler register of the Timer, which then drives the DMA to send the calculated led data to the GPIO. Refer to the figure at the bottom, it displays the peripherals which are used.


Now the concept works for the red led module. I changed the data calculation to work also with the RGB LED module, which has 2 red, 2 green, 2 blue data lines and only 3 row select lines (bevause it uses 1/8 scan mode and of course clock, latch and output enable).
With the BAM approach I was able to drive 8 panels in series, so in overall a width of 256 and a height of 16 RGB pixels, each with 8 Bit brightness resolution.

To test the display and the concept i wrote some functions to calculate the led data from a normal image array, display text and also to send data from a PC using the USB (as image and after the transfer for the entire image is finished it is calculated and updates the led data).

Last but not least:
I will not publish the whole project, only the files which I created or changed. The source code used as basis the STM32F429USBVCP project from (source is available at
There are bugs in the source code, it is a proof of concept.

Here are some photos of the LED module and the wiring (sorry for the poor quality):

alt alt alt alt

Here is the main source code of the proof of concept (updated version, uses less RAM):

Some notes about power supply: The microcontroller runs at nearly 3V therfore also the output pins can go max. to the same voltage. This can be a problem, because my modules are rated for 5V power supply. I had no problems going up to 5V power supply at the red LED matrix module, but the RGB module had problems above 4.5V. I recommend to get a high quality 5V power supply, which can slightly change the output voltage.
Another solution is to get some buffer ICs or level shifters and place it between the microcontroller output pins and the module.
I noticed using a good power supply and regulating it to 4.4V output voltage is good enough. I noticed no visual brightness change between 5V and 4.4V.

Stay charged!

comments powered by Disqus