From 2de3b66e3254c6cd25dea6420ef64f3ed58d49d5 Mon Sep 17 00:00:00 2001 From: David Zovko Date: Wed, 15 Jul 2020 15:18:14 +0200 Subject: [PATCH] Add MCP23017 libraries right into the Inkplate library --- Adafruit_MCP23017.cpp | 284 ++++++++++++++++++++++++++++++++++++++++++ Adafruit_MCP23017.h | 84 +++++++++++++ 2 files changed, 368 insertions(+) create mode 100755 Adafruit_MCP23017.cpp create mode 100755 Adafruit_MCP23017.h diff --git a/Adafruit_MCP23017.cpp b/Adafruit_MCP23017.cpp new file mode 100755 index 0000000..ee99eb8 --- /dev/null +++ b/Adafruit_MCP23017.cpp @@ -0,0 +1,284 @@ +/*************************************************** + 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; +} + + diff --git a/Adafruit_MCP23017.h b/Adafruit_MCP23017.h new file mode 100755 index 0000000..80d5e6c --- /dev/null +++ b/Adafruit_MCP23017.h @@ -0,0 +1,84 @@ +/*************************************************** + 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 + ****************************************************/ + +#ifndef _Adafruit_MCP23017_H_ +#define _Adafruit_MCP23017_H_ + +// Don't forget the Wire library +class Adafruit_MCP23017 { +public: + void begin(uint8_t addr); + void begin(void); + + void pinMode(uint8_t p, uint8_t d); + void digitalWrite(uint8_t p, uint8_t d); + void pullUp(uint8_t p, uint8_t d); + uint8_t digitalRead(uint8_t p); + + void writeGPIOAB(uint16_t); + uint16_t readGPIOAB(); + uint8_t readGPIO(uint8_t b); + + void setupInterrupts(uint8_t mirroring, uint8_t open, uint8_t polarity); + void setupInterruptPin(uint8_t p, uint8_t mode); + uint8_t getLastInterruptPin(); + uint8_t getLastInterruptPinValue(); + + private: + uint8_t i2caddr; + + uint8_t bitForPin(uint8_t pin); + uint8_t regForPin(uint8_t pin, uint8_t portAaddr, uint8_t portBaddr); + + uint8_t readRegister(uint8_t addr); + void writeRegister(uint8_t addr, uint8_t value); + + /** + * Utility private method to update a register associated with a pin (whether port A/B) + * reads its value, updates the particular bit, and writes its value. + */ + void updateRegisterBit(uint8_t p, uint8_t pValue, uint8_t portAaddr, uint8_t portBaddr); + +}; + +#define MCP23017_ADDRESS 0x20 + +// registers +#define MCP23017_IODIRA 0x00 +#define MCP23017_IPOLA 0x02 +#define MCP23017_GPINTENA 0x04 +#define MCP23017_DEFVALA 0x06 +#define MCP23017_INTCONA 0x08 +#define MCP23017_IOCONA 0x0A +#define MCP23017_GPPUA 0x0C +#define MCP23017_INTFA 0x0E +#define MCP23017_INTCAPA 0x10 +#define MCP23017_GPIOA 0x12 +#define MCP23017_OLATA 0x14 + + +#define MCP23017_IODIRB 0x01 +#define MCP23017_IPOLB 0x03 +#define MCP23017_GPINTENB 0x05 +#define MCP23017_DEFVALB 0x07 +#define MCP23017_INTCONB 0x09 +#define MCP23017_IOCONB 0x0B +#define MCP23017_GPPUB 0x0D +#define MCP23017_INTFB 0x0F +#define MCP23017_INTCAPB 0x11 +#define MCP23017_GPIOB 0x13 +#define MCP23017_OLATB 0x15 + +#define MCP23017_INT_ERR 255 + +#endif