📜 ⬆️ ⬇️

DIY controller LED panel on CPLD using BAM modulation

Some time ago, I participated in the discussion of the DIY project of a matrix LED clock.
And what surprised me was that the ancient monochrome 8x8 LED matrixes were used as a display device in increments of 5 millimeters. Moreover, complex printed circuit boards were divorced under them, and software dynamic display was made. And this is at a time when ready-made full-color LED panels 64x32 in 3 mm increments are available at a price of around $ 10-20 a long time ago. And the total range of such panels is very large and has a pixel pitch from 2 to 10 mm and almost any size.

At the same time, it is rather difficult to use such panels in DIY constructions - ready controllers cost quite a lot of money and do not have a normal API. It’s quite difficult to do quite a quick scan of the panel on microcontrollers commonly used in DIY. Moreover, time intervals must be maintained with high accuracy - otherwise a noticeable unevenness of brightness begins.

There are some good solutions at Adafruit , but they are all quite expensive and complicated.

After some thought, the thought arose - why not make an extremely inexpensive board that would be a bridge between a regular penny type Arduino board and a LED panel? After a couple of months, the fussing was born.

This article describes the second, improved version of the controller.

Task


As a basic task, I wanted to be able to manage the team panel with a total size of at least 64x64, while having the ability to work at least in Highcolor (RGB565) while maintaining an acceptable refresh rate (at least 50 Hz). In the first version of the controller, the basic task was fully implemented, but the idea of ​​implementing the task by another, very promising method arose, from which the second version was born.

Basic explanation of the device typical LED panel


At the input interface HUB75:


At each color input there is a chain of registers of type HC595 (but special 16-channel versions for LEDs). Registers so much that enough for the width of the panel. The inputs of the clock, parallel load and output resolution are common to all registers. The inputs ABCDE - this is the choice of a number - go to the usual descrambler.

Principle of operation:

  • set the data to the RGB inputs, click the CLK clock. Repeat until we load the entire line.
  • turn off the outputs OE = 1 (so that there is no interference)
  • issue the number of the loaded row to the decoder
  • we click parallel loading LAT - these lines are transferred to the output registers
    enable outputs OE = 0
  • repeat for the next row

That is the classic dynamic display. It is clear that with this method in one such cycle we can only turn on / off each specific LED.

In order to obtain brightness gradations with classic PWM, such a cycle has to be repeated N-1 times, where N is the number of brightness gradations (256 for RGB888). And considering that while it still flickers - all this must be done very, very quickly.

There is a workaround - Bit Angle Modulation (BAM). At the same time, the glow time in each cycle is proportional to the weight of the displayed bit. That is, for RGB888, only 8 display cycles are needed. A little more detail - here .

The first version of the controller used the classic PWM, which imposed a hard limit on the number of scan cycles. In the second version, BAM is implemented, which gave a huge speed gain.

Implementation


It was quite obvious that the usual microcontroller pulls only small panels — for large ones, there simply isn’t enough speed. Therefore, CPLD or FPGA is indispensable here - it is physically impossible to issue tens of MB / s on inexpensive microcontrollers.

As a memory, on the IXBT forum, I was recommended a very interesting FIFO memory Averlogic AL422B, which has approximately 400kbytes of memory and can operate at frequencies up to 50 MHz.

Considering that my main requirement was the maximum cheapness of the components, so that the finished shawl would be available to the home-builders - Altera EPM3064 - CPLD with 64 macro cells was chosen. At the same time, such a small number of macrocells does not allow making a dynamically configurable card — the configuration must be compiled directly into CPLD.

→ The resulting scheme is here.

Details:

  • CPLD EPM3064ATC44-10 - the price of Ali is about $ 13-15 per ten
  • FIFO RAM AL422B - the price of Ali is about $ 15 per ten
  • Quartz oscillator at 50 MHz. The board provides for installation in DIP14 / DIP8 / 7050 packages. Ali is priced at about $ 6-7 for a dozen
  • 3.3V stabilizer in SOT223 package. Price in Chip and Dip - 40p per item
  • IDC-10MS connector. Price in Chip and Dip - 3 p / piece
  • IDC-16MS connector. Price in Chip and Dip - 8 p / piece
  • IDC-14MS connector. Price in Chip and Dip - 7 p / piece
  • Capacitors 1µF 0805 - 8 pieces approximately 1 r / piece
  • Condenser 0.1 microfarad 0805 - approximately 1 r / piece
  • Resistor 10k 0805 - pennies

The total for parts is 1.5 + 1.5 + 0.7 = 3.7 $ and 40 + 3 + 8 + 7 + 8 * 1 + 1 = 67 p. All together within $ 5 - a penny.

→ The original drawing of the board is here.

→ Prepared gerber files for order

The board is prepared for the first version, in which there was no RE control. To use it with the second version, you must cut the jumper between pins 23 and 24 of the AL422B and throw the wiring from the pin 28 of the EPM3064 (it is on the pad) onto pin 24 of the AL422B.

When soldering the board, do not forget to solder the jumpers to the power on the reverse side of the board.





Calculations


Calculations of the necessary parameters are quite complicated.

The fact is that two processes are running in parallel in the controller - loading the data of the next line / indication of the line already loaded.

Processes run simultaneously, but end at different times, so a process executed faster waits for the completion of a longer process.

→ An Excel-plate was made for calculation.

Initial data:

  • CRYSTAL_FRQ (MHz) - generator frequency (50 MHz)
  • PIXEL_COUNT - the number of pixels in the download line. More details in the switching section
  • RGB_INPUTS - the number of RGB inputs used in the HUB75E interface of the panel used. 1 or 2
  • BYTES_PER_PIXEL - bytes per pixel. In our case, always 3 - RGB888
  • SCAN_LINES - the number of scan lines in the panel used. 8/16/32

Selectable parameters:

  • PRE_DELAY - the delay from the LAT signal to the activation of the OE, set in cycles
  • PRESCALER - prescaler for the main counter. That is, if the prescaler is 8 and the weight of the current bit is 4, then the OE will be enabled at 8 * 4 = 32 cycles
  • POST_DELAY - the minimum delay from switching off the OE to the next LAT signal, set in cycles

For example, we have a 32x32 panel with 8 scan lines and 2 RGB inputs. Such a panel has two HUB75E connectors, that is, physically these are two 32x16 panels. We connect these panels in series, that is, logically, this panel will look like 64x16.

PRE_DELAY and POST_DELAY are the blanking intervals before and after the output enable (OE) in order for the multiplexers to have time to switch the outputs and the keys to open / close. Without them, there will be “tricks” from burning pixels to adjacent lines. Values ​​are selected experimentally for a specific panel. Usually enough 15 cycles (set in cycles).

This raises the question of choosing a prescaler - how to choose it.

A small prescaler value gives a small frame display time, but reduces the overall brightness. A great value of the prescaler increases the frame display time, that is, when iterating, it causes the screen to flicker.

Let's try PRESCALER = 1

We get:

OE_EFFICIENCY - 8.3%, i.e. the panel will work for only 8.3% of the maximum possible brightness
FRAMES_PER_SECOND - 2034 fps - but the refresh rate of the image will be huge - more than 2000 fps.

The loss of brightness is very big.

Let's try PRESCALER = 16

We get:

OE_EFFICIENCY - 72.9%, that is, the panel will work at 72.9% of the possible maximum brightness
FRAMES_PER_SECOND - 1117 - and the picture refresh rate is very good - more than 1000 fps.
Well, it is quite normal - the efficiency of more than 50% is quite normal and the frame rate is very good.

The general rule of thumb is PRESCALER is about 8 times smaller than the product PIXEL_COUNT * RGB_INPUTS

Well, then read and check.

Switching LED panels


All panels are connected in series. Connection diagram: first from right to left, then from bottom to top. That is, we first connect the horizontal lines in series, then the output of the lower row to the entrance of the second from the bottom of the row, etc. to the top row.

The controller clings to the bottom right panel.

There are panels that have two input and two output connectors. Such panels are essentially just a mechanical assembly of two panels vertically. Commuted as two independent panels.

After the assembly, it is necessary to calculate the total length of the chain in pixels - for this we look at - how many panels did it all happen in the chain and multiply this number by the width of the panel in pixels. This number will then need to be driven into the PIXEL_COUNT value in the CPLD configuration and into the timing calculator.

FPGA Firmware


All necessary files lie on github . Download directly to the folder.

After registration, you need to download and install Quartus II 13.0sp1 from the Altera website. It is necessary to download EXACTLY this version - newer versions no longer support the MAX3000 series. Breaking it is not necessary - enough Web edition (free) version. When downloading, do not forget to tick the support for MAX3000 and Programmer. Just in case, I warn you - a large package, about two gigs. You will also need Altera USB Blaster - the regular price for ali is about $ 3.

Open the project al422_bam.qpf. On the left, open the bookmark file and open the file al422_bam.v - this is the main project file. It is necessary to configure the parameters:

How many RGB inputs on the panel - on the panels with the input HUB75 can be 1 or 2 RGB inputs. To find out how many inputs you can in this way - we take the number of pixels on the panel vertically. We divide it by the number of scan lines (indicated in the panel designation as 8S, for example). Divide by the number of input connectors (1 or 2). For example - I have a 32x32 panel, an 8S scan and two input connectors — 32/8/2 = 2 — means two RGB inputs.

`define RGB_outs 2 

How many scan lines on the panel - as supported by the standard HUB75E, it can be up to 32x. The number of scan lines is usually in the panel name in the form of 8S / 16S / 32S, respectively.

Only one required line should be uncommented:

 `define SCAN_x8 1 //`define SCAN_x16 1 //`define SCAN_x32 1 

The total number of horizontal pixels in the chain. Pixels are counted in the whole panel chain - see the section above “Switching LED panels”

 `define PIXEL_COUNT 64 

Phase output signals. The most typical configuration is such - OE is active at a low level (comments are cleared), CLK is working on the front (comments is worth), LAT is active at a high level (comments is worth). There are all sorts of strange options. Find out which one you have only in an experimental way or by removing the circuit and searching for datasheets on the used chips).

 //`define LED_LAT_ACTIVE_LOW 1 `define LED_OE_ACTIVE_LOW 1 //`define LED_CLK_ON_FALL 1 

Pre and post delay of OE signal relative to LAT and prescaler for main counter. See above.

 `define OE_PRESCALER 16 `define OE_PREDELAY 31 `define OE_POSTDELAY 31 

Everything, we press ctrl-L - the project is compiled. If you never screwed it up, there will be some warnings, but there should be no errors. Next we catch the soldered board to the USB Blaster, power up the board. In Quartus go to the tools - programmer. Select in the Hardware setup USB-blaster, click Start. Everything, CPLD is programmed.

Microcontroller part


The data output to the controller, in general, is extremely simple - we reset the address of the record and then sequentially issue data bytes, strobe them with a WCLK signal. And there seems to be even banal arduinka enough for work. But there are two problems:

a) It takes a lot of memory. Even a small 32x32 panel in RGB888 mode requires 3kBytes of memory for the screen buffer. Ordinary Arduino based on Atmega328 contain only 2kb of RAM. You can, of course, use the Mega Atmega2560 base card, which already contains 8 kByte of RAM, but even this is not enough for panels of normal size - a 128x64 panel in RGB565 mode requires 16kB of memory.

b) In the process of working with the AL422B, a glitch that was not documented anywhere appeared - when writing data at a speed of less than 2MB / s, the address counter does not work correctly and writes data "not there." Perhaps this is the glitch of my party. Probably no. But this glitch has to be bypassed. Considering that AVR8 works at 16 MHz, it is almost impossible to get data from it at the required speeds.

The proposed solution is to go to cheap shawls based on a 32-bit STM32F103C8T6 controller. Such a headscarf costs about $ 2.5 per piece on Ali, or about $ 1.7 when buying a dozen, that is, even Arduino Nano is cheaper. In this case, we get a full-fledged 32-bit microcontroller operating at 72 MHz and having 20 kB of RAM and 64 kB of flash (compare with Atmega328 2kB / 8kB, which stands on the Nano).

At the same time, such cards are quite successfully programmed in the Arduino environment. There is a good article about this in HykTimes , so I will not duplicate it. In general - do everything as described in the article.

In the Arduino environment, select the Generic STM32F103C board, variant STM32F103C8. Data goes through DMA, so you can use any optimization option.

Switching occurs as follows:

Hard-nailed in the library:
A0..A7 → DI0..DI7 AL422B
B0 → WCLK AL422B
B1 → WRST AL422B

Assigned in a sketch to the controller:
B10 → WE AL422B

Common wire:
G → GND

Well, do not forget to apply 5V / GND power from the panel to the corresponding pins of the controller.

Pinout connector on the controller to take from the scheme .



Software part


Since the task was to make everything as simple and accessible as possible, all the software was made for the Arduino environment and was designed as an LED_PANEL library.

The LED-PANEL library makes extensive use of the Adafruit GFX library, so it must be installed.

I strongly recommend not to put the LED_PANEL library in the libraries directory, but leave it in the folder with the sketch. The fact is that there are a lot of iron-bound parameters, and if you want to transfer work to a fatter microcontroller, you will have to change a lot of things in the code itself.

Initialization is approximately as follows:

 #include "LED_PANEL.h" #define width 32 #define height 32 #define bpp 3 #define scan_lines 8 #define RGB_inputs 2 #define we_out_pin PB10 LED_PANEL led_panel = LED_PANEL(width, height, bpp, scan_lines, RGB_inputs, we_out_pin); 

that is, we create an instance of the class LED_PANEL, for which we specify the parameters:

width - the total width of the panel in pixels (total)
height - the total height of the panel in pixels (total)
bpp - byte per pixel, 3 for RGB888. BAM version only works in RGB888
scan_lines - the number of scan lines - 8/16/32. It must match the mode stitched to the controller.
RGB_inputs - the number of RGB inputs in the connector HUB75 - 1/2. It must match the mode stitched to the controller.
we_out_pin — pin to which the WE pin is hooked

I pay attention that at initialization only pin WE is set. All other pins are hard-coded in the code, since they are tied to the used timer channels and DMA and changing them will result in significant changes in the code.

Launch and clear the screen in the setup section:

  led_panel.begin(); led_panel.clear(); 

begin initializes the necessary pins to the output, connects the timer and DMA
clear clears the buffer

For drawing, you can use all the standard procedures of the Adafruit GFX library - from the simplest drawPixel to the text output. The procedure used to issue a drawing to the buffer is:

 led_panel.show(); 

In this form, show initiates data transfer to the controller via DMA and immediately returns control. You can find out if the transfer is over using the led_panel.OutIsFree () function - if it says true, then the transfer has ended. There is a feature - if you call show when the transfer has not finished yet - it will be simply ignored.

 led_panel.show(false); 

analogue show (), but if you call show (false), and the transfer has not yet completed, the procedure will wait for the transfer to complete, then start a new transfer and return control:

 led_panel.show(true); 

analog show (false), but if you call show (true), then after the start of a new transfer, the procedure will not return control until the transfer is completed.

In general - everything.



Some notes on the software:

a) Gamma correction is introduced when recalculating colors from RGB565 (which the library uses) with the ExpandColor function. In all other cases, the linear transfer function is used, that is, the brightness is directly proportional to the value.
b) The software allows you to connect multiple LED-controllers to one microcontroller board. To do this, you must give the data bus to the controllers parallel to the RST and CLK lines. The required controller is selected via the WE line. In the software, you need to create a separate instance of the LED_PANEL class for each controller, and each instance during initialization must have different WE lines (the last parameter).

TO DO


- Deal with the "aiming" of colors on adjacent rows. It looks like a bad wiring of the panel itself (the keys are littering), but you need to check. Just arrived a new panel - I will check;
- Make a new version of the board - with the already diluted RE and the addition of output level converters in 5V;
- Make a class META_LED_PANEL, which allows you to combine several LED_PANEL in one virtual screen - this will allow you to create very large screens with multiple controllers;
- In the future, go to a more powerful series of CPLD, for example CycloneIV. This would significantly expand the possibilities if the cost is kept low (EP4CE6E22 costs about $ 5 per Chinese, while there are 100 times more macrocells and about 32 kB of internal memory). But I will do this sometime later. If you want. Since such developments take too much time.

Source: https://habr.com/ru/post/409743/