0.6.3 MCP23017_RT

This commit is contained in:
Rob Tillaart 2024-05-27 10:18:44 +02:00
parent 15fb91d42c
commit 52de8f6926
12 changed files with 545 additions and 125 deletions

View File

@ -22,6 +22,6 @@ compile:
# - leonardo
- m4
- esp32
# - esp8266
- esp8266
# - mega2560
- rpipico
- rpipico

View File

@ -6,12 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.6.3] 2024-05-25
- sync MCP23S17, add several interrupt functions (experimental)
- add **MCP23x17_registers.h**
- add **readReg16()** and **writeReg16()** optimization
- update readme.md
- update keywords.txt
## [0.6.2] 2024-03-02
- Fix #29, add parameter to **begin(bool pullup)**
- remove DATE field from examples as it adds no value.
- update GitHub/actions to version v4 in workflows.
## [0.6.1] 2024-01-05
- Fix URL in examples
- minor edits

View File

@ -1,7 +1,7 @@
//
// FILE: MCP23017.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.6.2
// VERSION: 0.6.3
// PURPOSE: Arduino library for I2C MCP23017 16 channel port expander
// DATE: 2019-10-12
// URL: https://github.com/RobTillaart/MCP23017_RT
@ -12,31 +12,6 @@
#include "MCP23017.h"
// Registers // DESCRIPTION DATASHEET
#define MCP23017_DDR_A 0x00 // Data Direction Register A P18
#define MCP23017_DDR_B 0x01 // Data Direction Register B P18
#define MCP23017_POL_A 0x02 // Input Polarity A P18
#define MCP23017_POL_B 0x03 // Input Polarity B P18
#define MCP23017_GPINTEN_A 0x04 // NOT USED interrupt enable P19
#define MCP23017_GPINTEN_B 0x05 // NOT USED
#define MCP23017_DEFVAL_A 0x06 // NOT USED interrupt def P19
#define MCP23017_DEFVAL_B 0x07 // NOT USED
#define MCP23017_INTCON_A 0x08 // NOT USED interrupt control P20
#define MCP23017_INTCON_B 0x09 // NOT USED
#define MCP23017_IOCR 0x0A // IO control register P20
#define MCP23017_IOCR2 0x0B // NOT USED
#define MCP23017_PUR_A 0x0C // Pull Up Resistors A P22
#define MCP23017_PUR_B 0x0D // Pull Up Resistors A P22
#define MCP23017_INTF_A 0x0E // NOT USED interrupt flag P22
#define MCP23017_INTF_B 0x0F // NOT USED
#define MCP23017_INTCAP_A 0x10 // NOT USED interrupt capture P23
#define MCP23017_INTCAP_B 0x11 // NOT USED
#define MCP23017_GPIO_A 0x12 // General Purpose IO A P23
#define MCP23017_GPIO_B 0x13 // General Purpose IO B P23
#define MCP23017_OLAT_A 0x14 // NOT USED output latch P24
#define MCP23017_OLAT_B 0x15 // NOT USED
MCP23017::MCP23017(uint8_t address, TwoWire *wire)
{
_address = address;
@ -49,13 +24,18 @@ bool MCP23017::begin(bool pullup)
{
// check connected
if (! isConnected()) return false;
// disable address increment (datasheet)
if (! writeReg(MCP23017_IOCR, 0b00100000)) return false;
// disable address increment - datasheet P20
// SEQOP: Sequential Operation mode bit
// 1 = Sequential operation disabled, address pointer does not increment.
// 0 = Sequential operation enabled, address pointer increments.
// if (! writeReg(MCP23017_IOCR, MCP23017_IOCR_SEQOP)) return false;
if (pullup)
{
// Force INPUT_PULLUP
if (! writeReg(MCP23017_PUR_A, 0xFF)) return false;
if (! writeReg(MCP23017_PUR_B, 0xFF)) return false;
if (! writeReg(MCP23x17_PUR_A, 0xFF)) return false; // 0xFF == all UP
if (! writeReg(MCP23x17_PUR_B, 0xFF)) return false; // 0xFF == all UP
}
return true;
}
@ -80,7 +60,10 @@ uint8_t MCP23017::getAddress()
}
///////////////////////////////////////////////////////////////////
//
// single pin interface
//
// pin = 0..15
// mode = INPUT, OUTPUT, INPUT_PULLUP (= same as INPUT)
bool MCP23017::pinMode1(uint8_t pin, uint8_t mode)
@ -96,10 +79,10 @@ bool MCP23017::pinMode1(uint8_t pin, uint8_t mode)
return false;
}
uint8_t dataDirectionRegister = MCP23017_DDR_A;
uint8_t dataDirectionRegister = MCP23x17_DDR_A;
if (pin > 7)
{
dataDirectionRegister = MCP23017_DDR_B;
dataDirectionRegister = MCP23x17_DDR_B;
pin -= 8;
}
uint8_t val = readReg(dataDirectionRegister);
@ -136,10 +119,10 @@ bool MCP23017::write1(uint8_t pin, uint8_t value)
_error = MCP23017_PIN_ERROR;
return false;
}
uint8_t IOR = MCP23017_GPIO_A;
uint8_t IOR = MCP23x17_GPIO_A;
if (pin > 7)
{
IOR = MCP23017_GPIO_B;
IOR = MCP23x17_GPIO_B;
pin -= 8;
}
@ -159,6 +142,7 @@ bool MCP23017::write1(uint8_t pin, uint8_t value)
{
val &= ~mask;
}
// only write when changed.
if (pre != val)
{
writeReg(IOR, val);
@ -178,10 +162,10 @@ uint8_t MCP23017::read1(uint8_t pin)
_error = MCP23017_PIN_ERROR;
return MCP23017_INVALID_READ;
}
uint8_t IOR = MCP23017_GPIO_A;
uint8_t IOR = MCP23x17_GPIO_A;
if (pin > 7)
{
IOR = MCP23017_GPIO_B;
IOR = MCP23x17_GPIO_B;
pin -= 8;
}
@ -196,8 +180,8 @@ uint8_t MCP23017::read1(uint8_t pin)
}
// pin = 0..15
// reverse = true or false
// pin = 0..15
// reversed = true or false
bool MCP23017::setPolarity(uint8_t pin, bool reversed)
{
if (pin > 15)
@ -205,10 +189,10 @@ bool MCP23017::setPolarity(uint8_t pin, bool reversed)
_error = MCP23017_PIN_ERROR;
return false;
}
uint8_t inputPolarityRegister = MCP23017_POL_A;
uint8_t inputPolarityRegister = MCP23x17_POL_A;
if (pin > 7)
{
inputPolarityRegister = MCP23017_POL_B;
inputPolarityRegister = MCP23x17_POL_B;
pin -= 8;
}
uint8_t val = readReg(inputPolarityRegister);
@ -241,10 +225,10 @@ bool MCP23017::getPolarity(uint8_t pin, bool &reversed)
_error = MCP23017_PIN_ERROR;
return false;
}
uint8_t inputPolarityRegister = MCP23017_POL_A;
uint8_t inputPolarityRegister = MCP23x17_POL_A;
if (pin > 7)
{
inputPolarityRegister = MCP23017_POL_B;
inputPolarityRegister = MCP23x17_POL_B;
pin -= 8;
}
uint8_t val = readReg(inputPolarityRegister);
@ -258,8 +242,8 @@ bool MCP23017::getPolarity(uint8_t pin, bool &reversed)
}
// pin = 0..15
// reverse = true or false
// pin = 0..15
// pullup = true or false
bool MCP23017::setPullup(uint8_t pin, bool pullup)
{
if (pin > 15)
@ -267,10 +251,10 @@ bool MCP23017::setPullup(uint8_t pin, bool pullup)
_error = MCP23017_PIN_ERROR;
return false;
}
uint8_t inputPullupRegister = MCP23017_PUR_A;
uint8_t inputPullupRegister = MCP23x17_PUR_A;
if (pin > 7)
{
inputPullupRegister = MCP23017_PUR_B;
inputPullupRegister = MCP23x17_PUR_B;
pin -= 8;
}
uint8_t val = readReg(inputPullupRegister);
@ -303,10 +287,10 @@ bool MCP23017::getPullup(uint8_t pin, bool &pullup)
_error = MCP23017_PIN_ERROR;
return false;
}
uint8_t inputPullupRegister = MCP23017_PUR_A;
uint8_t inputPullupRegister = MCP23x17_PUR_A;
if (pin > 7)
{
inputPullupRegister = MCP23017_PUR_B;
inputPullupRegister = MCP23x17_PUR_B;
pin -= 8;
}
uint8_t val = readReg(inputPullupRegister);
@ -320,9 +304,10 @@ bool MCP23017::getPullup(uint8_t pin, bool &pullup)
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
//
// 8 pins interface
//
// whole register at once
// port = 0..1
// value = 0..0xFF bit pattern
@ -333,22 +318,23 @@ bool MCP23017::pinMode8(uint8_t port, uint8_t value)
_error = MCP23017_PORT_ERROR;
return false;
}
if (port == 0) writeReg(MCP23017_DDR_A, value);
if (port == 1) writeReg(MCP23017_DDR_B, value);
if (port == 0) writeReg(MCP23x17_DDR_A, value);
if (port == 1) writeReg(MCP23x17_DDR_B, value);
_error = MCP23017_OK;
return true;
}
bool MCP23017::write8(uint8_t port, uint8_t value) // port = 0..1
// port = 0..1
bool MCP23017::write8(uint8_t port, uint8_t value)
{
if (port > 1)
{
_error = MCP23017_PORT_ERROR;
return false;
}
if (port == 0) writeReg(MCP23017_GPIO_A, value);
if (port == 1) writeReg(MCP23017_GPIO_B, value);
if (port == 0) writeReg(MCP23x17_GPIO_A, value);
if (port == 1) writeReg(MCP23x17_GPIO_B, value);
_error = MCP23017_OK;
return true;
}
@ -362,8 +348,8 @@ int MCP23017::read8(uint8_t port)
return MCP23017_INVALID_READ;
}
_error = MCP23017_OK;
if (port == 0) return readReg(MCP23017_GPIO_A);
return readReg(MCP23017_GPIO_B); // port == 1
if (port == 0) return readReg(MCP23x17_GPIO_A);
return readReg(MCP23x17_GPIO_B); // port == 1
}
@ -376,8 +362,8 @@ bool MCP23017::setPolarity8(uint8_t port, uint8_t mask)
_error = MCP23017_PORT_ERROR;
return false;
}
if (port == 0) writeReg(MCP23017_POL_A, mask);
if (port == 1) writeReg(MCP23017_POL_B, mask);
if (port == 0) writeReg(MCP23x17_POL_A, mask);
if (port == 1) writeReg(MCP23x17_POL_B, mask);
if (_error != MCP23017_OK)
{
return false;
@ -393,8 +379,8 @@ bool MCP23017::getPolarity8(uint8_t port, uint8_t &mask)
_error = MCP23017_PORT_ERROR;
return false;
}
if (port == 0) mask = readReg(MCP23017_POL_A);
if (port == 1) mask = readReg(MCP23017_POL_B);
if (port == 0) mask = readReg(MCP23x17_POL_A);
if (port == 1) mask = readReg(MCP23x17_POL_B);
if (_error != MCP23017_OK)
{
return false;
@ -412,8 +398,8 @@ bool MCP23017::setPullup8(uint8_t port, uint8_t mask)
_error = MCP23017_PORT_ERROR;
return false;
}
if (port == 0) writeReg(MCP23017_PUR_A, mask);
if (port == 1) writeReg(MCP23017_PUR_B, mask);
if (port == 0) writeReg(MCP23x17_PUR_A, mask);
if (port == 1) writeReg(MCP23x17_PUR_B, mask);
if (_error != MCP23017_OK)
{
return false;
@ -429,8 +415,8 @@ bool MCP23017::getPullup8(uint8_t port, uint8_t &mask)
_error = MCP23017_PORT_ERROR;
return false;
}
if (port == 0) mask = readReg(MCP23017_PUR_A);
if (port == 1) mask = readReg(MCP23017_PUR_B);
if (port == 0) mask = readReg(MCP23x17_PUR_A);
if (port == 1) mask = readReg(MCP23x17_PUR_B);
if (_error != MCP23017_OK)
{
return false;
@ -439,15 +425,15 @@ bool MCP23017::getPullup8(uint8_t port, uint8_t &mask)
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
//
// 16 pins interface
//
// two register at once
// value = 0x0000..0xFFFF bit pattern
bool MCP23017::pinMode16(uint16_t value)
{
writeReg(MCP23017_DDR_A, value >> 8);
writeReg(MCP23017_DDR_B, value & 0xFF);
writeReg16(MCP23x17_DDR_A, value);
_error = MCP23017_OK;
return true;
}
@ -456,8 +442,7 @@ bool MCP23017::pinMode16(uint16_t value)
// value = 0x0000..0xFFFF bit pattern
bool MCP23017::write16(uint16_t value)
{
writeReg(MCP23017_GPIO_A, value >> 8);
writeReg(MCP23017_GPIO_B, value & 0xFF);
writeReg16(MCP23x17_GPIO_A, value);
_error = MCP23017_OK;
return true;
}
@ -467,9 +452,7 @@ bool MCP23017::write16(uint16_t value)
uint16_t MCP23017::read16()
{
_error = MCP23017_OK;
uint16_t value = readReg(MCP23017_GPIO_A);
value <<= 8;
value += readReg(MCP23017_GPIO_B);
uint16_t value = readReg16(MCP23x17_GPIO_A);
return value;
}
@ -477,8 +460,7 @@ uint16_t MCP23017::read16()
// mask = 0x0000..0xFFFF bit pattern
bool MCP23017::setPolarity16(uint16_t mask)
{
writeReg(MCP23017_POL_A, mask >> 8);
writeReg(MCP23017_POL_B, mask & 0xFF);
writeReg16(MCP23x17_POL_A, mask);
if (_error != MCP23017_OK)
{
return false;
@ -490,9 +472,7 @@ bool MCP23017::setPolarity16(uint16_t mask)
// mask = 0x0000..0xFFFF bit pattern
bool MCP23017::getPolarity16(uint16_t &mask)
{
mask = readReg(MCP23017_POL_A);
mask <<= 8;
mask += readReg(MCP23017_POL_B);
mask = readReg16(MCP23x17_POL_A);
if (_error != MCP23017_OK)
{
return false;
@ -504,8 +484,7 @@ bool MCP23017::getPolarity16(uint16_t &mask)
// mask = 0x0000..0xFFFF bit pattern
bool MCP23017::setPullup16(uint16_t mask)
{
writeReg(MCP23017_PUR_A, mask >> 8);
writeReg(MCP23017_PUR_B, mask & 0xFF);
writeReg16(MCP23x17_PUR_A, mask);
if (_error != MCP23017_OK)
{
return false;
@ -517,9 +496,7 @@ bool MCP23017::setPullup16(uint16_t mask)
// mask = 0x0000..0xFFFF bit pattern
bool MCP23017::getPullup16(uint16_t &mask)
{
mask = readReg(MCP23017_PUR_A);
mask <<= 8;
mask += readReg(MCP23017_PUR_B);
mask = readReg16(MCP23x17_PUR_A);
if (_error != MCP23017_OK)
{
return false;
@ -528,21 +505,198 @@ bool MCP23017::getPullup16(uint16_t &mask)
}
///////////////////////////////////////////////////
//
// INTERRUPTS (experimental, see MCP23S17 - #40)
//
// TODO, catch writeReg errors
// TODO, MCP23x17_INT_MODE_ERROR?
// TODO, if register not changed no need to update?
// TODO, 8 bits optimize? more code vs speed?
//
// pin = 0..15, mode = { RISING, FALLING, CHANGE }
bool MCP23017::enableInterrupt(uint8_t pin, uint8_t mode)
{
if (pin > 15)
{
_error = MCP23017_PIN_ERROR;
return false;
}
// right mode
uint16_t intcon = readReg16(MCP23x17_INTCON_A);
if (mode == CHANGE)
{
// compare to previous value.
intcon &= ~(1 << pin);
}
else
{
uint16_t defval = readReg16(MCP23x17_DEFVAL_A);
if (mode == RISING)
{
intcon |= (1 << pin);
defval &= ~(1 << pin); // RISING == compare to 0
}
else if (mode == FALLING)
{
intcon |= (1 << pin);
defval |= ~(1 << pin); // FALLING == compare to 1
}
writeReg16(MCP23x17_DEFVAL_A, defval);
}
writeReg16(MCP23x17_INTCON_A, intcon);
// enable interrupt
uint16_t value = readReg16(MCP23x17_GPINTEN_A);
value |= (1 << pin);
return writeReg16(MCP23x17_GPINTEN_A, value);
}
bool MCP23017::disableInterrupt(uint8_t pin)
{
if (pin > 15)
{
_error = MCP23017_PIN_ERROR;
return false;
}
// disable interrupt
uint16_t value = readReg16(MCP23x17_GPINTEN_A);
value &= ~(1 << pin);
return writeReg16(MCP23x17_GPINTEN_A, value);
}
bool MCP23017::enableInterrupt16(uint16_t mask, uint8_t mode)
{
uint16_t intcon = 0, defval = 0;
// right mode
if (mode == CHANGE)
{
// compare to previous value.
intcon = ~mask;
}
else
{
if (mode == RISING)
{
intcon = mask;
defval = ~mask; // RISING == compare to 0
}
else if (mode == FALLING)
{
intcon = mask;
defval = mask; // FALLING == compare to 1
}
writeReg16(MCP23x17_DEFVAL_A, defval);
}
writeReg16(MCP23x17_INTCON_A, intcon);
// enable the mask
writeReg16(MCP23x17_GPINTEN_A, mask);
return true;
}
bool MCP23017::disableInterrupt16(uint16_t mask)
{
return writeReg16(MCP23x17_GPINTEN_A, ~mask);
}
// which pins caused the INT?
uint16_t MCP23017::getInterruptFlagRegister()
{
return readReg16(MCP23x17_INTF_A);
}
uint16_t MCP23017::getInterruptCaptureRegister()
{
return readReg16(MCP23x17_INTCAP_A);
}
// polarity: 0 = LOW, 1 = HIGH, 2 = NONE/ODR
bool MCP23017::setInterruptPolarity(uint8_t polarity)
{
if (polarity > 2) return false;
uint8_t reg = readReg(MCP23x17_IOCR);
reg &= ~(MCP23x17_IOCR_ODR | MCP23x17_IOCR_INTPOL);
// LOW is default set.
if (polarity == 2) reg |= MCP23x17_IOCR_ODR;
if (polarity == 1) reg |= MCP23x17_IOCR_INTPOL;
return writeReg(MCP23x17_IOCR, reg);
}
uint8_t MCP23017::getInterruptPolarity()
{
uint8_t reg = readReg(MCP23x17_IOCR);
if (reg & MCP23x17_IOCR_ODR) return 2;
if (reg & MCP23x17_IOCR_INTPOL) return 1;
return 0;
}
bool MCP23017::mirrorInterrupts(bool on)
{
if (on) return enableControlRegister(MCP23x17_IOCR_MIRROR);
return disableControlRegister(MCP23x17_IOCR_MIRROR);
}
bool MCP23017::isMirroredInterrupts()
{
return (readReg(MCP23x17_IOCR) & MCP23x17_IOCR_MIRROR) > 0;
}
/////////////////////////////////////////////
//
// MISC
//
int MCP23017::lastError()
{
int e = _error;
_error = MCP23017_OK; // reset error after read.
_error = MCP23017_OK; // reset error after read.
return e;
}
bool MCP23017::enableControlRegister(uint8_t mask)
{
uint8_t reg = readReg(MCP23x17_IOCR);
reg |= mask;
return writeReg(MCP23x17_IOCR, reg);
}
bool MCP23017::disableControlRegister(uint8_t mask)
{
uint8_t reg = readReg(MCP23x17_IOCR);
reg &= ~mask;
return writeReg(MCP23x17_IOCR, reg);
}
////////////////////////////////////////////////////
//
// PRIVATE
// PROTECTED
//
bool MCP23017::writeReg(uint8_t reg, uint8_t value)
{
_error = MCP23017_OK;
if (reg > MCP23x17_OLAT_B)
{
_error = MCP23017_REGISTER_ERROR;
return false;
}
// start write
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value);
@ -551,13 +705,23 @@ bool MCP23017::writeReg(uint8_t reg, uint8_t value)
_error = MCP23017_I2C_ERROR;
return false;
}
_error = MCP23017_OK;
return true;
}
uint8_t MCP23017::readReg(uint8_t reg)
{
uint8_t rv = 0;
_error = MCP23017_OK;
if (reg > MCP23x17_OLAT_B)
{
_error = MCP23017_REGISTER_ERROR;
return 0;
}
// start read
_wire->beginTransmission(_address);
_wire->write(reg);
if (_wire->endTransmission() != 0)
@ -565,17 +729,73 @@ uint8_t MCP23017::readReg(uint8_t reg)
_error = MCP23017_I2C_ERROR;
return 0;
}
else
{
_error = MCP23017_OK;
}
uint8_t n = _wire->requestFrom(_address, (uint8_t)1);
if (n != 1)
{
_error = MCP23017_I2C_ERROR;
return 0;
}
return _wire->read();
rv = _wire->read();
return rv;
}
// writes HIGH byte first, LOW byte last
bool MCP23017::writeReg16(uint8_t reg, uint16_t value)
{
_error = MCP23017_OK;
if (reg > MCP23x17_OLAT_B)
{
_error = MCP23017_REGISTER_ERROR;
return false;
}
// start write
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value >> 8);
_wire->write(value && 0xFF);
if (_wire->endTransmission() != 0)
{
_error = MCP23017_I2C_ERROR;
return false;
}
return true;
}
uint16_t MCP23017::readReg16(uint8_t reg)
{
uint16_t rv = 0;
_error = MCP23017_OK;
if (reg > MCP23x17_OLAT_B)
{
_error = MCP23017_REGISTER_ERROR;
return 0;
}
// start read
_wire->beginTransmission(_address);
_wire->write(reg);
if (_wire->endTransmission() != 0)
{
_error = MCP23017_I2C_ERROR;
return 0;
}
uint8_t n = _wire->requestFrom(_address, (uint8_t)2);
if (n != 2)
{
_error = MCP23017_I2C_ERROR;
return 0;
}
rv = _wire->read() << 8;
rv += _wire->read();
return rv;
}

View File

@ -2,7 +2,7 @@
//
// FILE: MCP23017.h
// AUTHOR: Rob Tillaart
// VERSION: 0.6.2
// VERSION: 0.6.3
// PURPOSE: Arduino library for I2C MCP23017 16 channel port expander
// DATE: 2019-10-12
// URL: https://github.com/RobTillaart/MCP23017_RT
@ -12,18 +12,18 @@
#include "Arduino.h"
#include "Wire.h"
#include "MCP23x17_registers.h"
#define MCP23017_LIB_VERSION (F("0.6.2"))
#define MCP23017_LIB_VERSION (F("0.6.3"))
#define MCP23017_OK 0x00
#define MCP23017_PIN_ERROR 0x81
#define MCP23017_I2C_ERROR 0x82
#define MCP23017_VALUE_ERROR 0x83
#define MCP23017_PORT_ERROR 0x84
#define MCP23017_INVALID_READ -100
#define MCP23017_REGISTER_ERROR 0xFF
#define MCP23017_INVALID_READ 0xFF
class MCP23017
@ -37,7 +37,7 @@ public:
// single pin interface
// mode = INPUT, OUTPUT or INPUT_PULLUP (==INPUT)
// mode: 0 = OUTPUT, 1 = INPUT, 1 = INPUT_PULLUP (==INPUT)
bool pinMode1(uint8_t pin, uint8_t mode);
bool write1(uint8_t pin, uint8_t value);
uint8_t read1(uint8_t pin);
@ -72,11 +72,43 @@ public:
bool setPullup16(uint16_t mask);
bool getPullup16(uint16_t &mask);
// INTERRUPTS (experimental)
// pin = 0..15, mode = { RISING, FALLING, CHANGE }
bool enableInterrupt(uint8_t pin, uint8_t mode);
bool disableInterrupt(uint8_t pin);
// mask = 0x0000..0xFFFF (overrides all earlier settings.
bool enableInterrupt16(uint16_t mask, uint8_t mode);
bool disableInterrupt16(uint16_t mask);
// which pins caused the INT?
uint16_t getInterruptFlagRegister();
uint16_t getInterruptCaptureRegister();
// polarity: 0 = LOW, 1 = HIGH, 2 = NONE/ODR
bool setInterruptPolarity(uint8_t polarity);
uint8_t getInterruptPolarity();
// merge INTA and INTB
bool mirrorInterrupts(bool on);
bool isMirroredInterrupts();
// debugging
int lastError();
// set/clear IOCR bit fields
bool enableControlRegister(uint8_t mask);
bool disableControlRegister(uint8_t mask);
protected:
// access to low level registers (just make these functions public).
// USE WITH CARE !!!
bool writeReg(uint8_t reg, uint8_t value);
uint8_t readReg(uint8_t reg);
bool writeReg16(uint8_t reg, uint16_t value);
uint16_t readReg16(uint8_t reg);
uint8_t _address;
TwoWire* _wire;

View File

@ -0,0 +1,47 @@
#pragma once
//
// FILE: MCP23x17_registers.h
// AUTHOR: Rob Tillaart
// PURPOSE: MCP23x17 register file
// URL: https://github.com/RobTillaart/MCP23017_RT
// URL: https://github.com/RobTillaart/MCP23S17
// REGISTERS // description datasheet
#define MCP23x17_DDR_A 0x00 // Data Direction Register A P18
#define MCP23x17_DDR_B 0x01 // Data Direction Register B P18
#define MCP23x17_POL_A 0x02 // Input Polarity A P18
#define MCP23x17_POL_B 0x03 // Input Polarity B P18
#define MCP23x17_GPINTEN_A 0x04 // Interrupt enable P19
#define MCP23x17_GPINTEN_B 0x05 // Interrupt enable P19
#define MCP23x17_DEFVAL_A 0x06 // Interrupt default P19
#define MCP23x17_DEFVAL_B 0x07 // Interrupt default P19
#define MCP23x17_INTCON_A 0x08 // Interrupt control register P20
#define MCP23x17_INTCON_B 0x09 // Interrupt control register P20
#define MCP23x17_IOCR 0x0A // IO control register P20
#define MCP23x17_IOCR2 0x0B // NOT USED
#define MCP23x17_PUR_A 0x0C // Pull Up Resistors A P22
#define MCP23x17_PUR_B 0x0D // Pull Up Resistors A P22
#define MCP23x17_INTF_A 0x0E // Interrupt flag register P22
#define MCP23x17_INTF_B 0x0F // Interrupt flag register P22
#define MCP23x17_INTCAP_A 0x10 // Interrupt capture register P23
#define MCP23x17_INTCAP_B 0x11 // Interrupt capture register P23
#define MCP23x17_GPIO_A 0x12 // General Purpose IO A P23
#define MCP23x17_GPIO_B 0x13 // General Purpose IO B P23
#define MCP23x17_OLAT_A 0x14 // NOT USED output latch P24
#define MCP23x17_OLAT_B 0x15 // NOT USED
// IOCR = IO CONTROL REGISTER bit masks - details datasheet P20
#define MCP23x17_IOCR_BANK 0x80 // Controls how the registers are addressed.
#define MCP23x17_IOCR_MIRROR 0x40 // INT Pins Mirror bit.
#define MCP23x17_IOCR_SEQOP 0x20 // Sequential Operation mode bit.
#define MCP23x17_IOCR_DISSLW 0x10 // Slew Rate control bit for SDA output.
#define MCP23x17_IOCR_HAEN 0x08 // Hardware Address Enable bit (MCP23S17 only).
#define MCP23x17_IOCR_ODR 0x04 // Configures the INT pin as an open-drain output.
#define MCP23x17_IOCR_INTPOL 0x02 // This bit sets the polarity of the INT output pin.
#define MCP23x17_IOCR_NI 0x01 // Not implemented.
// -- END OF FILE --

View File

@ -18,7 +18,7 @@ Arduino library for MCP23017 16 channel I2C port expander.
This library gives easy control over the 16 pins of a (I2C) MCP23017 chip.
This IC is strongly related tot the MCP23S17 SPI port expander - https://github.com/RobTillaart/MCP23S17
This IC is strongly related to the MCP23S17 SPI port expander - https://github.com/RobTillaart/MCP23S17
Programming Interface is kept the same as much as possible.
The **write1(pin, value)** is optimized.
@ -44,7 +44,7 @@ Some details see:
Note: the library has no provisions (yet) for detecting DEV D chips or handle them in a special way.
There is an idea to implement a derived class MCP23017_REVD that provides automatic support.
However low prio.
However low priority.
Note that the MCP23S017 (SPI version) does not have this "feature" for GPA7 and GPB7.
@ -87,6 +87,24 @@ Supports 100kHz, 400kHz and 1.7MHz
TODO - add performance data
#### I2C multiplexing
Sometimes you need to control more devices than possible with the default
address range the device provides.
This is possible with an I2C multiplexer e.g. TCA9548 which creates up
to eight channels (think of it as I2C subnets) which can use the complete
address range of the device.
Drawback of using a multiplexer is that it takes more administration in
your code e.g. which device is on which channel.
This will slow down the access, which must be taken into account when
deciding which devices are on which channel.
Also note that switching between channels will slow down other devices
too if they are behind the multiplexer.
- https://github.com/RobTillaart/TCA9548
#### Related
16 bit port expanders
@ -119,7 +137,7 @@ Can be overruled with Wire0..WireN.
Default sets the pins to INPUT PULLUP.
Returns false if not connected or a register could not be set.
- **bool isConnected()** returns true if connected, false otherwise.
- **uint8_t getADdress()** returns address set in the constructor.
- **uint8_t getAddress()** returns the address set in the constructor.
### Single pin interface
@ -142,8 +160,7 @@ Please note REVD remarks at top.
- **bool pinMode8(uint8_t port, uint8_t value)** port = 0..1, value = 0..255. Returns true if successful.
- **bool write8(uint8_t port, uint8_t value)** port = 0..1, value = 0..255. Returns true if successful.
- **uint8_t read8(uint8_t port)** port = 0..1, reads 8 pins into one byte.
- **bool setPolarity8(uint8_t port, uint8_t mask)** port = 0..1, sets polarity
for 8 channels at once.
- **bool setPolarity8(uint8_t port, uint8_t mask)** port = 0..1, sets polarity for 8 channels at once.
Returns true if successful.
- **bool getPolarity8(uint8_t port, uint8_t &mask)** port = 0..1, reads polarity of 8 channels at once.
Returns true if successful.
@ -157,8 +174,8 @@ Returns true if successful.
Please note REVD remarks at top.
- **bool pinMode16(uint16_t value)** value = 0..0xFFFF. Returns true if successful.
- **bool write16(uint16_t value)** value = 0..0xFFFF. Returns true if successful.
- **bool pinMode16(uint16_t value)** value = 0..0xFFFF, returns true if successful.
- **bool write16(uint16_t value)** value = 0..0xFFFF, returns true if successful.
- **uint16_t read16()** reads 16 pins into an uint16_t.
- **bool setPolarity16(uint16_t mask)** sets polarity for 16 channels.
Returns true if successful.
@ -169,6 +186,69 @@ Returns true if successful.
- **bool getPullup16(uint16_t &mask)** reads pull-up for 16 channels.
Returns true if successful.
The reading and writing to registers have been performance optimized for the 16 bit interface.
If there are problems please open an issue.
### Interrupts (experimental 0.6.3)
Read the datasheet for the details, page 24,25.
Note: Error handling is limited.
pin = 0..15
mode = { RISING, FALLING, CHANGE }
- **bool enableInterrupt(uint8_t pin, uint8_t mode)**
Returns true if successful.
Returns MCP23017_PIN_ERROR if pin > 15.
- **bool disableInterrupt(uint8_t pin)**
Returns true if successful.
Returns MCP23017_PIN_ERROR if pin > 15.
16 pin interface, overrides all earlier settings.
Sets all pins to the same interrupt mode { RISING, FALLING, CHANGE }.
- **bool enableInterrupt16(uint16_t mask, uint8_t mode)** mask = 0x0000..0xFFFF.
- **bool disableInterrupt16(uint16_t mask)**
Determine which pins caused the Interrupt. (datasheet).
- **uint16_t getInterruptFlagRegister()** Reads all 16 pins.
- **uint16_t getInterruptCaptureRegister()** Reads all 16 pins.
Is used to detect if multiple pins triggered an interrupt.
- **bool setInterruptPolarity(uint8_t ipol)** polarity: 0 = LOW, 1 = HIGH, 2 = NONE/ODR
- **uint8_t getInterruptPolarity()** return set value.
Merge INTA and INTB into one signal so only one line handles all interrupts.
This reduces the number of interrupt lines to handle, however one has
to read more registers to find the changed ones.
- **bool mirrorInterrupts(bool on)** enables / disables mirror mode.
- **bool isMirroredInterrupts()** returns set option (0,1 or 2).
### IO Control Register
The library supports setting bit fields in the IO control register.
Read the datasheet carefully!
- **bool enableControlRegister(uint8_t mask)** set IOCR bit fields
- **bool disableControlRegister(uint8_t mask)** clear IOCR bit fields
| constant | mask | description |
|:-----------------------|:------:|:--------------|
| MCP23x17_IOCR_BANK | 0x80 | Controls how the registers are addressed.
| MCP23x17_IOCR_MIRROR | 0x40 | INT Pins Mirror bit.
| MCP23x17_IOCR_SEQOP | 0x20 | Sequential Operation mode bit.
| MCP23x17_IOCR_DISSLW | 0x10 | Slew Rate control bit for SDA output.
| MCP23x17_IOCR_HAEN | 0x08 | Hardware Address Enable bit (MCP23S17 only).
| MCP23x17_IOCR_ODR | 0x04 | Configures the INT pin as an open-drain output.
| MCP23x17_IOCR_INTPOL | 0x02 | This bit sets the polarity of the INT output pin.
| MCP23x17_IOCR_NI | 0x01 | Not implemented.
### Error codes
@ -177,18 +257,19 @@ If one of the above functions return false, there might be an error.
- **int lastError()** Above functions set an error flag that can be read with this function.
Reading it will reset the flag to **MCP23017_OK**.
| Description | Value |
|:-----------------------|:-------:|
| MCP23017_OK | 0x00 |
| MCP23017_PIN_ERROR | 0x81 |
| MCP23017_I2C_ERROR | 0x82 |
| MCP23017_VALUE_ERROR | 0x83 |
| MCP23017_PORT_ERROR | 0x84 |
| name | value | description |
|:--------------------------|:-------:|:--------------|
| MCP23017_OK | 0x00 | No error |
| MCP23017_PIN_ERROR | 0x81 |
| MCP23017_I2C_ERROR | 0x82 | (compatibility)
| MCP23017_VALUE_ERROR | 0x83 |
| MCP23017_PORT_ERROR | 0x84 |
| MCP23017_REGISTER_ERROR | 0xFF | low level.
| MCP23017_INVALID_READ | 0xFF | low level.
## Future
#### Must
- Improve and extend documentation
@ -196,15 +277,26 @@ Reading it will reset the flag to **MCP23017_OK**.
#### Should
- extend error codes
- keep functional in sync
- sync error codes to MCP23x17
- buy additional hardware
- test with multiple devices.
- multi SELECT lines
- add example with interrupts
- test
- extend error codes
- optimize code - squeeze footprint
- fix TODO's in code
- investigate if REV D chips can be detected.
#### Could
- check need for writing in all functions (Polarity / Pull-up)
- check if bit mask changes.
- what is performance gain vs footprint?
- investigate and reimplement the INPUT_PULLUP for pinMode() ?
- initial value (16 bit?) as begin parameter (breaking change)
- depends on input output pull-up etc
- investigate auto address increment
- depends on input output pull-up etc.
- create a derived class **MCP23017_REVD**
#### Wont
@ -218,3 +310,4 @@ donate through PayPal or GitHub sponsors.
Thank you,

View File

@ -10,6 +10,7 @@
MCP23017 MCP(0x38);
void setup()
{
Serial.begin(115200);
@ -53,6 +54,7 @@ void setup()
Serial.println("done...");
}
void loop()
{
}

View File

@ -10,6 +10,7 @@
MCP23017 MCP(0x38);
void setup()
{
Serial.begin(230400);
@ -33,6 +34,7 @@ void setup()
Serial.println();
}
void loop()
{
}

View File

@ -36,8 +36,23 @@ getPolarity16 KEYWORD2
setPullup16 KEYWORD2
getPullup16 KEYWORD2
enableInterrupt KEYWORD2
disableInterrupt KEYWORD2
enableInterrupt16 KEYWORD2
disableInterrupt16 KEYWORD2
getInterruptFlagRegister KEYWORD2
getInterruptCaptureRegister KEYWORD2
setInterruptPolarity KEYWORD2
getInterruptPolarity KEYWORD2
mirrorInterrupts KEYWORD2
isMirroredInterrupts KEYWORD2
lastError KEYWORD2
enableControlRegister KEYWORD2
disableControlRegister KEYWORD2
# Instances (KEYWORD2)
@ -50,4 +65,6 @@ MCP23017_PIN_ERROR LITERAL1
MCP23017_I2C_ERROR LITERAL1
MCP23017_VALUE_ERROR LITERAL1
MCP23017_PORT_ERROR LITERAL1
MCP23017_REGISTER_ERROR LITERAL1
MCP23017_INVALID_READ LITERAL1

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/MCP23017_RT.git"
},
"version": "0.6.2",
"version": "0.6.3",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=MCP23017_RT
version=0.6.2
version=0.6.3
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for I2C MCP23017 16 channel port expander 16 IO-lines

View File

@ -49,12 +49,13 @@ unittest_teardown()
unittest(test_constants)
{
assertEqual(MCP23017_OK , 0x00);
assertEqual(MCP23017_PIN_ERROR , 0x81);
assertEqual(MCP23017_I2C_ERROR , 0x82);
assertEqual(MCP23017_VALUE_ERROR , 0x83);
assertEqual(MCP23017_PORT_ERROR , 0x84);
assertEqual(MCP23017_INVALID_READ, -100);
assertEqual(MCP23017_OK , 0x00);
assertEqual(MCP23017_PIN_ERROR , 0x81);
assertEqual(MCP23017_I2C_ERROR , 0x82);
assertEqual(MCP23017_VALUE_ERROR , 0x83);
assertEqual(MCP23017_PORT_ERROR , 0x84);
assertEqual(MCP23017_REGISTER_ERROR, 0xFF);
assertEqual(MCP23017_INVALID_READ , 0xFF);
}