
大家好今天想聊聊 ESP32 显示类项目中特别容易被问住的一个话题帧率。不管是 240×320 的小屏上跑 LVGL还是用 OV2640 做图像采集更或者想在板子上做 H.264 视频解码“帧率”都是决定项目体验的关键指标。最近有些开发板资料里出现了 ESP32_S31、ESP32_P4X 这样的命名网上说法也比较乱。本文会先把这两个名字背后的芯片差异理清楚再围绕帧率影响因素、测试方法和选型建议完整拆一遍最后给出可以直接复用的测帧率代码帮你把“到底是 S3 够用还是必须上 P4”这个问题彻底搞明白。1. 背景与核心概念1.1 命名辨析ESP32_S31、ESP32_P4X 到底是什么先说结论乐鑫官方芯片型号里没有“ESP32_S31”也没有“ESP32_P4X”。你看到这两个名字基本上来自下面几种情况。第一种是模组或开发板的细分丝印。ESP32-S3 系列本身就有很多变体比如 ESP32-S3-WROOM-1、ESP32-S3-WROOM-1U、ESP32-S3-FN4R8、ESP32-S3-FH4R2 等。部分第三方模组厂、开发板厂会在丝印上做简写写成“S31”“S3 DevKit”“S3R8”等。电商平台为了标题好写也会把 ESP32-S3 直接称为 ESP32_S31。第二种是接口文档或选购页里的笔误。ESP32-P4 是乐鑫面向高算力边缘计算推出的芯片官方开发板型号里有 ESP32-P4-Function-EV-Board、ESP32-P4-MINI-1 等。后缀里的 P4X 更多是某个专用板卡变体、项目代号不是官方标准型号。第三种是有开发者把射频参数里的 S31、S41 等符号误当成芯片型号。这类命名在资料检索时会引起很大的误解。所以后面文章里提到的“S31”默认指 ESP32-S3 系列开发板“P4X”默认指 ESP32-P4 系列开发板。这也是目前社区和工程资料里最常见的使用习惯。1.2 ESP32 系列芯片定位差异要理解帧率对比先要搞清楚芯片的定位。ESP32-S3 是一个“无线连接 中低算力”的 HMI 显示终端芯片。它集成了 2.4GHz WiFi 和 BLE 5CPU 是双核 Xtensa LX7最高 240MHz内置 SRAM可以外接 Octal PSRAM。S3 还带向量指令扩展适合做轻量级 AI 推理、图像预处理、数据采集所以常被用在智能音箱面板、温控器、小型人机交互界面等场景。ESP32-P4 的定位则是“高算力本地处理 多媒体接口”。它采用双核 RISC-V HP 处理器最高主频 400MHz另外还有一颗低功耗 RISC-V LP 协处理器可以做后台传感器采集。P4 集成了 MIPI-CSI 摄像头输入、MIPI-DSI 显示屏输出、H.264 硬件编解码、JPEG 编解码、USB OTG、以太网 MAC 等能力算力明显比 S3 高一个档次。但这里有一个容易被忽略的点P4 没有内置 WiFi 和蓝牙。它更像一个多媒体计算核心需要外接 ESP32-C6、ESP32-S3 或独立无线模组来补全联网能力。选型时如果只看到“P4 算力强”就下单做到联网功能时会发现还要再加一颗无线芯片这个成本和工作量必须在项目初期就纳入评估。1.3 MCU 场景里的“帧率”到底指什么提到帧率很多人第一反应是游戏帧率。但在 ESP32 开发里“帧率”至少包含三种含义它们的瓶颈完全不同。第一种是 GUI 刷新帧率单位 FPS表示屏幕每秒完成多少次完整的画面更新。常见测量对象是 LVGL、SquareLine Studio 生成的界面。影响它的是屏幕接口带宽、刷新缓冲区大小、CPU 渲染耗时、DMA 是否开启。第二种是摄像头采集帧率表示摄像头每一秒输出多少帧图像。常见场景是 OV2640、OV5640 等 DVP 摄像头或者 P4 支持的 MIPI-CSI 摄像头。影响它的是传感器 PCLK、分辨率、像素格式、DMA 带宽、片外 PSRAM 读写速度。第三种是视频解码帧率表示本地视频流或网络视频流每秒能解码多少帧。S3 基本靠 CPU 软解 MJPEG、AVI 这类格式P4 则有 H.264 硬件编解码模块解码相同分辨率视频时帧率会有数量级差异。搞清楚了这一点再去做对比测试就不会出现“有人测出来 60FPS有人测出来 15FPS”的争论——因为他们测的根本不是同一件事。2. 环境准备与版本说明2.1 开发板与屏幕/摄像头准备本文的对比测试建议准备两套开发板。S3 侧可以用常见的 ESP32-S3-DevKitC-1或者任何带 8MB Octal PSRAM 的 ESP32-S3 开发板。屏幕方面如果只是快速验证 LVGL可以用 240×320 的 SPI ILI9341或者 320×480 的 RGB/RM67162 屏幕。如果做摄像头采集准备一个 OV2640 或 OV5640 DVP 摄像头模块即可。P4 侧建议用乐鑫官方的 ESP32-P4-Function-EV-Board或者第三方的 ESP32-P4-MINI-1 核心板。P4 的显示接口通常走 MIPI-DSI 或 RGB DPI摄像头走 MIPI-CSI。屏幕选型时注意看开发板丝印上的接口类型很多 EV Board 自带 MIPI-DSI 屏幕排线座。接线方面没有统一答案因为不同屏幕、不同摄像头模块引脚差异很大。实际用时以板卡原理图为准关键确认这几类引脚LCD 数据线、时钟线、背光控制、触摸 I2C摄像头 SCCB 控制、PCLK、VSYNC、HREF、数据线。2.2 软件环境选择软件环境上S3 和 P4 的推荐开发路径略有不同。S3 生态非常成熟既可以用 ESP-IDF也可以用 Arduino-ESP32还可以通过 PlatformIO 管理工程。对新手来说Arduino LVGL 库是上手最快的方式对正式产品我更推荐 ESP-IDF调试手段更完整编译产物更可控。P4 因为发布相对较晚Arduino 支持还在完善中建议直接使用 ESP-IDF。请安装 ESP-IDF v5.3 或更新版本P4 的 target 支持需要较新的 IDF 版本。在命令行里通过idf.py set-target esp32p4切换目标芯片即可。安装 ESP-IDF 或 Arduino core 时如果网络不稳定很容易出现类似failed to install platform: esp32:3.3.11. 13 internal: download failed的报错这是 Arduino core 或 PlatformIO 平台包下载失败导致的。解决方法是配置国内镜像源或者手动下载平台压缩包解压到对应目录。2.3 示例工程目录结构无论使用哪种 IDE示例工程建议保持下面这种结构project/ ├── CMakeLists.txt ├── partitions.csv ├── sdkconfig.defaults └── main/ ├── CMakeLists.txt ├── app_main.c ├── lvgl_demo.c └── display.c采用这种结构的好处是显示驱动、UI 逻辑、摄像头逻辑可以拆成独立模块后面做帧率测试和代码优化时改一个文件不影响其他模块。3. 核心硬件规格对比3.1 芯片规格总览项目ESP32-S3ESP32-P4CPU双核 Xtensa LX7最高 240MHz双核 RISC-V HP最高 400MHz低功耗协处理器无RISC-V LP CoreWiFi/BLE2.4GHz WiFi BLE 5无需外接无线 SoC显示接口SPI / 8080 / RGB DPI 等MIPI-DSI、RGB DPI 等摄像头接口DVP 数字并行摄像头MIPI-CSI、DVP 等视频编解码JPEG 硬件编解码H.264 硬件编解码 JPEG内存内置 SRAM 可外接 PSRAM内置 SRAM 可外接 PSRAMUSBUSB-OTG / SerialUSB-OTG以太网无内置 MAC有以太网 MAC这张表不需要死记关键是记住 S3 强调“无线 够用”P4 强调“算力 多媒体接口”。3.2 哪些差异直接影响帧率直接拉大帧率差距的核心有三点。第一显示接口带宽。S3 的 LCD 接口虽然支持 SPI、8080、RGB DPI但接口本身仍受 SoC 内部总线和内存带宽限制。P4 加入了 MIPI-DSI同等屏幕分辨率下DSI 的时钟和带宽通常更充足刷大分辨率屏幕时优势明显。第二视频硬编解码。P4 内置 H.264 硬件编解码这是 S3 不具备的能力。S3 解码 H.264 基本靠软解可能只能跑低分辨率、低码率P4 可以硬解到明显更高的分辨率帧率和稳定性都会好很多。第三摄像头接口差异。S3 常见 DVP 并行摄像头接口本身也承担 LCD 控制功能两者共用 LCD_CAM 外设。P4 的 MIPI-CSI 是独立的多媒体通路摄像头输入和大屏输出可以并行工作这一条对“摄像头实时预览 GUI 显示”这类项目影响非常大。3.3 S3 的“AI”和 P4 的“AI”不是一回事ESP32-S3 宣传中经常提到 AI 加速实际上它支持的是向量指令扩展适合做低延迟的语音关键词识别、简单人体感应、轻量图像分类这类任务。它不是独立 NPU遇到稍大的神经网络模型时跑的还是 CPU 扩展指令。ESP32-P4 同样不是传统意义的独立 NPU但它主频更高、RISC-V 指令集在现代编译器下表现更好还集成了更丰富的媒体处理能力整体 AI 推理上限明显高于 S3。换句话说如果你要在板子上同时跑“摄像头图像预处理 显示渲染 AI 分类”P4 的余量会大很多。4. 影响帧率的关键技术点拆解4.1 屏幕接口怎么决定刷新率屏幕刷新帧率本质上是“每秒搬多少像素数据到屏幕”。这个带宽由接口类型和时钟决定。SPI 接口是串行传输比如 ILI9341 这类 240×320 屏幕RGB565 格式下每像素 16bit一帧就是 320×240×2153600 字节。SPI 时钟 40MHz 时理论传输一帧也要 30ms 左右所以很多人在 SPI 屏幕上跑 LVGL全屏刷新很难超过 30FPS。RGB DPI 接口是并行传输可以同时传 16bit 或 18bit 像素配合 DMA 和 PCLK屏幕越大优势越明显。但也正因如此RGB 屏幕通常会占用很多 GPIO并且需要较大的缓冲空间。MIPI-DSI 属于高速串行差分接口P4 支持该接口后可以更方便地驱动高分辨率、高刷新率的显示屏。对于 800×480、1280×720 这类大屏项目P4 的 MIPI-DSI 比 S3 的 SPI/RGB 方案更容易把帧率跑高。4.2 摄像头采集的帧率瓶颈摄像头采集帧率取决于传感器输出 PCLK、分辨率、像素格式以及控制器能不能及时把数据搬到内存。ESP32-S3 的 DVP 摄像头接口采集 YUV422 或 RGB565 时数据量会随着分辨率快速上涨。如果又不使用 PSRAM 做帧缓冲很容易出现采集一帧丢一帧的情况。更常见的做法是把摄像头输出改为 JPEG 格式比如 OV2640 直接输出 JPEGS3 只负责搬运这样可以显著降低内存带宽和存储压力。ESP32-P4 的 MIPI-CSI 支持更高带宽的传感器输入并且有 H.264/JPEG 硬编解码采集与编码可以由硬件模块分担。做“摄像头实时预览 本地录像”这类需要同时读帧、压缩、显示的项目时P4 的帧率上限通常比 S3 高一个量级。4.3 LVGL 渲染与 flush 回调LVGL 的帧率并不等于 LVGL 定时器周期。LVGL 内部有一个 refresh 周期通常默认 33ms 或 30ms但真正决定画面是否流畅的是flush回调也就是“LVGL 把一整块脏区域真正写到屏幕上”这个动作。如果只有一个很小的 bufferLVGL 会频繁触发局部刷新渲染次数多、中间开销大如果有双缓冲LVGL 可以在一个 buffer 刷屏的同时在另一个 buffer 里渲染下一帧帧率会明显提升。DMA 传输也很重要在flush回调里直接用spi_transfer_dma()或者esp_lcd_panel_draw_bitmap()代替 CPU 逐像素写可以把刷屏时间压缩到极低。4.4 视频解码能力差异S3 没有硬件 H.264 解码器。想播放视频只能选用 MJPEG、AVI 等软解友好的格式。MJPEG 本质是一串 JPEG 帧S3 可以利用硬件 JPEG 解码器来加速所以很多 S3 上的“视频播放”工程实际上是 MJPEG。P4 自带的 H.264 硬解让本地视频播放和网络视频流解码变得实用得多。同样的 720p 视频S3 软解可能非常吃力P4 利用硬件模块解码CPU 还能继续处理 UI 和业务逻辑。5. 完整实战案例帧率测试方法5.1 基于 LVGL 统计实际刷新 FPS不要用lv_timer去“算”FPS因为 LVGL 定时器周期不代表真实刷新完成帧率。下面这段代码通过统计flush回调的执行次数精确反映 LVGL 每秒往屏幕写了几次数据。// 文件路径main/fps_test.c #include stdio.h #include freertos/FreeRTOS.h #include freertos/task.h #include lvgl.h #include esp_log.h #define TAG FPS_TEST static volatile uint32_t s_flush_cnt 0; // 这个回调会被 LVGL 在需要刷屏时调用 // 实际项目中在这里调用 esp_lcd 或 SPI 驱动的写屏函数 void my_disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) { // 示例调用 LCD 面板驱动把 color_p 指向的像素数据刷到屏幕 // esp_lcd_panel_draw_bitmap(panel, area-x1, area-y1, area-x2, area-y2, color_p); s_flush_cnt; lv_disp_flush_ready(drv); } static void fps_monitor_task(void *arg) { while (1) { vTaskDelay(pdMS_TO_TICKS(1000)); uint32_t cnt s_flush_cnt; s_flush_cnt 0; ESP_LOGI(TAG, flush per second: %lu, (unsigned long)cnt); } } void fps_test_start(void) { xTaskCreate(fps_monitor_task, fps_monitor, 4096, NULL, 5, NULL); }这里有一个容易踩的坑lv_disp_flush_ready(drv)一定要被调用否则 LVGL 会认为屏幕还没刷完一直处于等待状态帧率自然上不去。如果你统计到的flush per second和屏幕刷新周期差距很大先检查 flush 回调是否在 DMA 传输完成后及时调用了lv_disp_flush_ready。5.2 摄像头采集帧率统计以 ESP32-S3 和 esp32-camera 驱动为例摄像头帧率统计通常是在获取帧缓冲的循环里计数。// 文件路径main/cam_fps_test.c #include stdio.h #include freertos/FreeRTOS.h #include freertos/task.h #include esp_camera.h #include esp_log.h #define TAG CAM_FPS static volatile uint32_t s_cam_frames 0; static void cam_monitor_task(void *arg) { while (1) { vTaskDelay(pdMS_TO_TICKS(1000)); uint32_t cnt s_cam_frames; s_cam_frames 0; ESP_LOGI(TAG, camera fps: %lu, (unsigned long)cnt); } } void cam_fps_test_start(void) { xTaskCreate(cam_monitor_task, cam_monitor, 4096, NULL, 5, NULL); } // 放在主循环或采集任务里 void cam_fps_running(void) { camera_fb_t *fb esp_camera_fb_get(); if (fb) { s_cam_frames; // 这里可以处理图像比如送到 LVGL 显示或者丢给编码器 // 不处理时一定要及时归还帧缓冲否则内存会被耗尽 esp_camera_fb_return(fb); } }测试时建议把摄像头分辨率设为 QVGA 或 VGAJPEG 质量调到 10 到 15观察帧率变化。如果帧率偏低优先检查xclk_freq_hz是否达到传感器支持的最大值以及fb_count是否设置了 2 到 3 个缓冲。5.3 如何分析测试数据帧率测试不要只跑一次建议整理成表格对比。可以分别测试下面几组变量测试项配置 A配置 B说明LVGL buffer 大小一行缓冲半屏双缓冲双缓冲通常能显著提升 FPSSPI 时钟20MHz40MHz更高时钟会提升 SPI 刷屏速度显示分辨率320×240800×480分辨率越高帧率越难维持摄像头格式RGB565JPEGJPEG 能大幅降低带宽压力是否开 DMA关开DMA 对大面积刷屏提升明显每改一个变量后重启板子再记录一次数据。这样很容易定位到项目里真正的帧率瓶颈是在 CPU 渲染、屏幕传输还是内存带宽。6. 常见问题与排查思路问题现象常见原因解决思路LVGL 界面只有十几 FPS单缓冲、SPI 频率过低、未开 DMA开启双缓冲提高 SPI 时钟