mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
496 lines
9.3 KiB
C++
496 lines
9.3 KiB
C++
//
|
|
// FILE: MCP23017.cpp
|
|
// AUTHOR: Rob Tillaart
|
|
// VERSION: 0.2.4
|
|
// PURPOSE: Arduino library for I2C MCP23017 16 channel port expander
|
|
// DATE: 2019-10-12
|
|
// URL: https://github.com/RobTillaart/MCP23017_RT
|
|
//
|
|
// HISTORY:
|
|
// 0.1.0 2019-10-12 initial version
|
|
// 0.1.1 2020-06-19 refactor; #pragma once
|
|
// 0.1.2 2021-01-01 add Arduino-CI + unit test
|
|
// 0.2.0 2021-01-11 Multiple Wire support Wire0 .. 5
|
|
// error handling
|
|
// 0.2.1 2021-02-17 fix #7 DDR is defined in ESP32
|
|
// 0.2.2 2021-04-23 fix for plpatformIO compatibility
|
|
// 0.2.3 2021-06-06 add lastError() unit test + minor refactor.
|
|
// 0.2.4 2021-09-16 add polarity and pullup support
|
|
|
|
|
|
#include "MCP23017.h"
|
|
|
|
|
|
#define MCP23017_DDR_A 0x00 // Data Direction Register
|
|
#define MCP23017_DDR_B 0x01
|
|
#define MCP23017_POL_A 0x02 // Input Polarity (0 == normal 1== reversed)
|
|
#define MCP23017_POL_B 0x03
|
|
#define MCP23017_PUR_A 0x0C // Pull Up Resistors
|
|
#define MCP23017_PUR_B 0x0D
|
|
|
|
#define MCP23017_IOCR 0x0A // IO control register
|
|
|
|
#define MCP23017_GPIOA 0x12
|
|
#define MCP23017_GPIOB 0x13
|
|
|
|
|
|
MCP23017::MCP23017(uint8_t addr, TwoWire *wire)
|
|
{
|
|
_addr = addr;
|
|
_wire = wire;
|
|
_error = MCP23017_OK;
|
|
}
|
|
|
|
|
|
#if defined(ESP8266) || defined(ESP32)
|
|
bool MCP23017::begin(const uint8_t dataPin, const uint8_t clockPin)
|
|
{
|
|
_wire = &Wire;
|
|
_wire->begin(dataPin, clockPin);
|
|
// check connected
|
|
if (! isConnected()) return false;
|
|
// disable address increment (datasheet)
|
|
if (! writeReg(MCP23017_IOCR, 0b00100000)) return false;
|
|
// Force INPUT_PULLUP
|
|
if (! writeReg(MCP23017_PUR_A, 0xFF)) return false;
|
|
if (! writeReg(MCP23017_PUR_B, 0xFF)) return false;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
|
|
bool MCP23017::begin()
|
|
{
|
|
_wire->begin();
|
|
// check connected
|
|
if (! isConnected()) return false;
|
|
// disable address increment (datasheet)
|
|
if (! writeReg(MCP23017_IOCR, 0b00100000)) return false;
|
|
// Force INPUT_PULLUP
|
|
if (! writeReg(MCP23017_PUR_A, 0xFF)) return false;
|
|
if (! writeReg(MCP23017_PUR_B, 0xFF)) return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MCP23017::isConnected()
|
|
{
|
|
_wire->beginTransmission(_addr);
|
|
if (_wire->endTransmission() != 0)
|
|
{
|
|
_error = MCP23017_I2C_ERROR;
|
|
return false;
|
|
}
|
|
_error = MCP23017_OK;
|
|
return true;
|
|
}
|
|
|
|
|
|
// single pin interface
|
|
// pin = 0..15
|
|
// mode = INPUT, OUTPUT, INPUT_PULLUP (= same as INPUT)
|
|
bool MCP23017::pinMode(uint8_t pin, uint8_t mode)
|
|
{
|
|
if (pin > 15)
|
|
{
|
|
_error = MCP23017_PIN_ERROR;
|
|
return false;
|
|
}
|
|
if ((mode != INPUT) && (mode != INPUT_PULLUP) && (mode != OUTPUT))
|
|
{
|
|
_error = MCP23017_VALUE_ERROR;
|
|
return false;
|
|
}
|
|
|
|
uint8_t dataDirectionRegister = MCP23017_DDR_A;
|
|
if (pin > 7)
|
|
{
|
|
dataDirectionRegister = MCP23017_DDR_B;
|
|
pin -= 8;
|
|
}
|
|
uint8_t val = readReg(dataDirectionRegister);
|
|
if (_error != MCP23017_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 != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// pin = 0..15
|
|
// value = LOW, HIGH
|
|
bool MCP23017::digitalWrite(uint8_t pin, uint8_t value)
|
|
{
|
|
if (pin > 15)
|
|
{
|
|
_error = MCP23017_PIN_ERROR;
|
|
return false;
|
|
}
|
|
uint8_t IOR = MCP23017_GPIOA;
|
|
if (pin > 7)
|
|
{
|
|
IOR = MCP23017_GPIOB;
|
|
pin -= 8;
|
|
}
|
|
|
|
uint8_t val = readReg(IOR);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint8_t mask = 1 << pin;
|
|
if (value)
|
|
{
|
|
val |= mask;
|
|
}
|
|
else
|
|
{
|
|
val &= ~mask;
|
|
}
|
|
writeReg(IOR, val);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
uint8_t MCP23017::digitalRead(uint8_t pin)
|
|
{
|
|
if (pin > 15)
|
|
{
|
|
_error = MCP23017_PIN_ERROR;
|
|
return MCP23017_INVALID_READ;
|
|
}
|
|
uint8_t IOR = MCP23017_GPIOA;
|
|
if (pin > 7)
|
|
{
|
|
IOR = MCP23017_GPIOB;
|
|
pin -= 8;
|
|
}
|
|
|
|
uint8_t val = readReg(IOR);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return MCP23017_INVALID_READ;
|
|
}
|
|
uint8_t mask = 1 << pin;
|
|
if (val & mask) return HIGH;
|
|
return LOW;
|
|
}
|
|
|
|
|
|
// pin = 0..15
|
|
// reverse = true or false
|
|
bool MCP23017::setPolarity(uint8_t pin, bool reversed)
|
|
{
|
|
if (pin > 15)
|
|
{
|
|
_error = MCP23017_PIN_ERROR;
|
|
return false;
|
|
}
|
|
uint8_t inputPolarityRegister = MCP23017_POL_A;
|
|
if (pin > 7)
|
|
{
|
|
inputPolarityRegister = MCP23017_POL_B;
|
|
pin -= 8;
|
|
}
|
|
uint8_t val = readReg(inputPolarityRegister);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
uint8_t mask = 1 << pin;
|
|
if (reversed)
|
|
{
|
|
val |= mask;
|
|
}
|
|
else
|
|
{
|
|
val &= ~mask;
|
|
}
|
|
writeReg(inputPolarityRegister, val);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MCP23017::getPolarity(uint8_t pin, bool &reversed)
|
|
{
|
|
if (pin > 15)
|
|
{
|
|
_error = MCP23017_PIN_ERROR;
|
|
return false;
|
|
}
|
|
uint8_t inputPolarityRegister = MCP23017_POL_A;
|
|
if (pin > 7)
|
|
{
|
|
inputPolarityRegister = MCP23017_POL_B;
|
|
pin -= 8;
|
|
}
|
|
uint8_t val = readReg(inputPolarityRegister);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
uint8_t mask = 1 << pin;
|
|
reversed = (val & mask) > 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
// pin = 0..15
|
|
// reverse = true or false
|
|
bool MCP23017::setPullup(uint8_t pin, bool pullup)
|
|
{
|
|
if (pin > 15)
|
|
{
|
|
_error = MCP23017_PIN_ERROR;
|
|
return false;
|
|
}
|
|
uint8_t inputPullupRegister = MCP23017_PUR_A;
|
|
if (pin > 7)
|
|
{
|
|
inputPullupRegister = MCP23017_PUR_B;
|
|
pin -= 8;
|
|
}
|
|
uint8_t val = readReg(inputPullupRegister);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
uint8_t mask = 1 << pin;
|
|
if (pullup)
|
|
{
|
|
val |= mask;
|
|
}
|
|
else
|
|
{
|
|
val &= ~mask;
|
|
}
|
|
writeReg(inputPullupRegister, val);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MCP23017::getPullup(uint8_t pin, bool &pullup)
|
|
{
|
|
if (pin > 15)
|
|
{
|
|
_error = MCP23017_PIN_ERROR;
|
|
return false;
|
|
}
|
|
uint8_t inputPullupRegister = MCP23017_PUR_A;
|
|
if (pin > 7)
|
|
{
|
|
inputPullupRegister = MCP23017_PUR_B;
|
|
pin -= 8;
|
|
}
|
|
uint8_t val = readReg(inputPullupRegister);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
uint8_t mask = 1 << pin;
|
|
pullup = (val & mask) > 0;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// 8 pins interface
|
|
// whole register at once
|
|
// port = 0..1
|
|
// value = 0..0xFF bit pattern
|
|
bool MCP23017::pinMode8(uint8_t port, uint8_t value)
|
|
{
|
|
if (port > 1)
|
|
{
|
|
_error = MCP23017_PORT_ERROR;
|
|
return false;
|
|
}
|
|
if (port == 0) writeReg(MCP23017_DDR_A, value);
|
|
if (port == 1) writeReg(MCP23017_DDR_B, value);
|
|
_error = MCP23017_OK;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MCP23017::write8(uint8_t port, uint8_t value) // port = 0..1
|
|
{
|
|
if (port > 1)
|
|
{
|
|
_error = MCP23017_PORT_ERROR;
|
|
return false;
|
|
}
|
|
if (port == 0) writeReg(MCP23017_GPIOA, value);
|
|
if (port == 1) writeReg(MCP23017_GPIOB, value);
|
|
_error = MCP23017_OK;
|
|
return true;
|
|
}
|
|
|
|
|
|
int MCP23017::read8(uint8_t port)
|
|
{
|
|
if (port > 1)
|
|
{
|
|
_error = MCP23017_PORT_ERROR;
|
|
return MCP23017_INVALID_READ;
|
|
}
|
|
_error = MCP23017_OK;
|
|
if (port == 0) return readReg(MCP23017_GPIOA);
|
|
return readReg(MCP23017_GPIOB); // port == 1
|
|
}
|
|
|
|
|
|
// port = 0..1
|
|
// mask = 0..0xFF bit pattern
|
|
bool MCP23017::setPolarity8(uint8_t port, uint8_t mask)
|
|
{
|
|
if (port > 1)
|
|
{
|
|
_error = MCP23017_PORT_ERROR;
|
|
return false;
|
|
}
|
|
if (port == 0) writeReg(MCP23017_POL_A, mask);
|
|
if (port == 1) writeReg(MCP23017_POL_B, mask);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MCP23017::getPolarity8(uint8_t port, uint8_t &mask)
|
|
{
|
|
if (port > 1)
|
|
{
|
|
_error = MCP23017_PORT_ERROR;
|
|
return false;
|
|
}
|
|
if (port == 0) mask = readReg(MCP23017_POL_A);
|
|
if (port == 1) mask = readReg(MCP23017_POL_B);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// port = 0..1
|
|
// mask = 0..0xFF bit pattern
|
|
bool MCP23017::setPullup8(uint8_t port, uint8_t mask)
|
|
{
|
|
if (port > 1)
|
|
{
|
|
_error = MCP23017_PORT_ERROR;
|
|
return false;
|
|
}
|
|
if (port == 0) writeReg(MCP23017_PUR_A, mask);
|
|
if (port == 1) writeReg(MCP23017_PUR_B, mask);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MCP23017::getPullup8(uint8_t port, uint8_t &mask)
|
|
{
|
|
if (port > 1)
|
|
{
|
|
_error = MCP23017_PORT_ERROR;
|
|
return false;
|
|
}
|
|
if (port == 0) mask = readReg(MCP23017_PUR_A);
|
|
if (port == 1) mask = readReg(MCP23017_PUR_B);
|
|
if (_error != MCP23017_OK)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
int MCP23017::lastError()
|
|
{
|
|
int e = _error;
|
|
_error = MCP23017_OK; // reset error after read.
|
|
return e;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////
|
|
//
|
|
// PRIVATE
|
|
//
|
|
|
|
bool MCP23017::writeReg(uint8_t reg, uint8_t value)
|
|
{
|
|
_wire->beginTransmission(_addr);
|
|
_wire->write(reg);
|
|
_wire->write(value);
|
|
if (_wire->endTransmission() != 0)
|
|
{
|
|
_error = MCP23017_I2C_ERROR;
|
|
return false;
|
|
}
|
|
_error = MCP23017_OK;
|
|
return true;
|
|
}
|
|
|
|
|
|
uint8_t MCP23017::readReg(uint8_t reg)
|
|
{
|
|
_wire->beginTransmission(_addr);
|
|
_wire->write(reg);
|
|
if (_wire->endTransmission() != 0)
|
|
{
|
|
_error = MCP23017_I2C_ERROR;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
_error = MCP23017_OK;
|
|
}
|
|
uint8_t n = _wire->requestFrom(_addr, (uint8_t)1);
|
|
if (n != 1)
|
|
{
|
|
_error = MCP23017_I2C_ERROR;
|
|
return 0;
|
|
}
|
|
return _wire->read();
|
|
}
|
|
|
|
|
|
// -- END OF FILE --
|