GY-63_MS5611/libraries/MCP23008/MCP23008.cpp
2024-07-06 11:16:32 +02:00

521 lines
9.4 KiB
C++

//
// FILE: MCP23008.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.3.4
// PURPOSE: Arduino library for I2C MCP23008 8 channel port expander
// DATE: 2019-10-12
// URL: https://github.com/RobTillaart/MCP23008
#include "MCP23008.h"
MCP23008::MCP23008(uint8_t address, TwoWire *wire)
{
_address = address;
_wire = wire;
_error = MCP23008_OK;
}
bool MCP23008::begin(bool pullup)
{
// check connected
if (! isConnected()) 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(MCP23x08_IOCR, MCP23008_IOCR_SEQOP)) return false;
if (pullup)
{
// Force INPUT_PULLUP
if (! writeReg(MCP23x08_PUR_A, 0xFF)) return false; // 0xFF == all UP
}
return true;
}
bool MCP23008::isConnected()
{
_wire->beginTransmission(_address);
if (_wire->endTransmission() != 0)
{
_error = MCP23008_I2C_ERROR;
return false;
}
_error = MCP23008_OK;
return true;
}
uint8_t MCP23008::getAddress()
{
return _address;
}
///////////////////////////////////////////////////////////////////
//
// single pin interface
//
// pin = 0..7
// mode = INPUT, OUTPUT, INPUT_PULLUP (= same as INPUT)
// do NOT use 0 or 1 for mode
bool MCP23008::pinMode1(uint8_t pin, uint8_t mode)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
if ((mode != INPUT) && (mode != INPUT_PULLUP) && (mode != OUTPUT))
{
_error = MCP23008_VALUE_ERROR;
return false;
}
uint8_t dataDirectionRegister = MCP23x08_DDR_A;
uint8_t val = readReg(dataDirectionRegister);
if (_error != MCP23008_OK)
{
return false;
}
uint8_t mask = 1 << pin;
// only work with valid
if ((mode == INPUT) || (mode == INPUT_PULLUP))
{
val |= mask;
}
else if (mode == OUTPUT)
{
val &= ~mask;
}
// other values won't change val ....
writeReg(dataDirectionRegister, val);
if (_error != MCP23008_OK)
{
return false;
}
return true;
}
// pin = 0..7
// value = LOW, HIGH
bool MCP23008::write1(uint8_t pin, uint8_t value)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
uint8_t IOR = MCP23x08_GPIO_A;
uint8_t val = readReg(IOR);
uint8_t pre = val;
if (_error != MCP23008_OK)
{
return false;
}
uint8_t mask = 1 << pin;
if (value)
{
val |= mask;
}
else
{
val &= ~mask;
}
// only write when changed.
if (pre != val)
{
writeReg(IOR, val);
if (_error != MCP23008_OK)
{
return false;
}
}
return true;
}
uint8_t MCP23008::read1(uint8_t pin)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return MCP23008_INVALID_READ;
}
uint8_t IOR = MCP23x08_GPIO_A;
uint8_t val = readReg(IOR);
if (_error != MCP23008_OK)
{
return MCP23008_INVALID_READ;
}
uint8_t mask = 1 << pin;
if (val & mask) return HIGH;
return LOW;
}
// pin = 0..7
// reversed = true or false
bool MCP23008::setPolarity(uint8_t pin, bool reversed)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
uint8_t inputPolarityRegister = MCP23x08_POL_A;
uint8_t val = readReg(inputPolarityRegister);
if (_error != MCP23008_OK)
{
return false;
}
uint8_t mask = 1 << pin;
if (reversed)
{
val |= mask;
}
else
{
val &= ~mask;
}
writeReg(inputPolarityRegister, val);
if (_error != MCP23008_OK)
{
return false;
}
return true;
}
bool MCP23008::getPolarity(uint8_t pin, bool &reversed)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
uint8_t inputPolarityRegister = MCP23x08_POL_A;
uint8_t val = readReg(inputPolarityRegister);
if (_error != MCP23008_OK)
{
return false;
}
uint8_t mask = 1 << pin;
reversed = (val & mask) > 0;
return true;
}
// pin = 0..7
// pullup = true or false
bool MCP23008::setPullup(uint8_t pin, bool pullup)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
uint8_t inputPullupRegister = MCP23x08_PUR_A;
uint8_t val = readReg(inputPullupRegister);
if (_error != MCP23008_OK)
{
return false;
}
uint8_t mask = 1 << pin;
if (pullup)
{
val |= mask;
}
else
{
val &= ~mask;
}
writeReg(inputPullupRegister, val);
if (_error != MCP23008_OK)
{
return false;
}
return true;
}
bool MCP23008::getPullup(uint8_t pin, bool &pullup)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
uint8_t inputPullupRegister = MCP23x08_PUR_A;
uint8_t val = readReg(inputPullupRegister);
if (_error != MCP23008_OK)
{
return false;
}
uint8_t mask = 1 << pin;
pullup = (val & mask) > 0;
return true;
}
///////////////////////////////////////////////////////////////////
//
// 8 pins interface
//
// whole register at once
// mask = 0x00..0xFF bit pattern
// bit 0 = output mode, bit 1 = input mode
bool MCP23008::pinMode8(uint8_t mask)
{
writeReg(MCP23x08_DDR_A, mask);
_error = MCP23008_OK;
return true;
}
bool MCP23008::write8(uint8_t value)
{
writeReg(MCP23x08_GPIO_A, value);
_error = MCP23008_OK;
return true;
}
int MCP23008::read8()
{
_error = MCP23008_OK;
return readReg(MCP23x08_GPIO_A);
}
// mask = 0..0xFF bit pattern
bool MCP23008::setPolarity8(uint8_t mask)
{
writeReg(MCP23x08_POL_A, mask);
if (_error != MCP23008_OK)
{
return false;
}
return true;
}
bool MCP23008::getPolarity8(uint8_t &mask)
{
mask = readReg(MCP23x08_POL_A);
if (_error != MCP23008_OK)
{
return false;
}
return true;
}
// mask = 0..0xFF bit pattern
bool MCP23008::setPullup8(uint8_t mask)
{
writeReg(MCP23x08_PUR_A, mask);
if (_error != MCP23008_OK)
{
return false;
}
return true;
}
bool MCP23008::getPullup8(uint8_t &mask)
{
mask = readReg(MCP23x08_PUR_A);
if (_error != MCP23008_OK)
{
return false;
}
return true;
}
///////////////////////////////////////////////////
//
// INTERRUPTS (experimental, see MCP23S17 - #40)
//
// TODO, catch writeReg errors
// TODO, MCP23x08_INT_MODE_ERROR?
// TODO, if register not changed no need to update?
// TODO, 8 bits optimize? more code vs speed?
//
// pin = 0..7, mode = { RISING, FALLING, CHANGE }
bool MCP23008::enableInterrupt(uint8_t pin, uint8_t mode)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
// right mode
uint8_t intcon = readReg(MCP23x08_INTCON_A);
if (mode == CHANGE)
{
// compare to previous value.
intcon &= ~(1 << pin);
}
else
{
uint8_t defval = readReg(MCP23x08_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
}
writeReg(MCP23x08_DEFVAL_A, defval);
}
writeReg(MCP23x08_INTCON_A, intcon);
// enable interrupt
uint16_t value = readReg(MCP23x08_GPINTEN_A);
value |= (1 << pin);
return writeReg(MCP23x08_GPINTEN_A, value);
}
bool MCP23008::disableInterrupt(uint8_t pin)
{
if (pin > 7)
{
_error = MCP23008_PIN_ERROR;
return false;
}
// disable interrupt
uint16_t value = readReg(MCP23x08_GPINTEN_A);
value &= ~(1 << pin);
return writeReg(MCP23x08_GPINTEN_A, value);
}
// which pins caused the INT?
uint8_t MCP23008::getInterruptFlagRegister()
{
return readReg(MCP23x08_INTF_A);
}
uint8_t MCP23008::getInterruptCaptureRegister()
{
return readReg(MCP23x08_INTCAP_A);
}
// polarity: 0 = LOW, 1 = HIGH, 2 = NONE/ODR
bool MCP23008::setInterruptPolarity(uint8_t polarity)
{
if (polarity > 2) return false;
uint8_t reg = readReg(MCP23x08_IOCR);
reg &= ~(MCP23x08_IOCR_ODR | MCP23x08_IOCR_INTPOL);
// LOW is default set.
if (polarity == 2) reg |= MCP23x08_IOCR_ODR;
if (polarity == 1) reg |= MCP23x08_IOCR_INTPOL;
return writeReg(MCP23x08_IOCR, reg);
}
uint8_t MCP23008::getInterruptPolarity()
{
uint8_t reg = readReg(MCP23x08_IOCR);
if (reg & MCP23x08_IOCR_ODR) return 2;
if (reg & MCP23x08_IOCR_INTPOL) return 1;
return 0;
}
/////////////////////////////////////////////
//
// MISC
//
int MCP23008::lastError()
{
int e = _error;
_error = MCP23008_OK; // reset error after read.
return e;
}
bool MCP23008::enableControlRegister(uint8_t mask)
{
uint8_t reg = readReg(MCP23x08_IOCR);
reg |= mask;
return writeReg(MCP23x08_IOCR, reg);
}
bool MCP23008::disableControlRegister(uint8_t mask)
{
uint8_t reg = readReg(MCP23x08_IOCR);
reg &= ~mask;
return writeReg(MCP23x08_IOCR, reg);
}
////////////////////////////////////////////////////
//
// DEBUG
//
uint8_t MCP23008::getPinMode8()
{
return readReg(0);
}
////////////////////////////////////////////////////
//
// PROTECTED
//
bool MCP23008::writeReg(uint8_t reg, uint8_t value)
{
_wire->beginTransmission(_address);
_wire->write(reg);
_wire->write(value);
if (_wire->endTransmission() != 0)
{
_error = MCP23008_I2C_ERROR;
return false;
}
_error = MCP23008_OK;
return true;
}
uint8_t MCP23008::readReg(uint8_t reg)
{
_wire->beginTransmission(_address);
_wire->write(reg);
if (_wire->endTransmission() != 0)
{
_error = MCP23008_I2C_ERROR;
return 0;
}
uint8_t n = _wire->requestFrom(_address, (uint8_t)1);
if (n != 1)
{
_error = MCP23008_I2C_ERROR;
return 0;
}
_error = MCP23008_OK;
return _wire->read();
}
// -- END OF FILE --