Drive LCD TFT displays with an FPGA
FPGA’s can be very advanced to get started using, especially if you are used to microcontrollers.
But when you first get the right feeling and the proper mindset you will soon see the endless possibilities with the programmable logic.
One of the great aspects of the logic is the speed and the full control of what happens at every single clock cycle.
With this full control it doesn’t takes many lines of code to generate a very time-critical signal such as a video signal.
In this short post I will walk thru our current test setup with an FPGA, the Spartan 3E, controlling a 18-bit 7″ 800×480 TFT display.
In the image above you can the TFT display at the top and the FPGA board at the bottom.
In between of these you will see a small green connector board, which is our own 7-inch LCD Breakout board with the required switch-mode power supply for powering the LED backlight, together with the interface connector with the 18-bit color signals, Pixel Clock, Data Enable and PWM.
The schematic of the board can be found here: A5-LCD_Breakout.pdf
The FPGA board itself is the inexpensive, low-level, “Spartan 3E Sample Pack“.
Though any FPGA board with the required I/O pins available can of course be used.
The VHDL code for this project, including the Xilinx Project files, can be downloaded here: FPGA_LCD_TFT_Control.zip
I will try to do a short summary of what happens in the code and why I have decided to make it such.
VHDL Code walkthru
To control a TFT display you have to generate the following two timed signals: DCLK (Pixel clock) and DE (Data Enable).
In the datasheet of the display you will be able to find the specific timing parameters of these signals, which also explains when to display the color data of each pixel.
At page 6 of the datasheet of our display we can see the timing table. From this table we will notice the required CLK frequency of around 33.26MHz. Generating this Pixel clock source is one of the major aspects of generating the video signal.
generic map (PERIOD => 20.0, DIV => 27, MUL => 18)
port map (CLK_IN => CLK, CLK_OUT => PixelClock);
Within the code for the FPGA I have decided to use the built in DCM (Digital Clock Manager) periphiral of the FPGA’s, to generate 33.33MHz from the on board 50MHz oscillator. This is done by diving the Input frequency by 27 and then multiply this by 18.
50MHz / 27 = 1.85MHz * 18 = 33.33MHz
Now when we have this clock signal internally in the FPGA too, we can use this signal to count the clock pulses, equal to each ‘pixel’, and then we are ready to create the timing of the DE signal.
According to the datasheet we have an 800 pixels (clock cycles, Tcph, too) wide display area where the Data Enable has to be on.
For every line scan we have a total of between 1000 and 1200 ‘pixels’, whereof between 200 and 400 are outside of the screen. These ‘off-screen’ pixels are used to sync the video signal.
When we have done a single line scan there is 479 more to go within the visisble display area. Yet again we do also have some ‘off-screen’ lines, in this case between 10 and 110 lines, to do the sync.
To ease the generation of the Data Enable signal and to take care of the current scan/pixel position, I have made two counters: Hcount and Vcount.
The Hcount is used to take care of the current Horizontal pixel, going from 0 to 799 when inside the visible display area, and then continuing from 799 to about 1200 for the ‘off-screen’ pixels.
The Vcount is used to take care of the current Vertical pixel, going from 0 to 479 when inside the visible display area, and then continuing from 479 to about 590 for the ‘off-screen’ pixels.
The Data Enable pin is then controlled by this single line of code:
Where the TDEH and TDE are definitions as found in the datasheet. In the top of our VHDL code all these timing definitions has been programmed as constants:
constant TDEL: integer := 256;
constant TDEB: integer := 45;
constant TDE: integer := 480;
So now we have managed to generate both the DCLK (Pixel clock) and the required DE (Data Enable) signal with the proper timing parameters.
The Hcounter will keep counting up to (TDEH+TDEL), which is the total horizontal pixel count (including the ‘off-screen’ pixels). Afterwards it resets to 0 and increase 1 to the Vcount, moving the “cursor” one line down.
This is then repeated until the Vcounter reaches the end at (TDE+TDEB) where it resets to 0 too.
Finally it is now just a matter of setting the 18-bit digital color lines to the proper pixel color at each clock cycle and then we have a functioning display system.