PCA9555 Experiments

Github Repository: https://github.com/agenteaty007/PCA9555_Experiments

Gallery: http://albertotech.com/pca9555-experiments-gallery

Overview

I made a breakout board for the PCA9555 IO Expander, which features 16 GPIOs and control via I2C. I used a Teensy 2.0 to test the breakout board and do some basic coding to learn how to do basic tasks like controlling outputs and reading from inputs.

Arduino Code: Blink

You can find the Arduino code for my version of Blink for the PCA9555 on GitHub.

On setup, you'll find the following lines:

Wire.begin(); //set microcontroller as master
Wire.beginTransmission(i2c_address);
Wire.write(0x06); //configure PORT0
Wire.write(0x00); //B00000000, 0-7 output
Wire.endTransmission();
Wire.beginTransmission(i2c_address);
Wire.write(0x02); //output PORT0
Wire.write(io); //default. all off, sourcing.
Wire.endTransmission();

The comments on the code should be pretty self-explanatory, but if I'll try to explain how I wrote this code. Before I start, I bought the PCA9555 IC from Digikey. I'm a developer for Acrobotic, so we'll be carrying a breakout board in store soon. I'll be referencing back to the datasheet of the PCA9555 a lot, so here's a link.

The microcontroller communicates with the PCA9555 via I2C, so we need to use the Wire library. The microcontroller will be the master while the PCA9555 is the slave, so we set the microcontroller as the master with Wire.begin().

To write the Blink code, we only need to controller the outputs of the PCA9555, so we will be writing to the PCA9555. Page 21 on the datasheet describes how to write to the registers (Figure 24 and 25). In short, writing to the register requires <address><command><data>.

The PCA9555 allows for a configurable I2c address by pulling A0, A1, and A2 high (H) or low (L). There's a table on page 19 on the datasheet (Table 2. Address Reference). On my case, I set A0 with a pull-up resistor and ground A1 and A2, so the address was 0x21. To make my code more flexible, I declared the address at the beginning of my code and named the byte variable i2c_address.

Also, in page 19, you'll find a table describing the different commands (Table 3. Command Byte). 0x06 configures PORT 0. PORT 0 consists of the pins 0.0 to 0.7. My breakout board only brings out 8 pins out of the 16 GPIO pins available on the PCA9555, so I only need PORT 0.

The basic lines of code required to control an output:

Wire.beginTransmission(i2c_address);
Wire.write(0x02); //output PORT0
Wire.write(B11111110); //0=sinking, 1-7 sourcing
Wire.endTransmission();
delay(500);

The command 0x02 sets the output PORT 0. For the data, I used a binary form because it's more descriptive and easier to read. In addition, the datasheet suggests on page 25 to use the PCA9555 to sink instead of source power when used as an output, specially LEDs, so my setup had the negative side of the LED connected to the PCA9555.

Arduino Code: Button

For the Button example, I decided to set pin 0.7 to be the input and keep pin 0.0 with the LED to give me feedback.

On setup, we have the following:

Wire.begin(); //set microcontroller as master
Wire.beginTransmission(i2c_address);
Wire.write(0x06); //configure PORT0
Wire.write(0x80); //B10000000, 0-6 output, 7 input
Wire.endTransmission();

These lines are similar to the Blink example, except the data been sent. 0x80, in hexadecimal, is 10000000 in binary, so pin 0.7 is set as an input while the rest are set as outputs. Page 20 of the datasheet includes "Register Descriptions".

On loop, we start with:

//Read inputs from PCA9555
Wire.beginTransmission(0x21);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(0x21,1);
byte input = Wire.read();

0x00 requests to read PORT 0, and then we poll a single byte containing the readings and store it on the byte variable input.

//Is button grounded?
if(input == B01111111)
{
Wire.beginTransmission(i2c_address);
Wire.write(0x02); //output PORT0
Wire.write(B00000000); //0-7 sinking
Wire.endTransmission();
}
else
{
Wire.beginTransmission(i2c_address);
Wire.write(0x02); //output PORT0
Wire.write(B11111111); //0-7 sourcing
Wire.endTransmission();
}

According to the "Register Descriptions" on page 20 of the datasheet, trying to read an output pin will give you back the current flip-flop state instead of an actual reading. The IO pins have an internal 100K pull-up resistor, which you can see on page 15 of the datasheet (Figure 18. Simplified Schematic of P-Port I/Os). All the output pins have been set as 1 by default on the setup procedure. Thus I know I can set my pushbutton to ground, so, when I press it, it will ground the pin. The result of PORT 0 will look like 01111111. When that happens, I decided to just ground all pins to make it easier for me, so the LED on pin 0.1 will turn on because power would be sunk, allowing for the LED to turn on.

Twitter's 140-sweet-characters

Blog/Quick Updates

  • Sep 14, 2014