Mini Linux LCD Monitor

LCD monitor mounted in a Rose-Bopla BG220 Handheld Enclosure

The first iterations of the LCD display were "smart" displays - they had intelligence to draw text, lines, etc. This meant to use the display, applications had to be specifically written to produce output that could be rendered by the display.

This became annoying having to write a yet another GUI interface for LinuxCNC, so I started looking into making the LCD display act as a mini LCD monitor that X-Windows could use.

This turned out to be simpler than I expected. I had previously purchased a FingerVu 436; a 4.3" USB LCD monitor with 480x272 resolution. This didn't work properly on linux, so I needed to modify the driver to make it work correctly (see here). After seeing how simple the driver was, it was clear I could use this for my device driver.

The linux driver uses a virtual Frame Buffer - that is, the driver duplicates the memory of the display in computer memory (400x240x16bits) and sends block updates every now and then when the buffer changes. Because my LCD is small (400x240), the worst case update is 400x240x2 = 230400 bytes for a full screen update. At the 480Mbps theoretical maximum for USB High Speed, I should be able to achieve a worst case refresh rate of 200Hz, way more than I need.

So why not just use the FingerVu 436? Firstly it is bigger than what will fit in my pendant - 3.2" is the max. Secondly, it draws a lot of current - the FingerVu 436 comes with a special dual lead USB cable (it must connect to 2 USB ports) implying it uses up to 1 amp of current.

Speed Improvements

I was impressed with the speed of the AVR32 chips, so I looked through the Atmel catalog and found the AT32UC3A3 family. In particular the AT32UC3A3256S. These chips have 2 killer features: EBI and USB HS.

External Bus Interface (EBI)

The AT32UC3A3 series of microcontrollers have an external bus interface - they have built in hardware to communicate with external memories. Static memories have a simple interface - Address, Data, WR, RW - which coincidentally is the same interface as the graphic LCD. This means we can use the EBI interface to drive the LCD.

Why is this good? Performance and simplifying the code are the main reasons.

When the interface is set up correctly, it is possible to write to the LCD in a single instruction...

#define LCD_COMMAND_ADDRESS     (AVR32_EBI_CS0_ADDRESS + (1<<22))
#define LCD_DATA_ADDRESS        (AVR32_EBI_CS0_ADDRESS + (1<<21))

#define LCD_COMMAND             ((volatile uint16_t *)LCD_COMMAND_ADDRESS)
#define LCD_DATA                ((volatile uint16_t *)LCD_DATA_ADDRESS)

*LCD_COMMAND = cmd;
*LCD_DATA = data;

This example sets the command, then writes a data word. These are 16bit transfers. Using bit-banging methods, you need to...

(some of these steps may be optimised out for continuous writing) To read from the LCD, all we need to do is...
data = *LCD_DATA;
whereas using bit-bang methods, we would need to change the direction of the data bus first.

The LCD is wired up to the microcontroller as follows...

LCDMicrocontroller
~CSGND
~RDEBI_NRD
~WREBI_NWE0
RSEBI_ADDR21
DATA0-15EBI_D0-EBI_D15

Note that the LCD RS line, used for selecting between command and data, is wired to the EBI address line. This means the data and command registers are effectively memory mapped to two different addresses in the microcontroller's address space.

The configuration of the EBI isn't quite as simple as just wiring up the correct pins. The timing of when data is placed on the bus, when the WR/RD pins are strobed, and when new data can be placed on the bus, must be set correctly. My initial attempt, based on the timing data in the LCD data sheet was too fast, so I had to slow it down a bit. Regardless, it was much faster than bit-banging.

High Speed USB

The High Speed USB is capable of transferring 480Mbps, whereas Full Speed USB peaks at 12Mbps. This makes a huge difference when transferring screens full of information at time.

At 480Mbps, or approximately 48M bytes/s, that is huge volume of data for a 60MHz microcontroller to handle. The USB peripheral normally operates by reading the USB data into a memory buffer, then alerting the microcontroller application that there is data present. The application can then process the data. Even with double buffering, this causes a stall in the USB data flow.

Luckily, the AT32UC3A3 microcontroller series USB peripherals support DMA (direct memory access) transfers. With a DMA transfer, you tell the microcontroller where you want to place the data. This can be any memory address you want. And given that we are using EBI to access the LCD, we can transfer the USB data directly to the LCD at the full transfer speed.

Protocol

At the moment, everything about the device and communications is hard coded. The linux driver expects the device to be a 400x240px LCD with 16bit pixels (R5G6B5) using a R61509V display driver.

There are 3 commands...

CMD_BOOTLOADER Execute the boot loader. Atmel DFU can then be used to update the firmware.
CMD_SET_BACKLIGHT Set the device backlight intensity.
CMD_BITBLT

Copy a block of data to the LCD. This command is split across 2 or more USB packets.

The first packet contains the rectangle of the display data that is going to be copied...

uint8_t cmd;
uint16_t x;
uint8_t y;
uint16_t width;
uint8_t height;
This packet is received using the microcontroller's standard double buffered transfer method. After it has been processed, the LCD is set up to receive the data, and the USB DMA transfer is set to move the data directly from USB to LCD. There is a potential race condition here, but as yet it hasn't been a problem.

Schematic

The schematic is shown below...

The schematic is pretty simple. The important parts are...

In addition, there are optional bits...

Code

Below are links to the firmware and the linux device driver.

The firmware is really crappy alpha/prototype software. It originally started out as a smart LCD with USB Serial interface. All that code is still there, but just not called. There is also code there to implement a bit-banged interface. The core code to implement the BITBLT command is in udi_cdc.c:udi_cdc_data_recevied(). This is the interrupt callback when data has been received by the USB CDC (serial) interface - that is wrong in so many ways.

The firmware is here . It is built with Atmel Studio 6.

The device driver firmware is a hacked version of udlfb. It has only been run and tested on Ubuntu 10.04.01 LTS, kernel 2.6.32-122-rtai.

The driver is here . Run make, then make install, probably as root (using sudo). I'm not an expert at linux device drivers, so if it doesn't build, google for help.

Action Shot

Here's a youtube video showing a couple of apps running, including LinuxCNC. Unfortunately the LinuxCNC Axis application has a title bar, menu and tool bar, so after displaying that, there is not much room for anything else. The video shows an early version of the firmware not painting properly when dragging the window. (EBI running too fast)

Future Enhancements