diff --git a/src/M5StackChan.cpp b/src/M5StackChan.cpp index 7ef143e..be76b4c 100644 --- a/src/M5StackChan.cpp +++ b/src/M5StackChan.cpp @@ -60,13 +60,13 @@ void M5StackChan_Class::io_expander_init() if (_io_expander) { // VM EN _io_expander->setDirection(0, true); // Output - _io_expander->setPullMode(0, true); // Pull-up + _io_expander->setPullMode(0, PY32IOExpander_Class::pull_up); setServoPowerEnabled(true); delay(200); // RGB - _io_expander->setDirection(13, true); // Output - _io_expander->setPullMode(13, true); // Pull-up + _io_expander->setDirection(13, true); // Output + _io_expander->setPullMode(13, PY32IOExpander_Class::pull_up); _io_expander->setDriveMode(13, false); // Push-pull _io_expander->setLedCount(12); delay(200); diff --git a/src/drivers/PY32IOExpander/PY32IOExpander.cpp b/src/drivers/PY32IOExpander/PY32IOExpander.cpp index 8867adf..e2f3a38 100644 --- a/src/drivers/PY32IOExpander/PY32IOExpander.cpp +++ b/src/drivers/PY32IOExpander/PY32IOExpander.cpp @@ -49,19 +49,12 @@ static constexpr uint8_t REG_PWM3_DUTY_H = 0x20; static constexpr uint8_t REG_PWM4_DUTY_L = 0x21; static constexpr uint8_t REG_PWM4_DUTY_H = 0x22; -void PY32IOExpander_Class::_writeBit(uint8_t reg_l, uint8_t reg_h, uint8_t pin, bool value) +bool PY32IOExpander_Class::_writeBit(uint8_t reg_l, uint8_t reg_h, uint8_t pin, bool value) { if (pin < 8) { - if (value) - bitOn(reg_l, 1 << pin); - else - bitOff(reg_l, 1 << pin); - } else { - if (value) - bitOn(reg_h, 1 << (pin - 8)); - else - bitOff(reg_h, 1 << (pin - 8)); + return value ? bitOn(reg_l, 1 << pin) : bitOff(reg_l, 1 << pin); } + return value ? bitOn(reg_h, 1 << (pin - 8)) : bitOff(reg_h, 1 << (pin - 8)); } bool PY32IOExpander_Class::_readBit(uint8_t reg_l, uint8_t reg_h, uint8_t pin) @@ -80,95 +73,133 @@ bool PY32IOExpander_Class::begin() return true; } -void PY32IOExpander_Class::setDirection(uint8_t pin, bool direction) +bool PY32IOExpander_Class::_setDirection(uint8_t pin, bool direction) { // direction: false=input (0), true=output (1) - _writeBit(REG_GPIO_M_L, REG_GPIO_M_H, pin, direction); + if (!_isValidPin(pin)) return false; + return _writeBit(REG_GPIO_M_L, REG_GPIO_M_H, pin, direction); } -void PY32IOExpander_Class::enablePull(uint8_t pin, bool enablePull) +bool PY32IOExpander_Class::_setPullMode(uint8_t pin, gpio_pull_t mode) { - if (enablePull) { - // Enable Pull Up by default if neither is set - bool pu = _readBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin); - bool pd = _readBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin); - if (!pu && !pd) { - _writeBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin, true); + if (!_isValidPin(pin)) return false; + switch (mode) { + case pull_none: { + bool pu_ok = _writeBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin, false); + bool pd_ok = _writeBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin, false); + return pu_ok && pd_ok; } - // If one is already set, leave it. - } else { - // Disable both - _writeBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin, false); - _writeBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin, false); - } -} - -void PY32IOExpander_Class::setPullMode(uint8_t pin, bool mode) -{ - // mode: false=down, true=up - if (mode) { - // Pull Up - _writeBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin, false); - _writeBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin, true); - } else { - // Pull Down - _writeBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin, false); - _writeBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin, true); + case pull_up: { + bool pd_ok = _writeBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin, false); + bool pu_ok = _writeBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin, true); + return pd_ok && pu_ok; + } + case pull_down: { + bool pu_ok = _writeBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin, false); + bool pd_ok = _writeBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin, true); + return pu_ok && pd_ok; + } + default: + return false; } } void PY32IOExpander_Class::setDriveMode(uint8_t pin, bool openDrain) { // openDrain: false=push-pull (0), true=open-drain (1) + if (!_isValidPin(pin)) return; _writeBit(REG_GPIO_DRV_L, REG_GPIO_DRV_H, pin, openDrain); } -void PY32IOExpander_Class::setHighImpedance(uint8_t pin, bool enable) +bool PY32IOExpander_Class::_setHighImpedance(uint8_t pin, bool enable) { - if (enable) { - // Input mode - setDirection(pin, false); - // Disable pulls - enablePull(pin, false); - } + if (!_isValidPin(pin)) return false; + if (!enable) return true; + // Input mode with the pulls disabled + bool dir_ok = _setDirection(pin, false); + bool pull_ok = _setPullMode(pin, pull_none); + return dir_ok && pull_ok; } bool PY32IOExpander_Class::getWriteValue(uint8_t pin) { + if (!_isValidPin(pin)) return false; return _readBit(REG_GPIO_O_L, REG_GPIO_O_H, pin); } -void PY32IOExpander_Class::digitalWrite(uint8_t pin, bool level) +bool PY32IOExpander_Class::_digitalWrite(uint8_t pin, bool level) { - _writeBit(REG_GPIO_O_L, REG_GPIO_O_H, pin, level); + if (!_isValidPin(pin)) return false; + return _writeBit(REG_GPIO_O_L, REG_GPIO_O_H, pin, level); } bool PY32IOExpander_Class::digitalRead(uint8_t pin) { + if (!_isValidPin(pin)) return false; return _readBit(REG_GPIO_I_L, REG_GPIO_I_H, pin); } -void PY32IOExpander_Class::resetIrq() +bool PY32IOExpander_Class::_resetIrq() { // Clear all interrupts by writing 1s to IS registers - writeRegister8(REG_GPIO_IS_L, 0xFF); - writeRegister8(REG_GPIO_IS_H, 0xFF); // Only bits 0-5 used for high byte (pins 8-13) + bool l_ok = writeRegister8(REG_GPIO_IS_L, 0xFF); + bool h_ok = writeRegister8(REG_GPIO_IS_H, 0xFF); // Only bits 0-5 used for high byte (pins 8-13) + return l_ok && h_ok; } -void PY32IOExpander_Class::disableIrq() +bool PY32IOExpander_Class::_disableIrq() { // Disable all interrupts - writeRegister8(REG_GPIO_IE_L, 0x00); - writeRegister8(REG_GPIO_IE_H, 0x00); + bool l_ok = writeRegister8(REG_GPIO_IE_L, 0x00); + bool h_ok = writeRegister8(REG_GPIO_IE_H, 0x00); + return l_ok && h_ok; } -void PY32IOExpander_Class::enableIrq() +bool PY32IOExpander_Class::_enableIrq() { // Enable all interrupts - writeRegister8(REG_GPIO_IE_L, 0xFF); - writeRegister8(REG_GPIO_IE_H, 0x3F); // Pins 8-13 + bool l_ok = writeRegister8(REG_GPIO_IE_L, 0xFF); + bool h_ok = writeRegister8(REG_GPIO_IE_H, 0x3F); // Pins 8-13 + return l_ok && h_ok; } +void PY32IOExpander_Class::enablePull(uint8_t pin, bool enablePull) +{ + if (!_isValidPin(pin)) return; + if (enablePull) { + // Enable Pull Up by default if neither is set + bool pu = _readBit(REG_GPIO_PU_L, REG_GPIO_PU_H, pin); + bool pd = _readBit(REG_GPIO_PD_L, REG_GPIO_PD_H, pin); + if (!pu && !pd) { + _setPullMode(pin, pull_up); + } + // If one is already set, leave it. + } else { + _setPullMode(pin, pull_none); + } +} + +#if PY32IOEXPANDER_STATUS_API +bool PY32IOExpander_Class::setDirection(uint8_t pin, bool direction) { return _setDirection(pin, direction); } +bool PY32IOExpander_Class::setPullMode(uint8_t pin, gpio_pull_t mode) { return _setPullMode(pin, mode); } +bool PY32IOExpander_Class::setPullMode(uint8_t pin, bool mode) { return _setPullMode(pin, mode ? pull_up : pull_down); } +bool PY32IOExpander_Class::setHighImpedance(uint8_t pin, bool enable) { return _setHighImpedance(pin, enable); } +bool PY32IOExpander_Class::digitalWrite(uint8_t pin, bool level) { return _digitalWrite(pin, level); } +bool PY32IOExpander_Class::resetIrq() { return _resetIrq(); } +bool PY32IOExpander_Class::disableIrq() { return _disableIrq(); } +bool PY32IOExpander_Class::enableIrq() { return _enableIrq(); } +#else +void PY32IOExpander_Class::setDirection(uint8_t pin, bool direction) { _setDirection(pin, direction); } +// mode: false=down, true=up +void PY32IOExpander_Class::setPullMode(uint8_t pin, bool mode) { _setPullMode(pin, mode ? pull_up : pull_down); } +bool PY32IOExpander_Class::setPullMode(uint8_t pin, gpio_pull_t mode) { return _setPullMode(pin, mode); } +void PY32IOExpander_Class::setHighImpedance(uint8_t pin, bool enable) { _setHighImpedance(pin, enable); } +void PY32IOExpander_Class::digitalWrite(uint8_t pin, bool level) { _digitalWrite(pin, level); } +void PY32IOExpander_Class::resetIrq() { _resetIrq(); } +void PY32IOExpander_Class::disableIrq() { _disableIrq(); } +void PY32IOExpander_Class::enableIrq() { _enableIrq(); } +#endif + uint16_t PY32IOExpander_Class::readDeviceUID() { uint8_t l = readRegister8(REG_UID_L); diff --git a/src/drivers/PY32IOExpander/PY32IOExpander.hpp b/src/drivers/PY32IOExpander/PY32IOExpander.hpp index 2a39dab..c59b25b 100644 --- a/src/drivers/PY32IOExpander/PY32IOExpander.hpp +++ b/src/drivers/PY32IOExpander/PY32IOExpander.hpp @@ -8,11 +8,26 @@ #include +// M5Unified 0.2.21 changed the IOExpander_Base virtuals: they report the write +// status as bool, setPullMode takes gpio_pull_t and enablePull was removed. +#if defined(M5UNIFIED_VERSION_MAJOR) && \ + ((M5UNIFIED_VERSION_MAJOR > 0) || (M5UNIFIED_VERSION_MINOR > 2) || \ + (M5UNIFIED_VERSION_MINOR == 2 && M5UNIFIED_VERSION_PATCH >= 21)) +#define PY32IOEXPANDER_STATUS_API 1 +#else +#define PY32IOEXPANDER_STATUS_API 0 +#endif + namespace m5 { class PY32IOExpander_Class : public IOExpander_Base { public: static constexpr std::uint8_t DEFAULT_ADDRESS = 0x6F; +#if !PY32IOEXPANDER_STATUS_API + // Same names and values as IOExpander_Base::gpio_pull_t in M5Unified 0.2.21+ + enum gpio_pull_t : std::uint8_t { pull_none = 0, pull_up = 1, pull_down = 2 }; +#endif + PY32IOExpander_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = 100000, m5::I2C_Class* i2c = &m5::In_I2C) : IOExpander_Base(i2c_addr, freq, i2c) @@ -23,6 +38,29 @@ class PY32IOExpander_Class : public IOExpander_Base { // IOExpander_Base overrides // false input, true output + // Return true when every register access was acknowledged. +#if PY32IOEXPANDER_STATUS_API + bool setDirection(uint8_t pin, bool direction) override; + + bool setPullMode(uint8_t pin, gpio_pull_t mode) override; + + // false down, true up (kept for callers written against the old API) + bool setPullMode(uint8_t pin, bool mode); + + // Kept for callers written against the old API: true enables the pull-up unless a pull + // is already set, false disables both pulls. + void enablePull(uint8_t pin, bool enablePull); + + bool setHighImpedance(uint8_t pin, bool enable) override; + + bool digitalWrite(uint8_t pin, bool level) override; + + bool resetIrq() override; + + bool disableIrq() override; + + bool enableIrq() override; +#else void setDirection(uint8_t pin, bool direction) override; void enablePull(uint8_t pin, bool enablePull) override; @@ -30,22 +68,25 @@ class PY32IOExpander_Class : public IOExpander_Base { // false down, true up void setPullMode(uint8_t pin, bool mode) override; - // false push-pull, true open-drain - void setDriveMode(uint8_t pin, bool openDrain); + bool setPullMode(uint8_t pin, gpio_pull_t mode); void setHighImpedance(uint8_t pin, bool enable) override; - bool getWriteValue(uint8_t pin) override; - void digitalWrite(uint8_t pin, bool level) override; - bool digitalRead(uint8_t pin) override; - void resetIrq() override; void disableIrq() override; void enableIrq() override; +#endif + + // false push-pull, true open-drain + void setDriveMode(uint8_t pin, bool openDrain); + + bool getWriteValue(uint8_t pin) override; + + bool digitalRead(uint8_t pin) override; // Extended functionality uint16_t readDeviceUID(); @@ -69,7 +110,17 @@ class PY32IOExpander_Class : public IOExpander_Base { void refreshLeds(); private: - void _writeBit(uint8_t reg_l, uint8_t reg_h, uint8_t pin, bool value); + // Version independent implementation; the overrides above wrap these. + bool _setDirection(uint8_t pin, bool direction); + bool _setPullMode(uint8_t pin, gpio_pull_t mode); + bool _setHighImpedance(uint8_t pin, bool enable); + bool _digitalWrite(uint8_t pin, bool level); + bool _resetIrq(); + bool _disableIrq(); + bool _enableIrq(); + + static bool _isValidPin(uint8_t pin) { return pin < 14; } + bool _writeBit(uint8_t reg_l, uint8_t reg_h, uint8_t pin, bool value); bool _readBit(uint8_t reg_l, uint8_t reg_h, uint8_t pin); }; } // namespace m5