尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ESP32-S2 HMI开发板实战:从驱动屏幕到LVGL界面开发

ESP32-S2 HMI开发板实战:从驱动屏幕到LVGL界面开发 去年年底拿到一块针对 ESP32-S2 的 HMI Dev Kit本来以为就是普通的评估板结果在工位上点了两天发现这块板子几乎把所有“低成本、跑界面、接外设”的脏活累活都干完了。今天想认真聊聊为什么 ESP32-S2 这种看起来参数不炸裂的芯片会专门出一个面向 HMI 设计的开发套件以及你拿到这种板子之后到底该怎么把它真正跑起来、做出一个看起来像样的人机界面。先说结论这块 Dev Kit 不是拿来跑 Demo 的它是拿来给你做产品原型的。它的核心任务就是把显示屏、触摸、按键、音频、外部存储这些 HMI 必备的外设全部接口化让你不用自己画板子直接在软件层面验证交互逻辑和视觉方案。适合三类人想快速做产品 demo 的嵌入式工程师、从传统工业 HMI 转向开源方案的开发者、以及准备拿 ESP32-S2 做低成本控制面板的创客。1. 一张开发板打动我的点它到底在给 HMI 行业解决什么问题1.1 S2 能站上 HMI 舞台靠的不是参数很多人一听 ESP32-S2第一反应是“这芯片不是老早就有了吗单核 LX7240MHz还没蓝牙”。没错拿去跑深度学习或者当网关主控S2 确实排不上号。但 HMI 这个场景恰恰不是堆参数的地方。HMI 的核心需求是什么我做了几年嵌入式总结下来就三件事屏幕刷得动、触摸点得准、接口够丰富。这三件事对 CPU 的绝对算力要求并不高真正吃资源的是显示缓冲、UI 对象树、图片解码这些“内存密集型”操作。ESP32-S2 板载 320KB SRAM再外挂 2MB 到 8MB PSRAM跑 LVGL 这种轻量级图形库完全够用关键是成本能压到非常低。另外ESP32-S2 有一手别人没有的绝活——原生 USB OTG。这意味着你可以把板子直接模拟成 USB 键盘、USB 鼠标、USB 串口甚至 USB 大容量存储设备。这在 HMI 上太实用了设备上电后可以插着 USB 线当调试口量产后就切到 Host 模式去接条码枪或者 U 盘升级固件。一块板解决两件事这在 S3 和经典 ESP32 上都做不到这么干净。1.2 用户拿它做什么几个看得见的真实场景我拿这块 Dev Kit 实际做了三个方向的原型做完之后才意识到它面向的 HMI 市场比我想象的大得多。第一个是工业控制面板。一个 4.3 寸屏左边显示设备状态右边几个按钮控制启停底部跑一个实时曲线。之前用传统串口屏换页面要一条一条指令发交互反馈慢半拍。换了 S2 LVGL 之后按钮事件直接在本地跑按下立刻变色PLC 的反馈通过 Modbus 轮询回来再更新状态整个操作手感完全不一样。第二个是智能家居中控屏。不需要很大的屏幕4 寸左右就行放在玄关墙上。S2 的 WiFi 连接 MQTT接收温湿度传感器数据、控制灯光开关、显示摄像头抓拍。关键是它支持 Deep-sleep待机电流能做到几十微安比那些动不动就要跑安卓的屏幕方案省电太多了。第三个是桌面小仪表。因为 USB OTG 可以做 Host插一个 USB 小键盘上去配合 LCD 屏直接变成一台电子计价器或者参数录入终端。这个组合成本一算比买整套商业 HMI 模块便宜一半以上。2. 为什么是 ESP32-S2以及和 S3、经典 ESP32 的选型对比2.1 硬件资源逐项拆解这块 Dev Kit 的核心芯片是 ESP32-S2单核 Xtensa LX7主频最高 240MHz。这里要注意一个关键点LX7 内核没有硬件浮点单元FPU这意味着跑复杂三角函数、大量浮点运算时会偏慢。但 HMI 界面里的坐标、尺寸、透明度计算基本都是整数运算LVGL 在这上面专门做了优化所以实际体验并不会卡除非你在界面上跑很重的 3D 变换或粒子特效。内存方面芯片内置 320KB SRAM其中 16KB 给 RTC 用。HMI 场景下这个容量不够跑完整 UI所以 Dev Kit 上一般都外挂了 PSRAM常见 2MB 到 8MB。LVGL 的显示缓冲区、图片解码缓冲区、字体缓存都放 PSRAM内部 SRAM 只放任务栈和关键数据结构这样跑 800x480 分辨率的界面也不会爆内存。存储方面芯片内置 128KB ROM 和 4MB FlashDev Kit 上通常再扩一个 SPI Flash。HMI 的 UI 固件、图片资源、中文字库都能塞进去。如果还不够板上一般会带 MicroSD 卡槽可以把图片、音频、配置文件全部放 SD 卡按需加载。外设接口这块是重头戏。S2 引出了 43 个 GPIO支持 SPI、I2C、UART、I2S、LCD 并行接口、SDIO、ADC、DAC、触摸按键。Dev Kit 上基本把常用接口都破线引出来了还集成了 USB 接口、电池充电管理、RGB LED、按键。2.2 与 ESP32、ESP32-S3 的对比别选错既然要做 HMI很多人会纠结为什么不用 ESP32 或者 ESP32-S3我把三颗芯片放在一起对比过结论很明确。对比项ESP32ESP32-S2ESP32-S3CPU 核心双核 Xtensa LX6单核 Xtensa LX7双核 Xtensa LX7最高主频240MHz240MHz240MHz硬件浮点有无有向量指令无无有PIE原生 USB无OTGOTG蓝牙有无有SRAM520KB320KB512KBPSRAM 支持最高 16MB最高 8MB部分版本需要额外的 GPIO最高 8MB支持 Octal典型成本中低高如果你做的是“需要动画流畅、有蓝牙、带复杂计算”的高端 HMI直接上 S3双核 FPU 向量指令带来的优势明显。如果只是纯显示 按键 串口通信S2 是最具性价比的选择省下来的钱够多买一块屏。经典 ESP32 虽然双核但架构偏老USB 接口要外接芯片在 HMI 场景反而不如 S2 干净。我个人的建议是面板尺寸在 4.3 寸以下、分辨率不超过 800x480、不需要蓝牙、不需要大量浮点运算时闭眼选 S2。超过这个范围老老实实上 S3别跟硬件性能对着干。3. 拿到开发板之后先搞清楚屏幕和触摸的接口设计3.1 屏幕选型与总线选择SPI 还是 RGB市面上的 HMI 开发板屏幕接口主要分两大类SPI 串行屏和 RGB 并行屏。这块 Dev Kit 通常支持 SPI 屏接口就四根线SCLK、MOSI、CS、DC加上背光控制和复位。SPI 屏的好处是省 GPIO坏处是刷新带宽有限。举个例子一块常见的 4.3 寸 480x272 屏幕SPI 接口跑 80MHz 时钟理论最大像素吞吐量是 80Mbps换算成 16 位色深的帧率大约是 80M / (480x272x16) ≈ 38fps。看起来还行但这是在完全不做任何其他的情况下。实际跑 LVGL加上 Dirty Area 局部刷新帧率完全够用界面操作不会有明显拖影。如果你的 Dev Kit 板载了 RGB 接口的高分辨率屏幕比如 800x480那建议直接用 RGB 并行接口。S2 的 LCD_CAM 外设可以驱动 RGB 屏配合 PSRAM 做帧缓冲刷新率能做到 60fps。不过代价是占用的 GPIO 很多而且需要更大的 PSRAM成本会上升。实操角度我建议你别一上来就追求高分辨率。先拿一块 320x240 或者 480x320 的 SPI 屏跑通整个 LVGL 流程再考虑换大屏。原因很简单分辨率越大内存开销、图片资源大小、优化难度都是指数级上升。先把交互逻辑跑顺再谈视觉升级。3.2 触摸方案选择电阻屏、电容屏还是触摸按键这块 Dev Kit 上通常会有触摸接口常见的触摸控制器有 XPT2046电阻屏、FT5x06 / GT911电容屏以及 ESP32-S2 自带的电容触摸传感器。电阻屏的优点是便宜、可以用任何物体操作、不怕油污。缺点是透光率低、不支持多点触摸、校准麻烦。工业现场环境复杂操作工经常戴手套电阻屏反而更实用。XPT2046 是 SPI 接口读取原始坐标后需要做校准换算LVGL 里提供了lv_indev的接口维护一个 2D 矩阵就能把坐标映射到屏幕像素。电容屏的体验更好支持多点触摸、操作灵敏。但玻璃表面在强光下反光严重而且戴手套时基本没法用。FT5x06 这类芯片通过 I2C 接口读取直接给 LVGL 上报坐标和触摸状态。S2 自带的电容触摸传感器非常适合做“无物理按键”的设计用 PCB 走线就能做出触摸按键配合 LVGL 的按钮控件在成本上几乎为零。我在一个项目中用 S2 的触摸按键替代了实体轻触开关用一块 PCB 上画几个圆焊盘就能实现上翻、下翻、确认、返回四个按键省掉了四个实体开关的成本。下面是一个简单的 XPT2046 触摸驱动初始化代码跑在 ESP-IDF 环境下#include driver/spi_master.h #include esp_lcd_panel_io.h spi_device_handle_t spi_touch; spi_device_interface_config_t touch_cfg { .mode 0, .clock_speed_hz 2500000, .spics_io_num TOUCH_CS_GPIO, .queue_size 1, }; spi_bus_add_device(SPI2_HOST, touch_cfg, spi_touch); esp_lcd_panel_io_handle_t touch_io NULL; esp_lcd_panel_io_spi_config_t io_config { .cs_gpio_num TOUCH_CS_GPIO, .dc_gpio_num -1, .spi_mode 0, .pclk_hz 2500000, .trans_queue_depth 1, }; esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, io_config, touch_io);4. 软件方案LVGL SquareLine Studio 驱动层一套组合拳打下来4.1 搭建 ESP-IDF LVGL 环境别走弯路拿到 Dev Kit 之后我建议直接用乐鑫官方 ESP-IDF搭配 LVGL 作为图形库。ESP-IDF 的组件管理器已经支持lvgl和esp_lvgl_port不用自己手动移植省掉很多低级麻烦。先安装好 ESP-IDF切换到 v5.1 或更新的稳定版本然后用idf.py create-project创建工程。在main/idf_component.yml里加上依赖dependencies: lvgl/lvgl: ^8.3.11 espressif/esp_lvgl_port: ^1.4.0 idf: 5.0注意这里我选了 LVGL v8.3。LVGL v9 已经发布API 变化比较大很多老的示例代码直接编译不过。如果你刚上手v8.3 资料多、社区稳定、SquareLine Studio 兼容性也好是当下最稳妥的选择。等熟悉之后再迁移到 v9 不迟。然后通过idf.py menuconfig配置 PSRAM、SPI 屏幕型号、触摸芯片型号。确认打开以下选项CONFIG_SPIRAMy CONFIG_SPIRAM_MODE_OCTy CONFIG_LV_COLOR_DEPTH_16y CONFIG_LV_MEM_SIZE_KILOBYTES64LV_MEM_SIZE是 LVGL 内部动态内存池大小这个值太小会导致控件创建失败界面无反应我推荐从 64KB 起步资源紧张就减到 32KB但要仔细看日志里有没有lv_mem告警。PSRAM 一定开启否则分辨率稍微高一点的屏幕就会出现esp_lcd_panel_handle初始化失败或者刷新卡顿。4.2 手写一个屏幕驱动的关键步骤屏幕点亮是整个 HMI 开发的第一步也是最容易卡住的一步。以常见 ST7789 驱动芯片为例核心流程是初始化 SPI 总线创建 LCD 面板 IO创建 LCD 面板配置显示参数最后注册到 LVGL。#include esp_lcd_panel_io.h #include esp_lcd_panel_ops.h #include esp_lcd_panel_st7789.h esp_lcd_panel_io_handle_t io_handle NULL; esp_lcd_panel_io_spi_config_t io_config { .dc_gpio_num LCD_DC_GPIO, .cs_gpio_num LCD_CS_GPIO, .pclk_hz 40 * 1000 * 1000, .lcd_cmd_bits 8, .lcd_param_bits 8, .spi_mode 0, .trans_queue_depth 10, }; esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, io_config, io_handle); esp_lcd_panel_handle_t panel NULL; esp_lcd_panel_dev_config_t panel_config { .reset_gpio_num LCD_RST_GPIO, .color_space ESP_LCD_COLOR_SPACE_RGB, .bits_per_pixel 16, }; esp_lcd_new_panel_st7789(io_handle, panel_config, panel); esp_lcd_panel_reset(panel); esp_lcd_panel_init(panel); esp_lcd_panel_invert_color(panel, true); esp_lcd_panel_disp_on_off(panel, true);注意esp_lcd_panel_invert_color这个函数很多 ST7789 屏如果不做颜色反转显示出来的颜色会偏色红蓝互换。这是因为部分屏幕默认的 RGB 排列顺序跟驱动不一致需要实测确认。我踩过这个坑当时怎么调都偏色最后加了一行invert_color就正常了。初始化完成之后把 LVGL 的flush_cb接到面板的draw_bitmap上void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) { esp_lcd_panel_draw_bitmap(panel, area-x1, area-y1, area-x2 1, area-y2 1, color_p); lv_disp_flush_ready(drv); }这里有个关键细节LVGL 默认会传完整的区域但 HMI 界面大部分时间只有局部变化LVGL 会自动裁剪所以area参数通常不是全屏而是一个小矩形。esp_lcd_panel_draw_bitmap只需要把这一小块数据发到屏幕就行这就是局部刷新能极大提升流畅度。4.3 用 SquareLine Studio 拖一个 UI然后接入代码手写 UI 布局能让你理解 LVGL 的底层逻辑但做产品原型时效率太低。SquareLine Studio 是 LVGL 官方的可视化设计工具导出的 UI 代码可以直接集成进 ESP-IDF 工程。流程很简单在 SquareLine Studio 里创建工程选择屏幕分辨率跟你的 Dev Kit 屏幕分辨率一致拖拽按钮、滑块、仪表盘、曲线图设置好属性后点导出。生成的是一个 C 文件和一个头文件里面包含ui_init()函数和所有控件的事件回调函数声明。接入工程时在main.c里调用#include ui.h void app_main(void) { // ... 初始化 LVGL、屏幕、触摸 ui_init(); }然后事件回调里写业务逻辑例如按钮按下时切换页面void btn_next_clicked(lv_event_t *e) { lv_scr_load_anim(ui_screen_2, LV_SCR_LOAD_ANIM_MOVE_LEFT, 300, 0, false); }SquareLine Studio 导出的代码有一个特点所有变量名都是ui_前缀的全局结构体方便你在任何地方引用控件。但这也带来一个问题就是全局变量非常多结构体巨大占用的内存也不小。如果 MCU 资源紧张建议不要引入 SquareLine手写 UI 更省内存。4.4 把 PLC/变频器数据接到开源 HMI 上很多传统 HMI 工程师关心的问题是西门子 PLC 怎样把变频器参数显示到 HMI 中在商用 HMI 软件里你需要配置变量表、关联驱动、绑定画面一套流程下来很繁琐。而在 ESP32-S2 LVGL 的开源方案里本质就是一句话通过串口或网络把数据读到 MCU再更新到对应控件上。最常用的方式是 Modbus RTU。SP32-S2 上有多个 UART接一个 RS485 收发器比如 MAX3485就能挂到 Modbus 总线上。ESP-IDF 提供了esp-modbus组件配置好串口参数和从站地址就能读写保持寄存器。假设要读取变频器的运行频率从站地址 1寄存器地址 0x1001代码如下#include modbus_params.h #include mbcontroller.h uint16_t freq_reg 0; esp_err_t err modbus_master_read_holding_registers(slave_info, 0x1001, 1, freq_reg); if (err ESP_ERR_OK) { lv_label_set_text_fmt(ui_freq_label, %.1f Hz, (float)freq_reg / 10.0f); }实际工程中需要在主循环里定时轮询比如每 200ms 读一次变频器状态再把频率、电流、温度写到 UI 控件上。这里要注意Modbus 的轮询不要跟 LVGL 的刷新冲突。建议把 Modbus 轮询放在独立的任务里通过队列把结果发回 UI 任务或者直接用 LVGL 的lv_timer定时回调来读取但回调里不能做耗时操作否则界面会卡顿。5. 常见问题排查实录持续更新5.1 为什么屏幕会花屏、闪烁、触摸偏怎么治花屏的原因通常是 SPI 时钟频率太高或者电源供电不足。ST7789 这类屏幕SPI 时钟从 20MHz 往上调不是每块屏都能稳定跑 80MHz。调试方法很简单从 20MHz 开始每 10MHz 一档往上涨跑到花屏就退一档。这跟超频 CPU 是一个道理每块屏体质不一样不能光看 datasheet。闪烁问题要区分是整屏闪还是局部闪。整屏闪通常是因为背光 PWM 频率太低把背光驱动的 PWM 频率从 1kHz 提高到 10kHz 以上肉眼基本就看不出来了。局部闪则大概率是 DMA 配置问题或者 LVGL 的 flush 回调里没有等上一次传输完成就开始下一次。用esp_lcd_panel_draw_bitmap时内部会等 SPI 传输完成但如果你自己写了 DMA一定要加semaphore同步。触摸偏位的现象我遇到过很多次多半是触摸控制器输出的原始坐标没有做映射。XPT2046 这类电阻屏左下角到右上角是线性变化的需要计算屏幕尺寸和触摸坐标的比例关系。常见做法是校准三点算出旋转、缩放矩阵。在 LVGL 里通过indev_drv.read_cb回调把坐标修正后写入>lv_obj_add_event_cb(ui_btn_start, btn_start_event_cb, LV_EVENT_CLICKED, NULL);然后回调函数里判断事件码static void btn_start_event_cb(lv_event_t *e) { lv_event_code_t code lv_event_get_code(e); if (code LV_EVENT_CLICKED) { // 启动设备 } }如果按钮在disabled状态LVGL 是收不到点击事件的。检查一下有没有不小心调用了lv_obj_add_state(btn, LV_STATE_DISABLED)或者父容器设置了lv_obj_set_click为 false。这些低级问题排查起来很费时间建议先打印日志确认事件到底有没有触发。5.3 固件升级后黑屏、触屏失效的处理Dev Kit 支持 USB 下载和 OTA 升级。USB 下载掉电再插上偶尔会出现电脑识别不到串口的问题。这可能不是板子坏了而是 USB-JTAG 状态异常。按住板子上的 BOOT 键再插 USB或者按一下复位键大多数情况下能恢复。这跟“HMI panel image updater”这类工具的使用体验很类似刷机工具本质上都是把固件镜像写入 Flash区别只是上位机界面的差异。触屏失效比较隐蔽。最常见的是触摸 I2C 总线上挂了多个设备地址冲突导致初始化失败。排查完 I2C 地址之后还要确认触摸中断引脚是否被复用。某些 Dev Kit 的触摸复位引脚和 PSRAM 的引脚共用初始化顺序不对会导致触摸芯片一直处于复位状态。我的做法是查看触摸芯片的 ID 寄存器比如 FT5x06 读取0xA8和0xA9寄存器得到0x55 0x06确认芯片组地址。如果读出来是0xFF 0xFF说明 I2C 通信不正常需要查硬件连接和电平转换。5.4 HMI 开发中内存不足的终极解法HMI 项目越做越大图片资源越来越多内存不足是必然经历的阶段。解决办法按优先级排序把大图片转成 C 数组只保留需要的分辨率不要一张 800x480 的图直接放到 320x240 的屏幕上。使用 LVGL 的图片缩放和缓存不要每帧都重新解码 PNG 或 JPEG。LVGL 提供了lv_img_cache设置合适的缓存大小能显著提升图片加载速度。字体裁剪中文字库按需生成别一上来就全量导入 GB2312 字库。用字体工具只生成需要用到的字符能省掉 90% 以上的字库空间。如果 PSRAM 还有富余可以开CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY把大任务的栈放到 PSRAM释放内部 SRAM。我把 UI 项目从 480x272 升级到 800x480 时内存直接从 45KB 涨到 120KB一度以为只能换 S3 了。后来优化了图片资源和字体硬是在 S2 上跑了下来。所以别急着换芯片先优化资源。6. 写在最后一些踩坑后的真心话这块 ESP32-S2 Dev Kit 给我最大的感受是它把 HMI 设计的门槛从“硬件工程师手工调屏”拉到了“软件工程师拖拽界面”的层面。以前做一个带触摸屏的产品要自己画板、调背光、写驱动、做校准一个屏幕点亮能折腾一周。现在这块板子把这些脏活全部预置好了你拿到的第一天就能跑 LVGL 的 demo剩下的大把时间可以花在真正重要的交互设计上。在这个过程中我也踩了不少坑挑几个印象最深的分享出来。第一个是别迷信高分辨率。我一开始直接上了 800x480 的 RGB 屏结果 PSRAM 分配、刷新率调优、图片缩放碰到一堆问题进度严重滞后。后来换回 480x320 的 SPI 屏两天就把 UI 框架搭完了。做 HMI 先跑通流程细节优化后面再补。第二个是模块化驱动代码。不要把所有初始化代码堆在一个 main.c 里屏幕、触摸、LVGL、业务逻辑各分一个文件调试的时候改哪里都清清楚楚。特别是 LVGL 的回调函数千万别写匿名函数不然出问题的时候日志里的调用栈根本定位不到函数名。第三个是串口日志一定要从一开始就打开。idf.py monitor太好用了很多“屏幕点不亮”“按钮没反应”的问题日志里其实都写了原因。比如触摸初始化失败会打印I2C device not foundLVGL 内存不足会打印lv_mem告警。你只要养成看日志的习惯排查问题的速度能快一半。最后再分享一个小技巧如果你们项目里有人还在用传统 HMI 软件做仿真调试遇到按钮无反应、按钮灰色这类问题时别急着怀疑工程配置先查一下变量表有没有连上 PLC、仿真环境有没有真正启动。这些都是高频低级问题跟开源 HMI 一样越是低级问题越容易被忽视。这块 Dev Kit 后续我还会继续玩准备把它接到一个真正的 PLC 项目里把所有交互做成产品级。如果你也在做 HMI 方向的选型建议拿一块亲手跑一跑比看一百篇评测都管用。
返回列表