Skip to content
This repository was archived by the owner on Oct 28, 2026. It is now read-only.

Latest commit

 

History

History
814 lines (594 loc) · 20.3 KB

papers3_hardware_api.md

File metadata and controls

814 lines (594 loc) · 20.3 KB

M5PaperS3 硬件底层与 API 参考手册

目标平台: M5Stack PaperS3 (ESP32-S3R8) 显示器: ED047TC1 4.7" EPD 墨水屏 (960×540, 16级灰度) 框架: UIFlow MicroPython (基于 M5Unified + M5GFX) 源码: m5stack/uiflow-micropython + lovyan03/M5GFX


目录

  1. 硬件规格与引脚映射
  2. M5 模块总览
  3. M5.Display — M5GFX 图形库
  4. M5.Touch — 触摸系统
  5. M5.Btn — 物理按键
  6. M5.Power — 电源管理
  7. M5.Imu — 陀螺仪 (BMI270)
  8. M5.Spk — 蜂鸣器
  9. M5.Mic — 麦克风 (如已启用)
  10. M5.Board — 板级识别
  11. WiFi — 网络
  12. machine.SDCard — SD 卡
  13. UIFlow 启动框架 (PaperS3)
  14. 完整示例

1. 硬件规格与引脚映射

1.1 硬件规格

项目 规格
SKU C139
SoC ESP32-S3R8 (Xtensa LX7 双核, 240MHz)
PSRAM 8MB Octal (OPI)
Flash 16MB (外部 SPI, Quad I/O)
显示屏 4.7" EPD_ED047TC1, 960×540, 16级灰度
触控 GT911 电容触摸 (两指, 多种手势)
传感器 BMI270 陀螺仪 (I2C 0x68)
RTC BM8563 (I2C 0x51)
电池 1800mAh 锂电池
蜂鸣器 无源蜂鸣器 (PWM)
microSD SPI 模式 (20MHz)
USB Type-C (OTG/CDC/MSC/烧录)

1.2 EPD 显示屏 (8-bit 并口)

EPD 信号 ESP32-S3 GPIO
DB0~DB7 GPIO6,14,7,12,9,11,8,10
XSTL GPIO13
XLE GPIO15
SPV GPIO17
CKV GPIO18
PWR GPIO45

1.3 I2C 总线 (共享: SCL=GPIO2, SDA=GPIO1)

外设 I2C 地址 用途
BM8563 RTC 0x51 时钟、休眠唤醒
GT911 触摸 — 电容触摸面板
BMI270 陀螺仪 0x68 运动检测

1.4 microSD 卡 (SPI)

SD 信号 ESP32-S3 GPIO
CS GPIO47
SCK GPIO39
MOSI GPIO38
MISO GPIO40

1.5 其他引脚

功能 GPIO 说明
BUZ_PWM GPIO21 无源蜂鸣器
USB_DET GPIO5 USB 供电检测 (>0.2V)
外扩接口 GPIO1, GPIO2 HC1.25-4PLT

2. M5 模块总览

所有外设通过 M5 单例对象访问(M5.begin() 初始化后可用):

模块 访问路径 用途
图形 M5.Display.* M5GFX 绘图库
触摸 M5.Touch.* GT911 电容触摸
按键 M5.BtnA/BtnB/BtnC/* (PaperS3 无独立按键)
电源 M5.Power.* 电池、充电、休眠
IMU M5.Imu.* BMI270 陀螺仪
喇叭 M5.Spk.* 蜂鸣器
麦克风 M5.Mic.* (视固件版本)
板级 M5.Board 主板识别常量

3. M5.Display — M5GFX 图形库

M5GFX 是 M5Stack 的底层图形库,基于 LovyanGFX。所有颜色使用 24-bit RGB 格式 (0xRRGGBB),墨水屏自动映射到 16 级灰度。

3.1 显示控制

M5.Display.startWrite()

M5.Display.endWrite()

成对使用的批量绘制函数。startWrite() 后所有绘图操作被缓冲,调用 endWrite() 才刷入显示缓冲区并触发 EPD 刷新。对墨水屏至关重要,能减少闪烁和刷新次数。

M5.Display.startWrite()
M5.Display.fillRect(0, 0, 960, 540, 0xFFFFFF)
M5.Display.drawString("Hello", 10, 10)
M5.Display.endWrite()

M5.Display.show()

将帧缓冲区内容刷新到实际显示屏幕。某些设备需要显式调用。

M5.Display.clear()

清除整个屏幕内容,填充默认背景色(白色)。

M5.Display.fillScreen(color)

用指定颜色填充整个屏幕。

M5.Display.close()

关闭显示资源,程序结束时调用。

width() → int

height() → int

返回屏幕的宽度和高度(像素)。

  • PaperS3 默认: 960 × 540 (ESPDIY 驱动)
  • 注意: UIFlow 启动应用是竖屏设计 (旋转后 540 × 960)

getRotation() → int

setRotation(rotation)

获取/设置屏幕旋转角度。参数范围 0~7:

  • 0 = 270°
  • 1 = 正常 (默认)
  • 2 = 90°
  • 3 = 180°
  • 4~7 = 上下颠倒变体

getColorDepth() → int

setColorDepth(depth)

获取/设置颜色深度。常见值: 1 (单色), 8 (256色), 16 (65K色), 24 (1677万色)。

getBrightness() → int

setBrightness(brightness)

获取/设置亮度(墨水屏通常不支持调亮,函数返回固定值)。


3.2 EPD 墨水屏专用

M5.Display.isEPD() → bool

返回 True(PaperS3 是电子墨水屏设备)。

M5.Display.getEpdMode() → int

获取当前 EPD 刷新模式。

M5.Display.setEpdMode(mode)

设置墨水屏刷新模式。

模式 值 说明
EPD_QUALITY 1 最高质量,刷新最慢 (~2-3s)
EPD_TEXT 2 文本模式,兼顾速度与质量
EPD_FAST 3 快速刷新
EPD_FASTEST 4 最快刷新,质量最低

建议: 开发阶段用 EPD_FASTEST(4) 减少闪烁;最终显示用 EPD_QUALITY(1)。


3.3 基本绘图

像素与线条

M5.Display.drawPixel(x, y, color)          # 单像素
M5.Display.drawLine(x0, y0, x1, y1, color) # 直线

矩形

M5.Display.drawRect(x, y, w, h, color)       # 空心矩形
M5.Display.fillRect(x, y, w, h, color)       # 实心矩形
M5.Display.drawRoundRect(x, y, w, h, r, color)   # 圆角空心
M5.Display.fillRoundRect(x, y, w, h, r, color)   # 圆角实心
  • r: 圆角半径(像素)

圆形与椭圆

M5.Display.drawCircle(x, y, r, color)         # 空心圆
M5.Display.fillCircle(x, y, r, color)         # 实心圆
M5.Display.drawEllipse(x0, y0, x1, y1, color) # 空心椭圆
M5.Display.fillEllipse(x0, y0, x1, y1, color) # 实心椭圆
M5.Display.drawEllipseArc(x0, y0, x1, y1, start, end, color)
M5.Display.fillEllipseArc(x0, y0, x1, y1, start, end, color)
  • start, end: 角度(度)

三角形

M5.Display.drawTriangle(x0, y0, x1, y1, x2, y2, color)
M5.Display.fillTriangle(x0, y0, x1, y1, x2, y2, color)

弧线

M5.Display.drawArc(x, y, r, start_angle, end_angle, color)
M5.Display.fillArc(x, y, r, start_angle, end_angle, color)

QR 码

M5.Display.drawQR(text, x, y, size, color)

3.4 文字处理

文字绘制

M5.Display.drawString(text, x, y)            # 左对齐
M5.Display.drawCenterString(text, x, y)      # 居中
M5.Display.drawRightString(text, x, y)       # 右对齐
  • 对齐以 (x, y) 为参考点

文字属性

M5.Display.setTextColor(fg_color)            # 仅前景色
M5.Display.setTextColor(fg_color, bg_color)  # 前景+背景(不透明)
M5.Display.setTextSize(size)                 # 缩放倍数 (1=原生大小)
M5.Display.setFont(font)                     # 设置字体(内置或自定义)
M5.Display.setCursor(x, y)                   # 设置光标位置
M5.Display.getCursor() → (x, y)              # 获取光标

# .vlw 字体加载
M5.Display.loadFont(path)                    # 从文件加载 .vlw 字体
M5.Display.setFont(path)                     # 另一种设置方式
M5.Display.unloadFont()                      # 卸载当前字体

关于 textSize: textSize(1) 对应 .vlw 字体的原生像素大小(例如 HarmonyOS_Sans_SC_Regular_28.vlw 就是 28px)。如果有 _28.vlw,默认应该用 textSize(1) 而非 2。

文字度量

M5.Display.textWidth(text) → int            # 文字像素宽度
M5.Display.fontHeight() → int               # 当前字体高度

打印模式

M5.Display.print(text)                       # 在光标位置输出
M5.Display.printf(format, *args)             # 格式化输出
M5.Display.setTextScroll(enable)             # 文字滚动模式

3.5 图像绘制

PNG (支持透明通道)

M5.Display.drawPng(filename, x, y)

JPEG

M5.Display.drawJpg(filename, x, y)

BMP

M5.Display.drawBmp(filename, x, y)

内存图像

M5.Display.drawImage(data, x, y, w, h)       # 像素数据
M5.Display.drawRawBuf(x, y, data, w, h)      # 原始缓冲区

3.6 画布 (Sprite)

canvas = M5.Display.newCanvas(width, height)  # 创建离屏画布
# 在画布上绘图(与 Display 方法相同)
canvas.fillRect(0, 0, 100, 50, 0x000000)
canvas.drawString("Sprite", 10, 10)
# 渲染到屏幕
M5.Display.drawImage(canvas, x, y, w, h)

适用场景: 预先渲染复杂图形,减少屏幕刷新次数,提升动画流畅度。


3.7 像素数据读写

M5.Display.readData(x, y) → bytes
M5.Display.readData16(x, y) → int   # RGB565 格式 (0~65535)
M5.Display.readData32(x, y) → int   # 32-bit 格式

M5.Display.writeData(data)          # 写入原始数据
M5.Display.writeData16(value)       # 写入16位颜色
M5.Display.writeData32(value)       # 写入32位颜色

3.8 低级命令

M5.Display.writeCommand(cmd)         # 发送命令字节
M5.Display.writeCommand16(cmd)       # 发送16位命令

普通应用不需要直接使用,用于访问显示控制器的特殊功能。


4. M5.Touch — 触摸系统

基于 GT911 电容触摸控制器。触摸数据通过 M5.Touch 读取,在 M5.update() 中自动轮询。

API 函数

M5.Touch.getCount() → int

返回当前触摸点数(0 = 无触摸,≥1 = 有触摸)。

M5.Touch.getX() → int

获取最新触摸点的 X 坐标。

M5.Touch.getY() → int

获取最新触摸点的 Y 坐标。

M5.Touch.getDetail(index) → int

获取指定触摸点的详细状态。detail 值的含义:

值 含义 说明
0 按下 手指首次触摸屏幕
1 释放 手指离开屏幕
2 长按 持续按住超过阈值时间

注意: getDetail(0) 返回的 detail 值的[9]位用于判断 isHolding(UIFlow framework 中实现)。

M5.Touch.getTouchPointRaw() → (int, int)

获取原始触摸坐标(可能未经校准)。

典型触摸处理

def handle_touch():
    if M5.Touch.getCount() == 0:
        return
    x = M5.Touch.getX()
    y = M5.Touch.getY()
    detail = M5.Touch.getDetail(0)
    
    if detail == 0:      # 按下
        print(f"Touch down at ({x}, {y})")
    elif detail == 1:    # 释放
        print(f"Touch up at ({x}, {y})")
    elif detail == 2:    # 长按
        print(f"Long press at ({x}, {y})")

5. M5.Btn — 物理按键

PaperS3 只有一个物理按键(电源/复位/下载),没有独立的 A/B/C 按键。

API 函数 (理论上可用)

M5.BtnA.isPressed() → bool
M5.BtnA.isReleased() → bool
M5.BtnA.isHolding() → bool
M5.BtnA.wasClicked() → bool
M5.BtnA.wasPressed() → bool
M5.BtnA.wasReleased() → bool
M5.BtnA.wasHold() → bool
M5.BtnA.wasSingleClicked() → bool
M5.BtnA.wasChangePressed() → bool
M5.BtnA.lastChange() → int  # 毫秒时间戳

实际: PaperS3 上的物理按键主要用于电源/固件下载模式切换。UIFlow 启动框架中通过触摸侧边栏操作,不依赖物理按键。


6. M5.Power — 电源管理

PaperS3 没有传统电源管理芯片 (AXP192/AXP2101),电池管理由 PMS150G + LGS4056H 独立处理。

API 函数

电池信息

M5.Power.getBatteryLevel() → int       # 电池电量百分比 (0~100)
M5.Power.getBatteryVoltage() → float   # 电池电压 (mV)
M5.Power.getBatteryCurrent() → float   # 电池电流 (mA)
M5.Power.isCharging() → bool           # 是否正在充电

休眠控制

M5.Power.powerOff()                    # 关机
M5.Power.deepSleep()                   # 深度休眠
M5.Power.lightSleep()                  # 浅度休眠
M5.Power.timerSleep(ms)               # 定时休眠

充电控制

M5.Power.setBatteryCharge(enable)      # 启用/禁用充电
M5.Power.setChargeCurrent(ma)          # 设置充电电流 (mA)
M5.Power.setChargeVoltage(mv)          # 设置充电电压 (mV)

外部输出

M5.Power.setExtOutput(enable)          # 外部引脚输出
M5.Power.getExtOutput() → bool
M5.Power.setUsbOutput(enable)          # USB 输出
M5.Power.getUsbOutput() → bool

LED 与振动

M5.Power.setLed(brightness)            # 设置 LED 亮度 (0~255)
M5.Power.setVibration(enable)          # 振动马达 (如支持)

其他

M5.Power.getKeyState() → int           # 按键状态
M5.Power.getType() → int               # 电源芯片类型
M5.Power.getPortVbus() → int           # VBUS 电压
M5.Power.getPortCurrent() → int        # 端口电流

7. M5.Imu — 陀螺仪 (BMI270)

BMI270 6-axis IMU (加速度计 + 陀螺仪),I2C 地址 0x68。

API 函数

M5.Imu.getAccel() → (x, y, z)         # 加速度 (m/s²)
M5.Imu.getGyro() → (x, y, z)          # 角速度 (°/s)
M5.Imu.getMag() → (x, y, z)           # 地磁 (如支持)
M5.Imu.isEnabled() → bool             # 是否已启用
M5.Imu.getType() → int                # IMU 芯片型号

典型用法

accel = M5.Imu.getAccel()
print(f"Accel: x={accel[0]:.2f}, y={accel[1]:.2f}, z={accel[2]:.2f}")

8. M5.Spk — 蜂鸣器

PaperS3 的无源蜂鸣器使用 PWM 驱动 (GPIO21)。

API 函数

初始化与状态

M5.Spk.begin()                         # 初始化
M5.Spk.end()                           # 关闭
M5.Spk.config(sample_rate, stereo, ...) # 配置
M5.Spk.isRunning() → bool
M5.Spk.isEnabled() → bool
M5.Spk.isPlaying() → bool
M5.Spk.getPlayingChannels() → int
M5.Spk.stop()                          # 停止播放

音量控制

M5.Spk.setVolume(volume)               # 0~255
M5.Spk.getVolume() → int
M5.Spk.getVolumePercentage() → float   # 0.0~1.0
M5.Spk.setAllChannelVolume(volume)     # 所有通道音量
M5.Spk.setChannelVolume(ch, volume)    # 指定通道
M5.Spk.getChannelVolume(ch) → int

播放

M5.Spk.tone(frequency, duration_ms)    # 单音
M5.Spk.playRaw(data, len, ...)         # 播放原始 PCM 数据
M5.Spk.playWav(data, ...)              # 播放 WAV 数据(内存)
M5.Spk.playWavFile(path)               # 播放 WAV 文件

典型用法

M5.Spk.tone(440, 200)      # 播放 A4 音 200ms
time.sleep_ms(100)
M5.Spk.tone(880, 200)      # 播放 A5 音 200ms

9. M5.Mic — 麦克风

取决于固件是否启用。PaperS3 硬件上无内置麦克风,此模块仅供兼容。

M5.Mic.config(sample_rate, ...)        # 配置
M5.Mic.begin()                         # 开始录音
M5.Mic.end()                           # 停止
M5.Mic.isRunning() → bool
M5.Mic.isEnabled() → bool

10. M5.Board — 板级识别

M5.getBoard()                          # 返回 board ID
M5.Board.M5PaperS3                     # = 19 (常量)

典型用法

board = M5.getBoard()
if board == M5.Board.M5PaperS3:
    print("Running on M5PaperS3!")

# 或直接:
import M5
board_id = M5.Board.M5PaperS3   # 值为 19

UIFlow 启动示例

from startup import startup
startup(boot_opt=2, timeout=60)  # 2 = BOOT_OPT_MENU_NET

11. WiFi — 网络

PaperS3 使用 ESP32-S3 内置 WiFi (2.4GHz, 802.11 b/g/n)。

import network
wlan = network.WLAN(network.STA_IF)    # Station 模式
wlan.active(True)
wlan.connect("SSID", "password")
while not wlan.isconnected():
    pass
print("IP:", wlan.ifconfig()[0])

UIFlow 有内建的 Startup 类封装网络连接:

from startup import Startup
wlan = Startup()
wlan.connect_network(ssid, pswd, timeout=60)

12. machine.SDCard — SD 卡

SPI 模式,20MHz,引脚 GPIO47/39/38/40。

import machine, os

sd = machine.SDCard(
    slot=3,                   # SPI 模式
    sck=39, miso=40,
    mosi=38, cs=47,
    freq=20000000
)
os.mount(sd, "/sd")           # 挂载到 /sd
print(os.listdir("/sd"))      # 列出文件
os.umount("/sd")              # 卸载

注意: UIFlow 的 machine.SDCard 使用自定义实现,slot=3 表示 SPI 模式,slot=1 表示 SDMMC 1-bit 模式。


13. UIFlow 启动框架 (PaperS3)

UIFlow 固件内置的启动应用框架,使用 papers3/ 目录下的模块。

启动流程

M5.begin()
  → startup() 调度到 PaperS3_Startup
    → WiFi 连接
    → 显示 startup.png 启动画面
    → Framework 启动:
       - StatusBarApp (状态栏: WiFi/云连接)
       - DevApp (开发者信息)
       - SettingsApp (WiFi 配置)
       - ListApp (文件浏览器/启动器)

AppBase 生命周期

install → launch → view → ready (async run loop) → hide → exit → uninstall
阶段 回调 说明
安装 on_install() 设置触摸热区描述符
启动 on_launch() 初始化数据
显示 on_view() 绘制界面
就绪 on_ready() 创建 async task
运行 on_run() async 循环 (每 500ms)
隐藏 on_hide() 取消 async task

Framework API

from modules.startup.papers3 import framework

fw = framework.Framework()
fw.install(app)              # 安装应用
fw.install_bar(app)          # 安装状态栏
fw.install_launcher(app)     # 安装启动器
fw.start()                   # 启动框架主循环

触摸分发: Framework 检测触摸 → 判断是否在应用热区内 → 切换到对应应用 → 派发 _click_event_handler(x, y, fw)。

触摸消抖: 150ms。

键盘支持: CardKB (I2C 0x5F) 左右键切换应用。


14. 完整示例

基础绘图示例

import M5
import time

def setup():
    M5.begin()
    
    M5.Display.startWrite()
    
    # 清屏
    M5.Display.fillScreen(0xFFFFFF)
    
    # 绘制矩形
    M5.Display.fillRect(20, 20, 200, 100, 0x000000)
    M5.Display.drawRect(20, 140, 200, 100, 0x000000)
    
    # 圆角矩形
    M5.Display.fillRoundRect(260, 20, 200, 100, 20, 0x333333)
    M5.Display.drawRoundRect(260, 140, 200, 100, 20, 0x000000)
    
    # 圆形
    M5.Display.fillCircle(120, 340, 60, 0x000000)
    M5.Display.drawCircle(120, 380, 60, 0x666666)
    
    # 文字
    M5.Display.setTextColor(0x000000)
    M5.Display.setTextSize(2)
    M5.Display.drawString("Hello PaperS3!", 100, 480)
    
    # 线条
    M5.Display.drawLine(0, 530, 540, 530, 0x000000)
    
    M5.Display.endWrite()

def loop():
    M5.update()
    time.sleep_ms(100)

EPD 模式切换示例

import M5

# 设置快速刷新(开发调试用)
M5.Display.setEpdMode(4)   # EPD_FASTEST

# 检查是否为 EPD 屏
if M5.Display.isEPD():
    print("EPD display detected")
    print(f"Current mode: {M5.Display.getEpdMode()}")

触摸 + 蜂鸣器反馈

import M5
import time

def setup():
    M5.begin()
    M5.Display.setEpdMode(4)

def loop():
    M5.update()
    
    if M5.Touch.getCount() > 0:
        x = M5.Touch.getX()
        y = M5.Touch.getY()
        detail = M5.Touch.getDetail(0)
        
        if detail == 0:  # 按下
            # 蜂鸣器反馈
            M5.Spk.tone(880, 50)
            
            # 绘制触摸点
            M5.Display.fillCircle(x, y, 10, 0x000000)
    
    time.sleep_ms(20)

SD 卡 + WiFi 示例

import machine
import os
import network

# 挂载 SD 卡
sd = machine.SDCard(slot=3, sck=39, miso=40,
                    mosi=38, cs=47, freq=20000000)
os.mount(sd, "/sd")

# 读取文件
with open("/sd/config.txt", "r") as f:
    print(f.read())

# 连接 WiFi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("SSID", "password")

文档生成: 基于 m5stack/uiflow-micropython 源码中 cmodules/m5unified/ 和 components/M5Unified/ 的 C 绑定代码, 以及 M5GFX 库源码分析。最后更新: 2026-05-23