LVGL 显示与触摸
Hello World 用于确认启动和串口,PWM 用于理解背光控制。Zephyr 还可以管理显示、触摸、网络、蓝牙和多线程;LVGL 在显示驱动之上负责控件、布局、动画与交互。本实验开始让 ESP32-S3-Touch-LCD-1.28 的屏幕显示实际内容。
本页属于显示效果体验。系统怎样组织显示和触摸驱动,见 Zephyr 的组成;怎样把界面应用用于另一块 PCB,见 板卡适配。
本例针对 Touch 板和本机 Zephyr main 工作区,不能直接套用到无 Touch 板。2026-09-21 固件编译通过,生成 build/waveshare-lvgl-main/zephyr/zephyr.bin。准备本例时未检测到 /dev/ttyACM*,尚未烧录,屏幕效果与触摸坐标需实板验收。不要把编译成功当成已经点亮。
预期效果
- 深蓝背景、青绿色进度环,按 0% → 100% → 0% 循环,每次更新间隔 50 ms。
- 中间显示百分比与启动后的秒数;百分比是演示数据,不是电量、CPU 占用或传感器读数。
- 下方 PAUSE 按钮暂停动画,变成 RESUME 后可继续;运行时间始终更新。
- 首版使用英文字体,避免没有中文字形造成方框;中文显示需要后续添加合适的字体资源。
进度环大小为 220×220,放在 240×240 圆屏内。背光固定全亮,不是 PWM 呼吸效果。
硬件与软件分工
本机板卡 DTS 已配置 GC9A01(galaxycore,gc9x01x)显示控制器和 CST816S(hynitron,cst8xx)触摸控制器,不需要另外烧录所谓“屏幕固件”。重新烧录本例会替换之前运行的背光程序。
| 部件 | 接口 / GPIO | 软件作用 |
|---|---|---|
| 圆屏 | SPI2,SCLK 10、MOSI 11、CS 9 | 显示驱动传输 RGB565 像素 |
| 屏幕控制 | DC 8、RESET 14 | 命令/数据切换与复位 |
| 背光 | GPIO2,高电平点亮 | 本例应用启动后打开 |
| 触摸 | I2C1,SDA 6、SCL 7 | 输入驱动读取触摸坐标 |
| 触摸控制 | IRQ 5、RESET 13 | 触摸中断与复位 |
src/main.c 创建 LVGL 控件和事件回调
→ LVGL 绘制与 Zephyr 显示适配层
→ GC9A01 驱动 → SPI → 屏幕
手指 → CST816S → Zephyr input → LVGL → 按钮事件
板卡的 chosen 指定 zephyr,display 与 zephyr,touch。当前 main 的 LVGL 适配层可根据 chosen 自动建立触摸指针设备。本例主线程统一调用 lv_timer_handler();按钮回调和 LVGL 定时器也在这条处理路径中操作界面。
工程位置
~/Projects/zephyr-docs/examples/waveshare-lvgl/
├── CMakeLists.txt
├── prj.conf
├── app.overlay
└── src/main.c
本例直接编译文档仓库内的应用,避免复制两份后修改了其中一份、却编译另一份。boards/ 提供硬件定义,examples/waveshare-lvgl/ 才是应用源码,输出统一放到固件工作区的 build/waveshare-lvgl-main/。
prj.conf 开启显示、输入、LVGL、控件和字体;LVGL 内存池 32 KiB,RGB565 的 10% 绘图缓冲约 11.25 KiB,降低整帧缓冲占用。此计算不包含驱动、线程栈及其他对象的内存。
app.overlay 把显示 SPI 频率设为 20 MHz,用于首次验证;关闭 LEDC 和原 pwmleds 节点,另用 GPIO2 控制背光。板卡默认 LEDC pinctrl 还包含触摸中断 GPIO5,因此不应直接把旧 PWM 示例配置全部搬进显示工程。需要调光时再单独配置 GPIO2 的 PWM。
编译
以下命令针对已经准备好的新工作区,不是旧 zephyr-workspace:
source ~/Projects/zephyr-main-workspace/.venv-py312-main/bin/activate
export ZEPHYR_SDK_INSTALL_DIR=~/.local/opt/zephyr-sdk-1.0.1
cd ~/Projects/zephyr-main-workspace/zephyr
west build -p always -b esp32s3_touch_lcd_1_28/esp32s3/procpu \
~/Projects/zephyr-docs/examples/waveshare-lvgl \
-d build/waveshare-lvgl-main
首次接入其他工作区需要先完成 环境搭建 与模块准备。不要手动用 pip 安装 LVGL;它是 Zephyr west 管理的 C 库模块。
可检查最终合并配置:
rg 'CONFIG_(LVGL|DISPLAY|INPUT_CST8XX|LV_Z_POINTER|LV_COLOR_DEPTH)' \
build/waveshare-lvgl-main/zephyr/.config
若缺少 LVGL 模块,在对应工作区执行 west update lvgl 后重新编译。不要为修本例而直接改上游板卡 DTS。
烧录与查看日志
先退出占用串口的监视器。以下烧录命令在 Zephyr 源码根目录执行,端口先按实际设备确认:
cd ~/Projects/zephyr-main-workspace/zephyr
west flash -d build/waveshare-lvgl-main --esp-device /dev/ttyACM0
cd build/waveshare-lvgl-main
west espressif monitor -p /dev/ttyACM0 -b 115200
进入下载模式需要时按住 BOOT 再复位;下载后释放 BOOT,再正常复位启动。日志仍为 waiting for download 时,应用尚未运行,优先检查启动模式。不要把 --esp-device 从中间断行。
预期应用日志(待实板验证,不是已采集输出):
LVGL dashboard ready: 240x240, touch=ready
Touch: animation paused
Touch: animation running
最后两行需要点击按钮才会出现。退出监视器使用 Ctrl + ]。
实板验收与排错
- 正常启动后观察背景、文字、圆环是否出现,留意花屏、颜色异常和重启。
- 观察进度数字与圆环共同变化;运行时间应持续增加。
- 点击 PAUSE,检查圆环暂停、时间继续,串口出现暂停日志。
- 点击 RESUME,确认恢复;再检查按钮点击区域是否与画面重合。
| 现象 | 优先检查 |
|---|---|
| 黑屏且无应用日志 | BOOT 状态、串口端口、是否写入新构建目录 |
Display or backlight GPIO not ready | 最终 DTS、驱动初始化日志,确认是 Touch 目标 |
| 背光亮但没有图案 | GC9A01/SPI 初始化日志,确认编译的是 LVGL 应用 |
| 图案正常但点击无效 | touch=ready、CST8XX 日志、GPIO5 是否被其他功能占用 |
| 点击位置旋转或镜像 | 核对显示旋转和触摸坐标变换,不能只旋转画面 |
| 花屏或刷新异常 | SPI 频率、供电和显示日志,先保持本例 20 MHz |
从效果回到程序
打开 examples/waveshare-lvgl/src/main.c,按创建顺序找 screen、ring、label 和 button。lv_obj_align 相对于父对象定位;改变一个 y 偏移,先预测它会移向屏幕哪一侧,再验证,而不是同时改所有坐标。
lv_timer_create(update_ui, 50, NULL) 创建的是 LVGL 定时器,不是新的 Zephyr 线程。本例在 main 循环中调用 lv_timer_handler(),因此按钮事件和 update_ui 都经这条处理路径执行。k_sleep(10 ms) 为处理循环让出 CPU,50 ms 才是进度值的名义更新间隔,两者不能混为屏幕刷新率。
练习:把更新间隔改为 100 ms,保持每次加一不变,预期从 0 到 100 的时间约加倍。再阅读 pause 回调,解释为何进度暂停但 uptime 仍增长。不要在按钮回调中加入几秒钟的 sleep,否则会阻塞同一条 LVGL 处理路径。
屏幕空白时把问题拆开:显示设备是否 ready、背光是否打开、有没有创建控件、有没有持续处理 LVGL。每一步分别有日志或代码可查,而不是再加一次 CONFIG_LVGL=y。
下一步可以做什么
在显示和触摸验收通过后,可以逐步增加多页面切换、真实传感器曲线、联网校时的表盘、蓝牙状态页。每次先实现一个数据来源,避免把模拟百分比标成真实测量值。
版本与资料
本例以本机 Zephyr 52b48529aec(4.4.99 开发版本)、SDK 1.0.1、Python 3.12 虚拟环境为基线。main 会变化,复现问题时同时记录提交号;旧版 4.2.0 未验证本例,特别是触摸自动适配配置不能假设相同。
- Zephyr 板卡资料
- 微雪 Touch 板资料
- 本机参考示例:
zephyr/samples/subsys/display/lvgl/。