StopWatch は内部に M5PM1 + M5IOE1 を統合しており、ハードウェア回路と組み合わせることで多段階電源スイッチ設計を実現しています。異なる電源レベルのスイッチは、関連する周辺機器やインターフェースへの電源供給をそれぞれ制御します。ユーザーは動作要件に応じて電源有効レベルを切り替え、未使用の周辺機器をオフにすることで、デバイス全体の低消費電力化を実現できます。
M5PM1 / M5IOE1 ドライバライブラリを使用すると、M5PM1 / M5IOE1 のピン機能を簡単に設定でき、低消費電力ウェイクアップや周辺機器の電源スイッチ制御に利用できます。
DCDC3V3_EN_PP、LDO3V3_EN_PP、CHG_EN_PP を有効化)、M5Unified の初期化処理中にさらに M5IOE1 が制御されて L3B が有効になり、他の周辺機器への給電や有効化ピンが利用可能になります。この電源レベルでは、バッテリーが M5PM1 への給電を維持します。バッテリー残量が尽きない限りこの電源レベルは保持され、M5PM1 は基本的なボタン操作による電源 ON/OFF をサポートします。
このレベルでは IMU と RTC 周辺機器への給電を有効にします。このレベルの電源スイッチは M5PM1 の LDO3V3_EN_PP (PM_3V3_L1_EN) を使用しており、以下の API で制御できます。
pm1.setLdoEnable(true); // L1 ON
pm1.setLdoEnable(false); // L1 OFF 同時に、IMU の INT1 割り込みピンと RTC のタイマー割り込みピンは M5PM1 の PYG4 に接続されています。関連レジスタを設定することで、IMU / RTC への給電を維持したまま M5PM1 をスリープ状態に移行できます。デバイスを反転させることで IMU 割り込み(アクティブ High)を発生させるか、RTC タイマー割り込み(アクティブ Low)を発生させることで M5PM1 をウェイクアップできます。以下の API で IMU / RTC (L1) の給電維持を実現できます。
pm1.setLdoEnable(true);
pm1.ldoSetPowerHold(true);
pm1.setLedEnLevel(true);
pm1.shutdown(); この電源レベルでは、ESP32-S3、Grove インターフェース、M5IOE1 拡張チップ、ユーザーボタンのプルアップ電源、EXT.PORT インターフェースの 3V3_L2 出力、および CH442E スイッチチップへの給電が有効になります(このスイッチチップは EXT.PORT の MUX_IO_1/2 機能を UART0 / USB に切り替えるために使用されます)。
Grove 拡張インターフェースの入出力電源は、M5PM1 の EXT_5V_EN (BOOST5V_EN_PP) ピンを通じて制御する必要があります。
ESP32-S3 がスリープ状態のときは電源レベルは L2、動作中のときは L3A です。ESP32-S3 は M5PM1 をスリープ状態へ制御することで、自身の電源供給を遮断できます (L2 -> L1/L0)。
Grove EXT_5V インターフェースをオフにすると入力モードになり、この場合は外部 5V 入力電源が必要です。外部給電がない用途では、以下の API で EXT_5V 出力モードを再度有効にできます。
M5.Power.setExtOutput(true); // EXT_5V OUTPUT
// M5.Power.setExtOutput(false); // EXT_5V INPUT L3B レベルでは、ディスプレイ、スピーカー、振動モーターなど、消費電力の比較的大きい周辺機器への給電または関連設定を制御します。このレベルは M5IOE1 IO 拡張チップによって制御され、一部の周辺機器を個別に制御できます。
| M5IOE1 | PYG1 | PYG3 | PYG9 | PYG8 | PYG10 | PYG4 | PYG5 |
|---|---|---|---|---|---|---|---|
| Ext.Port Select | PYB_MUX_CTR | ||||||
| Audio L3B | PYB_AU_EN | ||||||
| Vibration Motor | PYB_MT_PWM | ||||||
| 3V3_L3B | PYB_L3B_EN | ||||||
| Speaker AMP AW8737A | PYB_SPK_EN | ||||||
| Touch | PYB_TP_RST | ||||||
| AMOLED | PYB_OLED_RST |
MUX_IO_1/2 の USB / UART 機能切り替え個別の周辺機器電源制御は、以下の M5IOE1 API を使用して独立して行えます:
ioe1.pinMode(M5IOE1_PIN_1, OUTPUT);
ioe1.digitalWrite(M5IOE1_PIN_1, LOW); M5PM1 はソフトウェアから手動でスリープ状態へ移行させることができ、デバイス全体の消費電力を低減できます。デフォルト構成では、直接スリープに入ると L0 電源レベルまで戻り、M5PM1 のみが通電状態になります。
pm1.shutdown(); 一部の特殊な利用シーン(IMU ウェイクアップや ESP32-S3 SoC スリープなど)では、M5PM1 をスリープさせつつ、一部の周辺機器レベルへの給電を保持して、ウェイクアップソースまたは状態保持に利用できます。
この種の用途では、M5PM1 がスリープモードに入る前に、対象レベルの電源スイッチピン状態を設定し、Power Hold を有効にする必要があります。
M5PM1 は、I2C 通信のアイドル時間に基づく自動スリープをサポートしており、システム全体の消費電力を低減できます。スリープ移行後、ESP32-S3 と M5PM1 間の最初の通信は M5PM1 のウェイクアップに使われるため失敗し、有効な通信はその次のトランザクションから行われます。
m5pm1_err_t setI2cSleepTime(uint8_t seconds); M5PM1 はタイマー機能をサポートしており、カウントダウン終了後に電源 ON、電源 OFF、リセットなどの対応する動作を実行できます。
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; サンプル説明:デバイス起動後、Button A をクリックすると 10 秒後に電源 ON をトリガーするタイマーを設定し、Button B をクリックすると 10 秒後に M5PM1 の電源 OFF をトリガーします(電源ボタンをクリックすると再度起動できます)。
#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_100K);
if (err == M5PM1_OK) {
Serial.println("PM1 initialization successful");
} else {
Serial.printf("PM1 initialization failed, error code: %d\n", err);
}
pm1.setSingleResetDisable(false);
M5.Display.fillScreen(BLACK);
M5.Display.setTextColor(WHITE);
M5.Display.setTextFont(&fonts::FreeMonoBold12pt7b);
M5.Display.setCursor(80, 150);
M5.Display.println("Timer Power Test");
M5.Display.println(" BtnA: After 10s ON");
M5.Display.println(" BtnB: After 10s OFF");
}
void loop(void)
{
M5.update();
if (M5.BtnA.wasPressed()) {
M5.Display.fillScreen(BLACK);
M5.Display.setCursor(40, 80);
M5.Display.println("Shutdown");
M5.Display.println(" After 10s");
M5.Display.println(" Power ON");
delay(500);
pm1.timerSet(10, M5PM1_TIM_ACTION_POWERON);
pm1.shutdown();
}
if (M5.BtnB.wasPressed()) {
M5.Display.fillScreen(BLACK);
M5.Display.setCursor(40, 80);
M5.Display.println(" After 10s");
M5.Display.println(" Power OFF");
delay(500);
pm1.timerSet(10, M5PM1_TIM_ACTION_POWEROFF);
}
} 電源を L1 モードに切り替えると、デバイス全体では IMU、RTC、M5PM1 のみが通電状態になります。IMU ウェイクアップ機能を設定すると、M5PM1 もスリープ状態に入りますが、IMU 動作を維持するために L1 出力電源 (3V3_L1_EN) は保持されます。
この状態でデバイスを反転または移動させると IMU ウェイクアップ信号が発生し、M5PM1 がウェイクアップして再起動します。
M5PM1 がウェイクアップすると、L0、L1、L2 の通電シーケンスが再実行され、ESP32-S3 も再初期化されます。
サンプル説明:デバイス起動後、Button A をクリックすると IMU 割り込みモードを設定し、M5PM1 の L1 電源保持を有効にしてスリープに入ります。デバイスを反転または移動させると M5PM1 がウェイクアップし、ESP32-S3 が再起動します。
#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);
Wire1.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_0, true);
pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_0, M5PM1_GPIO_WAKE_FALLING); // Falling edge
} else {
Serial.printf("PM1 initialization failed, error code: %d\n", err);
}
pm1.setSingleResetDisable(false);
// Check if sensor is connected and initialize
// Address defaults to 0x68)
while(imu.beginI2C(BMI2_I2C_PRIM_ADDR, Wire1) != 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.setFont(&fonts::FreeMonoBold12pt7b);
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("IMU Wakeup Test\n\n");
M5.Display.println(" Press BtnA to Sleep");
M5.Display.println(" Shake to wake up");
}
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.clear();
M5.Display.setCursor(40, 100);
M5.Display.println("Power OFF");
delay(1000);
// Shutdown
pm1.setLdoEnable(true);
pm1.ldoSetPowerHold(true);
pm1.setLedEnLevel(true);
pm1.shutdown();
}
}サンプル動作デモ:
M5PM1 の PYG1_IRQ ピンは、回路上 ESP32-S3 の G12 に接続されています。チェーン型ウェイクアップ信号を利用することで ESP32-S3 をウェイクアップできます。実装手順は以下の通りです。
PYG0 を入力モードに設定します。このピンは IMU の INT1 出力信号に接続されています。PYG1_IRQ を IRQ 出力信号ピンに設定します。PYG0(IMU 割り込み信号)の状態が変化すると、PYG1_IRQ が割り込み信号を出力します。G12 に設定します。PYG1_IRQ -> ESP32-S3 ウェイクアップの順で動作します。サンプル説明:デバイス起動後、Button A をクリックすると IMU 割り込みモードを設定します。デバイスを反転または移動させると GPIO 割り込みハンドラがトリガーされます。再度 Button A をクリックすると M5PM1 IRQ フラグをクリアし、再テストできます。
#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);
Wire1.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_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);
}
pm1.setSingleResetDisable(false);
// Check if sensor is connected and initialize
// Address defaults to 0x68)
while(imu.beginI2C(BMI2_I2C_PRIM_ADDR, Wire1) != 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.setFont(&fonts::FreeMonoBold12pt7b);
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("IMU IRQ Test\n\n");
M5.Display.println(" Press BtnA to Sleep");
M5.Display.println(" Shake to wake up");
}
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");
uint16_t status = 0;
imu.getInterruptStatus(&status);
Serial.printf("BMI270 interrupt status: 0x%04X\n", status);
M5.Display.setCursor(40, 130);
M5.Display.println("PM1 IRQ triggered");
}
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_HIGH;// active - high
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.clear();
M5.Display.setCursor(40, 100);
M5.Display.println("Now Shake!");
// Choose either of the two pieces of code below.
pinMode(GPIO_NUM_12, INPUT_PULLUP);
attachInterrupt(GPIO_NUM_12, pm1_irq_handler, FALLING);
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_12, 0); // 0 = Low
// rtc_gpio_pullup_en(GPIO_NUM_12);
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
}
}サンプル動作デモ:
電源を L1 モードに切り替えると、デバイス全体では IMU、RTC、M5PM1 のみが通電状態になります。RTC タイマーウェイクアップ機能を設定すると、M5PM1 はスリープ状態に入りつつ、RTC の動作を維持するために L1 出力電源 (3V3_L1_EN) を保持します。
この状態では、RTC タイマーウェイクアップを設定することで M5PM1 をウェイクアップさせ、ESP32-S3 を再び起動できます。
M5PM1 がウェイクアップすると、L0、L1、L2 の通電シーケンスが再実行され、ESP32-S3 も再初期化されます。
サンプル説明:デバイス起動後、Button A をクリックすると RTC タイマーウェイクアップを設定し、M5PM1 はスリープに入ります。5 秒後に RTC ウェイクアップが発生し、ESP32-S3 が再起動します。
#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_100K);
if (err == M5PM1_OK) {
Serial.println("PM1 initialization successful");
pm1.gpioSetWakeEnable(M5PM1_GPIO_NUM_0, true);
pm1.gpioSetWakeEdge(M5PM1_GPIO_NUM_0, M5PM1_GPIO_WAKE_FALLING); // Falling edge
} else {
Serial.printf("PM1 initialization failed, error code: %d\n", err);
}
pm1.setSingleResetDisable(false);
M5.Display.setFont(&fonts::FreeMonoBold12pt7b);
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("RTC Wakeup Test\n\n");
M5.Display.println(" Press BtnA to Sleep");
}
void loop(void)
{
M5.update();
if (M5.BtnA.wasPressed()) {
M5.Rtc.clearIRQ();
if (M5.Rtc.setTimerIRQ(5000)){// 5s later wakeup
Serial.println("RTC IRQ enabled successfully");
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("Power OFF");
M5.Display.setCursor(40, 130);
M5.Display.printf("5s later wakeup");
delay(500);
// Shutdown
pm1.setLdoEnable(true);
pm1.ldoSetPowerHold(true);
pm1.setLedEnLevel(true);
pm1.shutdown();
} else {
Serial.println("Failed to enable RTC IRQ");
}
}
}サンプル動作デモ:
RTC ウェイクアップの設定手順は IMU ESP32-S3 ウェイクアップとほぼ同じです。違いは、RTC ウェイクアップでは IMU のように BMI270 割り込み設定が不要な点ですが、RTC IRQ -> M5PM1 GPIO -> PYG1_IRQ -> ESP32-S3 G12 というチェーンには同様に依存します。
#include <M5Unified.h>
#include <M5PM1.h>
#include "driver/rtc_io.h"
M5PM1 pm1;
void setup(void) {
M5.begin();
Serial.begin(115200);
M5.Display.setFont(&fonts::FreeMonoBold12pt7b);
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("RTC IRQ Test\n\n");
M5.Display.println(" Press BtnA to Sleep");
// 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_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);
}
pm1.setSingleResetDisable(false);
delay(200);
M5.Rtc.clearIRQ();
delay(200);
}
volatile bool pm1IrqTriggered = false;
void ARDUINO_ISR_ATTR pm1_irq_handler() {
pm1IrqTriggered = true;
}
void loop(void) {
M5.update();
if (pm1IrqTriggered) {
pm1IrqTriggered = false;
M5.Rtc.setTimerIRQ(0);
M5.Rtc.clearIRQ();
pm1.irqClearGpioAll();
pm1.irqClearSysAll();
pm1.irqClearBtnAll();
Serial.println("PM1 IRQ triggered");
M5.Display.setCursor(40, 130);
M5.Display.println("PM1 IRQ triggered");
}
if (M5.BtnA.wasPressed()) {
if (M5.Rtc.setTimerIRQ(5000)){// Set Timer IRQ
Serial.println("RTC IRQ enabled successfully");
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("Wait for IRQ to wake up...");
// Choose either of the two pieces of code below.
pinMode(GPIO_NUM_12, INPUT_PULLUP);
attachInterrupt(GPIO_NUM_12, pm1_irq_handler, FALLING);
// esp_sleep_enable_ext0_wakeup(GPIO_NUM_12, 0); // 0 = Low
// rtc_gpio_pullup_en(GPIO_NUM_12);
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
} else {
Serial.println("Failed to enable RTC IRQ");
}
}
}サンプル動作デモ:
M5IOE1 は独立した IO 拡張チップとして、L3B レベルの周辺機器への給電や関連設定の一部(ディスプレイ、スピーカー、振動モーターなど)を制御します。M5IOE1 API を通じて、これらの周辺機器を個別に制御し、より柔軟な電源管理を実現できます。
IOE_TP_EN、IOE_OLED_RST、IOE_L3B_EN ピンを制御していません。これらのピンを Low にしてから再度給電した場合、正常な表示とタッチ操作を行うにはディスプレイの再初期化が必要です。今後の M5Unified ライブラリ更新での対応をお待ちください。制御 API も同様で、例えば ioe1.digitalWrite(IOE_L3B_EN, LOW) を使用して L3B 電源スイッチを制御できます。#include <M5Unified.h>
#include <M5PM1.h>
#include <M5IOE1.h>
M5PM1 pm1;
M5IOE1 ioe1;
#define IOE_MUX_EN M5IOE1_PIN_1
#define IOE_AU_EN M5IOE1_PIN_3
#define IOE_MT_PWM M5IOE1_PIN_9
#define IOE_L3B_EN M5IOE1_PIN_8
#define IOE_SPK_EN M5IOE1_PIN_10
#define IOE_TP_EN M5IOE1_PIN_4
#define IOE_OLED_RST M5IOE1_PIN_5
void setup(void)
{
M5.begin();
Serial.begin(115200);
// Initialize PM1
m5pm1_err_t pm1_err = pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_100K);
if (pm1_err == M5PM1_OK) {
Serial.println("PM1 initialization successful");
} else {
Serial.printf("PM1 initialization failed, error code: %d\n", pm1_err);
}
pm1.setSingleResetDisable(false);
// Initialize IOE1
m5ioe1_err_t ioe1_err = ioe1.begin(&M5.In_I2C, M5IOE1_DEFAULT_ADDR, 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);
}
ioe1.pinMode(IOE_MUX_EN, OUTPUT);
ioe1.pinMode(IOE_AU_EN, OUTPUT);
ioe1.pinMode(IOE_MT_PWM, OUTPUT);
ioe1.pinMode(IOE_L3B_EN, OUTPUT);
ioe1.pinMode(IOE_SPK_EN, OUTPUT);
ioe1.pinMode(IOE_TP_EN, OUTPUT);
ioe1.pinMode(IOE_OLED_RST, OUTPUT);
ioe1.setPwmFrequency(2000);
ioe1.setPwmDuty(M5IOE1_PWM_CH1, 0);
M5.Display.setFont(&fonts::FreeMonoBold12pt7b);
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("IOE1 Power Test Begin\n\n");
delay(1000);
}
void loop(void)
{
M5.update();
// MUX IO
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("MUX IO set to UART0");
ioe1.digitalWrite(IOE_MUX_EN, LOW);
delay(1000);
M5.Display.setCursor(40, 130);
M5.Display.printf("MUX IO set to USB");
ioe1.digitalWrite(IOE_MUX_EN, HIGH);
delay(1000);
// Speaker
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("Speaker off");
ioe1.digitalWrite(IOE_SPK_EN, LOW);
M5.Speaker.tone(10000, 100);
M5.Display.setCursor(40, 130);
M5.Display.printf("No sound");
delay(1000);
M5.Display.setCursor(40, 160);
M5.Display.printf("1s later hear sound");
ioe1.digitalWrite(IOE_SPK_EN, HIGH);
delay(1000);
M5.Speaker.tone(4000, 200);
delay(1000);
// Vibration Motor
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("Motor off");
ioe1.setPwmDuty(M5IOE1_PWM_CH1, 0);
M5.Display.setCursor(40, 130);
M5.Display.printf("No vibration");
delay(1000);
M5.Display.setCursor(40, 160);
M5.Display.printf("1s vibrate");
ioe1.digitalWrite(IOE_MT_PWM, HIGH);
ioe1.setPwmDuty(M5IOE1_PWM_CH1, 50);
delay(1000);
ioe1.setPwmDuty(M5IOE1_PWM_CH1, 0);
delay(200);
// Audio
M5.Display.clear();
M5.Display.setCursor(40, 100);
M5.Display.printf("Audio off");
ioe1.digitalWrite(IOE_AU_EN, LOW);
M5.Speaker.end();
M5.Speaker.begin();
M5.Speaker.setVolume(128);
M5.Speaker.tone(10000, 100);
M5.Display.setCursor(40, 130);
M5.Display.printf("No sound");
delay(1000);
M5.Display.setCursor(40, 160);
M5.Display.printf("1s later hear sound");
ioe1.digitalWrite(IOE_AU_EN, HIGH);
M5.Speaker.end();
M5.Speaker.begin();
M5.Speaker.setVolume(128);
delay(1000);
M5.Speaker.tone(6000, 200);
delay(1000);
}書き込み後、このプログラムは背面拡張インターフェース切り替え、スピーカー、振動モーター、およびオーディオアンプ制御の動作を順番にデモ表示します。