Creating an EXTA with a 32x64 video wall LED panel
As a side project of our DVTI camera project, we’ve built a simple EXTA based on a cheap video wall LED panel with 32 by 64 LEDs. At ESOP 2019, a couple of people have asked how this EXTA works, so we’ve decided to put this information on the web.
On the LED panel, you see a faint red border, centered around 20 by 50 pixels which compose the main are of the EXTA. Two lines below the bottom border you see a single green LED. The green dot jumps one position to the right every time a new second begins.
There’s no PCB built specifically for this project. We use a small FPGA breakout board to drive the panel. To synchronize the EXTA with the 1PPS signal, we feed an 1PPS pulse from one of our camera boards into the FPGA breakout board (via the yellow wire between the two green boards in the photo above).
What do I need to build the EXTA?
The list is short:
- 32 by 64 LED panel. Adafruit sells them, they also offer nice explanations and pinout descriptions for the panel. Alternatively, you also find these panels on ebay and Ali Express. If I remember correctly, we got ours from Ali Express for about USD 15, “free shipping”.
- FPGA board. Any FPGA should be OK for this task. We used a small Xilinx Virtex 2 FPGA (XC2V40). A while ago, I was able to buy a large number of these from ebay at a very low price to experiment with BGA soldering, and most of the boards work fine. BTW, there are also many FPGA development boards available if you don’t like soldering, or you can officially get small FPGAs for less then USD 10, e.g. this still in-production XC3S50A.
- Power supplies for the LED panel and FPGA board. For the LED panel, we use a 5V 20W wall adapter.
- 1PPS source. Most GPS receivers provide an 1PPS signal as an output. You can use the output of the GPS receiver as an input to the FPGA, or alternatively, if you create a PCB, you can add a GPS receiver module.
- FPGA design file. I’ve put our VHDL code on Github, see here. Even though we use a Xilinx FPGA, this should also work on Intel and Lattice, I guess.
How to connect the parts together?
The complex part are the connections to the LED panel. The panels usually come with a power supply connector at the center, an input connector at the left side and an output connector at the right side to chain multiple panels together. We only use the input and power connectors:
Here is a more detailed schematic for the connection between the FPGA board and the input connector of the LED panel:
The connections consist of 2 groups of R/G/B, one for the upper half of the display and one for the lower half, the 4 address lines A to D, the clock input CLK, and a Strobe and Output Enable input.
How does the code work?
Let’s start with a quick discussion how the panel works.
From a technical view, the panel actually consists of two separate 16x64 areas, top and bottom. The information which LEDs to enable and which to disable is clocked serially into the panel, row by row. Each LED has 3 color components, R, G and B, and there are corresponding inputs R1/G1/B1 and R2/G2/B2 for the top and bottom half of the display. Signals A to D serve as a 4-bit (0 to 15) row address and are effective for the top and bottom half of the display at the same time. Once a row is completely shifted into the display, it can be loaded into the LED registers via the Strobe signal.
One may wonder how it’s possible to make the LEDs darker and brighter and how to mix colors. The way to do this is to pulse-width modulate (PWM) the LEDs. For example, to make one LED appear at 50% red and 25% blue, you can first set R=1, G=0, B=1, then R=1, G=B=0, and finally R=G=B=0 twice, and repeat this pattern over and over again. Due to the high refresh rate of the display, the LED appears at 50% red and 25% green. We use PWM in the FPGA design to dim the border to about 1/64 intensity.
So, how does the code work?
The main part of the code is a small state machine which drives the control signals of the LED panel:
-- state machine for the LED panel control signals
case s_state is
-- start with everything 'low'
when IDLE =>
s_oe <= '0';
s_clk <= '0';
s_stb <= '0';
s_state <= CLK_HI; -- clock in 64 bits
when CLK_HI =>
s_clk <= '1';
Another important part is where once every millisecond, the x and y position of the main EXTA LED is calculated:
elsif s_slow_ctr = s_speed_threshold then
s_slow_ctr <= (others => '0');
-- one millisecond has elapsed, activate the next LED
if s_x = 7 then
-- right border reached
s_x <= "111000"; -- back to left border; 56 down to 7
-- one line down
if s_top = '0' then -- bottom, s_y from 6 to 15
if s_y = 6 then
s_top <= '1';
s_y <= "01001"; -- top: 9 to 0
Notice the s_speed_threshold signal which is set to a constant value based on the user-selected scan speed of the EXTA. By clicking on the pushbutton, the user can select frame rates 1fps, 2fps, 4fps, 8fps and 16fps. For example, at 1fps, the constant is 0x80e7, which corresponds to 32999 and results in a 1ms exposure time per LED at 33 MHz input clock frequency to the FPGA.
Important: If you clock the FPGA with a different frequency than 33 MHz, you need to adapt the speed threshold constants:
-- by clicking on the push button, the user can change the speed of the moving LED
s_speed_threshold <= x"080e7" when s_speed_level = 1
else x"04073" when s_speed_level = 2
else x"02039" when s_speed_level = 4
else x"0101c" when s_speed_level = 8
The code to drive the R/G/B inputs is combinatorial logic:
-- upper half of the display
s_r2 <= '1' when (s_border_pwm_ctr = "000000" and s_top_border = '1')
or (s_top = '1' and s_pos_match = '1') else '0';
s_g2 <= '1' when s_top = '1' and s_pos_match = '1' else '0';
s_b2 <= '1' when (s_border_pwm_ctr = "000000" and s_speed_indicator = '1')
or (s_top = '1' and s_pos_match = '1') else '0';-- lower half of the display
s_r1 <= '1' when (s_border_pwm_ctr = "000000" and s_bottom_border = '1')
or (s_top = '0' and s_pos_match = '1') else '0';
s_g1 <= '1' when (s_border_pwm_ctr = "000000" and s_second_indicator = '1')
or (s_top = '0' and s_pos_match = '1') else '0';
s_b1 <= '1' when s_top = '0' and s_pos_match = '1' else '0';
“Position match” means the current x/y coordinate matches the position where the white EXTA LED should appear. Notice that for the lower half of the display, we also set “red” for the bottom border and “green” for the seconds indicator (bold in the code above).
I hope the information above is useful. If you have any questions or comments, please put them in the comments section below. Also, if there are a few people who are interested in a complete board for this EXTA, or even a complete EXTA, let me know and I will check what it would cost to produce a small number.