HD44780 custom characters

I`ve connected the LCD to a max7323 I2C port expander and that to the arduino. After modifying the 4 bit I2C library available elsewhere on here to suit the maxim part, the LCD works great! However I want to try adding some custom characters.

Well I searched around and found lots of information but most of it confused me even more than before so I just have to turn to the pros for help :smiley:

I found a program to generate the character data, 040E1F0E0E0E0E00. I understand that I have to pull RS low, move to a CGRAM address, RS high again, then fill it with the character data, but I really don`t know where to start :-/

This is the library I`m using.

/*      Modified 04/02/09
*      AW
*      Support Max7323
*      removed MCP23008 register addressing
*      invert backlight on/off function
*/

#include "LCDI2C4Bit.h"
#include <Wire.h>

extern "C" {
  #include <stdio.h>  //not needed yet
  #include <string.h> //needed for strlen()
  #include <inttypes.h>
  #include "WConstants.h"  //all things wiring / arduino
}

//command bytes for LCD
#define CMD_CLR 0x01
#define CMD_RIGHT 0x1C
#define CMD_LEFT 0x18
#define CMD_HOME 0x02

//stuff the library user might call---------------------------------

//constructor.  num_lines must be 1 or 2, currently.

byte dataPlusMask = 0; // TODO!!!

LCDI2C4Bit::LCDI2C4Bit( int devI2CAddress, int num_lines, int lcdwidth) {
  myNumLines = num_lines;
  myWidth = lcdwidth;
  myAddress = devI2CAddress;
}

void SetMCPReg( byte deviceAddr, byte reg, byte val ) {
  Wire.beginTransmission(deviceAddr);
  Wire.send(val);
//  Wire.send(val);
  Wire.endTransmission();
}

void SendToLCD( byte deviceAddr, byte data ) {
  data |= dataPlusMask;
  SetMCPReg(deviceAddr,0x0A,data);
  data ^= 0x80; // E
  delayMicroseconds(1);
  SetMCPReg(deviceAddr,0x0A,data);
  data ^= 0x80; // E
  delayMicroseconds(1);
  SetMCPReg(deviceAddr,0x0A,data);
  delay(1);

}

void WriteLCDByte( byte deviceAddr, byte bdata ) {
  SendToLCD(deviceAddr,bdata >> 4);
  SendToLCD(deviceAddr,bdata & 0x0F);
}

void LCDI2C4Bit::init( void ) {
  dataPlusMask = 0; // initial: 0
//  SetMCPReg(myAddress,0x05,0x0C); // set CONFREG (0x05) to 0  not needed for max7323
//  SetMCPReg(myAddress,0x00,0x00); // set IOREG (0x00) to 0      not needed
  //
  delay(50);
  SendToLCD(myAddress,0x03);
  delay(5);
  SendToLCD(myAddress,0x03);
  delayMicroseconds(100);
  SendToLCD(myAddress,0x03);
  delay(5);
  SendToLCD(myAddress,0x02);
  WriteLCDByte(myAddress,0x28);
  WriteLCDByte(myAddress,0x08);
  WriteLCDByte(myAddress,0x0C); // turn on, cursor off, no blinking
  delayMicroseconds(60);
  WriteLCDByte(myAddress,0x01); // clear display
  delay(3);
}

void LCDI2C4Bit::backLight( bool turnOn ) {
  dataPlusMask |= 0x40; // Lights mask
  if (turnOn) dataPlusMask ^= 0x40;
  SetMCPReg(myAddress,0x0A,dataPlusMask);
}


void LCDI2C4Bit::print( int value ) {
  dataPlusMask |= 0x10; // RS
  WriteLCDByte(myAddress,(byte)value);
  dataPlusMask ^= 0x10; // RS
}

void LCDI2C4Bit::printIn( char value[] ) {
  for ( char *p = value; *p != 0; p++ )
    print(*p);
}

void LCDI2C4Bit::clear() {
  commandWrite(CMD_CLR);
}

void LCDI2C4Bit::cursorTo(int line_num, int x) {
  commandWrite(CMD_HOME);
  int targetPos = x + line_num * myWidth;
  for ( int i = 0; i < targetPos; i++)
    commandWrite(0x14);
}

void LCDI2C4Bit::commandWrite( int command ) {
  // RS - leave low
  WriteLCDByte(myAddress,command);
  delay(1);
}

I just finished my LCD library, with custom character loading, last night! :slight_smile:

I'm not sure about all HD44780 compatible controllers, but mine can only load 8 custom characters.
DDRAM values 0 - 7 will display the custom characters, with 8-15 just mirroring 0-7.

First you have to write the CGRAM address for the character you want, then write 8 bytes for each of the 8 rows (of the 5x8 character; the first three bits of each byte are ignored).

Guessing from the library your using it would be something like this:

void LCDI2C4Bit::setCustomCharacter(uint8_t slot, uint8_t bitmask[8])
{
      //64 = write CGRAM address
      //slot<<3 = 3 bit (8 possible) cust. char. address
      commandWrite(64 | (slot << 3));

      //write the character to CGRAM
      dataPlusMask |= 0x10;
      commandWrite(bitmask[0]);
      commandWrite(bitmask[1]);
      commandWrite(bitmask[2]);
      commandWrite(bitmask[3]);
      commandWrite(bitmask[4]);
      commandWrite(bitmask[5]);
      commandWrite(bitmask[6]);
      commandWrite(bitmask[7]);
      dataPlusMask ^= 0x10;

      //set DDRAM address to make sure next write command is to DDRAM instead of CGRAM
      //this will trash your cursor position, but prevent you from accidentally overwriting custom characters
      commandWrite(128);
}

some_funct()
{
      //test character
      uint8_t testCharA[8] = {0b00011110,
                        0b00010101,
                        0b00001010,
                        0b00010101,
                        0b00001010,
                        0b00010101,
                        0b00001010,
                        0b00010101      };

      //load CGRAM slot 3 with custom character
      myLcd.setCustomCharacter(3, testCharA);

      //display custom character stored CGRAM address 3
      myLcd.write(3);
}

Wow thats awesome, I didnt expect a reply so quick!
I will try that tonight and see how it goes. Thanks aspartame :slight_smile: