/pub/Develop/Hardware/Projekte/LM641541 - LCD Display 640x480/

k1.spdns.de / Develop / Hardware / Projekte / LM641541 - LCD Display 640x480 /

LM641541 LCD Display Controller Board

Last reviewed: 2012-06-25

Project state

Hardware finished. Software testing in progress.
2012-03-11 PCB ordered.
2012-03-26 PCB arrived.
2012-05-21 PCB soldered.
2012-05-26 PCB fixed error.
2012-06-04 Case finished.
2012-06-18 Software testing in progress.


This is my 4th AVR project: a controller board based on an AVR ATMega8 for a LM641541 b&w dual-scan LC display.
It uses two 41464 DRAM chips for a 64k frame buffer. As you can see from the circuit drawing (Eagle source) it does not connect the DRAMs as external RAM to the microcontroller (which is not possible with an ATMega8, it would need an ATMega16 or higher and would require SRAM). Instead the RAM is connected purely over i/o port pins. Two 74HC163 4-bit counters are used to generate the column address when sending the frame buffer to the display.


Technical Specs
Display                 LM641541: 640x480 pixel, b&w (grey shades possible)
Board                   professional made double layer 1/3 Euro board (100x52mm)
Power supply options 5V ~200mA: USB plug, serial plug, ISP plug oder 2-pin header
Data input 5V serial link with HW handshake, 6-pin Mini-DIN plug
LCD voltage µC controlled inverter
Controller ATMega8, 16 MHz, ISP programmable
Frame buffer 2x 41464 4 bit DRAMs
Frame address counter 2x 74HC163

LCD Voltage

The LCD voltage of approx. -20V is generated by an inverter which is controlled by the microcontroller.

The inverter is driven by a PWM signal with 120kHz. One A/D converter is used to measure the actual voltage and the PWM on/off ratio is regulated to meet the desired value. This value is adjustable by the user by control codes to find the best contrast for the display. The A/D converter is free-running and performs approx. 9500 measurements and therefore interrupts per second.

Display Refresh

The display is refreshed on an interrupt with approx. 61 Hz. Refreshing the display takes more than 10,000,000 clock cycles per second, so interrupts are re-enabled in the frame refresh interrupt routine and only temeporarily blocked for one scan line.

The frame buffer is viewed as 256 rows x 256 bytes/row. The display is a "dual scan" display, which means, it has an upper display half and a lower display half which are refreshed simultaneously. 8 bits are written per pixel clock, 4 for the upper and 4 for the lower half. Each 4-bit nibble comes from one 41464 DRAM. Due to the straight-forward 256x256 design, HW scrolling in all 4 directions can be implemented.

Sending one row works as follows:

Write row address into DRAMs:
• write first address nibble into first 74HC163 counter
• write 2nd address nibble into first counter and shift nibble from 1st counter to 2nd counter. The two counters are wired as a 4 bit wide shift register.
• Activate /RAS for the DRAMs.

Write column address into DRAMs:
• write first address nibble into first 74HC163 counter
• write 2nd address nibble into first counter and shift nibble from 1st counter to 2nd counter.
• Activate /CAS for the DRAMs.

Send 160 bytes:

Then for all 160 bytes which make one scan row, one byte is sent to the display in 4 CPU clock cycles.
Each byte is one nibble from one DRAM for the upper display half and one nibble from the other DRAM for the lower display half:

CpuClk      1...2...3...4...1...
____ ____
AddrClk \_______/
Addr valid ============> <=
CAS / \___________/
Data valid <========>
PixelClk ____/ \____

AddrClk is the clock to the HC163 address counters,
CAS is for the DRAMs (obviously) and
PixelClk is the CP2 to the LCD display.
There are a lot of timing requirements to meet.


The circuit uses the DRAMs outside two parameters:
maximum refresh delay and maximum RAS cycle time:

(1) The DRAM refresh is done exclusively by the screen refresh at 61Hz. The specs say 4ms (250Hz), but this is really worst case. Most DRAMs keep their contents for a second and more. This eliminates expensive extra cycles for additional refreshes every 4ms. This would have been very hard to implement, because you must write all rows to the LCD with the same timing, or brightness flicker will occur.

(2) The whole display row is transferred in one RAS page cycle, which takes approx. 160*4/16MHz = 40µs. The specs say that a RAS cycle may be up to 10000ns long, but this is a rough number as well. Otherwise i had to spend lots of extra cycles to split one row into chunks of 10µs.

Both assumptions are safely inside the limits of actual DRAM chips.


There's an In-System-Programming (ISP) header on the board, so the microcontroller can be programmed on-board. This is useful if it is soldered-in or if you have no programmer which connects an external quarz oscillator to the controller during programming. Once the ATmega8 is programmed and the fuse bits are set, it requires an external clock source, even for (re-)programming.

The board can be powered from the ISP connector.

Serial Connection

The terminal is connected over a 5 Volt serial line to it's data source. The serial link has and requires hardware flow control with RTS/CTS. Data flow will be somewhat in chunks, because the screen refresh regularly eats all process time.

For the socket i use a Mini-DIN socket with 6 pins. This is a private standard:

1 RxD
2 TxD
5 +5V
6 RxD handshake
8 TxD handshake

Cables always cross the data and handshake lines.

The board can be powered from the serial connection.

Timers, Ports and Interrupts

8-Bit Timer/Counter0:   Used for Display Refresh Interrupt
16-Bit Timer/Counter1: Used for LCD Vee PWM
  Port B Pin 1 "OC1A"
INT1    Used for UART TxD Handshake
  Port D Pin 3 "INT1"
ADC5    Used for regulating the LCD Vee PWM
Port C Pin 5 "ADC5"
Watchdog timer:

Enabled for LCD Vee safety. Reset by the ADC interrupt.

Brown-out detection:

For LCD Vee safety, brown-out detection should be enabled.

Port B

Used for 'slow' control signals.
Signals are typically set and reset with macros set() and clr()

Bit 0   (1)  DRAM RAS
Bit 1 PWM for LCD Vee LCD voltage pump PWM output (OC1A)
Bit 2 (1) DRAM WE
Bit 3 HC163 SLD Address Counter 74HC163 pin "SLD": count/load
Bit 4 (1) DRAM OE
Bit 5 (0) LCD FrameStart LCD control line "S": active high

Normally following bits are always in the 'removed' state: RAS=1, OE=1, WE=1 and FrameStart=0.
Functions which activate those lines must always reset them afterwards, e.g. by calling macro RESET_SLOWCONTROLS.
The state of HC163_SLD is undetermined and must always be set before use.

Port C

Used for data bus.

Bit 0-3      Data Bus
Bit 4 (0) LCD row clock LCD control line "CP1": falling edge: latch row data
Bit 5 (0) ADC for LCD Vee LCD voltage sensor (input)

Normally LCD_RowClock=0. It is only set inside the frame refresh routine.
Bit 5 (Vee ADC) should be 0 too. (no pullup)
Direction of data bus and state of data lines is undetermined and must always be set before use.

Port D

Used for 'fast' control signals.
Signals are typically set and reset by loading precalculated values into the port.

Bit 0        RxD               Serial data input
Bit 1 TxD Serial data output
Bit 2 RxD Handshake RxD control (output)
Bit 3 TxD Handshake TxD control (input)
Bit 4 (0) LCD PixelClock LCD control line "CP2"
Bit 5 (1) DRAM CAS upper half of screen
Bit 6 (1) HC163 Clock Address/Counter 74HC163: clock
Bit 7 (1) DRAM CAS lower half of screen

Normally following bits are always in the 'removed' state: LCD_PicelClock=0, both CAS=1, HC163_Clk=1. Functions which activate those lines must always reset them afterwards.

Terminal Emulation

The terminal emulation follows no standard for the control codes, but uses the 8-bit ISO LATIN-1 character set for text. It knows roughly 160 graphics characters and you can define 32 custom user defined graphics characters which are stored in EEprom. It supports the standard character attributes, though ITALICS may look ugly :-), and double width and double height printing. The frame buffer is pixel-based and you can print custom graphics characters, so you can display any image on the display. The larger frame buffer is used to enable horizontal scrolling and for printing one line ahead before scrolling it into screen.

Character set

Normal characters

These are stored in program ROM in 192*12 = 2304 valuable bytes.

0x20 .. 0x7F    ASCII
0x80 .. 0x9F UDG (user def'd graphics) from EEprom
0xA0 .. 0xFF LATIN-1
Graphics characters

When the graphics attribute is set, then the terminal calculates the following graphics characters:

0x20 .. 0x2F    4/4 Block Graphics, black&white
0x30 .. 0x3F: 4/4 Block Graphics, grey/white
0x40 .. 0x4F: 4/4 Block Graphics, black/grey
0x50 .. 0x57: 1/8 .. 8/8 Bargraph from left (see unicode U+2580)
0x58 .. 0x5F: 1/8 .. 8/8 Bargraph from right (see unicode U+2580)
0x60 .. 0x6B: 1/12 .. 12/12 Bargraph from bottom (see unicode U+2580)
0x6C .. 0x77: 1/12 .. 12/12 Bargraph from top (see unicode U+2580)
0x78 .. 0xAF: (undefined)
0xB0 .. 0xFF: Line Graphics (see unicode U+2500)

The line graphics are calculated with the following formula:

0xAF + A*27 + B*9 + C*3 * D

where A/B/C/D = left/top/right/bottom line stub
with 0/1/2 = no/thin/thick line
note: code point 0xAF (no line at all) does not exist

Control Codes

All control codes are in range 0x00 to 0x1F. Arguments are not encoded but simply follow as binary bytes.

0x00 RESET

Reset all settings, initialize hardware, clear screen and home cursor.

0x01 CLS

clear screen, home cursor, reset print attributes.

0x02 MOVE_TO_POSITION, <byte:row>, <byte:col>

set cursor to row <row> and column <col>.
<row> and <col> are binary.
top row is 0, leftmost column is 0.
High values are considered to be negative.
May move outside the screen:
• SHOW_CURSOR or printing a character will scroll screen.

0x03 MOVE_TO_COL <byte:col>

set cursor to column <col>.
<col> is binary.
leftmost column is 0.
High values are considered to be negative.
May move outside the screen:
• SHOW_CURSOR or printing a character will scroll screen.


terminal responds with:

0x04, <byte:row>, <byte:col>    

<row> and <col> are binary,
top row is 0, leftmost column is 0.


Save current cursor position and attributes.


Restore cursor position and attributes from saved values.


Display the cursor blob.

The cursor blob is removed whenever a character or control code except 0x07 (SHOW_CURSOR) is printed.

Displaying the cursor blob forces the cursor position into the screen like before printing a character.
Additionally, if the logical screen size is larger than the physical screen size (40x80) then the visible window is shifted horizontally to include the cursor position. If the cursor is above or below the visible screen (there is at most one line above and below the screen) then the visible window is not shifted vertically to make the cursor position visible. I see no real need for this and the hardware would make it complicated.


Move cursor left one position.
Wraps at left border to previous line.
May move outside the screen:
• SHOW_CURSOR or printing a character will scroll screen.
Can be used after 0x12 REPEAT_NEXT_CHAR.

0x09 TAB

Move cursor right to next multiple-of-eight position.
Wraps at right border to next line.
May move outside the screen:
• SHOW_CURSOR or printing a character will scroll screen.
Can be used after 0x12 REPEAT_NEXT_CHAR.


Move cursor down one line.
May move outside the screen:
• SHOW_CURSOR or printing a character will scroll screen.
Can be used after 0x12 REPEAT_NEXT_CHAR.


Move cursor up one line.
May move outside the screen:
• SHOW_CURSOR or printing a character will scroll screen.
Can be used after 0x12 REPEAT_NEXT_CHAR.


Move cursor right one position.
Wraps at right border to next line.
May move outside the screen:
• SHOW_CURSOR or printing a character will scroll screen.
Can be used after 0x12 REPEAT_NEXT_CHAR.


Move cursor to start of current line.


Clears line from under the cursor to the end of line.
Uses current setting for background color.
Does not move the cursor.


Receives a bitmap of 12x8 pixels and prints it like a character.
• first byte is top row
• left bit is left pixel
Can be used after 0x12 REPEAT_NEXT_CHAR.

0x10 SET_ATTRIBUTES, <byte:attributes>

Sets the print attributes to <attributes>.
Attributes correspond to bits in <attributes>:

bit 0 = BOLD
bit 2 = INVERTED
bit 3 = ITALIC

OVERPRINT means OR-ing the bits from the character matrix with the bits already set in the screen.

DOUBLE_WIDTH and DOUBLE_HEIGHT are only supported for:
• Print character

All other control codes, e.g. MOVE_TO_POSITION, ignore these settings!

0x11 XON

no effect

0x12 REPEAT_NEXT_CHAR, <byte:count>

Repeat next character or control code <count> times.
Also works with these control codes:

0x13 XOFF

no effect

0x14 SET_BACKGROUND_COLOR, <byte:color>

Sets the background color to white (sort of), if <color> is 0, else black.
Removed due to code size overflow.

0x15 SCROLL_SCREEN, <byte:dir>

Scrolls the screen left, right, up or down depending on <dir>.
<dir> may be one of: 'l', 'r', 'u' or 'd'.
Uses hardware scrolling.
Can be used after 0x12 REPEAT_NEXT_CHAR.

0x16 CLEAR_WINDOW, <byte:rows>, <byte:cols>

Clears a rectangle of <rows> x <cols> with the current attribute setting for inverse.
Top/left corner is at the current cursor position.
The cursor position does not move.

0x17 COPY_WINDOW, <byte:dest_row>, <byte:dest_col>, <byte:rows>, <byte:cols>

Copy rectangular screen area from current cursor position to the destination position.
No error checking due to program flash size limits.
Might be removed if program space exceeds 8k limit. :-/

0x18 ECHO_CHAR, <byte:char>

Sends the received character <char> back to the host.
May be used for testing or synchronizing the received character stream.
Can be used after 0x12 REPEAT_NEXT_CHAR.

0x19 MOVE_WINDOW, <byte:col>

Shifts the visible screen window horizontally inside a wider logical screen to column <col>.
The vertical window position cannot be moved.

0x1A SET_LOGICAL_SCREEN_SIZE, <byte:rows>, <byte:cols>

Set the logical screen size to <rows> x <cols>.
• the value for <rows> is limited to 40 or 42.
• the value for <cols> must be between 80 and 128.
If rows=42, then the visible screen window shows rows 1 to 40 (out of rows 0 to 41).
note: the visible screen window cannot be shifted vertically. The invisible rows may be used in games or text editors to print a row of text before scrolling it into the visible area.
If cols>80 then the visible screen can be shifted horizontally, either explicitely by 0x15 SCROLL_SCREEN or implicitely when showing the cursor blob with 0x07 SHOW_CURSOR.

0x1B ESC <byte:control>

depending on <control> set permanent settings in EEPROM:

0 write Vee_lcd and sio_baudrate to EEPROM
1 increase LCD contrast voltage (lighter)
2 decrease LCD contrast voltage (darker)
4 set UDG: <byte:char>, <byte[12]> must follow
   <char> must be in range 0x80 .. 0x9F
6…17: set sio baudrate to 150 << (n/2) * (2+n&1)
applied after write_to_eeprom + reset
6 150<<3 * 2 = 2400
7 150<<3 * 3 = 3600
8 150<<4 * 2 = 4800
9 150<<4 * 3 = 7200
10 150<<5 * 2 = 9600 (default)
11 150<<5 * 3 = 14k4
12 150<<6 * 2 = 19k2
13 150<<6 * 3 = 28k8
14 150<<7 * 2 = 38k4
15 150<<7 * 3 = 57k6 (error=2.13%)
16 150<<8 * 2 = 76k8
17 150<<8 * 3 = 115k2 (error=2.13%)

LCD voltage and SIO baudrate are only written to EEprom if the escape code 0x00 follows.
The LCD voltage setting is applied immediately (but not neccessarily stored in EEprom).
For the baudrate setting take effect the terminal must be reset, e.g. by sending the 0x00 RESET control code.
UDG characters are written immediately.

0x1C GET_BMP_OF_CHAR_MATRIX, <byte:char>

the terminal responds with

0x1C, <byte[12]>

sending the character matrix of character <char> back to the host.

This may be used to create user-defined characters which match the terminal's characters.
This control code can be used to retrieve the built-in ASCII and LATIN-1 characters, the user defined UDG graphics in EEprom and the built-in graphics characters, if the terminal is currently in graphics characters mode. Also, the attributes BOLD, UNDERLINE, INVERTED and ITALIC are applied.


the terminal responds with

0x1D, <byte[12]>

sending the matrix of the character cell in screen at the current cursor position back to the host.

The bytes include all attributes, including the overall background color set with 0x14 SET_BACKGROUND_COLOR.
The cursor blob is removed before the operation.
This operation advances the cursor like printing a character.


the terminal responds with this string:

0x1E, "MyLCD,LM641541,ssz=40*80,fsz=42*128,csz=12*8,gs=2\n"

Line one identifies the terminal emulation and physical device,
line two gives some basic information about geometry and colors:

ssz = physical screen size [characters]
fsz = frame buffer size & max. possible virtual screen size [characters]
csz = character size [pixels]
gs = grey scales

Known Bugs and Todo

Need more testing:


Name Letzte Änderung Länge 
Data Sheets/ 2012-06-04 19:03
Eagle/ 2014-08-23 20:19
Images/ 2012-06-05 15:10 32 
Software/ 2012-02-08 15:38

powered by vipsi - your friendly VIP Script Interpreter

Valid HTML Valid CSS