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
74 changes: 53 additions & 21 deletions src/utility/Power_Class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ namespace m5
static constexpr uint8_t ip2315_i2c_addr = 0x75; // M5PaperMono USB fast-charger
static constexpr int M5PaperS3_CHG_STAT_PIN = GPIO_NUM_4;

static void init_papermono_ip2315_access(void)
{
auto& ioe1 = M5.getIOExpander(0);
ioe1.setHighImpedance(M5IOE1_Class::gpio11, false);
ioe1.setDirection(M5IOE1_Class::gpio11, true);
ioe1.digitalWrite(M5IOE1_Class::gpio11, false);
}

static void set_papermono_ip2315_enabled(bool enable)
{
M5.getIOExpander(0).digitalWrite(M5IOE1_Class::gpio11, enable);
}

static bool wait_papermono_ip2315_ready(void)
{
m5gfx::delay(2);
for (int i = 0; i < 64; ++i)
{
if (M5.In_I2C.scanID(ip2315_i2c_addr, i2c_freq)) { return true; }
}
return false;
}

#elif defined (CONFIG_IDF_TARGET_ESP32C6)
static constexpr int M5NanoC6_LED_PIN = GPIO_NUM_7;

Expand Down Expand Up @@ -258,20 +281,23 @@ namespace m5
case board_t::board_M5PaperMono:
_rtcIntPin = GPIO_NUM_1;
_pmic = pmic_t::pmic_m5pm1;
// M5PaperMono charging is controlled by the IP2316 charger (not PM1).
// Enable IP2316 readout/control by driving IOE1 IO11 ("CHARGE READ") high.
// IP2316 stays off the I2C bus while IO11 is low, and answers ~1.3ms after high
// (measured), so polling its address is enough; no fixed startup delay is needed.
// IO11 = bit10 of the 16-bit GPIO regs = bit2 of the high byte (P14-P9).
{
auto& ioe1 = M5.getIOExpander(0);
ioe1.setHighImpedance(M5IOE1_Class::gpio11, false);
ioe1.setDirection(M5IOE1_Class::gpio11, true);
ioe1.digitalWrite(M5IOE1_Class::gpio11, true);
}
// Wait for the IP2316 to wake, then enable battery charging (SYS_CTL1 0x01 bit0 = EN_CHG).
for (int i = 0; i < 64 && !M5.In_I2C.scanID(ip2315_i2c_addr, i2c_freq); ++i) {}
M5.In_I2C.bitOn(ip2315_i2c_addr, 0x01, 1 << 0, i2c_freq);
_wakeupPin = GPIO_NUM_1; // PY IQR

M5pm1.clearWakeSource();
M5pm1.clearIRQStatus();
M5pm1.setGPIOIRQMaskBits(0x1E); // enable GPIO0 interrupt, disable other GPIO IRQ

M5pm1.setGPIOFunction(M5PM1_Class::gpio0, M5PM1_Class::gpio);
M5pm1.setGPIOMode(M5PM1_Class::gpio0, M5PM1_Class::input);

M5pm1.setGPIOMode(M5PM1_Class::gpio1, M5PM1_Class::output);
M5pm1.setGPIODrive(M5PM1_Class::gpio1, M5PM1_Class::push_pull);
M5pm1.setGPIOPull(M5PM1_Class::gpio1, M5PM1_Class::pull_up);
M5pm1.setGPIOOutput(M5PM1_Class::gpio1, true);
M5pm1.setGPIOFunction(M5PM1_Class::gpio1, M5PM1_Class::irq);

// Keep IP2316 off the I2C bus until charge control/status is requested.
init_papermono_ip2315_access();
break;

case board_t::board_M5Capsule:
Expand Down Expand Up @@ -1613,10 +1639,13 @@ namespace m5
return;
}
// M5PaperMono: charging is controlled by the IP2316 charger, not PM1.
// IP2316 SYS_CTL1 (0x01) bit0 = EN_CHG. (IO11 was driven high in begin().)
if (M5.getBoard() == board_t::board_M5PaperMono) {
if (enable) { M5.In_I2C.bitOn (ip2315_i2c_addr, 0x01, 1 << 0, i2c_freq); }
else { M5.In_I2C.bitOff(ip2315_i2c_addr, 0x01, 1 << 0, i2c_freq); }
set_papermono_ip2315_enabled(true);
if (wait_papermono_ip2315_ready()) {
if (enable) { M5.In_I2C.bitOn (ip2315_i2c_addr, 0x01, 1 << 0, i2c_freq); }
else { M5.In_I2C.bitOff(ip2315_i2c_addr, 0x01, 1 << 0, i2c_freq); }
}
set_papermono_ip2315_enabled(false);
return;
}
M5pm1.setBatteryCharge(enable);
Expand Down Expand Up @@ -1856,15 +1885,18 @@ namespace m5
{
// Running from battery (no external power) -> not charging.
if (M5pm1.getPowerSource() == M5PM1_Class::battery) { return is_charging_t::is_discharging; }
// External power present. The IP2316 charger (IO11 enabled in begin()) reports
// External power present. The IP2316 charger reports
// its state in REG_CHG_STAT(0xC7): bit7 = charging in progress (measured:
// 0x82 charging / 0x45 charge-complete / 0x00 charge-disabled).
if (M5.In_I2C.scanID(ip2315_i2c_addr, i2c_freq))
set_papermono_ip2315_enabled(true);
is_charging_t res = is_charging_t::is_discharging;
if (wait_papermono_ip2315_ready())
{
uint8_t chg_stat = M5.In_I2C.readRegister8(ip2315_i2c_addr, 0xC7, i2c_freq);
return (chg_stat & (1 << 7)) ? is_charging_t::is_charging : is_charging_t::is_discharging;
res = (chg_stat & (1 << 7)) ? is_charging_t::is_charging : is_charging_t::is_discharging;
}
return is_charging_t::is_discharging; // fallback: charger not responding -> not charging
set_papermono_ip2315_enabled(false);
return res;
}
break;

Expand Down
26 changes: 24 additions & 2 deletions src/utility/RTC_Class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@

namespace m5
{
#if defined (CONFIG_IDF_TARGET_ESP32S3)
static void clear_m5pm1_rtc_irq(void)
{
if (M5.getBoard() == board_t::board_M5PaperMono
&& M5.Power.getType() == Power_Class::pmic_t::pmic_m5pm1)
{
M5.Power.M5pm1.clearWakeSource();
M5.Power.M5pm1.clearIRQStatus();
}
}
#else
static void clear_m5pm1_rtc_irq(void) {}
#endif

bool RTC_Class::begin(I2C_Class* i2c, board_t board)
{
if (i2c)
Expand Down Expand Up @@ -114,7 +128,10 @@ namespace m5

std::uint32_t RTC_Class::setTimerIRQ(std::uint32_t timer_msec)
{
return _rtc_instance ? _rtc_instance->setTimerIRQ(timer_msec) : 0;
if (!_rtc_instance) { return 0; }
auto result = _rtc_instance->setTimerIRQ(timer_msec);
clear_m5pm1_rtc_irq();
return result;
}

int RTC_Class::setAlarmIRQ(const tm* datetime)
Expand All @@ -129,7 +146,10 @@ namespace m5

int RTC_Class::setAlarmIRQ(const rtc_date_t* date, const rtc_time_t* time)
{
return _rtc_instance ? _rtc_instance->setAlarmIRQ(date, time) : -1;
if (!_rtc_instance) { return -1; }
auto result = _rtc_instance->setAlarmIRQ(date, time);
clear_m5pm1_rtc_irq();
return result;
}

bool RTC_Class::getIRQstatus(void)
Expand All @@ -141,12 +161,14 @@ namespace m5
{
if (!_rtc_instance) { return; }
_rtc_instance->clearIRQ();
clear_m5pm1_rtc_irq();
}

void RTC_Class::disableIRQ(void)
{
if (!_rtc_instance) { return; }
_rtc_instance->disableIRQ();
clear_m5pm1_rtc_irq();
}

void RTC_Class::setSystemTimeFromRtc(struct timezone* tz)
Expand Down
55 changes: 21 additions & 34 deletions src/utility/led/LED_PaperMono_Class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,43 @@

#include "LED_PaperMono_Class.hpp"
#include "../../M5Unified.hpp"
#include "../M5IOE1_Class.hpp"

#if defined (CONFIG_IDF_TARGET_ESP32S3)

namespace m5
{
// RGBLED_R = PMIC -> LED_EN_PP
// RGBLED_G = PYB -> G8
// RGBLED_B = PYB -> G2
// RGBLED_B = PYB -> G9

static constexpr size_t led_count = 1;
static constexpr uint8_t m5pm1_i2c_addr = 0x6E;
static constexpr uint8_t m5ioe1_i2c_addr = 0x4F;
static constexpr uint32_t i2c_freq = 100000;
static constexpr auto ioe1_led_g_pin = M5IOE1_Class::gpio8;
static constexpr auto ioe1_led_b_pin = M5IOE1_Class::gpio9;

bool LED_PaperMono_Class::begin(void)
{
// PM1 LED_EN (red), set PP mode and enable LED output
M5.In_I2C.bitOff(m5pm1_i2c_addr, 0x13, 0x20, i2c_freq);

// IOE1 IO2 (blue), IO8 (green) output
M5.In_I2C.bitOn(m5ioe1_i2c_addr, 0x03, 0x82, i2c_freq);
auto& ioe1 = static_cast<M5IOE1_Class&>(M5.getIOExpander(0));
ioe1.setDirection(ioe1_led_g_pin, true);
ioe1.setDirection(ioe1_led_b_pin, true);
ioe1.setHighImpedance(ioe1_led_g_pin, false);
ioe1.setHighImpedance(ioe1_led_b_pin, false);

// IOE1 IO2 (blue), IO8 (green) push-pull
M5.In_I2C.bitOff(m5ioe1_i2c_addr, 0x13, 0x82, i2c_freq);


{
setBrightness(_brightness);
return true;
}
return false;
setBrightness(_brightness);
return true;
}

void LED_PaperMono_Class::setColors(const RGBColor* values, size_t index, size_t length)
{
if (index + length > led_count) {
if (index >= led_count) {
return;
}
if (length > led_count - index) {
length = led_count - index;
}
std::copy(values, values + length, &_rgb_buffer + index);
Expand All @@ -47,16 +48,13 @@ namespace m5
void LED_PaperMono_Class::setBrightness(const uint8_t brightness)
{
_brightness = brightness;
std::array<uint8_t, led_count> br_buffer;
br_buffer.fill(brightness);
// writeRegister(0x80, br_buffer.data(), br_buffer.size());
}

void LED_PaperMono_Class::display(void)
{
// RED = PMIC -> LED_EN_PP
// GREEN = PYB -> G8
// BLUE = PYB -> G2
// BLUE = PYB -> G9
uint32_t br = _brightness + 1;
br = br * br;

Expand All @@ -73,23 +71,12 @@ namespace m5
M5.In_I2C.bitOn(m5pm1_i2c_addr, 0x06, 0x10, i2c_freq);
}

if (g < 2048) {
M5.In_I2C.bitOff(m5ioe1_i2c_addr, 0x05, 0x80, i2c_freq);
} else {
M5.In_I2C.bitOn(m5ioe1_i2c_addr, 0x05, 0x80, i2c_freq);
}

if (b < 2048) {
M5.In_I2C.bitOff(m5ioe1_i2c_addr, 0x05, 0x02, i2c_freq);
} else {
M5.In_I2C.bitOn(m5ioe1_i2c_addr, 0x05, 0x02, i2c_freq);
}
auto& ioe1 = static_cast<M5IOE1_Class&>(M5.getIOExpander(0));
ioe1.digitalWrite(ioe1_led_g_pin, g >= 2048);
ioe1.digitalWrite(ioe1_led_b_pin, b >= 2048);

{ // PWM2 (IO8) for green
if (g > 4095) g = 4095;
uint8_t data[2] = { uint8_t(g & 0xFF), uint8_t((g >> 8) | 0x80) };
M5.In_I2C.writeRegister(m5ioe1_i2c_addr, 0x1D, data, sizeof(data), i2c_freq);
}
if (g > 4095) { g = 4095; }
ioe1.setPwmDuty(M5IOE1_Class::pwm_ch2, g, g > 0);
}
}

Expand Down
63 changes: 63 additions & 0 deletions src/utility/power/M5PM1_Class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace m5
// M5PM1 registers
static constexpr const uint8_t M5PM1_REG_DEVICE_ID = 0x00;
static constexpr const uint8_t M5PM1_REG_PWR_SRC = 0x04;
static constexpr const uint8_t M5PM1_REG_WAKE_SRC = 0x05;
static constexpr const uint8_t M5PM1_REG_PWR_CFG = 0x06;
static constexpr const uint8_t M5PM1_REG_I2C_CFG = 0x09;
static constexpr const uint8_t M5PM1_REG_WDT_CNT = 0x0A;
Expand All @@ -22,12 +23,19 @@ namespace m5
static constexpr const uint8_t M5PM1_REG_GPIO_OUT = 0x11;
static constexpr const uint8_t M5PM1_REG_GPIO_IN = 0x12;
static constexpr const uint8_t M5PM1_REG_GPIO_DRV = 0x13;
static constexpr const uint8_t M5PM1_REG_GPIO_PUPD0 = 0x14;
static constexpr const uint8_t M5PM1_REG_GPIO_PUPD1 = 0x15;
static constexpr const uint8_t M5PM1_REG_GPIO_FUNC0 = 0x16;
static constexpr const uint8_t M5PM1_REG_GPIO_FUNC1 = 0x17;
static constexpr const uint8_t M5PM1_REG_VBAT_L = 0x22;
static constexpr const uint8_t M5PM1_REG_VIN_L = 0x24;
static constexpr const uint8_t M5PM1_REG_5VOUT_L = 0x26;
static constexpr const uint8_t M5PM1_REG_IRQ_STATUS1 = 0x40;
static constexpr const uint8_t M5PM1_REG_IRQ_STATUS2 = 0x41;
static constexpr const uint8_t M5PM1_REG_IRQ_STATUS3 = 0x42;
static constexpr const uint8_t M5PM1_REG_IRQ_MASK1 = 0x43;
static constexpr const uint8_t M5PM1_REG_IRQ_MASK2 = 0x44;
static constexpr const uint8_t M5PM1_REG_IRQ_MASK3 = 0x45;

static constexpr const uint8_t M5PM1_PWR_CFG_CHG_EN = 1 << 0;
static constexpr const uint8_t M5PM1_PWR_CFG_LDO_EN = 1 << 2;
Expand Down Expand Up @@ -104,6 +112,18 @@ namespace m5
: bitOff(M5PM1_REG_GPIO_MODE, mask);
}

bool M5PM1_Class::setGPIOPull(gpio_t pin, gpio_pull_t pull)
{
if (!is_valid_gpio(pin)) { return false; }
auto num = gpio_num(pin);
auto reg = num < 4 ? M5PM1_REG_GPIO_PUPD0 : M5PM1_REG_GPIO_PUPD1;
auto shift = static_cast<std::uint8_t>((num < 4 ? num : num - 4) * 2);
std::uint8_t mask = 0x03 << shift;
std::uint8_t reg_val = readRegister8(reg);
reg_val = (reg_val & ~mask) | (static_cast<std::uint8_t>(pull) << shift);
return writeRegister8(reg, reg_val);
}

bool M5PM1_Class::setGPIODrive(gpio_t pin, gpio_drive_t drive)
{
if (!is_valid_gpio(pin)) { return false; }
Expand Down Expand Up @@ -131,6 +151,49 @@ namespace m5
return readRegister8(M5PM1_REG_GPIO_OUT) & (1 << gpio_num(pin));
}

bool M5PM1_Class::clearWakeSource(std::uint8_t mask)
{
auto src = readRegister8(M5PM1_REG_WAKE_SRC);
return writeRegister8(M5PM1_REG_WAKE_SRC, src & ~mask);
}

bool M5PM1_Class::clearGPIOIRQStatus(void)
{
return writeRegister8(M5PM1_REG_IRQ_STATUS1, 0x00);
}

bool M5PM1_Class::clearSystemIRQStatus(void)
{
return writeRegister8(M5PM1_REG_IRQ_STATUS2, 0x00);
}

bool M5PM1_Class::clearButtonIRQStatus(void)
{
return writeRegister8(M5PM1_REG_IRQ_STATUS3, 0x00);
}

bool M5PM1_Class::clearIRQStatus(void)
{
return clearGPIOIRQStatus()
&& clearSystemIRQStatus()
&& clearButtonIRQStatus();
}

bool M5PM1_Class::setGPIOIRQMaskBits(std::uint8_t mask)
{
return writeRegister8(M5PM1_REG_IRQ_MASK1, mask & 0x1F);
}

bool M5PM1_Class::setSystemIRQMaskBits(std::uint8_t mask)
{
return writeRegister8(M5PM1_REG_IRQ_MASK2, mask & 0x3F);
}

bool M5PM1_Class::setButtonIRQMaskBits(std::uint8_t mask)
{
return writeRegister8(M5PM1_REG_IRQ_MASK3, mask & 0x07);
}

bool M5PM1_Class::setBatteryCharge(bool enable)
{
return enable ? bitOn(M5PM1_REG_PWR_CFG, M5PM1_PWR_CFG_CHG_EN)
Expand Down
Loading
Loading