diff --git a/lib/PCA95x5/LICENSE b/lib/PCA95x5/LICENSE new file mode 100644 index 0000000..f6f330a --- /dev/null +++ b/lib/PCA95x5/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Hideaki Tai + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/PCA95x5/PCA95x5.h b/lib/PCA95x5/PCA95x5.h new file mode 100644 index 0000000..a82d73a --- /dev/null +++ b/lib/PCA95x5/PCA95x5.h @@ -0,0 +1,187 @@ +/* + * @Description: None + * @version: V1.0.0 + * @Author: None + * @Date: 2023-07-14 09:26:36 + * @LastEditors: LILYGO_L + * @LastEditTime: 2023-07-14 09:45:19 + * @License: GPL 3.0 + */ +#include +#include + +namespace PCA95x5 { + +namespace Reg { + enum : uint8_t { + INPUT_PORT_0, + INPUT_PORT_1, + OUTPUT_PORT_0, + OUTPUT_PORT_1, + POLARITY_INVERSION_PORT_0, + POLARITY_INVERSION_PORT_1, + CONFIGURATION_PORT_0, + CONFIGURATION_PORT_1, + }; +} + +namespace Port { + enum Port : uint8_t { + P00, + P01, + P02, + P03, + P04, + P05, + P06, + P07, + P10, + P11, + P12, + P13, + P14, + P15, + P16, + P17, + }; +} // namespace Port + +namespace Level { + enum Level : uint8_t { L, H }; + enum LevelAll : uint16_t { L_ALL = 0x0000, H_ALL = 0xFFFF }; +} // namespace Level + +namespace Polarity { + enum Polarity : uint8_t { ORIGINAL, INVERTED }; + enum PolarityAll : uint16_t { ORIGINAL_ALL = 0x0000, INVERTED_ALL = 0xFFFF }; +} // namespace Polarity + +namespace Direction { + enum Direction : uint8_t { OUT, IN }; + enum DirectionAll : uint16_t { OUT_ALL = 0x0000, IN_ALL = 0xFFFF }; +} // namespace Direction + +template +class PCA95x5 { + union Ports { + uint16_t w; + uint8_t b[2]; + }; + + static constexpr uint8_t BASE_I2C_ADDR = 0x20; + + WireType* wire {nullptr}; + uint8_t addr {BASE_I2C_ADDR}; + Ports input {0x0000}; + Ports output {0xFFFF}; + Ports pol {0x0000}; + Ports dir {0xFFFF}; + uint8_t status {0x00}; + +public: + void attach(WireType& wire, uint8_t i2c_addr = BASE_I2C_ADDR) { + this->wire = &wire; + this->addr = i2c_addr; + } + + uint16_t read() { + read_bytes(this->addr, Reg::INPUT_PORT_0, this->input.b, 2); + return this->input.w; + } + Level::Level read(const Port::Port port) { + uint16_t v = read(); + return (v & (1 << port)) ? Level::H : Level::L; + } + Level::Level read(uint8_t port) { + uint16_t v = read(); + return (v & (1 << port)) ? Level::H : Level::L; + } + + bool write(const uint16_t value) { + this->output.w = value; + return write_impl(); + } + bool write(const Port::Port port, const Level::Level level) { + if (level == Level::H) { + this->output.w |= (1 << port); + } else { + this->output.w &= ~(1 << port); + } + return write_impl(); + } + bool write(uint8_t port, uint8_t level) { + if (level == Level::H) { + this->output.w |= (1 << port); + } else { + this->output.w &= ~(1 << port); + } + return write_impl(); + } + + bool polarity(const uint16_t value) { + this->pol.w = value; + return polarity_impl(); + } + bool polarity(const Port::Port port, const Polarity::Polarity pol) { + if (pol == Polarity::INVERTED) { + this->pol.w |= (1 << port); + } else { + this->pol.w &= ~(1 << port); + } + return polarity_impl(); + } + + bool direction(const uint16_t value) { + this->dir.w = value; + return direction_impl(); + } + + bool direction(const Port::Port port, const Direction::Direction dir) { + if (dir == Direction::IN) { + this->dir.w |= (1 << port); + } else { + this->dir.w &= ~(1 << port); + } + return direction_impl(); + } + + uint8_t i2c_error() const { + return status; + } + +private: + bool write_impl() { + return write_bytes(this->addr, Reg::OUTPUT_PORT_0, this->output.b, 2); + } + + bool polarity_impl() { + return write_bytes(this->addr, Reg::POLARITY_INVERSION_PORT_0, this->pol.b, 2); + } + + bool direction_impl() { + return write_bytes(this->addr, Reg::CONFIGURATION_PORT_0, this->dir.b, 2); + } + + int8_t read_bytes(const uint8_t dev, const uint8_t reg, uint8_t* data, const uint8_t size) { + wire->beginTransmission(dev); + wire->write(reg); + wire->endTransmission(); + wire->requestFrom(dev, size); + int8_t count = 0; + while (wire->available()) data[count++] = wire->read(); + return count; + } + + bool write_bytes(const uint8_t dev, const uint8_t reg, const uint8_t* data, const uint8_t size) { + wire->beginTransmission(dev); + wire->write(reg); + for (uint8_t i = 0; i < size; i++) wire->write(data[i]); + status = wire->endTransmission(); + return (status == 0); + } +}; + +} // namespace PCA95x5 + +using PCA9535 = PCA95x5::PCA95x5<>; +using PCA9555 = PCA95x5::PCA95x5<>; diff --git a/lib/PCA95x5/README.md b/lib/PCA95x5/README.md new file mode 100644 index 0000000..52c4283 --- /dev/null +++ b/lib/PCA95x5/README.md @@ -0,0 +1,127 @@ +# PCA95x5 + +Arduino library for [PCA9535](https://www.ti.com/product/PCA9535) and [PCA9555](https://www.ti.com/product/PCA9555) (Remote 16-bit I2C and SMBus I/O Expander with Interrupt Output and Configuration Registers). + +## Usage + +You can specify a port using either index (`0` - `15`) or enum (`PCA95x5::Port::P02`, etc.). + +### Input + +```C++ +#include + +// select one of them +PCA9535 ioex; +// PCA9555 ioex; + +void setup() { + Serial.begin(115200); + delay(2000); + + Wire.begin(); + ioex.attach(Wire); + ioex.polarity(PCA95x5::Polarity::ORIGINAL_ALL); + ioex.direction(PCA95x5::Direction::IN_ALL); +} + +void loop() { + Serial.println(ioex.read(), BIN); + delay(1000); +} +``` + +### Output + +```C++ +#include + +// select one of them +// PCA9535 ioex; +PCA9555 ioex; + +void setup() { + Serial.begin(115200); + delay(2000); + + Wire.begin(); + ioex.attach(Wire); + ioex.polarity(PCA95x5::Polarity::ORIGINAL_ALL); + ioex.direction(PCA95x5::Direction::OUT_ALL); + ioex.write(PCA95x5::Level::L_ALL); +} + +void loop() { + for (size_t i = 0; i < 16; ++i) { + Serial.print("set port high: "); + Serial.println(i); + + ioex.write(i, PCA95x5::Level::H); + Serial.println(ioex.read(), BIN); + delay(500); + } + + for (size_t i = 0; i < 16; ++i) { + Serial.print("set port low: "); + Serial.println(i); + + ioex.write(i, PCA95x5::Level::L); + Serial.println(ioex.read(), BIN); + delay(500); + } +} +``` + +## APIs + +```C++ +void attach(WireType& wire, uint8_t i2c_addr = BASE_I2C_ADDR); +uint16_t read(); +Level::Level read(const uint8_t port); +bool write(const uint16_t value); +bool write(const uint8_t port, const Level::Level level); +bool polarity(const uint16_t value); +bool polarity(const uint8_t port, const Polarity::Polarity pol); +bool direction(const uint16_t value); +bool direction(const uint8_t port, const Direction::Direction dir); +uint8_t i2c_error() const; +``` + +```C++ +namespace Port { + enum Port : uint8_t { + P00, + P01, + P02, + P03, + P04, + P05, + P06, + P07, + P10, + P11, + P12, + P13, + P14, + P15, + P16, + P17, + }; +} +namespace Level { + enum Level : uint8_t { L, H }; + enum LevelAll : uint16_t { L_ALL = 0x0000, H_ALL = 0xFFFF }; +} +namespace Polarity { + enum Polarity : uint8_t { ORIGINAL, INVERTED }; + enum PolarityAll : uint16_t { ORIGINAL_ALL = 0x0000, INVERTED_ALL = 0xFFFF }; +} +namespace Direction { + enum Direction : uint8_t { OUT, IN }; + enum DirectionAll : uint16_t { OUT_ALL = 0x0000, IN_ALL = 0xFFFF }; +} +``` + +## License + +MIT diff --git a/lib/PCA95x5/examples/input/input.ino b/lib/PCA95x5/examples/input/input.ino new file mode 100644 index 0000000..e410f5d --- /dev/null +++ b/lib/PCA95x5/examples/input/input.ino @@ -0,0 +1,18 @@ +#include + +PCA9555 ioex; + +void setup() { + Serial.begin(115200); + delay(2000); + + Wire.begin(); + ioex.attach(Wire); + ioex.polarity(PCA95x5::Polarity::ORIGINAL_ALL); + ioex.direction(PCA95x5::Direction::IN_ALL); +} + +void loop() { + Serial.println(ioex.read(), BIN); + delay(1000); +} diff --git a/lib/PCA95x5/examples/output/output.ino b/lib/PCA95x5/examples/output/output.ino new file mode 100644 index 0000000..7f83943 --- /dev/null +++ b/lib/PCA95x5/examples/output/output.ino @@ -0,0 +1,34 @@ +#include + +PCA9555 ioex; + +void setup() { + Serial.begin(115200); + delay(2000); + + Wire.begin(); + ioex.attach(Wire); + ioex.polarity(PCA95x5::Polarity::ORIGINAL_ALL); + ioex.direction(PCA95x5::Direction::OUT_ALL); + ioex.write(PCA95x5::Level::L_ALL); +} + +void loop() { + for (size_t i = 0; i < 16; ++i) { + Serial.print("set port high: "); + Serial.println(i); + + ioex.write(i, PCA95x5::Level::H); + Serial.println(ioex.read(), BIN); + delay(500); + } + + for (size_t i = 0; i < 16; ++i) { + Serial.print("set port low: "); + Serial.println(i); + + ioex.write(i, PCA95x5::Level::L); + Serial.println(ioex.read(), BIN); + delay(500); + } +} diff --git a/lib/PCA95x5/library.json b/lib/PCA95x5/library.json new file mode 100644 index 0000000..15ee852 --- /dev/null +++ b/lib/PCA95x5/library.json @@ -0,0 +1,20 @@ +{ + "name": "PCA95x5", + "keywords": "i2c,mux,io expander,pca9535,pca9555", + "description": "Arduino library for PCA9535 and PCA9555 (Remote 16-bit I2C and SMBus I/O Expander with Interrupt Output and Configuration Registers)", + "repository": + { + "type": "git", + "url": "https://github.com/hideakitai/PCA95x5.git" + }, + "authors": + { + "name": "Hideaki Tai", + "url": "https://github.com/hideakitai", + "maintainer": true + }, + "version": "0.1.1", + "license": "MIT", + "frameworks": "arduino", + "platforms": "*" +} diff --git a/lib/PCA95x5/library.properties b/lib/PCA95x5/library.properties new file mode 100644 index 0000000..2294e50 --- /dev/null +++ b/lib/PCA95x5/library.properties @@ -0,0 +1,9 @@ +name=PCA95x5 +version=0.1.1 +author=hideakitai +maintainer=hideakitai +sentence=Arduino library for PCA9535 and PCA9555 (Remote 16-bit I2C and SMBus I/O Expander with Interrupt Output and Configuration Registers) +paragraph=Arduino library for PCA9535 and PCA9555 (Remote 16-bit I2C and SMBus I/O Expander with Interrupt Output and Configuration Registers) +category=Device Control +url=https://github.com/hideakitai +architectures=*