From 9336c5750dbab2e26b9328af18907b3c6a5067c8 Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Tue, 18 Aug 2026 04:54:31 +0000 Subject: [PATCH 1/2] Return the write status from the IO expander virtuals --- src/utility/IOExpander_Base.hpp | 24 +++++++++++++++++------ src/utility/M5IOE1_Class.cpp | 30 ++++++++++++++--------------- src/utility/M5IOE1_Class.hpp | 12 ++++++------ src/utility/PI4IOE5V6408_Class.cpp | 31 +++++++++++++++--------------- src/utility/PI4IOE5V6408_Class.hpp | 12 ++++++------ 5 files changed, 61 insertions(+), 48 deletions(-) diff --git a/src/utility/IOExpander_Base.hpp b/src/utility/IOExpander_Base.hpp index a382930..f43eba1 100644 --- a/src/utility/IOExpander_Base.hpp +++ b/src/utility/IOExpander_Base.hpp @@ -24,18 +24,24 @@ namespace m5 IOExpander_Base(const IOExpander_Base&) = delete; // false input, true output - virtual void setDirection(uint8_t pin, bool direction) = 0; + /// @return true only when the requested state was completely established; + /// false for an invalid pin or I2C failure. + virtual bool setDirection(uint8_t pin, bool direction) = 0; /// Set the GPIO pull resistor state. /// @return true only when the requested state was completely established; /// false for an invalid pin, mode, or I2C failure. virtual bool setPullMode(uint8_t pin, gpio_pull_t mode) = 0; - virtual void setHighImpedance(uint8_t pin, bool enable) = 0; + /// @return true only when the requested state was completely established; + /// false for an invalid pin or I2C failure. + virtual bool setHighImpedance(uint8_t pin, bool enable) = 0; virtual bool getWriteValue(uint8_t pin) = 0; - virtual void digitalWrite(uint8_t pin, bool level) = 0; + /// @return true only when the requested state was completely established; + /// false for an invalid pin or I2C failure. + virtual bool digitalWrite(uint8_t pin, bool level) = 0; virtual bool digitalRead(uint8_t pin) = 0; @@ -48,11 +54,17 @@ namespace m5 return false; } - virtual void resetIrq() = 0; + /// @return true only when the requested state was completely established; + /// false on I2C failure. + virtual bool resetIrq() = 0; - virtual void disableIrq() = 0; + /// @return true only when the requested state was completely established; + /// false on I2C failure. + virtual bool disableIrq() = 0; - virtual void enableIrq() = 0; + /// @return true only when the requested state was completely established; + /// false on I2C failure. + virtual bool enableIrq() = 0; }; } diff --git a/src/utility/M5IOE1_Class.cpp b/src/utility/M5IOE1_Class.cpp index 5fb8d9c..bb55ba1 100644 --- a/src/utility/M5IOE1_Class.cpp +++ b/src/utility/M5IOE1_Class.cpp @@ -39,13 +39,13 @@ namespace m5 return _init; } - void M5IOE1_Class::setDirection(uint8_t pin, bool direction) + bool M5IOE1_Class::setDirection(uint8_t pin, bool direction) { - if (!_isValidPin(pin)) { return; } + if (!_isValidPin(pin)) { return false; } // false=input, true=output. MODE bit set means output. const auto reg = _regForPin(M5IOE1_REG_GPIO_MODE_L, pin); const auto bit = _bitForPin(pin); - direction ? bitOn(reg, bit) : bitOff(reg, bit); + return direction ? bitOn(reg, bit) : bitOff(reg, bit); } bool M5IOE1_Class::setPullMode(uint8_t pin, gpio_pull_t mode) @@ -71,13 +71,13 @@ namespace m5 } } - void M5IOE1_Class::setHighImpedance(uint8_t pin, bool enable) + bool M5IOE1_Class::setHighImpedance(uint8_t pin, bool enable) { - if (!_isValidPin(pin)) { return; } + if (!_isValidPin(pin)) { return false; } // M5IOE1 exposes drive mode here: 0=push-pull, 1=open-drain. const auto reg = _regForPin(M5IOE1_REG_GPIO_DRV_L, pin); const auto bit = _bitForPin(pin); - enable ? bitOn(reg, bit) : bitOff(reg, bit); + return enable ? bitOn(reg, bit) : bitOff(reg, bit); } bool M5IOE1_Class::getWriteValue(uint8_t pin) @@ -86,12 +86,12 @@ namespace m5 return (readRegister8(_regForPin(M5IOE1_REG_GPIO_OUT_L, pin)) & _bitForPin(pin)) != 0; } - void M5IOE1_Class::digitalWrite(uint8_t pin, bool level) + bool M5IOE1_Class::digitalWrite(uint8_t pin, bool level) { - if (!_isValidPin(pin)) { return; } + if (!_isValidPin(pin)) { return false; } const auto reg = _regForPin(M5IOE1_REG_GPIO_OUT_L, pin); const auto bit = _bitForPin(pin); - level ? bitOn(reg, bit) : bitOff(reg, bit); + return level ? bitOn(reg, bit) : bitOff(reg, bit); } bool M5IOE1_Class::digitalRead(uint8_t pin) @@ -135,21 +135,21 @@ namespace m5 return writeRegister(reg, data, sizeof(data)); } - void M5IOE1_Class::resetIrq() + bool M5IOE1_Class::resetIrq() { std::uint8_t data[2] = { 0x00, 0x00 }; - writeRegister(M5IOE1_REG_GPIO_IS_L, data, sizeof(data)); + return writeRegister(M5IOE1_REG_GPIO_IS_L, data, sizeof(data)); } - void M5IOE1_Class::disableIrq() + bool M5IOE1_Class::disableIrq() { std::uint8_t data[2] = { 0x00, 0x00 }; - writeRegister(M5IOE1_REG_GPIO_IE_L, data, sizeof(data)); + return writeRegister(M5IOE1_REG_GPIO_IE_L, data, sizeof(data)); } - void M5IOE1_Class::enableIrq() + bool M5IOE1_Class::enableIrq() { std::uint8_t data[2] = { 0xFF, 0x3F }; - writeRegister(M5IOE1_REG_GPIO_IE_L, data, sizeof(data)); + return writeRegister(M5IOE1_REG_GPIO_IE_L, data, sizeof(data)); } } diff --git a/src/utility/M5IOE1_Class.hpp b/src/utility/M5IOE1_Class.hpp index be1568b..0fd5e4a 100644 --- a/src/utility/M5IOE1_Class.hpp +++ b/src/utility/M5IOE1_Class.hpp @@ -45,15 +45,15 @@ namespace m5 bool begin(); - void setDirection(uint8_t pin, bool direction) override; + bool setDirection(uint8_t pin, bool direction) override; bool setPullMode(uint8_t pin, gpio_pull_t mode) override; - void setHighImpedance(uint8_t pin, bool enable) override; + bool setHighImpedance(uint8_t pin, bool enable) override; bool getWriteValue(uint8_t pin) override; - void digitalWrite(uint8_t pin, bool level) override; + bool digitalWrite(uint8_t pin, bool level) override; bool digitalRead(uint8_t pin) override; bool getInputLevel(uint8_t pin, bool* level) override; @@ -79,11 +79,11 @@ namespace m5 bool setPwmDuty12bit(pwm_channel_t channel, std::uint32_t duty12, pwm_polarity_t polarity = pwm_polarity_t::normal, bool enable = true); - void resetIrq() override; + bool resetIrq() override; - void disableIrq() override; + bool disableIrq() override; - void enableIrq() override; + bool enableIrq() override; private: static bool _isValidPin(uint8_t pin) { return pin < 14; } diff --git a/src/utility/PI4IOE5V6408_Class.cpp b/src/utility/PI4IOE5V6408_Class.cpp index 0f71450..a1be83f 100644 --- a/src/utility/PI4IOE5V6408_Class.cpp +++ b/src/utility/PI4IOE5V6408_Class.cpp @@ -20,12 +20,12 @@ bool PI4IOE5V6408_Class::begin() } // false input, true output -void PI4IOE5V6408_Class::setDirection(uint8_t pin, bool direction) +bool PI4IOE5V6408_Class::setDirection(uint8_t pin, bool direction) { if (direction) { - bitOn(0x03, 1 << pin); // Output, set 1 + return bitOn(0x03, 1 << pin); // Output, set 1 } else { - bitOff(0x03, 1 << pin); // Input, set 0 + return bitOff(0x03, 1 << pin); // Input, set 0 } } @@ -47,12 +47,12 @@ bool PI4IOE5V6408_Class::setPullMode(uint8_t pin, gpio_pull_t mode) } } -void PI4IOE5V6408_Class::setHighImpedance(uint8_t pin, bool enable) +bool PI4IOE5V6408_Class::setHighImpedance(uint8_t pin, bool enable) { if (enable) { - bitOn(0x07, 1 << pin); + return bitOn(0x07, 1 << pin); } else { - bitOff(0x07, 1 << pin); + return bitOff(0x07, 1 << pin); } } @@ -62,12 +62,12 @@ bool PI4IOE5V6408_Class::getWriteValue(uint8_t pin) return (data & (1 << pin)) != 0; } -void PI4IOE5V6408_Class::digitalWrite(uint8_t pin, bool level) +bool PI4IOE5V6408_Class::digitalWrite(uint8_t pin, bool level) { if (level) { - bitOn(0x05, 1 << pin); + return bitOn(0x05, 1 << pin); } else { - bitOff(0x05, 1 << pin); + return bitOff(0x05, 1 << pin); } } @@ -77,18 +77,19 @@ bool PI4IOE5V6408_Class::digitalRead(uint8_t pin) return (data & (1 << pin)) != 0; } -void PI4IOE5V6408_Class::resetIrq() +bool PI4IOE5V6408_Class::resetIrq() { - readRegister8(0x13); + uint8_t value; + return readRegister(0x13, &value, 1); } -void PI4IOE5V6408_Class::disableIrq() +bool PI4IOE5V6408_Class::disableIrq() { - writeRegister8(0x11, 0B11111111); + return writeRegister8(0x11, 0B11111111); } -void PI4IOE5V6408_Class::enableIrq() +bool PI4IOE5V6408_Class::enableIrq() { - writeRegister8(0x11, 0x0); + return writeRegister8(0x11, 0x0); } } diff --git a/src/utility/PI4IOE5V6408_Class.hpp b/src/utility/PI4IOE5V6408_Class.hpp index b7cba8a..6948022 100644 --- a/src/utility/PI4IOE5V6408_Class.hpp +++ b/src/utility/PI4IOE5V6408_Class.hpp @@ -31,23 +31,23 @@ namespace m5 bool begin(); // false input, true output - void setDirection(uint8_t pin, bool direction) override; + bool setDirection(uint8_t pin, bool direction) override; bool setPullMode(uint8_t pin, gpio_pull_t mode) override; - void setHighImpedance(uint8_t pin, bool enable) override; + bool setHighImpedance(uint8_t pin, bool enable) override; bool getWriteValue(uint8_t pin) override; - void digitalWrite(uint8_t pin, bool level) override; + bool digitalWrite(uint8_t pin, bool level) override; bool digitalRead(uint8_t pin) override; - void resetIrq() override; + bool resetIrq() override; - void disableIrq() override; + bool disableIrq() override; - void enableIrq() override; + bool enableIrq() override; }; } From f5afb020675921aff272e3fdfa1d435db770881a Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Tue, 18 Aug 2026 05:18:51 +0000 Subject: [PATCH 2/2] Validate PI4IOE5V6408 pins and state the acknowledged-write contract --- src/utility/IOExpander_Base.hpp | 18 +++++++++++------- src/utility/M5IOE1_Class.hpp | 2 ++ src/utility/PI4IOE5V6408_Class.cpp | 3 +++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/utility/IOExpander_Base.hpp b/src/utility/IOExpander_Base.hpp index f43eba1..32f901a 100644 --- a/src/utility/IOExpander_Base.hpp +++ b/src/utility/IOExpander_Base.hpp @@ -18,28 +18,32 @@ namespace m5 , pull_down = 2 }; + /// Mutating methods report their result: the written value is not read + /// back, and read-modify-write accesses are not atomic against + /// concurrent access to the same register. + IOExpander_Base(std::uint8_t i2c_addr, std::uint32_t freq = 400000, m5::I2C_Class* i2c = &m5::In_I2C) : I2C_Device(i2c_addr, freq, i2c) {} IOExpander_Base(const IOExpander_Base&) = delete; // false input, true output - /// @return true only when the requested state was completely established; + /// @return true when every required register access was acknowledged; /// false for an invalid pin or I2C failure. virtual bool setDirection(uint8_t pin, bool direction) = 0; /// Set the GPIO pull resistor state. - /// @return true only when the requested state was completely established; + /// @return true when every required register access was acknowledged; /// false for an invalid pin, mode, or I2C failure. virtual bool setPullMode(uint8_t pin, gpio_pull_t mode) = 0; - /// @return true only when the requested state was completely established; + /// @return true when every required register access was acknowledged; /// false for an invalid pin or I2C failure. virtual bool setHighImpedance(uint8_t pin, bool enable) = 0; virtual bool getWriteValue(uint8_t pin) = 0; - /// @return true only when the requested state was completely established; + /// @return true when every required register access was acknowledged; /// false for an invalid pin or I2C failure. virtual bool digitalWrite(uint8_t pin, bool level) = 0; @@ -54,15 +58,15 @@ namespace m5 return false; } - /// @return true only when the requested state was completely established; + /// @return true when every required register access was acknowledged; /// false on I2C failure. virtual bool resetIrq() = 0; - /// @return true only when the requested state was completely established; + /// @return true when every required register access was acknowledged; /// false on I2C failure. virtual bool disableIrq() = 0; - /// @return true only when the requested state was completely established; + /// @return true when every required register access was acknowledged; /// false on I2C failure. virtual bool enableIrq() = 0; }; diff --git a/src/utility/M5IOE1_Class.hpp b/src/utility/M5IOE1_Class.hpp index 0fd5e4a..f5ca244 100644 --- a/src/utility/M5IOE1_Class.hpp +++ b/src/utility/M5IOE1_Class.hpp @@ -49,6 +49,8 @@ namespace m5 bool setPullMode(uint8_t pin, gpio_pull_t mode) override; + /// On the M5IOE1 this selects the drive mode (open-drain), which keeps + /// sinking the pin while the output latch is low; it is not a disconnect. bool setHighImpedance(uint8_t pin, bool enable) override; bool getWriteValue(uint8_t pin) override; diff --git a/src/utility/PI4IOE5V6408_Class.cpp b/src/utility/PI4IOE5V6408_Class.cpp index a1be83f..44cd71b 100644 --- a/src/utility/PI4IOE5V6408_Class.cpp +++ b/src/utility/PI4IOE5V6408_Class.cpp @@ -22,6 +22,7 @@ bool PI4IOE5V6408_Class::begin() // false input, true output bool PI4IOE5V6408_Class::setDirection(uint8_t pin, bool direction) { + if (pin >= 8) { return false; } if (direction) { return bitOn(0x03, 1 << pin); // Output, set 1 } else { @@ -49,6 +50,7 @@ bool PI4IOE5V6408_Class::setPullMode(uint8_t pin, gpio_pull_t mode) bool PI4IOE5V6408_Class::setHighImpedance(uint8_t pin, bool enable) { + if (pin >= 8) { return false; } if (enable) { return bitOn(0x07, 1 << pin); } else { @@ -64,6 +66,7 @@ bool PI4IOE5V6408_Class::getWriteValue(uint8_t pin) bool PI4IOE5V6408_Class::digitalWrite(uint8_t pin, bool level) { + if (pin >= 8) { return false; } if (level) { return bitOn(0x05, 1 << pin); } else {