Interfacing with MCP23008 - Pin Codes

I have a custom arduino type board and I'm trying to interface with the MCP23008 port expander to save pins on the board. This port expander will allow me to control 8 relays based on certain conditions by sending 5v from each pin to base on each npn transistor.

I have done quite a bit of searching and found a few examples of how to interface with the chip and to turn on/off all GPIO pins. I don't see any examples of turning on/off individual pins. I'm pretty new to hex and sending codes to different chips. I've looked at the datasheet but I can't really make sense of what the actual addresses are for each pin or what the code is to make the pin go high/low.

Could someone give me an example of how to turn on/off individual pins on the port expander and a list of the pin addresses? Better yet explain how to know the addresses of each pin and the code to turn the pin high/low. I'd really like to learn how to know so I don't have to ask anymore for other chips. Also, any tutorials on hex?

Thanks

You can't command individual pins to go on and off you can only write whole bytes. Therefore you need to have a variable of the bit pattern you are writing. Then if you want to control an individual bit you simply set or clear the bit in that variable and write the whole thing again. You can do this with the bitSet() and bitClear() functions.

look at:- Arduino Playground - BitMath

This project uses the chips big brother, two 8 bit registers and the code changes individual bits:-
http://www.thebox.myzen.co.uk/Hardware/MIDI_Footsteps.html

Thank you very much! I think it finally just clicked.

You send a binary code to the port expander indicating the values for the 8 pins. When the data sheet indicated bit 7 for pin GP7, it meant that the 7th bit(first number in 8 digit binary) controlled the pin status.

For example:
0x00 = 00000000 - All pins low.
0xC7 = 11000111 - Pins 7,6,2,1,0 High / Pins 5,4,3 Low

As you said, this is the current state value, to switch pins on/off this state value has to be modified/replaced.

Correct?

I do have one more thing I was wondering about. I notice the LCD using one MCP23008 is listed with an address of "0xA7". If I set the second MCP23008 for the relays to "0xA6", how do the chips know which is which?

If you're asking how to set the chips' I2C address, you do that by connecting the address pins (A0,A1,A2) to Vdd or ground. You specify the I2C address in Wire.beginTransmission().

See page 8 of http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf

Yeah I looked over some examples and pin assignments and realized that's how the address was set. The datasheet didn't make it very clear, at least for me anyway.

Thanks for the help.

Yeah, that's not too clear from the MCP datasheets.

Also remember that you give the Wire library 7-bit I2C addresses - adding the low bit (R/W) of the I2C control byte is done internally by the functions!

Yeah, that's not too clear from the MCP datasheets.

You must be joking, on page 5 there is a list of all the pins, it says:-

A2/SO 3 1 3 I/O Hardware address input (MCP23008)/
Serial data output (MCP23S08).
A2 must be biased externally.
A1 4 2 4 I Hardware address input. Must be biased externally.
A0 5 3 5 I Hardware address input. Must be biased externally.

then on page 7 it says:-

? MCP23008 has address pins A2, A1 and A0.

then on page 8 it says:-

The MCP23008 is a slave I2C device that supports 7-bit
slave addressing, with the read/write bit filling out the
control byte. The slave address contains four fixed bits
and three user-defined hardware address bits (pins A2,
A1 and A0). Figure 1-2 shows the control byte format.

With a diagram showing the pins in the bit array of the address.

Short of tattooing it on your forehead in a reverse manor so you can see it in the mirror I don't see how it could be much more clear. :-?

Haha, good point. Well, when you're new to chips that require hardware addressing then it may not be so clear. :wink:

Mike, what I meant is that the sheet is not too clear that you set A0,A1, and A2 by tying them to Vdd or ground. "Must be biased externally" is not too informative, given the lack of any data on the pins' physical characteristics.

For example, you can find schematics floating around where people have connected the address pins through series resistors, or even pull-up or pull-down arrangements.

Imagine the fusillade of requests for more information a newbie would be met with if he posted something like:

I have a pin that needs to be externally biased. How do I do that?

:smiley:

I have one more question if you guys don't mind. I have everything working fine as far as sending specific "codes" to set specific things on and off, but only in a specific pattern without any thought to previous values. IE: Having certain things on already and turning a certain thing off without knowing or changing other bits.

I was looking through the BitMath section and I think I found my answer, I would just like someone to verify my thinking.

In BitMath I found:
x &= ~(1 << n); // forces nth bit of x to be 0. all other bits left alone.
x |= (1 << n); // forces nth bit of x to be 1. all other bits left alone.
x ^= (1 << n); // toggles nth bit of x. all other bits left alone.

Bit 0 is on the right and Bit 7 is on the left in an 8 bit number. So am I correct in the following example?

Say I start with 00110000. Currently Pins 11 & 12 are on.
If I do "x |= (1 << 2);"
I should end up with "00110100" right? Turning Pin 14 on and leaving everything else the way it was before my statement?

The same with: "x &= (1 << 2);"
This should give me "00110000" turning Pin 14 back off?

Basically I will have different things running at different times and I want to trigger a specific item to turn on or off without having to check what other things are on or off.

Also, is there a way to tell if a specific bit is a 0 or a 1?

I should end up with "00110100" right? Turning Pin 14 on and leaving everything else the way it was before my statement?

yes that is right. There is an easer command to use now with bitSet() and bitClear() but I still prefer to use this method.

is there a way to tell if a specific bit is a 0 or a 1?

Yes you AND the variable with an other number (called a mask), the mask has a one in the bit position you want to check. So say you want to check bit 3 in a variable called var then
if(var & 0x08) { // its a 1 } else { // it's a zero }

Yes you AND the variable with an other number (called a mask), the mask has a one in the bit position you want to check. So say you want to check bit 3 in a variable called var then
if(var & 0x08) { // its a 1 } else { // it's a zero }

Ok, so 0x08 is 1000, hence the 1 being in bit 3. This operation is only looking at the 1 and not the 0's? The same as 0x04 "0100" would only look at bit2?

IE: If my byte"var" is 00001011 then "if(var & 0x08)" is going to return true even though bits 0,1 are not 0's?

You can only test one bit at a time with this technique. Think about the result you want to get. If you want to test more than one bit at a time you have three potential results:-

  1. all bits the same
  2. some bits the same
  3. no bits at all

So to test if all the bits are the same make your mask with bits you want to check, say bits 3 and 2 - mask = 0x0C and do
if( (var & mask) == mask ) { // all the bits in the mask match the bits in the variable }

Well that's what I was looking at doing, only polling a single bit. I was just making sure that "if(var & 0x08)" only polled bit 3, no matter if var was actually 1000 or 1100, etc...