PCA9555 32-Bit GPIO Expander with Arduino and LCD Display

by Lewis Loflin

In this series I'll be connecting the PCA9555D 32-bit GPIO expander board to an Arduino micro-controller. First we started out simple with counting from 0 to 255 in binary on eight LEDs. See Part 1 for technical information and getting started.

Here in part four we have connected a type a Hd44780 type LCD display and learn how to enter data and manipulate character date and strings. In addition to the LCD display we will scan a keypad, use hardware interrupts, all connected through a single PCA9555 module. In addition I'll reveal some code little discussed with Arduino and we will convert/manipulate code for output to the LCD display.

In part two we learned to connect a 4X4 keypad to an 8-bit port. Using "polling" the routine getKey() we will check for a key press and return a BCD value from 0 to 16 displayed on the eight LEDs. See Part 2

In part three we learned to use the interrupt pin to jump to the key scan routine only when a key is pressed. A BCD value is output on the eight LEDs. See Part 3



HD44780 display
Connections to Hd44780 display

How to connect LCD display:
E = connected display pin 6 (E), hi to lo transition will clock info into display. Register for command or data is selected by RS. Data is output in port0, two bits of port1 on the MCP23060 (bits 6, 7) are to control E and RS. R/W goes to ground. Pins 15 and 16 are the back light.

PCA9555 Demo Board



#include <Wire.h> // specify use of Wire.h library.

int i, j;
byte temp1;

volatile byte flag_bit = LOW;

void setup()

{
  pinMode(2, INPUT);  // IRQ input
  
  // setup direction registers U1
  Wire.begin();
  Wire.beginTransmission(0x20);  
  Wire.write(0x06);  // pointer
  Wire.write(0x0F);  // DDR Port0 bits 0-3 input 4-7 output 
  Wire.write(0x00);  // DDR Port1 all output
  Wire.endTransmission(); 

  Wire.beginTransmission(0x20);   
  Wire.write(0x02);  // pointer
  Wire.write(0x00);  // U1 bits 4-7 PORT0 LOW
  Wire.endTransmission(); 
  
  // Keypad connected to U1 PORT0.
  // Invert bits on read of U1 PORT0.

  Wire.beginTransmission(0x20);  
  Wire.write(0x04);  // pointer
  Wire.write(0xFF);  // invert bits PORT0 on read
  Wire.endTransmission(); 

  // clear LEDs U1 PORT1
  Wire.beginTransmission(0x20);  
  Wire.write(0x03); // begin here
  Wire.write(0x00); 
  Wire.endTransmission();

  attachInterrupt(0, flag1, FALLING); 

  // Use 0x21 (U2) for LCD display.

  Wire.beginTransmission(0x21);  // setup direction registers U2
  Wire.write(0x06);  // pointer
  Wire.write(0x00);  // DDR Port1 all output 
  Wire.write(0x00);  // DDR Port1 all output
  Wire.endTransmission(); 

  // setup PORT0 D7 = E; D6 = RS 
  Wire.beginTransmission(0x21); 
  Wire.write(0x02); //pointer
  Wire.write(B10000000); // setup for command mode by default
  Wire.endTransmission(); 

   /* 
   writeCommand(byte) sends a control code to display.
   Hd44780 display commands are:
   
   0x0f = initiate display cursor on blinking
   0x0c = initiate display cursor off
   0x01 = clear display fills display with spaces (0x20).
   0x02 = HOME returns to first line first character
   0x38 = 2 lines X 16 char 8 bits mode. Defaults to 1 line mode.
   0x10 = cursor left
   0x14 = cursor right
   0x18 = Shifts entire display left
   0x1c = Shifts entire display right 
   
   1st line on display is 0 and 2nd line is 1.
   Characters are numbered 0-15.
   One can also go to a specific location.
   writeCommand(0x80); // go to line 0 column 0
   writeCommand(0x80 + 0x40); // go to line 1 column 0 
   */

  writeCommand(0x38); // setup for 2 lines
  writeCommand(0x0F); // blinking cursor

  writeCommand(0x02); // home
  writeCommand(0x01); // clear


}  // end setup

void loop() {

  if (flag_bit = HIGH)  {
    detachInterrupt(0); 
    temp1 = getKey(); // scan keypad for BCD code.
    flag_bit = 0x00;
    delay(100);
    attachInterrupt(0, flag1, FALLING);  
  } 
  // Output message and string length
  if (temp1 == 0x16)  {
    char String1[] = "Hello World!\n";
    typeln(String1, 1); // write to line 1 LCD
    // could use typeln("Hello World!\n"); instead.
    char String2[2];
    int j = sizeof(String1);
    itoa(j, String2, 10); // convert int to string
    //   String2[sizeof(String2)] = '\n';
    strcat(String2, " bytes!\n"); // concat strings
    typeln(String2, 2); // write to line 2 LCD

  }

  if (temp1 != 0x00)   {
    Wire.beginTransmission(0x20);  
    Wire.write(0x03); // begin here
    Wire.write(temp1); // display code on LEDs
    Wire.endTransmission();
    if ((temp1 <= 0x39) && (temp1 >= 0x30)) type(temp1); // output number 0-9
    if (temp1 == 0x11)  type(0x20); // print space
    if (temp1 == 0x12)  writeCommand(0x01); // clear
    if (temp1 == 0x13)  writeCommand(0x10); // cursor left
    if (temp1 == 0x14)  writeCommand(0x14); // cursor right
    if (temp1 == 0x15)  writeCommand(0x02); // home
  }
} // end loop


void flag1() // set bit
{
  flag_bit = HIGH; 
} 


// Below we pass a pointer to array1[0]. If no '\n' 
// then the limit is 16 char. (0 - 15)
void typeln(char *array1, int i)   {
  delayMicroseconds(1000);
  if (i == 1) writeCommand(0x80); // begin on 1st line
  if (i == 2) writeCommand(0x80 + 0x40); // begin on 2nd line 
  for (int j = 0; (array1[j] != '\n') && (j < 16); j++)   type(array1[j]); 
}


/*
void writeCommand(byte x)
Send control command to Hd44780 display 
 E High to Low transition write command or data to Hd44780 display
 RS 0 for command, default 1 for data
 setup port0 D7 = E; D6 = RS 
 Data is at port 1 register 0x03
 control is at port 0 register 0x02  */

void writeCommand(byte x)   {

  Wire.beginTransmission(0x21);  
  Wire.write(0x03); // begin here
  Wire.write(x);  // command code
  Wire.endTransmission();

  Wire.beginTransmission(0x21);  
  Wire.write(0x02); // pointer
  Wire.write(B10000000);  // command mode E high
  Wire.endTransmission();

  Wire.beginTransmission(0x21);  
  Wire.write(0x02); // pointer 
  Wire.write(B00000000);  // char mode E low
  Wire.endTransmission();

  delayMicroseconds(100);

  Wire.beginTransmission(0x21); 
  Wire.write(0x02); //  pointer
  Wire.write(B10000000);  // E high back to command mode
  Wire.endTransmission();
} 

/*

void type(byte x) 
Send char to Hd44780 display 
 E High to Low transition write command or data to Hd44780 display
 RS 0 for command, default 1 for data
 setup port0 D7 = E; D6 = RS 
 Data is at port 1 register 0x03
 control is at port 0 register 0x02 */

void type(byte x)   {

  Wire.beginTransmission(0x21);  
  Wire.write(0x03); // begin here
  Wire.write(x);  // data
  Wire.endTransmission();

  Wire.beginTransmission(0x21);  
  Wire.write(0x02); // pointer
  Wire.write(B11000000);  // char mode E high
  Wire.endTransmission();

  Wire.beginTransmission(0x21); 
  Wire.write(0x02); // pointer 
  Wire.write(B01000000);  // char mode E low
  Wire.endTransmission();

  delayMicroseconds(100);

  Wire.beginTransmission(0x21);  
  Wire.write(0x02); //  pointer
  Wire.write(B10000000);  // E high back to command mode
  Wire.endTransmission();

} 


byte getKey()  {

  Wire.beginTransmission(0x20);  
  Wire.write(0);  // set data pointer
  Wire.endTransmission();
  Wire.requestFrom(0x20, 1);
  byte c = Wire.read();
  if ((c & 0x0F) == 0) return 0x00;  // no key pressed


  Wire.beginTransmission(0x20);
  Wire.write(0x02);
  Wire.write(B11100000); // check row L1
  Wire.endTransmission();

  Wire.beginTransmission(0x20);  
  Wire.write(0);  // set data pointer
  Wire.endTransmission();
  Wire.requestFrom(0x20, 1);
  c = Wire.read();
  if ((c & 0x1F) > 0x10) {
    if (c==B00010001) c = 0x30; 
    if (c==B00010010) c = 0x31; 
    if (c==B00010100) c = 0x32; 
    if (c==B00011000) c = 0x33;
    delay(100);
    resetPort0();
    return c;
  }

  Wire.beginTransmission(0x20);
  Wire.write(0x02);
  Wire.write(B11010000); // check row L2
  Wire.endTransmission(); 

  Wire.beginTransmission(0x20);  
  Wire.write(0);  // set data pointer
  Wire.endTransmission();
  Wire.requestFrom(0x20, 1);
  c = Wire.read();
  if ((c & 0x2F) > 0x20) {
    if (c==B00100001) c = 0x34; 
    if (c==B00100010) c = 0x35; 
    if (c==B00100100) c = 0x36; 
    if (c==B00101000) c = 0x37;
    delay(100);
    resetPort0();
    return c;
  }


  Wire.beginTransmission(0x20);
  Wire.write(0x02);
  Wire.write(B10110000); // check row L3
  Wire.endTransmission(); 

  Wire.beginTransmission(0x20);  
  Wire.write(0);  // set data pointer
  Wire.endTransmission();
  Wire.requestFrom(0x20, 1);
  c = Wire.read();
  if ((c & 0x4F) > 0x40) {
    if (c==B01000001) c = 0x38; 
    if (c==B01000010) c = 0x39; 
    if (c==B01000100) c = 0x11; 
    if (c==B01001000) c = 0x12;
    delay(100);
    resetPort0();
    return c;
  }


  Wire.beginTransmission(0x20);
  Wire.write(0x02);
  Wire.write(B01110000); // check row L4
  Wire.endTransmission(); 

  Wire.beginTransmission(0x20);  
  Wire.write(0);  // set data pointer
  Wire.endTransmission();
  Wire.requestFrom(0x20, 1);
  c = Wire.read();
  if ((c & 0x8F) > 0x80) {
    if (c==B10000001) c = 0x13; 
    if (c==B10000010) c = 0x14; 
    if (c==B10000100) c = 0x15; 
    if (c==B10001000) c = 0x16;
    delay(100);
    resetPort0();
    return c;
  }
}  // end getKey()

// reset port 0 
void  resetPort0()   {

  Wire.beginTransmission(0x20);  // 
  Wire.write(0x02);  // pointer
  Wire.write(0x00);  // clear bits 4-7 PORT0
  Wire.endTransmission(); 
}

Added September 20, 2013:

Added June 7, 2013:

You Tube Arduino Microcontroller Video Series March 2012: