Hi guys,
I have recently been trying to interface to a number of MCP23017 chips and have found that although there is an existing Centipede library for these it doesnt seem to be very easy to set up the addresses for the chips, and it doesn't provide access to the more advanced functions such as pullups etc that the MCP23017 provides easily without lots of I2C reads/writes.
So I have made my own library which does this - it supports everything except Interrupts at present. If it is thought to be useful enough, could people please help me to get it onto its own page at arduino.cc? It also caches register values where possible to save on i2c reads/writes.
Only notes:
The i2c address the constructor expects is just the value of the address pins ie A2, A1, A0 ie a number between 0 and 7 decimal.
You should ensure the Wire library is initiated first ie. Wire.begin() before init-ing the MCP23017.
Call init() before you try to use any of the other functions
The arduino-pin like functions (pinMode, digitalRead, digitalWrite) should work as expected ie you can enable the internal pullup by setting pinMode to INPUT and then digitalWriting that pin HIGH.
Pin 0 is GPB0
Pin 15 is GPA7
I hope it is useful!
Any comments/bugfixes: please message me
Header file:
/* MCP23017 library for Arduino
Copyright (C) 2009 David Pye <davidmpye@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MCP23017_H
#define MCP23017_H
#include "WProgram.h"
#include "Wire.h"
//Register defines from data sheet - we set IOCON.BANK to 0
//as it is easier to manage the registers sequentially.
#define IODIR 0x00
#define IPOL 0x2
#define GPINTEN 0x4
#define DEFVAL 0x6
#define INTCON 0x8
#define IOCON 0x0A
#define GPPU 0x0C
#define INTF 0x0E
#define INTCAP 0x10
#define GPIO 0x12
#define OLAT 0x14
#define I2C_BASE_ADDRESS 0x40
class MCP23017
{
public:
//NB the i2c address here is the value of the A0, A1, A2 pins ONLY
//as the class takes care of its internal base address.
//so i2cAddress should be between 0 and 7
MCP23017(int i2cAddress);
void init();
//These functions provide an 'arduino'-like functionality for accessing
//pin states/pullups etc.
void pinMode(int pin, int mode);
void digitalWrite(int pin, int val);
int digitalRead(int pin);
//These provide a more advanced mapping of the chip functionality
//See the data sheet for more information on what they do
//Returns a word with the current pin states (ie contents of the GPIO register)
word digitalWordRead();
//Allows you to write a word to the GPIO register
void digitalWordWrite(word w);
//Sets up the polarity mask that the MCP23017 supports
//if set to 1, it will flip the actual pin value.
void inputPolarityMask(word mask);
//Sets which pins are inputs or outputs (1 = input, 0 = output) NB Opposite to arduino's
//definition for these
void inputOutputMask(word mask);
//Allows enabling of the internal 100k pullup resisters (1 = enabled, 0 = disabled)
void internalPullupMask(word mask);
//Interrupt functionality (not yet implemented)
private:
void writeRegister(int regaddress, byte val);
void writeRegister(int regaddress, word val);
word readRegister(int regaddress);
//Our actual i2c address
byte _i2cAddress;
//Cached copies of the register vales
word _GPIO, _IODIR, _GPPU;
};
#endif
Main source file:
/* MCP23017 library for Arduino
Copyright (C) 2009 David Pye <davidmpye@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MCP23017.h"
MCP23017::MCP23017(int i2cAddress) {
_i2cAddress = (I2C_BASE_ADDRESS >>1) | (i2cAddress & 0x07);
//Default state is 0 for our pins
_GPIO = 0x0000;
_IODIR = 0x0000;
_GPPU = 0x0000;
}
void MCP23017::init() {
//Set the IOCON.BANK bit to 0 to enable sequential addressing
//IOCON 'default' address is 0x05, but will
//change to our definition of IOCON once this write completes.
writeRegister(0x05, (byte)0x0);
//Our pins default to being outputs by default.
writeRegister(IODIR, (word)_IODIR);
}
void MCP23017::pinMode(int pin, int mode) {
//Arduino defines OUTPUT as 1, but
//MCP23017 uses OUTPUT as 0. (input is 0x1)
mode = !mode;
if (mode) _IODIR |= 1 << pin;
else _IODIR &= ~(1 << pin);
writeRegister(IODIR, (word)_IODIR);
}
int MCP23017::digitalRead(int pin) {
_GPIO = readRegister(GPIO);
if ( _GPIO & (1 << pin)) return HIGH;
else return LOW;
}
void MCP23017::digitalWrite(int pin, int val) {
//If this pin is an INPUT pin, a write here will
//enable the internal pullup
//otherwise, it will set the OUTPUT voltage
//as appropriate.
bool isOutput = !(_IODIR & 1<<pin);
if (isOutput) {
//This is an output pin so just write the value
if (val) _GPIO |= 1 << pin;
else _GPIO &= ~(1 << pin);
writeRegister(GPIO, (word)_GPIO);
}
else {
//This is an input pin, so we need to enable the pullup
if (val) _GPPU |= 1 << pin;
else _GPPU &= ~(1 << pin);
writeRegister(GPPU, (word)_GPPU);
}
}
word MCP23017::digitalWordRead() {
_GPIO = readRegister(GPIO);
return _GPIO;
}
void MCP23017::digitalWordWrite(word w) {
_GPIO = w;
writeRegister(GPIO, (word)_GPIO);
}
void MCP23017::inputPolarityMask(word mask) {
writeRegister(IPOL, mask);
}
void MCP23017::inputOutputMask(word mask) {
_IODIR = mask;
writeRegister(IODIR, (word)_IODIR);
}
void MCP23017::internalPullupMask(word mask) {
_GPPU = mask;
writeRegister(GPPU, (word)_GPPU);
}
//PRIVATE
void MCP23017::writeRegister(int regAddress, byte data) {
Wire.beginTransmission(_i2cAddress);
Wire.send(regAddress);
Wire.send(data);
Wire.endTransmission();
}
void MCP23017::writeRegister(int regAddress, word data) {
Wire.beginTransmission(_i2cAddress);
Wire.send(regAddress);
Wire.send(highByte(data));
Wire.send(lowByte(data));
Wire.endTransmission();
}
word MCP23017::readRegister(int regAddress) {
word returnword = 0x00;
Wire.beginTransmission(_i2cAddress);
Wire.send(regAddress);
Wire.endTransmission();
Wire.requestFrom((int)_i2cAddress, 2);
//Wait for our 2 bytes to become available
while (Wire.available() < 2) { }
//high byte
returnword = Wire.receive() << 8;
//low byte
returnword |= Wire.receive();
return returnword;
}