A Flat Panel Display Controller
A DSP controller is used to generate video signals for a flat panel display. Also outlined is the technique for determining the function of unknown synchronization lines for the display.
In this project, a DSP processor is used to generate video signals to drive a flat-panel display. Code is adaptable to drive most raster-scan displays.
Displays come as two basic types: 'intelligent' displays with on-board memory and raster-scan displays which operate like a TV monitor. 'Intelligent' displays, like those commonly employing the Hitachi 44780 controller, feature a microprocessor interface. Characters (in ASCII format) are sent to the display which buffers the data to internal memory and scans it out to the display automatically. Once a character (or other data) is written to the display, it will be displayed until further data is sent.
Raster-scan displays, like a TV, require a number of synchronization signals to operate. The display is addressed as multiple, consecutive, horizontal lines, which scan from left to right, displaying a large number of pixels on the line as it advances. At the end of a line, the display begins to scan the next line from left to right until the entire frame (consisting of many lines) is displayed. The display is then reset so that scanning begins again from the top-left corner.
The Finlux EL Display
Scavenged from an industrial terminal, the Finlux MD512.256-EL35 electroluminescent panel was employed in this project. Examining the wiring of the display in the terminal, a ten-pin connector was found to supply data and synchronization signals to the panel. Of these ten terminals, five are clearly ground (evident on the circuit board) leaving five to be determined. An oscilloscope was used to probe these and rough notes made as seen to the left. A 61Hz signal was found first - this is the vertical sync signal which resets the trace to the top-left corner of the display. This is also the refresh rate of the display and represents the appearance of a new frame. A higher-speed 15.96KHz signal was also found. This is the horizontal sync line and corresponds to the tracing of a single line on the display. The dot clock was easy to identify as a constant 6MHz signal, and the video data was identified by shorting that line (the only line found without a constant clock signal) alternately to ground or +5V ... with the other sync signals present, shorting this line to ground makes the entire display go dark, shorting to +5V makes the entire display go bright (all pixels light). At that point the display was disconnected from the original terminal circuitry. The vertical and horizontal sync frequencies are those used by standard NTSC video - it is likely that the original terminal employed a 'standard' video generator chip simply using separated sync signals instead of combining them as is done with composite video.
I suppose it would have been easier to search for the datasheet on the display _first_. A later search revealed that the datasheet for the original display was not available however the one for a newer version (the MDL512.256-EL37) was from originally available from Planar Systems (which no longer produces these panels: these EL panels are now made by Lumineq). The pinout on the newer display is quite different (unlike the EL-35, the newer display has an integrated high-voltage inverter), and so the time spent with the oscilloscope was not wasted, but the datasheet does outline the proper use of the sync signals as well as explain the presence of two video lines: one handling data for odd columns and one for even columns. It was first thought that this was an interlaced system but it was found from the datasheets that there are two video signals which load two consecutive pixels on a line. A single dot clock advances the display two pixels to the right on a single line. The original display was configured as 512 across by 256 lines however only 256 clocks are needed to scan a single line. Wiring the two video data lines together results in a 256-by-256 pixel display with 65536 pixels in total as was done in this project to reduce the storage resources required for an image as well as speed scanning of line data. This arrangement, employing two video input signals, is quite unusual and few displays feature this scheme (including newer Finlux displays which simply require 512 clocks to scan a single line).
Assuming you are using a Finlux display like this one, the display plugs into the power supply unit via a short ribbon cable and that power supply unit (MDPU-61) has two ten-pin connectors: J1 for video signals and J2 for power as outlined below:
- J1 Pinout:
- Pin 1 - Video Data (even columns)
- Pin 3 - Dot Clock (6MHz)
- Pin 5 - Horizontal Sync (15KHz)
- Pin 7 - Vertical Sync / Frame Sync (60Hz)
- Pin 9 - Video Data (odd columns)
- Pins 2,4,6,8,10 - Ground
- J2 Pinout:
- Pins 1,2,9,10 - Ground
- Pin 3,4 - +5V supply (200mA)
- Pin 5,6,7,8 - +15V supply (800mA)
I might add that if you are utilizing a panel with an unknown pinout all is by no means lost! If possible, power-up the panel with an existing driver and identify power and signal pins. Most panels require some type of driver which may be integrated (in a newer panel) or a separate board (an older panel or with some laptops where space is a premium). Be sure to identify this board as it will also reveal power connections. Next, identify the Vertical, Horizontal, and Dot Clock signals by their frequency using a oscilloscope as described above for this display. Most displays use relatively standard frequencies similar to those used by regular video systems (e.g. computer monitors).
In order to test the display a Microchip dsPIC30F2010 DSP microcontroller was used. This 30 MIPS chip is more than fast enough to generate real-time video signals, many smaller micros lack the speed for this and will result in a slow scan rate and hence flickering of the display. A PIC DEM-28 demonstration board was employed with just five wires connecting the demo board I/O header pins to the display and a small program was written to generate sync signals and draw a diagonal line across the display. The basic algorithm to generate video is this:
- Generate a vertical Sync signal to start the frame
- Generate a horizontal Sync signal to start a line
- Present video data for a single pixel (on/off)
- Clock the dot clock line to shift the video data into the display
- Clock 256 pixels of video data into the display to complete the line
- At the end of a line, generate a horizontal Sync pulse to start the next line
- Continue to scan all 256 lines of data into the display
- At the end of the last line, delay 300 microseconds for the 'back porch' of the signal (vertical retrace)
- Generate vertical Sync and start the next frame
Timing is, of course, critical and must conform to the requirements of the display. Many displays, like this one, can be refreshed at a rate ranging from zero (static) to 65 Hz. The timing for many digital panels like this is not nearly as critical as that of the video signal for a TV however slow clock rates will result in a great deal of flicker evident on the display.
The code for the basic test program may be Downloaded Here. The wiring from the PICDEM-28 board to the panel for this test setup is as follows:
- Pin RB0 on the PICDEM board is VIDEO (connect to both video inputs)
- Pin RB1 on the PICDEM board is H Sync (approx 15 KHz)
- Pin RB2 on the PICDEM board is V Sync (approx 60 Hz)
- Pin RB3 on the PICDEM board is Dot CLock (approx 6MHz)
If you have a panel which does _not_ feature two video inputs (which affect consecutive pixels), you will likely see a display in which the triangle appears twice: on each half (left/right) of the screen. Displays like these (e.g. the Finlux "-39" series which are probably more common than my "-35" panel), require 512 dot clocks per line instead of 256. An easy fix, which will configure the display, effectively, as 256 pixels across is to generate two dot clocks for each pixel as follows at line 88 in the test code:
In order to display graphical images a converter was written which reads RAW image files and produces video data in the form of a table which is pasted into the source file for the project. RAW image files are essentially a bitmap format (BMP) with only 256 colour depth (one byte per pixel), no compression, and unlike a BMP file - no header. Many formats could be used with minor modification. If, for example, a 24-bit BMP format was desired one would need to strip the header then average the colour of the three-byte RGB value for each pixel converting it into a single-bit representing an on or off pixel at that location. Regardless, original images must match the resolution of the display - in this case 256*256 pixels.
For the first prototype, the video format chosen for storage in memory was a series of 16-bit words each representing sixteen consecutive pixels. Sixteen words are required per line, and 256 lines per frame so that a single image consists of 256*16 or 4KWords of memory (8K Bytes). The MSB (D15) of each word represents the first pixel. In this manner, consider the following image which features a single vertical line as the second pixel from the left:
As a table, it is encoded as:
.HWORD 0x4000, 0x0000, 0x0000 ...
.HWORD 0x4000, 0x0000, 0x0000 ...
The source code for the graphical file display may be Downloaded Here. Two large tables at the end of the code are images in the above format which are displayed alternately every few seconds. The original target for this code was a 30F6014 processor (above, on the dsPICDEM 1.1 board) which features much more program memory than the 30F2010 - required for the large image files (32 bytes / 16 words are required to store a single row of pixels fo 8KB are required per image). As such, the code assumes the panel is wired to RC0-RC3 of the processor.
The sign now serves as a office door sign: when my office was painted recently my name sign was removed but never replaced ... the moving EL display now served that purpose and more, displaying not only my name and email contact but also my office hours. Finally, a dedicated circuit was built to drive the panel based on a 30F4013 processor. It was built on a 44-pin card-edge board and features a six-pin RJ-12 connector for use with an ICD-2 debugger (currently used to upload graphics images, to be replaced later using an RS-232 or USB connection). Nostalia fans will recognize the 'Archer' proto board from Radio Shack (no longer an entity in Canada). The entire panel and driver is powered from a large "line lump" supply capable of +15V/1A and +5V/0.5A.
.equ __30F4013, 1 .include "p30f4013.inc" .equ VIDEO,#9 ;Video Signal .equ HSYNC,#11 ;Horiz sync 15.96 KHz .equ VSYNC,#10 ;Frame clock 61Hz .equ DCLK,#12 ;Dot Clock 6MHz
As well, use LATB instead of LATC (since port B is now used for all video lines in this prototype circuit), and set TRISB to configure RB9-RB12 as outputs instead of TRISC by changing:
You can download the source code for a rather crude graphic file converter Here. The converter takes eight-bit, two-colour RAW format files and converts them into unencoded (one bit equals one pixel) graphics files.
A major improvement was the use of compressed video formats. I chose to use run-length compression in which the MSB of each byte of encoded video represents the video level and the lower-seven bits represents the number of pixels of that level (add one first) so that a single ON pixel would become 0x80 (D7=high for ON, Count=0+1). An entire line of 256 blanks would become two bytes: 0x7F, 0x7F - for a blank line, then, the storage requirements are reduced from 32 bytes (16 words of 16-bits each) to just two bytes. In the above example image, each line would be encoded as 0x00, 0x80, 0x7F, 0x7D. the algorithm for playback of compressed images is as follows:
- Set the pointer to the first byte of graphics data
(W7 is used as the data pointer, a tblrdl.b reads the byte of data into w4)
- Generate a vertical Sync signal to start the frame
(The vertical pulse goes High->Low->High)
- Generate a horizontal Sync signal to start a line
(Set the horizontal sync signal high)
- Set the video data to the MSB for the data byte (on/off)
(The video signal is set to the D7-bit value of the data byte and the clock signal pulsed High->Low to load the pixel)
- Clock the dot clock I/O line to generate as many pixels as required (lower 7 bits +1 = count)
(DotCtr increments and w4 - the lower 7 bits of which hold the number of consecutive pixels of this value to display - is decremented for each pixel displayed)
- Get the next data byte and repeat
(Use another tblrdl.b)
- When 256 pixels are clocked out, generate a horizontal Sync pulse to start the next line
(When DotCtr==0x100, regardless of the data, end the line immediately)
- Get next data byte and continue to scan all 256 lines of data into the display
(LineCtr==0x100 when the frame is complete)
- At the end of the last line scanned, delay 300 microseconds for the 'back porch' of the signal (vertical retrace)
(Simply waste 9000 cycles at 30MIPS)
- Generate vertical Sync and start the next frame
While encoding is tedious (the algorithm looks for a change in video state or 128 consecutive pixels of the same video level) playback is fast and actually improves the rate at which images may be drawn on the display since there are few memory accesses (e.g. 128 'off' pixels, encoded as 0x7F, require only one memory access to get the data byte and 128 clocks can be rapidly generated to clock 128 consecutive pixels! Despite the fact that timing of the 'back porch' was critical, the timing of pixel clocks was not critical at all and can be rapidly clocked. The encoding method employed also terminates all counts at the end of a line (256 pixels). Further compression may be achieved by foregoing that process however this format was chosen to allow the easy implementation of multiple display panels driven from a single video generator (an original design feature unimplemented here). A simple encoder in QBASIC format can be found Here.
(2007) ... And I _finally_ managed to write a better converter in C++ which may be Downloaded Here. For input, the program accepts a monochrome (one colour depth) BMP file of size 256*256 named "panel.bmp" and outputs a file called "code.txt" which is suitable for cutting and pasting into the dsPIC source file directly (in the data segment). I really _must_ add a windows front-end!
As well as the display of graphical images, the display may also be used to display text. A basic text display was written which displays 32 characters across by 16 lines. That format was chosen to utilize eight-by-eight pixel characters which were extracted from a circa 1980 Ohio Scientific model 600 computer with a CG-4 character generator ROM. The ROM was read using an EPROM programmer and the bit pattern for the characters was converted from an Intel HEX file to ".BYTE" defines for a table in dsPIC program memory. One unique feature of the Ohio Scientific ROM was support for a host of characters including gaming characters. For example, while character 65 (decimal) was still "A", character 0 (as seen to the left) was a diagram of a race-car used for various games! My flat panel display can hence display gaming characters as well as basic line graphics.
The character generator stores each char pattern as eight bytes. Each byte represents the state of eight consecutive pixels (one bit per pixel) and eight bytes represent eight rows. For example, the pattern for the character "A" (65 dec) is encoded in the chargen ROM as ".BYTE 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00". The pattern becomes obvious when the hexadecimal values are converted into individual 0's and 1's:
00001000 ;0x08 00010100 ;0x14 00100010 ;0x22 00100010 ;0x22 00111110 ;0x3E 00100010 ;0x22 00100010 ;0x22 00000000 ;0x00
Scanning of text to the display is accomplished as follows:
- The char is retrieved from the buffer (either RAM or ROM)
(If the message is in flash, a tblrdl.b is used)
- The char is multiplied by eight to obtain the byte at which the char pattern starts (each char is eight bytes apart in memory)
- Add this to the base address of the chargen table to get the base address of the character pattern
- Now, add a cyclical number (0-7) called 'Raster' which defines the actual scan line (0 for the top line - 0x08 in the example 'A' - 7 for the bottom line - 0x00 in the example 'A')
(The address is now that of the eight pixels for that specific line)
- The buffer is incremented to the next char in the line and the next eight pixels output
- At the end of a line, Raster is incremented and the same text is scanned again, this time the second line
(if a CR is encountered, blank pixels are scanned to the end of the line)
- The process continues until all eight raster lines for the same text are scanned to the display, the line is then complete
- The buffer is incremented to the start of the next line and the scanning process continues until the display is full (16 lines)
Some code is designed to accelerate the display, for example, a space (char 32) is rapidly scanned as eight consecutive dots. When a return (char 13) is encountered, simply scan blank dots rapidly to the end of the line.
Implementing a scrolling display can be done by simply moving the pointer to the beginning of the buffer ahead at periodic intervals however this will result in a 'jerky' display where the entire display shifts up one line at a time. For smooth scrolling, the display is moved up one raster at a time so that the top line (and bottom line) will display an incomplete line (i.e. only some of the eight rows which make-up a full line). Several variables are required to keep track of the current top line (ScrollLine) and the current raster line (Scroll Scan). ScrollScan cycles from 0-7: when zero the display begins with the top row of pixels for a complete line, when 7, only the last of the eight rows is scanned.
The primary motivation behind the design was the allowance to implement integrated graphics and text into one seamless display. If a graphic image is to be displayed within text, it is simply a matter of scanning the graphics lines after the text (and vice-versa), keeping track of the number of lines scanned to ensure it stays at 256 (one continuous frame).
A second Prototype Panel Driver was built featuring two processors to drive two flat panel displays on my office door - the bottom panel (#1) displaying graphics like the above display and the top panel (#2) displaying text which is updated frequently from my office PC. The prototype circuit has a single RJ-12 ICD connector with a set of three SPDT DIP switches allowing use of the single debugger with either processor for simplified software debugging.
Uploaded messages from the PC via RS-232 serial are connected to a MAX232 driver chip and passed to UART #1 on processor #2. Although messages will be frequently uploaded to the text panel, occasionally the graphics on panel #1 may be updated as well. For this purpose, a serial link exists between the second UART panel #2 and the graphical display panel - code on the text panel is designed to pass messages through on request.
The Prototype Code for panel #2 (text display with serial upload) may be downloaded. This is not a debugged version! Updated code will be put on this page when available.
- BASIC TEST CODE in ASM30 format. This demonstration uses the PICDEM-28 board with a 30F2010 chip.
- GRAPHICAL DISPLAY TEST CODE in ASM30 format. This program displays a graphics file stores in the large tables at the end of the program.
- RAW GRAPHICS CONVERTER in QBASIC format. The converter takes eight-bit, two-colour RAW format files and converts them into unencoded (one bit equals one pixel) graphics files for use with the display.
- BASIC ENCODER UTILITY in QBASIC format. Encodes graphics files into run-length limited format.
- CONVERTER.CPP in C++. Compile using Microsoft Visual C++ 6.0 as a Win-32 console application. This utility converts BMP files into encoded data (as PIC code).
- CONVERTER.EXE. The above program, compiled.
- TEXT DISPLAY CODE in ASM30 format. This code allows messages to be uploaded via the serial port and displayed as text characters.
- TEXT DISPLAY CODE 2 in ASM30 format. This code allows messages in flash program memory to be displayed. This code is much simpler than the above since it lacks serial upload capability although future code will include a loader to place uploaded messages directly into flash memory.