Electronic – arduino – Problem writing to MCP23017 I/O Expander registers (i2c)

arduinoexpandergpioi2cmicrocontroller

I am creating a basic cable tester device and using I2C to interface with two MCP23017 I/O expander chips.

In my code each MCP23017 slave has its own address and I can communicate with both of them properly via I2C.

The idea is that both ends of a "cable" will be placed in the two sockets. Currently I've programmed it to operate on only 8 wires (Using only GPA7-GPA0 on both chips). Basically the idea is that a logic high pulse will be sent individually on each wire (looping 8 times) from the first MCP23017 chip (pins configured as output and is Slave 1) and received by the second MCP23017 chip (pins configured as input and is Slave 2). Now the LEDs light up just fine indicating that the connection exists however I can't get it to write to the registers on Slave 2.

I am writing to the first chips OLATA and reading from the second chips (Slave 2) GPIOA register. So if OLATA is high on GPA7 (first chip) I wish to read GPIOA GPA7 pin on the second chip. Via these reads I can perform bitwise operations to determine the cable types etc.

My problem is that even though all the LEDS light up as expected nothing is being written on the second (bottom most) I/O expander chip. This is no good for me as I need data writes to the registers to occur so I can read the value on these registers to perform operations on. The relevant I2C code for the I/O expander is provided below, these three functions are each run 8 times in the order shown, one for each GPAx pin:

http://pastebin.com/c3H2pbZp

Now the problem is that nothing is being written to the ReadPin variable, it's empty for all 8 GPAx pins on Slave 2 (bottom most I/O expander chip). Also uint8_t Value is 1<

Any ideas? Please help as this is the only main issue I have that is preventing me from proceeding with writing other program logic.

Thanks!

PS: FYI none of my interrupt pins on both MCP23017 chips are connected to anything and the reset pins are connected to VCC. Also the GPAx pins that are setup as inputs on the second slave don't have resistors connected to them. Just the pin and then the LED to GND.

EDIT: Schematic: (right click to view full size)

http://www.avrfreaks.net/modules/PNphpBB2/files/cable_tester_schematic_107.png

Also yes I know on the Schematic theres no GND connection in VSS for Slave 1 but please note that I have my present setup on a protoboard with all connections inc that VSS connection correctly wired.

Best Answer

Okay, I can see the changes.

As shown now, the LEDs on the receiving IC will block the data high as they are reversed. The LEDs on the transmitting side aren't a good idea either. If you want LEDs you need to put them from the data line to ground through a resistor, not in series with the connection to cable socket.

Just seen your picture - it looks like you have removed the LEDs, good (I was just about to suggest this.. :-) )
So now it looks as if you have direct connections from IC1 to IC2. If this is the case then if the code (looks reasonable at a glance) and IC wiring are correct then it should work.

If you can confirm with a multimeter that the input pins are seeing a high (or low) voltage and the read value is different, then this would confirm the issue is one or the other of the above. Maybe just apply a known voltage directly and see if you can read that okay)

However, if you are reading different values when the pullups are on/off then that would seem to indicate the read is correct. Try reading a direct voltage and post results, I'm just checking the datasheet for the ICs, will add more shortly.

EDIT - about the pullups:

You can use the internal pullups if you don't mind the line "relaxing" to high when not driven (i.e. default state 1) These are often used for interfacing with open drain buses, or for button to ground, etc to save an external pullup.
If you want to have the lines default state low though, (as is the case for you) you need a pulldown to stop the high impedance floating. Since the IC in question doesn't have internal pulldowns, you need to add them externally.

EDIT - Doh! I've just seen the problem...

In your code you set 1 pin at a time to output and all the rest to inputs. This means that if you have internal pullups on, the undriven pins will default to high! When a pin is set to input, it is high impedance, so effectively it's like disconnecting that end of the line, and the weak pullups will pull the receiving end high.
You need to keep all pins as outputs, and just set one high at a time, this will keep all the pins driven - try this with the pullups on, it should work.
If you know the lines will be driven correctly all the time, you don't need the pullups, but it doesn't hurt to keep them on.

Here is the relevant code (in the intialise registers function):

i2c_start_wait(SLAVE_ADDRESS(0x4E)+I2C_WRITE); // Address Slave 1
    i2c_write(0x00);        // Set memory pointer to the IODIRA register (IODIRA address is 0x00 - see Page 9)
    i2c_write(~(Value));    // Set only one pin at a time as an output and everything else as inputs
    i2c_stop(); 

Change it to:

i2c_start_wait(SLAVE_ADDRESS(0x4E)+I2C_WRITE); // Address Slave 1
    i2c_write(0x00);        // Set memory pointer to the IODIRA register (IODIRA address is 0x00 - see Page 9)
    i2c_write(0x00);    // Set all pins as outputs
    i2c_stop();