The code: LCD-Driver-0.1.zip
I recently built a couple of XuLA FPGA prototyping boards for me and some friends. It's a nice little FPGA board which can be bought from XESS for either 40$ or 70$. The price difference between the two board being a higher end FPGA (8$ more expensive according to DigiKey) on the second board. With some soldering skills, it can also be built for much less than that. The SDRAM IC can be recycled from old ram sticks, which helps bring down the price. The FPGA on the lower-end XuLA board is _the_ cheapest FPGA available from DigiKey as of writing this.
As a first project for this new toy, I decided to make an LCD controller. This seemed like an excellent first project because it could enable people at my local hackerspace to make use of some of our most expensive - and most useless, until now - electronic junk: LCD screens. Like many other hackerspaces probably do, we have an ever-growing stack of old laptops in various stages of disrepair. With the bare LCD panels in some of them still going for over 150$, it's a real shame not to use them in our DIY projects.
I was struck by the lack of information about how to make this work, yet how simple the final solution was. I will try to address some of the pitfalls which made this project non-trivial for me, and lower the barrier for people who would like to do the same.
The LCD backlight is controlled by a small inverter board which takes DC current and converts it to a high AC current to light the fluorescent tube attached to the LCD panel. There are two things which make the backlight difficult to use. First, the pinout, which we need in order to use the backlight at all, is almost impossible to find online. Second, it usually runs on 12v, which means an external power supply is required since the XuLA runs on 3.3v.
The pinout is not easy to figure out. If the original laptop is still attached to the LCD, one can measure the voltage on each pin on the backlight controller board and simply replicate that. That was not an option for the LCD I chose, and I had no luck finding a datasheet for any of the inverters I have. Here's how to figure out the pinout of the inverter by trial and error. First, find the ground and +12v pins. Both will usually be attached to the biggest traces, and the +12v will be connected to a SMD fuse, typically marked by a 1 or 2. The fuse may also be noted F1 on the PCB. It's important to note at this point that if the current is applied backwards, this fuse will blow. Test the continuity to figure out if it's blown. The fuse in this picture blew so I replaced it with a blob of solder. After connecting the two appropriate pins to ground and 12 volts, the LCD may stay dark, this is normal. Usually, one of the other pins on the inverter board will be the "enable" pin, which will typically only need between 2.5 to 5v. Try shorting each of the remaining pin to 2.5v, if the LCD doesnt light up, try with 5v, and then 12v. In this picture, the enable pin worked only after applying 12v to it, so I bridged it together with the 12v pin. One of the other pin may be used to control the brightness of the LCD with a pwm pulse. On the specific board I have, one of those pins also controlled the annoying blue light at the bottom of the screen.
Your success at not blowing the backlight controller and figuring out its pinout may vary. I blew a good half dozen before realizing what I was doing wrong, so be careful.
The model of LCD panel I used is a B140EW01. To figure this out, there is usually a sticker on the back of the LCD. The datasheet for it was particularly easy to find, your mileage may vary. A first look at the datasheet tells us the LCD panel can be controlled with only 8 signal lines, which are 4 pairs of LVDS lines, in addition to Vcc and Ground. LVDS stands for low voltage differential signaling. It's not important to understand how it works, just that the Spartan-3A FPGA on the XuLA board can interface with those signals easily. In fact, this FPGA can control 8 pairs of LVDS signals at once, more than we need. I left out the EDID-related pins for now. With these pins, it would be possible to identify the display and its caracteristics automatically, but since this was my first FPGA project, I decided to keep the scope small and leave EDID out.
On this picture and the one below, you can see the test points on the back of the LCD, which I soldered wires to. I used an old 80-pin IDE cable to solder onto the test points. There are four LVDS signals involved here: CK1IN, RXIN0, RXIN1 and RXIN2, which are respectively the clock signal and the data line 0, 1 and 2. Each signal is made of a pair of lines: a positive and a negative line. On the PCB (see picture), each pair of lines are noted CK1INP, CK1INN and so on. One thing to remember is that on the FPGA side there are also positive and negative pins, and the pins are also paired together, you cannot use any two FPGA pins at random. Pairs of FPGA pins must match pairs of LVDS lines on the LCD panel. The Spartan-3A FPGA also only support LVDS signals on bank 0 and 2. Here's the connections I used in this project:
|LCD||FPGA Pin||XuLA Channel|
The LCD power lines are also connected to the 3.3v on the XuLA.
One of the hardest part of this project was figuring out the correct timing for sending the data to the display. There is almost no information in the datasheet for this particular display, but fortunately it is possible to figure it out by looking at other LCD datasheets. Some of them are more verbose. The datasheet for this display also refers to the SN75LVDS86 datasheet which is the LVDS controller in use. This datasheet has some great information on the timing of the four LVDS signals, but does very little to explain how the data is interpreted, since this is left to the "upper" protocol layer which does not matter to the SN75LVDS86.
A single pixel is sent to the display within one pulse of the clock. The pulse is divided in 7 time slots, during each slot a single bit of information is read from each data line. The clock line must be high during 4 time slots and low for the 3 remaining slots. A new pixel starts during the third time slot after the clock goes high. The diagram on the left shows the timing of a single pixel.
There are 7 time slots and 3 lines, that's 21 bits of data per pixel. This data is made of three bits for synchronization and 6 bits per color (red, green, blue). The synchronization bits are Data Enable (DE), the Horizontal Sync (HSync) and Vertical Sync (VSync). Not all pixels are valid, some of them are transmitted but are never be displayed. This is far from obvious from reading the datasheet, but just like VGA, WXGA has a syncronization sequences, which consists of a certain number of invalid pixels which must be sent after valid pixels. This is called a blank time in various specs. This is how the HSync and VSync are used; they mark valid pixels within rows and columns. The HSync bit must be set when a valid line of pixel is transmitted, and unset when the horizontal synchronization pixels are transmitted. The VSync bit must be set when any line of pixel which contain valid data is being transmitted. The Data Enable bit must be set when any valid pixel is transmitted, in other words whenever both VSync and HSync are set. The timing diagram on the left shows how this is supposed to work. The mapping between the 21 bits and their respective timeslot and LVDS line can be gathered from the datasheet of various other LCD panels. This third diagram shows the mapping for this display.
More on the timing for this particular LCD can be found in its datasheet, on page 14 under Interface Timings. It says the "X blank time" is typically 160 pixels, and the active area is 1280 pixels for a total of 1440 pixels per lines. The datasheet does not mention the horizontal blanking, but it says the active area on the Y axis is 768 lines long and the total area is 823 lines. We can deduce that the vertical blanking area must be 55 lines.
The datasheet mentions a frame rate of 60 Hz. In order to achieve this rate, we must send individual pixels at a rate of ~72 Mhz (60 Hz x 1440 x 823 = 71.11 Mhz). Knowing that we need to send 7 bits of data in series for every pixel, we would need the FPGA to run at a clock frequency of 504 Mhz. Unfortunately the maximum clock frequency we can get out of the XuLA is 384 Mhz (using a 32 times multiplier on the 12 Mhz clock). In the demo code below, I'm running the FPGA at 252 Mhz which will result in a frame rate of 30 Hz, half the typical value. With the LCD screen i'm using, this works fine, except for a small flicker. Your mileage may vary.
The VHDL code is fairly simple to write, once you get used to the syntax. I won't explain all of it, I'll just leave you with the code to try and this picture of the code running on my test LCD.
The code: LCD-Driver-0.1.zip