Lexmark Optrex I2C LCD Display

Introduction

A few years back, a local factory (Bosch) was getting rid of some office equipment. I had a neighbour that worked there, and so he offered me the Lexmark Optra s 2455 laser printer for free!

It was an excellent printer (came with a toner that lasted for 15000 sheets from memory, perfect for my university entrance preparations), save for the fact that it lacked duplex.

Naturally, the printer is no longer in service due to age and so together with my dad, we salvaged a lot of its internals. One component that immediately interested me was the LCD, which will be the subject of my notebook.

Disclaimer!

I am still very new to the MBed and the I2C Bus. Everything provided here will be referenced accordingly, and I provide no warranties or guarantees as to the operation and safe implementation of the following information. Please exercise your own sensibility and discretion.

Should there be ANY MISTAKE in the information I provide, please DO NOT HESITATE to let me know. This is my first entry, and so first impressions really matter. =]

Aim

This notebook entry will document entirely the requirements to control this LCD since there is not much information around. This work is based off another person's efforts, and I will reference them accordingly.

I am writing this to help myself reinforce the I2C Bus, and to brush up on writing for future projects.

Materials

This LCD was taken out of a Lexmark Optra s 2455 laser printer: Link.

References

The code is heavily reliant, and I am grateful for the efforts from the following people: cr0sh Link and Enif Link.

Thanks to the addresses discovered, I am now able to completely control the LCD via I2C.

Returning to the topic, for reference is a picture of the LCD module: Link.

LCD Enclosure LCD Front LCD Back

Implementation

This entry will not explain the I2C bus except for when I think there is a need for some explanation.

I will divide the implementation into 4 subsections: The device Address, the LCD, the LED and the Controls.

Addressing

Anyone that has dealt with the I2C protocol, whether extensively or not, will be faced with initial task of finding the address in order to communicate with the device. More often than not, this information is readily available from manufacturer datasheets, or a quick Google Search would work.

Unfortunately in this case, not much is available. In this event, a simple Bus Pirate could be used to "probe" the line for any response. Any further discussion of this is beyond the scope of this entry.

For addressing, it is extremely common and very easy (I still make the same mistakes) to be confused between a 7-bit address and an 8-bit address.

Let me assure you that the MBed takes an 8-bit address! Link So what do you do if you are given a 7-bit address?

Write the 7-bit address in binary, and then add a "0" to the end (write) or "1" to the end (read). What does this even mean?! I was thoroughly confused when I first began, and so I hope to clear any confusion:

  1. This LCD is located at 7-bit address 0x21, which is 00100001 in binary.
    • Perform a Shift Left Logical to obtain 0100001x.
      1. To WRITE to the device, we set x to "0". Thus, write address is 01000010, which is 0x42. Note that on the MBed, x is automatically set to "0" for a write instruction, so you only need to remember address "0x42".
      2. To READ from the device, we set x to "1". Thus, the read address is 01000011, which is 0x43. Since the MBed automatically sets x = "1" for a read instruction, you can enter the read address as "0x43" (quite logical really) OR "0x42" even!
  2. To do the following:
    • Writing to the device: Write address is 0x42.
    • Reading from the device: FIRST write 0x43 to address 0x42, THEN read from address 0x43.

Note that not all devices follow this scheme. In a more general application, the I2C operation on an MBED works like this:

  • To write to a register of a device, simply put in the relevant 8 bit address.
  • To read from a register in a device, first write to the device the address you plan on reading from, then you will proceed to actually read from the device.

The reason I am saying all this is that the I2C bus is commonly used. I am currently in the midst of a quadcopter build, and being able to correctly address the sensors, registers AND commands is a pain in the backside! The best is to understand the foundations thoroughly before endeavouring anywhere to save time and unnecessary headaches.

Please note that this LCD behaves non-linearly to other I2C devices: After every write, you MUST read 2 bytes.

LCD

The LCD is a 16x2 display.

The following commands are possible for the LCD: Clear Screen and Print Text.

In order to Clear the LCD, we must write commands 0x00, 0x00 to register 0x88 of the device, then read 2 bytes back.

A simple implementation:

// Code to clear the LCD:

#include "mbed.h"

I2C i2cname(p9, p10)                                         // SDA and SCL Pin Number.

int main()
{ 
     i2cname.frequency(400000);                              // NOT necessary. Default I2C speed is 100kHz, Fast mode is 400kHz.

     char buffer[2];                                         // Create an array for storing the data read.
     char command[] = {0x88, 0x00, 0x00};                    // Send command 0x00, 0x00 to register 0x88 to CLEAR the LCD.

     i2cname.write(0x42, command, sizeof(command));          // Send the command to device located at 0x42.
     i2cname.read(0x43, buffer, 2);                          // Read 2 bytes from the device to ensure LCD operates correctly.
}

To send text to the LCD, it becomes slightly more complex. The string must be in hex, and we need to tell the device the position to write to. Basically, the data structure is:

Write <CharPos*8+line>, 0x00, Char1, Char2, Char3....... to register 0x55 @ address 0x42.

A simple implementation:

// Code to display text on the LCD:

#include "mbed.h"

I2C i2cname(p9,p10);                                             // SDA and SCL Pin Number.

int main()
{
    i2cname.frequency(400000);                                   // NOT necessary. Default I2C speed is 100kHz, Fast mode is 400kHz.
    char buffer[2];                                              // Create an array for storing the data read.

    // Send command 0x00, 0x00, 0x00 (Start at position 0 on first line of LCD) to register 0x55, along with text in hex to print to the LCD.
    char text[] = {0x55, 0x00, 0x00, 0x00, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x49, 0x61, 0x6e, 0x21}; 

    i2cname.write(0x42, text, sizeof(text));                     // Send the command to device located at 0x42.
    i2cname.read(0x43, buffer, 2);                               // Read 2 bytes from the device to ensure LCD operates correctly.
}

LED

The LED on the panel can either stay On, Off and Flash.

The commands are simple.

  • To turn the LED OF: send 0x01, 0x25, 0x25 to register 0xB0.
  • To turn the LED ON: send 0x11, 0x25, 0x25 to register 0xB0.
  • To FLASH the LED: send 0x21, 0x25, 0x25 to register 0xB0.

Do not forget to read two bytes after every write!

Here is the code for a quick demonstration:

// Code to turn the LED OFF, then ON, then OFF, then FLASH:

#include "mbed.h"

I2C i2cname(p9, p10);

int main()
{ 
    char ledoff[] = {0xB0, 0x01, 0x25, 0x25};               // Send 0x01, 0x25, 0x25 to register 0xB0 to turn LED OFF.
    char ledon[] = {0xB0, 0x11, 0x25, 0x25};                // Send 0x11, 0x25, 0x25 to register 0xB0 to turn LED ON.           
    char ledflash[] = {0xB0, 0x21, 0x25, 0x25};             // Send 0x21, 0x25, 0x25 to register 0xB0 to FLASH LED.

    char buffer[2];                                         // Create an array to store data read.

    i2cname.write(0x42, ledoff, sizeof(ledoff));            // Send OFF command to 0x42.
    i2cname.read(0x43, buffer, 2);                          // Read 2 bytes from the device to ensure LCD operates correctly.
    wait(3);
    i2cname.write(0x42, ledon, sizeof(ledon));              // Send ON command to 0x42.
    i2cname.read(0x43, buffer, 2);                          
    wait(3);
    i2cname.write(0x42, ledoff, sizeof(ledoff));            
    i2cname.read(0x43, buffer, 2);
    wait(3);
    i2cname.write(0x42, ledflash, sizeof(ledflash));        // Send LED FLASH command to 0x42.
    i2cname.read(0x43, buffer, 2);
    wait(3);
    i2cname.write(0x42, ledoff, sizeof(ledoff));
    i2cname.read(0x43, buffer, 2);
}

Controls

This panel contains the following buttons: Left, Right, Select, Up/Back/Return, Go and Stop.

To read the button state, write 0x43 to register 0x11 at address 0x42. Then read 2 bytes from address 0x43.

The read bytes will be of the structure {0x11, 0xXX}, where for 0xXX is:

  • 0x01 = Up/Back/Return,
  • 0x02 = Select,
  • 0x04 = Left,
  • 0x08 = Right,
  • 0x10 = Go, and
  • 0x20 = Stop.

Results

When I have a chance, I will take some photos and upload them here to demonstrate all the control I have over the panel.

Conclusion

It was fun learning how to use the I2C protocol on the MBed. The MBed library has really simplified the task of trying to read and write from the I2C bus. Coming from a background of bit-banging the I2C, this I am extremely grateful for the higher-level possibilities using the MBed I2C library.

For those that just want some quick implementation of I2C communication, I hope the code provided here will be of great use. For those that actually have this display, I hope that you can use this notebook entry to actual control your device.

When I finalise the library for this LCD panel, I will publish it, and will definitely link this entry.

Closing Remark

Thank you everyone for your time. I apologise for the extended entry (my law-background certainly thrives on any opportunity to write, given that there is not much writing in my engineering degree), and sincerely hope that I have been of use to your understanding.


Please log in to post comments.