Tuesday, July 2, 2013

Object-Oriented LCD management

One for the latest developments in Cosa is the object-oriented interface for LCD devices. The goal has been to create a common interface for the Cosa LCD device drivers (PCD8544, ST7565 and HD44780) and allow LCD devices to act as an IOStream::Device and finally adapt to different forms of ports (e.g. parallel and serial communication). With a common interface applications may change output device more easily. Below is an UML Inheritance Diagram for IOStream::Device with all Cosa classes that currently implement the interface and are inter-changeable.

Fig.1: IOStream class hierarchy
The Cosa LCD class is basically a name space for the LCD::Device abstract class which holds the common interface for LCDs. This interface is an extension of the IOStream::Device interface with some LCD specific methods. By providing an implementation of IOStream::putchar() an LCD::Device can directly be used for text output with number conversion, string and formated output both function (e.g. print) and operator<< notation.

Fig.2: IOStream::Device interface
Above are the member functions in the IOStream::Device interface. The "abstract" implementation acts as a null device which accepts any input and always returns end-of-file (EOF(-1)). By overriding putchar() an output stream device reuses all other output methods, i.e. puts(), puts_P() and write(). The default implementation of these use putchar().

Number conversion (binary to textual representation) is performed by the IOStream class which delegates the actual output to the attached IOStream::Device. The IOStream and the null device code is reused by all devices.

The LCD::Device class extends the IOStream::Device class with additional methods typical for LCD. Most of these are pure abstract and must be defined by the device driver.

Fig.3: LCD::Device Interface
The abstract LCD::Device captures some display properties, cursor, tabulator and text mode handling. If an application only uses the abstract LCD interface the code may be more easily adapted for different LCD devices. Also as the LCD device acts as an IOStream::Device the text output may be redirected to any other device that implements the interface. See figure 1 for the current classes that implement the interface.

There are basically two major types of LCD devices; pixel and character based. Some pixel based devices restrict drawing of pixels to a vertical byte, 8 pixels at a time, but allow horizontal cursor handling on pixel level. Examples of this type of LCD devices are PCD8544 and ST7565. HD44780 aka 1602/1604/2004 are typical character based devices where the characters codes are written to the device and not pixels.

Fig. 4: HD44780 Device Driver collaboration diagram
Another issue when dealing with LCD devices is the method of communication. Most devices use serial communication such as I2C or SPI to reduce the number of pins. The alternative is parallel communication. This is the default communication method for the HD44780 LCD device. It allows 8-bit or 4-bit command/data parallel communication. An I2C IO expander may be used to adapt HD44780 to serial communication over the I2C bus. To handle adaption of the device driver an abstract IO port is defined.

Fig. 5: HD44780::IO class hierarchy
The HD44780 device driver can now be adapted to any of these communication modules by simply changing the IO port device. The Cosa LCD class hierarchy is constructed for Standard Arduino, Mega, Mighty and possible to be scaled down to ATtiny8X. The footprint both SRAM and PROGMEM of the LCD device driver becomes especially important for micro controllers with limited program memory.

The HD44780 device driver requires 2.5 Kbyte program memory for parallel port, 3.5 Kbyte with I2C expander. The PCD8544 device driver with font (System7X5) requires 3.5 Kbyte. And last, ST7565, 4 Kbyte including font. All devices drivers include IOStream::Device, LCD::Device and default virtual methods.

Fig. 6: Benchmarking the LCD device drivers
The above benchmark configurations are (left to right, top to bottom):
  1. Arduino Mega, 20x4 character, HD44780 with MJKDZ I2C IO expander.
  2. Arduino Nano, 128x64 pixel, ST7565, software bit serial, OutputPin::write().
  3. Arduino Nano, 84x48 pixel, PCD8544, software bit serial.
  4. Arduino Uno, 16x2 character, HD44780, 4-bit direct port access, D4..D7.
The example sketch CosaLCDspeed performs basic benchmarking of the different device drivers. The benchmarks are:
  1. Clear the display, lcd.display_clear().
  2. Fill the display with characters, lcd.putchar(), WIDTH * HEIGHT number of characters per iteration and lcd.set_cursor() per line.
  3. Write a string, lcd.puts(), 2x16 characters per iteration, and lcd.set_cursor() per iteration.
  4. Write a string from program memory, lcd.puts_P(), as previous.
  5. Write an integer in decimal format, lcd.set_cursor() and number print per iteration (trace << num).
  6. Write an integer in binary format, as previous (trace << bin << num).
The below measurements are in operations per second:

Tab. 1: Benchmark operations per second
Please note that the benchmark putchar() is adjusted with the number of characters written (80, 160, 84, resp 32). The New LiquidCrystal Library reports 299 fps and Cosa LCD HD44780 driver 564 fps (putchar() benchmark 4-bit) which is 1.9X faster and 6.5X faster than the original LiquidCrystal Library.

The Cosa I2C IO expander handler is further optimized to achieve higher performance by reducing the transmission overhead. Writing a command or data byte to the I2C IO expander is transmitted as a sequence of five bytes instead of four X two byte transmissions (address and port data, total eight bytes). This reduces the transmission time with nearly 40%. This method can be applied further to reduce string output (see puts() and compare with puts_P() in tab. 1). The I2C@100khz improvement is over 1.7X faster compared to that reported by New LiquidCrystal Library benchmarks (31 fps). With I2C@400khz the improvement is over 4.6X faster.

Fig.7: ATtiny84 acting as an I2C LCD slave device. Running 4-bit parallel LCD device driver
Running the benchmark on an ATtiny85/84 with the internal 8 Mhz clock shows also great improvements; 4.9X for 4-bit parallel mode and 1.26X for I2C mode. Using an ATtiny as a Virtual LCD slave device on I2C gives 2.3X compared using an I2C IO expander. The ATtiny acts as an I2C slave device and uses the 4-bit parallel mode.

With the new USI based TWI master device driver support even an ATtiny85 can be used with an LCD. 

Fig.8: ATtiny85 connected to LCD with I2C IO expander
For the pixel based displays (PCD8544 and ST7565) the actual number of bytes transmitted is 6X per character.

For further details see the Cosa on-line documentation and the LCD example sketches

No comments:

Post a Comment