From 99c3b27b0e415f011a9433c4bc8fe13b1d6324ce Mon Sep 17 00:00:00 2001 From: Rob Tillaart Date: Fri, 7 Jan 2022 16:29:21 +0100 Subject: [PATCH] initial version (#1) * initial version --- .arduino-ci.yml | 11 + .github/workflows/arduino-lint.yml | 13 + .github/workflows/arduino_test_runner.yml | 17 + .github/workflows/jsoncheck.yml | 18 + LICENSE | 2 +- MCP23S17.cpp | 572 ++++++++++++++++++ MCP23S17.h | 93 +++ README.md | 76 ++- .../MCP23S17_digitalRead.ino | 66 ++ .../MCP23S17_digitalWrite.ino | 74 +++ .../MCP23S17_performance.ino | 105 ++++ .../MCP23S17_performance/performance_0.1.0.md | 31 + library.json | 23 + library.properties | 11 + test/unit_test_001.cpp | 69 +++ 15 files changed, 1179 insertions(+), 2 deletions(-) create mode 100644 .arduino-ci.yml create mode 100644 .github/workflows/arduino-lint.yml create mode 100644 .github/workflows/arduino_test_runner.yml create mode 100644 .github/workflows/jsoncheck.yml create mode 100644 MCP23S17.cpp create mode 100644 MCP23S17.h create mode 100644 examples/MCP23S17_digitalRead/MCP23S17_digitalRead.ino create mode 100644 examples/MCP23S17_digitalWrite/MCP23S17_digitalWrite.ino create mode 100644 examples/MCP23S17_performance/MCP23S17_performance.ino create mode 100644 examples/MCP23S17_performance/performance_0.1.0.md create mode 100644 library.json create mode 100644 library.properties create mode 100644 test/unit_test_001.cpp diff --git a/.arduino-ci.yml b/.arduino-ci.yml new file mode 100644 index 0000000..cecf585 --- /dev/null +++ b/.arduino-ci.yml @@ -0,0 +1,11 @@ +compile: + # Choosing to run compilation tests on 2 different Arduino platforms + platforms: + - uno + # - due + # - zero + # - leonardo + - m4 + - esp32 + # - esp8266 + # - mega2560 diff --git a/.github/workflows/arduino-lint.yml b/.github/workflows/arduino-lint.yml new file mode 100644 index 0000000..b2ca058 --- /dev/null +++ b/.github/workflows/arduino-lint.yml @@ -0,0 +1,13 @@ + +name: Arduino-lint + +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: arduino/arduino-lint-action@v1 + with: + library-manager: update + compliance: strict diff --git a/.github/workflows/arduino_test_runner.yml b/.github/workflows/arduino_test_runner.yml new file mode 100644 index 0000000..096b975 --- /dev/null +++ b/.github/workflows/arduino_test_runner.yml @@ -0,0 +1,17 @@ +--- +name: Arduino CI + +on: [push, pull_request] + +jobs: + runTest: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - run: | + gem install arduino_ci + arduino_ci.rb diff --git a/.github/workflows/jsoncheck.yml b/.github/workflows/jsoncheck.yml new file mode 100644 index 0000000..04603d0 --- /dev/null +++ b/.github/workflows/jsoncheck.yml @@ -0,0 +1,18 @@ +name: JSON check + +on: + push: + paths: + - '**.json' + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: json-syntax-check + uses: limitusus/json-syntax-check@v1 + with: + pattern: "\\.json$" + diff --git a/LICENSE b/LICENSE index ea27d2d..5777550 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Rob Tillaart +Copyright (c) 2021-2022 Rob Tillaart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MCP23S17.cpp b/MCP23S17.cpp new file mode 100644 index 0000000..c612a1c --- /dev/null +++ b/MCP23S17.cpp @@ -0,0 +1,572 @@ +// +// FILE: MCP23S17.cpp +// AUTHOR: Rob Tillaart +// VERSION: 0.1.0 +// PURPOSE: Arduino library for SPI MCP23S17 16 channel port expander +// DATE: 2021-12-30 +// URL: https://github.com/RobTillaart/MCP23S17_RT + +// +// HISTORY: +// 0.1.0 2021-12-30 initial version (a 2019 version did not make it) + + +#include "Arduino.h" +#include "MCP23S17.h" + + +// Registers // description datasheet +#define MCP23S17_DDR_A 0x00 // Data Direction Register A P18 +#define MCP23S17_DDR_B 0x01 // Data Direction Register B P18 +#define MCP23S17_POL_A 0x02 // Input Polarity A P18 +#define MCP23S17_POL_B 0x03 // Input Polarity B P18 +#define MCP23S17_GPINTEN_A 0x04 // NOT USED interrupt enable P19 +#define MCP23S17_GPINTEN_B 0x05 // NOT USED +#define MCP23S17_DEFVAL_A 0x06 // NOT USED interrupt def P19 +#define MCP23S17_DEFVAL_B 0x07 // NOT USED +#define MCP23S17_INTCON_A 0x08 // NOT USED interrupt control P20 +#define MCP23S17_INTCON_B 0x09 // NOT USED +#define MCP23S17_IOCR 0x0A // IO control register P20 +#define MCP23S17_IOCR2 0x0B // NOT USED +#define MCP23S17_PUR_A 0x0C // Pull Up Resistors A P22 +#define MCP23S17_PUR_B 0x0D // Pull Up Resistors A P22 +#define MCP23S17_INTF_A 0x0E // NOT USED interrupt flag P22 +#define MCP23S17_INTF_B 0x0F // NOT USED +#define MCP23S17_INTCAP_A 0x10 // NOT USED interrupt capture P23 +#define MCP23S17_INTCAP_B 0x11 // NOT USED +#define MCP23S17_GPIO_A 0x12 // General Purpose IO A P23 +#define MCP23S17_GPIO_B 0x13 // General Purpose IO B P23 +#define MCP23S17_OLAT_A 0x14 // NOT USED output latch P24 +#define MCP23S17_OLAT_B 0x15 // NOT USED + + +// low level read / write masks +#define MCP23S17_WRITE_REG 0x40 +#define MCP23S17_READ_REG 0x41 + +/* +MCP23S17::MCP23S17() +{ +} +*/ + + +MCP23S17::MCP23S17(uint8_t select, uint8_t dataIn, uint8_t dataOut, uint8_t clock, uint8_t address) +{ + _address = address; + _select = select; + _dataIn = dataIn; + _dataOut = dataOut; + _clock = clock; + _error = MCP23S17_OK; + _hwSPI = false; +} + + +MCP23S17::MCP23S17(uint8_t select, uint8_t address) +{ + _address = address; + _select = select; + _error = MCP23S17_OK; + _hwSPI = true; +} + + +bool MCP23S17::begin() +{ + ::pinMode(_select, OUTPUT); + ::digitalWrite(_select, HIGH); + + _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(); + } + else + { + ::pinMode(_dataIn, INPUT); + ::pinMode(_dataOut, OUTPUT); + ::pinMode(_clock, OUTPUT); + ::digitalWrite(_dataOut, LOW); + ::digitalWrite(_clock, LOW); + } + + // check connected + if (! isConnected()) return false; + + // disable address increment (datasheet) + if (! writeReg(MCP23S17_IOCR, 0b00100000)) return false; // TODO MAGIC NR + // Force INPUT_PULLUP + if (! writeReg(MCP23S17_PUR_A, 0xFF)) return false; + if (! writeReg(MCP23S17_PUR_B, 0xFF)) return false; + return true; +} + + +bool MCP23S17::isConnected() +{ + _error = MCP23S17_OK; + return true; +} + + +// single pin interface +// pin = 0..15 +// mode = INPUT, OUTPUT, INPUT_PULLUP (= same as INPUT) +bool MCP23S17::pinMode(uint8_t pin, uint8_t mode) +{ + if (pin > 15) + { + _error = MCP23S17_PIN_ERROR; + return false; + } + if ((mode != INPUT) && (mode != INPUT_PULLUP) && (mode != OUTPUT)) + { + _error = MCP23S17_VALUE_ERROR; + return false; + } + + uint8_t dataDirectionRegister = MCP23S17_DDR_A; + if (pin > 7) + { + dataDirectionRegister = MCP23S17_DDR_B; + pin -= 8; + } + uint8_t val = readReg(dataDirectionRegister); + if (_error != MCP23S17_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 != MCP23S17_OK) + { + return false; + } + return true; +} + + +// pin = 0..15 +// value = LOW, HIGH +bool MCP23S17::digitalWrite(uint8_t pin, uint8_t value) +{ + if (pin > 15) + { + _error = MCP23S17_PIN_ERROR; + return false; + } + uint8_t IOR = MCP23S17_GPIO_A; + if (pin > 7) + { + IOR = MCP23S17_GPIO_B; + pin -= 8; + } + + uint8_t val = readReg(IOR); + if (_error != MCP23S17_OK) + { + return false; + } + + uint8_t mask = 1 << pin; + if (value) + { + val |= mask; + } + else + { + val &= ~mask; + } + writeReg(IOR, val); + if (_error != MCP23S17_OK) + { + return false; + } + return true; +} + + +uint8_t MCP23S17::digitalRead(uint8_t pin) +{ + if (pin > 15) + { + _error = MCP23S17_PIN_ERROR; + return MCP23S17_INVALID_READ; + } + uint8_t IOR = MCP23S17_GPIO_A; + if (pin > 7) + { + IOR = MCP23S17_GPIO_B; + pin -= 8; + } + + uint8_t val = readReg(IOR); + if (_error != MCP23S17_OK) + { + return MCP23S17_INVALID_READ; + } + uint8_t mask = 1 << pin; + if (val & mask) return HIGH; + return LOW; +} + + +// pin = 0..15 +// reversed = true or false +bool MCP23S17::setPolarity(uint8_t pin, bool reversed) +{ + if (pin > 15) + { + _error = MCP23S17_PIN_ERROR; + return false; + } + uint8_t inputPolarityRegister = MCP23S17_POL_A; + if (pin > 7) + { + inputPolarityRegister = MCP23S17_POL_B; + pin -= 8; + } + uint8_t val = readReg(inputPolarityRegister); + if (_error != MCP23S17_OK) + { + return false; + } + uint8_t mask = 1 << pin; + if (reversed) + { + val |= mask; + } + else + { + val &= ~mask; + } + writeReg(inputPolarityRegister, val); + if (_error != MCP23S17_OK) + { + return false; + } + return true; +} + + +bool MCP23S17::getPolarity(uint8_t pin, bool &reversed) +{ + if (pin > 15) + { + _error = MCP23S17_PIN_ERROR; + return false; + } + uint8_t inputPolarityRegister = MCP23S17_POL_A; + if (pin > 7) + { + inputPolarityRegister = MCP23S17_POL_B; + pin -= 8; + } + uint8_t val = readReg(inputPolarityRegister); + if (_error != MCP23S17_OK) + { + return false; + } + uint8_t mask = 1 << pin; + reversed = (val & mask) > 0; + return true; +} + + +// pin = 0..15 +// pullup = true or false +bool MCP23S17::setPullup(uint8_t pin, bool pullup) +{ + if (pin > 15) + { + _error = MCP23S17_PIN_ERROR; + return false; + } + uint8_t inputPullupRegister = MCP23S17_PUR_A; + if (pin > 7) + { + inputPullupRegister = MCP23S17_PUR_B; + pin -= 8; + } + uint8_t val = readReg(inputPullupRegister); + if (_error != MCP23S17_OK) + { + return false; + } + uint8_t mask = 1 << pin; + if (pullup) + { + val |= mask; + } + else + { + val &= ~mask; + } + writeReg(inputPullupRegister, val); + if (_error != MCP23S17_OK) + { + return false; + } + return true; +} + + +bool MCP23S17::getPullup(uint8_t pin, bool &pullup) +{ + if (pin > 15) + { + _error = MCP23S17_PIN_ERROR; + return false; + } + uint8_t inputPullupRegister = MCP23S17_PUR_A; + if (pin > 7) + { + inputPullupRegister = MCP23S17_PUR_B; + pin -= 8; + } + uint8_t val = readReg(inputPullupRegister); + if (_error != MCP23S17_OK) + { + return false; + } + uint8_t mask = 1 << pin; + pullup = (val & mask) > 0; + return true; +} + + +void MCP23S17::setSPIspeed(uint32_t speed) +{ + _SPIspeed = speed; + _spi_settings = SPISettings(_SPIspeed, MSBFIRST, SPI_MODE0); +}; + + + +/////////////////////////////////////////////////////////////////////// +// 8 pins interface +// whole register at once +// port = 0..1 +// value = 0..0xFF bit pattern +bool MCP23S17::pinMode8(uint8_t port, uint8_t value) +{ + if (port > 1) + { + _error = MCP23S17_PORT_ERROR; + return false; + } + if (port == 0) writeReg(MCP23S17_DDR_A, value); + if (port == 1) writeReg(MCP23S17_DDR_B, value); + _error = MCP23S17_OK; + return true; +} + + +bool MCP23S17::write8(uint8_t port, uint8_t value) // port = 0..1 +{ + if (port > 1) + { + _error = MCP23S17_PORT_ERROR; + return false; + } + if (port == 0) writeReg(MCP23S17_GPIO_A, value); + if (port == 1) writeReg(MCP23S17_GPIO_B, value); + _error = MCP23S17_OK; + return true; +} + + +int MCP23S17::read8(uint8_t port) +{ + if (port > 1) + { + _error = MCP23S17_PORT_ERROR; + return MCP23S17_INVALID_READ; + } + _error = MCP23S17_OK; + if (port == 0) return readReg(MCP23S17_GPIO_A); + return readReg(MCP23S17_GPIO_B); // port == 1 +} + + +// port = 0..1 +// mask = 0..0xFF bit pattern +bool MCP23S17::setPolarity8(uint8_t port, uint8_t mask) +{ + if (port > 1) + { + _error = MCP23S17_PORT_ERROR; + return false; + } + if (port == 0) writeReg(MCP23S17_POL_A, mask); + if (port == 1) writeReg(MCP23S17_POL_B, mask); + if (_error != MCP23S17_OK) + { + return false; + } + return true; +} + + +bool MCP23S17::getPolarity8(uint8_t port, uint8_t &mask) +{ + if (port > 1) + { + _error = MCP23S17_PORT_ERROR; + return false; + } + if (port == 0) mask = readReg(MCP23S17_POL_A); + if (port == 1) mask = readReg(MCP23S17_POL_B); + if (_error != MCP23S17_OK) + { + return false; + } + return true; +} + + +// port = 0..1 +// mask = 0..0xFF bit pattern +bool MCP23S17::setPullup8(uint8_t port, uint8_t mask) +{ + if (port > 1) + { + _error = MCP23S17_PORT_ERROR; + return false; + } + if (port == 0) writeReg(MCP23S17_PUR_A, mask); + if (port == 1) writeReg(MCP23S17_PUR_B, mask); + if (_error != MCP23S17_OK) + { + return false; + } + return true; +} + + +bool MCP23S17::getPullup8(uint8_t port, uint8_t &mask) +{ + if (port > 1) + { + _error = MCP23S17_PORT_ERROR; + return false; + } + if (port == 0) mask = readReg(MCP23S17_PUR_A); + if (port == 1) mask = readReg(MCP23S17_PUR_B); + if (_error != MCP23S17_OK) + { + return false; + } + return true; +} + + +int MCP23S17::lastError() +{ + int e = _error; + _error = MCP23S17_OK; // reset error after read. + return e; +} + + +//////////////////////////////////////////////////// +// +// PRIVATE +// + +bool MCP23S17::writeReg(uint8_t reg, uint8_t value) +{ + _error = MCP23S17_OK; + + if (reg > MCP23S17_OLAT_B) + { + _error = 0xFF; // TODO MAGIC NR + 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(); + } + else + { + swSPI_transfer(MCP23S17_WRITE_REG | (_address << 1) ); + swSPI_transfer(reg); + swSPI_transfer(value); + } + ::digitalWrite(_select, HIGH); + return true; +} + + +uint8_t MCP23S17::readReg(uint8_t reg) +{ + uint8_t rv = 0; + + _error = MCP23S17_OK; + + if (reg > MCP23S17_OLAT_B) + { + _error = 0xFF; // TODO MAGIC NR + 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(); + } + else + { + swSPI_transfer(MCP23S17_READ_REG | (_address << 1) ); + swSPI_transfer(reg); + rv = swSPI_transfer(0xFF); + } + ::digitalWrite(_select, HIGH); + return rv; +} + + +uint8_t MCP23S17::swSPI_transfer(uint8_t val) +{ + uint8_t clk = _clock; + uint8_t dao = _dataOut; + uint8_t dai = _dataIn; + + uint8_t rv = 0; + for (uint8_t mask = 0x80; mask > 0; mask >>= 1) + { + ::digitalWrite(dao, (val & mask)); + ::digitalWrite(clk, HIGH); + if (::digitalRead(dai) == HIGH) rv |= mask; + ::digitalWrite(clk, LOW); + } + return rv; +} + + +// -- END OF FILE -- + diff --git a/MCP23S17.h b/MCP23S17.h new file mode 100644 index 0000000..60b4b85 --- /dev/null +++ b/MCP23S17.h @@ -0,0 +1,93 @@ +#pragma once +// +// FILE: MCP23S17.h +// AUTHOR: Rob Tillaart +// VERSION: 0.1.0 +// PURPOSE: Arduino library for SPI MCP23S17 16 channel port expander +// DATE: 2021-12-30 +// URL: https://github.com/RobTillaart/MCP23S17_RT + + +#include "Arduino.h" +#include "SPI.h" + + +#define MCP23S17_LIB_VERSION (F("0.1.0")) + +#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_INVALID_READ -100 + + +class MCP23S17 +{ +public: + // MCP23S17(); + + 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); + + bool begin(); + bool isConnected(); // needed ? + + + // single pin interface + // mode = INPUT, OUTPUT or INPUT_PULLUP (==INPUT) + bool pinMode(uint8_t pin, uint8_t mode); + bool digitalWrite(uint8_t pin, uint8_t value); + uint8_t digitalRead(uint8_t pin); + + bool setPolarity(uint8_t pin, bool reversed); + bool getPolarity(uint8_t pin, bool &reversed); + bool setPullup(uint8_t pin, bool pullup); + bool getPullup(uint8_t pin, bool &pullup); + + + // 8 pins interface + // port = 0..1 + // value = bit pattern + bool pinMode8(uint8_t port, uint8_t value); + bool write8(uint8_t port, uint8_t value); + int read8(uint8_t port); + + + bool setPolarity8(uint8_t port, uint8_t mask); + bool getPolarity8(uint8_t port, uint8_t &mask); + bool setPullup8(uint8_t port, uint8_t mask); + bool getPullup8(uint8_t port, uint8_t &mask); + + // speed in Hz + void setSPIspeed(uint32_t speed); + uint32_t getSPIspeed() { return _SPIspeed; }; + + // debugging + bool usesHWSPI() { return _hwSPI; }; + int lastError(); + +private: + bool writeReg(uint8_t reg, uint8_t value); + uint8_t readReg(uint8_t reg); + + uint8_t _address = 0; + uint8_t _select = 0; + uint8_t _dataOut = 0; + uint8_t _dataIn = 0; + 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; + SPISettings _spi_settings; + + uint8_t swSPI_transfer(uint8_t val); +}; + + +// -- END OF FILE -- + diff --git a/README.md b/README.md index e7ca535..14f1853 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,76 @@ + +[![Arduino CI](https://github.com/RobTillaart/MCP23S17/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) +[![Arduino-lint](https://github.com/RobTillaart/MCP23S17/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/MCP23S17/actions/workflows/arduino-lint.yml) +[![JSON check](https://github.com/RobTillaart/MCP23S17/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/MCP23S17/actions/workflows/jsoncheck.yml) +[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/MCP23S17/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/RobTillaart/MCP23S17.svg?maxAge=3600)](https://github.com/RobTillaart/MCP23S17/releases) + + # MCP23S17 -Arduino library for SPI based MCP23S17 + +Arduino library for MCP23S17 16 channel SPI port expander. + + +## Description + +This experimental library gives easy control over the 16 pins of a (SPI) MCP23S17 chip. + +This IC is strongly related tot the MCP23017 I2C port expander - https://github.com/RobTillaart/MCP23017_RT +Programming Interface is kept the same as much as possible. + + +## Interface + +### Constructor + +- **MCP23S17(uint8_t select, uint8_t data, uint8_t clock)** constructor SW SPI +- **MCP23S17(uint8_t select)** constructor HW SPI +- **bool begin()** returns true if successful. +- **bool isConnected()** returns true if connected, false otherwise. + + +### Single pin interface + +- **bool pinMode(uint8_t pin, uint8_t mode)** pin = 0..15, mode = INPUT, OUTPUT, returns true if successful. +- **bool digitalWrite(uint8_t pin, uint8_t value)** pin = 0..15, value = LOW(0) HIGH (!0), returns true if successful. +- **uint8_t digitalRead(uint8_t pin)** pin = 0..15, returns LOW or HIGH, might set the lastError(); +- **bool setPolarity(uint8_t pin, bool reversed)** pin = 0..15, set reversed flag, returns true if successful. +- **bool getPolarity(uint8_t pin, bool &reversed)** pin = 0..15, reads reversed flag, returns true if successful. +- **bool setPullup(uint8_t pin, bool pullup)** pin = 0..15, set pull-up flag, returns true if successful. +- **bool getPullup(uint8_t pin, bool &pullup)** pin = 0..15, reads pull-up flag, returns true if successful. + + +### 8 pins interface + +- **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 getPolarity8(uint8_t port, uint8_t &mask)** port = 0..1, reads polarity of 8 channels at once. +- **bool setPullup8(uint8_t port, uint8_t mask)** port = 0..1,, sets pull-up for 8 channels at once. +- **bool getPullup8(uint8_t port, uint8_t &mask)** port = 0..1, reads pull-up for 8 channels at once. + + +### Error codes + +- **int lastError()** Above functions set an error flag that can be read with this function. +Reading it will reset the flag to **MCP23S17_OK**. + +| DESCRIPTION | VALUE | +|:----------------------|:-----:| +| MCP23S17_OK | 0x00 | +| MCP23S17_PIN_ERROR | 0x81 | +| MCP23S17_I2C_ERROR | 0x82 | +| MCP23S17_VALUE_ERROR | 0x83 | +| MCP23S17_PORT_ERROR | 0x84 | + + +## Operation + +See examples. + + +## Future + +- keep functional in sync with MCP23017_RT + diff --git a/examples/MCP23S17_digitalRead/MCP23S17_digitalRead.ino b/examples/MCP23S17_digitalRead/MCP23S17_digitalRead.ino new file mode 100644 index 0000000..133a975 --- /dev/null +++ b/examples/MCP23S17_digitalRead/MCP23S17_digitalRead.ino @@ -0,0 +1,66 @@ +// +// FILE: MCP23S17_digitalRead.ino +// AUTHOR: Rob Tillaart +// DATE: 2021-12-30 +// PUPROSE: test MCP23017 library + + +#include "MCP23S17.h" +#include "SPI.h" + + +MCP23S17 MCP(10); +int rv = 0; + + +void setup() +{ + Serial.begin(115200); + Serial.println(); + Serial.print("MCP23S17_test version: "); + Serial.println(MCP23S17_LIB_VERSION); + delay(100); + + SPI.begin(); + rv = MCP.begin(); + Serial.println(rv); + + rv = MCP.pinMode8(0, 0xFF); // CHECK + Serial.println(rv); + rv = MCP.pinMode8(1, 0xFF); + Serial.println(rv); + + + Serial.print("HWSPI: "); + Serial.println(MCP.usesHWSPI()); + + Serial.println("TEST digitalRead(pin)"); + for (int pin = 0; pin < 16; pin++) + { + int val = MCP.digitalRead(pin); + Serial.print(val); + Serial.print(' '); + delay(100); + } + Serial.println(); +} + + +void loop() +{ + delay(1000); + Serial.println("TEST digitalRead(pin)"); + for (int pin = 0; pin < 16; pin++) + { + int val = MCP.digitalRead(pin); + Serial.print(val); + Serial.print(' '); + delay(100); + } + Serial.println(); + +} + + +// -- END OF FILE -- + diff --git a/examples/MCP23S17_digitalWrite/MCP23S17_digitalWrite.ino b/examples/MCP23S17_digitalWrite/MCP23S17_digitalWrite.ino new file mode 100644 index 0000000..8f6b4a4 --- /dev/null +++ b/examples/MCP23S17_digitalWrite/MCP23S17_digitalWrite.ino @@ -0,0 +1,74 @@ +// +// FILE: MCP23S17_digitalWrite.ino +// AUTHOR: Rob Tillaart +// DATE: 2021-12-30 +// PUPROSE: test MCP23017 library + + +#include "MCP23S17.h" +#include "SPI.h" + +MCP23S17 MCP(10, 5, 6, 7); // SW SPI address 0x00 + + +void setup() +{ + Serial.begin(115200); + Serial.println(); + Serial.print("MCP23S17_test version: "); + Serial.println(MCP23S17_LIB_VERSION); + delay(100); + + SPI.begin(); + bool b = MCP.begin(); + // Serial.println(b ? "true" : "false"); + delay(100); + + MCP.pinMode8(0, 0x00); // 0 = output , 1 = input + MCP.pinMode8(1, 0x00); + + Serial.println("TEST digitalWrite(0)"); + delay(100); + for (int i = 0; i < 16; i++) + { + MCP.digitalWrite(0, i % 2); // alternating HIGH/LOW + Serial.print(i % 2); + Serial.print(' '); + delay(100); + } + Serial.println(); + Serial.println(); + + Serial.println("TEST digitalWrite(pin)"); + delay(100); + for (int pin = 0; pin < 16; pin++) + { + MCP.digitalWrite(pin, 1 - pin % 2); // alternating HIGH/LOW + Serial.print(1 - pin % 2); + Serial.print(' '); + delay(100); + } + Serial.println(); + Serial.println(); + + Serial.println("TEST read back"); + + for (int pin = 0; pin < 16; pin++) + { + int val = MCP.digitalRead(pin); + Serial.print(val); + Serial.print(' '); + delay(100); + } + Serial.println(); + Serial.println(); +} + + +void loop() +{ +} + + +// -- END OF FILE -- + diff --git a/examples/MCP23S17_performance/MCP23S17_performance.ino b/examples/MCP23S17_performance/MCP23S17_performance.ino new file mode 100644 index 0000000..351f81d --- /dev/null +++ b/examples/MCP23S17_performance/MCP23S17_performance.ino @@ -0,0 +1,105 @@ +// +// FILE: MCP23S17_performance.ino +// AUTHOR: Rob Tillaart +// DATE: 2021-12-30 +// 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 + +uint32_t start, stop; + + +void setup() +{ + Serial.begin(115200); + Serial.println(); + Serial.print("MCP23S17_test version: "); + Serial.println(MCP23S17_LIB_VERSION); + Serial.println(); + delay(100); + + SPI.begin(); + bool b = MCP.begin(); + + Serial.print("HWSPI: "); + Serial.println(MCP.usesHWSPI()); + + MCP.pinMode8(0, 0x00); // 0 = output , 1 = input + MCP.pinMode8(1, 0x00); + + Serial.println("\ntime in microseconds\n"); + delay(100); + + + Serial.print("TEST digitalWrite(0, value):\t"); + delay(100); + start = micros(); + for (int i = 0; i < 16; i++) + { + MCP.digitalWrite(0, i & 0x01); // alternating HIGH/LOW + } + stop = micros(); + Serial.println((stop - start) / 16.0); + + + Serial.print("TEST digitalWrite(pin, value):\t"); + delay(100); + start = micros(); + for (int pin = 0; pin < 16; pin++) + { + MCP.digitalWrite(pin, 1 - pin % 2); // alternating HIGH/LOW + } + stop = micros(); + Serial.println((stop - start) / 16.0); + + + Serial.print("TEST digitalRead(pin):\t"); + delay(100); + start = micros(); + for (int pin = 0; pin < 16; pin++) + { + volatile int val = MCP.digitalRead(pin); + } + stop = micros(); + Serial.println((stop - start) / 16.0); + Serial.println(); + + + ////////////////////////////////////////////////// + // + // write8 read8 interface + // + Serial.print("TEST write8(port, mask):\t"); + delay(100); + start = micros(); + MCP.write8(0, 0xAA); // alternating HIGH/LOW + MCP.write8(1, 0xAA); // alternating HIGH/LOW + stop = micros(); + Serial.println((stop - start) / 2.0); + + + Serial.print("TEST read8(port):\t"); + delay(100); + start = micros(); + volatile int val = MCP.read8(0); + val = MCP.read8(1); + stop = micros(); + Serial.println((stop - start) / 2.0); + Serial.println(); + + Serial.println("\ndone..."); +} + + +void loop() +{ +} + + +// -- END OF FILE -- + diff --git a/examples/MCP23S17_performance/performance_0.1.0.md b/examples/MCP23S17_performance/performance_0.1.0.md new file mode 100644 index 0000000..60df424 --- /dev/null +++ b/examples/MCP23S17_performance/performance_0.1.0.md @@ -0,0 +1,31 @@ + + +# Performance test + +Test sketch MCP23S17_performance.ino + +Max clock frequency 10 MHz, for an UNO this is 8 MHz (divider of CPU clock) + +### Library version: 0.1.0 + +| Action | SW SPI | HW 1 MHz | HW 2 MHz | HW 4 MHz | HW 8 MHz | +|:------------------------------|--------:|---------:|---------:|---------:|---------:| +| TEST digitalWrite(0, value) | 892.25 | 87.75 | 64.00 | 52.25 | 46.25 | +| TEST digitalWrite(pin, value) | 893.75 | 89.75 | 65.25 | 53.00 | 47.50 | +| TEST digitalRead(pin) | 447.75 | 44.75 | 32.75 | 26.75 | 24.25 | +| | | | | | | +| TEST write8(port, mask) | 448.00 | 44.00 | 32.00 | 26.00 | 22.00 | +| TEST read8(port) | 448.00 | 44.00 | 32.00 | 26.00 | 22.00 | + + +### Notes + +Performance as expected. + + +### Future + +- test ESP32 and other platforms +- register based IO version for the SW SPI on AVR ? + + diff --git a/library.json b/library.json new file mode 100644 index 0000000..8bb9ec6 --- /dev/null +++ b/library.json @@ -0,0 +1,23 @@ +{ + "name": "MCP23S17", + "keywords": "MCP23S17,SPI, 16 IO, polarity, pullup", + "description": "Arduino library for SPI MCP23S17 16 channel port expander", + "authors": + [ + { + "name": "Rob Tillaart", + "email": "Rob.Tillaart@gmail.com", + "maintainer": true + } + ], + "repository": + { + "type": "git", + "url": "https://github.com/RobTillaart/MCP23S17_RT.git" + }, + "version": "0.1.0", + "license": "MIT", + "frameworks": "arduino", + "platforms": "*", + "headers": "MCP23S17.h" +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..7f9177c --- /dev/null +++ b/library.properties @@ -0,0 +1,11 @@ +name=MCP23S17_RT +version=0.1.0 +author=Rob Tillaart +maintainer=Rob Tillaart +sentence=Arduino library for SPI MCP23S17 16 channel port expander 16 IO-lines +paragraph= setPolarity, setPullup +category=Signal Input/Output +url=https://github.com/RobTillaart/MCP23S17_RT +architectures=* +includes=MCP23S17.h +depends= diff --git a/test/unit_test_001.cpp b/test/unit_test_001.cpp new file mode 100644 index 0000000..23dc7c8 --- /dev/null +++ b/test/unit_test_001.cpp @@ -0,0 +1,69 @@ +// +// FILE: unit_test_001.cpp +// AUTHOR: Rob Tillaart +// DATE: 2021-12-30 +// PURPOSE: unit tests for the MCP23017 +// https://github.com/RobTillaart/MCP23017_RT +// https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md +// + +// supported assertions +// ---------------------------- +// assertEqual(expected, actual); // a == b +// assertNotEqual(unwanted, actual); // a != b +// assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b)) +// assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b)) +// assertLess(upperBound, actual); // a < b +// assertMore(lowerBound, actual); // a > b +// assertLessOrEqual(upperBound, actual); // a <= b +// assertMoreOrEqual(lowerBound, actual); // a >= b +// assertTrue(actual); +// assertFalse(actual); +// assertNull(actual); + +// // special cases for floats +// assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon +// assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon +// assertInfinity(actual); // isinf(a) +// assertNotInfinity(actual); // !isinf(a) +// assertNAN(arg); // isnan(a) +// assertNotNAN(arg); // !isnan(a) + +#include + + +#include "Arduino.h" +#include "MCP23S17.h" + + +unittest_setup() +{ + fprintf(stderr, "MCP23S17_LIB_VERSION: %s\n", (char *) MCP23S17_LIB_VERSION); +} + + +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); +} + + +unittest(test_constructor) +{ + +} + + +unittest_main() + +// --------