Arduino入門

2. デバイス&サンプル

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

アクセサリー

6. アプリケーション

ToughC5 M5PM1 & M5IOE1 電源管理

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

ToughC5 は M5PM1 と M5IOE1 を内蔵し、ハードウェア回路と組み合わせて多段階の電源スイッチを実現しています。各段階は、コントローラーの動作状態と周辺機器・インターフェースへの給電状態に対応します。動作要件に応じてコントローラーの状態や電源イネーブルを切り替え、使用していない周辺機器を停止してシステム全体の消費電力を抑えられます。

M5PM1 / M5IOE1 ドライバーライブラリ

M5PM1 / M5IOE1 ドライバーライブラリを使用すると、低消費電力時のウェイクアップや周辺機器の電源制御に使う各ピンの機能を簡単に設定できます。

注意事項
ToughC5 の電源段階は、給電範囲とコントローラーの動作状態によって分類されており、各段階の電源が順に直列接続されているわけではありません。L0 は RTC のコイン電池による給電、L1 は本体のバッテリー給電と M5PM1 による電源管理、L2 / L3A はコントローラーと関連する周辺機器への給電に対応します。L2 と L3A は、ESP32-C5 がディープスリープ中か動作中かによって区別されます。L3B の周辺機器はそれぞれの制御ピンで個別に有効化でき、同時に有効化する必要はありません。

L0

この電源段階では、RTC はコイン電池から給電され、時計の動作を維持します。コイン電池の残量がある限り、この段階の電源は維持されます。

L1

この電源段階では、本体のバッテリーが接続され、M5PM1 への給電が維持されてシステム全体の電源を管理します。後続の段階に属するコントローラーと周辺機器は、必要に応じて有効化できます。

L2 / L3A

この段階では ESP32-C5、M5IOE1 拡張チップ、GROVE 入力 / 出力インターフェース、およびタッチスクリーンへの給電が有効になり、M5PM1 と RTC への給電も維持されます。M5PM1 の DC-DC 出力 3V3_L2 から ESP32-C5 と M5IOE1 に給電し、LDO 出力 3V3_L2_LDO からタッチスクリーンに給電します。以下の API でこの 2 系統の電源を制御できます。

pm1.setDcdcEnable(true); // L2 ON
pm1.setDcdcEnable(false); // L2 OFF
pm1.setLdoEnable(true); // L2 LDO ON
pm1.setLdoEnable(false); // L2 LDO OFF

ESP32-C5 がディープスリープ中の場合、電源は L2 段階にあり、ESP32-C5、M5IOE1、タッチスクリーンへの給電は維持されますが、M5BUS 3V3 は有効になりません。ESP32-C5 が動作中の場合は L3A 段階となり、M5BUS 3V3 が有効になります。両者は同じ給電段階に属し、コントローラーの動作状態によって区別されます。ESP32-C5 は M5PM1 をスリープに移行させることで、自身への給電を停止できます (L2 -> L1/L0)。

L3B

L3B 段階では LCD、ブザー、Grove などの周辺機器への給電や関連設定を制御します。M5IOE1 I/O 拡張チップによって制御され、この段階に属する一部の周辺機器を個別に操作できます。

M5IOE1 PYG1 PYG3 PYG4_ADC2 PYG5_ADC3 PYG6 PYG10_PWM4 PYG14
Charger PYB_CHG_PROG PYB_CHG_STAT
LCD PYB_LCD_RST PYB_LCD_EN
LCD BL PYB_BL_EN
microSD PYB_TF_EN PYB_TF_DET
  • PYB_CHG_PROG: 充電電流設定の制御
  • PYB_CHG_STAT: 充電状態入力。Low は充電中を示します
  • PYB_LCD_RST: LCD リセット
  • PYB_LCD_EN: LCD 電源イネーブル
  • PYB_BL_EN: LCD バックライトイネーブル
  • PYB_TF_EN: microSD 電源イネーブル
  • PYB_TF_DET: microSD 検出。Low はカード検出を示します

周辺機器への給電は、以下の M5IOE1 API で個別に制御できます。

ioe1.pinMode(M5IOE1_PIN_6, OUTPUT);
ioe1.digitalWrite(M5IOE1_PIN_6, HIGH);

2. M5PM1 スリープ

手動スリープ

プログラムから M5PM1 を手動でスリープ状態にして、システム全体の消費電力を抑えられます。デフォルトでは、スリープを設定すると電源は L0 段階に戻り、M5PM1 のみが給電状態を維持します。

pm1.shutdown();

タッチウェイクアップなどの低消費電力回路を維持する必要がある場合は、M5PM1 をスリープモードにする前に、必要な電源保持状態とウェイクアップソースを設定してください。ToughC5 には物理ボタンがないため、スリープ後はタッチスクリーンで復帰します。設定方法は以下の M5PM1 ウェイクアップの各節を参照してください。

I2C アイドル時のスリープ

M5PM1 は I2C 通信がアイドル状態のときに自動でスリープするよう設定でき、システム全体の消費電力を抑えられます。スリープに入った後、ESP32-C5 からの最初の通信は M5PM1 の復帰に使われるため、失敗する場合があります。復帰後の次の通信から正常に行われます。

m5pm1_err_t setI2cSleepTime(uint8_t seconds);

3. M5PM1 タイマー

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 秒後に再度電源が入り、その直後に電源がオフになります。右側に触れると 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 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
#include <M5Unified.h>
#include <M5PM1.h>

M5PM1 pm1;

void setup(void)
{
    auto cfg = M5.config();
    cfg.clear_display = false;
    M5.begin(cfg);
    Serial.begin(115200);
    M5.Display.setRotation(1);
    M5.Display.setTextDatum(middle_center);
    M5.Display.setTextColor(TFT_BLACK, TFT_WHITE);
    M5.Display.setFont(&fonts::FreeSansBold12pt7b);
    M5.Display.clear(TFT_WHITE);
    M5.Display.drawString("Timer Power Test", M5.Display.width() / 2, 30);
    M5.Display.setFont(&fonts::FreeSansBold9pt7b);
    M5.Display.drawString("Touch left: Power ON after 10s", M5.Display.width() / 2, 90);
    M5.Display.drawString("Touch right: Power OFF after 10s", M5.Display.width() / 2, 140);

    const m5pm1_err_t err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_400K);
    if (err != M5PM1_OK) {
        Serial.printf("M5PM1 init failed: %d\n", err);
        while (true) {
            delay(1000);
        }
    }
    Serial.println("M5PM1 initialized");
}

void loop(void)
{
    M5.update();
    if (!M5.Touch.getCount()) {
        return;
    }

    const auto touch = M5.Touch.getDetail(0);
    if (!touch.wasClicked()) {
        return;
    }

    M5.Display.clear(TFT_WHITE);
    M5.Display.setTextDatum(middle_center);
    M5.Display.setTextColor(TFT_BLACK, TFT_WHITE);
    M5.Display.setFont(&fonts::FreeSansBold12pt7b);
    if (touch.x < M5.Display.width() / 2) {
        M5.Display.drawString("Power ON after 10s", M5.Display.width() / 2, 120);
        Serial.println("Touch left: power on timer set for 10 seconds");
        const m5pm1_err_t timerErr = pm1.timerSet(10, M5PM1_TIM_ACTION_POWERON);
        Serial.printf("Power on timer result: %d\n", timerErr);
        delay(200);
        Serial.println("Requesting shutdown");
        pm1.shutdown();
    } else {
        M5.Display.drawString("Power OFF after 10s", M5.Display.width() / 2, 120);
        Serial.println("Touch right: power off timer set for 10 seconds");
        const m5pm1_err_t timerErr = pm1.timerSet(10, M5PM1_TIM_ACTION_POWEROFF);
        Serial.printf("Power off timer result: %d\n", timerErr);
    }

    delay(20);
}

4. RTC ウェイクアップ

RTC による M5PM1 のウェイクアップ

電源が L1 モードに切り替わると、ToughC5 の RTC と M5PM1 は給電状態を維持します。RTC のタイマーウェイクアップを設定すると、M5PM1 はスリープに入り、RTC の動作に必要な L1 電源を維持します。

RTC タイマーで復帰を設定できます。RTC の PM_RTC_INT 割り込みは M5PM1 GPIO3 に接続されており、M5PM1 を復帰させて ESP32-C5 に再給電します。

M5PM1 が復帰すると、L1、L2、L3A の電源投入シーケンスを再実行し、ESP32-C5 は初期化をやり直します。

例: 起動後に画面を 1 回タッチして RTC タイマーウェイクアップを設定すると、M5PM1 はスリープに入ります。5 秒後に RTC が復帰を起動し、M5PM1 が給電を再開して ESP32-C5 に再給電します。

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
#include <M5Unified.h>
#include <M5PM1.h>

M5PM1 pm1;

void setup(void)
{
    M5.begin();
    Serial.begin(115200);
    // Initialize PM1
    m5pm1_err_t err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_400K);

    if (err == M5PM1_OK) {
        Serial.println("PM1 initialization successful");
        pm1.gpioSetWakeEnable(M5PM1_GPIO_NUM_3, true);
        pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_3, M5PM1_GPIO_WAKE_FALLING);  // Falling edge
    } else {
        Serial.printf("PM1 initialization failed, error code: %d\n", err);
    }

    M5.Display.setTextDatum(middle_center);
    M5.Display.setTextColor(TFT_BLACK, TFT_WHITE);
    M5.Display.setFont(&fonts::FreeSansBold12pt7b);
    M5.Display.clear(TFT_WHITE);
    M5.Display.drawString("Touch to set RTC wakeup", M5.Display.width() / 2, 80);
    M5.Display.setFont(&fonts::FreeSansBold9pt7b);
    M5.Display.drawString("Wake up after 5 seconds", M5.Display.width() / 2, 130);
}

void loop(void)
{
    M5.update();
    if (M5.Touch.getCount() && M5.Touch.getDetail(0).wasClicked()) {
        M5.Rtc.clearIRQ();
        if (!M5.Rtc.setTimerIRQ(5000)) {
            M5.Display.clear(TFT_WHITE);
            M5.Display.drawString("RTC timer failed", M5.Display.width() / 2, 120);
            return;
        }
        Serial.println("RTC IRQ enabled successfully");
        M5.Display.clear(TFT_WHITE);
        M5.Display.drawString("5s later wakeup", M5.Display.width() / 2, 120);
        pm1.setLdoEnable(true);
        pm1.ldoSetPowerHold(true);
        delay(50);
        pm1.shutdown();
    }
}

画面に触れると、プログラムは RTC 割り込みをクリアして 5 秒のタイマーを設定します。M5PM1 の L1 電源を維持しながら、システム全体の電源をオフにします。タイマーが終了すると、RTC が PM_RTC_INT を介して M5PM1 GPIO3 を起動します。M5PM1 が給電を再開し、ESP32-C5 は初期化をやり直します。

RTC による ESP32-C5 のウェイクアップ

RTC による ESP32-C5 のウェイクアップ設定は、RTC による M5PM1 のウェイクアップと似ています。違いは、RTC 割り込みが直接再給電に使われず、RTC IRQ -> M5PM1 GPIO3 -> M5PM1 GPIO1 IRQout -> ESP32-C5 G4 の信号経路を使うことです。手順は次の通りです。

  1. M5PM1 GPIO3 を入力モードに設定します。このピンは RTC の PM_RTC_INT 割り込み信号に接続されています。
  2. M5PM1 GPIO1 を IRQout 出力信号ピンに設定します。GPIO3 (RTC の割り込み信号) の状態が変化すると、GPIO1 IRQout が割り込み信号を出力します。
  3. ESP32-C5 をスリープに設定し、ウェイクアップピンを G4 に設定します。
  4. RTC タイマーが終了すると、RTC ウェイクアップ信号が M5PM1 GPIO1 IRQout を起動し、ESP32-C5 を復帰させます。

例: 起動後に画面に触れて 5 秒の RTC タイマーを設定します。タイマーが終了すると GPIO 割り込みハンドラーが起動します。コメントアウトされているディープスリープ部分を有効にすると、RTC タイマー終了後に ESP32-C5 が復帰し、setup() から再実行されます。

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 77 78 79 80 81 82 83
#include <M5Unified.h>
#include <M5PM1.h>
#include "driver/rtc_io.h"

M5PM1 pm1;

void setup(void)
{
    M5.begin();
    Serial.begin(115200);
    // Initialize PM1
    m5pm1_err_t err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_400K);

    if (err == M5PM1_OK) {
        Serial.println("PM1 initialization successful");
        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_GPIO3, M5PM1_IRQ_MASK_DISABLE);
        pm1.gpioSetMode(M5PM1_GPIO_NUM_3, M5PM1_GPIO_MODE_INPUT);
        pm1.gpioSetPull(M5PM1_GPIO_NUM_3, M5PM1_GPIO_PULL_UP);

        pm1.gpioSetMode(M5PM1_GPIO_NUM_1, M5PM1_GPIO_MODE_OUTPUT);
        pm1.gpioSetDrive(M5PM1_GPIO_NUM_1, M5PM1_GPIO_DRIVE_PUSHPULL);
        pm1.gpioSetFunc(M5PM1_GPIO_NUM_1, M5PM1_GPIO_FUNC_IRQ);
    } else {
        Serial.printf("PM1 initialization failed, error code: %d\n", err);
    }

    M5.Display.setTextDatum(middle_center);
    M5.Display.setTextColor(TFT_BLACK, TFT_WHITE);
    M5.Display.setFont(&fonts::FreeSansBold12pt7b);
    M5.Display.clear(TFT_WHITE);
    M5.Display.drawString("Touch to set RTC IRQ", M5.Display.width() / 2, 80);
    M5.Rtc.clearIRQ();
}

volatile bool pm1IrqTriggered = false;

void ARDUINO_ISR_ATTR pm1_irq_handler() {
    pm1IrqTriggered = true;
}

void loop(void)
{
    M5.update();

    if (pm1IrqTriggered) {
        pm1IrqTriggered = false;
        M5.Rtc.clearIRQ();
        M5.Rtc.setTimerIRQ(0);
        pm1.irqClearGpioAll();
        pm1.irqClearSysAll();
        pm1.irqClearBtnAll();
        Serial.println("PM1 IRQ triggered");
        M5.Display.drawString("PM1 IRQ triggered", M5.Display.width() / 2, 130);
    }

    if (M5.Touch.getCount() && M5.Touch.getDetail(0).wasClicked()) {
        if (!M5.Rtc.setTimerIRQ(5000)) {
            M5.Display.clear(TFT_WHITE);
            M5.Display.drawString("RTC timer failed", M5.Display.width() / 2, 100);
            Serial.println("Failed to enable RTC IRQ");
            return;
        }
        Serial.println("RTC IRQ enabled successfully");
        M5.Display.clear(TFT_WHITE);
        M5.Display.drawString("Wait for RTC IRQ", M5.Display.width() / 2, 80);
        // Choose either of the two pieces of code below.
        pinMode(GPIO_NUM_4, INPUT_PULLUP);
        attachInterrupt(GPIO_NUM_4, pm1_irq_handler, FALLING);

        // esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, 0);  // 0 = Low
        // rtc_gpio_pullup_en(GPIO_NUM_4);
        // Serial.println("Going to sleep now");
        // esp_deep_sleep_start();
    }
}

RTC 割り込みが発生すると、M5PM1 GPIO1 が IRQ 信号を出力します。ESP32-C5 G4 が Low を検出するとディープスリープから復帰します。復帰後に ESP32-C5 はリセットされ、プログラムは setup() から実行されます。

5. TP ウェイクアップ

TP による M5PM1 のウェイクアップ

電源が L2 / L3A モードに切り替わると、ToughC5 のタッチ回路と M5PM1 は給電状態を維持します。タッチウェイクアップを設定すると、M5PM1 はスリープに入り、タッチ検出を維持するため 3V3_L2_LDO のタッチ電源を維持します。

画面に触れるとウェイクアップ信号が発生します。タッチ割り込み PM_TP_INT は M5PM1 GPIO0 に接続されており、M5PM1 を復帰させて ESP32-C5 に再給電します。

M5PM1 が復帰すると、L1、L2、L3A の電源投入シーケンスを再実行し、ESP32-C5 は初期化をやり直します。

例: 起動後に画面を 1 回タッチして、タッチウェイクアップと M5PM1 LDO 電源の保持を設定すると、M5PM1 はスリープに入ります。もう一度画面に触れると TP ウェイクアップが起動し、M5PM1 が給電を再開して ESP32-C5 に再給電します。

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
#include <M5PM1.h>
#include <M5Unified.h>

M5PM1 pm1;

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

    if (pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_400K) != M5PM1_OK) {
        Serial.println("PM1 initialization failed");
        while (true) {
            delay(1000);
        }
    }

    Serial.println("PM1 initialization successful");
    M5.Display.setTextDatum(middle_center);
    M5.Display.setTextColor(TFT_BLACK, TFT_WHITE);
    M5.Display.setFont(&fonts::FreeSansBold12pt7b);
    M5.Display.clear(TFT_WHITE);
    M5.Display.drawString("Touch to sleep", M5.Display.width() / 2, 120);

    pm1.gpioSetFunc(M5PM1_GPIO_NUM_0, M5PM1_GPIO_FUNC_WAKE);
    pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_0, M5PM1_GPIO_WAKE_FALLING);
    pm1.gpioSetWakeEnable(M5PM1_GPIO_NUM_0, true);
    pm1.setLdoEnable(true);
    pm1.ldoSetPowerHold(true);
}

void loop(void)
{
    M5.update();
    if (M5.Touch.getCount() && M5.Touch.getDetail(0).wasClicked()) {
        M5.Display.clear(TFT_WHITE);
        M5.Display.drawString("Touch wakeup enabled", M5.Display.width() / 2, 120);
        delay(200);
        pm1.shutdown();
    }
}

起動時に M5PM1 GPIO0 のウェイクアップ機能を設定し、3V3_L2_LDO のタッチ電源を維持します。画面に触れるとシステム全体の電源がオフになります。もう一度画面に触れると、PM_TP_INT が M5PM1 GPIO0 を介してウェイクアップを起動し、M5PM1 が給電を再開して ESP32-C5 は初期化をやり直します。このサンプルは M5PM1 のウェイクアップ設定のみを示し、タッチ座標は読み取りません。

TP による ESP32-C5 のウェイクアップ

TP による ESP32-C5 のウェイクアップ設定は、RTC による ESP32-C5 のウェイクアップと似ています。違いは、ウェイクアップソースがタッチ割り込み PM_TP_INT であり、TP IRQ -> M5PM1 GPIO0 -> M5PM1 GPIO1 IRQout -> ESP32-C5 G4 の信号経路を使うことです。

例: 起動後に画面を 1 回タッチして TP 割り込みを設定し、G4 割り込みハンドラーを登録します。もう一度画面に触れると GPIO 割り込みハンドラーが起動します。コメントアウトされているディープスリープ部分を有効にすると、画面へのタッチで ESP32-C5 が復帰し、setup() から再実行されます。

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 77
#include <M5Unified.h>
#include <M5PM1.h>
#include "driver/rtc_io.h"

M5PM1 pm1;

void setup(void)
{
    M5.begin();
    Serial.begin(115200);
    // Initialize PM1
    m5pm1_err_t err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_400K);

    if (err == M5PM1_OK) {
        Serial.println("PM1 initialization successful");
        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_GPIO0, M5PM1_IRQ_MASK_DISABLE);
        pm1.gpioSetMode(M5PM1_GPIO_NUM_0, M5PM1_GPIO_MODE_INPUT);
        pm1.gpioSetPull(M5PM1_GPIO_NUM_0, M5PM1_GPIO_PULL_UP);

        pm1.gpioSetMode(M5PM1_GPIO_NUM_1, M5PM1_GPIO_MODE_OUTPUT);
        pm1.gpioSetDrive(M5PM1_GPIO_NUM_1, M5PM1_GPIO_DRIVE_PUSHPULL);
        pm1.gpioSetFunc(M5PM1_GPIO_NUM_1, M5PM1_GPIO_FUNC_IRQ);
    } else {
        Serial.printf("PM1 initialization failed, error code: %d\n", err);
    }

    M5.Display.setTextDatum(middle_center);
    M5.Display.setTextColor(TFT_BLACK, TFT_WHITE);
    M5.Display.setFont(&fonts::FreeSansBold12pt7b);
    M5.Display.clear(TFT_WHITE);
    M5.Display.drawString("Touch to set TP IRQ", M5.Display.width() / 2, 80);
}

volatile bool pm1IrqTriggered = false;

void ARDUINO_ISR_ATTR pm1_irq_handler() {
    pm1IrqTriggered = true;
}

void loop(void)
{
    M5.update();

    if (pm1IrqTriggered) {
        pm1IrqTriggered = false;
        pm1.irqClearGpioAll();
        pm1.irqClearSysAll();
        pm1.irqClearBtnAll();
        Serial.println("PM1 IRQ triggered");
        M5.Display.drawString("PM1 IRQ triggered", M5.Display.width() / 2, 130);
    }

    if (M5.Touch.getCount() && M5.Touch.getDetail(0).wasClicked()) {
        pm1.irqClearGpioAll();
        pm1.irqClearSysAll();
        pm1.irqClearBtnAll();
        Serial.println("TP IRQ enabled successfully");
        M5.Display.clear(TFT_WHITE);
        M5.Display.drawString("Wait for TP IRQ", M5.Display.width() / 2, 80);
        // Choose either of the two pieces of code below.
        pinMode(GPIO_NUM_4, INPUT_PULLUP);
        attachInterrupt(GPIO_NUM_4, pm1_irq_handler, FALLING);

        // esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, 0);  // 0 = Low
        // rtc_gpio_pullup_en(GPIO_NUM_4);
        // Serial.println("Going to sleep now");
        // esp_deep_sleep_start();
    }
}

画面に触れると、プログラムは M5PM1 の割り込みフラグをクリアし、ESP32-C5 G4 に立ち下がりエッジ割り込みを登録します。もう一度画面に触れると、TP 割り込みが M5PM1 GPIO0 から GPIO1 の IRQ 出力に転送され、G4 割り込みが起動します。プログラムは画面とシリアルポートに結果を表示します。

6. M5IOE1 周辺機器の制御

ToughC5 の M5IOE1 は、LCD、バックライト、microSD など、一部の L3B 周辺機器への給電と関連設定を制御します。M5IOE1 API を使うと、これらの周辺機器を個別に制御でき、より柔軟な電源管理が可能です。

ToughC5 の M5IOE1 の主なピンを以下に示します。

M5IOE1 ピン ToughC5 の機能
M5IOE1_PIN_3 充電状態入力
M5IOE1_PIN_4 LCD リセット
M5IOE1_PIN_5 LCD 電源イネーブル
M5IOE1_PIN_6 microSD 電源イネーブル
M5IOE1_PIN_10 LCD バックライトイネーブル
M5IOE1_PIN_14 microSD 検出入力

以下の例では M5IOE1 を直接初期化し、LCD リセット、LCD 電源、バックライト、microSD 電源の制御を順番に示します。また、充電状態と TF カード検出状態を読み取ります。PYB_TF_DET は Low アクティブで、Low が読み取られた場合は microSD カードが検出されたことを示します。

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 77 78
#include <Arduino.h>
#include <Wire.h>
#include <M5IOE1.h>

M5IOE1 ioe1;

#define IOE_CHG_STAT M5IOE1_PIN_3
#define IOE_LCD_RST  M5IOE1_PIN_4
#define IOE_LCD_EN   M5IOE1_PIN_5
#define IOE_TF_EN    M5IOE1_PIN_6
#define IOE_BL_EN    M5IOE1_PIN_10
#define IOE_TF_DET   M5IOE1_PIN_14

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

    // Initialize IOE1.
    const m5ioe1_err_t ioe1_err =
        ioe1.begin(&Wire, M5IOE1_DEFAULT_ADDR_2, LP_SDA, LP_SCL,
                   M5IOE1_I2C_FREQ_100K);
    if (ioe1_err == M5IOE1_OK) {
        Serial.println("IOE1 initialization successful");
    } else {
        Serial.printf("IOE1 initialization failed, error code: %d\n", ioe1_err);
        while (true) delay(1000);
    }

    ioe1.pinMode(IOE_CHG_STAT, INPUT);
    ioe1.pinMode(IOE_LCD_RST, OUTPUT);
    ioe1.pinMode(IOE_LCD_EN, OUTPUT);
    ioe1.pinMode(IOE_TF_EN, OUTPUT);
    ioe1.pinMode(IOE_BL_EN, OUTPUT);
    ioe1.pinMode(IOE_TF_DET, INPUT_PULLUP);

    // Default states: LCD on, backlight on, and microSD power on.
    ioe1.digitalWrite(IOE_LCD_EN, HIGH);
    ioe1.digitalWrite(IOE_TF_EN, HIGH);
    ioe1.digitalWrite(IOE_BL_EN, HIGH);
    ioe1.digitalWrite(IOE_LCD_RST, LOW);
    delay(10);
    ioe1.digitalWrite(IOE_LCD_RST, HIGH);

    Serial.println("IOE1 peripheral control test begin");
}

void loop(void)
{
    // LCD reset.
    Serial.println("LCD reset LOW");
    ioe1.digitalWrite(IOE_LCD_RST, LOW);
    delay(100);
    Serial.println("LCD reset HIGH");
    ioe1.digitalWrite(IOE_LCD_RST, HIGH);
    delay(1000);

    // LCD and backlight power.
    Serial.println("LCD and backlight power OFF");
    ioe1.digitalWrite(IOE_BL_EN, LOW);
    ioe1.digitalWrite(IOE_LCD_EN, LOW);
    delay(1000);
    Serial.println("LCD and backlight power ON");
    ioe1.digitalWrite(IOE_LCD_EN, HIGH);
    ioe1.digitalWrite(IOE_BL_EN, HIGH);
    delay(1000);

    // microSD power.
    Serial.println("microSD power OFF");
    ioe1.digitalWrite(IOE_TF_EN, LOW);
    delay(1000);
    Serial.println("microSD power ON");
    ioe1.digitalWrite(IOE_TF_EN, HIGH);
    delay(1000);

    const int chargeState = ioe1.digitalRead(IOE_CHG_STAT);
    const int tfState = ioe1.digitalRead(IOE_TF_DET);
    Serial.printf("CHG_STAT: %d, TF_DET: %d\n", chargeState, tfState);
}

書き込みが成功すると、プログラムは LCD のリセット、LCD とバックライトへの給電、microSD への給電制御を順番に示し、充電状態と TF カード検出状態をシリアルポートに出力します。

Page Tools
PDF
On This Page