Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/M5StackChan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
143 changes: 87 additions & 56 deletions src/drivers/PY32IOExpander/PY32IOExpander.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
Expand Down
65 changes: 58 additions & 7 deletions src/drivers/PY32IOExpander/PY32IOExpander.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,26 @@

#include <M5Unified.hpp>

// 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)
Expand All @@ -23,29 +38,55 @@ 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;

// 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();
Expand All @@ -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
Expand Down