0.2.1 MCP23S17

This commit is contained in:
rob tillaart 2022-07-01 12:10:38 +02:00
parent 68e9c6858c
commit ef99b9be5f
9 changed files with 526 additions and 61 deletions

View File

@ -3,9 +3,9 @@ compile:
platforms:
- uno
# - due
# - zero
- zero
# - leonardo
- m4
- esp32
# - esp8266
- esp8266
# - mega2560

View File

@ -1,7 +1,7 @@
//
// FILE: MCP23S17.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// VERSION: 0.2.1
// PURPOSE: Arduino library for SPI MCP23S17 16 channel port expander
// DATE: 2021-12-30
// URL: https://github.com/RobTillaart/MCP23S17
@ -12,7 +12,13 @@
// 0.1.1 2022-01-10 add 16 bit interface
// 0.1.2 2022-01-12 change the URL for library manager
// 0.1.3 2022-04-13 fix compiling for NANO33 BLE
//
// 0.2.0 2022-06-28 fix #10 incorrect mask
// 0.2.1 2022-06-29 add SPIClass as parameter for constructor (See #10)
// redo constructors.
// add getAddress() + optimized (_address << 1)
// update readme.md
#include "Arduino.h"
@ -57,16 +63,11 @@
#define MCP23S17_WRITE_REG 0x40
#define MCP23S17_READ_REG 0x41
/*
MCP23S17::MCP23S17()
{
}
*/
// SW SPI
MCP23S17::MCP23S17(uint8_t select, uint8_t dataIn, uint8_t dataOut, uint8_t clock, uint8_t address)
{
_address = address;
_address = (address << 1);
_select = select;
_dataIn = dataIn;
_dataOut = dataOut;
@ -76,11 +77,18 @@ MCP23S17::MCP23S17(uint8_t select, uint8_t dataIn, uint8_t dataOut, uint8_t cloc
}
MCP23S17::MCP23S17(uint8_t select, uint8_t address)
// HW SPI
MCP23S17::MCP23S17(uint8_t select, SPIClass* spi)
{
_address = address;
MCP23S17(select, 0x00, spi);
}
MCP23S17::MCP23S17(uint8_t select, uint8_t address, SPIClass* spi)
{
_address = (address << 1);
_select = select;
_error = MCP23S17_OK;
_mySPI = spi;
_hwSPI = true;
}
@ -90,14 +98,13 @@ bool MCP23S17::begin()
::pinMode(_select, OUTPUT);
::digitalWrite(_select, HIGH);
_spi_settings = SPISettings(_SPIspeed, MSBFIRST, SPI_MODE0);// 8 MHz - datasheet page 8
_spi_settings = SPISettings(_SPIspeed, MSBFIRST, SPI_MODE0); // 8 MHz - datasheet page 8
if (_hwSPI)
{
// TODO - ESP32 specific support - see MCP_ADC.
mySPI = &SPI;
mySPI->end();
mySPI->begin();
// _mySPI = &SPI; // set in constructor #10
_mySPI->end();
_mySPI->begin();
}
else
{
@ -124,6 +131,7 @@ bool MCP23S17::begin()
}
// to keep interface in sync with I2C MCP23017 library.
bool MCP23S17::isConnected()
{
_error = MCP23S17_OK;
@ -131,6 +139,12 @@ bool MCP23S17::isConnected()
}
uint8_t MCP23S17::getAddress()
{
return (_address >> 1);
}
// single pin interface
// pin = 0..15
// mode = INPUT, OUTPUT, INPUT_PULLUP (= same as INPUT)
@ -600,21 +614,23 @@ bool MCP23S17::writeReg(uint8_t reg, uint8_t value)
if (reg > MCP23S17_OLAT_B)
{
_error = 0xFF; // TODO MAGIC NR
_error = MCP23S17_REGISTER_ERROR;
return false;
}
::digitalWrite(_select, LOW);
if (_hwSPI)
{
mySPI->beginTransaction(_spi_settings);
mySPI->transfer(MCP23S17_WRITE_REG | (_address << 1) );
mySPI->transfer(reg);
mySPI->transfer(value);
mySPI->endTransaction();
_mySPI->beginTransaction(_spi_settings);
// _address already shifted
_mySPI->transfer(MCP23S17_WRITE_REG | _address );
_mySPI->transfer(reg);
_mySPI->transfer(value);
_mySPI->endTransaction();
}
else
{
swSPI_transfer(MCP23S17_WRITE_REG | (_address << 1) );
// _address already shifted
swSPI_transfer(MCP23S17_WRITE_REG | _address );
swSPI_transfer(reg);
swSPI_transfer(value);
}
@ -631,22 +647,24 @@ uint8_t MCP23S17::readReg(uint8_t reg)
if (reg > MCP23S17_OLAT_B)
{
_error = 0xFF; // TODO MAGIC NR
_error = MCP23S17_REGISTER_ERROR;
return false;
}
::digitalWrite(_select, LOW);
if (_hwSPI)
{
mySPI->beginTransaction(_spi_settings);
mySPI->transfer(MCP23S17_READ_REG | (_address << 1) ); // TODO OPTIMIZE n times
mySPI->transfer(reg);
rv = mySPI->transfer(0xFF);
mySPI->endTransaction();
_mySPI->beginTransaction(_spi_settings);
// _address already shifted
_mySPI->transfer(MCP23S17_READ_REG | _address );
_mySPI->transfer(reg);
rv = _mySPI->transfer(0xFF);
_mySPI->endTransaction();
}
else
{
swSPI_transfer(MCP23S17_READ_REG | (_address << 1) );
// _address already shifted
swSPI_transfer(MCP23S17_READ_REG | _address );
swSPI_transfer(reg);
rv = swSPI_transfer(0xFF);
}

View File

@ -2,7 +2,7 @@
//
// FILE: MCP23S17.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// VERSION: 0.2.1
// PURPOSE: Arduino library for SPI MCP23S17 16 channel port expander
// DATE: 2021-12-30
// URL: https://github.com/RobTillaart/MCP23S17
@ -12,26 +12,36 @@
#include "SPI.h"
#define MCP23S17_LIB_VERSION (F("0.2.0"))
#define MCP23S17_LIB_VERSION (F("0.2.1"))
#define MCP23S17_OK 0x00
#define MCP23S17_PIN_ERROR 0x81
#define MCP23S17_SPI_ERROR 0x82
#define MCP23S17_VALUE_ERROR 0x83
#define MCP23S17_PORT_ERROR 0x84
#define MCP23S17_REGISTER_ERROR 0xFF
#define MCP23S17_INVALID_READ -100
#define MCP23S17_INVALID_READ 0xFF
const uint32_t MCP23S17_TYP_SPI_SPEED = 8000000;
const uint32_t MCP23S17_MAX_SPI_SPEED = 10000000;
class MCP23S17
{
public:
// SW SPI
MCP23S17(uint8_t select, uint8_t dataIn, uint8_t dataOut, uint8_t clock, uint8_t address = 0x00);
MCP23S17(uint8_t select, uint8_t address = 0x00);
// HW SPI
MCP23S17(uint8_t select, SPIClass* spi);
MCP23S17(uint8_t select, uint8_t address = 0x00, SPIClass* spi = &SPI);
bool begin();
bool isConnected(); // needed ?
bool isConnected();
uint8_t getAddress(); // typically returns 0x00
// single pin interface
@ -90,9 +100,10 @@ private:
uint8_t _clock = 0;
uint8_t _error = MCP23S17_OK;
bool _hwSPI = false;
uint32_t _SPIspeed = 8000000UL; // 1MHz is a safe value TODO CHECK datasheet
SPIClass * mySPI;
bool _hwSPI = true;
// 10 MHz is maximum, 8 is a better clock divider
uint32_t _SPIspeed = MCP23S17_TYP_SPI_SPEED;
SPIClass * _mySPI;
SPISettings _spi_settings;
uint8_t swSPI_transfer(uint8_t val);

View File

@ -23,10 +23,30 @@ Programming Interface is kept the same as much as possible.
### Constructor
- **MCP23S17(uint8_t select, uint8_t data, uint8_t clock)** constructor SW SPI
- **MCP23S17(uint8_t select)** constructor HW SPI
- **MCP23S17(uint8_t select, uint8_t dataIn, uint8_t dataOut, uint8_t clock, uint8_t address = 0x00)** constructor SW SPI.
- **MCP23S17(uint8_t select, SPIClass\* spi)** constructor HW SPI with explicit SPI interface selected.
- **MCP23S17(uint8_t select, uint8_t address = 0x00, SPIClass\* spi = &SPI)** constructor HW SPI with optional address pins and SPI interface.
- **bool begin()** returns true if successful.
- **bool isConnected()** returns true if connected, false otherwise. (dummy)
- **bool isConnected()** returns true if connected, false otherwise. (dummy for compatibility reasons)
- **uint8_t getAddress()** returns the address set in the constructor.
Default = 0, range = 0..7.
The two constructors allow to call 4 different constructors.
```cpp
- MCP23S17(10); // select pin only
- MCP23S17(10, 7); // select pin + address pins
- MCP23S17(10, 7, &SPI2); // select pin + address pins + SPI port
- MCP23S17(10, &SPI2); // select pin + SPI port
```
#### sharing select lines
(not tested)
Technically two chips could use the same select pin and a different address.
The constructors would allow to setup such a configuration.
I assume that this is less used and IMHO not recommended.
### Single pin interface
@ -67,13 +87,14 @@ Programming Interface is kept the same as much as possible.
- **int lastError()** Above functions set an error flag that can be read with this function.
Reading it will reset the flag to **MCP23S17_OK**.
| NAME | VALUE | DESCRIPTION |
|:----------------------|:-----:|:------------|
| MCP23S17_OK | 0x00 | No error |
| MCP23S17_PIN_ERROR | 0x81 |
| MCP23S17_I2C_ERROR | 0x82 |
| MCP23S17_VALUE_ERROR | 0x83 |
| MCP23S17_PORT_ERROR | 0x84 |
| NAME | VALUE | DESCRIPTION |
|:------------------------|:------:|:------------|
| MCP23S17_OK | 0x00 | No error |
| MCP23S17_PIN_ERROR | 0x81 |
| MCP23S17_I2C_ERROR | 0x82 | (compatibility)
| MCP23S17_VALUE_ERROR | 0x83 |
| MCP23S17_PORT_ERROR | 0x84 |
| MCP23S17_REGISTER_ERROR | 0xFF | low level.
## Operation
@ -83,8 +104,16 @@ See examples.
## Future
- improve documentation
- references to I2C ?
- keep functional in sync with MCP23017_RT
- **isConnected()** is not really needed
- implement ESP32 specific support - see MCP_ADC.begin()
- replace magic numbers with a defined constant
- implement ESP32 specific support in begin()
- see MCP_ADC.begin()
- SW_SPI is roughly equal in performance as HW SPI on ESP32.
- investigate and reimplement the INPUT_PULLUP for pinMode() ?
#### wont
- check address range in constructor.

View File

@ -0,0 +1,151 @@
//
// FILE: MCP23S17_test.ino
// AUTHOR: Rob Tillaart
// DATE: 2022-06-28
// PUPROSE: test MCP23017 library
#include "MCP23S17.h"
#include "SPI.h"
// MCP23S17 MCP(10, 12, 11, 13); // SW SPI address 0x00
MCP23S17 MCP(10); // HW SPI address 0x00
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.print("MCP23S17_LIB_VERSION: ");
Serial.println(MCP23S17_LIB_VERSION);
Serial.println();
delay(100);
SPI.begin();
bool b = MCP.begin();
Serial.println(b ? "true" : "false");
MCP.pinMode8(0, 0x00); // 0 = output , 1 = input
MCP.pinMode8(1, 0x00);
//////////////////////////////////////////////////////////////
//
// test 8 bit interface
//
Serial.println("TEST digitalWrite8(port, value)");
for (uint16_t i = 0; i < 256; i++)
{
MCP.write8(0, i);
MCP.write8(1, i);
uint8_t mask0 = MCP.read8(0);
uint8_t mask1 = MCP.read8(1);
if ((mask0 != i) || (mask1 != i))
{
Serial.print(i);
Serial.print("\t");
Serial.print(MCP.read8(0));
Serial.print("\t");
Serial.print(MCP.read8(1));
Serial.println();
}
}
Serial.println("TEST setPullup8(port, value)");
for (uint16_t i = 0; i < 256; i++)
{
MCP.setPullup8(0, i);
MCP.setPullup8(1, i);
uint8_t mask0, mask1;
MCP.getPullup8(0, mask0);
MCP.getPullup8(1, mask1);
if ((mask0 != i) || (mask1 != i))
{
Serial.print(i);
Serial.print("\t");
Serial.print(mask0);
Serial.print("\t");
Serial.print(mask1);
Serial.println();
}
}
Serial.println("TEST setPolarity8(port, value)");
for (uint16_t i = 0; i < 256; i++)
{
MCP.setPolarity8(0, i);
MCP.setPolarity8(1, i);
uint8_t mask0, mask1;
MCP.getPolarity8(0, mask0);
MCP.getPolarity8(1, mask1);
if ((mask0 != i) || (mask1 != i))
{
Serial.print(i);
Serial.print("\t");
Serial.print(mask0);
Serial.print("\t");
Serial.print(mask1);
Serial.println();
}
}
//////////////////////////////////////////////////////////////
//
// test 16 bit interface
//
Serial.println("TEST digitalWrite16(value)");
for (uint32_t i = 0; i < 65535; i++)
{
MCP.write16(i);
if (MCP.read16() != i)
{
Serial.print(i);
Serial.print("\t");
Serial.print(MCP.read16());
Serial.println();
}
}
Serial.println("TEST setPullup16(value)");
for (uint32_t i = 0; i < 65535; i++)
{
MCP.setPullup16(i);
uint16_t mask = 0;
MCP.getPullup16(mask);
if (mask != i)
{
Serial.print(i);
Serial.print("\t");
Serial.print(mask);
Serial.println();
}
}
Serial.println("TEST setPolarity16(value)");
for (uint32_t i = 0; i < 65535; i++)
{
MCP.setPolarity16(i);
uint16_t mask = 0;
MCP.getPolarity16(mask);
if (mask != i)
{
Serial.print(i);
Serial.print("\t");
Serial.print(mask);
Serial.println();
}
}
}
void loop()
{
}
// -- END OF FILE --

View File

@ -0,0 +1,63 @@
//
// FILE: MCP23S17_test_connection.ino
// AUTHOR: Rob Tillaart
// DATE: 2022-06-28
// PUPROSE: test MCP23017 library
//
// see issue #13 library
#include "MCP23S17.h"
#include "SPI.h"
// MCP23S17 MCP(10, 12, 11, 13); // SW SPI address 0x00
MCP23S17 MCP(10, 4); // HW SPI address 4
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.print("MCP23S17_LIB_VERSION: ");
Serial.println(MCP23S17_LIB_VERSION);
Serial.println();
delay(100);
SPI.begin();
MCP.begin();
// test connected
int c = testConnection(MCP);
Serial.print("connection: ");
Serial.println(c);
}
void loop()
{
}
int testConnection(MCP23S17 & mcp)
{
uint16_t magic_test_number = 0xABCD;
// Read the current polarity config to restore later
uint16_t old_value;
if (! mcp.getPolarity16(old_value)) return -1;
// Write the magic number to polarity register
if (! mcp.setPolarity16(magic_test_number)) return -2;
// Read back the magic number from polarity register
uint16_t temp;
if (! mcp.getPolarity16(temp)) return -3;
// Write old config to polarity register
if (! mcp.setPolarity16(old_value)) return -4;
// Check the magic connection test
if (temp != magic_test_number) return -5;
return 0; // OK
}
// -- END OF FILE --

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/MCP23S17.git"
},
"version": "0.2.0",
"version": "0.2.1",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=MCP23S17
version=0.2.0
version=0.2.1
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for SPI MCP23S17 16 channel port expander 16 IO-lines

View File

@ -49,18 +49,211 @@ unittest_teardown()
unittest(test_constants)
{
assertEqual(MCP23S17_OK , 0x00);
assertEqual(MCP23S17_PIN_ERROR , 0x81);
assertEqual(MCP23S17_SPI_ERROR , 0x82);
assertEqual(MCP23S17_VALUE_ERROR , 0x83);
assertEqual(MCP23S17_PORT_ERROR , 0x84);
assertEqual(MCP23S17_INVALID_READ, -100);
assertEqual(MCP23S17_OK , 0x00);
assertEqual(MCP23S17_PIN_ERROR , 0x81);
assertEqual(MCP23S17_SPI_ERROR , 0x82);
assertEqual(MCP23S17_VALUE_ERROR , 0x83);
assertEqual(MCP23S17_PORT_ERROR , 0x84);
assertEqual(MCP23S17_REGISTER_ERROR, 0xFF);
assertEqual(MCP23S17_INVALID_READ , 0xFF);
fprintf(stderr, "\nSPI-speed\n");
assertEqual(MCP23S17_TYP_SPI_SPEED , 8000000);
assertEqual(MCP23S17_MAX_SPI_SPEED , 10000000);
}
unittest(test_constructor)
unittest(test_SW_constructor)
{
MCP23S17 mcp_sw(10, 11, 12, 13); // default address 0x00
assertEqual(0x00, mcp_sw.getAddress());
assertFalse(mcp_sw.usesHWSPI());
assertEqual(MCP23S17_OK, mcp_sw.lastError());
for (int addr = 0; addr < 8; addr++)
{
MCP23S17 mcp_sw(10, 11, 12, 13, addr);
assertEqual(addr, mcp_sw.getAddress());
}
}
unittest(test_HW_constructors)
{
MCP23S17 mcp_hw0(10);
assertEqual(0x00, mcp_hw0.getAddress());
assertTrue(mcp_hw0.usesHWSPI());
assertEqual(MCP23S17_OK, mcp_hw0.lastError());
for (int addr = 0; addr < 8; addr++)
{
MCP23S17 mcp_hw(10, addr);
assertEqual(addr, mcp_hw.getAddress());
}
MCP23S17 mcp_hw1(10, &SPI);
assertEqual(0x00, mcp_hw1.getAddress());
assertTrue(mcp_hw1.usesHWSPI());
for (int addr = 0; addr < 8; addr++)
{
MCP23S17 mcp_hw(10, addr, &SPI);
assertEqual(addr, mcp_hw.getAddress());
assertTrue(mcp_hw.usesHWSPI());
}
}
////////////////////////////////////////////////////////////
//
// 1 bit interface
//
unittest(test_pinMode)
{
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.pinMode(16, INPUT));
assertEqual(MCP23S17_PIN_ERROR, mcp_hw.lastError());
assertFalse(mcp_hw.pinMode(0, 4));
assertEqual(MCP23S17_VALUE_ERROR, mcp_hw.lastError());
}
unittest(test_digitalWrite)
{
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.digitalWrite(16, 0));
assertEqual(MCP23S17_PIN_ERROR, mcp_hw.lastError());
}
unittest(test_digitalRead)
{
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertEqual(MCP23S17_INVALID_READ, mcp_hw.digitalRead(16));
assertEqual(MCP23S17_PIN_ERROR, mcp_hw.lastError());
}
unittest(test_Polarity)
{
bool flag;
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.setPolarity(16, true));
assertEqual(MCP23S17_PIN_ERROR, mcp_hw.lastError());
assertFalse(mcp_hw.getPolarity(16, flag));
assertEqual(MCP23S17_PIN_ERROR, mcp_hw.lastError());
}
unittest(test_Pullup)
{
bool flag;
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.setPullup(16, true));
assertEqual(MCP23S17_PIN_ERROR, mcp_hw.lastError());
assertFalse(mcp_hw.getPullup(16, flag));
assertEqual(MCP23S17_PIN_ERROR, mcp_hw.lastError());
}
////////////////////////////////////////////////////////////
//
// 8 bit interface
//
unittest(test_pinMode8)
{
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.pinMode8(2, 0xFF));
assertEqual(MCP23S17_PORT_ERROR, mcp_hw.lastError());
}
unittest(test_Write8)
{
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.write8(2, HIGH));
assertEqual(MCP23S17_PORT_ERROR, mcp_hw.lastError());
}
unittest(test_Read8)
{
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertEqual(MCP23S17_INVALID_READ, mcp_hw.read8(2));
assertEqual(MCP23S17_PORT_ERROR, mcp_hw.lastError());
}
unittest(test_Polarity8)
{
uint8_t mask;
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.setPolarity8(2, 0xFF));
assertEqual(MCP23S17_PORT_ERROR, mcp_hw.lastError());
assertFalse(mcp_hw.getPolarity8(2, mask));
assertEqual(MCP23S17_PORT_ERROR, mcp_hw.lastError());
}
unittest(test_Pullup8)
{
uint8_t mask;
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
assertFalse(mcp_hw.setPullup8(2, 0xFF));
assertEqual(MCP23S17_PORT_ERROR, mcp_hw.lastError());
assertFalse(mcp_hw.getPullup8(2, mask));
assertEqual(MCP23S17_PORT_ERROR, mcp_hw.lastError());
}
////////////////////////////////////////////////////////////
//
// 16 bit interface
//
// no testable calls yet.
////////////////////////////////////////////////////////////
//
// MISC
//
unittest(test_SPIspeed)
{
MCP23S17 mcp_hw(10);
assertEqual(MCP23S17_OK, mcp_hw.lastError());
for (uint32_t speed = 1000000; speed < 16000000; speed += 1000000)
{
mcp_hw.setSPIspeed(speed);
assertEqual(speed, mcp_hw.getSPIspeed());
}
}