# Introduction

The Mizar32 LCD Display is an add-on hardware module that plugs into all six of the Mizar32 main board's bus connectors. It has a 16-column by 2-line Liquid Crystal Display, where each position can display one of a range of characters, and it has six push buttons: left, right, up, down, select and reset.
The red reset button resets the on-board PIC microcontroller that controls the display, while the five buttons can be read by the main processor over the I2C bus.

# Hardware view

The LCD display board is run by a PIC 16F84 microcontroller, which is a low-power processor running a program written by Simplemachines that talks to the Ampire 162B LCD panel, detects button presses and communicates with the main AVR32 processor over the I2C bus.
The LCD panel itself hosts another microprocessor, the SED 1278, which receives commands from the PIC, generates characters for the LCD panel and keeps the display refreshed. It has an internal character memory of 40 characters by two lines, of which 16x2 are shown at any one time on the LCD screen. The SED 1278's program is in ROM and cannot be changed.

## I2C bus speed

When talking with the LCD display over I2C, the Mizar32's I2C bus must be set to a speed no faster than 50kHz. If the Mizar32 talks with other I2C devices at speeds up to 100kHz, this will not confuse the LCD display: it is able to recognise its own address (and reject all other addresses) at up to 100kHz but can only process data packets at a maximum of 50kHz.

## Power supply

The LCD display module needs the Mizar32 to be powered from its 7.2 volt DC power jack. If you wish to run it from the Mizar32's USB power supply, you can solder a wire on the underside of the LCD board from pin 1 of the BUS5 connector (near the centre of the board's edge) to pin 2 of the LCD1 connector (near the "Left" button).

# I2C command set

The board responds to the 8-bit I2C command bytes 0x7C to 0x7F (7-bit slave addresses 62 and 63):
| Command | Function |
| 0x7C | Send commands to the LCD |
| 0x7D | Read the LCD RAM address |
| 0x7E | Send data bytes to the LCD |
| 0x7F | Read the push-button switches |

## I2C command 7C: Send LCD commands

Physically the LCD display has 2 rows of 16 characters, but internally it has a character memory which holds 40 characters per row and the 16x2 visible display only ever shows a portion of the internal character memory.
Printing past the end of the 40th character of the first row of the character memory passes automatically to the start of the second row and going past the 40th character of the second row continues at the start of the first row. Similarly, when moving the cursor left from the first character of either row moves it to the 40th position of the other row.

### Clear display and return home
#### Command byte 0: Reset the LCD module

For the SED1278, command byte 0 is a no-op. Here, instead, the PIC recognises it as a special case and resets the SED:
* clear screen
* no cursor displayer
* two rows of characters
* 8x5 pixel character font
* cursor moves right when character data are sent; display does not shift
You can do this with the following functions:
| Language | Function |
| eLua | mizar32.lcd.reset |
| PicoLisp | (mizar32-lcd-reset) |

#### Command byte 1: Clear display

Command byte 1 writes spaces into every position of the character memory, moves the cursor to the first row, first column of the character memory and resets the display shift so that the first characters of each line of the internal character memory are aligned to the first characters of the physical display.
| Language | Function |
| eLua | mizar32.lcd.clear |
| PicoLisp | (mizar32-lcd-clear) |

#### Command byte 2: Return home

Command byte 2 moves the cursor to the first row, first column of the character memory and resets the display shift. It's the same as "clear display" without clearing the contents of the character memory.
| Language | Function |
| eLua | mizar32.lcd.home |
| PicoLisp | (mizar32-lcd-home) |

### Data entry modes
#### Command byte 4: when printing, move cursor right

After command byte 4, printing a character to the LCD display makes the cursor move one position to the right: if the cursor goes off the right edge of the physical display, it continues to write to the internal character memory but the cursor, and these characters, are not shown on the physical display.

#### Command byte 5: when printing, shift display right

This is another way to print text backwards, this time keeping the cursor in the same position in the display and shifting the text one place to the right each time a character is printed. If you are in this mode with the cursor at (1,1) and print "Hello", you'll end up with the H in the fifth position of the first row and "olle" in the first four positions of the second, because going off either end of one row of the 40-column character memory always continues at the opposite end of the other line.

#### Command byte 6: when printing, move cursor left

When command byte 6 has been sent, printing a character makes the cursor move one place to the left, so if you print "Hello", the writing comes out backwards on the display "olleH" leaving the cursor to the left of the last character. To see this, you would first have to move the cursor to the right of the display, otherwise it would immediately go off the left edge of the display, leaving only an "H" visible and printing the "olle" at the end of the second line (off the screen).

#### Command byte 7: when printing, shift display left

After command byte 7, each time a character is sent to the display, the cursor remains in the same physical position and all the displayed characters move left by one place. Another way to see it is that the 16x2 physical display moves one place to the right on the virtual display so that, for example, if it was displaying columns 1-16, it then displays columns 2-17. To see the text you print, you would first have to move the cursor to the right side of the display, otherwise everything you display would immediately be shifted off the left edge of the display, and would only re-appear on the right edge when you had printed another 24 characters (24 because the virtual display is 40 characters wide and the physical one 16 wide).
In Alcor6L, these four options can be selected with the following functions:
| Language | Function | Function domain |
| eLua | mizar32.lcd.setup(display_shift, right_to_left) | {true, false} |
| PicoLisp | (mizar32-lcd-setup display-shift right-to-left) | {T, NIL} |
By default, the display doesn't shift and characters are printed left-to-right.

### Cursor modes

Turning the display on and off and saying what type of cursor you want displayed are done together.

#### Command byte 8: turn display off

This turns the display off, showing a blank panel. However, it remembers what data are in the 40x2 character memory, the user-defined characters, the cursor position and which part of the character memory was being displayed on the physical display.

#### Command byte 12: turn display on with no cursor

If the display was off, it is turned back on, but no cursor will be displayed.

#### Command byte 13: turn display on and show blinking block cursor

If the display was off, it is turned back on, and at the cursor position a blinking black block will be displayed.

#### Command byte 14: turn display on with underline cursor

If the display was off, it is turned back on, and at the cursor position, the bottom line of the character cell will be all black.

#### Command byte 15: turn display on with underline and blinking block cursor

In this case, you get both the constant underline and the blinking block. I'm not sure why you would want that...
In Alcor6L, the cursor type can be selected using the following functions:
| Language | Function | Function domain |
| eLua | mizar32.lcd.cursor(what) | what = {"none", "line", "block"} |
| PicoLisp | (mizar32-lcd-cursor 'what) | what = {none, line, block} |
You can also turn the LCD display off and on using:
| Language | Function | Function domain |
| eLua | mizar32.lcd.display(what) | what = {"on", "off"} |
| PicoLisp | (mizar32-lcd-display 'what) | what = {on, off} |
When you turn the display back on, Alcor6L remembers the type of cursor and restores it. This works with all Alcor6L languages.

### Cursor or display shift

There is another way to shift the visible part of the 40-column memory left or right, and to move the cursor one place left or right.

#### Command byte 16: Move cursor one place left

This moves the cursor one character place left both in the memory and on the display. If it moves off the visible part of the display, the cursor is not visibile, and it if moves left from column 1, it goes to column 40 of the other line.

#### Command byte 20: Move cursor one place right

This moves the cursor one character place right both in the memory and on the display. If it moves off the visible part of the display, the cursor is not visible, and it if moves right from column 40, it goes to column 1 of the other line.
In Alcor6L, these can be done using the following functions:
| Language | Function | Function domain |
| eLua | mizar32.lcd.cursor(dir) | dir = {"left", "right"} |
| PicoLisp | (mizar32-lcd-cursor 'dir) | dir = {left, right} |

#### Command byte 24: Shift the display one place left

This changes which part of the virtual display is visible on the physical display. It shifts all the visible characters to the left by one place, revealing a new column of characters from the virtual display at the right edge.
When shifting the display, the cursor remains in the same place in the internal character memory, so on the physical display it is also seen to move one place to the left.

#### Command byte 28: Shift the display one place right

The reverse effect is obtained with command 28, which moves the displayed characters and cursor one place to the right and reveals two new characters on the left side. Again, the cursor stays with the character that it was sitting on before.
In Alcor6L, these can be done using:
| Language | Function | Function domain |
| eLua | mizar32.lcd.display(dir) | dir = {"left", "right"} |
| PicoLisp | (mizar32-lcd-display 'dir) | dir = {left, right} |

### Set display operating mode
#### Command bytes 32-63

Bits 4, 8 and 16 of these commands set the display operating mode:
| Bit | Meaning | Values | Values |
| Bit | Meaning | 0 | 1 |
| 4 | Display font type | 5x8 | 5x11 |
| 8 | Number of display lines | 1 line | 2 lines |
| 16 | Interface data length | 4 bits | 8 bits |
For the Mizar32 display module, you need 5x8, 2 lines, 4 bits, which is the default setting made on power-up.

### Set RAM addresses
#### Command bytes 64-127: Set CG RAM address

Command bytes 64 to 127, move the cursor from the display to the character-generator RAM. The following data bytes do not display anything on the screen, but instead they say which dots are clear and which black of the eight user-definable characters with codes 0 to 7 (and 8 to 15).
The first user-definable character's definition is in locations 64 to 71, the second from 72 to 79, and so on. Each byte defines one row of the character working from top to bottom, with the five least significant bits defining the five pixels of each row in order 16, 8, 4, 2, 1 from left to right. A "one" bit is displayed black.
For example, to define characters 0 and 1 as left-pointing and right-pointing black triangles:
| 8 | 4 | 2 | 1 | Sum | 1 6 | 8 | 4 | 2 | Sum |
| | | | * | 1 | * | | | | 16 |
| | | * | * | 3 = 2 + 1 | * | * | | | 24 = 16 + 8 |
| | * | * | * | 7 = 4 + 2 + 1 | * | * | * | | 28 = 16 + 8 + 4 |
| * | * | * | * | 15 = 8 + 4 + 2 + 1 | * | * | * | * | 30 = 16 + 8 + 4 + 2 |
| | * | * | * | 7 = 4 + 2 + 1 | * | * | * | | 28 = 16 + 8 + 4 |
| | | * | * | 3 = 2 + 1 | * | * | | | 24 = 16 + 8 |
| | | | * | 1 | * | | | | 16 |
| | | | | 0 | | | | | 0 |
you would send command byte 64 followed by 1, 3, 7, 15, 7, 3, 1, 0, 16, 24, 28, 30, 28, 24, 16, 0 and to return to regular character-writing mode to display them, you need to issue a "Set DD RAM address" command byte (see the next entry). If the characters are already displayed on the screen, their shape changes instantly when they are redefined. In Alcor6L, you can do all of this with the function calls:
For the left-pointing triangle,
| Language | Example |
| eLua | mizar32.lcd.definechar(0, {1, 3, 7, 15, 7, 3, 1}) |
| PicoLisp | (mizar32-lcd-definechar 0 (1 3 7 15 7 3 1)) |
For the right-pointing triangle,
| Language | Example |
| eLua | mizar32.lcd.definechar(1, {16, 24, 28, 30, 28, 24, 16}) |
| PicoLisp | (mizar32-lcd-definechar 1 (16 24 28 30 28 24 16)) |
Unlike the command byte interface, Alcor6L's definechar function for the LCD switches you back to normal character-writing mode and leaves the text cursor where it was.

#### Command bytes 128-255: Set DD RAM address

If the top bit of a command byte is set, the lower 7 bits set the position of the cursor within the data memory, which is where the next character will be stored in the display's character memory (and shown on the screen when the display is shifted so as to show that part of the character memory).
| Codes | Where the cursor moves to |
| 128-167 | The first row of 40 characters |
| 192-231 | The second row of 40 characters |
I don't know what happens if you set a DD RAM address of 168-191 or 232-255.
In Alcor6L you can use the following:
| Language | Function | Function domain |
| eLua | mizar32.lcd.goto(row, column) | row = {1, 2}, column = {1, 2, .., 40} |
| PicoLisp | (mizar32-lcd-goto row column) | row = {1, 2}, column = {1, 2, .., 40} |

## I2C command 7D: Read Address Counter

I2C command 0x7D reads the current position of the cursor within the character memory and sends it back over the I2C bus.
This is the internal memory address of where the next character will be stored if you send a data byte. The byte that is returned is a value from 0 to 127 that corresponds to lower 7 bits of the codes used in the "Set DD RAM address" command above. In Alcor6L, you can use the following:
In eLua, the following function will return values 1 or 2 for row and from 1 to 40 for column.
| Language | Example |
| eLua | row, column = mizar32.lcd.getpos |
In PicoLisp, the following function will return a list whose car will be 1 or 2 and its cadr will be a value between 1 and 40.
| Language | Example |
| PicoLisp | (setq pos-tab (mizar32-lcd-getpos)) |

## I2C command 7E: Send LCD data bytes

I2C command 0x7E sends data to the LCD display.
Each data byte you send usually displays one character on the LCD display and moves the cursor right one position.
| Codes | What they display |
| 0-7 | Eight user-defined characters |
| 8-15 | The same eight user-defined characters |
| 16-31 | Blank |
| 32-126 | Standard ASCII characters |
| 127 | |
| 128-159 | Blank |
| 160-255 | Chinese characters and special symbols |
The exception is when you have just sent one of command bytes 64-127, which make the following data bytes set the pixels of a user-definable character (see above, "Set CG RAM address").
In Alcor6L, you can use one of the following functions:
| Language | Example |
| eLua | mizar32.lcd.print(data) |
| PicoLisp | (mizar32-lcd-prinl data) |
where data is a list of one or more strings and numbers, separated by commas. Strings are the normal way to display messages of ASCII text, while a numerical datum should have a value from 0 to 255 and displays a single character from the above table. To print a whole number in decimal, instead, you need to format it first. For example if the variable gen contains a whole number that fits in 3 characters (0-999), you could use:
| Language | Example |
| eLua | mizar32.lcd.print(string.format("%3d", gen)) |
| PicoLisp | (mizar32-lcd-prinl (format gen)) |

## I2C command 7F: Read push-buttons

I2C command 0x7F fetches the current state of the LCD module's push-button switches as a byte with some combination of the lower 5 bits set to indicate which buttons are currently held down.
| Key | Select | Left | Right | Up | Down |
| Bit | 1 | 2 | 4 | 8 | 16 |
The display can reliably detect whether the Select button is held down and any two of the other four buttons. If three of the other buttons are held down, the module returns a byte saying that all four are pressed (this is a consequence of the way the buttons are connected on the circuit board to give you five buttons with only four wires).
In Alcor6L, you can use the following:
| Language | Example |
| eLua | buttons = mizar32.lcd.buttons |
| PicoLisp | (setq lcd-buttons (mizar32-lcd-buttons)) |
Each function returns a string containing a selection of the characters L, R, U, D and S to say whether the Left, Right, Up, Down and Select buttons are currently held down. If none are pressed, an empty string is returned. The hardware allows Select to be detected reliably and up to two of the other four, but if three of Left, Right, Up and Down are being held, all four are returned.

# Software view

If you are feeling brave, you can talk to the LCD Display module using the above codes and eLua's i2c platform module primitives. For example, to display "Hello" you can say:
In eLua:
In PicoLisp:
You can also download the code for the above example from our github page.
SimpleMachines has added a platform module to Alcor6L, (Mizar32's LCD module), documented above, which does all the I2C magic and special timing for you. Using this module to achieve the above, you can just say:
| Language | Example |
| eLua | mizar32.lcd.print("Hello") |
| PicoLisp | (mizar32-lcd-prinl 'Hello) |
It provides the following functions.

## LCD reset

| Language | Example |
| eLua | mizar32.lcd.reset |
| PicoLisp | (mizar32-lcd-reset) |
Initializes the display, resetting everything to as initial state: clear screen, no cursor, displaying columns 1-16 of the 40-column memory, ready to print at (1,1), writing text from left to right and moving the cursor one place right after each character. You don't have to call reset at the start of your program, but doing so does will ensure that your program still works if the display has been left in a funny state by some previous run.

## LCD setup

| Language | Example |
| eLua | mizar32.lcd.setup(display_shift, right_to_left) |
| PicoLisp | (mizar32-lcd-setup display_shift right_to_left) |
This can be used to set some of the stranger operating modes of the LCD display. Both parameters are optional and if you omit them, they default to false, which sets sensible mode.
display_shift: If true, then with each character you subsequently print, the cursor will move by one place in the character memory as usual but the display's contents will also move by one position horizontally in the opposite direction so that the cursor remains in the same column of the physical display. This can be used to achieve "scrolling text" effects. Note, however, that when the cursor passes from column 40 to column 1 or vice versa, it flips over to the other row.
right_to_left: If true, text will be printed right-to-left: the cursor will move one position to the left in the character memory and, if display shifting is also enabled, the contents of the display will shift to the right so that the cursor stays in the same column on the screen.

## LCD clear

| Language | Example |
| eLua | mizar32.lcd.clear |
| PicoLisp | (mizar32-lcd-clear) |
Clears the display, moves the cursor to the top left (position 1,1) and resets the display shift to show columns 1 to 16.

## LCD home

| Language | Example |
| eLua | mizar32.lcd.home |
| PicoLisp | (mizar32-lcd-home) |
Moves the cursor to the top left (position 1,1) and resets the display shift.

## LCD cursor goto

| Language | Example |
| eLua | mizar32.lcd.goto( row, column ) |
| PicoLisp | (mizar32-lcd-goto row column) |
Moves the cursor to the specified row and column.
* row: A number (1 or 2) giving the row you want to move to.
* column: A number (1 to 40) giving the position within that row in the character memory.

## LCD cursor position

| Language | Example | Return value |
| eLua | row, column = mizar32.lcd.getpos | row, column |
| PicoLisp | (setq lcd-pos (mizar32-lcd-getpos)) | (car lcd-pos), (cadr lcd-pos) |
Returns the current cursor position.
| Symbol | Meaning |
| row | A number (1 or 2) giving the current row. |
| column | A number (1 to 40) giving the current column in the character memory. |

## LCD print

| Language | Example |
| eLua | mizar32.lcd.print([data1] [, data2] ... [datan]) |
| PicoLisp | (mizar32-lcd-prinl [data1] [data2] ... [datan]) |
Writes into the LCD character memory starting at the current cursor position. The cursor will advance by one position for each character printed. When it goes past column 40, it moves to column 1 of the other line, (and vice versa when printing right-to-left).
Each item of data can be a string or an integer. Strings are the normal way to display messages of ASCII text. An integer parameter should have a value from 0 to 255 to display a single character, which can be one of the user-defined characters 0-7, the regular ASCII characters 32-125 plus 126 and 127 for right- and left-pointing arrows and the Chinese, Greek and mathematical symbols with codes 160-255.

## LCD cursor settings

| Language | Example |
| eLua | mizar32.lcd.cursor(what) |
| PicoLisp | (mizar32-lcd-cursor what) |
Sets the type of cursor that is displayed at the cursor position or move the cursor left or right.
what is string to say what should be done:
* "none", "line" or "block" will display, respectively, no visible cursor, a constant underline or a blinking solid block at the cursor position.
* "left" or "right" move the cursor one position left or right in the character memory and on the display without changing the underlying characters. The display never shifts in this case and, as usual, the cursor wraps between column 40 of one row and column 1 of the other.

## LCD display settings

| Language | Example |
| eLua | mizar32.lcd.display(what) |
| PicoLisp | (mizar32-lcd-display what) |
Turns the physical display on or off, or shifts the displayed characters left or right.
what is string to say what should be done:
* "off" and "on" turn the physical display off or back on again. While the display is off it appears blank but the contents of the character memory, the position and type of cursor, user-defined characters and setup mode are all remembered and you can write to the character memory and perform all other operations while the display is off. This allows you to update the display without the viewer seeing too much flickering.
* "left" or "right" shift the displayed characters one place left or right. For example, if it was displaying the usual columns 1-16 and you say mizar32.lcd.display("left") (or the equivalent in other languages), it will then display columns 2-17: the visible characters move left but the window onto the character memory moves right.

## User defined characters

| Language | Example |
| eLua | mizar32.lcd.definechar(code, glyph) |
| PicoLisp | (mizar32-lcd-definechar code glyph) |
Programs one of the eight user-definable characters whose codes are 0 to 7. When it has been defined, a character can be displayed using mizar32.lcd.print(n), where n is a number from 0 to 7. If the character in question is already being displayed, its visible form will change immediately on the display. At power-on, the 8 characters are defined as random garbage.
* code: A number (0 to 7) saying which of the characters you wish to redefine.
* glyph: A table of up to eight numbers giving the bit-patterns for the eight rows of the character, in order from top to bottom. Each of these number is a value from 0 to 31, to define which of the 5 bits in the row should be black. The pixels' values from left to right are 16, 8, 4, 2 and 1. For example, {1, 3, 7, 15, 31, 15, 7, 3, 1, 0} would define a left-pointing solid triangle in the top 7 rows. Extra rows are ignored, and missing rows are blanked.
To print in French using Lisp, you can try the following:
Please note: You may also download the above code french.l from our examples repository on github.

## LCD buttons

| Language | Example |
| eLua | buttons = mizar32.lcd.buttons |
| PicoLisp | (setq buttons (mizar32-lcd-buttons)) |
Tells which of the five user buttons are currently pressed.
* buttons is a string (buttons is a list in PicoLisp) containing up to five of the characters L, R, U, D and S to say whether the Left, Right, Up, Down and Select buttons are currently held down. If none are pressed, an empty string is returned. The hardware allows Select to be detected reliably and up to two of the other four: if three of Left, Right, Up and Down are being held, all four are returned.
For example, a fragment of code used in a game could be:
In eLua:
In PicoLisp:

# Further reading

* The circuit diagram for the LCD module
* The datasheet for Ampire's 162B LCD module
* Interfacing LCD with 8051, an article by Parveen Kumar about the 162B
* The source code for the Mizar32 Display Module's firmware
* The manual for eLua's mizar32.lcd module