ToughC5 内部集成 M5PM1 + M5IOE1,结合硬件电路实现了多级电源开关,不同级别对应不同的主控运行状态及外设与接口供电状态。用户可根据运行需求,切换主控运行状态及相关电源使能,关闭未使用的外设部分,实现整机低功耗。
使用 M5PM1 / M5IOE1 驱动库能够非常便捷地配置 M5PM1 / M5IOE1 的引脚功能,用于低功耗唤醒以及外设供电开关。
此级电源条件下,RTC 由纽扣电池保持供电,用于维持时钟运行。纽扣电池电量未耗尽的情况下,该层电源将一直保持。
此级电源条件下,整机电池接入,M5PM1 保持供电,用于管理整机电源,后续层级的主控及外设可按需开启。
此级电源条件下,将新启用 ESP32-C5、M5IOE1 拓展芯片、GROVE 输入 / 输出接口及触摸屏相关供电,M5PM1 和 RTC 的供电继续保持。该层级使用 M5PM1 的 DCDC 输出 3V3_L2 为 ESP32-C5 和 M5IOE1 供电,使用 LDO 输出 3V3_L2_LDO 为触摸屏供电,可通过以下 API 实现这两路电源的开关控制。
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 层级控制 LCD、蜂鸣器、Grove 等外设的供电或相关配置,该层级基于 M5IOE1 IO 拓展芯片实现控制,允许独立控制该层级的一些外设。
| 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 |
具体的外设供电控制,通过下方 M5IOE1 的 API 进行独立控制:
ioe1.pinMode(M5IOE1_PIN_6, OUTPUT);
ioe1.digitalWrite(M5IOE1_PIN_6, HIGH); M5PM1 可通过程序手动控制进入休眠状态,来降低整机功耗。默认状态下,直接设置休眠将回退至 L0 级电源,此时仅 M5PM1 保持供电。
pm1.shutdown(); 在需要保留触摸唤醒等低功耗电路的使用场景中,应在 M5PM1 进入休眠模式前配置对应的电源保持状态和唤醒源。ToughC5 没有实体按键,休眠后的唤醒操作通过触摸屏完成,具体配置请参考下方的 M5PM1 唤醒小节。
M5PM1 支持配置 I2C 空闲通信自动休眠状态,来降低整机功耗。进入休眠状态后,ESP32-C5 与 M5PM1 的首次通信将用于 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; 案例说明:通过触摸屏幕左右两侧配置定时器。触摸左侧设置 10s 后重新开机并立即关机,触摸右侧设置 10s 后关机(可通过单击电源按键重新开机)。
#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);
}电源切换至 L1 Mode,ToughC5 的 RTC 和 M5PM1 处于供电状态。当配置 RTC 定时唤醒功能后,M5PM1 进入休眠状态,同时保持 RTC 所需的 L1 电源,用于维持 RTC 工作。
此时可通过配置 RTC 定时器唤醒,RTC 的 PM_RTC_INT 中断连接到 M5PM1 GPIO3,触发 M5PM1 唤醒,ESP32-C5 重新上电。
M5PM1 唤醒后将重新运行 L1、L2 和 L3A 上电流程,ESP32-C5 重新执行初始化。
案例说明:设备开机后,触摸屏幕一次配置 RTC 定时器唤醒,M5PM1 进入休眠。5s 后触发 RTC 唤醒,M5PM1 恢复供电,ESP32-C5 重新上电。
#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 唤醒 M5PM1 类似,区别在于 RTC 中断不直接用于重新上电,而是依赖 RTC IRQ → M5PM1 GPIO3 → M5PM1 GPIO1 IRQout → ESP32-C5 G4 这条链路,实现步骤如下:
PM_RTC_INT 中断信号。IRQout 输出信号引脚。当 GPIO3 (RTC 产生中断信号) 引脚状态变化时,GPIO1 IRQout 引脚将输出中断信号。IRQout -> 唤醒 ESP32-C5。案例说明:设备开机后,触摸屏幕配置 5 秒 RTC 定时器。定时到期即可触发 GPIO 中断处理函数。若启用代码中注释的深度休眠部分,RTC 定时到期后会唤醒 ESP32-C5,并从 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_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 检测到低电平并从深度休眠中唤醒。唤醒后 ESP32-C5 会复位,程序从 setup() 开始执行。
电源切换至 L2 / L3A Mode,ToughC5 的触摸电路和 M5PM1 处于供电状态。当配置触摸唤醒功能后,M5PM1 进入休眠状态,同时保持 3V3_L2_LDO 触摸电源,用于维持触摸检测。
此时可通过触摸屏幕产生唤醒信号,触摸中断 PM_TP_INT 连接到 M5PM1 GPIO0,触发 M5PM1 唤醒,ESP32-C5 重新上电。
M5PM1 唤醒后将重新运行 L1、L2 和 L3A 上电流程,ESP32-C5 重新执行初始化。
案例说明:设备开机后,触摸屏幕一次配置触摸唤醒和 M5PM1 LDO 电源保持,M5PM1 进入休眠。再次触摸屏幕即可触发 TP 唤醒,M5PM1 恢复供电,ESP32-C5 重新上电。
#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 的配置步骤与 RTC 唤醒 ESP32-C5 类似,区别在于唤醒源为触摸中断 PM_TP_INT,并依赖 TP IRQ → M5PM1 GPIO0 → M5PM1 GPIO1 IRQout → ESP32-C5 G4 这条链路。
案例说明:设备开机后,触摸屏幕一次配置 TP 中断并注册 G4 中断处理函数。再次触摸屏幕即可触发 GPIO 中断处理函数。若启用代码中注释的深度休眠部分,触摸屏幕后会唤醒 ESP32-C5,并从 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_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 中断,程序在屏幕和串口显示触发结果。
ToughC5 的 M5IOE1 负责 L3B 级外设的部分供电及相关配置,如 LCD、背光和 microSD 等。通过 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 为低有效,读取到低电平表示检测到 microSD 卡。
#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 卡检测状态。