Mizar32 I2C LCD character display module

Introduction edit

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 edit

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 edit

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.

 
Mizar32 LCD module USB power hack

Power supply edit

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 edit

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 edit

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 edit

Command byte 0: Reset the LCD module edit

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 edit

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 edit

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 edit

Command byte 4: when printing, move cursor right edit

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 edit

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 edit

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 edit

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 edit

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

Command byte 8: turn display off edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

Command bytes 32-63 edit

Bits 4, 8 and 16 of these commands set the display operating mode:

Bit Meaning Values
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 edit

Command bytes 64-127: Set CG RAM address edit

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:

1
6
8 4 2 1 Sum 1
6
8 4 2 1 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 edit

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 edit

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 edit

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 edit

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 edit

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:

i2c.setup(0, 50000)
i2c.start(0)
ack = i2c.address(0, 63, i2c.TRANSMITTER)
if ack then
  i2c.write(0, "Hello")
end
i2c.stop(0)

In PicoLisp:

(de mizar32-lcd-write-i2c (x)
   (i2c-setup 0 50000)
   (i2c-start 0)
   (if (not (= 0 (i2c-address 0 63 *i2c-transmitter*)))
      (i2c-write 0 x) )
   (i2c-stop 0) )

(mizar32-lcd-write-i2c 'Hello)

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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 edit

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:

# A PicoLisp program to demonstrate
# user defined characters.
(de print-msg ()
   (mizar32-lcd-clear)
   # define the French accent character `é'.
   (mizar32-lcd-definechar 0 (1 2 14 17 31 16 14 0))
   # print Liberté.
   (mizar32-lcd-prinl 'Libert 0 ",")
   # print égalité.
   (mizar32-lcd-prinl 0 'galit 0 ",")
   (mizar32-lcd-goto 2 1)
   # print fraternité in the next line·
   (mizar32-lcd-prinl 'fraternit 0) )

(print-msg)

Please note: You may also download the above code french.l from our examples repository on github.

LCD buttons edit

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:

pressed = mizar32.lcd.buttons()
if pressed:find("S") then do_select() end
if pressed:find("L") then do_move_left() end
if pressed:find("R") then do_move_right() end

In PicoLisp:

(setq buttons (mizar32-lcd-buttons))
(if (member "S" (chop buttons))
   (do-select) )

Further reading edit