/*************************************************** This is a library for the MCP23017 i2c port expander These displays use I2C to communicate, 2 pins are required to interface Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution ****************************************************/ #include #ifdef __AVR #include #elif defined(ESP8266) #include #endif #include "Adafruit_MCP23017.h" #if ARDUINO >= 100 #include "Arduino.h" #else #include "WProgram.h" #endif // minihelper to keep Arduino backward compatibility static inline void wiresend(uint8_t x) { #if ARDUINO >= 100 Wire.write((uint8_t) x); #else Wire.send(x); #endif } static inline uint8_t wirerecv(void) { #if ARDUINO >= 100 return Wire.read(); #else return Wire.receive(); #endif } /** * Bit number associated to a give Pin */ uint8_t Adafruit_MCP23017::bitForPin(uint8_t pin){ return pin%8; } /** * Register address, port dependent, for a given PIN */ uint8_t Adafruit_MCP23017::regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr){ return(pin<8) ?portAaddr:portBaddr; } /** * Reads a given register */ uint8_t Adafruit_MCP23017::readRegister(uint8_t addr){ // read the current GPINTEN Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); wiresend(addr); Wire.endTransmission(); Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); return wirerecv(); } /** * Writes a given register */ void Adafruit_MCP23017::writeRegister(uint8_t regAddr, uint8_t regValue){ // Write the register Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); wiresend(regAddr); wiresend(regValue); Wire.endTransmission(); } /** * Helper to update a single bit of an A/B register. * - Reads the current register value * - Writes the new register value */ void Adafruit_MCP23017::updateRegisterBit(uint8_t pin, uint8_t pValue, uint8_t portAaddr, uint8_t portBaddr) { uint8_t regValue; uint8_t regAddr=regForPin(pin,portAaddr,portBaddr); uint8_t bit=bitForPin(pin); regValue = readRegister(regAddr); // set the value for the particular bit bitWrite(regValue,bit,pValue); writeRegister(regAddr,regValue); } //////////////////////////////////////////////////////////////////////////////// /** * Initializes the MCP23017 given its HW selected address, see datasheet for Address selection. */ void Adafruit_MCP23017::begin(uint8_t addr) { if (addr > 7) { addr = 7; } i2caddr = addr; Wire.begin(); // set defaults! // all inputs on port A and B writeRegister(MCP23017_IODIRA,0xff); writeRegister(MCP23017_IODIRB,0xff); } /** * Initializes the default MCP23017, with 000 for the configurable part of the address */ void Adafruit_MCP23017::begin(void) { begin(0); } /** * Sets the pin mode to either INPUT or OUTPUT */ void Adafruit_MCP23017::pinMode(uint8_t p, uint8_t d) { updateRegisterBit(p,(d==INPUT),MCP23017_IODIRA,MCP23017_IODIRB); } /** * Reads all 16 pins (port A and B) into a single 16 bits variable. */ uint16_t Adafruit_MCP23017::readGPIOAB() { uint16_t ba = 0; uint8_t a; // read the current GPIO output latches Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); wiresend(MCP23017_GPIOA); Wire.endTransmission(); Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 2); a = wirerecv(); ba = wirerecv(); ba <<= 8; ba |= a; return ba; } /** * Read a single port, A or B, and return its current 8 bit value. * Parameter b should be 0 for GPIOA, and 1 for GPIOB. */ uint8_t Adafruit_MCP23017::readGPIO(uint8_t b) { // read the current GPIO output latches Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); if (b == 0) wiresend(MCP23017_GPIOA); else { wiresend(MCP23017_GPIOB); } Wire.endTransmission(); Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1); return wirerecv(); } /** * Writes all the pins in one go. This method is very useful if you are implementing a multiplexed matrix and want to get a decent refresh rate. */ void Adafruit_MCP23017::writeGPIOAB(uint16_t ba) { Wire.beginTransmission(MCP23017_ADDRESS | i2caddr); wiresend(MCP23017_GPIOA); wiresend(ba & 0xFF); wiresend(ba >> 8); Wire.endTransmission(); } void Adafruit_MCP23017::digitalWrite(uint8_t pin, uint8_t d) { uint8_t gpio; uint8_t bit=bitForPin(pin); // read the current GPIO output latches uint8_t regAddr=regForPin(pin,MCP23017_OLATA,MCP23017_OLATB); gpio = readRegister(regAddr); // set the pin and direction bitWrite(gpio,bit,d); // write the new GPIO regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB); writeRegister(regAddr,gpio); } void Adafruit_MCP23017::pullUp(uint8_t p, uint8_t d) { updateRegisterBit(p,d,MCP23017_GPPUA,MCP23017_GPPUB); } uint8_t Adafruit_MCP23017::digitalRead(uint8_t pin) { uint8_t bit=bitForPin(pin); uint8_t regAddr=regForPin(pin,MCP23017_GPIOA,MCP23017_GPIOB); return (readRegister(regAddr) >> bit) & 0x1; } /** * Configures the interrupt system. both port A and B are assigned the same configuration. * Mirroring will OR both INTA and INTB pins. * Opendrain will set the INT pin to value or open drain. * polarity will set LOW or HIGH on interrupt. * Default values after Power On Reset are: (false,flase, LOW) * If you are connecting the INTA/B pin to arduino 2/3, you should configure the interupt handling as FALLING with * the default configuration. */ void Adafruit_MCP23017::setupInterrupts(uint8_t mirroring, uint8_t openDrain, uint8_t polarity){ // configure the port A uint8_t ioconfValue=readRegister(MCP23017_IOCONA); bitWrite(ioconfValue,6,mirroring); bitWrite(ioconfValue,2,openDrain); bitWrite(ioconfValue,1,polarity); writeRegister(MCP23017_IOCONA,ioconfValue); // Configure the port B ioconfValue=readRegister(MCP23017_IOCONB); bitWrite(ioconfValue,6,mirroring); bitWrite(ioconfValue,2,openDrain); bitWrite(ioconfValue,1,polarity); writeRegister(MCP23017_IOCONB,ioconfValue); } /** * Set's up a pin for interrupt. uses arduino MODEs: CHANGE, FALLING, RISING. * * Note that the interrupt condition finishes when you read the information about the port / value * that caused the interrupt or you read the port itself. Check the datasheet can be confusing. * */ void Adafruit_MCP23017::setupInterruptPin(uint8_t pin, uint8_t mode) { // set the pin interrupt control (0 means change, 1 means compare against given value); updateRegisterBit(pin,(mode!=CHANGE),MCP23017_INTCONA,MCP23017_INTCONB); // if the mode is not CHANGE, we need to set up a default value, different value triggers interrupt // In a RISING interrupt the default value is 0, interrupt is triggered when the pin goes to 1. // In a FALLING interrupt the default value is 1, interrupt is triggered when pin goes to 0. updateRegisterBit(pin,(mode==FALLING),MCP23017_DEFVALA,MCP23017_DEFVALB); // enable the pin for interrupt updateRegisterBit(pin,HIGH,MCP23017_GPINTENA,MCP23017_GPINTENB); } uint8_t Adafruit_MCP23017::getLastInterruptPin(){ uint8_t intf; // try port A intf=readRegister(MCP23017_INTFA); for(int i=0;i<8;i++) if (bitRead(intf,i)) return i; // try port B intf=readRegister(MCP23017_INTFB); for(int i=0;i<8;i++) if (bitRead(intf,i)) return i+8; return MCP23017_INT_ERR; } uint8_t Adafruit_MCP23017::getLastInterruptPinValue(){ uint8_t intPin=getLastInterruptPin(); if(intPin!=MCP23017_INT_ERR){ uint8_t intcapreg=regForPin(intPin,MCP23017_INTCAPA,MCP23017_INTCAPB); uint8_t bit=bitForPin(intPin); return (readRegister(intcapreg)>>bit) & (0x01); } return MCP23017_INT_ERR; }