Not a bad tutorial- [using just the wire library] [ Two's complement ] the 23017 data sheet again http://ww1.microchip.com/downloads/en/DeviceDoc/21952b.pdf
tronixstuff 230x1 tutorial revisited:
Tutorial: Maximising your Arduino’s I/O ports with MCP23017 | tronixstuff.com
Now to examine how to use the IC in our sketches. As you should know by now most I2C devices have several registers that can be addressed. Each address holds one byte of data that determines various options. With the MCP23017 the registers can be ordered in one of two ways – see tables 1.3 and 1.4 on page nine of the data sheet. In our examples we will use the addresses listed on table 1.4. So the first command to use in void setup() is:
Wire.beginTransmission(0x20);
Wire.send(0x12);
Wire.send(0x20); // use table 1.4 addressing
Wire.endTransmission();
The next is to set the I/O ports as inputs or outputs. First we will work with outputs. When the MCP23017 is turned on or reset, it defaults to inputs so we need to change them. So we use:
Wire.beginTransmission(0x20);
Wire.send(0x00); // IODIRA register
Wire.send(0x00); // set all of bank A to outputs
Wire.send(0x00); // set all of bank B to outputs
Wire.endTransmission();
Go back to the data sheet and see table 1.4. Notice how we started with the IODIRA (“I/O direction, bank A”) register at 0×00 and sent two bytes? You can do this without having to separate address the second register. This only works when the registers have sequential addresses, as in this example we wanted a byte to go to 0×00 then 0×01. We sent zero which in binary is 00000000 – each bit refers to one output of the bank and refers to I/O pins 7~0.
So now we are in void loop() or a function of your own creation and want to control some output pins. To control bank A, we use:
Wire.beginTransmission(0x20);
Wire.send(0x12); // address bank A
Wire.send(??); // value to send
Wire.endTransmission();
… replacing ?? with the binary or equivalent hexadecimal or decimal value to send to the register. To calculate the required number, consider each I/O pin from 7 to 0 matches one bit of a binary number – 1 for on, 0 for off. So you can insert a binary number representing the output levels. Or if binary does your head in, convert it to hexadecimal. So for example, you want pins 7 and 1 on. In binary that would be 10000010, in hexadecimal that is 0×82, or 130 decimal. (Using decimals is convenient if you want to display values from an incrementing value or function result).
If you had some LEDs via resistors connected to the outputs, you would have this as a result of sending 0×82:
Now if you want to address all the outputs at once, just send the byte for bank B after bank A. For example, we want bank A to be 11001100 and bank B to be 10001000 – so we send the following:
Wire.beginTransmission(0x20);
Wire.send(0xCC); // address bank A
Wire.send(0x88); // address bank B
Wire.endTransmission();
… with the results as such (bank B on the left, bank A on the right):
You can also just address bank B, if so bank A does not change. Now let’s put all of this output knowledge into a more detailed example. From a hardware perspective we are using a circuit as described above, with the addition of a 560 ohm resistor followed by an LED thence to ground from on each of the sixteen outputs. Here is the sketch (download):
Example 41.1
/*
Example 41.1 - Microchip MCP23017 with Arduino
http://tronixstuff.wordpress.com/tutorials > chapter 41
John Boxall | CC by-sa-nc
*/
// pins 15~17 to GND, I2C bus address is 0x20
#include "Wire.h"
void setup()
{
Wire.begin(); // wake up I2C bus
// setup addressing style
Wire.beginTransmission(0x20);
Wire.send(0x12);
Wire.send(0x20); // use table 1.4 addressing
Wire.endTransmission();
// set I/O pins to outputs
Wire.beginTransmission(0x20);
Wire.send(0x00); // IODIRA register
Wire.send(0x00); // set all of bank A to outputs
Wire.send(0x00); // set all of bank B to outputs
Wire.endTransmission();
}
void binaryCount()
{
for (byte a=0; a<256; a++)
{
Wire.beginTransmission(0x20);
Wire.send(0x12); // GPIOA
Wire.send(a); // bank A
Wire.send(a); // bank B
Wire.endTransmission();
delay(100);
}
}
void loop()
{
binaryCount();
delay(500);
}
And here is the example blinking away:
Although that may have seemed like a simple demonstration, it was created show how the outputs can be used. So now you know how to control the I/O pins set as outputs. Note that you can’t source more than 25 mA of current from each pin, so if switching higher current loads use a transistor and an external power supply and so on.
Now let’s turn the tables and work on using the I/O pins as digital inputs. The MCP23017 I/O pins default to input mode, so all we need to do is set the addressing method as such in void setup()
// setup addressing style
Wire.beginTransmission(0x20);
Wire.send(0x12);
Wire.send(0x20); // use table 1.4 addressing
Wire.endTransmission();