CoreMatrix は Stamp Timer Power2(M5PM1)と M5IOE1 を内蔵し、ハードウェア回路と組み合わせて多段階の電源スイッチを実現しています。各段階は、コントローラーの動作状態と周辺機器・インターフェースへの給電状態に対応します。動作要件に応じてコントローラーの状態や電源イネーブルを切り替え、使用していない周辺機器を停止してシステム全体の消費電力を抑えられます。
M5PM1 / M5IOE1 ドライバーライブラリを使用すると、低消費電力時のウェイクアップや周辺機器の電源制御に使う各ピンの機能を簡単に設定できます。
この電源段階では、バッテリーから M5PM1 への給電が維持されます。バッテリー残量がある限り、この段階の電源は維持され、M5PM1 は基本的なボタンによる電源オン / オフ操作をサポートします。
また、L0 ドメインには M5PM1 の EXT_5V ブースト出力(5V_INOUT)も含まれます。これは M5-Bus に 5V を供給し、変換後に M5BUS_3V3_L0 を生成します。この出力は M5PM1 の EXT_5V_EN で個別に制御され、デフォルトではオフで、M5Unified の初期化時に自動的にオンになります。L1 ~ L3B の段階的なスイッチ制御とは連動しません。
出力をオフにすると、5V_INOUT は外部 5V 入力として使用できます。出力をオンにする場合は、M5-Bus の 5V ピンから逆供給しないでください。
pm1.setBoostEnable(true); // M5-Bus 5V + M5-Bus 3V3 ON
pm1.setBoostEnable(false); // M5-Bus 5V + M5-Bus 3V3 OFF この電源段階では、IMU、KEY1~KEY3 ボタンのプルアップ、および関連ロジックへの給電が有効になります。この段階の電源は M5PM1 の 3V3_L1 を使用し、以下の API でオン / オフを制御できます。
pm1.setLdoEnable(true); // L1 ON
pm1.setLdoEnable(false); // L1 OFF また、BMI270 の INT1 は M5PM1 G4 に、KEY1~KEY3 はそれぞれ M5PM1 G0~G2 に接続されています。IMU またはボタンで M5PM1 をウェイクアップする場合は、L1 への給電を維持したうえで M5PM1 をスリープさせます。
pm1.setLdoEnable(true);
pm1.ldoSetPowerHold(true);
pm1.setLedEnLevel(true);
pm1.shutdown(); この電源段階では、ESP32-C61、M5IOE1、I2C プルアップ、およびコントローラー関連の周辺機器への給電が有効になります。この段階では M5PM1 の DC-DC 出力 3V3_L3A を使用し、以下の API でオン / オフを制御できます。
pm1.setDcdcEnable(true); // L3A ON
pm1.setDcdcEnable(false); // L3A OFF L3B 段階では LED マトリクス、microSD、ブザー、Grove などの周辺機器への給電や関連設定を制御します。M5IOE1 I/O 拡張チップによって制御され、この段階に属する一部の周辺機器を個別に操作できます。
| M5IOE1 | PYG1 | PYG2 | PYG3 | PYG4 | PYG5 | PYG8 | PYG10 | PYG11 |
|---|---|---|---|---|---|---|---|---|
| microSD | PYB_TF_EN | PYB_TFCARD_DET | ||||||
| Buzzer | PYB_BUZZER_EN | PYB_BUZZER_PWM | ||||||
| Charge | PYB_CHG_PROG | PYB_CHG_STAT | ||||||
| LED Matrix | PYB_LEDS_EN | |||||||
| Grove 5V | PYB_GROVE_EN |
TF_3V3_L3B 電源イネーブルBUZZER_3V3_L3B 電源イネーブルLEDS_3V3_L3B 電源イネーブルM5IOE1_PWM_CH3(インデックス 2)周辺機器への給電は、以下の M5IOE1 API で個別に制御できます。
ioe1.pinMode(M5IOE1_PIN_4, OUTPUT);
ioe1.digitalWrite(M5IOE1_PIN_4, HIGH); Grove インターフェースの 5V 出力は PYB_GROVE_EN(PYG5)で制御します。M5Unified の cfg.output_power はデフォルトで true のため、M5.begin() の実行後は Grove 5V がすでにオンになっています。以下の API で制御できます。
M5.Power.setExtOutput(true); // 打开 Grove 5V
M5.Power.setExtOutput(false); // 关闭 Grove 5V プログラムから M5PM1 を手動でスリープ状態にして、システム全体の消費電力を抑えられます。デフォルトでは、スリープを直接設定すると低消費電力の給電状態に戻り、M5PM1 のみが給電状態を維持します。
pm1.shutdown(); IMU やボタンのプルアップなど L1 の回路を維持する必要がある場合は、M5PM1 ドライバーライブラリで L1 の電源保持と対応するウェイクアップソースを設定してから、pm1.shutdown() を実行してください。
M5PM1 は I2C がアイドル状態のときに自動でスリープするよう設定できます。スリープに入った後、ESP32 からの最初の通信は M5PM1 の復帰に使われるため、有効な通信は次の通信で行われます。
m5pm1_err_t setI2cSleepTime(uint8_t seconds); 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; 例: KEY1 を押すと 3 秒後に自動で電源が入り、その直後に電源がオフになるよう設定されます。KEY2 を押すと 5 秒後に電源がオフになるよう設定されます。電源オフ後は PWR_BTN を 1 回押すと再び電源を入れられます。
#include <M5Unified.h>
#include <M5PM1.h>
M5PM1 pm1;
static void drawState(uint8_t state)
{
M5.Display.startWrite();
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
if (state == 0) {
M5.Display.fillRect(7, 7, 2, 2, TFT_WHITE);
} else if (state == 1) {
M5.Display.fillRect(3, 3, 4, 4, TFT_WHITE);
} else {
M5.Display.fillRect(9, 9, 4, 4, TFT_WHITE);
}
M5.Display.endWrite();
M5.Display.display();
}
void setup()
{
auto cfg = M5.config();
cfg.serial_baudrate = 115200;
M5.begin(cfg);
M5.Display.setColorDepth(8);
M5.Display.setRotation(1);
M5.Display.setBrightness(128);
const m5pm1_err_t err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_100K);
if (err != M5PM1_OK) {
Serial.printf("M5PM1 init failed: %d\n", err);
while (true) delay(1000);
}
Serial.println("KEY1: power on after 3s");
Serial.println("KEY2: power off after 5s");
drawState(0);
}
void loop()
{
M5.update();
if (M5.BtnA.wasClicked()) {
Serial.println("Set power-on timer: 3s");
drawState(1);
delay(200);
pm1.timerSet(3, M5PM1_TIM_ACTION_POWERON);
pm1.shutdown();
}
if (M5.BtnB.wasClicked()) {
Serial.println("Set power-off timer: 5s");
drawState(2);
pm1.timerSet(5, M5PM1_TIM_ACTION_POWEROFF);
}
delay(20);
}電源が L1 モードに切り替わると、CoreMatrix の IMU、KEY1~KEY3 ボタンのプルアップ、および M5PM1 が給電状態を維持します。IMU ウェイクアップ機能を設定すると、M5PM1 はスリープに入り、BMI270 の動作に必要な L1 出力電源(3V3_L1_EN)を維持します。
この状態でデバイスを裏返したり動かしたりすると、BMI270 の INT1 が Low を出力し、M5PM1 がウェイクアップして再起動します。
M5PM1 が復帰すると、L0、L1、L3A の電源投入シーケンスを再実行し、ESP32-C61 は初期化をやり直します。
例: 起動後にボタン A(KEY1)を 1 回押すと、IMU 割り込みモードと M5PM1 の L1 電源保持が設定され、M5PM1 はスリープに入ります。この状態でデバイスを裏返したり動かしたりすると M5PM1 がウェイクアップし、ESP32-C61 に再給電されます。
#include <M5Unified.h>
#include <M5PM1.h>
#include <Wire.h>
#include "SparkFun_BMI270_Arduino_Library.h"
BMI270 imu;
M5PM1 pm1;
void setup(void)
{
M5.begin();
Serial.begin(115200);
Wire.setPins(M5.getPin(m5::pin_name_t::in_i2c_sda), M5.getPin(m5::pin_name_t::in_i2c_scl));
// Initialize PM1
m5pm1_err_t err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_100K);
if (err == M5PM1_OK) {
Serial.println("PM1 initialization successful");
pm1.gpioSetWakeEnable(M5PM1_GPIO_NUM_4, true);
pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_4, M5PM1_GPIO_WAKE_FALLING); // Falling edge
} else {
Serial.printf("PM1 initialization failed, error code: %d\n", err);
}
// Check if sensor is connected and initialize
// Address defaults to 0x68
while(imu.beginI2C(BMI2_I2C_PRIM_ADDR, Wire) != BMI2_OK)
{
Serial.println("Error: BMI270 not connected, check wiring and I2C address!");
delay(1000);
}
imu.disableFeature(BMI2_ANY_MOTION);
Serial.println("BMI270 initialization successful");
M5.Display.setColorDepth(8);
M5.Display.setRotation(1);
M5.Display.setBrightness(128);
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(7, 7, 2, 2, TFT_WHITE);
M5.Display.display();
}
void loop(void)
{
M5.update();
if (M5.BtnA.wasPressed()) {
int8_t ret = imu.enableFeature(BMI2_ANY_MOTION);
// Optional
// bmi2_sens_config config;
// config.type = BMI2_ANY_MOTION;
// config.cfg.any_motion.threshold = 0xA0; // 1LSB equals to 0.48mg. Default is 83mg. Lower is more sensitive
// config.cfg.any_motion.duration = 0x0A; // 1LSB equals 20ms. Default is 100ms.
// ret |= imu.setConfig(config);
//
bmi2_int_pin_config intPinConfig;
intPinConfig.pin_type = BMI2_INT1;
intPinConfig.int_latch = BMI2_INT_NON_LATCH;
intPinConfig.pin_cfg[0].lvl = BMI2_INT_ACTIVE_LOW;
intPinConfig.pin_cfg[0].od = BMI2_INT_PUSH_PULL;
intPinConfig.pin_cfg[0].output_en = BMI2_INT_OUTPUT_ENABLE;
intPinConfig.pin_cfg[0].input_en = BMI2_INT_INPUT_DISABLE;
ret |= imu.setInterruptPinConfig(intPinConfig);
ret |= imu.mapInterruptToPin(BMI2_ANY_MOTION_INT, BMI2_INT1);
if (!ret) {
Serial.println("BMI270 AnyMotionInterrupt enabled successfully");
} else {
Serial.println("Failed to enable BMI270 AnyMotionInterrupt");
}
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(5, 5, 6, 6, TFT_WHITE);
M5.Display.display();
delay(1000);
// Shutdown
pm1.setLdoEnable(true);
pm1.ldoSetPowerHold(true);
pm1.setLedEnLevel(true);
pm1.shutdown();
}
}サンプル動作デモ:
M5PM1 の G3 IRQ ピンは、回路上で ESP32-C61 の G2 に接続されており、連鎖的なウェイクアップ信号によって ESP32-C61 をウェイクアップできます。手順は次の通りです。
例: 起動後にボタン A(KEY1)を 1 回押すと、IMU 割り込みモードが設定され、ESP32-C61 G2 の立ち下がりエッジ割り込みが登録されます。デバイスを裏返したり動かしたりすると GPIO 割り込みハンドラーが起動し、マトリクスの表示が待機状態からトリガー済み状態に変わります。もう一度ボタン A を押すと M5PM1 IRQ フラグがクリアされ、再度テストできます。コード内でコメントアウトされているディープスリープ部分を有効にすると、動作後に ESP32-C61 が復帰し、setup() から再実行されます。
#include <M5Unified.h>
#include <M5PM1.h>
#include <Wire.h>
#include "SparkFun_BMI270_Arduino_Library.h"
#include "driver/rtc_io.h"
BMI270 imu;
M5PM1 pm1;
void setup(void)
{
M5.begin();
Serial.begin(115200);
Wire.setPins(M5.getPin(m5::pin_name_t::in_i2c_sda), M5.getPin(m5::pin_name_t::in_i2c_scl));
// Initialize PM1
m5pm1_err_t err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_100K);
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_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);
pm1.gpioSetMode(M5PM1_GPIO_NUM_3, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_3, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetPull(M5PM1_GPIO_NUM_3, M5PM1_GPIO_PULL_UP);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_3, true);
pm1.gpioSetFunc(M5PM1_GPIO_NUM_3, M5PM1_GPIO_FUNC_IRQ);
} else {
Serial.printf("PM1 initialization failed, error code: %d\n", err);
}
// Check if sensor is connected and initialize
// Address defaults to 0x68
while(imu.beginI2C(BMI2_I2C_PRIM_ADDR, Wire) != BMI2_OK)
{
Serial.println("Error: BMI270 not connected, check wiring and I2C address!");
delay(1000);
}
imu.disableFeature(BMI2_ANY_MOTION);
Serial.println("BMI270 initialization successful");
M5.Display.setColorDepth(8);
M5.Display.setRotation(1);
M5.Display.setBrightness(128);
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(7, 7, 2, 2, TFT_WHITE);
M5.Display.display();
}
volatile bool pm1IrqTriggered = false;
void ARDUINO_ISR_ATTR pm1_irq_handler()
{
pm1IrqTriggered = true;
}
void loop(void)
{
M5.update();
if (pm1IrqTriggered) {
pm1IrqTriggered = false;
uint16_t status = 0;
imu.getInterruptStatus(&status);
Serial.printf("BMI270 interrupt status: 0x%04X\n", status);
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(4, 4, 8, 8, TFT_WHITE);
M5.Display.display();
}
if (M5.BtnA.wasPressed()) {
pm1.irqClearGpioAll();
pm1.irqClearSysAll();
pm1.irqClearBtnAll();
int8_t ret = imu.enableFeature(BMI2_ANY_MOTION);
// Optional
// bmi2_sens_config config;
// config.type = BMI2_ANY_MOTION;
// ret |= imu.getConfig(&config);
// config.cfg.any_motion.threshold = 0xE0; // 1LSB equals to 0.48mg. Default is 83mg. Lower is more sensitive
// config.cfg.any_motion.duration = 0x0A; // 1LSB equals 20ms. Default is 100ms.
// ret |= imu.setConfig(config);
bmi2_int_pin_config intPinConfig;
intPinConfig.pin_type = BMI2_INT1;
intPinConfig.int_latch = BMI2_INT_NON_LATCH;
intPinConfig.pin_cfg[0].lvl = BMI2_INT_ACTIVE_LOW; // Active low
intPinConfig.pin_cfg[0].od = BMI2_INT_PUSH_PULL;
intPinConfig.pin_cfg[0].output_en = BMI2_INT_OUTPUT_ENABLE;
intPinConfig.pin_cfg[0].input_en = BMI2_INT_INPUT_DISABLE;
ret |= imu.setInterruptPinConfig(intPinConfig);
ret |= imu.mapInterruptToPin(BMI2_ANY_MOTION_INT, BMI2_INT1);
if (!ret) {
Serial.println("BMI270 AnyMotionInterrupt enabled successfully");
} else {
Serial.println("Failed to enable BMI270 AnyMotionInterrupt");
}
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(5, 5, 6, 6, TFT_WHITE);
M5.Display.display();
// Choose either of the two pieces of code below.
pinMode(GPIO_NUM_2, INPUT_PULLUP);
attachInterrupt(GPIO_NUM_2, pm1_irq_handler, FALLING);
// esp_sleep_enable_ext1_wakeup(1ULL << GPIO_NUM_2, ESP_EXT1_WAKEUP_ANY_LOW);
// rtc_gpio_pullup_en(GPIO_NUM_2);
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
}
}サンプル動作デモ:
電源が L1 モードに切り替わると、CoreMatrix の KEY1~KEY3 ボタンのプルアップと M5PM1 が給電状態を維持します。KEY3 ウェイクアップ機能を設定すると、M5PM1 はスリープに入り、ボタンのプルアップとウェイクアップロジックの動作に必要な L1 出力電源(3V3_L1_EN)を維持します。
この状態で KEY3 を押すと、M5PM1 G2 に立ち下がりエッジのウェイクアップ信号が発生し、M5PM1 が復帰して再起動します。
M5PM1 が復帰すると、L0、L1、L3A の電源投入シーケンスを再実行し、ESP32-C61 は初期化をやり直します。
例: 起動後にボタン B(KEY2)を 1 回押すと、KEY3 ウェイクアップと M5PM1 の L1 電源保持が設定され、M5PM1 はスリープに入ります。この状態で KEY3 を押すと M5PM1 がウェイクアップし、ESP32-C61 に再給電されます。
#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_100K) != M5PM1_OK) {
Serial.println("PM1 initialization failed");
while (true) {
delay(1000);
}
}
Serial.println("PM1 initialization successful");
M5.Display.setColorDepth(8);
M5.Display.setRotation(1);
M5.Display.setBrightness(128);
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(7, 7, 2, 2, TFT_WHITE);
M5.Display.display();
pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_2, M5PM1_GPIO_WAKE_FALLING);
pm1.gpioSetWakeEnable(M5PM1_GPIO_NUM_2, true);
pm1.setLdoEnable(true);
pm1.ldoSetPowerHold(true);
}
void loop(void)
{
M5.update();
if (M5.BtnB.wasPressed()) {
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(5, 5, 6, 6, TFT_WHITE);
M5.Display.display();
Serial.println("KEY3 wakeup enabled");
delay(1000);
// Shutdown
pm1.setLedEnLevel(true);
pm1.shutdown();
}
}サンプル動作デモ:
M5PM1 の G3 IRQ ピンは、回路上で ESP32-C61 の G2 に接続されており、連鎖的なウェイクアップ信号によって ESP32-C61 をウェイクアップできます。手順は次の通りです。
例: 起動後にボタン B(KEY2)を 1 回押すと、KEY3 に対応する M5PM1 G2 割り込みが設定され、ESP32-C61 G2 の立ち下がりエッジ割り込みが登録されます。KEY3 を押すと GPIO 割り込みハンドラーが起動し、マトリクスの表示が待機状態からトリガー済み状態に変わります。もう一度ボタン B を押すと M5PM1 IRQ フラグがクリアされ、再度テストできます。コード内でコメントアウトされているディープスリープ部分を有効にすると、KEY3 を押した後に ESP32-C61 が復帰し、setup() から再実行されます。
#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_100K);
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_GPIO2, M5PM1_IRQ_MASK_DISABLE);
pm1.gpioSetMode(M5PM1_GPIO_NUM_2, M5PM1_GPIO_MODE_INPUT);
pm1.gpioSetPull(M5PM1_GPIO_NUM_2, M5PM1_GPIO_PULL_UP);
pm1.gpioSetMode(M5PM1_GPIO_NUM_3, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetDrive(M5PM1_GPIO_NUM_3, M5PM1_GPIO_DRIVE_PUSHPULL);
pm1.gpioSetPull(M5PM1_GPIO_NUM_3, M5PM1_GPIO_PULL_UP);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_3, true);
pm1.gpioSetFunc(M5PM1_GPIO_NUM_3, M5PM1_GPIO_FUNC_IRQ);
} else {
Serial.printf("PM1 initialization failed, error code: %d\n", err);
}
M5.Display.setColorDepth(8);
M5.Display.setRotation(1);
M5.Display.setBrightness(128);
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(7, 7, 2, 2, TFT_WHITE);
M5.Display.display();
}
volatile bool pm1IrqTriggered = false;
void ARDUINO_ISR_ATTR pm1_irq_handler()
{
pm1IrqTriggered = true;
}
void loop(void)
{
M5.update();
if (pm1IrqTriggered) {
pm1IrqTriggered = false;
Serial.println("PM1 IRQ triggered");
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(4, 4, 8, 8, TFT_WHITE);
M5.Display.display();
}
if (M5.BtnB.wasPressed()) {
pm1.irqClearGpioAll();
pm1.irqClearSysAll();
pm1.irqClearBtnAll();
Serial.println("KEY3 IRQ enabled successfully");
M5.Display.fillScreen(TFT_BLACK);
M5.Display.drawRect(0, 0, 16, 16, TFT_WHITE);
M5.Display.fillRect(5, 5, 6, 6, TFT_WHITE);
M5.Display.display();
// Choose either of the two pieces of code below.
pinMode(GPIO_NUM_2, INPUT_PULLUP);
attachInterrupt(GPIO_NUM_2, pm1_irq_handler, FALLING);
// esp_sleep_enable_ext1_wakeup(1ULL << GPIO_NUM_2, ESP_EXT1_WAKEUP_ANY_LOW);
// rtc_gpio_pullup_en(GPIO_NUM_2);
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
}
}サンプル動作デモ:
CoreMatrix の M5IOE1 は、L3B 段階の周辺機器の電源イネーブルと状態の読み取りを担当します。M5IOE1 API を使うと、LED マトリクス、microSD、ブザー、Grove 5V を個別に制御し、充電状態とカード挿入状態を読み取れます。
| M5IOE1 ピン | CoreMatrix の機能 |
|---|---|
PYG1 | microSD 電源イネーブル |
PYG2 | ブザー電源イネーブル |
PYG3 | 充電電流設定 |
PYG4 | LED マトリクス電源イネーブル |
PYG5 | Grove 5V 出力イネーブル |
PYG8 | 充電状態入力 |
PYG10 | microSD 検出入力 |
PYG11 | ブザー PWM 出力 |
以下の例では M5IOE1 を直接初期化し、LED マトリクス、microSD、Grove 5V の電源をオンにし、ブザーの電源をオフにして、充電状態と microSD 検出状態を読み取ります。
#include <M5Unified.h>
#include <M5IOE1.h>
M5IOE1 ioe1;
#define IOE_TF_EN M5IOE1_PIN_1
#define IOE_BUZZER_EN M5IOE1_PIN_2
#define IOE_LEDS_EN M5IOE1_PIN_4
#define IOE_GROVE_EN M5IOE1_PIN_5
#define IOE_CHG_STAT M5IOE1_PIN_8
#define IOE_TF_DET M5IOE1_PIN_10
void setup()
{
auto cfg = M5.config();
cfg.serial_baudrate = 115200;
M5.begin(cfg);
M5.Display.setColorDepth(8);
M5.Display.setRotation(1);
M5.Display.setBrightness(128);
const m5ioe1_err_t err = ioe1.begin(&M5.In_I2C, M5IOE1_DEFAULT_ADDR_2, M5IOE1_I2C_FREQ_100K);
if (err != M5IOE1_OK) {
Serial.printf("M5IOE1 init failed: %d\n", err);
while (true) delay(1000);
}
ioe1.pinMode(IOE_TF_EN, OUTPUT);
ioe1.pinMode(IOE_BUZZER_EN, OUTPUT);
ioe1.pinMode(IOE_LEDS_EN, OUTPUT);
ioe1.pinMode(IOE_GROVE_EN, OUTPUT);
ioe1.pinMode(IOE_CHG_STAT, INPUT);
ioe1.pinMode(IOE_TF_DET, INPUT_PULLUP);
ioe1.digitalWrite(IOE_TF_EN, HIGH);
ioe1.digitalWrite(IOE_LEDS_EN, HIGH);
ioe1.digitalWrite(IOE_GROVE_EN, HIGH);
ioe1.digitalWrite(IOE_BUZZER_EN, LOW);
Serial.println("CoreMatrix L3B rails enabled");
}
void loop()
{
M5.update();
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);
delay(1000);
}書き込みに成功すると、プログラムは 1 秒ごとに CHG_STAT と TF_DET の状態をシリアルポートに出力します。CHG_STAT が Low の場合は充電中、TF_DET が Low の場合は microSD カードが挿入されていることを示します。