Repository: adafruit/Adafruit-MCP23017-Arduino-Library Branch: master Commit: 9e67e3732d1a Files: 17 Total size: 35.6 KB Directory structure: gitextract_3qygkpm0/ ├── .github/ │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ └── githubci.yml ├── README.md ├── examples/ │ ├── mcp23xxx_blink/ │ │ └── mcp23xxx_blink.ino │ ├── mcp23xxx_button/ │ │ └── mcp23xxx_button.ino │ ├── mcp23xxx_combo/ │ │ └── mcp23xxx_combo.ino │ └── mcp23xxx_interrupt/ │ └── mcp23xxx_interrupt.ino ├── keywords.txt ├── library.properties ├── license.txt └── src/ ├── Adafruit_MCP23X08.cpp ├── Adafruit_MCP23X08.h ├── Adafruit_MCP23X17.cpp ├── Adafruit_MCP23X17.h ├── Adafruit_MCP23XXX.cpp └── Adafruit_MCP23XXX.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ Thank you for opening an issue on an Adafruit Arduino library repository. To improve the speed of resolution please review the following guidelines and common troubleshooting steps below before creating the issue: - **Do not use GitHub issues for troubleshooting projects and issues.** Instead use the forums at http://forums.adafruit.com to ask questions and troubleshoot why something isn't working as expected. In many cases the problem is a common issue that you will more quickly receive help from the forum community. GitHub issues are meant for known defects in the code. If you don't know if there is a defect in the code then start with troubleshooting on the forum first. - **If following a tutorial or guide be sure you didn't miss a step.** Carefully check all of the steps and commands to run have been followed. Consult the forum if you're unsure or have questions about steps in a guide/tutorial. - **For Arduino projects check these very common issues to ensure they don't apply**: - For uploading sketches or communicating with the board make sure you're using a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes very hard to tell the difference between a data and charge cable! Try using the cable with other devices or swapping to another cable to confirm it is not the problem. - **Be sure you are supplying adequate power to the board.** Check the specs of your board and plug in an external power supply. In many cases just plugging a board into your computer is not enough to power it and other peripherals. - **Double check all soldering joints and connections.** Flakey connections cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. - **Ensure you are using an official Arduino or Adafruit board.** We can't guarantee a clone board will have the same functionality and work as expected with this code and don't support them. If you're sure this issue is a defect in the code and checked the steps above please fill in the following fields to provide enough troubleshooting information. You may delete the guideline and text above to just leave the following details: - Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE** - Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO VERSION HERE** - List the steps to reproduce the problem below (if possible attach a sketch or copy the sketch code in too): **LIST REPRO STEPS BELOW** ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ Thank you for creating a pull request to contribute to Adafruit's GitHub code! Before you open the request please review the following guidelines and tips to help it be more easily integrated: - **Describe the scope of your change--i.e. what the change does and what parts of the code were modified.** This will help us understand any risks of integrating the code. - **Describe any known limitations with your change.** For example if the change doesn't apply to a supported platform of the library please mention it. - **Please run any tests or examples that can exercise your modified code.** We strive to not break users of the code and running tests/examples helps with this process. Thank you again for contributing! We will try to test and integrate the change as soon as we can, but be aware we have many GitHub repositories to manage and can't immediately respond to every request. There is no need to bump or check in on a pull request (it will clutter the discussion of the request). Also don't be worried if the request is closed or not integrated--sometimes the priorities of Adafruit's GitHub code (education, ease of use) might not match the priorities of the pull request. Don't fret, the open source community thrives on forks and GitHub makes it easy to keep your changes in a forked repo. After reviewing the guidelines above you can delete this text from the pull request. ================================================ FILE: .github/workflows/githubci.yml ================================================ name: Arduino Library CI on: [pull_request, push, repository_dispatch] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v4 with: python-version: '3.x' - uses: actions/checkout@v3 - uses: actions/checkout@v3 with: repository: adafruit/ci-arduino path: ci - name: pre-install run: bash ci/actions_install.sh - name: test platforms run: python3 ci/build_platform.py main_platforms clang-format: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v4 with: python-version: '3.x' - uses: actions/checkout@v3 - uses: actions/checkout@v3 with: repository: adafruit/ci-arduino path: ci - name: pre-install run: bash ci/actions_install.sh - name: clang run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r . doxygen: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/checkout@v3 with: repository: adafruit/ci-arduino path: ci # The Adafruit doxygen setup is configured to only look for files in the root directory - name: move src contents to root run: mv -v src/* . - name: doxygen env: GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} PRETTYNAME : "Adafruit MCP23017 Library" run: bash ci/doxy_gen_and_deploy.sh ================================================ FILE: README.md ================================================ # Adafruit MCP23017 Arduino Library [![Build Status](https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit-MCP23017-Arduino-Library/html/index.html) This is a library for the MCP23008/17 I2C and MCP23S08/17 SPI Port Expanders. Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Carter Nelson for Adafruit Industries. BSD license, check license.txt for more information All text above must be included in any redistribution To install, use the Arduino IDE Library Manager. # Pin Addressing When using single pin operations such as _pinMode(pinId, dir)_ or _digitalRead(pinId)_ or _digitalWrite(pinId, val)_ then the pins are addressed using the ID's below. For example, for set the mode of _GPB0_ then use _pinMode(8, ...)_. **NOTE** The MCP23008 and MCP23S08 only have _GPAx_ pins. | MCP23x08 Pin # | MCP23x17 Pin # | Pin Name | Pin ID | | :------------: | :------------: | :------: | :----: | | 10 | 21 | GPA0 | 0 | | 11 | 22 | GPA1 | 1 | | 12 | 23 | GPA2 | 2 | | 13 | 24 | GPA3 | 3 | | 14 | 25 | GPA4 | 4 | | 15 | 26 | GPA5 | 5 | | 16 | 27 | GPA6 | 6 | | 17 | 28 | GPA7 | 7 | | -- | 1 | GPB0 | 8 | | -- | 2 | GPB1 | 9 | | -- | 3 | GPB2 | 10 | | -- | 4 | GPB3 | 11 | | -- | 5 | GPB4 | 12 | | -- | 6 | GPB5 | 13 | | -- | 7 | GPB6 | 14 | | -- | 8 | GPB7 | 15 | # Use of HW address pins for SPI device Library supports MCP23Sxx HW pin addressing (A2, A1, A0 for S17 and A1, A0 for S08) To use it provide HW address to begin_SPI(CS, SPI, HW_ADDR) function, and as a result each SPI message will contain correct chip address. Example: mcp.begin_SPI(10, &SPI, 0b101); HW Address recognition must be enabled by enableAddrPins() function. **NOTE** Calling enableAddrPins() will enable IOCON.HAEN bit for all active (CS low) devices on SPI bus. **NOTE** There is hardware bug in the MCP23S17 chip, see "MCP23S17 Rev. A Silicon Errata". As a result, if using device with A2 = high, and not using addressing, hw address must be set to 0b1XX In such case, even if not using addressing, initalize your MCP23S17 chip with 0b1XX address, eg: mcp.begin_SPI(10, &SPI, 0b100);. # Warning Some people have reported an undocumented bug that can potentially corrupt the I2C bus. It occurs if an MCP230XX input pin state changes during I2C readout. **This should be very rare.** For more information, see this [forum post](https://www.microchip.com/forums/m646539.aspx) and this [knowledge base article](https://microchipsupport.force.com/s/article/On-MCP23008-MCP23017-SDA-line-change-when-GPIO7-input-change>). ================================================ FILE: examples/mcp23xxx_blink/mcp23xxx_blink.ino ================================================ // Blinks an LED attached to a MCP23XXX pin. // ok to include only the one needed // both included here to make things simple for example #include #include #define LED_PIN 0 // MCP23XXX pin LED is attached to // only used for SPI #define CS_PIN 6 // uncomment appropriate line Adafruit_MCP23X08 mcp; //Adafruit_MCP23X17 mcp; void setup() { Serial.begin(9600); //while (!Serial); Serial.println("MCP23xxx Blink Test!"); // uncomment appropriate mcp.begin if (!mcp.begin_I2C()) { //if (!mcp.begin_SPI(CS_PIN)) { Serial.println("Error."); while (1); } // configure pin for output mcp.pinMode(LED_PIN, OUTPUT); Serial.println("Looping..."); } void loop() { mcp.digitalWrite(LED_PIN, HIGH); delay(500); mcp.digitalWrite(LED_PIN, LOW); delay(500); } ================================================ FILE: examples/mcp23xxx_button/mcp23xxx_button.ino ================================================ // Reads a button attached to a MCP23XXX pin. // ok to include only the one needed // both included here to make things simple for example #include #include #define BUTTON_PIN 1 // MCP23XXX pin button is attached to // only used for SPI #define CS_PIN 6 // uncomment appropriate line Adafruit_MCP23X08 mcp; //Adafruit_MCP23X17 mcp; void setup() { Serial.begin(9600); //while (!Serial); Serial.println("MCP23xxx Button Test!"); // uncomment appropriate mcp.begin if (!mcp.begin_I2C()) { //if (!mcp.begin_SPI(CS_PIN)) { Serial.println("Error."); while (1); } // configure pin for input with pull up mcp.pinMode(BUTTON_PIN, INPUT_PULLUP); Serial.println("Looping..."); } void loop() { // LOW = pressed, HIGH = not pressed if (!mcp.digitalRead(BUTTON_PIN)) { Serial.println("Button Pressed!"); delay(250); } } ================================================ FILE: examples/mcp23xxx_combo/mcp23xxx_combo.ino ================================================ // Controls an LED via an attached button. // ok to include only the one needed // both included here to make things simple for example #include #include #define LED_PIN 0 // MCP23XXX pin LED is attached to #define BUTTON_PIN 1 // MCP23XXX pin button is attached to // only used for SPI #define CS_PIN 6 // uncomment appropriate line Adafruit_MCP23X08 mcp; //Adafruit_MCP23X17 mcp; void setup() { Serial.begin(9600); //while (!Serial); Serial.println("MCP23xxx Combo Test!"); // uncomment appropriate mcp.begin if (!mcp.begin_I2C()) { //if (!mcp.begin_SPI(CS_PIN)) { Serial.println("Error."); while (1); } // configure LED pin for output mcp.pinMode(LED_PIN, OUTPUT); // configure button pin for input with pull up mcp.pinMode(BUTTON_PIN, INPUT_PULLUP); Serial.println("Looping..."); } void loop() { mcp.digitalWrite(LED_PIN, !mcp.digitalRead(BUTTON_PIN)); } ================================================ FILE: examples/mcp23xxx_interrupt/mcp23xxx_interrupt.ino ================================================ // NOTE: This is a simple example that only reads the INTA or INTB pin // state. No actual interrupts are used on the host microcontroller. // MCP23XXX supports the following interrupt modes: // * CHANGE - interrupt occurs if pin changes to opposite state // * LOW - interrupt occurs while pin state is LOW // * HIGH - interrupt occurs while pin state is HIGH // ok to include only the one needed // both included here to make things simple for example #include #include #define BUTTON_PIN 1 // MCP23XXX pin used for interrupt #define INT_PIN 7 // microcontroller pin attached to INTA/B // only used for SPI #define CS_PIN 6 // uncomment appropriate line Adafruit_MCP23X08 mcp; //Adafruit_MCP23X17 mcp; void setup() { Serial.begin(9600); //while (!Serial); Serial.println("MCP23xxx Interrupt Test!"); // uncomment appropriate mcp.begin if (!mcp.begin_I2C()) { //if (!mcp.begin_SPI(CS_PIN)) { Serial.println("Error."); while (1); } // configure MCU pin that will read INTA/B state pinMode(INT_PIN, INPUT); // OPTIONAL - call this to override defaults // mirror INTA/B so only one wire required // active drive so INTA/B will not be floating // INTA/B will be signaled with a LOW mcp.setupInterrupts(true, false, LOW); // configure button pin for input with pull up mcp.pinMode(BUTTON_PIN, INPUT_PULLUP); // enable interrupt on button_pin mcp.setupInterruptPin(BUTTON_PIN, LOW); Serial.println("Looping..."); } void loop() { if (!digitalRead(INT_PIN)) { Serial.print("Interrupt detected on pin: "); Serial.println(mcp.getLastInterruptPin()); Serial.print("Pin states at time of interrupt: 0b"); Serial.println(mcp.getCapturedInterrupt(), 2); delay(250); // debounce // NOTE: If using DEFVAL, INT clears only if interrupt // condition does not exist. // See Fig 1-7 in datasheet. mcp.clearInterrupts(); // clear } } ================================================ FILE: keywords.txt ================================================ ####################################### # Syntax Coloring Map for MCP23017 ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### Adafruit_MCP23X08 KEYWORD1 Adafruit_MCP23X17 KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### begin_I2C KEYWORD2 begin_SPI KEYWORD2 configureInterrupt KEYWORD2 enableInterrupt KEYWORD2 disableInterrupt KEYWORD2 getLastInterruptPin KEYWORD2 writeGPIO KEYWORD2 readGPIO KEYWORD2 writeGPIOA KEYWORD2 readGPIOA KEYWORD2 writeGPIOB KEYWORD2 readGPIOB KEYWORD2 writeGPIOAB KEYWORD2 readGPIOAB KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### ================================================ FILE: library.properties ================================================ name=Adafruit MCP23017 Arduino Library version=2.3.2 author=Adafruit maintainer=Adafruit sentence=Arduino Library for MCP23XXX I2C and SPI GPIO port expanders paragraph=Arduino Library for MCP23008, MCP23S08, MCP23017, and MCP23S17 I2C and SPI GPIO port expanders category=Signal Input/Output url=https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library architectures=* depends=Adafruit BusIO ================================================ FILE: license.txt ================================================ Software License Agreement (BSD License) Copyright (c) 2012, Adafruit Industries All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: src/Adafruit_MCP23X08.cpp ================================================ /*! * @file Adafruit_MCP23X08.cpp */ #include "Adafruit_MCP23X08.h" /**************************************************************************/ /*! @brief default ctor. */ /**************************************************************************/ Adafruit_MCP23X08::Adafruit_MCP23X08() { pinCount = 8; } /**************************************************************************/ /*! @brief Enable usage of HW address pins (A0, A1) on MCP23S08 Send this message as first message after chip init. By default HW address pins are disabled. (Register IOCON, bit HAEN = 0 on chip reset) */ /**************************************************************************/ void Adafruit_MCP23X08::enableAddrPins() { if (!spi_dev) // I2C dev always use addr, only makes sense for SPI dev return; Adafruit_BusIO_Register GPIOAddr(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_IOCON, 0), 2); GPIOAddr.write((1 << 3), 1); } ================================================ FILE: src/Adafruit_MCP23X08.h ================================================ /*! * @file Adafruit_MCP23X08.h */ #ifndef __ADAFRUIT_MCP23X08_H__ #define __ADAFRUIT_MCP23X08_H__ #include "Adafruit_MCP23XXX.h" /**************************************************************************/ /*! @brief Class for MCP23008 I2C and MCP23S08 SPI variants. */ /**************************************************************************/ class Adafruit_MCP23X08 : public Adafruit_MCP23XXX { public: Adafruit_MCP23X08(); void enableAddrPins(); }; #endif ================================================ FILE: src/Adafruit_MCP23X17.cpp ================================================ /*! * @file Adafruit_MCP23X17.cpp */ #include "Adafruit_MCP23X17.h" /**************************************************************************/ /*! @brief default ctor. */ /**************************************************************************/ Adafruit_MCP23X17::Adafruit_MCP23X17() { pinCount = 16; } /**************************************************************************/ /*! @brief Bulk read all pins on Port A. @returns current pin states of port as a uint8_t. */ /**************************************************************************/ uint8_t Adafruit_MCP23X17::readGPIOA() { return readGPIO(0); } /**************************************************************************/ /*! @brief Bulk write all pins on Port A. @param value pin states to write as uint8_t. */ /**************************************************************************/ void Adafruit_MCP23X17::writeGPIOA(uint8_t value) { writeGPIO(value, 0); } /**************************************************************************/ /*! @brief Bulk read all pins on Port B. @returns current pin states of port as a uint8_t. */ /**************************************************************************/ uint8_t Adafruit_MCP23X17::readGPIOB() { return readGPIO(1); } /**************************************************************************/ /*! @brief Bulk write all pins on Port B. @param value pin states to write as uint8_t. */ /**************************************************************************/ void Adafruit_MCP23X17::writeGPIOB(uint8_t value) { writeGPIO(value, 1); } /**************************************************************************/ /*! @brief Bulk read all pins on Port A and B. @returns current pin states of ports as a uint16_t. */ /**************************************************************************/ uint16_t Adafruit_MCP23X17::readGPIOAB() { Adafruit_BusIO_Register GPIO(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPIO, 0), 2); return GPIO.read(); } /**************************************************************************/ /*! @brief Bulk write all pins on Port A and Port B. @param value pin states to write as uint16_t. */ /**************************************************************************/ void Adafruit_MCP23X17::writeGPIOAB(uint16_t value) { Adafruit_BusIO_Register GPIO(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPIO, 0), 2); GPIO.write(value, 2); } /**************************************************************************/ /*! @brief Enable usage of HW address pins (A0, A1, A2) on MCP23S17 Send this message as first message after chip init, as it will set bits in IOCON register to default (except HAEN) By default pins are not used and disabled (see README for details) This message is sent to all devices on bus (no hw_addr is added to msg as it's not enabled yet) Due to HW bug in the chip message must be sent twice (to addr 0b000 and 0b1xx) */ /**************************************************************************/ void Adafruit_MCP23X17::enableAddrPins() { if (!spi_dev) // I2C dev always use addr, only makes sense for SPI dev return; Adafruit_BusIO_Register GPIOAddr(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_IOCON, 0), 2); // Send message to address 0b000 regardless of chip addr, // Because addressing is not yet enabled uint8_t tmp = this->hw_addr; this->hw_addr = 0; // Temporary set hw addr to 0 Adafruit_BusIO_Register GPIONoAddr(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_IOCON, 0), 2); this->hw_addr = tmp; GPIONoAddr.write((1 << 3), 1); // Bit3: HAEN, devices with A2 = 0 GPIOAddr.write((1 << 3), 1); // Devices with A2 = 1 (if any) } ================================================ FILE: src/Adafruit_MCP23X17.h ================================================ /*! * @file Adafruit_MCP23X17.h */ #ifndef __ADAFRUIT_MCP23X17_H__ #define __ADAFRUIT_MCP23X17_H__ #include "Adafruit_MCP23XXX.h" /**************************************************************************/ /*! @brief Class for MCP23017 I2C and MCP23S17 SPI variants. */ /**************************************************************************/ class Adafruit_MCP23X17 : public Adafruit_MCP23XXX { public: Adafruit_MCP23X17(); uint8_t readGPIOA(); void writeGPIOA(uint8_t value); uint8_t readGPIOB(); void writeGPIOB(uint8_t value); uint16_t readGPIOAB(); void writeGPIOAB(uint16_t value); void enableAddrPins(); }; #endif ================================================ FILE: src/Adafruit_MCP23XXX.cpp ================================================ /*! * @file Adafruit_MCP23XXX.cpp * * @mainpage Adafruit MCP23X08/17 Library * * @section intro_sec Introduction * * This is a library for the MCP23008/17 I2C and MCP23S08/17 SPI port * expanders. * Adafruit invests time and resources providing this open source code, * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * * @section author Author * * Written by Carter Nelson for Adafruit Industries. * * @section license License * * BSD license, all text above must be included in any redistribution */ #include "Adafruit_MCP23XXX.h" /**************************************************************************/ /*! @brief Initialize MCP using I2C. @param i2c_addr I2C address @param wire Pointer to Wire instance @return true if initialization successful, otherwise false. */ /**************************************************************************/ bool Adafruit_MCP23XXX::begin_I2C(uint8_t i2c_addr, TwoWire *wire) { i2c_dev = new Adafruit_I2CDevice(i2c_addr, wire); return i2c_dev->begin(); } /**************************************************************************/ /*! @brief Initialize MCP using hardware SPI. @param cs_pin Pin to use for SPI chip select @param theSPI Pointer to SPI instance @param _hw_addr Hardware address (pins A2, A1, A0) @return true if initialization successful, otherwise false. */ /**************************************************************************/ bool Adafruit_MCP23XXX::begin_SPI(uint8_t cs_pin, SPIClass *theSPI, uint8_t _hw_addr) { this->hw_addr = _hw_addr; spi_dev = new Adafruit_SPIDevice(cs_pin, 1000000, SPI_BITORDER_MSBFIRST, SPI_MODE0, theSPI); return spi_dev->begin(); } /**************************************************************************/ /*! @brief Initialize MCP using software SPI. @param cs_pin Pin to use for SPI chip select @param sck_pin Pin to use for SPI clock @param miso_pin Pin to use for SPI MISO @param mosi_pin Pin to use for SPI MOSI @param _hw_addr Hardware address (pins A2, A1, A0) @return true if initialization successful, otherwise false. */ /**************************************************************************/ bool Adafruit_MCP23XXX::begin_SPI(int8_t cs_pin, int8_t sck_pin, int8_t miso_pin, int8_t mosi_pin, uint8_t _hw_addr) { this->hw_addr = _hw_addr; spi_dev = new Adafruit_SPIDevice(cs_pin, sck_pin, miso_pin, mosi_pin); return spi_dev->begin(); } /**************************************************************************/ /*! @brief Configures the specified pin to behave either as an input or an output. @param pin the Arduino pin number to set the mode of @param mode INPUT, OUTPUT, or INPUT_PULLUP */ /**************************************************************************/ void Adafruit_MCP23XXX::pinMode(uint8_t pin, uint8_t mode) { Adafruit_BusIO_Register IODIR(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_IODIR, MCP_PORT(pin))); Adafruit_BusIO_Register GPPU(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPPU, MCP_PORT(pin))); Adafruit_BusIO_RegisterBits dir_bit(&IODIR, 1, pin % 8); Adafruit_BusIO_RegisterBits pullup_bit(&GPPU, 1, pin % 8); dir_bit.write((mode == OUTPUT) ? 0 : 1); pullup_bit.write((mode == INPUT_PULLUP) ? 1 : 0); } /**************************************************************************/ /*! @brief Reads the value from a specified digital pin, either HIGH or LOW. @param pin the Arduino pin number you want to read @returns HIGH or LOW */ /**************************************************************************/ uint8_t Adafruit_MCP23XXX::digitalRead(uint8_t pin) { Adafruit_BusIO_Register GPIO(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPIO, MCP_PORT(pin))); Adafruit_BusIO_RegisterBits pin_bit(&GPIO, 1, pin % 8); return ((pin_bit.read() == 0) ? LOW : HIGH); } /**************************************************************************/ /*! @brief Write a HIGH or a LOW value to a digital pin. @param pin the Arduino pin number @param value HIGH or LOW */ /**************************************************************************/ void Adafruit_MCP23XXX::digitalWrite(uint8_t pin, uint8_t value) { Adafruit_BusIO_Register GPIO(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPIO, MCP_PORT(pin))); Adafruit_BusIO_RegisterBits pin_bit(&GPIO, 1, pin % 8); pin_bit.write((value == LOW) ? 0 : 1); } /**************************************************************************/ /*! @brief Bulk read all pins on a port. @param port 0 for Port A, 1 for Port B (MCP23X17 only). @returns current pin states of port as a uint8_t. */ /**************************************************************************/ uint8_t Adafruit_MCP23XXX::readGPIO(uint8_t port) { Adafruit_BusIO_Register GPIO(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPIO, port)); return GPIO.read() & 0xFF; } /**************************************************************************/ /*! @brief Bulk write all pins on a port. @param value pin states to write as a uint8_t. @param port 0 for Port A, 1 for Port B (MCP23X17 only). */ /**************************************************************************/ void Adafruit_MCP23XXX::writeGPIO(uint8_t value, uint8_t port) { Adafruit_BusIO_Register GPIO(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPIO, port)); GPIO.write(value); } /**************************************************************************/ /*! @brief Configure the interrupt system. @param mirroring true to OR both INTA and INTB pins. @param openDrain true for open drain output, false for active drive output. @param polarity HIGH or LOW */ /**************************************************************************/ void Adafruit_MCP23XXX::setupInterrupts(bool mirroring, bool openDrain, uint8_t polarity) { Adafruit_BusIO_Register IOCON(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_IOCON)); Adafruit_BusIO_RegisterBits mirror_bit(&IOCON, 1, 6); Adafruit_BusIO_RegisterBits openDrain_bit(&IOCON, 1, 2); Adafruit_BusIO_RegisterBits polarity_bit(&IOCON, 1, 1); mirror_bit.write(mirroring ? 1 : 0); openDrain_bit.write(openDrain ? 1 : 0); polarity_bit.write((polarity == HIGH) ? 1 : 0); } /**************************************************************************/ /*! @brief Enable interrupt and set mode for given pin. @param pin Pin to enable. @param mode CHANGE, LOW, HIGH */ /**************************************************************************/ void Adafruit_MCP23XXX::setupInterruptPin(uint8_t pin, uint8_t mode) { Adafruit_BusIO_Register GPINTEN(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPINTEN, MCP_PORT(pin))); Adafruit_BusIO_Register INTCON(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_INTCON, MCP_PORT(pin))); Adafruit_BusIO_Register DEFVAL(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_DEFVAL, MCP_PORT(pin))); Adafruit_BusIO_RegisterBits enable_bit(&GPINTEN, 1, pin % 8); Adafruit_BusIO_RegisterBits config_bit(&INTCON, 1, pin % 8); Adafruit_BusIO_RegisterBits defval_bit(&DEFVAL, 1, pin % 8); enable_bit.write(1); // enable it config_bit.write((mode == CHANGE) ? 0 : 1); // set mode defval_bit.write((mode == LOW) ? 1 : 0); // set defval } /**************************************************************************/ /*! @brief Disable interrupt for given pin. @param pin Pin to disable. */ /**************************************************************************/ void Adafruit_MCP23XXX::disableInterruptPin(uint8_t pin) { Adafruit_BusIO_Register GPINTEN(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_GPINTEN, MCP_PORT(pin))); Adafruit_BusIO_RegisterBits enable_bit(&GPINTEN, 1, pin % 8); enable_bit.write(0); } /**************************************************************************/ /*! @brief Clear interrupts. NOTE:If using DEFVAL, INT clears only if interrupt condition does not exist. See Fig 1-7 in datasheet. */ /**************************************************************************/ void Adafruit_MCP23XXX::clearInterrupts() { // reading INTCAP register(s) clears interrupts // just call this and ignore return getCapturedInterrupt(); } /**************************************************************************/ /*! @brief Gets the pin that caused the latest interrupt, from INTF, without clearing any interrupt flags. @returns Pin that caused lastest interrupt. */ /**************************************************************************/ uint8_t Adafruit_MCP23XXX::getLastInterruptPin() { uint8_t intf; // Port A Adafruit_BusIO_Register INTFA(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_INTF, 0)); INTFA.read(&intf); for (uint8_t pin = 0; pin < 8; pin++) { if (intf & (1 << pin)) { return pin; } } // Port B ? if (pinCount > 8) { Adafruit_BusIO_Register INTFB(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_INTF, 1)); INTFB.read(&intf); for (uint8_t pin = 0; pin < 8; pin++) { if (intf & (1 << pin)) { return pin + 8; } } } return MCP23XXX_INT_ERR; } /**************************************************************************/ /*! @brief Get pin states captured at time of interrupt. @returns Mutli-bit value representing pin states. */ /**************************************************************************/ uint16_t Adafruit_MCP23XXX::getCapturedInterrupt() { uint16_t intcap; uint8_t intf; // Port A Adafruit_BusIO_Register INTCAPA(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_INTCAP, 0)); INTCAPA.read(&intf); intcap = intf; // Port B ? if (pinCount > 8) { Adafruit_BusIO_Register INTCAPB(i2c_dev, spi_dev, MCP23XXX_SPIREG, getRegister(MCP23XXX_INTCAP, 1)); INTCAPB.read(&intf); intcap |= (uint16_t)intf << 8; } return intcap; } /**************************************************************************/ /*! @brief helper to get register address @param baseAddress base register address @param port 0 for A, 1 for B (MCP23X17 only) @returns calculated register address */ /**************************************************************************/ uint16_t Adafruit_MCP23XXX::getRegister(uint8_t baseAddress, uint8_t port) { // MCP23x08 uint16_t reg = baseAddress; // MCP23x17 BANK=0 if (pinCount > 8) { reg <<= 1; reg |= port; } // for SPI, add opcode as high byte return (spi_dev) ? (0x4000 | (hw_addr << 9) | reg) : reg; } ================================================ FILE: src/Adafruit_MCP23XXX.h ================================================ /*! * @file Adafruit_MCP23XXX.h */ #ifndef __ADAFRUIT_MCP23XXX_H__ #define __ADAFRUIT_MCP23XXX_H__ #include #include #include #include // registers #define MCP23XXX_IODIR 0x00 //!< I/O direction register #define MCP23XXX_IPOL 0x01 //!< Input polarity register #define MCP23XXX_GPINTEN 0x02 //!< Interrupt-on-change control register #define MCP23XXX_DEFVAL \ 0x03 //!< Default compare register for interrupt-on-change #define MCP23XXX_INTCON 0x04 //!< Interrupt control register #define MCP23XXX_IOCON 0x05 //!< Configuration register #define MCP23XXX_GPPU 0x06 //!< Pull-up resistor configuration register #define MCP23XXX_INTF 0x07 //!< Interrupt flag register #define MCP23XXX_INTCAP 0x08 //!< Interrupt capture register #define MCP23XXX_GPIO 0x09 //!< Port register #define MCP23XXX_OLAT 0x0A //!< Output latch register #define MCP23XXX_ADDR 0x20 //!< Default I2C Address #define MCP23XXX_SPIREG \ ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE //!< SPI register type #define MCP_PORT(pin) ((pin < 8) ? 0 : 1) //!< Determine port from pin number #define MCP23XXX_INT_ERR 255 //!< Interrupt error /**************************************************************************/ /*! @brief Base class for all MCP23XXX variants. */ /**************************************************************************/ class Adafruit_MCP23XXX { public: // init bool begin_I2C(uint8_t i2c_addr = MCP23XXX_ADDR, TwoWire *wire = &Wire); bool begin_SPI(uint8_t cs_pin, SPIClass *theSPI = &SPI, uint8_t _hw_addr = 0x00); bool begin_SPI(int8_t cs_pin, int8_t sck_pin, int8_t miso_pin, int8_t mosi_pin, uint8_t _hw_addr = 0x00); // main Arduino API methods void pinMode(uint8_t pin, uint8_t mode); uint8_t digitalRead(uint8_t pin); void digitalWrite(uint8_t pin, uint8_t value); // bulk access uint8_t readGPIO(uint8_t port = 0); void writeGPIO(uint8_t value, uint8_t port = 0); // interrupts void setupInterrupts(bool mirroring, bool openDrain, uint8_t polarity); void setupInterruptPin(uint8_t pin, uint8_t mode = CHANGE); void disableInterruptPin(uint8_t pin); void clearInterrupts(); uint8_t getLastInterruptPin(); uint16_t getCapturedInterrupt(); protected: Adafruit_I2CDevice *i2c_dev = nullptr; ///< Pointer to I2C bus interface Adafruit_SPIDevice *spi_dev = nullptr; ///< Pointer to SPI bus interface uint8_t pinCount; ///< Total number of GPIO pins uint8_t hw_addr; ///< HW address matching A2/A1/A0 pins uint16_t getRegister(uint8_t baseAddress, uint8_t port = 0); private: uint8_t buffer[4]; }; #endif