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
47 changes: 30 additions & 17 deletions src/M5Unified.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "M5Unified.hpp"
#include "utility/PI4IOE5V6408_Class.hpp"
#include "utility/M5IOE1_Class.hpp"

#if !defined (M5UNIFIED_PC_BUILD)
#include <soc/soc.h>
Expand Down Expand Up @@ -428,7 +429,6 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
static constexpr uint8_t es8388_i2c_addr = 0x10;
static constexpr uint8_t pi4io1_i2c_addr = 0x43;
static constexpr uint8_t m5pm1_i2c_addr = 0x6E;
static constexpr uint8_t m5ioe1_i2c_addr = 0x4F;
#if defined (CONFIG_IDF_TARGET_ESP32S3)
static constexpr uint8_t aw88298_i2c_addr = 0x36;
static constexpr uint8_t aw9523_i2c_addr = 0x58;
Expand Down Expand Up @@ -599,15 +599,17 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
};
if (enabled)
{
self->In_I2C.bitOn(m5ioe1_i2c_addr, 0x05, 0b00000100, 100000); // Enable Audio Power (M5IOE1_G3)
auto& ioe1 = self->getIOExpander(0);
ioe1.digitalWrite(M5IOE1_Class::gpio3, true); // Enable Audio Power (M5IOE1_G3)
self->delay(10);
in_i2c_bulk_write(es8311_i2c_addr0, enabled_bulk_data, 100000, 3);
self->In_I2C.bitOn(m5ioe1_i2c_addr, 0x06, 0b00000010, 100000); // Enable PA (M5IOE1_G10)
ioe1.digitalWrite(M5IOE1_Class::gpio10, true); // Enable PA (M5IOE1_G10)
}
else
{
self->In_I2C.bitOff(m5ioe1_i2c_addr, 0x06, 0b00000010, 100000); // Disable PA (M5IOE1_G10)
self->In_I2C.bitOff(m5ioe1_i2c_addr, 0x05, 0b00000100, 100000); // Disable Audio Power (M5IOE1_G3)
auto& ioe1 = self->getIOExpander(0);
ioe1.digitalWrite(M5IOE1_Class::gpio10, false); // Disable PA (M5IOE1_G10)
ioe1.digitalWrite(M5IOE1_Class::gpio3, false); // Disable Audio Power (M5IOE1_G3)
}
#endif
return true;
Expand Down Expand Up @@ -1041,7 +1043,7 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
};
if (enabled)
{
self->In_I2C.bitOn(m5ioe1_i2c_addr, 0x05, 0b00000100, 100000); // Enable Audio Power (M5IOE1_G3)
self->getIOExpander(0).digitalWrite(M5IOE1_Class::gpio3, true); // Enable Audio Power (M5IOE1_G3)
self->delay(5);
}
m5gfx::i2c::i2c_temporary_switcher_t backup_i2c_setting(1, GPIO_NUM_47, GPIO_NUM_48);
Expand Down Expand Up @@ -1730,6 +1732,14 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
_io_expander[0].reset(ioexp);
}
break;
case board_t::board_M5PaperMono:
case board_t::board_M5StopWatch:
{
auto ioexp = new M5IOE1_Class;
ioexp->begin();
_io_expander[0].reset(ioexp);
}
break;
#endif
default:
break;
Expand Down Expand Up @@ -1999,20 +2009,23 @@ static constexpr const uint8_t _pin_table_mbus[][31] = {
case board_t::board_M5StopWatch:
m5gfx::pinMode(GPIO_NUM_1, m5gfx::pin_mode_t::input);
m5gfx::pinMode(GPIO_NUM_2, m5gfx::pin_mode_t::input);
// M5IOE1@0x4F: display power M5IOE1_G8
// M5IOE1 PIN4: display power
{
this->In_I2C.writeRegister8(m5ioe1_i2c_addr, 0x23, 0x00, 100000);
this->In_I2C.bitOff(m5ioe1_i2c_addr, 0x13, 0b00001000, 100000);
this->In_I2C.bitOn(m5ioe1_i2c_addr, 0x03, 0b00001000, 100000);
this->In_I2C.bitOn(m5ioe1_i2c_addr, 0x05, 0b00001000, 100000);
auto& ioe1 = getIOExpander(0);
ioe1.setHighImpedance(M5IOE1_Class::gpio4, false);
ioe1.setDirection(M5IOE1_Class::gpio4, true);
ioe1.digitalWrite(M5IOE1_Class::gpio4, true);
}
// M5IOE1_G3 codec power / M5IOE1_G10 PA
this->In_I2C.bitOff(m5ioe1_i2c_addr, 0x13, 0b00000100, 100000);
this->In_I2C.bitOff(m5ioe1_i2c_addr, 0x14, 0b00000010, 100000);
this->In_I2C.bitOn(m5ioe1_i2c_addr, 0x03, 0b00000100, 100000);
this->In_I2C.bitOn(m5ioe1_i2c_addr, 0x04, 0b00000010, 100000);
this->In_I2C.bitOff(m5ioe1_i2c_addr, 0x05, 0b00000100, 100000);
this->In_I2C.bitOff(m5ioe1_i2c_addr, 0x06, 0b00000010, 100000);
{
auto& ioe1 = getIOExpander(0);
ioe1.setHighImpedance(M5IOE1_Class::gpio3, false);
ioe1.setHighImpedance(M5IOE1_Class::gpio10, false);
ioe1.setDirection(M5IOE1_Class::gpio3, true);
ioe1.setDirection(M5IOE1_Class::gpio10, true);
ioe1.digitalWrite(M5IOE1_Class::gpio3, false);
ioe1.digitalWrite(M5IOE1_Class::gpio10, false);
}
break;

#elif defined (CONFIG_IDF_TARGET_ESP32P4)
Expand Down
139 changes: 139 additions & 0 deletions src/utility/M5IOE1_Class.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) M5Stack. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include "M5IOE1_Class.hpp"

namespace m5
{
namespace
{
static constexpr std::uint8_t M5IOE1_REG_UID_L = 0x00;
static constexpr std::uint8_t M5IOE1_REG_GPIO_MODE_L = 0x03;
static constexpr std::uint8_t M5IOE1_REG_GPIO_OUT_L = 0x05;
static constexpr std::uint8_t M5IOE1_REG_GPIO_IN_L = 0x07;
static constexpr std::uint8_t M5IOE1_REG_GPIO_PU_L = 0x09;
static constexpr std::uint8_t M5IOE1_REG_GPIO_PD_L = 0x0B;
static constexpr std::uint8_t M5IOE1_REG_GPIO_IE_L = 0x0D;
static constexpr std::uint8_t M5IOE1_REG_GPIO_IS_L = 0x11;
static constexpr std::uint8_t M5IOE1_REG_GPIO_DRV_L = 0x13;
static constexpr std::uint8_t M5IOE1_REG_PWM1_DUTY_L = 0x1B;
static constexpr std::uint8_t M5IOE1_REG_I2C_CFG = 0x23;
static constexpr std::uint8_t M5IOE1_REG_PWM_FREQ_L = 0x25;
static constexpr std::uint8_t M5IOE1_PWM_POLARITY = 1 << 6;
static constexpr std::uint8_t M5IOE1_PWM_ENABLE = 1 << 7;
static constexpr std::uint32_t M5IOE1_I2C_FREQ_100K = 100000;
}

bool M5IOE1_Class::begin()
{
if (!_init) {
_freq = M5IOE1_I2C_FREQ_100K;
std::uint8_t uid[2] = {};
auto res = readRegister(M5IOE1_REG_UID_L, uid, sizeof(uid));
if (res) {
// Disable I2C idle sleep and keep the IOE1 bus at its default 100 kHz speed.
writeRegister8(M5IOE1_REG_I2C_CFG, 0x00);
}
_init = res;
}
return _init;
}

void M5IOE1_Class::setDirection(uint8_t pin, bool direction)
{
if (!_isValidPin(pin)) { return; }
// 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);
}

void M5IOE1_Class::enablePull(uint8_t pin, bool enablePull)
{
if (!_isValidPin(pin)) { return; }
const auto pu_reg = _regForPin(M5IOE1_REG_GPIO_PU_L, pin);
const auto pd_reg = _regForPin(M5IOE1_REG_GPIO_PD_L, pin);
const auto bit = _bitForPin(pin);
if (enablePull) {
bitOn(pu_reg, bit);
} else {
bitOff(pu_reg, bit);
bitOff(pd_reg, bit);
}
}

void M5IOE1_Class::setPullMode(uint8_t pin, bool mode)
{
if (!_isValidPin(pin)) { return; }
const auto pu_reg = _regForPin(M5IOE1_REG_GPIO_PU_L, pin);
const auto pd_reg = _regForPin(M5IOE1_REG_GPIO_PD_L, pin);
const auto bit = _bitForPin(pin);
// false=pull-down, true=pull-up.
mode ? bitOn(pu_reg, bit) : bitOff(pu_reg, bit);
mode ? bitOff(pd_reg, bit) : bitOn(pd_reg, bit);
}

void M5IOE1_Class::setHighImpedance(uint8_t pin, bool enable)
{
if (!_isValidPin(pin)) { return; }
// 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);
}

bool M5IOE1_Class::getWriteValue(uint8_t pin)
{
if (!_isValidPin(pin)) { return false; }
return (readRegister8(_regForPin(M5IOE1_REG_GPIO_OUT_L, pin)) & _bitForPin(pin)) != 0;
}

void M5IOE1_Class::digitalWrite(uint8_t pin, bool level)
{
if (!_isValidPin(pin)) { return; }
const auto reg = _regForPin(M5IOE1_REG_GPIO_OUT_L, pin);
const auto bit = _bitForPin(pin);
level ? bitOn(reg, bit) : bitOff(reg, bit);
}

bool M5IOE1_Class::digitalRead(uint8_t pin)
{
if (!_isValidPin(pin)) { return false; }
return (readRegister8(_regForPin(M5IOE1_REG_GPIO_IN_L, pin)) & _bitForPin(pin)) != 0;
}

void M5IOE1_Class::setPwmFrequency(std::uint16_t frequency)
{
std::uint8_t data[2] = { static_cast<std::uint8_t>(frequency & 0xFF), static_cast<std::uint8_t>(frequency >> 8) };
writeRegister(M5IOE1_REG_PWM_FREQ_L, data, sizeof(data));
}

void M5IOE1_Class::setPwmDuty(std::uint8_t channel, std::uint16_t duty12, bool enable, bool polarity)
{
if (channel > pwm_ch4) { return; }
duty12 &= 0x0FFF;
std::uint8_t high = static_cast<std::uint8_t>(duty12 >> 8);
if (enable) { high |= M5IOE1_PWM_ENABLE; }
if (polarity) { high |= M5IOE1_PWM_POLARITY; }
std::uint8_t data[2] = { static_cast<std::uint8_t>(duty12 & 0xFF), high };
writeRegister(static_cast<std::uint8_t>(M5IOE1_REG_PWM1_DUTY_L + channel * 2), data, sizeof(data));
}

void M5IOE1_Class::resetIrq()
{
std::uint8_t data[2] = { 0x00, 0x00 };
writeRegister(M5IOE1_REG_GPIO_IS_L, data, sizeof(data));
}

void M5IOE1_Class::disableIrq()
{
std::uint8_t data[2] = { 0x00, 0x00 };
writeRegister(M5IOE1_REG_GPIO_IE_L, data, sizeof(data));
}

void M5IOE1_Class::enableIrq()
{
std::uint8_t data[2] = { 0xFF, 0x3F };
writeRegister(M5IOE1_REG_GPIO_IE_L, data, sizeof(data));
}
}
78 changes: 78 additions & 0 deletions src/utility/M5IOE1_Class.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) M5Stack. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#ifndef __M5_M5IOE1_CLASS_H__
#define __M5_M5IOE1_CLASS_H__

#include "IOExpander_Base.hpp"

namespace m5
{
class M5IOE1_Class : public IOExpander_Base
{
public:
static constexpr std::uint8_t DEFAULT_ADDRESS = 0x4F;
static constexpr std::uint32_t DEFAULT_FREQ = 100000;

enum gpio_t : std::uint8_t
{ gpio1 = 0
, gpio2 = 1
, gpio3 = 2
, gpio4 = 3
, gpio5 = 4
, gpio6 = 5
, gpio7 = 6
, gpio8 = 7
, gpio9 = 8
, gpio10 = 9
, gpio11 = 10
, gpio12 = 11
, gpio13 = 12
, gpio14 = 13
};

enum pwm_channel_t : std::uint8_t
{ pwm_ch1 = 0 // IO_9
, pwm_ch2 = 1 // IO_8
, pwm_ch3 = 2 // IO_11
, pwm_ch4 = 3 // IO_10
};

M5IOE1_Class(std::uint8_t i2c_addr = DEFAULT_ADDRESS, std::uint32_t freq = DEFAULT_FREQ, m5::I2C_Class* i2c = &m5::In_I2C)
: IOExpander_Base(i2c_addr, freq, i2c)
{}

bool begin();

void setDirection(uint8_t pin, bool direction) override;

void enablePull(uint8_t pin, bool enablePull) override;

void setPullMode(uint8_t pin, bool mode) override;

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 setPwmFrequency(std::uint16_t frequency);

void setPwmDuty(std::uint8_t channel, std::uint16_t duty12, bool enable = true, bool polarity = false);

void resetIrq() override;

void disableIrq() override;

void enableIrq() override;

private:
static bool _isValidPin(uint8_t pin) { return pin < 14; }
static std::uint8_t _regForPin(std::uint8_t reg_low, std::uint8_t pin) { return reg_low + (pin >> 3); }
static std::uint8_t _bitForPin(std::uint8_t pin) { return 1 << (pin & 7); }
};
}

#endif
Loading
Loading