显示基础与接口
无论驱动哪种类型的屏幕,像素、色深、帧缓冲、显示方向与坐标系这些基础概念都是通用的;而屏幕与主控之间的数据通路(即显示接口)则直接决定了刷新带宽和引脚开销。本章介绍这两部分内容,作为后续各屏幕章节的基础。
1. 显示的公共概念
1.1 像素与分辨率
屏幕上的画面由一个个像素(Pixel)组成。分辨率描述屏幕横向和纵向各有多少个像素,例如 240×320 表示横向 240 个、纵向 320 个像素。驱动屏幕的本质,是为每个像素指定要显示的颜色。
1.2 色深
色深(Color Depth)指每个像素用多少位(bit)来描述颜色,直接决定了一个像素占用的数据量:
| 色深 | 每像素数据量 | 可表示颜色 | 典型场景 |
|---|---|---|---|
| 1bpp(单色) | 1 bit | 黑 / 白 | 单色 OLED、RLCD、黑白墨水屏 |
| RGB565 | 16 bit(2 字节) | 65536 色 | 嵌入式彩屏的主流格式 |
| RGB888 | 24 bit(3 字节) | 约 1677 万色 | 高色彩要求的场景 |
RGB565 用 5 位表示红色、6 位表示绿色、5 位表示蓝色,在色彩效果和数据量之间取得平衡,是 ESP32 上彩屏开发最常用的格式。
嵌入式 LCD 常用 RGB565,是因为一个像素正好占 16 位(2 字节),比 24 位 RGB888 少占三分之一的显存和传输带宽。对 SPI、I80 等接口而言,少传 1 字节通常能直接降低刷新压力,因此很多中小尺寸 LCD 示例和图形库默认使用 RGB565。
绿色分量占 6 位,是因为人眼对绿色亮度变化更敏感,给 G 多分配 1 位可让亮度过渡更细。这样总位数仍保持 16 位,同时把更多层级留给对视觉亮度影响较大的绿色通道。
由“分辨率 × 色深”可以算出一帧完整画面的数据量:
| 屏幕示例 | 分辨率 | 色深 | 一帧数据量 |
|---|---|---|---|
| 0.96 英寸单色 OLED | 128×64 | 1bpp | 1 KB |
| 1.28 英寸圆形 LCD | 240×240 | RGB565 | 112.5 KB |
| 2.4 英寸 LCD | 240×320 | RGB565 | 150 KB |
| 4.3 英寸 RGB 屏 | 800×480 | RGB565 | 750 KB |
帧数据量直接影响两个关键因素:刷新一帧需要传输多少数据(影响帧率),以及缓存一帧画面需要多少内存(影响内存方案)。
1.3 帧缓冲与显存(GRAM)
屏幕上的画面需要持续维持,因此必须有一块内存保存当前画面的全部像素数据,这块内存称为帧缓冲(Frame Buffer)。它可能位于两个位置:
- 屏幕侧:多数 SPI / I80 接口的屏幕,其驱动 IC 内部集成了显存(GRAM)。主控把像素数据写入 GRAM 后,驱动 IC 自行负责持续刷新面板。主控无需保存整帧画面,内存压力小,画面不更新时主控无需执行任何操作。
- 主控侧:RGB 接口的屏幕没有 GRAM,主控必须在自己的内存中维护完整帧缓冲,并按固定时序不间断地向屏幕输出数据。以 800×480 RGB565 为例,一帧就需要 750 KB,远超 ESP32 的片内 RAM,因此驱动 RGB 屏的开发板都需要配备 PSRAM。
这是选择接口时最核心的区别之一:SPI 屏对主控内存要求低,RGB 屏必须有 PSRAM。
1.4 刷新与画面撕裂
屏幕并不会在写入后立即显示对应内容,驱动 IC 会按固定的刷新率(如 60 Hz)周期性地把显存内容扫描到面板上。如果主控写入 GRAM 的过程恰好跨越了一次扫描,屏幕上就会同时出现新旧两帧的内容,称为画面撕裂(Tearing)。
部分屏幕引出了 TE(Tearing Effect)引脚,在每次扫描开始时输出一个同步脉冲。主控可以等待该信号后再写入数据,从而避开扫描过程、消除撕裂。静态画面或低速刷新的应用一般无需处理撕裂问题。
有关撕裂的更多讲解,请参考 ESP-IoT-Solution 编程指南 - LCD 屏幕撕裂详解。
1.5 全屏刷新与窗口刷新
带 GRAM 的屏幕支持窗口寻址(Window Addressing):先通过命令指定一个矩形区域,之后写入的像素数据只落在该区域内。因此更新画面时不必重传整帧,只刷新变化的部分即可,这能显著降低传输量。LVGL 等图形库的“局部刷新”就是基于这一机制实现的。
墨水屏的“局刷”是另一个概念,它涉及电泳粒子的驱动波形,详见 E-Paper 章节。
1.6 显示方向与坐标系
屏幕的原生方向由面板和驱动 IC 的行列排列决定。以原生宽度为 W、高度为 H 的屏幕为例,通常将左上角定义为坐标原点 (0, 0),X 轴向右递增,Y 轴向下递增。模组安装到设备后,屏幕的物理方向可能与原生方向不同,因此需要将显示内容旋转到对应方向。
显示方向改变后,图形库使用的逻辑坐标系也会随之改变。以原生画面顺时针旋转为例,原坐标 (x, y) 与旋转后坐标的关系如下:
| 顺时针旋转角度 | 逻辑宽度 × 高度 | 原坐标映射到旋转后坐标 |
|---|---|---|
| 0° | W × H | (x, y) |
| 90° | H × W | (H - 1 - y, x) |
| 180° | W × H | (W - 1 - x, H - 1 - y) |
| 270° | H × W | (y, W - 1 - x) |
实际开发时通常不需要手动计算这些坐标,屏幕驱动或图形库会提供显示方向设置。按照像素转换发生的位置,旋转可分为两种方式:
| 旋转方式 | 实现原理 | 特点 |
|---|---|---|
| 控制器侧旋转(通常称为硬件旋转) | 带 GRAM 的驱动 IC 通过行列交换和轴向翻转改变像素寻址方向,部分驱动 IC 使用 MADCTL 等寄存器控制 | 使用驱动 IC 自身的寻址能力,主控无需重排整帧像素,也不需要增加完整的旋转缓冲区;可用方向取决于驱动 IC |
| 主控侧软件旋转 | CPU、软件画布或 GUI 框架先转换帧缓冲区,再将转换后的像素写入屏幕 | 不依赖驱动 IC 提供完整的旋转能力,但会增加内存占用、数据复制量和计算开销 |
对于 0°、90°、180°、270° 旋转,如果驱动 IC 支持所需方向,应优先使用控制器侧旋转。它直接使用屏幕控制器的寻址功能,配置完成后仍可按旋转后的逻辑坐标绘图。驱动 IC 不支持行列交换,或者应用需要其他角度和图像变换时,再使用主控侧软件旋转。
Arduino_GFX 的 setRotation()、u8g2 构造器中的 U8G2_R0~U8G2_R3 等接口用于设置显示方向。设置后,图形库会按照目标方向调整逻辑坐标和宽高;像素最终由驱动 IC 重新寻址,还是在主控侧完成转换,取决于具体驱动和缓冲方式。
不同图形库对旋转编号和起始方向的定义不完全相同,部分驱动 IC 也只支持轴向翻转,不能完成 90° / 270° 的行列交换。设置方向前,应查阅所用图形库和驱动的说明;设置后,应读取图形库提供的逻辑宽度和高度,并检查四角的图形是否完整。自行编写驱动时,还需确认不同方向下的窗口寻址偏移。
触摸 IC 通常仍按触摸面板的原生方向输出坐标。显示方向改变后,触摸坐标需要执行相同的轴交换和镜像变换,否则触点位置会与画面错位。具体处理方法见 触摸控制的“坐标与显示的对齐”章节。
2. 显示接口对比
屏幕与主控之间常见的接口如下表,核心差异在于数据线数量(决定带宽和引脚开销)以及屏端是否带 GRAM(决定主控内存需求):
| 接口 | 数据线 | 典型带宽 | 屏端 GRAM | 典型应用 |
|---|---|---|---|---|
| I2C | 1(双向) | ≤ 1 Mbps | 有 | 0.96 英寸等小尺寸单色 OLED |
| SPI(4 线) | 1 | 40~80 Mbps | 有 | 3.5 英寸以下彩屏、墨水屏、RLCD |
| QSPI | 4 | SPI 的 4 倍 | 有 | 1~2 英寸高分辨率 AMOLED |
| I80(8080 并口) | 8 / 16 | 较高 | 有 | 中小尺寸 LCD |
| RGB 并口 | 16 / 18 / 24 | 高 | 无 | 3.5~7 英寸大屏 |
| MIPI-DSI | 1~2 对差分线 | 很高 | 可有可无 | 高分辨率屏(ESP32-P4) |
2.1 SPI:最常用的接口
SPI 是 ESP32 上最常见的屏幕接口,引脚少、接线简单,几乎所有系列的 ESP32 都支持。典型的 4 线 SPI 屏幕模块引脚如下(与微雪屏幕模块的丝印对应):
| 引脚 | 说明 |
|---|---|
| VCC / GND | 电源 |
| DIN(MOSI) | 数据线,主控 → 屏幕 |
| CLK(SCLK) | 时钟线 |
| CS | 片选,低电平有效 |
| DC | 命令 / 数据选择:低电平表示发送命令,高电平表示发送像素数据 |
| RST | 复位,低电平有效。部分模块通过上拉电阻将其固定为高电平,此时无需占用 GPIO,驱动代码中复位引脚参数填 -1 |
| BL | 背光控制(LCD 特有,可接 PWM 调节亮度) |
其中 DC 引脚是显示 SPI 相较普通 SPI 多出的关键信号:屏幕驱动 IC 通过该引脚的电平,区分收到的字节是命令还是数据。
2.2 帧率的三个环节
日常所说的“帧率”实际包含三个相互独立的环节,画面的实际流畅度由三者中的最小值决定:
-
渲染帧率:主控生成图像数据的速率。取决于主控性能与画面复杂度(局部更新通常快于全屏更新),是动态变化的。
-
接口帧率:图像数据经显示接口传输到驱动 IC 的速率。由接口带宽与一帧数据量决定,初始化后基本固定:
接口帧率上限 ≈ 接口带宽 ÷ 一帧数据量 -
屏幕刷新率:驱动 IC 将显存内容扫描到面板的速率(见刷新与画面撕裂)。SPI / I80 屏由驱动 IC 决定(可通过命令配置),RGB 屏则等于接口帧率。
接口帧率是选型时最需要估算的环节。以 40 MHz 的 SPI 驱动 240×320 RGB565 屏幕为例:一帧约 1.23 Mbit,接口帧率上限约为 40 Mbps ÷ 1.23 Mbit ≈ 32 帧/秒(实际还有命令与协议开销,会更低一些)。这一估算可以解释几个常见现象:
- SPI 驱动 2 英寸级别的屏幕运行 GUI 是流畅的,但驱动更大分辨率则带宽不足。
- AMOLED 分辨率高(如 410×502、466×466),SPI 带宽不够,所以普遍采用 QSPI。
- 800×480 这类大屏一帧 750 KB,串行接口均无法达到可用帧率,必须使用 RGB 或 MIPI-DSI 接口。
若接口帧率估算充足而实际画面仍不流畅,瓶颈通常在渲染帧率,应优化绘制逻辑或降低画面复杂度,而非提高接口时钟。
2.3 各接口小结
- I2C:引脚最少(与其他 I2C 器件共用总线),但带宽最低,只适合小尺寸单色 OLED。
- SPI:通用性最好的选择,本教程大部分示例基于 SPI 屏幕。
- QSPI:将 SPI 的 1 根数据线扩展为 4 根,协议相近,带宽提升至 4 倍,是高分辨率 AMOLED 的主流接口。
- I80:并行传输带宽高于 SPI,但占用 8~16 个 GPIO,在引脚紧张的 ESP32 上使用较少。
- RGB:主控直接输出面板时序,带宽高、成本低,但占用 GPIO 多且必须整帧缓冲(依赖 PSRAM)。
- MIPI-DSI:手机屏使用的高速差分接口,引脚少带宽高,目前仅 ESP32-P4 支持。
3. ESP32 的显示接口支持
不同系列的 ESP32 支持的显示接口不同,选屏前需要确认主控能力:
| 接口 | 支持的 ESP32 系列 |
|---|---|
| I2C / SPI / QSPI | 全系列(通过通用 I2C / SPI 外设) |
| I80 | 原生支持:ESP32(I2S 的 LCD 模式)、ESP32-S2、ESP32-S3、ESP32-S31、ESP32-P4(专用 LCD 外设) 模拟支持:ESP32-C5 / C6 / H2 等无原生 I80 外设的型号,可用 Parlio 外设模拟 I80 时序 |
| RGB | ESP32-S3、ESP32-S31、ESP32-P4 |
| MIPI-DSI | ESP32-P4 |
各系列的具体支持情况以 ESP-IoT-Solution:LCD 开发指南 - 支持的接口类型 为准。
其中 ESP32-P4 接口最完整,是高分辨率屏幕的首选;ESP32-S3 凭借专用 LCD 外设和可选的大容量 PSRAM,是目前带屏应用最常用的型号;新推出的 ESP32-S31 支持的接口类型与 ESP32-S3 相同,RGB 屏的实测帧率更高;ESP32-C3 / C6 等主要使用通用 SPI,适合搭配中小尺寸 SPI 屏。
在 Arduino 生态中,上述接口差异大多已由图形驱动库封装,常用的库有:
| 库 | 适用范围 |
|---|---|
| GFX Library for Arduino(Arduino_GFX) | 彩屏通用图形库,支持 SPI / QSPI / I80 / RGB 多种总线与大量驱动 IC |
| LovyanGFX | 彩屏图形库,性能优秀,支持触摸与多种总线 |
| TFT_eSPI | 经典 SPI 彩屏图形库,通过配置文件适配屏幕 |
| u8g2 | 单色屏图形库,OLED / RLCD 常用,字体资源丰富 |
| GxEPD2 | 墨水屏驱动库 |
| 微雪示例程序包 | 各开发板 / 屏幕模块的官方驱动与示例,引脚和参数已适配好 |
各类屏幕分别适合用哪个库,在对应章节和 GUI 框架 章节中具体介绍。
使用微雪的带屏开发板(如 ESP32-S3-Touch-LCD 系列)时,屏幕已直连主控,无需接线;官方示例程序包中的引脚定义和初始化参数可直接使用。
4. 驱动一块屏幕的通用流程
各类屏幕与接口的驱动流程基本一致,理解该流程有助于阅读后续章节的示例代码:
- 初始化总线:配置 SPI / I2C 等接口的引脚和时钟频率。
- 复位屏幕:拉低 RST 引脚一段时间后释放,使驱动 IC 回到确定状态。
- 发送初始化序列:向驱动 IC 写入一串配置命令,设置电源、扫描方向、像素格式等。这串命令由屏厂针对具体面板调校,一般直接使用微雪示例或驱动库中现成的初始化代码,无需自己编写。
- 设置写入窗口:指定接下来要更新的矩形区域(全屏或局部)。
- 写入像素数据:按选定的像素格式(如 RGB565)连续写入该区域的像素,画面随之显示。
后续更新画面就是重复第 4、5 步。使用 Arduino_GFX、u8g2 这类库时,第 1~3 步由库的 begin() 完成,第 4、5 步则被封装为 drawPixel()、fillRect()、drawBitmap() 等绘图 API,但库底层执行的仍是上述五个步骤。
5. 延伸阅读
- 乐鑫 ESP-IoT-Solution:LCD 开发指南:官方中文资料,基于 esp_lcd / ESP-IDF,包含各接口的参数对比、典型连接方式与 LCD 术语表,可作为本章的进阶补充。
了解这些公共概念后,即可进入具体屏幕类型的章节。若尚未确定屏幕类型,可回到 显示屏总览 查看选型对比。