1. Format the project text and fix some issues

This commit is contained in:
onexs-xsi
2026-01-21 15:34:26 +08:00
parent 58f803e479
commit e2ccec30c3
12 changed files with 2142 additions and 2494 deletions
+14 -194
View File
@@ -1,207 +1,27 @@
# M5PM1
# M5PM1
## Overview
**SKU: N/A**
**SKU:N/A**
M5PM1 is a dual-platform (ESP-IDF & Arduino) driver library for M5Stack PM1 Power Management IC. It provides comprehensive power management features including:
- Battery charging and monitoring
- Multiple power rails (DCDC 5V, LDO 3.3V)
- 5 GPIO pins with various functions (GPIO/IRQ/WAKE/PWM/ADC)
- PWM output (2 channels)
- ADC input (2 channels + temperature)
- NeoPixel LED control (up to 32 LEDs)
- Watchdog timer
- RTC RAM (32 bytes, retained in sleep)
- I2C auto-sleep/wake feature
## Features
- **Dual-Platform Support**: Works with both Arduino and ESP-IDF frameworks
- **Arduino-Style API**: Familiar `pinMode()`, `digitalWrite()`, `digitalRead()` functions
- **I2C Auto-Wake**: Automatically handles PM1 sleep mode wake-up
- **Comprehensive Power Management**: Battery charging, voltage monitoring, power hold
## Hardware
- **I2C Address**: 0x6E (default)
- **I2C Speed**: 100KHz (default), 400KHz (configurable)
- **GPIO Pins**: 5 (GPIO0-GPIO4)
- **PWM Channels**: 2 (GPIO3, GPIO4)
- **ADC Channels**: 2 (GPIO1, GPIO2) + Internal Temperature
## Pin Functions
| GPIO | Special Function | Description |
|------|-----------------|-------------|
| GPIO0 | LED_EN | NeoPixel LED enable |
| GPIO1 | ADC1 | Analog input channel 1 |
| GPIO2 | ADC2 | Analog input channel 2 |
| GPIO3 | PWM0 | PWM output channel 0 |
| GPIO4 | PWM1 | PWM output channel 1 |
## Usage
### Arduino
```cpp
#include <M5PM1.h>
M5PM1 pm1;
void setup() {
Serial.begin(115200);
Wire.begin(38, 39); // SDA, SCL
if (pm1.begin(&Wire) != M5PM1_OK) {
Serial.println("PM1 init failed!");
while (1) delay(100);
}
// Use Arduino-style API
pm1.pinMode(0, OUTPUT);
pm1.digitalWrite(0, HIGH);
// Read battery voltage
uint16_t vbat;
if (pm1.readVbat(&vbat) == M5PM1_OK) {
Serial.printf("Battery: %d mV\n", vbat);
}
}
void loop() {
// Toggle GPIO0
pm1.digitalWrite(0, HIGH);
delay(500);
pm1.digitalWrite(0, LOW);
delay(500);
}
```
### ESP-IDF
```cpp
#include "M5PM1.h"
M5PM1 pm1;
void app_main() {
// Initialize with self-created I2C bus
if (pm1.begin(I2C_NUM_0, M5PM1_DEFAULT_ADDR, 21, 22, 100000) != M5PM1_OK) {
ESP_LOGE("PM1", "Init failed!");
return;
}
// Or use existing i2c_master_bus_handle_t
// pm1.begin(existing_bus_handle, M5PM1_DEFAULT_ADDR, 100000);
// Configure GPIO
pm1.gpioSetFunc(M5PM1_GPIO_NUM_0, M5PM1_GPIO_FUNC_GPIO);
pm1.gpioSetMode(M5PM1_GPIO_NUM_0, M5PM1_GPIO_MODE_OUTPUT);
pm1.gpioSetOutput(M5PM1_GPIO_NUM_0, 1);
}
```
## API Reference
### Initialization
| Method | Description |
|--------|-------------|
| `begin()` | Initialize the PM1 device |
| `setAutoWakeEnable()` | Enable/disable auto-wake feature |
| `sendWakeSignal()` | Manually send wake signal |
### GPIO Functions
| Method | Description |
|--------|-------------|
| `pinMode()` | Set GPIO mode (Arduino-style) |
| `digitalWrite()` | Set GPIO output (Arduino-style) |
| `digitalRead()` | Read GPIO input (Arduino-style) |
| `gpioSetFunc()` | Set GPIO function (GPIO/IRQ/WAKE/OTHER) |
| `gpioSetMode()` | Set GPIO direction |
| `gpioSetOutput()` | Set GPIO output level |
| `gpioGetInput()` | Get GPIO input level |
| `gpioSetPull()` | Set GPIO pull-up/pull-down |
| `gpioSetDrive()` | Set GPIO drive mode |
### Power Management
| Method | Description |
|--------|-------------|
| `getPowerSource()` | Get current power source |
| `getWakeSource()` | Get wake source flags |
| `setPowerConfig()` | Set power configuration |
| `setChargeEnable()` | Enable/disable battery charging |
| `setDcdcEnable()` | Enable/disable 5V DCDC |
| `setLdoEnable()` | Enable/disable 3.3V LDO |
| `shutdown()` | Shutdown the system |
| `reboot()` | Reboot the system |
### Voltage Reading
| Method | Description |
|--------|-------------|
| `readVref()` | Read reference voltage |
| `readVbat()` | Read battery voltage |
| `readVin()` | Read VIN voltage |
| `read5VInOut()` | Read 5VINOUT voltage |
### PWM Functions
| Method | Description |
|--------|-------------|
| `setPwmFrequency()` | Set PWM frequency |
| `getPwmFrequency()` | Get PWM frequency |
| `setPwmDuty()` | Set PWM duty (percentage) |
| `getPwmDuty()` | Get PWM duty (percentage) |
| `setPwmDuty12bit()` | Set PWM duty (12-bit) |
| `getPwmDuty12bit()` | Get PWM duty (12-bit) |
| `analogWrite()` | Arduino-compatible PWM output |
### ADC Functions
| Method | Description |
|--------|-------------|
| `analogRead()` | Read ADC value |
| `isAdcBusy()` | Check ADC busy status |
| `disableAdc()` | Disable ADC conversion |
### NeoPixel Functions
| Method | Description |
|--------|-------------|
| `setLeds()` | Configure and set all LEDs |
| `setLedCount()` | Set LED count |
| `setLedColor()` | Set LED color |
| `refreshLeds()` | Refresh LED display |
| `disableLeds()` | Disable LED output |
### Watchdog Functions
| Method | Description |
|--------|-------------|
| `wdtSet()` | Set watchdog timeout |
| `wdtFeed()` | Feed the watchdog |
| `wdtGetCount()` | Get watchdog countdown |
### RTC RAM Functions
| Method | Description |
|--------|-------------|
| `writeRtcRAM()` | Write to RTC RAM |
| `readRtcRAM()` | Read from RTC RAM |
M5PM1 is a dual-platform (ESP-IDF & Arduino) driver library for M5Stack PM1 Power Management IC. It provides comprehensive power management features including battery charging and monitoring, multiple power rails (DCDC 5V, LDO 3.3V), 5 GPIO pins with various functions (GPIO/IRQ/WAKE/PWM/ADC), built-in NeoPixel LED driver, Watchdog timer, RTC RAM, and I2C auto-sleep/wake feature.
## Related Link
- [Document & Datasheet](https://docs.m5stack.com/en/unit/pm1)
- N/A
## Required Libraries
## Required Libraries:
- None (standalone library)
- N/A
## Examples
- examples/basic_power_adc/basic_power_adc.ino
- examples/gpio_pwm/gpio_pwm.ino
- examples/interrupt_button_sleep/interrupt_button_sleep.ino
- examples/neopixel/neopixel.ino
## License
- [M5PM1 - MIT](LICENSE)
+154
View File
@@ -0,0 +1,154 @@
# M5PM1 功能总览(中文)
面向 Arduino/ESP-IDF 双平台的 PM1 电源管理驱动库功能分类说明。
- **版本**: 1.0.1
- **默认 I2C 地址**: `0x6E`
- **I2C 速度**: 默认 100 kHz,可切换 400 kHz(切换后需重新初始化 I2C)
## 项目结构与角色
- `src/M5PM1.h`: 公共 API、寄存器/枚举、缓存结构定义。
- `src/M5PM1.cpp`: 驱动实现、冲突校验、快照与缓存逻辑。
- `src/M5PM1_i2c_compat.h`: Arduino/ESP-IDF I2C 读写兼容封装。
## 示例
- `examples/basic_power_adc/basic_power_adc.ino`: 初始化、设备信息、VBAT/VIN/5V 读数、ADC/温度采样、电源轨控制示例。
- `examples/gpio_pwm/gpio_pwm.ino`: GPIO 输出/输入读取与 PWM 呼吸灯示例。
- `examples/interrupt_button_sleep/interrupt_button_sleep.ino`: GPIO 中断、按钮 API、定时器/唤醒与关机示例。
- `examples/neopixel/neopixel.ino`: NeoPixel 彩虹渐变示例。
## 功能分类
### 1) 初始化与日志
- **初始化**
- Arduino: `begin(TwoWire*, addr, sda, scl, speed)`
- ESP-IDF: `begin(i2c_port_t, addr, sda, scl, speed)`
- ESP-IDF: `begin(i2c_master_bus_handle_t, addr, speed)`
- ESP-IDF: `begin(i2c_bus_handle_t, addr, speed)`
- **日志**
- `setLogLevel`/`getLogLevel`
### 2) 设备信息
- `getDeviceId`/`getDeviceModel`/`getHwVersion`/`getSwVersion`
### 3) GPIO 基础与高级控制
- **Arduino 风格**
- `pinMode`/`digitalWrite`/`digitalRead`
- **带返回值版本**
- `pinModeWithRes`/`digitalWriteWithRes`/`digitalReadWithRes`
- **高级配置**
- `gpioSet`/`gpioSetFunc`/`gpioSetMode`/`gpioSetOutput`/`gpioGetInput`
- `gpioSetPull`/`gpioSetDrive`/`ledEnSetDrive`
- `gpioSetWakeEnable`/`gpioSetWakeEdge`
- **引脚状态与校验**
- `dumpPinStatus`/`verifyPinConfig`/`getPinStatus`/`getPinStatusArray`
### 4) 电源保持
- `gpioSetPowerHold`/`gpioGetPowerHold`
- `ldoSetPowerHold`/`ldoGetPowerHold`
- `dcdcSetPowerHold`/`dcdcGetPowerHold`
### 5) ADC 与温度
- `analogRead`/`isAdcBusy`/`disableAdc`/`readTemperature`
### 6) PWM
- `setPwmFrequency`/`getPwmFrequency`
- `setPwmDuty`/`getPwmDuty` (0-100%)
- `setPwmDuty12bit`/`getPwmDuty12bit` (0-4095)
- `setPwmConfig`
- `analogWrite` (0-255)
### 7) 电压读取
- `readVref`/`getRefVoltage`/`readVbat`/`readVin`/`read5VInOut`
### 8) 电源管理与电池
- `getPowerSource`
- `getWakeSource`/`clearWakeSource`
- `setPowerConfig`/`getPowerConfig`/`clearPowerConfig`
- `setChargeEnable`/`setDcdcEnable`/`setLdoEnable`/`set5VInOutEnable`/`setLedEnLevel`
- `setBatteryLvp`
### 9) 看门狗与定时器
- `wdtSet`/`wdtFeed`/`wdtGetCount`
- `timerSet`/`timerClear`
### 10) 按钮
- `btnSetConfig`/`btnGetState`/`btnGetFlag`
- `setSingleResetDisable`/`getSingleResetDisable`
- `setDoubleOffDisable`/`getDoubleOffDisable`
### 11) 中断
- **状态读取与清除**
- `irqGetGpioStatus`/`irqClearGpio`
- `irqGetSysStatus`/`irqClearSys`
- `irqGetBtnStatus`/`irqClearBtn`
- **枚举式读取**
- `irqGetGpioStatusEnum`/`irqGetSysStatusEnum`/`irqGetBtnStatusEnum`
- **中断屏蔽**
- `irqSetGpioMask`/`irqGetGpioMask`/`irqSetGpioMaskAll`/`irqGetGpioMaskAll`
- `irqSetSysMask`/`irqGetSysMask`/`irqSetSysMaskAll`/`irqGetSysMaskAll`
- `irqSetBtnMask`/`irqGetBtnMask`/`irqSetBtnMaskAll`/`irqGetBtnMaskAll`
### 12) 系统命令
- `sysCmd`/`shutdown`/`reboot`/`enterDownloadMode`
- `setDownloadLock`/`getDownloadLock`
### 13) NeoPixel
- `setLeds`/`setLedCount`/`setLedColor`/`refreshLeds`/`disableLeds`
### 14) AW8737A 脉冲控制
- `setAw8737aPulse`/`refreshAw8737aPulse`
### 15) RTC RAM
- `writeRtcRAM`/`readRtcRAM`
### 16) I2C 配置与自动唤醒
- `setI2cConfig`/`switchI2cSpeed`/`getI2cSpeed`
- `setI2cSleepTime`/`getI2cSleepTime`
- `setAutoWakeEnable`/`isAutoWakeEnabled`/`sendWakeSignal`
- 兼容层: `M5PM1_i2c_compat.h`
### 17) 状态快照/缓存/校验
- `setAutoSnapshot`/`isAutoSnapshotEnabled`/`updateSnapshot`/`verifySnapshot`
- `validateConfig`
- `getCachedPwmFrequency`/`getCachedPwmState`/`getCachedAdcState`
- `getCachedPowerConfig`/`getCachedButtonConfig`
- `getCachedIrqMasks`/`getCachedIrqStatus`
## 引脚与功能限制
| 功能区域 | 说明 | 备注 |
| :--- | :--- | :--- |
| **GPIO 互斥** | GPIO0/2 共享 WAKE/IRQ 线;GPIO3/4 互斥 | GPIO0/2 互斥 |
| **唤醒 (WAKE)** | GPIO0, GPIO2 支持唤醒 | GPIO1 不支持 WAKE |
| **ADC 通道** | GPIO1=ADC1, GPIO2=ADC2 | 温度为内部通道 |
| **PWM 通道** | GPIO3=PWM0, GPIO4=PWM1 | 频率全通道共享 |
| **NeoPixel** | 数据区 0x60 - 0x9F | 刷新时 I2C 短暂不可中断 |
| **I2C 休眠** | 空闲超时可进入低功耗 | PWM 使能或下载模式下禁止休眠 |
| **寄存器区块** | 0x00 - 0x0C, 0x10 - 0x19, 0x20 - 0x2A, 0x30 - 0x35 0x38 - 0x3D, 0x40 - 0x45, 0x48 - 0x4A, 0x50 0x53, 0x60 - 0x9F, 0xA0 - 0xBF | 支持连续读写 |
## 版本与依赖
- **版本**: 1.0.1
- **平台**: Arduino / ESP-IDF (>=4.4)
- **依赖**: espressif/i2c_bus
File diff suppressed because it is too large Load Diff
-273
View File
@@ -1,273 +0,0 @@
PY32 PMIC 说明手册
================================================================================
基本信息
================================================================================
I2C 地址: 0x6E
硬件/固件版本: HW:5 / SW:6 (硬件修订: 5, 固件主版本: 6)
文档版本: 1.7 (2025-12-13)
代码仓库: stamp_timerpower2_code
后续软件工程师: @任张涛
================================================================================
一、 芯片概述
================================================================================
[功能]
1. 电源管理: 支持 3.3V LDO/DCDC、5V IN/OUT、充电控制
2. GPIO 控制: 5路可复用 GPIO (输入/输出、NeoPixel、ADC、PWM、Wake In、IRQ Out)
3. 定时唤醒: 32-bit 定时器 (最大 214748364 秒), 支持多种唤醒动作
4. 安全监控: 看门狗, 低压保护
5. 存储备份: 32 字节 RTC RAM
[接口]
I2C 协议 (支持 100kHz/400kHz 模式, 默认支持 100kHz, 400kHz 需要配置)
--------------------------------------------------------------------------------
GPIO 功能分布表
--------------------------------------------------------------------------------
| 序号 | PIN | GPIO | WAKE支持情况 | IRQ | 复用功能 |
|-------|-----|------|---------------------|------|-----------|
| GPIO0 | 3 | 支持 | 支持 (与GPIO2互斥) | 支持 | Neopixel |
| GPIO1 | 17 | 支持 | 不支持 | 支持 | ADC |
| GPIO2 | 20 | 支持 | 支持 (与GPIO0互斥) | 支持 | ADC |
| GPIO3 | 13 | 支持 | 支持 (与GPIO4互斥) | 支持 | PWM |
| GPIO4 | 12 | 支持 | 支持 (与GPIO3互斥) | 支持 | PWM |
--------------------------------------------------------------------------------
内部 Pin 功能分布表
--------------------------------------------------------------------------------
| PIN | IO类型 | 上下拉 | 功能描述 |
|-----|----------|--------|----------|
| 1 | 模拟输出 | 无 | 开关电流采样: 高电平开启采样, 低电平关闭采样。默认为高电平 |
| 2 | 模拟输出 | 无 | 使能电池充电: 低电平停止充电, 高电平开启充电。默认为高电平 |
| 3 | GPIO | 无 | GPIO0, 默认为输入 |
| 4 | 电源VSS | 无 | 无 |
| 5 | 模拟输出 | 无 | LED灯控制, 输出低电平或高电平, 默认为高电平 |
| 6 | 电源VCC | 无 | 无 |
| 7 | 输入 | 上拉 | 按键检测 |
| 8 | I2C | 无 | I2C SDA |
| 9 | I2C | 无 | I2C SCL |
| 10 | 模拟输出 | 无 | 5V DC/DC控制, 输出低电平或高电平, 默认为低电平 |
| 11 | ADC | 无 | 电池ADC输入 (分压系数为1:1) |
| 12 | GPIO | 无 | GPIO4, 默认为输入 |
| 13 | GPIO | 无 | GPIO3, 默认为输入 |
| 14 | 模拟输出 | 无 | 3V3 DC/DC控制, 输出低电平或高电平, 默认为高电平 |
| 15 | 开漏输出 | 无 | 控制ESP32 BOOT, 默认为高电平 |
| 16 | ADC | 无 | 5VIN ADC输入 (分压系数为1:1) |
| 17 | GPIO | 无 | GPIO1, 默认为输入 |
| 18 | 模拟输出 | 无 | 3V3 LDO控制, 输出低电平或高电平, 默认为高电平 |
| 19 | ADC | 无 | 5VOUT ADC值输入 (分压系数为1:1) |
| 20 | GPIO | 无 | GPIO2, 默认为输入 |
================================================================================
二、 注意事项
================================================================================
1. 内部通道 ADC 的使用
- 3个通道都有特定功能, 必须严格对应接入。
- ADC 5VOUT:
* 5VOUT 有输入和输出功能, 5VOUT 升压打开前, 需要先检测 5VOUT 的电压, 确保上面没有电源输入才能打开升压, 否则保持输入。
* 低电压阈值检测。
* 5VOUT 电源插入唤醒。
* 5VOUT 电源移除与插入检测。
- ADC BAT:
* 低电压阈值检测。
* 电池插入移除检测。
- ADC 5VIN:
* 低电压阈值检测。
* 5VIN 插入移除检测。
2. 低电压自动关机
- 低电压阈值由寄存器 BATT_LVP(0x08) 决定。
- 在 5VIN 或 5VINOUT 没有插入的前提下, 电池电压低于阈值, 会自动关机。
================================================================================
逻辑流程描述
================================================================================
[流程1: 低电压保护 (LVP) 与自动关机]
步骤:
1. 检测是否触发 LVP (低电压保护)。
2. 判断: 5VIN 或 5VINOUT 是否有电?
- 是: 进入 [正常运行]。
- 否: 判断 VBAT 是否大于最低电压?
- 是: 进入 [正常运行]。
- 否: 进入 [DeepSleep待机]。
注: 在没有插入 Vin 的情况下, 需要 BAT 电压大于 BATT_LVP 电压 + 100mV, 才能退出低电压待机循环。
[流程2: 关机逻辑]
步骤:
1. 接收到关机指令。
2. 检查寄存器 gpio_power_hold_cfg 的 bit5/6 是否为 1?
- 否: bit5=0 (3.3V LDO关闭); bit6=0 (5V VOUT关闭)。
- 是: bit5=1 (3.3V LDO保持); bit6=1 (5V VOUT保持)。
3. 执行操作:
(1) 3.3V DCDC 关闭。
(2) LED_EN 关闭。
(3) 充电使能不受影响。
[流程3: 下载模式]
步骤:
1. 进入下载模式。
2. 复位电源与gpio寄存器; 置位 power_gpio_hold 寄存器。
3. 下电。
4. BOOT_OUT 输出低电平。
5. 延时 300ms。
6. 上电。
7. 延时 500ms。
8. BOOT_OUT 输出高电平。
[流程4: 复位模式]
步骤:
1. 进入复位模式。
2. 复位电源与gpio寄存器; 置位 power_gpio_hold 寄存器。
3. 下电。
4. 延时 500ms。
5. 上电。
================================================================================
特殊功能细节与寄存器访问限制
================================================================================
[寄存器访问]
I2C Burst 读写支持连续读写特定区块:
0x00~0x0C, 0x10~0x19, 0x20~0x2A, 0x30~0x35, 0x38~0x3D, 0x40~0x45, 0x48~0x4A, 0x50、0x53, 0x60~0x9F, 0xA0~0xBF。
超出范围需分次操作。
[GPIO 限制]
1. GPIO1 与 SDA 共用中断线, 不可用干唤醒。
2. GPIO0/2 共用中断线; GPIO3/4 共用中断线 (均为互斥使用)。
3. GPIO_WAKE_EN (0x18) 和 GPIO_WAKE_CFG (0x19) 的配置不会因为进入下载模式、复位和关机而失效。
4. WAKE 是否启用上下拉由 GPIO_PU/PD_0 (0x14) 和 GPIO_PU/PD_1 (0x15) 控制。
5. GPIO_DRV (0x13) 输出类型拥有最高优先级, 不会随复用功能失效。默认为开漏, 即使复用为 PWM 或 Neopixel, 若配置为开漏则仍为开漏。
[电源事件]
IRQ Status 2 中的电池插入/移除事件仅在充电未使能 (CHG_EN=0) 时有效; 5VINOUT 事件仅在 INPUT 模式 (5VIN/OUT=0) 有效。
[NeoPixel 数据]
数据存储在 PIX0_L=0x60 ... PIX31_H=0x9F (共64字节)。刷新时 (REFRESH=1) 禁止 I2C 中断约 7ms。
[PWM 配置]
PWM0/1 的高低位配置分为两个字节, 需一次写入两个字节, 否则可能在极短时间内出现参数不一致。
[按键逻辑]
单击复位; 双击关机; 长按进入下载模式; 具体时间由 BTN_CFG_1 决定。
================================================================================
三、 寄存器映射表 (Register Map)
================================================================================
说明: 以下表格中 "Def" 代表默认值 (Default)。
Reset行为包括: 按键复位, 命令复位, I2C看门狗复位, 用户定时器复位。
关机行为包括: 按键关机, 命令关机, 用户定时器关机。
| Register | Type | Addr | Bits | R/W | Def | 描述 | Reset后状态 | Download后状态 | 关机后状态 |
|---|---|---|---|---|---|---|---|---|---|
| Device_ID | Sys | 0x00 | [7:0] | R | 0x50 | 设备类型 | - | - | - |
| Device_Model | Sys | 0x01 | [7:0] | R | 0x20 | 设备型号 | - | - | - |
| HW_REV | Sys | 0x02 | [7:0] | R | 0x05 | 硬件版本号 | - | - | - |
| SW_REV | Sys | 0x03 | [7:0] | R | 0x06 | 固件版本号 | - | - | - |
| PWR_SRC | Sys | 0x04 | [7:3] Rsv; [2:0] VALID | R | - | 电源来源位图 | - | - | - |
| WAKE_SRC | Sys | 0x05 | [7] Rsv; [6:0] FLAGS | R/W | - | 唤醒源标志 | - | - | - |
| PWR_CFG | Sys | 0x06 | [7:5] Rsv; [4] LED CTRL; [3] 5V IO; [2] 3V3 LDO; [1] 3V3 DCDC; [0] CHG EN | R/W | 0x17 | 电源管理位 | 0b00010011x (充电状态不受复位影响) | 0b00010011x (充电状态不受复位影响) | 0b00010011x (充电状态不受关机影响) |
| HOLD_CFG | Sys | 0x07 | [7] Rsv; [6] 5V IO; [5] 3V3 LDO; [4:0] GPIO4~0 | R/W | 0x00 | 置1则对应状态在关机后保留; 复位或下载模式时此寄存器复位为0 | 0x00 | 0x00 | - |
| BATT_LVP | Sys | 0x08 | [7:0] | R/W | 0x40 | 低压阈值: 2000mV + n*7.81mV | - | - | - |
| I2C_CFG | Sys | 0x09 | [7:4] Rsv; [4] SPD; [3:0] SLP_TO | R/W | 0x00 | SPD: 0=100k, 1=400k; SLP_TO: I2C超时休眠(秒)。PWM使能或下载模式时禁止休眠 | - | - | - |
| WDT_CNT | Sys | 0x0A | [7:0] | R/W | 0x00 | 看门狗倒计时(秒), 0=关闭 | - | - | - |
| WDT_KEY | Sys | 0x0B | [7:0] | W | - | 写 0xA5 清零/复位 | - | - | - |
| SYS_CMD | Sys | 0x0C | [7:4] KEY(0xA); [1:0] CMD | W | - | CMD: 01=关机, 10=重启, 11=下载 | - | - | - |
| GPIO_MODE | GPIO | 0x10 | [7:5] Rsv; [4:0] GPIO4~0 | R/W | 0x00 | 1=输出, 0=输入 (需GPIO_FUNC=00) | 由Hold寄存器决定 (1=保持, 0=复位) | 0x00 | 由Hold寄存器决定 |
| GPIO_OUT | GPIO | 0x11 | [7:5] Rsv; [4:0] GPIO4~0 | R/W | 0x00 | 写1=高电平 | 由Hold寄存器决定 | 0x00 | 由Hold寄存器决定 |
| GPIO_IN | GPIO | 0x12 | 同上 | R | - | 实时输入值 | - | - | - |
| GPIO_DRV | GPIO | 0x13 | [7:6] Rsv; [5] LED; [4:0] GPIO | R/W | 0x1F | 1=开漏, 0=推挽 | 由Hold寄存器决定 (LED不受影响) | 0x1F | 由Hold寄存器决定 |
| GPIO_PU/PD_0 | GPIO | 0x14 | [7:6] G3; [5:4] G2; [3:2] G1; [1:0] G0 | R/W | 0x00 | 00=无, 01=上拉, 10=下拉 | 由Hold寄存器决定 | 0x00 | 由Hold寄存器决定 |
| GPIO_PU/PD_1 | GPIO | 0x15 | [7:2] Rsv; [1:0] GPIO4 | R/W | 0x00 | 同上 | 由Hold寄存器决定 | 0x00 | 由Hold寄存器决定 |
| GPIO_FUNC0 | GPIO | 0x16 | [7:6] G3; [5:4] G2; [3:2] G1; [1:0] G0 | R/W | 0x00 | 00=GPIO, 01=IRQ, 11=特殊, 10=保留 | 由Hold寄存器决定 | 0x00 | 由Hold寄存器决定 |
| GPIO_FUNC1 | GPIO | 0x17 | [7:2] Rsv; [1:0] GPIO4 | R/W | 0x00 | 同上 | 由Hold寄存器决定 | 0x00 | 由Hold寄存器决定 |
| GPIO_WAKE_EN | GPIO | 0x18 | [7:5] Rsv; [4:0] GPIO4~0 | R/W | 0x00 | 1=使能WAKE; 注意中断线互斥规则 | - | - | - |
| GPIO_WAKE_CFG | GPIO | 0x19 | [7:5] Rsv; [4:0] GPIO4~0 | R/W | 0x00 | 1=上升沿, 0=下降沿 | - | - | - |
| VREF_L/H | ADC | 0x20 | [7:0] | R | - | MCU ADC 参考电压 (L:0x20, H:0x21) | - | - | - |
| VBAT_L/H | ADC | 0x22 | [7:0] | R | - | 电池电压 (L:0x22, H:0x23) | - | - | - |
| VIN_L/H | ADC | 0x24 | [7:0] | R | - | 5VIN 电压 (L:0x24, H:0x25) | - | - | - |
| 5VOUT_L/H | ADC | 0x26 | [7:0] | R | - | 5VOUT 电压 (L:0x26, H:0x27) | - | - | - |
| ADC_RES_L/H | ADC | 0x28 | [7:0] / [3:0] Data | R | - | ADC 转换结果 | - | - | - |
| ADC_CTRL | ADC | 0x2A | [3:1] CH_SEL; [0] START | R/W | 0x00 | START=1开始; CH_SEL: 1/2=GPIO, 6=Temp | - | - | - |
| PWM0_L/HC | PWM | 0x30 | [7:0] / [5] POL [4] EN | R/W | 0x00 | PWM0 控制 | - | - | - |
| PWM1_L/HC | PWM | 0x32 | [7:0] / [5] POL [4] EN | R/W | 0x00 | PWM1 控制 | - | - | - |
| PWM_FREQ_L/H | PWM | 0x34 | [7:0] | R/W | F4/01 | PWM 频率 (16bit) | - | - | - |
| TIM_CNT_BYTE | Tim | 0x38 | [7:0] (0x38-0x3B) | R/W | 0x00 | 定时唤醒计数器 (32bit, 秒) | - | - | - |
| TIM_CFG | Tim | 0x3C | [3] ARM; [2:0] ACTION | R/W | 0x00 | ACTION: 000=停, 001=标志, 010=重启, 011=上电, 100=关机 | - | - | - |
| TIM_KEY | Tim | 0x3D | [7:0] | W | - | 写 0xA5 清零重载 | - | - | - |
| IRQ Status 1 | IRQ | 0x40 | [4:0] GPIO4~0 | R/W | 0x00 | GPIO 电平变化 | - | - | - |
| IRQ Status 2 | IRQ | 0x41 | [5/4] Bat Rm/Add; [3/2] 5Vout Rm/Add; [1/0] 5Vin Rm/Add | R/W | 0x00 | 电源事件 (写1清零) | - | - | - |
| IRQ Status 3 | IRQ | 0x42 | [2] Dbl Click; [1] Wake; [0] Click | R/W | 0x00 | 按键/唤醒事件 (bit0=RST中断, bit1=Wakeup中断, bit2=双击中断) | - | - | - |
| IRQ Mask 1/2/3| IRQ | 0x43 | 对应 Status 位 | R/W | 0x00 | 写1屏蔽对应中断 (0x43~0x45) | - | - | - |
| BTN_Status | BTN | 0x48 | [7] Event; [0] Status | R | 0x00 | 按键状态 | - | - | - |
| BTN_CFG_1 | BTN | 0x49 | [7] Lock; [6:5] Dbl; [4:3] Long; [2:1] Single; [0] Sngl_Rst_Dis | R/W | 0x2A | 按键时间配置; bit0=1禁止单击复位 | - | - | - |
| BTN_CFG_2 | BTN | 0x4A | [0] Dbl_Off_Dis | R/W | 0x00 | bit0=1禁止双击关机 | - | - | - |
| NEO_CFG | RGB | 0x50 | [6] Refresh; [5:0] Count | R/W | 0x00 | NeoPixel控制 | - | - | - |
| PULSE_CTRL | Pulse| 0x53 | [7] Refresh; [6:5] Num; [4:0] GPIO | R/W | 0x00 | AW8737A 脉冲触发 | - | - | - |
| NEO_PIXn | RGB | 0x60 | [7:0] | R/W | 0x00 | 0x60-0x9F RGB数据缓存 | - | - | - |
| RTC_MEM | RTC | 0xA0 | [7:0] | R/W | 0x00 | 0xA0-0xBF RTC数据缓存 | - | - | - |
================================================================================
四、 关键寄存器功能详解
================================================================================
1. PWR_SRC (0x04) - 电源来源状态
- [2] BAT: 电池有效
- [1] 5VINOUT: 5VINOUT 有效 (仅当 5V 升压关闭时有效)
- [0] 5VIN: 5VIN 有效
- 注: 系统通过 ADC 检测电压判断电源来源。建议无电池时关闭电池充电。
2. WAKE_SRC (0x05) - 唤醒来源
- [6] 5V INOUT 插入唤醒 (仅当 5V 升压关闭时)
- [5] EXT_WAKE: GPIO 唤醒
- [4] CMD_RST: 复位命令
- [3] RSTBTN: 按键复位
- [2] PWRBTN: 电源按钮
- [1] VIN: 5VIN 插入
- [0] TIM: 定时器
3. HOLD_CFG (0x07) - 状态保持
- 将对应位设为 1, 则关机后对应的 GPIO、LDO 或 5VINOUT 状态会保持原样。
- 注意: 进入下载模式或触发任何 Reset (包括看门狗), 该寄存器会清零。
4. BATT_LVP (0x08) - 低压保护
- 低压阈值计算: 2.0 V + (reg_value * 7.81 mV)。
- 重新开机条件: 电池电压 > 阈值+100mV; 或插入 5VIN; 或插入 5VINOUT。
5. I2C_CFG (0x09) - I2C配置
- SPD: 0=100k, 1=400k。
- SLP_TO: 若设置非0, 代表 I2C 总线空闲多少秒后芯片休眠。
- 限制: PWM 使能或下载模式下, I2C 空闲休眠强制关闭。
6. GPIO_MODE / OUT / DRV
- GPIO_MODE: 1=输出, 0=输入。需 GPIO_FUNC 设置为 00 才生效。
- GPIO_OUT: 1=高, 0=低。
- GPIO_DRV: 1=开漏, 0=推挽。DRV 优先级最高, 若复用功能开启但 DRV=1, 仍为开漏。
7. ADC_CTRL (0x2A) - ADC控制
- CH_SEL: 通道选择。1=GPIO1, 2=GPIO2, 6=内部温度。
- 使用 GPIO 作 ADC 输入时, 必须将对应的 GPIO_FUNC 设为 11。
8. IRQ Status 注意事项
- 若没有 GPIO 被配置为 IRQ 引脚, Status 寄存器会自动清零。
- 若要使用 Wakeup IRQ (Status 3 bit1), 请勿清除 WAKE_SRC 寄存器, 直到重新配置 GPIO 为 IRQ 引脚。
================================================================================
五、 附加功能说明与变更记录 (Change Log)
================================================================================
[附加功能说明]
(1) 32-bit 定时器最大秒数限制为 214749364 秒。
(2) 关机时, GPIO 恢复为默认输入无上下拉; 电源恢复默认状态 (除非 HOLD_CFG 对应位为 1)。
(3) 进入下载模式, HOLD_CFG 清零, I2C 看门狗/休眠/用户定时器停止, GPIO 恢复默认。
(4) LED 提示逻辑: 复位闪烁1次; 下载模式 500ms 间隔闪烁; 屏蔽按键复位且有 IRQ 时 200ms 间隔闪烁; 屏蔽关机且有 IRQ 时 100ms 间隔闪烁。
(5) 充电功能默认打开。
[文档变更记录]
| 版本 | 日期 | 变更描述 |
|------|------------|----------|
| 1.0 | 2025-06-30 | 初始版本 |
| 1.1 | 2025-07-23 | 硬件修订: 3, 固件主版本: 2 |
| 1.2 | 2025-08-04 | 修订部分描述错误 |
| 1.3 | 2025-09-01 | 硬件修订: 4, 固件主版本: 3 |
| 1.4 | 2025-09-10 | 对关键寄存器详解进行了补充 |
| 1.5 | 2025-09-17 | 硬件修订: 5, 固件主版本: 4; (1) LED 指示灯在单击或双击中断触发指示; (2) 升级失败时 GPIO 和 bootout 设置为开漏; (3) 改用 ADC 采样判断 5VINOUT; (4) PIN1 逻辑修改。 |
| 1.6 | 2025-11-04 | 硬件修订: 5, 固件主版本: 5; (1) 添加 AW8737A 调频脉冲输出; (2) 添加 DCDC_5V 保持功能; (3) 看门狗默认关闭。 |
| 1.7 | 2025-12-13 | 硬件修订: 5, 固件主版本: 6; (1) 更新寄存器映射; (2) 新增 BTN_Status (0x48); (3) 移除 UID 寄存器, 新增 Device_ID/Model。 |
@@ -0,0 +1,188 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include <Arduino.h>
#include <M5PM1.h>
/*
* 基础电源/ADC示例:读取设备信息、电压、ADC/温度,并演示电源轨开关。
* Basic power/ADC demo: read device info, voltages, ADC/temp, and toggle rails.
*
* 接线:PM1通过I2C连接;若PM1_I2C_SDA/SCL为-1则使用默认Wire引脚。
* Wiring: PM1 via I2C; if PM1_I2C_SDA/SCL = -1, default Wire pins are used.
*
* ADC1=GPIO1、ADC2=GPIO2,使用前需将GPIO功能设为OTHER。
* ADC1=GPIO1, ADC2=GPIO2; set GPIO func to OTHER before ADC reads.
*
* 注意:切换DCDC/LDO/5VINOUT可能影响外接负载供电,请确认不会误断电。一般DCDC为芯片提供供电,若关闭DCDC则芯片有可能直接下电。
* Note: toggling DCDC/LDO/5VINOUT may affect external loads; typically DCDC powers the chip, disabling it may cause immediate power loss.
*/
M5PM1 pm1;
#define LOGI(fmt, ...) Serial.printf("[PM1][I] " fmt "\r\n", ##__VA_ARGS__)
#define LOGW(fmt, ...) Serial.printf("[PM1][W] " fmt "\r\n", ##__VA_ARGS__)
#define LOGE(fmt, ...) Serial.printf("[PM1][E] " fmt "\r\n", ##__VA_ARGS__)
#ifndef PM1_I2C_SDA
#define PM1_I2C_SDA 47
#endif
#ifndef PM1_I2C_SCL
#define PM1_I2C_SCL 48
#endif
#ifndef PM1_I2C_FREQ
#define PM1_I2C_FREQ M5PM1_I2C_FREQ_100K
#endif
static const uint8_t PWR_CFG_CHG = 0x01;
static const uint8_t PWR_CFG_DCDC = 0x02;
static const uint8_t PWR_CFG_LDO = 0x04;
static const uint8_t PWR_CFG_5V = 0x08;
static void printDivider() {
Serial.println("--------------------------------------------------");
}
static void printDeviceInfo() {
uint8_t id = 0;
uint8_t model = 0;
uint8_t hw = 0;
uint8_t sw = 0;
if (pm1.getDeviceId(&id) == M5PM1_OK) {
LOGI("Device ID: 0x%02X", id);
}
if (pm1.getDeviceModel(&model) == M5PM1_OK) {
LOGI("Device Model: 0x%02X", model);
}
if (pm1.getHwVersion(&hw) == M5PM1_OK) {
LOGI("HW Version: 0x%02X", hw);
}
if (pm1.getSwVersion(&sw) == M5PM1_OK) {
LOGI("SW Version: 0x%02X", sw);
}
}
static void printPowerConfig(uint8_t cfg) {
LOGI("Power CFG: CHG=%s DCDC=%s LDO=%s 5V=%s",
(cfg & PWR_CFG_CHG) ? "ON" : "OFF",
(cfg & PWR_CFG_DCDC) ? "ON" : "OFF",
(cfg & PWR_CFG_LDO) ? "ON" : "OFF",
(cfg & PWR_CFG_5V) ? "ON" : "OFF");
}
static void demoPowerRails() {
uint8_t cfg = 0;
if (pm1.getPowerConfig(&cfg) != M5PM1_OK) {
LOGW("Get power config failed");
return;
}
LOGI("Power rail demo start (only enable)");
printPowerConfig(cfg);
// 只开启不关闭。
// Only enable, do not disable.
pm1.setChargeEnable(true);
LOGI("Charge -> ON");
delay(300);
pm1.setDcdcEnable(true);
LOGI("DCDC -> ON");
delay(300);
pm1.setLdoEnable(true);
LOGI("LDO -> ON");
delay(300);
pm1.set5VInOutEnable(true);
LOGI("5V IN/OUT -> ON");
delay(300);
if (pm1.getPowerConfig(&cfg) == M5PM1_OK) {
printPowerConfig(cfg);
}
LOGI("Power rail demo done");
}
static void printVoltages() {
uint16_t mv = 0;
// 电压读数单位为 mV。
// Voltage readings are in mV.
if (pm1.readVref(&mv) == M5PM1_OK) {
LOGI("Vref: %u mV (%.3f V)", mv, mv / 1000.0f);
}
if (pm1.readVbat(&mv) == M5PM1_OK) {
LOGI("VBAT: %u mV (%.3f V)", mv, mv / 1000.0f);
}
if (pm1.readVin(&mv) == M5PM1_OK) {
LOGI("VIN: %u mV (%.3f V)", mv, mv / 1000.0f);
}
if (pm1.read5VInOut(&mv) == M5PM1_OK) {
LOGI("5V IN/OUT: %u mV (%.3f V)", mv, mv / 1000.0f);
}
}
static void printAdcAndTemp() {
uint16_t adc = 0;
uint16_t temp = 0;
// ADC1=GPIO1ADC2=GPIO2
// ADC1=GPIO1, ADC2=GPIO2
if (pm1.analogRead(M5PM1_ADC_CH_1, &adc) == M5PM1_OK) {
LOGI("ADC1 (GPIO1): %u", adc);
}
if (pm1.analogRead(M5PM1_ADC_CH_2, &adc) == M5PM1_OK) {
LOGI("ADC2 (GPIO2): %u", adc);
}
if (pm1.readTemperature(&temp) == M5PM1_OK) {
LOGI("Temp: %u", temp);
}
}
void setup() {
Serial.begin(115200);
delay(200);
printDivider();
LOGI("Basic + ADC + Power demo start");
m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PM1_I2C_SDA, PM1_I2C_SCL, PM1_I2C_FREQ);
if (err != M5PM1_OK) {
LOGE("PM1 begin failed: %d", err);
while (true) {
delay(1000);
}
}
printDeviceInfo();
printDivider();
demoPowerRails();
printDivider();
// ADC通道需要将GPIO1/2切到OTHER功能。
// ADC channels require GPIO1/2 to be set as OTHER function.
pm1.gpioSetFunc(M5PM1_GPIO_NUM_1, M5PM1_GPIO_FUNC_OTHER);
pm1.gpioSetFunc(M5PM1_GPIO_NUM_2, M5PM1_GPIO_FUNC_OTHER);
}
void loop() {
static uint32_t lastMs = 0;
const uint32_t intervalMs = 2000;
if (millis() - lastMs >= intervalMs) {
lastMs = millis();
// 每2秒输出一次电压、ADC、温度信息。
// Print voltages, ADC, and temperature every 2 seconds.
printVoltages();
printAdcAndTemp();
printDivider();
}
}
+125
View File
@@ -0,0 +1,125 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include <Arduino.h>
#include <M5PM1.h>
/*
* GPIO 输入/输出 + PWM 呼吸灯示例。
* GPIO input/output + PWM breathing LED demo.
*
* 接线建议:
* - GPIO0:接LED(串电阻)到GND,演示普通GPIO翻转。
* - GPIO1:接按键到GND(内部上拉),用于输入读取。
* - PWM_CH_0 对应 GPIO3:接LED(串电阻)到GND,演示PWM呼吸灯。
* Wiring suggestion:
* - GPIO0: LED (with resistor) to GND for GPIO toggling.
* - GPIO1: Button to GND (internal pull-up) for input reading.
* - PWM_CH_0 maps to GPIO3: LED (with resistor) to GND for PWM breathing.
*
* 注意:PWM 使用 GPIO3/4,且需要将引脚功能设为 OTHER。
* Note: PWM uses GPIO3/4 and requires GPIO func set to OTHER.
*
* 若使用 NeoPixel,请勿将 GPIO0 用作普通 GPIO。
* If using NeoPixel, do not use GPIO0 as a normal GPIO.
*/
M5PM1 pm1;
#define LOGI(fmt, ...) Serial.printf("[PM1][I] " fmt "\r\n", ##__VA_ARGS__)
#define LOGW(fmt, ...) Serial.printf("[PM1][W] " fmt "\r\n", ##__VA_ARGS__)
#define LOGE(fmt, ...) Serial.printf("[PM1][E] " fmt "\r\n", ##__VA_ARGS__)
#ifndef PM1_I2C_SDA
#define PM1_I2C_SDA 47
#endif
#ifndef PM1_I2C_SCL
#define PM1_I2C_SCL 48
#endif
#ifndef PM1_I2C_FREQ
#define PM1_I2C_FREQ M5PM1_I2C_FREQ_100K
#endif
static const m5pm1_gpio_num_t GPIO_OUT = M5PM1_GPIO_NUM_0;
static const m5pm1_gpio_num_t GPIO_IN = M5PM1_GPIO_NUM_1;
static const m5pm1_pwm_channel_t PWM_CH = M5PM1_PWM_CH_0;
static void printDivider() {
Serial.println("--------------------------------------------------");
}
void setup() {
Serial.begin(115200);
delay(200);
printDivider();
LOGI("GPIO + PWM demo start");
m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PM1_I2C_SDA, PM1_I2C_SCL, PM1_I2C_FREQ);
if (err != M5PM1_OK) {
LOGE("PM1 begin failed: %d", err);
while (true) {
delay(1000);
}
}
// GPIO0 作为普通输出脚。
// GPIO0 works as a normal output pin.
pm1.pinMode(GPIO_OUT, OUTPUT);
pm1.digitalWrite(GPIO_OUT, LOW);
// GPIO1 作为输入脚,内部上拉,按键接GND即可触发低电平。
// GPIO1 as input with pull-up; button to GND pulls it low.
pm1.pinMode(GPIO_IN, INPUT_PULLUP);
// PWM_CH_0 对应 GPIO3,需要设为 OTHER 才能输出PWM。
// PWM_CH_0 maps to GPIO3; set func to OTHER for PWM output.
// 使用 ANALOG 模式将引脚功能设为 OTHER (适用于 PWM/ADC)
// Use ANALOG mode to set pin function to OTHER (for PWM/ADC)
pm1.pinMode(M5PM1_GPIO_NUM_3, ANALOG);
// PWM 频率全通道共享。
// PWM frequency is shared across channels.
pm1.setPwmFrequency(1000);
pm1.setPwmDuty(PWM_CH, 0, false, true);
LOGI("GPIO OUT=%u, GPIO IN=%u, PWM CH=%u", GPIO_OUT, GPIO_IN, PWM_CH);
printDivider();
}
void loop() {
static uint32_t lastBlinkMs = 0;
static uint32_t lastLogMs = 0;
static uint32_t lastPwmMs = 0;
static bool ledOn = false;
static int duty = 0;
static int step = 2;
uint32_t now = millis();
if (now - lastBlinkMs >= 500) {
lastBlinkMs = now;
ledOn = !ledOn;
pm1.digitalWrite(GPIO_OUT, ledOn ? HIGH : LOW);
}
if (now - lastPwmMs >= 20) {
lastPwmMs = now;
duty += step;
if (duty >= 100) {
duty = 100;
step = -step;
} else if (duty <= 0) {
duty = 0;
step = -step;
}
pm1.setPwmDuty(PWM_CH, static_cast<uint8_t>(duty), false, true);
}
if (now - lastLogMs >= 1000) {
lastLogMs = now;
int inLevel = pm1.digitalRead(GPIO_IN);
LOGI("GPIO_IN=%d PWM_DUTY=%d%%", inLevel, duty);
}
}
@@ -0,0 +1,178 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include <Arduino.h>
#include <M5PM1.h>
/*
* 中断/按键/关机与唤醒示例。
* Interrupt/button/sleep & wake demo.
*
* IRQ_GPIO=GPIO2 用于外部中断/唤醒输入;GPIO0/2 共用线,勿同时启用唤醒。
* IRQ_GPIO=GPIO2 for external IRQ/wake; GPIO0/2 share a line, don't enable both.
*
* 双击电源键触发 PM1 关机(延迟5秒),10 秒后定时唤醒。
* Double-click power button triggers PM1 shutdown (5s delay); timer wakes after 10s.
*/
M5PM1 pm1;
#define LOGI(fmt, ...) Serial.printf("[PM1][I] " fmt "\r\n", ##__VA_ARGS__)
#define LOGW(fmt, ...) Serial.printf("[PM1][W] " fmt "\r\n", ##__VA_ARGS__)
#define LOGE(fmt, ...) Serial.printf("[PM1][E] " fmt "\r\n", ##__VA_ARGS__)
#ifndef PM1_I2C_SDA
#define PM1_I2C_SDA 47
#endif
#ifndef PM1_I2C_SCL
#define PM1_I2C_SCL 48
#endif
#ifndef PM1_I2C_FREQ
#define PM1_I2C_FREQ M5PM1_I2C_FREQ_100K
#endif
#ifndef PM1_ESP_IRQ_GPIO
#define PM1_ESP_IRQ_GPIO 13
#endif
volatile bool irqFlag = false;
void IRAM_ATTR pm1_irq_handler() {
irqFlag = true;
}
static const m5pm1_gpio_num_t IRQ_GPIO = M5PM1_GPIO_NUM_1;
static const uint32_t WAKE_TIMER_SEC = 10;
static void printDivider() {
Serial.println("--------------------------------------------------");
}
static void printWakeSource(uint8_t src) {
LOGI("Wake source mask: 0x%02X", src);
if (src & M5PM1_WAKE_SRC_TIM) LOGI("- TIMER");
if (src & M5PM1_WAKE_SRC_VIN) LOGI("- VIN");
if (src & M5PM1_WAKE_SRC_PWRBTN) LOGI("- PWR_BTN");
if (src & M5PM1_WAKE_SRC_RSTBTN) LOGI("- RST_BTN");
if (src & M5PM1_WAKE_SRC_CMD_RST) LOGI("- CMD_RESET");
if (src & M5PM1_WAKE_SRC_EXT_WAKE) LOGI("- EXT_WAKE");
if (src & M5PM1_WAKE_SRC_5VINOUT) LOGI("- 5VINOUT");
}
static void enterSleep() {
LOGW("Prepare for shutdown with wake sources");
// 先配置10s的定时开机
// Configure 10s timer wake up first
pm1.timerSet(WAKE_TIMER_SEC, M5PM1_TIM_ACTION_POWERON);
// 触发双击后等待5s关机
// Wait 5s before shutdown after double click
LOGW("Wait 5s before shutdown...");
delay(5000);
// 关闭LED_EN灯显(将默认电平配置为低电平)
// Turn off LED_EN indicator (by setting default level to LOW)
pm1.setLedEnLevel(false);
vTaskDelay(100 / portTICK_PERIOD_MS);
LOGW("Shutdown now. Wake by GPIO%u or %us timer", IRQ_GPIO, WAKE_TIMER_SEC);
pm1.shutdown();
}
void setup() {
Serial.begin(115200);
delay(200);
printDivider();
LOGI("Interrupt + Button + Sleep demo start");
m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PM1_I2C_SDA, PM1_I2C_SCL, PM1_I2C_FREQ);
if (err != M5PM1_OK) {
LOGE("PM1 begin failed: %d", err);
while (true) {
delay(1000);
}
}
// 读取并清除一次唤醒来源,便于调试本次启动原因。
// Read and clear wake source once for debugging this boot reason.
uint8_t wakeSrc = 0;
if (pm1.getWakeSource(&wakeSrc, M5PM1_CLEAN_ONCE) == M5PM1_OK) {
printWakeSource(wakeSrc);
}
// 清空timer设置,避免误触发。
// Clear timer settings to avoid mis-trigger.
pm1.timerClear();
// 禁用单击复位/双击关机,避免与示例流程冲突。
// Disable single-reset/double-off to avoid conflicts with demo flow.
pm1.setSingleResetDisable(true);
pm1.setDoubleOffDisable(true);
pm1.btnSetConfig(M5PM1_BTN_TYPE_CLICK, M5PM1_BTN_DELAY_250MS);
pm1.btnSetConfig(M5PM1_BTN_TYPE_DOUBLE, M5PM1_BTN_DELAY_250MS);
pm1.btnSetConfig(M5PM1_BTN_TYPE_LONG, M5PM1_BTN_DELAY_500MS);
pm1.gpioSetFunc(IRQ_GPIO, M5PM1_GPIO_FUNC_IRQ);
pm1.gpioSetMode(IRQ_GPIO, M5PM1_GPIO_MODE_INPUT);
pm1.gpioSetPull(IRQ_GPIO, M5PM1_GPIO_PULL_UP);
// 设置ESP32的中断引脚,用于接收PM1的IRQ信号。
// Setup ESP32 interrupt pin to receive IRQ signal from PM1.
pinMode(PM1_ESP_IRQ_GPIO, INPUT_PULLUP);
attachInterrupt(PM1_ESP_IRQ_GPIO, pm1_irq_handler, FALLING);
// 启用 PM1 GPIO1 的中断输出(这里的Mask Disable代表允许产生中断)。
// Enable PM1 GPIO1 IRQ output (Mask Disable means interrupt enabled).
pm1.irqSetGpioMask(IRQ_GPIO, M5PM1_IRQ_MASK_DISABLE);
// 启用按钮中断输出。
// Enable button IRQ output.
pm1.irqSetBtnMaskAll(M5PM1_IRQ_MASK_DISABLE);
pm1.irqSetGpioMaskAll(M5PM1_IRQ_MASK_ENABLE);
pm1.irqSetSysMaskAll(M5PM1_IRQ_MASK_ENABLE);
printDivider();
}
void loop() {
if (irqFlag) {
irqFlag = false;
uint8_t gpioIrq = 0;
if (pm1.irqGetGpioStatus(&gpioIrq, M5PM1_CLEAN_ONCE) == M5PM1_OK) {
if (gpioIrq != M5PM1_IRQ_GPIO_NONE) {
LOGI("GPIO IRQ status: 0x%02X", gpioIrq);
if (gpioIrq & M5PM1_IRQ_GPIO0) LOGI("- GPIO0 IRQ triggered");
if (gpioIrq & M5PM1_IRQ_GPIO1) LOGI("- GPIO1 IRQ triggered");
if (gpioIrq & M5PM1_IRQ_GPIO2) LOGI("- GPIO2 IRQ triggered");
if (gpioIrq & M5PM1_IRQ_GPIO3) LOGI("- GPIO3 IRQ triggered");
if (gpioIrq & M5PM1_IRQ_GPIO4) LOGI("- GPIO4 IRQ triggered");
}
}
uint8_t btnIrq = 0;
if (pm1.irqGetBtnStatus(&btnIrq, M5PM1_CLEAN_ONCE) == M5PM1_OK) {
if (btnIrq != M5PM1_IRQ_BTN_NONE) {
LOGI("Button IRQ status: 0x%02X", btnIrq);
if (btnIrq & M5PM1_IRQ_BTN_CLICK) LOGI("- Single Click triggered");
if (btnIrq & M5PM1_IRQ_BTN_DOUBLE) {
LOGI("- Double Click triggered");
enterSleep();
}
if (btnIrq & M5PM1_IRQ_BTN_WAKE) LOGI("- Wake Button triggered");
}
}
} else if (digitalRead(PM1_ESP_IRQ_GPIO) == LOW) {
// 存在未处理的中断信号,读取以清除(但忽略内容)
// Unhandled IRQ signal exists, read to clear (but ignore content)
uint8_t dummy;
pm1.irqGetGpioStatus(&dummy, M5PM1_CLEAN_ALL);
pm1.irqGetBtnStatus(&dummy, M5PM1_CLEAN_ALL);
pm1.irqGetSysStatus(&dummy, M5PM1_CLEAN_ALL);
}
delay(10);
}
+114
View File
@@ -0,0 +1,114 @@
/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include <Arduino.h>
#include <M5PM1.h>
/*
* NeoPixel 使 GPIO0 LED_EN/
* NeoPixel rainbow demo (uses GPIO0 for LED_EN/data-related function).
*
* NeoPixel GPIO0 LED 31 5-bit
* Note: NeoPixel is only supported on GPIO0, and LED count max is 31 (5-bit limit).
*/
M5PM1 pm1;
#define LOGI(fmt, ...) Serial.printf("[PM1][I] " fmt "\r\n", ##__VA_ARGS__)
#define LOGW(fmt, ...) Serial.printf("[PM1][W] " fmt "\r\n", ##__VA_ARGS__)
#define LOGE(fmt, ...) Serial.printf("[PM1][E] " fmt "\r\n", ##__VA_ARGS__)
#ifndef PM1_I2C_SDA
#define PM1_I2C_SDA 47
#endif
#ifndef PM1_I2C_SCL
#define PM1_I2C_SCL 48
#endif
#ifndef PM1_I2C_FREQ
#define PM1_I2C_FREQ M5PM1_I2C_FREQ_100K
#endif
static const uint8_t LED_COUNT = 8;
static const uint8_t BRIGHTNESS = 64;
static void printDivider() {
Serial.println("--------------------------------------------------");
}
static uint8_t scale(uint8_t v, uint8_t scale) {
// 通过亮度系数缩放颜色,避免过亮。
// Scale color by brightness to avoid excessive intensity.
return static_cast<uint8_t>((static_cast<uint16_t>(v) * scale) / 255);
}
static m5pm1_rgb_t wheel(uint8_t pos) {
pos = 255 - pos;
m5pm1_rgb_t c = {0, 0, 0};
if (pos < 85) {
c.r = 255 - pos * 3;
c.g = 0;
c.b = pos * 3;
} else if (pos < 170) {
pos -= 85;
c.r = 0;
c.g = pos * 3;
c.b = 255 - pos * 3;
} else {
pos -= 170;
c.r = pos * 3;
c.g = 255 - pos * 3;
c.b = 0;
}
c.r = scale(c.r, BRIGHTNESS);
c.g = scale(c.g, BRIGHTNESS);
c.b = scale(c.b, BRIGHTNESS);
return c;
}
void setup() {
Serial.begin(115200);
delay(200);
printDivider();
LOGI("NeoPixel demo start");
m5pm1_err_t err = pm1.begin(&Wire, M5PM1_DEFAULT_ADDR, PM1_I2C_SDA, PM1_I2C_SCL, PM1_I2C_FREQ);
if (err != M5PM1_OK) {
LOGE("PM1 begin failed: %d", err);
while (true) {
delay(1000);
}
}
// GPIO0 设置为 OTHER 以启用 NeoPixel;避免与 GPIO/IRQ/WAKE 冲突。
// Set GPIO0 to OTHER for NeoPixel; avoid GPIO/IRQ/WAKE conflicts.
pm1.gpioSetFunc(M5PM1_GPIO_NUM_0, M5PM1_GPIO_FUNC_OTHER);
// LED_EN 默认高电平使能灯带。
// LED_EN default high level enables LEDs.
pm1.setLedEnLevel(true);
// 设置灯带数量(1-31)。
// Set LED count (1-31).
pm1.setLedCount(LED_COUNT);
LOGI("LED count: %u", LED_COUNT);
printDivider();
}
void loop() {
static uint8_t offset = 0;
for (uint8_t i = 0; i < LED_COUNT; ++i) {
// 计算彩虹色,并写入缓存。
// Compute rainbow color and write to buffer.
m5pm1_rgb_t c = wheel(static_cast<uint8_t>(i * 256 / LED_COUNT + offset));
pm1.setLedColor(i, c);
}
// 刷新后才会显示到灯带。
// Refresh is required to apply colors to LEDs.
pm1.refreshLeds();
offset++;
delay(40);
}
View File
+737 -626
View File
File diff suppressed because it is too large Load Diff
+575 -526
View File
File diff suppressed because it is too large Load Diff
+57 -32
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
@@ -21,7 +21,8 @@
// ============================
#ifndef M5PM1_I2C_READ_BYTE
static inline bool M5PM1_I2C_READ_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t *data) {
static inline bool M5PM1_I2C_READ_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t *data)
{
wire->beginTransmission(addr);
wire->write(reg);
if (wire->endTransmission(false) != 0) {
@@ -36,7 +37,8 @@ static inline bool M5PM1_I2C_READ_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg,
#endif
#ifndef M5PM1_I2C_READ_BYTES
static inline bool M5PM1_I2C_READ_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len, uint8_t *data) {
static inline bool M5PM1_I2C_READ_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len, uint8_t *data)
{
wire->beginTransmission(addr);
wire->write(start_reg);
if (wire->endTransmission(false) != 0) {
@@ -53,7 +55,8 @@ static inline bool M5PM1_I2C_READ_BYTES(TwoWire *wire, uint8_t addr, uint8_t sta
#endif
#ifndef M5PM1_I2C_READ_REG16
static inline bool M5PM1_I2C_READ_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t *data) {
static inline bool M5PM1_I2C_READ_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t *data)
{
uint8_t buf[2];
if (!M5PM1_I2C_READ_BYTES(wire, addr, reg, 2, buf)) {
return false;
@@ -66,7 +69,8 @@ static inline bool M5PM1_I2C_READ_REG16(TwoWire *wire, uint8_t addr, uint8_t reg
#endif
#ifndef M5PM1_I2C_WRITE_BYTE
static inline bool M5PM1_I2C_WRITE_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t data) {
static inline bool M5PM1_I2C_WRITE_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg, uint8_t data)
{
wire->beginTransmission(addr);
wire->write(reg);
wire->write(data);
@@ -78,7 +82,9 @@ static inline bool M5PM1_I2C_WRITE_BYTE(TwoWire *wire, uint8_t addr, uint8_t reg
#endif
#ifndef M5PM1_I2C_WRITE_BYTES
static inline bool M5PM1_I2C_WRITE_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len, const uint8_t *data) {
static inline bool M5PM1_I2C_WRITE_BYTES(TwoWire *wire, uint8_t addr, uint8_t start_reg, size_t len,
const uint8_t *data)
{
wire->beginTransmission(addr);
wire->write(start_reg);
for (size_t i = 0; i < len; i++) {
@@ -92,7 +98,8 @@ static inline bool M5PM1_I2C_WRITE_BYTES(TwoWire *wire, uint8_t addr, uint8_t st
#endif
#ifndef M5PM1_I2C_WRITE_REG16
static inline bool M5PM1_I2C_WRITE_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t data) {
static inline bool M5PM1_I2C_WRITE_REG16(TwoWire *wire, uint8_t addr, uint8_t reg, uint16_t data)
{
uint8_t buf[2];
// 小端模式:低字节在前
// Little-endian: low byte first
@@ -105,7 +112,8 @@ static inline bool M5PM1_I2C_WRITE_REG16(TwoWire *wire, uint8_t addr, uint8_t re
// PM1睡眠模式唤醒信号
// Wake signal for PM1 sleep mode
#ifndef M5PM1_I2C_SEND_WAKE
static inline void M5PM1_I2C_SEND_WAKE(TwoWire *wire, uint8_t addr) {
static inline void M5PM1_I2C_SEND_WAKE(TwoWire *wire, uint8_t addr)
{
// Send START signal to generate SDA falling edge for PM1 wake
// PM1 uses SDA pin (PB4) falling edge to trigger EXTI4 interrupt for wakeup
wire->beginTransmission(addr);
@@ -113,11 +121,11 @@ static inline void M5PM1_I2C_SEND_WAKE(TwoWire *wire, uint8_t addr) {
}
#endif
#else // ESP-IDF
#else // ESP-IDF
#include <esp_err.h>
#include <driver/i2c_master.h> // ESP-IDF native i2c_master driver
#include <i2c_bus.h> // esp-idf-lib i2c_bus component
#include <i2c_bus.h> // esp-idf-lib i2c_bus component
#ifdef __cplusplus
extern "C" {
@@ -128,10 +136,10 @@ extern "C" {
// I2C Driver Type Selection
// ============================
typedef enum {
M5PM1_I2C_DRIVER_NONE = 0, // 未初始化 / Not initialized
M5PM1_I2C_DRIVER_SELF_CREATED, // 使用 i2c_port_t 自创建 / Self-created using i2c_port_t
M5PM1_I2C_DRIVER_MASTER, // ESP-IDF 原生 i2c_master 驱动 / ESP-IDF native i2c_master driver
M5PM1_I2C_DRIVER_BUS // esp-idf-lib i2c_bus 组件 / esp-idf-lib i2c_bus component
M5PM1_I2C_DRIVER_NONE = 0, // 未初始化 / Not initialized
M5PM1_I2C_DRIVER_SELF_CREATED, // 使用 i2c_port_t 自创建 / Self-created using i2c_port_t
M5PM1_I2C_DRIVER_MASTER, // ESP-IDF 原生 i2c_master 驱动 / ESP-IDF native i2c_master driver
M5PM1_I2C_DRIVER_BUS // esp-idf-lib i2c_bus 组件 / esp-idf-lib i2c_bus component
} m5pm1_i2c_driver_t;
// ============================
@@ -140,19 +148,22 @@ typedef enum {
// ============================
#ifndef M5PM1_I2C_READ_BYTE
static inline esp_err_t M5PM1_I2C_READ_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t *data) {
static inline esp_err_t M5PM1_I2C_READ_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t *data)
{
return i2c_bus_read_byte(dev, reg, data);
}
#endif
#ifndef M5PM1_I2C_READ_BYTES
static inline esp_err_t M5PM1_I2C_READ_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len, uint8_t *data) {
static inline esp_err_t M5PM1_I2C_READ_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len, uint8_t *data)
{
return i2c_bus_read_bytes(dev, start_reg, len, data);
}
#endif
#ifndef M5PM1_I2C_READ_REG16
static inline esp_err_t M5PM1_I2C_READ_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t *data) {
static inline esp_err_t M5PM1_I2C_READ_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t *data)
{
uint8_t buf[2];
esp_err_t ret = i2c_bus_read_bytes(dev, reg, 2, buf);
if (ret == ESP_OK) {
@@ -165,19 +176,23 @@ static inline esp_err_t M5PM1_I2C_READ_REG16(i2c_bus_device_handle_t dev, uint8_
#endif
#ifndef M5PM1_I2C_WRITE_BYTE
static inline esp_err_t M5PM1_I2C_WRITE_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t data) {
static inline esp_err_t M5PM1_I2C_WRITE_BYTE(i2c_bus_device_handle_t dev, uint8_t reg, uint8_t data)
{
return i2c_bus_write_byte(dev, reg, data);
}
#endif
#ifndef M5PM1_I2C_WRITE_BYTES
static inline esp_err_t M5PM1_I2C_WRITE_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len, const uint8_t *data) {
return i2c_bus_write_bytes(dev, start_reg, len, (uint8_t*)data);
static inline esp_err_t M5PM1_I2C_WRITE_BYTES(i2c_bus_device_handle_t dev, uint8_t start_reg, size_t len,
const uint8_t *data)
{
return i2c_bus_write_bytes(dev, start_reg, len, (uint8_t *)data);
}
#endif
#ifndef M5PM1_I2C_WRITE_REG16
static inline esp_err_t M5PM1_I2C_WRITE_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t data) {
static inline esp_err_t M5PM1_I2C_WRITE_REG16(i2c_bus_device_handle_t dev, uint8_t reg, uint16_t data)
{
uint8_t buf[2];
// 小端模式:低字节在前
// Little-endian: low byte first
@@ -193,19 +208,23 @@ static inline esp_err_t M5PM1_I2C_WRITE_REG16(i2c_bus_device_handle_t dev, uint8
// ============================
#ifndef M5PM1_I2C_MASTER_READ_BYTE
static inline esp_err_t M5PM1_I2C_MASTER_READ_BYTE(i2c_master_dev_handle_t dev, uint8_t reg, uint8_t *data) {
static inline esp_err_t M5PM1_I2C_MASTER_READ_BYTE(i2c_master_dev_handle_t dev, uint8_t reg, uint8_t *data)
{
return i2c_master_transmit_receive(dev, &reg, 1, data, 1, -1);
}
#endif
#ifndef M5PM1_I2C_MASTER_READ_BYTES
static inline esp_err_t M5PM1_I2C_MASTER_READ_BYTES(i2c_master_dev_handle_t dev, uint8_t start_reg, size_t len, uint8_t *data) {
static inline esp_err_t M5PM1_I2C_MASTER_READ_BYTES(i2c_master_dev_handle_t dev, uint8_t start_reg, size_t len,
uint8_t *data)
{
return i2c_master_transmit_receive(dev, &start_reg, 1, data, len, -1);
}
#endif
#ifndef M5PM1_I2C_MASTER_READ_REG16
static inline esp_err_t M5PM1_I2C_MASTER_READ_REG16(i2c_master_dev_handle_t dev, uint8_t reg, uint16_t *data) {
static inline esp_err_t M5PM1_I2C_MASTER_READ_REG16(i2c_master_dev_handle_t dev, uint8_t reg, uint16_t *data)
{
uint8_t buf[2];
esp_err_t ret = i2c_master_transmit_receive(dev, &reg, 1, buf, 2, -1);
if (ret == ESP_OK) {
@@ -218,17 +237,20 @@ static inline esp_err_t M5PM1_I2C_MASTER_READ_REG16(i2c_master_dev_handle_t dev,
#endif
#ifndef M5PM1_I2C_MASTER_WRITE_BYTE
static inline esp_err_t M5PM1_I2C_MASTER_WRITE_BYTE(i2c_master_dev_handle_t dev, uint8_t reg, uint8_t data) {
static inline esp_err_t M5PM1_I2C_MASTER_WRITE_BYTE(i2c_master_dev_handle_t dev, uint8_t reg, uint8_t data)
{
uint8_t buf[2] = {reg, data};
return i2c_master_transmit(dev, buf, 2, -1);
}
#endif
#ifndef M5PM1_I2C_MASTER_WRITE_BYTES
static inline esp_err_t M5PM1_I2C_MASTER_WRITE_BYTES(i2c_master_dev_handle_t dev, uint8_t start_reg, size_t len, const uint8_t *data) {
static inline esp_err_t M5PM1_I2C_MASTER_WRITE_BYTES(i2c_master_dev_handle_t dev, uint8_t start_reg, size_t len,
const uint8_t *data)
{
// 需要在数据前添加寄存器地址
// Need to prepend register address
uint8_t *buf = (uint8_t*)malloc(len + 1);
uint8_t *buf = (uint8_t *)malloc(len + 1);
if (buf == NULL) return ESP_ERR_NO_MEM;
buf[0] = start_reg;
memcpy(buf + 1, data, len);
@@ -239,7 +261,8 @@ static inline esp_err_t M5PM1_I2C_MASTER_WRITE_BYTES(i2c_master_dev_handle_t dev
#endif
#ifndef M5PM1_I2C_MASTER_WRITE_REG16
static inline esp_err_t M5PM1_I2C_MASTER_WRITE_REG16(i2c_master_dev_handle_t dev, uint8_t reg, uint16_t data) {
static inline esp_err_t M5PM1_I2C_MASTER_WRITE_REG16(i2c_master_dev_handle_t dev, uint8_t reg, uint16_t data)
{
uint8_t buf[3];
buf[0] = reg;
// 小端模式:低字节在前
@@ -253,7 +276,8 @@ static inline esp_err_t M5PM1_I2C_MASTER_WRITE_REG16(i2c_master_dev_handle_t dev
// 使用i2c_bus的PM1睡眠模式唤醒信号
// Wake signal for PM1 sleep mode using i2c_bus
#ifndef M5PM1_I2C_SEND_WAKE
static inline esp_err_t M5PM1_I2C_SEND_WAKE(i2c_bus_device_handle_t dev, uint8_t reg) {
static inline esp_err_t M5PM1_I2C_SEND_WAKE(i2c_bus_device_handle_t dev, uint8_t reg)
{
// Read any register to generate I2C start signal for wake
uint8_t dummy;
return i2c_bus_read_byte(dev, reg, &dummy);
@@ -263,7 +287,8 @@ static inline esp_err_t M5PM1_I2C_SEND_WAKE(i2c_bus_device_handle_t dev, uint8_t
// 使用i2c_master的PM1睡眠模式唤醒信号
// Wake signal for PM1 sleep mode using i2c_master
#ifndef M5PM1_I2C_MASTER_SEND_WAKE
static inline esp_err_t M5PM1_I2C_MASTER_SEND_WAKE(i2c_master_bus_handle_t bus, uint8_t addr) {
static inline esp_err_t M5PM1_I2C_MASTER_SEND_WAKE(i2c_master_bus_handle_t bus, uint8_t addr)
{
// Use i2c_master_probe to generate START signal for wake
return i2c_master_probe(bus, addr, 10);
}
@@ -273,6 +298,6 @@ static inline esp_err_t M5PM1_I2C_MASTER_SEND_WAKE(i2c_master_bus_handle_t bus,
}
#endif
#endif // ARDUINO
#endif // ARDUINO
#endif // __M5PM1_I2C_COMPAT_H__
#endif // __M5PM1_I2C_COMPAT_H__