pdf-icon

Arduino入門

2. デバイス&サンプル

5. 拡張モジュール&サンプル

アクセサリー

6. アプリケーション

Stamp-S3Bat M5PM1 電源管理

1. 多段階電源スイッチ設計

Stamp-S3Bat 内部には M5PM1 が集積されており、ハードウェア回路と組み合わせることで多段階電源スイッチを実現しています。各段階の電源スイッチは、対応する周辺機器およびインターフェースへの電力供給を制御します。ユーザーは動作要件に応じて、異なる段階の電源を有効に切り替え、未使用の周辺機器部分の電源をオフにすることで、装置全体の低消費電力化を実現できます。

M5PM1 ドライバライブラリ

M5PM1 ドライバライブラリを使用することで、低消費電力状態からの復帰および周辺機器への電力供給の切り替えに用いる M5PM1 のピン機能を非常に簡単に設定できます。

注意事項
Stamp-S3Bat の多段階電源スイッチ設計は直列構造ではありません。L1~L3B スイッチへの電源入力はいずれも L1(SYS_VBUS)段階の電源から供給されており、前段の電源から供給されているわけではありません。そのため、異なる段階の電源スイッチを個別に制御することが可能です。上記の段階分けは周辺機器への電力供給と消費電力に基づいて区分されています。

M5PM1 の起動通電後、L1、L2、L3A は自動的にオンになります(既定でDCDC3V3_EN_PPCHG_EN_PPがオン)。この時点で装置上の ESP32-S3 は通電し初期化を完了しますが、EXT_5V_OUT の出力は無効であり、RGB LED への電力供給もオフの状態です。

L1

この電源段階では、電池から M5PM1 への電力供給が維持されます。電池残量がなくならない限り、この段階の電源は保持され、M5PM1 は基本的なボタン操作による電源オン/オフをサポートします。

L2 / L3A

この電源段階では、ESP32-S3 メインコントローラーへの電力供給が有効になります。ESP32-S3 がスリープ状態の場合は電源が L2 段階となり、ESP32-S3 が動作状態の場合は電源が L3A 段階となります。

ESP32-S3 は M5PM1 を制御してスリープ状態に移行させることで、自身への電力供給を遮断(L2→L1/L0)することができます。

L3B

この電源段階では、M5PM1 を介して EXT_5V_OUT 出力インターフェースおよび RGB LED への電力供給をさらに有効にすることができます。以下の API によりスイッチ制御を実現できます。

  • EXT_5V_OUT出力をオンにする:PY_G1(5VOUT_EN)により制御します。
pm1.gpioSetFunc(M5PM1_GPIO_NUM_1, M5PM1_GPIO_FUNC_GPIO);
pm1.gpioSetMode(M5PM1_GPIO_NUM_1, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_1, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_1, true);
  • RGB LEDへの電力供給をオンにする:PY_LED_EN により制御します。
pm1.setLedEnLevel(true)

2. M5PM1 スリープ

手動スリープ

M5PM1 はプログラムにより手動でスリープ状態に移行させ、装置全体の消費電力を低減することができます。既定の状態では、スリープを設定すると電源は L1 段階に戻ります。このとき M5PM1 のみに電力が供給され、その他の復帰要因がない場合は、PWRボタンを 1 回押して復帰させる必要があります。

スリープ前に外部復帰 IO またはその他の復帰要因(タイマーなど)を設定しておくと、装置がスリープ状態に移行した後、復帰要因によるトリガーで M5PM1 を復帰させ、ESP32-S3 への電力供給を再開することができます。

pm1.shutdown();

IO 復帰

Stamp‑S3Bat で本サンプルプログラムを実行すると、起動後に M5PM1 の G4(WAKE)が復帰 IO として設定されます。シャットダウン後、WAKE ピンをローレベルにして立ち下がりエッジを発生させることで、装置を復帰させることができます。

#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>

M5PM1 pm1;

static const uint8_t PIN_SDA = 48;
static const uint8_t PIN_SCL = 47;

void setup()
{
    Serial.begin(115200);
    delay(300);

    Wire.end();
    Wire.begin(PIN_SDA, PIN_SCL, 100000U);

    // Initialize PM1
    m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_100K);

    if (err != M5PM1_OK) {
        Serial.printf("[PM1][E] PM1 initialization failed: %d\r\n", err);
        while (true) {
            delay(1000);
        }
    }
    Serial.printf("[PM1][I] PM1 initialization successful\r\n");

    pm1.irqClearGpioAll();
    pm1.irqClearSysAll();
    pm1.irqClearBtnAll();

    pm1.irqSetGpioMaskAll(M5PM1_IRQ_MASK_ENABLE);
    pm1.irqSetSysMaskAll(M5PM1_IRQ_MASK_ENABLE);
    pm1.irqSetBtnMaskAll(M5PM1_IRQ_MASK_ENABLE);

    pm1.irqSetGpioMask(M5PM1_IRQ_GPIO4, M5PM1_IRQ_MASK_DISABLE);
    pm1.gpioSetMode(M5PM1_GPIO_NUM_4, M5PM1_GPIO_MODE_INPUT);
    pm1.gpioSetPull(M5PM1_GPIO_NUM_4, M5PM1_GPIO_PULL_UP);

    // Configure G4 as IO wake source: low-level trigger (falling edge)
    m5pm1_err_t err_wake_en = pm1.gpioSetWakeEnable(M5PM1_GPIO_NUM_4, true);
    m5pm1_err_t err_wake_ed = pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_4, M5PM1_GPIO_WAKE_FALLING);
    if (err_wake_en != M5PM1_OK || err_wake_ed != M5PM1_OK) {
        Serial.printf("[PM1][E] GPIO wake config failed, en:%d edge:%d\r\n", err_wake_en, err_wake_ed);
        while (true) {
            delay(1000);
        }
    }
    Serial.printf("[PM1][I] Shutdown now, wait GPIO4 low level to wake\r\n");
    delay(1000);
    m5pm1_err_t err_shutdown = pm1.shutdown();
    if (err_shutdown != M5PM1_OK) {
        Serial.printf("[PM1][E] shutdown failed: %d\r\n", err_shutdown);
    }
}

void loop()
{
}

スリープ時状態保持

特殊な使用シナリオにおいて、M5PM1 をスリープ状態に移行させて消費電力を低減しつつ、チップの GPIO ピンの現在の状態を維持することが求められます。本機能を実現するには、M5PM1 がスリープ状態に移行する前に対象ピンの状態設定を完了し、GPIO 状態保持機構を有効にする必要があります。

  • M5PM1 をスリープ状態に移行させ、かつM5PM1_GPIO_NUM_1(EXT_5V_OUT)の電源出力を維持します。
pm1.gpioSetMode(M5PM1_GPIO_NUM_1, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_1, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_1, true);
pm1.gpioSetPowerHold(M5PM1_GPIO_NUM_1, true);

Serial.println("[DEMO] GPIO1 HIGH + HOLD");
delay(1000);
Serial.println("[DEMO] Shutdown now");
delay(300);
pm1.shutdown();
  • M5PM1 をスリープ状態に移行させ、かつM5PM1_GPIO_NUM_4(WAKE)のハイレベル出力を維持します。
pm1.gpioSetMode(M5PM1_GPIO_NUM_4, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_4, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_4, true);
pm1.gpioSetPowerHold(M5PM1_GPIO_NUM_4, true);

Serial.println("[DEMO] GPIO4 HIGH + HOLD");
delay(1000);
Serial.println("[DEMO] Shutdown now");
delay(300);
pm1.shutdown();

I2C アイドルスリープ

M5PM1 は、I2C 通信がアイドル状態のときに自動的にスリープ状態に移行するよう設定することで、装置全体の消費電力を低減する機能をサポートします。スリープ状態に移行した後、ESP32-S3 と M5PM1 の最初の通信は M5PM1 の復帰に使用されるため通信に失敗し、有効な通信は復帰後の次の通信から行われます。

m5pm1_err_t setI2cSleepTime(uint8_t seconds);

3. M5PM1 Timer タイマー

M5PM1 はタイマー機能の設定をサポートしており、カウントダウン終了後に電源オン、電源オフ、リセットなどの対応する動作を実行することができます。

m5pm1_err_t timerSet(uint32_t seconds, m5pm1_tim_action_t action);
typedef enum {
    M5PM1_TIM_ACTION_STOP = 0b000,     // 停止、動作なし
                                       // Stop, no action
    M5PM1_TIM_ACTION_FLAG = 0b001,     // フラグ設定のみ
                                       // Set flag only
    M5PM1_TIM_ACTION_REBOOT = 0b010,   // システムリセット
                                       // System reboot
    M5PM1_TIM_ACTION_POWERON = 0b011,  // 電源オン
                                       // Power on
    M5PM1_TIM_ACTION_POWEROFF = 0b100  // 電源オフ
                                       // Power off
} m5pm1_tim_action_t;

タイマーによる電源オン/オフサンプル

装置に電源を投入して本プログラムを実行すると、10 秒後に自動的に電源がオフになります。

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#include <M5Unified.h>
#include <M5PM1.h>
#include <Wire.h>

M5PM1 pm1;

void setup(void)
{
    M5.begin();
    Serial.begin(115200);

    auto sda = M5.getPin(m5::pin_name_t::in_i2c_sda);
    auto scl = M5.getPin(m5::pin_name_t::in_i2c_scl);

    Wire.end();
    Wire.begin(sda, scl, 100000U);

    if (pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, sda, scl, M5PM1_I2C_FREQ_100K) != M5PM1_OK) {
        Serial.println("PM1 init failed");
        return;
    }

    Serial.println("Set timer: power off after 10s");
    pm1.timerSet(10, M5PM1_TIM_ACTION_POWEROFF);
}

void loop(void) {

}

装置で本プログラムを実行すると直ちに電源がオフになり、10 秒後に自動的に再起動します。

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#include <M5Unified.h>
#include <M5PM1.h>
#include <Wire.h>

M5PM1 pm1;

void setup(void)
{
    M5.begin();
    Serial.begin(115200);

    auto sda = M5.getPin(m5::pin_name_t::in_i2c_sda);
    auto scl = M5.getPin(m5::pin_name_t::in_i2c_scl);

    Wire.end();
    Wire.begin(sda, scl, 100000U);

    if (pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, sda, scl, M5PM1_I2C_FREQ_100K) != M5PM1_OK) {
        Serial.println("PM1 init failed");
        return;
    }

    Serial.println("Set timer: power on after 10s");
    pm1.timerSet(10, M5PM1_TIM_ACTION_POWERON);
    pm1.shutdown();  // Must enter shutdown first, then PM1 can wake it up by timer.
}

void loop(void) {

}

4. RGB LED 制御

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>

M5PM1 pm1;

#define PIN_SCL 47
#define PIN_SDA 48

static const uint8_t LED_COUNT  = 1;
static const uint8_t BRIGHTNESS = 64;

static const m5pm1_rgb_t COLOR_GREEN = {0, BRIGHTNESS, 0};

void setup()
{
    Serial.begin(115200);
    delay(2000);

    Wire.end();
    Wire.begin(PIN_SDA, PIN_SCL, 100000U);

    // Initialize PM1
    m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_400K);

    if (err != M5PM1_OK) {
        Serial.printf("[PM1][E] PM1 initialization failed: %d\r\n", err);
        while (true) {
            delay(1000);
        }
    }
    Serial.printf("[PM1][I] PM1 initialization successful\r\n");

    // Configure PM1 GPIO0 for LED output path.
    pm1.gpioSetFunc(M5PM1_GPIO_NUM_0, M5PM1_GPIO_FUNC_OTHER);
    pm1.gpioSetDrive(M5PM1_GPIO_NUM_0, M5PM1_GPIO_DRIVE_PUSHPULL);
    pm1.gpioSetOutput(M5PM1_GPIO_NUM_0, true);
    pm1.pinMode(M5PM1_GPIO_NUM_0, M5PM1_OTHER);

    // Enable LED output and set LED count.
    m5pm1_err_t err1 = pm1.setLedEnLevel(true);
    if (err1 != M5PM1_OK) {
        Serial.printf("[PM1][E] Failed to enable LED output: %d\r\n", err1);
    }

    m5pm1_err_t err2 = pm1.setLedCount(LED_COUNT);
    if (err2 != M5PM1_OK) {
        Serial.printf("[PM1][E] Failed to set LED count: %d\r\n", err2);
    }

    // Keep LED solid green.
    m5pm1_err_t err3 = pm1.setLedColor(0, COLOR_GREEN);
    m5pm1_err_t err4 = pm1.refreshLeds();
    if (err3 != M5PM1_OK || err4 != M5PM1_OK) {
        Serial.printf("[PM1][E] Failed to set GREEN LED, color:%d refresh:%d\r\n", err3, err4);
    }
}

void loop()
{
    delay(1000);
}

5. 充電制御

PY_G3_CHG_PROGにより、充電 IC による電池への充電電流を切り替えます(ローレベル:650mA/フローティング:200mA)。

マクロ定義CHARGE_CURRENT_650MA_ENABLEDを 1 に設定すると、650mA の大電流充電モードが有効になります。

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>

M5PM1 pm1;

#define PIN_SCL 47
#define PIN_SDA 48
#define CHARGE_CURRENT_650MA_ENABLED 1
static const uint8_t LED_COUNT = 1;
static const uint8_t LED_BRIGHTNESS = 64;

static const m5pm1_gpio_num_t PY_G3_CHG_PROG = M5PM1_GPIO_NUM_3;

#if CHARGE_CURRENT_650MA_ENABLED
static const m5pm1_rgb_t LED_COLOR = {0, LED_BRIGHTNESS, 0};
#else
static const m5pm1_rgb_t LED_COLOR = {0, 0, LED_BRIGHTNESS};
#endif

static bool applyChargeCurrentConfig()
{
#if CHARGE_CURRENT_650MA_ENABLED
    return pm1.gpioSetMode(PY_G3_CHG_PROG, M5PM1_GPIO_MODE_OUTPUT) == M5PM1_OK &&
           pm1.gpioSetPull(PY_G3_CHG_PROG, M5PM1_GPIO_PULL_NONE) == M5PM1_OK &&
           pm1.gpioSetOutput(PY_G3_CHG_PROG, false) == M5PM1_OK;
#else
    return pm1.gpioSetMode(PY_G3_CHG_PROG, M5PM1_GPIO_MODE_INPUT) == M5PM1_OK &&
           pm1.gpioSetPull(PY_G3_CHG_PROG, M5PM1_GPIO_PULL_NONE) == M5PM1_OK;
#endif
}

static bool applyRgbLedConfig()
{
    bool ok = pm1.gpioSetFunc(M5PM1_GPIO_NUM_0, M5PM1_GPIO_FUNC_OTHER) == M5PM1_OK &&
              pm1.gpioSetDrive(M5PM1_GPIO_NUM_0, M5PM1_GPIO_DRIVE_PUSHPULL) == M5PM1_OK &&
              pm1.gpioSetOutput(M5PM1_GPIO_NUM_0, true) == M5PM1_OK;
    pm1.pinMode(M5PM1_GPIO_NUM_0, M5PM1_OTHER);
    return ok && pm1.setLedEnLevel(true) == M5PM1_OK && pm1.setLedCount(LED_COUNT) == M5PM1_OK &&
           pm1.setLedColor(0, LED_COLOR) == M5PM1_OK && pm1.refreshLeds() == M5PM1_OK;
}

void setup()
{
    delay(200);

    Wire.end();
    Wire.begin(PIN_SDA, PIN_SCL, 100000U);

    m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_400K);

    pm1.setChargeEnable(true);

    if (err != M5PM1_OK) {
        while (true) {
            delay(1000);
        }
    }

    if (!applyChargeCurrentConfig()) {
        while (true) {
            delay(1000);
        }
    }

    if (!applyRgbLedConfig()) {
        while (true) {
            delay(1000);
        }
    }
}

void loop()
{
    delay(1000);
}

6. 電源状態

Stamp-S3Bat の現在の電源状態を読み取ります。

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#include <Arduino.h>
#include <M5PM1.h>
#include <Wire.h>

M5PM1 pm1;

static const uint8_t PIN_SCL            = 47;
static const uint8_t PIN_SDA            = 48;
static const uint32_t PRINT_INTERVAL_MS = 2000;

static void printVoltages()
{
    uint16_t mv = 0;

    if (pm1.readVref(&mv) == M5PM1_OK) {
        Serial.printf("[PM1][I] Vref: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
    }
    if (pm1.readVbat(&mv) == M5PM1_OK) {
        Serial.printf("[PM1][I] VBAT: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
    }
    if (pm1.readVin(&mv) == M5PM1_OK) {
        Serial.printf("[PM1][I] VIN: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
    }
    if (pm1.read5VInOut(&mv) == M5PM1_OK) {
        Serial.printf("[PM1][I] 5V IN/OUT: %u mV (%.3f V)\r\n", mv, mv / 1000.0f);
    }
}

void setup()
{
    Serial.begin(115200);
    delay(300);
    Serial.println("[DEMO] Boot");

    Wire.end();
    Wire.begin(PIN_SDA, PIN_SCL, 100000U);
    if (pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PIN_SDA, PIN_SCL, M5PM1_I2C_FREQ_400K) != M5PM1_OK) {
        Serial.println("[DEMO] PM1 init failed");
        return;
    }
    Serial.println("[DEMO] PM1 init ok");
}

void loop()
{
    static uint32_t lastMs = 0;
    if (millis() - lastMs >= PRINT_INTERVAL_MS) {
        lastMs = millis();
        printVoltages();
    }
}
On This Page