RP2350-ePaper-1.54 使用教程

产品特性

  • 采用了 Raspberry Pi 研发的 RP2350A 微控制器芯片
  • 独特的双核、双架构,搭载了双核 ARM Cortex-M33 处理器和双核 Hazard3 RISC-V 处理器,时钟运行频率高达 150MHz,支持用户在两种架构间灵活切换
  • 内置 520KB 的 SRAM 和 16MB 的片上 Flash
  • 采用 Type-C 接口,紧跟时代潮流,无需纠结正反插
  • 板载 1.54 英寸电子墨水屏,分辨率 200 × 200,具备高对比度、宽视角等特性
  • 支持 USB1.1 主机和设备
  • 支持低功耗睡眠和休眠模式
  • 可通过 USB 识别为大容量存储器进行拖放式下载
  • RP2350A 的 7 个 GPIO 引脚,USB+/- 引脚引出
  • 1 个 I2C,1 个 UART,2 个 12 位 ADC,以及 6 个可控 PWM 通道
  • 精确的片上时钟和定时器
  • 内置温度传感器,可实时监测芯片温度
  • 12 个可编程 I/O (PIO) 状态机,用于自定义外设支持

板载资源


  1. RP2350 双核、双架构处理器,运行频率高达 150MHz
  2. Micro SD 卡座 支持 Micro SD 卡扩展存储
  3. BOOT 按键 复位时按下可进入下载模式
  4. 电池充放电管理芯片 用于锂电池充放电管理
  5. PWR 电源按键 用于电源控制
  6. SHTC3 温湿度传感器 提供环境温湿度测量,便于实现环境监测功能
  7. USB Type-C 接口 用于程序下载,支持 USB 1.1 主机和从设备模式
  8. 麦克风 用于音频采集
  9. MX1.25 锂电池接口 可用于接入 3.7V 锂电池,支持充放电,峰值充电电流 500mA
  10. ES8311 低功耗音频编解码芯片
  11. NS4150B 音频功率放大器芯片
  12. 2 × 6PIN 2.54mm 间距排母 用于外接扬声器
  13. MX1.25 喇叭接口 用于外接扬声器
  14. 16MB NOR-Flash 用于程序与数据存储
  15. 触摸屏排线座子
  16. 双色 LED (可编程 LED + 充电指示 LED)
  17. 屏幕排线座子
  18. PCF85063 RTC 时钟芯片,支持时间保持功能

接口介绍


产品尺寸


MicroPython 开发

本章节包含以下部分,请按需阅读:

MicroPython 入门教程

初次接触 Pico MicroPython 开发,想要快速上手?我们为您准备了一套通用的入门教程。此教程旨在帮助开发者快速熟悉 Thonny IDE 并上手开发。教程内容涵盖环境搭建、项目创建、组件使用及外设编程等,帮助您迈出 MicroPython 编程的第一步。

配置开发环境

请参考 安装和配置 Thonny IDE 教程 下载安装 Thonny IDE。

示例程序

MicroPython 示例程序位于 示例程序包 的 examples\MicroPython 目录中。

示例程序基础例程说明依赖库
01_SD挂载 SD 卡-
02_RTC获取 RTC 数据-
03_GUIGUI 显示程序-
04_KEY按键测试-
05_SHTC3温湿度传感器测试-
06_TOUCH触摸屏测试-
07_ES8311ES8311 音频录音与播放测试-

01_SD

【程序说明】

  • 使用 SPI 与 SD 卡通讯,并将 SD 卡挂载到开发板,挂载成功后,可通过 Thonny 查看和修改 SD 卡中的内容。

【硬件连接】

  • 插入 SD 卡
  • 使用 USB 线把板子接入电脑

【代码分析】

  • sdcard.SDCard(spi, cs, baudrate):创建 SD 卡对象,并将初始化的 SPI 接口与 CS 引脚绑定到 SD 卡驱动中。
  • uos.mount(sd, '/sd'):将 SD 卡文件系统挂载到 /sd 目录下。挂载成功后,用户即可通过 /sd 路径对 SD 卡进行文件读写操作,例如创建、读取或删除文件。

【运行效果】

  • 通过 thonny 将 01_SD 文件下的所有 py 文件上传到开发板中,并复位。开发板复位后会根据 boot.py 的程序,自动将 SD 挂载到 sd 目录下。


02_RTC

【程序说明】

  • 使用 I2C 与 板载 RTC 芯片通讯,设定并读取 RTC 时间数据,同时测试 RTC 中断是否正常。

【硬件连接】

  • 使用 USB 线把板子接入电脑

【代码分析】

  • RTC = PCF85063():创建 RTC 对象。
  • RTC.setDate(weekday, day, month, year):设置 RTC 日期。
  • RTC.setTime(hour, minute, second):设置 RTC 时间。
  • RTC.readTime():读取 RTC 时间。
  • RTC.setAlarm(second, minute, hour, day, weekday):设置 RTC 闹钟。
  • RTC.enableAlarm():使能 RTC 闹钟。

【运行效果】

  • 使用 thonny 运行 02_RTC 文件下的 py 文件。


03_GUI

【程序说明】

  • 通过 SPI 通信驱动显示屏,利用 GUI 绘图函数绘制文字、边框与色块,刷新屏幕完成显示。

【硬件连接】

  • 使用 USB 线把板子接入电脑

【代码分析】

  • epd = EPD_1in54 :创建 LCD 对象。
  • epd.Clear(0xff):清除全屏。
  • epd.fill(0xff):填充全屏颜色。
  • epd.text("RP2350-Touch-ePaper-1.54", 0, 30, 0x00):在屏幕上写文字。
  • epd.hline(10, 150, 80, 0x00):画水平线。
  • epd.vline(10, 90, 60, 0x00):画垂直线。
  • epd.display(epd.buffer):刷新屏幕(真正显示出来)。

【运行效果】

  • 使用 thonny 运行 03_GUI 文件下的 py 文件。


04_KEY

【程序说明】

  • 演示 RP2350 MicroPython 下的两路按键输入与常用按键事件识别:BOOT(BOOTSEL 特殊按键)与 POWER(普通 GPIO24)。
  • 支持事件:downupclick(单击)、double(双击)、long(长按),并在串口/Thonny Shell 中打印事件日志。
  • 示例代码文件:key_events.py(可作为库导入复用,也可作为主程序脚本直接运行)。

【硬件连接】

  • BOOT:使用板载 BOOTSEL 按键。
  • POWER:使用板载 POWER 按键(内部接到 GPIO24)。
  • 使用 USB 线把板子接入电脑,并用 Thonny 连接 MicroPython 解释器。

【代码分析】

  • BootKey:BOOTSEL 读取封装
    • BOOTSEL 不是普通 GPIO,MicroPython 一般通过 rp2.bootsel_button()(或部分版本用 machine.bootsel_button())提供读取接口。
    • pressed():返回当前是否按下(True 表示按下)。
  • GpioKey:普通 GPIO 按键读取封装
    • Pin(pin, Pin.IN, pull):将指定 GPIO 配置为输入,并配置上拉/下拉。
    • active_low=True:表示按下为 0,松开为 1(常见接法)。
  • Key:按键事件状态机(轮询式)
    • debounce_ms:消抖时间。只有当电平变化稳定持续超过该时间,才认为状态真的变化。
    • double_ms:双击判定窗口。单击事件会在“松开后等待 double_ms”确认没有第二次点击后才发出。
    • long_ms:长按阈值。按住超过 long_ms 时立即触发一次 long(不必等待松开)。
    • poll(now):每隔一段时间调用一次,内部完成:
      • 读取原始电平(raw)
      • 消抖得到稳定状态(stable),并在稳定按下/松开时发送 down/up
      • 长按检测:稳定按下持续超过 long_ms 发送 long
      • 单击/双击聚合:松开后累积点击次数,超时后将 1 次转换为 click,2 次转换为 double
  • main(banner=False, poll_ms=10):程序入口
    • banner=True 时打印操作提示(适合“作为主程序脚本”运行时引导初学者)。
    • poll_ms 为轮询间隔,越小越灵敏但占用 CPU 越多(常用 5~20ms)。

【运行效果】

  • 通过 Thonny 直接运行 04_KEY 文件夹下的 py 文件。
    • 在电脑上用 Thonny 打开 key_events.py,确认解释器选择的是开发板对应的 MicroPython 端口。
    • 点击绿色 Run 按钮,Thonny 会把脚本发送到开发板执行。
    • 这种方式下脚本通常会以 __main__ 方式执行,因此会自动进入 main(banner=True),直接看到提示与按键事件输出。
    • 如果你想把它当库来用(导入但不自动运行),请把文件上传到开发板后在自己的 main.py 里显式调用 key_events.main(...)
  • 作为主程序脚本运行(上电自动执行)
    • 将 key_events.py 上传到开发板根目录并重命名为 main.py
    • 复位后会先打印提示信息(banner),随后按键触发时输出事件,例如:
      • power down / power up / power click
      • power double
      • power long
      • boot down / boot up / boot click ...
  • 作为可复用库运行(推荐)
    • 保持文件名 key_events.py 不变,上传到开发板。
    • 在你自己的 main.py 中显式调用:
      • import key_events
      • key_events.main(banner=True)(或 banner=False
  • 停止运行
    • Thonny 点击 Stop(中断)按钮,或在 Shell/REPL 中按 Ctrl+C 结束轮询循环并返回交互提示符。


05_SHTC3

【程序说明】

  • 演示 RP2350 MicroPython 下通过 I2C 读取 SHTC3 温湿度传感器。
  • 支持读取:
    • 温度(°C)
    • 相对湿度(%RH)

【硬件连接】

  • 已内置传感器:SHTC3(I2C)I2C 地址:默认 0x70
  • 用 USB 线把板子接入电脑,并用 Thonny 连接 MicroPython 解释器。

【代码分析】

  • 常量配置

  • I2C 默认参数(见 shtc3.py 文件头部的常量):

    • SHTC3_I2C_NUM:I2C 控制器编号
    • SHTC3_I2C_SCL / SHTC3_I2C_SDA:默认引脚
    • SHTC3_I2C_ADDR:I2C 地址
    • SHTC3_I2C_FREQ:I2C 频率
  • SHTC3 指令集:

    • SHTC3_REG_WAKEUP / SHTC3_REG_SLEEP / SHTC3_REG_SOFTRESET / SHTC3_REG_READID
    • SHTC3_MEAS_ALL:根据 stretch/low_power/hum_first 组合选择“测量指令”
  • SHTC3:传感器驱动类

  • __init__(..., i2c=None, crc_fail_return=(None, None))

    • i2c:允许外部传入已创建好的 I2C 对象,方便多设备共享总线
    • crc_fail_return:CRC 校验失败时的返回值(默认 (None, None)
    • 初始化阶段会执行:
      • wakeup():唤醒
      • soft_reset():软复位
  • crc8(buffer)

    • 用于校验 SHTC3 返回的 CRC-8(多项式 0x31,初值 0xFF
  • read_id()

    • 读取设备 ID,并验证 CRC;CRC 不通过返回 None
  • measurement(hum_first=False, low_power_meas=False, stretch=False, ...)

    • 读取一次温湿度并返回 (temperature_c, humidity_rh)
    • 参数说明:
      • low_power_meas=True:低功耗测量(更省电,但重复性/精度表现略受影响,温度影响更明显)
      • stretch=True:启用 clock stretching(是否需要取决于你的 I2C 总线/驱动实现)
      • hum_first:传感器返回的两个数据块(温度/湿度)谁在前
    • 兼容参数:
      • hum_frist:保留了旧示例中的拼写(typo)兼容
  • read(low_power=False, stretch=False, ...)

    • 更友好的别名:等价于 measurement(hum_first=False, ...)
  • main(...):示例入口(单文件一体化)

  • 当 shtc3.py 作为脚本直接运行时,会执行:

    • main():循环读取并打印温湿度
    • Ctrl+C 可中断循环
  • 参数说明:

    • interval_s:打印间隔(秒)
    • crc_fail_return:可覆盖驱动默认 CRC 失败返回值

【运行效果】

  • 方式 A:通过 Thonny 直接运行 05_SHTC3 文件夹下的 py 文件。

  • 在电脑上用 Thonny 打开 shtc3.py,确认解释器选择的是开发板对应的 MicroPython 端口。

  • 点击 Run 执行,默认会进入 main(),看到类似输出:

    • SHTC3 ID: 0x....
    • Temperature: 25.12 °C, Humidity: 45.67 %
  • 停止运行:Thonny 点击 Stop,或在 REPL 里按 Ctrl+C

  • 方式 B:作为库导入复用(推荐)

  1. 将 shtc3.py 上传到开发板文件系统(与 main.py 同目录通常最方便)。
  2. 在你自己的 main.py 里使用:
import time
from shtc3 import SHTC3

shtc3 = SHTC3()
while True:
t, rh = shtc3.read()
if t is not None and rh is not None:
print(t, rh)
time.sleep(1)


06_TOUCH

【程序说明】

  • 该例程仅限于 RP2350-Touch-ePaper-1.54 使用。
  • 这是一个“触摸测试界面”示例:在 1.54 英寸 200×200 墨水屏上画 4 个触摸区域(Area A~D),触摸后在屏幕下方显示坐标与区域名称,并在串口打印触摸坐标。
  • 屏幕显示采用:先全刷显示“底图”,再切换到局刷模式做交互更新(更快、更省时间)。
  • 触摸芯片为 FT6336U:通过 I2C 读取触摸点坐标,使用 INT 引脚中断触发读取,再由主循环取出触摸点。
  • 示例代码文件:
    • main.py
    • FT6336U.py

【硬件连接】

  • 使用 USB 线把板子接入电脑,并用 Thonny 连接 MicroPython 解释器。

【代码分析】

  • EPD_1in54:墨水屏驱动 + FrameBuffer 绘图封装(main.py)

    • 继承 framebuf.FrameBuffer:把 buffer 当作 200×200 单色画布使用,然后把 buffer 推到屏幕。
    • send_command() / send_data():通过 SPI 写命令/数据到屏幕控制器。
    • ReadBusy():轮询 BUSY 引脚等待屏幕空闲(墨水屏刷新期间必须等待)。
    • displayPartBaseImage():把“底图”写入两次缓冲区(0x24/0x26),用于后续局刷更稳定(减少残影的常见做法)。
    • init(update):两种刷新模式初始化
      • FULL_UPDATE:全刷,慢但干净
      • PART_UPDATE:局刷,快但可能有残影,需要底图配合
  • touch_ft6336u:FT6336U 触摸驱动(FT6336U.py)

    • I2C 初始化(__init__
      • machine.I2C(id=1, scl=Pin(7), sda=Pin(6), freq=400_000):使用 I2C1,400kHz。
      • int = Pin(8, IN, PULL_UP):触摸中断脚,下降沿触发回调。
      • rst = Pin(16, OUT):复位脚,上电时按时序拉高/拉低复位芯片。
    • init_chip():初始化与读 ID
      • 读取寄存器 0xA3 获取 chip id,示例里期望 0x64,并打印 “init ok” 或 “ID error”。
    • 中断读取触摸(int_cb / read_touch_data
      • int_cb() 触发后调用 read_touch_data(),先读 TD_STATUS(0x02) 得到触摸点数,再读 TOUCH1_X(0x03) 起的 4 字节拼出 x/y。
      • 这里做的是“单点触摸”:只解析 Touch1。
    • 主循环取点(get_touch_xy
      • 有新点时返回 [{"x":..., "y":...}],并把 point_count 清零,保证一次触摸只取一次(更像“事件”而不是连续坐标流)。
  • 主程序流程(main.py)

    • 初始化屏幕并清屏:epd = EPD_1in54()epd.Clear(0xff)(0xff 表示白色)。
    • 初始化触摸:touch = touch_ft6336u(),会在串口看到 FT6336U ID 相关输出。
    • 绘制静态 UI:标题、分割线、4 个矩形按钮区域(Area A~D)、坐标显示区。
    • 全刷显示底图:epd.displayPartBaseImage(epd.buffer),然后 epd.init(epd.part_update) 切到局刷模式。
    • 触摸消抖与轮询:
      • touch_debounce = 500:500ms 内只响应一次触摸,避免中断/抖动导致连发。
      • touch.get_touch_xy():拿到一次触摸点后:
        • 更新屏幕下方坐标与区域名称
        • 对对应区域做“黑底白字”高亮
        • displayPartial() 局刷
        • 1 秒后恢复按钮原样,再局刷一次

【运行效果】

  • 方式 A:通过 Thonny 直接运行运行 06_TOUCH 文件夹下的 py 文件。

    • Thonny 选择正确的 MicroPython 解释器端口。
    • 将 FT6336U.py 上传到设备内置 RP2350 的 MicroPython 文件系统根目录。
    • 打开并运行 main.py。
    • 串口/Thonny Shell 预期输出包含:
      • Initializing e-Paper...
      • Initializing touch screen...
      • FT6336U ID = 0x64FT6336U init ok!
      • 触摸时打印:Touch detected: (x, y)
    • 屏幕预期显示:
      • 顶部 “Touch Test”
      • 4 个区域按钮(Area A~D)
      • 下方实时更新 Touch: 区域名与 Pos: 坐标
  • 方式 B:作为上电自启程序

    • 将 main.py 与 FT6336U.py 上传到设备内置 RP2350 的 MicroPython 文件系统根目录。

    • 复位后自动运行。


07_ES8311

【程序说明】

  • 演示 RP2350 MicroPython 下驱动 ES8311 音频 Codec:通过 I2C 配置寄存器,通过 PIO 实现 I2S 音频数据收发,并用 PIO 输出 MCLK;固件支持 rp2.DMA 时可启用 DMA 加速。
  • 参考示例默认参数(可在 main.py 中修改):
    • 采样率:24kHz(SAMPLE_FREQ = 24000,支持的采样率组合由 es8311.py 的 COEFF_DIV 表决定)
    • 位宽:ADC 16bit(res_in=16)、DAC 16bit(res_out=16
    • 录音:单通道 int16(PIO RX 只采集一个 16-bit 通道,便于 DMA 对齐与稳定)
    • 回放:可配置为单声道复制到双声道(示例默认 channel_count = 2
  • 示例代码文件:
    • main.py:可直接运行的录音回放环回测试
    • es8311.py:ES8311 I2C 寄存器驱动(时钟/格式/音量/静音/麦克风增益)
    • audio_pio_mpy.py:PIO I2S + MCLK 输出(可选 DMA 接口)

【硬件连接】

  • 使用 Type-C 线把产品接入电脑,并用 Thonny 连接 MicroPython 解释器即可进行测试与调试。

【代码分析】

  • es8311.py:ES8311 寄存器驱动(I2C)

    • write_reg() / read_reg():单寄存器写/读封装。
    • COEFF_DIV + sample_frequency_config(mclk, rate):按 MCLK 与采样率查表配置分频(示例 MCLK = SAMPLE_FREQ * 256)。
    • fmt_config(res_in, res_out):设置 I2S 数据位宽与工作模式(示例使用 I2S 格式,并打开相关模式位)。
    • init(...):芯片复位 -> 时钟配置 -> 格式配置 -> ADC/DAC 相关寄存器初始化 -> 设置音量与麦克风增益。
    • volume_set()/volume_get()mute()microphone_gain_set():常用控制接口。
  • audio_pio_mpy.py:PIO I2S 收发 + MCLK 输出(可选 DMA)

    • mclk_pio():极简方波输出,用 StateMachine 产生 MCLK。
    • audio_pio_out():等待 ES8311 输出的 LRCLK/BCLK,同步输出数据;每次 pull() 取一个 32-bit 帧(左 16bit + 右 16bit)。
    • audio_pio_in():等待 LRCLK/BCLK,同步采样输入数据;autopush=True, push_thresh=16,只采集一个 16-bit 通道并推入 RX FIFO。
    • AudioPIO
      • mclk_pio_init()/dout_pio_init()/din_pio_init():初始化各 StateMachine 及引脚方向。
      • audio_in_into():轮询从 RX FIFO 取样(带超时提示,便于定位 BCLK/LRCLK/DIN 连接问题)。
      • dma_record_into() / dma_play_from_i16():固件支持 rp2.DMA 时使用 DMA 从/到 PIO FIFO 直接搬运,提高实时性并降低 CPU 占用。
  • main.py:录音回放环回示例

    • 参数区:SAMPLE_FREQMCLK_FREQRECORD_DURATION_MSDAC_VOLUMEMIC_GAINUSE_DMA 等。
    • init_hardware():打开功放使能脚(PA_CTRL_PIN)并初始化 I2C。
    • init_es8311():读取 Chip ID,调用 codec.init(...),解除静音。
    • init_audio_pio():配置 PIO 引脚与 StateMachine ID(默认 SM_DOUT_ID=0SM_DIN_ID=5SM_MCLK_ID=2)。
    • 主循环:录音(DMA/非 DMA)-> 打印 min/max/clipped -> condition_mic_samples() 去直流与衰减 -> 回放(DMA/非 DMA)。

【运行效果】

  • 通过 Thonny 运行
    • 通过 thonny 将 07_ES8311 文件夹下的所有 py 文件上传到设备根目录。
    • 在电脑上用 Thonny 打开 main.py,点击 Run 运行。
    • 正常运行后,串口会打印 I2C 扫描结果、ES8311 Chip ID,以及每次环回的录音/回放统计信息(Cycle 计数、录音耗时、min/max、clipped 等)。
  • 上传到设备运行
    • 通过 thonny 将 07_ES8311 文件夹下的所有 py 文件上传到设备根目录。
    • 将设备复位/重新上电,设备会自动执行根目录的 main.py(请确保 3 个文件都已上传:main.pyes8311.pyaudio_pio_mpy.py)。
  • 停止运行
    • Thonny 点击 Stop(中断)按钮,或在 Shell/REPL 中按 Ctrl+C 结束循环并返回交互提示符。



C/C++ 开发

本章节包含以下部分,请按需阅读:

配置开发环境

请参考 安装和配置 Pico C/C++ 开发环境教程 下载安装 Pico VS Code。

示例程序

C/C++ 示例程序位于 示例程序包 的 examples\C 目录中。

示例程序基础例程说明依赖库
01_FatFsFAT 文件系统,SD 卡 支持 SPI/SDIO 通讯-
02_ES8311开发板音频测试程序-
03_GUIGUI 显示程序-
04_LVGLLVGL 显示程序LVGL V8.4

01_FatFs

【程序说明】

  • 使用 SPI 或 PIO 模拟的 SDIO 与 SD 卡通讯,实现类似于 busybox 或 DOS 的命令行界面。

【硬件连接】

  • 插入 SD 卡
  • 使用 USB 线把板子接入电脑

【代码分析】

  • sd_init_driver():初始化 SD 卡驱动。
  • getchar_timeout_us():获取串口输入。
  • process_stdio():处理串口输入。

【运行效果】

  1. 使用 putty 或者 mobaxterm 等终端工具,打开开发板对应的 USB 串行端口

  2. 按 Enter 键启动命令行界面 (CLI)。您应该会看到类似这样的提示:

    >
  3. 输入 help 指令可以得到可用指令,如下

    > help
    setrtc <DD> <MM> <YY> <hh> <mm> <ss>:
    Set Real Time Clock
    Parameters: new date (DD MM YY) new time in 24-hour format (hh mm ss)
    e.g.:setrtc 16 3 21 0 4 0

    date:
    Print current date and time

    lliot <drive#>:
    !DESTRUCTIVE! Low Level I/O Driver Test
    e.g.: lliot 1

    format [<drive#:>]:
    Creates an FAT/exFAT volume on the logical drive.
    e.g.: format 0:

    mount [<drive#:>]:
    Register the work area of the volume
    e.g.: mount 0:

    unmount <drive#:>:
    Unregister the work area of the volume

    chdrive <drive#:>:
    Changes the current directory of the logical drive.
    <path> Specifies the directory to be set as current directory.
    e.g.: chdrive 1:

    getfree [<drive#:>]:
    Print the free space on drive

    cd <path>:
    Changes the current directory of the logical drive.
    <path> Specifies the directory to be set as current directory.
    e.g.: cd 1:/dir1

    mkdir <path>:
    Make a new directory.
    <path> Specifies the name of the directory to be created.
    e.g.: mkdir /dir1

    ls:
    List directory

    cat <filename>:
    Type file contents

    simple:
    Run simple FS tests

    big_file_test <pathname> <size in bytes> <seed>:
    Writes random data to file <pathname>.
    <size in bytes> must be multiple of 512.
    e.g.: big_file_test bf 1048576 1
    or: big_file_test big3G-3 0xC0000000 3

    cdef:
    Create Disk and Example Files
    Expects card to be already formatted and mounted

    start_logger:
    Start Data Log Demo

    stop_logger:
    Stop Data Log Demo

02_ES8311

【程序说明】

  • 使用 PIO 模拟的 I2S 与 ES8311 通讯,实现音频的输入与输出。

【硬件连接】

  • 连接喇叭
  • 使用 USB 线把板子接入电脑

【代码分析】

  • Es8311_Init():初始化 ES8311。
  • Es8311_Sample_Frequency_Config():配置采样率。
  • Es8311_Microphone_Config():配置麦克风。
  • Es8311_Microphone_Gain_Set():设置麦克风增益。
  • Es8311_Voice_Volume_Set():设置音量。
  • Sine_440hz_Out():输出 440 Hz 正弦波。
  • Happy_Birthday_Out():生日快乐电子乐。
  • Loopback_Test():录音播放测试。
  • Music_Out():播放音乐。

【运行效果】

  • 使用 VS Code 导入并编译 02_ES8311 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 02_ES8311\uf2 目录下 uf2 尾缀文件进行快速验证。

03_GUI

【程序说明】

  • 使用 SPI 与 显示屏通讯,并通过 GUI 实现显示文本和图片等功能。

【硬件连接】

  • 使用 USB 线把板子接入电脑

【代码分析】

底层硬件接口

我们对硬件操作进行了底层的封装,由于硬件平台不一样,内部的实现是不一样的,如果需要了解内部实现可以去对应的目录中查看,在 DEV_Config.c(.h) 可以看到很多定义,在目录:lib\Config

  • 模块初始化与退出的处理

    void DEV_Module_Init(void);
    void DEV_Module_Exit(void);
    提示

    这里是处理使用显示屏前与使用完之后一些 GPIO 的处理。

  • GPIO 读写

    void DEV_Digital_Write(uint_16 Pin, uint_8 Value);
    uint_8 DEV_Digital_Read(uint_16 Pin);
  • SPI 写数据

    void DEV_SPI_WriteByte(uint_8 Value);

上层应用

对于屏幕而言,如果需要进行画图、显示中英文字符、显示图片等怎么办,这些都是上层应用做的。这有很多小伙伴有问到一些图形的处理,我们这里提供了一些基本的功能 在如下的目录中可以找到 GUI,在目录:lib\GUI\GUI_Paint.c(.h)


在如下目录下是 GUI 依赖的字符字体,在目录:lib\Fonts


  • 新建图像属性:新建一个图像属性,这个属性包括图像缓存的名称、宽度、高度、翻转角度、颜色

    void Paint_NewImage(uint16_t *image, uint16_t Width, uint16_t Height, uint16_t Rotate, uint16_t Color)
    参数:
    image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
    Width: 图像缓存的宽度;
    Height: 图像缓存的高度;
    Rotate: 图像的翻转的角度
    Color: 图像的初始颜色;
  • 选择图像缓存:选择图像缓存,选择的目的是你可以创建多个图像属性,图像缓存可以存在多个,你可以选择你所创建的每一张图像

    void Paint_SelectImage(uint8_t *image)
    参数:
    image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
  • 图像旋转:设置选择好的图像的旋转角度,最好使用在 Paint_SelectImage() 后,可以选择旋转 0、90、180、270 度

    void Paint_SetRotate(uint16_t Rotate)
    参数:
    Rotate: 图像选择角度,可以选择 ROTATE_0、ROTATE_90、ROTATE_180、ROTATE_270 分别对应 090180270
    提示

    不同选择角度下,坐标对应起始像素点不同,这里以 1.14 为例,四张图,按顺序为 0°, 90°, 180°, 270°。仅做为参考。


  • 图像镜像翻转:设置选择好的图像的镜像翻转,可以选择不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像。

    void Paint_SetMirroring(uint8_t mirror)
    参数:
    mirror: 图像的镜像方式,可以选择 MIRROR_NONE、MIRROR_HORIZONTAL、MIRROR_VERTICAL、MIRROR_ORIGIN 分别对应不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像
  • 设置点在缓存中显示位置和颜色:这里是 GUI 最核心的一个函数、处理点在缓存中显示位置和颜色。

    void Paint_SetPixel(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color)
    参数:
    Xpoint: 点在图像缓存中 X 位置
    Ypoint: 点在图像缓存中 Y 位置
    Color: 点显示的颜色
  • 图像缓存填充颜色:把图像缓存填充为某颜色,一般作为屏幕刷白的作用。

    void Paint_Clear(uint16_t Color)
    参数:
    Color: 填充的颜色
  • 图像缓存部分窗口填充颜色:把图像缓存的某部分窗口填充为某颜色,一般作为窗口刷白的作用,常用于时间的显示,刷白上一秒。

    void Paint_ClearWindows(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color)
    参数:
    Xstart: 窗口的 X 起点坐标
    Ystart: 窗口的 Y 起点坐标
    Xend: 窗口的 X 终点坐标
    Yend: 窗口的 Y 终点坐标
    Color: 填充的颜色
  • 画点:在图像缓存中,在(Xpoint, Ypoint)上画点,可以选择颜色,点的大小,点的风格

    void Paint_DrawPoint(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style)
    参数:
    Xpoint: 点的 X 坐标
    Ypoint: 点的 Y 坐标
    Color: 填充的颜色
    Dot_Pixel: 点的大小,提供默认的 8 种大小点
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Dot_Style: 点的风格,大小扩充方式是以点为中心扩大还是以点为左下角往右上扩大
    typedef enum {
    DOT_FILL_AROUND = 1,
    DOT_FILL_RIGHTUP,
    } DOT_STYLE;
  • 画线:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画线,可以选择颜色,线的宽度,线的风格

    void Paint_DrawLine(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color, LINE_STYLE Line_Style , LINE_STYLE Line_Style)
    参数:
    Xstart: 线的 X 起点坐标
    Ystart: 线的 Y 起点坐标
    Xend: 线的 X 终点坐标
    Yend: 线的 Y 终点坐标
    Color: 填充的颜色
    Line_width: 线的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Line_Style: 线的风格,选择线是以直线连接还是以虚线的方式连接
    typedef enum {
    LINE_STYLE_SOLID = 0,
    LINE_STYLE_DOTTED,
    } LINE_STYLE;
  • 画矩形:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画一个矩形,可以选择颜色,线的宽度,是否填充矩形内部

    void Paint_DrawRectangle(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
    Xstart: 矩形的 X 起点坐标
    Ystart: 矩形的 Y 起点坐标
    Xend: 矩形的 X 终点坐标
    Yend: 矩形的 Y 终点坐标
    Color: 填充的颜色
    Line_width: 矩形四边的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Draw_Fill: 填充,是否填充矩形的内部
    typedef enum {
    DRAW_FILL_EMPTY = 0,
    DRAW_FILL_FULL,
    } DRAW_FILL;
  • 画圆:在图像缓存中,以 (X_Center Y_Center) 为圆心,画一个半径为 Radius 的圆,可以选择颜色,线的宽度,是否填充圆内部

    void Paint_DrawCircle(uint16_t X_Center, uint16_t Y_Center, uint16_t Radius, uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
    X_Center: 圆心的 X 坐标
    Y_Center: 圆心的 Y 坐标
    Radius: 圆的半径
    Color: 填充的颜色
    Line_width: 圆弧的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Draw_Fill: 填充,是否填充圆的内部
    typedef enum {
    DRAW_FILL_EMPTY = 0,
    DRAW_FILL_FULL,
    } DRAW_FILL;
  • 写 Ascii 字符:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一个 Ascii 字符,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawChar(uint16_t Xstart, uint16_t Ystart, const uint8_t Ascii_Char, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    Ascii_Char:Ascii 字符
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写英文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串英文字符,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawString_EN(uint16_t Xstart, uint16_t Ystart, const uint8_t * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pString: 字符串,字符串是一个指针
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写中文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串中文字符,可以选择 GB2312 编码字符字库、字体前景色、字体背景色

    void Paint_DrawString_CN(uint16_t Xstart, uint16_t Ystart, const uint8_t * pString, cFONT* font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pString: 字符串,字符串是一个指针
    Font: GB2312 编码字符字库,在 Fonts 文件夹中提供了以下字体:
    font12CN:ascii 字符字体 11*21,中文字体 16*21
    font24CN:ascii 字符字体 24*41,中文字体 32*41
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写数字:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串数字,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawNum(uint16_t Xpoint, uint16_t Ypoint, uint32_t Nummber, sFONT* Font, uint16_t Digit,uint16_t Color_Foreground, uint16_t Color_Background);
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    Nummber: 显示的数字,这里使用的是 32 位长的 int 型保存,可以最大显示到 2147483647
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Digit: 显示小数点位数
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 显示时间:在图像缓存中,在 (Xstart Ystart) 为左顶点,显示一段时间,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色;

    void Paint_DrawTime(uint16_t Xstart, uint16_t Ystart, PAINT_TIME *pTime, sFONT* Font, uint16_t Color_Background, uint16_t Color_Foreground)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pTime: 显示的时间,这里定义好了一个时间的结构体,只要把时分秒各位数传给参数;
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色

【运行效果】

  • 使用 VS Code 导入并编译 03_GUI 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 firmware\C 目录下 03_GUI.uf2 进行快速验证。


04_LVGL

【程序说明】

  • 使用 SPI 与 显示屏通讯,并通过 LVGL 实现显示文本和图片等功能。

【硬件连接】

  • 使用 USB 线把板子接入电脑

【代码分析】

源码结构

  • LVGL 库的源码位于工程文件夹的 lib\lvgl ,使用的版本号为 8.4,二次开发请参考对应版本的开发文档。

  • LVGL 库的相关设置在工程文件夹的 examples\inc\lv_conf.h 中,可以设置显示刷新频率、系统占用数据等。

  • LVGL 库的应用代码位于工程文件夹的 main.c lv_port\lv_port_disp.c lv_port\lv_port_indev.c

    VSCode-Example-6

LVGL 初始化

在使用 LVGL 图像库之前,您需要先初始化 LVGL。

  • LVGL 库的初始化函数

    代码位置:main.c

    实现功能:主要用于初始化 LVGL 所需的硬件和结构体变量。

    LVGL_Init();
  • LVGL 库核心初始化

    代码位置:main.c

    /*2.Init LVGL core*/
    lv_init();

LVGL 运行

LVGL 库定时调用心跳接口函数 lv_tick_inc 来通知 LVGL 过去的时间,以便 LVGL 能够更新其内部的时间状态,处理与时间相关的任务,例如动画、定时器等。在主函数的循环中还需要调用 lv_task_handler 函数,以便 LVGL 及时处理事件和任务,保证用户界面的响应和刷新。

  • LVGL 心跳接口

    代码位置:main.c

    实现方式:需要确保 lv_task_handler 的优先级低于 lv_tick_inc 的优先级,所以在本例中 lv_tick_inc 在定时器回调函数中调用。

    //每 5ms 调用一次定时器回调函数
    add_repeating_timer_ms(5, repeating_lvgl_timer_callback, NULL, &lvgl_timer);

    static bool repeating_lvgl_timer_callback(struct repeating_timer *t)
    {
    lv_tick_inc(5);
    return true;
    }
  • LVGL 任务处理器

    代码位置:examples\src\LCD_XinXX_LVGL_test.c

    实现方式:要处理 LVGL 的任务,需要定期调用 lv_timer_handler(),本例中在主函数的循环中进行调用。

    int main()
    {
    ...
    while(1)
    {
    lv_task_handler();
    DEV_Delay_ms(5);
    ...
    }
    }

LVGL 显示

要实现 LVGL 显示,必须初始化一个显示驱动,并设置显示驱动的各个属性,例如,颜色格式、绘制缓冲区、渲染模式以及显示回调函数。在每个 LV_DISP_DEF_REFR_PERIOD(在 lv_conf.h 中设置),LVGL 会检测 UI 上是否发生了一些需要重绘的事情。例如,按下按钮、更改图表、发生动画等。需要重新绘制时,LVGL 调用显示回调函数完成图像在刷新区的绘制。

  • LVGL 显示刷新率设置

    代码位置:examples\inc\lv_conf.h

    设置方式:在 lv_conf.h 中还能设置显示缓冲区刷新频率的时间,可以修改这个定义来改变屏幕的刷新时间。

    #define LV_DISP_DEF_REFR_PERIOD 10 // 单位:ms,这里为 10ms
  • LVGL 显示颜色设置

    代码位置:examples\inc\lv_conf.h

    设置目的:由于 lv_color_t 结构体在默认状态下所构建的像素颜色储存方式与本例需要传输的数据不一致,直接进行传输会导致显示的图像出现色差。

    #define LV_COLOR_16_SWAP 1
  • LVGL 显示相关变量定义

    代码位置:lv_port\lv_port_disp.c

    实现功能:定义显示驱动 disp_drv,绘制缓冲区 disp_buf。本例绘制缓冲区 buf0 设置为整个屏幕显示面积,能够在降低大面积刷屏锯齿的同时有效提高屏幕刷新率。

    static lv_disp_drv_t disp_drv;
    static lv_disp_draw_buf_t disp_buf;
    static lv_color_t *buf0;
  • LVGL 显示设备注册

    代码位置:lv_port\lv_port_disp.c

    实现功能:根据设计需求完善 LVGL 库核心结构体变量,初始化显示驱动 disp_drv,并设置绘制缓冲区,该缓冲区是 LVGL 用来渲染屏幕内容的简单数组。一旦渲染准备就绪,绘制缓冲区的内容将使用显示驱动程序中设置的 disp_drv_flush_cb 函数发送到显示器。


    static UBYTE img[(((EPD_1IN54_V2_WIDTH % 8 == 0)? (EPD_1IN54_V2_WIDTH / 8 ): (EPD_1IN54_V2_WIDTH / 8 + 1)) * EPD_1IN54_V2_HEIGHT)] = {0};
    UBYTE *BlackImage = img;
    Paint_NewImage(BlackImage, EPD_1IN54_V2_WIDTH, EPD_1IN54_V2_HEIGHT, ROTATE_0, WHITE);

    static lv_disp_draw_buf_t draw_buf_dsc_1 = {0};
    static lv_color_t buf_1[MY_DISP_HOR_RES * MY_DISP_VER_RES] = {0};
    static lv_color_t buf_2[MY_DISP_HOR_RES * MY_DISP_VER_RES] = {0};
    lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, buf_2, MY_DISP_HOR_RES * MY_DISP_VER_RES); /*Initialize the display buffer*/

    /*-----------------------------------
    * Register the display in LVGL
    *----------------------------------*/

    lv_disp_drv_init(&disp_drv); /*Basic initialization*/

    /*Set up the functions to access to your display*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = MY_DISP_HOR_RES;
    disp_drv.ver_res = MY_DISP_VER_RES;

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = disp_flush;
    disp_drv.user_data = BlackImage;

    /*Set a display buffer*/
    disp_drv.draw_buf = &draw_buf_dsc_1;

    /*Required for Example 3)*/
    #if 1
    disp_drv.full_refresh = 1;
    #endif
    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);

  • LVGL 显示回调函数

    代码位置:lv_port\lv_port_disp.c

    实现功能:主要完成图像在刷新区的绘制。

    void disp_flush( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p )
    参数:
    lv_disp_drv_t *disp_drv: 显示驱动结构体指针,包含了与显示相关的信息和函数指针。该参数常用于通知刷新完成
    const lv_area_t *area : 区域结构体指针,包含待刷新区域的位置信息。在本例中,用于创建 TFT 显示的窗口
    lv_color_t *color_p : 颜色结构体指针,表示要在刷新区域内显示的颜色数据。在本例中,作为 DMA 输入读取地址将数据传输到 SPI 总线,完成图像的绘制
  • LVGL 显示回调函数实现

    代码位置:lv_port\lv_port_disp.c


    static void disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p)
    {
    UBYTE *BlackImage = (UBYTE *)drv->user_data;
    uint16_t *buffer = (uint16_t *)color_p;

    for(int y = area->y1; y <= area->y2; y++)
    {
    for(int x = area->x1; x <= area->x2; x++)
    {
    uint8_t color = (*buffer < 0x7fff) ? BLACK : WHITE;
    Paint_SetPixel(x,y, color);
    buffer++;
    }
    }

    EPD_1IN54_V2_DisplayPart(BlackImage);
    lv_disp_flush_ready(drv);
    }

  • LVGL 刷新完成通知实现

    代码位置:lv_port\lv_port_disp.c

    实现功能:每一次图像刷新完成后都需要通知 LVGL 核心,以便 LVGL 准备下个刷新图像的渲染。

    lv_disp_flush_ready(drv);

LVGL 输入

在 LVGL 中,允许用户注册输入设备,如触摸板、鼠标、键盘或编码器等设备。用户可以通过这些输入设备控制用户界面,实现更好的交互。

  • LVGL 调用输入设备回调函数的频率设置

    代码位置:examples\inc\lv_conf.h

    设置方式: LVGL 默认每 30ms 调用一次输入设备回调函数来更新输入设备所触发的事件,可以在 lv_conf.h 中进行设置。

    #define LV_INDEV_DEF_READ_PERIOD 30 // 单位:ms,这里为 30ms
  • LVGL 输入设备注册

    代码位置:lv_port\lv_port_indev.c

    设置方式:按键及触摸设备 indev_drv 并初始化。

    void lv_port_indev_init(void)
    {
    static lv_indev_drv_t indev_drv;


    #if INPUTDEV_TS
    FT6336U_Init(FT6336U_Point_Mode);
    // /*4.Init touch screen as input device*/
    lv_indev_drv_init(&indev_ts);
    indev_ts.type = LV_INDEV_TYPE_POINTER;
    indev_ts.read_cb = ts_read_cb;
    lv_indev_t * ts_indev = lv_indev_drv_register(&indev_ts);
    DEV_KEY_Config(Touch_INT_PIN);
    //Enable touch IRQ
    DEV_IRQ_SET(Touch_INT_PIN, GPIO_IRQ_EDGE_RISE, &touch_callback);
    #endif

    keypad_init();

    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = keypad_read;
    indev_keypad = lv_indev_drv_register(&indev_drv);
    }
  • LVGL 的输入设备回调函数

    代码位置:lv_port\lv_port_indev.c

    实现功能:主要用于更新输入事件。

    static void keypad_read(lv_indev_drv_t * drv, lv_indev_data_t*data);
    参数:
    lv_indev_drv_t *indev_drv: LVGL 中的输入设备驱动结构体指针。在本例中,该结构体为按键输入设备驱动
    lv_indev_data_t *data : LVGL 中的输入设备数据结构体指针。在本例中,该结构体用于存储输入设备的状态和数据,包括当前的按键状态(按下或释放)

    static void ts_read_cb(lv_indev_drv_t * drv, lv_indev_data_t*data);
    参数:
    lv_indev_drv_t *indev_drv: LVGL 中的输入设备驱动结构体指针。在本例中,该结构体为触摸输入设备驱动
    lv_indev_data_t *data : LVGL 中的输入设备数据结构体指针。在本例中,该结构体用于存储输入设备的状态和数据,包括当前的触摸状态(按下或释放)

LVGL 控件布局

在 LVGL 中,我们能够建立各种不同的用户界面。界面的基本组成部分是对象,也称为控件(Widgets),比如按钮(Button)、标签(Label)、图像(Image)、列表(List)、图表或文本区域。在一个界面中,可以同时创建多个控件,并且我们可以设置它们的位置、尺寸、父对象、样式以及事件处理程序等基本属性。

  • LVGL 控件的对齐定位

    代码位置:lv_app_hwtest.c

    实现功能:使控件能够基于参考点进行偏移定位。控件对齐偏移的参考点控件的中心。

    对齐标准: LVGL 库具备内部对齐和外部对齐两种方式。默认以左上角作为原点,向左为水平方向的正方向,向下为垂直方向的正方向。

    //将 btn 控件定位在中心点向左偏移 45 个像素
    lv_obj_align(sw, LV_ALIGN_CENTER, -45, 0);


  • LVGL 控件切换字体大小

    代码位置:examples\inc\lv_conf.hlv_app_hwtest.c

    实现功能:在实际运用时一个界面可能需要运用多种字体大小,可以在 lv_conf.h 中使能多种字体大小,且可以设置默认字体大小。设置字体大小时需要对控件风格化,使控件能够按照设置的风格进行渲染。利用 lv_obj_add_style 函数可以实现对控件各个部位在不同状态下的渲染。

    #define LV_FONT_MONTSERRAT_16 1 // 使能 16 号字体
    #define LV_FONT_MONTSERRAT_18 1 // 使能 18 号字体
    #define LV_FONT_DEFAULT &lv_font_montserrat_18 // 设置默认字体大小为 18 号

    static lv_style_t style_label;
    lv_style_init(&style_label); // 初始化风格
    lv_style_set_text_font(&style_label, &lv_font_montserrat_16); // 设置字体大小为 16 号
    lv_obj_add_style(label,&style_label,0); // 设置 label 主题的风格
  • LVGL 控件事件处理

    代码位置:lv_app_hwtest.c

    实现功能:在 LVGL 中,可以给控件添加事件处理回调函数,使控件发生被点击、滚动、重绘等事件时,触发事件从而进入事件处理回调函数。在程序中调用 lv_obj_add_event_cb(obj, event_cb, filter, user_data) 函数,为控件 obj 添加事件 filter 的处理函数 event_cb,当控件 obj 触发 filter 事件时,系统会自动调用 event_cb 函数。最后一个参数是指向事件中可用的任何自定义数据的指针。

    //为控件 sw 添加事件 LV_EVENT_VALUE_CHANGED 的处理函数 sw_event_cb
    lv_obj_add_event_cb(sw, sw_event_cb,LV_EVENT_VALUE_CHANGED,NULL);

【运行效果】

  • 使用 VS Code 导入并编译 04_LVGL 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 firmware\C 目录下 04_LVGL.uf2 文件进行快速验证。



Arduino 开发

本章节包含以下部分,请按需阅读:

配置开发环境

请参考 安装和配置 Arduino IDE 教程 下载安装 Arduino IDE。

示例程序

Arduino 示例程序位于 示例程序包 的 examples\Arduino 目录中。

示例程序基础例程说明依赖库
01_GUIGUI 显示程序-

01_GUI

【程序说明】

  • 使用 SPI 与 显示屏通讯,并通过 GUI 实现显示文本和图片等功能。

【硬件连接】

  • 使用 USB 线把板子接入电脑

【代码分析】

底层硬件接口

我们对硬件操作进行了底层的封装,由于硬件平台不一样,内部的实现是不一样的,如果需要了解内部实现可以去对应的目录中查看,在 DEV_Config.c(.h) 可以看到很多定义,在目录:c\lib\Config

  • 模块初始化与退出的处理

    void DEV_Module_Init(void);
    void DEV_Module_Exit(void);
    提示

    这里是处理使用显示屏前与使用完之后一些 GPIO 的处理。

  • GPIO 读写

    void DEV_Digital_Write(uint_16 Pin, uint_8 Value);
    uint_8 DEV_Digital_Read(uint_16 Pin);
  • SPI 写数据

    void DEV_SPI_WriteByte(uint_8 Value);

上层应用

对于屏幕而言,如果需要进行画图、显示中英文字符、显示图片等怎么办,这些都是上层应用做的。这有很多小伙伴有问到一些图形的处理,我们这里提供了一些基本的功能 在如下的目录中可以找到 GUI,在目录:c\lib\GUI\GUI_Paint.c(.h)


在如下目录下是 GUI 依赖的字符字体,在目录:c\lib\Fonts


  • 新建图像属性:新建一个图像属性,这个属性包括图像缓存的名称、宽度、高度、翻转角度、颜色

    void Paint_NewImage(uint16_t *image, uint16_t Width, uint16_t Height, uint16_t Rotate, uint16_t Color)
    参数:
    image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
    Width: 图像缓存的宽度;
    Height: 图像缓存的高度;
    Rotate: 图像的翻转的角度
    Color: 图像的初始颜色;
  • 选择图像缓存:选择图像缓存,选择的目的是你可以创建多个图像属性,图像缓存可以存在多个,你可以选择你所创建的每一张图像

    void Paint_SelectImage(uint8_t *image)
    参数:
    image: 图像缓存的名称,实际上是一个指向图像缓存首地址的指针;
  • 图像旋转:设置选择好的图像的旋转角度,最好使用在 Paint_SelectImage() 后,可以选择旋转 0、90、180、270 度

    void Paint_SetRotate(uint16_t Rotate)
    参数:
    Rotate: 图像选择角度,可以选择 ROTATE_0、ROTATE_90、ROTATE_180、ROTATE_270 分别对应 090180270
    提示

    不同选择角度下,坐标对应起始像素点不同,这里以 1.14 为例,四张图,按顺序为 0°, 90°, 180°, 270°。仅做为参考。


  • 图像镜像翻转:设置选择好的图像的镜像翻转,可以选择不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像。

    void Paint_SetMirroring(uint8_t mirror)
    参数:
    mirror: 图像的镜像方式,可以选择 MIRROR_NONE、MIRROR_HORIZONTAL、MIRROR_VERTICAL、MIRROR_ORIGIN 分别对应不镜像、关于水平镜像、关于垂直镜像、关于图像中心镜像
  • 设置点在缓存中显示位置和颜色:这里是 GUI 最核心的一个函数、处理点在缓存中显示位置和颜色。

    void Paint_SetPixel(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color)
    参数:
    Xpoint: 点在图像缓存中 X 位置
    Ypoint: 点在图像缓存中 Y 位置
    Color: 点显示的颜色
  • 图像缓存填充颜色:把图像缓存填充为某颜色,一般作为屏幕刷白的作用。

    void Paint_Clear(uint16_t Color)
    参数:
    Color: 填充的颜色
  • 图像缓存部分窗口填充颜色:把图像缓存的某部分窗口填充为某颜色,一般作为窗口刷白的作用,常用于时间的显示,刷白上一秒。

    void Paint_ClearWindows(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color)
    参数:
    Xstart: 窗口的 X 起点坐标
    Ystart: 窗口的 Y 起点坐标
    Xend: 窗口的 X 终点坐标
    Yend: 窗口的 Y 终点坐标
    Color: 填充的颜色
  • 画点:在图像缓存中,在(Xpoint, Ypoint)上画点,可以选择颜色,点的大小,点的风格

    void Paint_DrawPoint(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style)
    参数:
    Xpoint: 点的 X 坐标
    Ypoint: 点的 Y 坐标
    Color: 填充的颜色
    Dot_Pixel: 点的大小,提供默认的 8 种大小点
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Dot_Style: 点的风格,大小扩充方式是以点为中心扩大还是以点为左下角往右上扩大
    typedef enum {
    DOT_FILL_AROUND = 1,
    DOT_FILL_RIGHTUP,
    } DOT_STYLE;
  • 画线:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画线,可以选择颜色,线的宽度,线的风格

    void Paint_DrawLine(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color, LINE_STYLE Line_Style , LINE_STYLE Line_Style)
    参数:
    Xstart: 线的 X 起点坐标
    Ystart: 线的 Y 起点坐标
    Xend: 线的 X 终点坐标
    Yend: 线的 Y 终点坐标
    Color: 填充的颜色
    Line_width: 线的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Line_Style: 线的风格,选择线是以直线连接还是以虚线的方式连接
    typedef enum {
    LINE_STYLE_SOLID = 0,
    LINE_STYLE_DOTTED,
    } LINE_STYLE;
  • 画矩形:在图像缓存中,从 (Xstart, Ystart) 到 (Xend, Yend) 画一个矩形,可以选择颜色,线的宽度,是否填充矩形内部

    void Paint_DrawRectangle(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
    Xstart: 矩形的 X 起点坐标
    Ystart: 矩形的 Y 起点坐标
    Xend: 矩形的 X 终点坐标
    Yend: 矩形的 Y 终点坐标
    Color: 填充的颜色
    Line_width: 矩形四边的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Draw_Fill: 填充,是否填充矩形的内部
    typedef enum {
    DRAW_FILL_EMPTY = 0,
    DRAW_FILL_FULL,
    } DRAW_FILL;
  • 画圆:在图像缓存中,以 (X_Center Y_Center) 为圆心,画一个半径为 Radius 的圆,可以选择颜色,线的宽度,是否填充圆内部

    void Paint_DrawCircle(uint16_t X_Center, uint16_t Y_Center, uint16_t Radius, uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
    参数:
    X_Center: 圆心的 X 坐标
    Y_Center: 圆心的 Y 坐标
    Radius: 圆的半径
    Color: 填充的颜色
    Line_width: 圆弧的宽度,提供默认的 8 种宽度
    typedef enum {
    DOT_PIXEL_1X1 = 1, // 1 x 1
    DOT_PIXEL_2X2 , // 2 X 2
    DOT_PIXEL_3X3 , // 3 X 3
    DOT_PIXEL_4X4 , // 4 X 4
    DOT_PIXEL_5X5 , // 5 X 5
    DOT_PIXEL_6X6 , // 6 X 6
    DOT_PIXEL_7X7 , // 7 X 7
    DOT_PIXEL_8X8 , // 8 X 8
    } DOT_PIXEL;
    Draw_Fill: 填充,是否填充圆的内部
    typedef enum {
    DRAW_FILL_EMPTY = 0,
    DRAW_FILL_FULL,
    } DRAW_FILL;
  • 写 Ascii 字符:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一个 Ascii 字符,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawChar(uint16_t Xstart, uint16_t Ystart, const uint8_t Ascii_Char, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    Ascii_Char:Ascii 字符
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写英文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串英文字符,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawString_EN(uint16_t Xstart, uint16_t Ystart, const uint8_t * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pString: 字符串,字符串是一个指针
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写中文字符串:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串中文字符,可以选择 GB2312 编码字符字库、字体前景色、字体背景色

    void Paint_DrawString_CN(uint16_t Xstart, uint16_t Ystart, const uint8_t * pString, cFONT* font, uint16_t Color_Foreground, uint16_t Color_Background)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pString: 字符串,字符串是一个指针
    Font: GB2312 编码字符字库,在 Fonts 文件夹中提供了以下字体:
    font12CN:ascii 字符字体 11*21,中文字体 16*21
    font24CN:ascii 字符字体 24*41,中文字体 32*41
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 写数字:在图像缓存中,在 (Xstart Ystart) 为左顶点,写一串数字,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色

    void Paint_DrawNum(uint16_t Xpoint, uint16_t Ypoint, uint32_t Nummber, sFONT* Font, uint16_t Digit,uint16_t Color_Foreground, uint16_t Color_Background);
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    Nummber: 显示的数字,这里使用的是 32 位长的 int 型保存,可以最大显示到 2147483647
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Digit: 显示小数点位数
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色
  • 显示时间:在图像缓存中,在 (Xstart Ystart) 为左顶点,显示一段时间,可以选择 Ascii 码可视字符字库、字体前景色、字体背景色;

    void Paint_DrawTime(uint16_t Xstart, uint16_t Ystart, PAINT_TIME *pTime, sFONT* Font, uint16_t Color_Background, uint16_t Color_Foreground)
    参数:
    Xstart: 字符的左顶点 X 坐标
    Ystart: 字体的左顶点 Y 坐标
    pTime: 显示的时间,这里定义好了一个时间的结构体,只要把时分秒各位数传给参数;
    Font: Ascii 码可视字符字库,在 Fonts 文件夹中提供了以下字体:
    font8:5*8 的字体
    font12:7*12 的字体
    font16:11*16 的字体
    font20:14*20 的字体
    font24:17*24 的字体
    Color_Foreground: 字体颜色
    Color_Background: 背景颜色

【运行效果】




相关资料

1. 硬件资料

开发板设计文件

2. 技术手册

数据手册

3. 官方资料

4. 开发软件

5. 示例程序