LCD

LCD(Liquid Crystal Display,液晶显示器)是 ESP32 项目中最常用的屏幕类型:全彩显示、尺寸和分辨率选择多、价格低、驱动生态成熟。若尚未确定屏幕选型,LCD 是适合入门的类型。
本章介绍 LCD 的成像原理与常见驱动 IC。阅读本章前,建议先了解 显示基础与接口 中的公共概念。
1. 显示原理
LCD 属于透射式显示:液晶本身不发光,画面的光来自屏幕最底层的背光(Backlight),液晶层的作用是控制透过的光量。下面从像素的构成开始,说明颜色的控制方式。
1.1 像素的组成与排列
用放大镜观察一块点亮的 LCD,会发现每个像素并不是一个整体,而是由红(R)、绿(G)、蓝(B)三个细长的子像素(Subpixel)并排组成,最常见的是纵向条状排列(RGB Stripe)。
因此,一块 240×320 的屏幕,面板上实际排布着 720(240×3)列 × 320 行的子像素阵列。子像素是面板上最小的控制单元:每个子像素有独立的电极,可以单独控制;但其上方覆盖的滤光片颜色固定,因此子像素不能变色,只能调节自身的亮度。
控制一个像素的颜色,等价于分别设定其 R、G、B 三个子像素的亮度:三者全亮为白色,全灭为黑色,仅 R 亮为红色,R、G 同亮为黄色。正常视距下人眼无法分辨单个子像素,感知到的是三者混合后的颜色。
1.2 子像素如何控光
子像素的亮度由面板的多层结构配合调节。实际面板由多层材料叠合而成,与控光直接相关的部分可简化为以下几层(从背光到屏幕表面):
- 背光层:LED 光源加导光板,提供均匀的白色光,常亮。
- 偏光片(垂直):背光发出的是向各个方向振动的自然光,穿过这一片后,只剩下沿垂直方向振动的偏振光。
- 液晶层:液晶分子呈螺旋状扭转排列,会使穿过的偏振光方向随之旋转。向子像素电极施加电压后,分子趋于沿电场方向排齐:电压越高,扭转越弱,偏振方向被旋转的角度越小。
- 偏光片(水平):透光方向与第一片垂直,只放行水平方向的偏振光。透光量由前一层的旋转角度决定:液晶完整扭转 90° 时,偏振方向恰好转为水平,光全部通过(最亮);液晶不扭转时,偏振方向保持垂直,光被完全挡住(最暗);旋转角度介于两者之间时,光部分通过。电压连续可调,透光量(亮度)随之连续可调,因此液晶层常被称为电压控制的光阀(Light Valve)。
- 彩色滤光片:覆盖在子像素上的滤色膜,只放行对应颜色。比如,R 子像素的滤光片只让红光通过,G、B 也是同理。
注意两片偏光片的方向是互相垂直的:若没有液晶层,垂直偏振光将被水平偏光片完全挡住,屏幕恒为黑色。液晶层的作用,正是在这两片偏光片之间按电压调节偏振方向的旋转角度。综合各层分工:背光提供光源,两片偏光片限定偏振方向,液晶的扭转角度决定透光量,滤光片决定输出颜色,每个子像素由此成为一个亮度可调的单色光点。
1.3 从像素数据到颜色
结合以上两节,即可完整描述像素数据到颜色的转换过程。以 RGB565 格式为例,16 位数据中 R 占 5 位、G 占 6 位、B 占 5 位,对应 R、B 子像素各 32 级亮度、G 子像素 64 级(这一位数分配的缘由见 显示基础与接口的色深章节)。驱动 IC 收到像素值后,将三个分量分别转换为对应子像素的驱动电压,控制各自的透光量:
| 像素值(RGB565) | R / G / B 分量 | 子像素状态 | 显示颜色 |
|---|---|---|---|
0xFFFF | 31 / 63 / 31 | 全部最大透光 | 白 |
0x0000 | 0 / 0 / 0 | 全部遮光 | 黑 |
0xF800 | 31 / 0 / 0 | 仅 R 透光 | 红 |
0xFFE0 | 31 / 63 / 0 | R、G 透光 | 黄 |
后续章节的核心任务,本质上是将一帧这样的像素值写入驱动 IC 的 GRAM(Graphics RAM,图形随机存取存储器),剩下的电压转换由驱动 IC 自动完成。
这一原理也解释了 LCD 的两个固有特点:
- 黑色不够纯:显示黑色时背光仍然常亮,液晶只能尽量遮挡而无法完全挡住,因此黑色偏灰,对比度低于自发光的 OLED / AMOLED。
- 功耗与画面内容无关:功耗主要来自背光,无论显示何种内容,背光都以相同功率工作。
1.4 TN 与 IPS
按液晶分子的排列方式,嵌入式常见的 LCD 面板分为两类:
| 面板类型 | 可视角 | 色彩表现 | 成本 |
|---|---|---|---|
| TN | 窄,斜视时偏色发白 | 一般 | 低 |
| IPS | 接近全视角 | 好 | 略高 |
2. 特性与局限
优势:
- 全彩显示,是运行彩色 GUI(如 LVGL,Light and Versatile Graphics Library)的常用屏幕类型。
- 尺寸与分辨率覆盖广,从 0.96 英寸到 7 英寸以上均有成熟产品。
- 成本在各类彩屏中最低,驱动资料和库支持丰富。
- 无烧屏问题,适合长时间显示固定内容(对比 OLED / AMOLED)。
局限:
- 背光持续耗电,不适合电池供电的常显场景。背光可通过 PWM 调光降低功耗(对应模块的 BL 引脚)。
- 对比度有限,黑色发灰。
- 阳光直射下,背光被环境光淹没,可读性一般。
若应用对功耗或户外可读性要求较高,可参考 显示屏总览 评估 RLCD 或 E-Paper 方案。
3. 常见驱动 IC 与接口
一块 LCD 模组通常由面板、背光源、驱动 IC 和 FPC 排线四部分构成。面板由驱动 IC 控制,主控实际通信的对象是这颗 IC。选屏或编程时,驱动 IC 型号比屏幕尺寸更关键:它决定了初始化命令、支持的接口和分辨率上限。常见型号如下:
| 驱动 IC | 常见分辨率 / GRAM | 常见接口 | 常见屏幕 |
|---|---|---|---|
| ST7735S | 132×162 | SPI | 0.96~1.8 英寸小屏 |
| GC9A01 | 240×240 | SPI | 1.28 英寸圆形屏 |
| ST7789 | 240×320 | SPI / I80 | 1.3~2.4 英寸,最常见 |
| ST7796 | 320×480 | SPI / I80 | 3.5~4 英寸 |
| AXS15231B | 320×480 / 172×640 | QSPI | 3.49~3.5 英寸屏 |
| ST77916 | 360×360 | QSPI | 1.85 英寸圆形 / 方形屏 |
几点说明:
- SPI、I80、QSPI 屏通常由驱动 IC 接收像素数据并完成屏内刷新;RGB 屏由 ESP32-S3 持续输出显示时序,驱动方式不同,本表不展开。
- 同一颗 IC 往往支持多种接口,实际可用哪种取决于面板引出的排线;微雪模块的接口类型见对应产品页。
- 面板分辨率可以小于 IC 的 GRAM 分辨率。例如 1.69 英寸屏为 240×280,使用 ST7789(GRAM 为 240×320),显示区域相对 GRAM 存在 20 行偏移。这正是驱动代码中偏移量(offset)参数的来源,配置错误会导致画面错位或边缘花屏。
4. Arduino + Arduino_GFX 点亮示例
本节核心逻辑适用于所有 ESP32 开发板,操作步骤以 微雪 ESP32-S3-Touch-LCD-1.69 为例。该开发板板载 1.69 英寸 240×280 IPS 屏,驱动 IC 为 ST7789V2。如果使用其他开发板或屏幕模块,只需替换引脚定义和驱动 IC 对应的类即可。
示例使用社区开源的 GFX Library for Arduino(Arduino_GFX)。它支持 SPI / QSPI / I80 / RGB 多种总线和绝大多数常见驱动 IC,是 Arduino 下驱动彩屏的通用选择。
4.1 安装库
在 Arduino IDE 的库管理器(工具 > 管理库)中搜索 GFX Library for Arduino 并安装。其他安装方式参考 Arduino 库管理教程。
4.2 确认引脚
ESP32-S3-Touch-LCD-1.69 的屏幕已直连主控,引脚固定如下(使用屏幕模块时按实际接线修改):
| 信号 | GPIO | 说明 |
|---|---|---|
| LCD_DC | GPIO4 | 命令 / 数据选择 |
| LCD_CS | GPIO5 | 片选 |
| LCD_CLK | GPIO6 | SPI 时钟 |
| LCD_DIN | GPIO7 | SPI 数据(MOSI) |
| LCD_RST | GPIO8 | 复位 |
| LCD_BL | GPIO15 | 背光控制 |
4.3 示例代码
#include <Arduino_GFX_Library.h>
// 引脚定义(ESP32-S3-Touch-LCD-1.69 或 ESP32-S3-LCD-1.69)
#define LCD_DC 4
#define LCD_CS 5
#define LCD_CLK 6
#define LCD_DIN 7
#define LCD_RST 8
#define LCD_BL 15
// 1. 创建 SPI 总线对象
Arduino_DataBus *bus = new Arduino_ESP32SPI(
LCD_DC, LCD_CS, LCD_CLK, LCD_DIN, GFX_NOT_DEFINED /* 无需 MISO */);
// 2. 创建显示对象:ST7789,240×280,IPS 面板,GRAM 偏移 20 行
Arduino_GFX *gfx = new Arduino_ST7789(
bus, LCD_RST, 0 /* 初始方向 */, true /* IPS */,
240 /* 宽 */, 280 /* 高 */,
0 /* 列偏移1 */, 20 /* 行偏移1 */,
0 /* 列偏移2 */, 20 /* 行偏移2 */);
void setup() {
// 3. 初始化屏幕
gfx->begin();
// 4. 打开背光
pinMode(LCD_BL, OUTPUT);
digitalWrite(LCD_BL, HIGH);
// 5. 清屏
gfx->fillScreen(RGB565_BLACK);
// 6. 显示文字
gfx->setTextColor(RGB565_WHITE);
gfx->setTextSize(2);
gfx->setCursor(60, 120);
gfx->println("Hello, LCD!");
// 7. 绘制白色矩形框
gfx->drawRect(20, 100, 200, 60, RGB565_WHITE);
// 8. 在底部绘制色块
int blockY = 230;
int blockW = 40;
int blockH = 40;
gfx->fillRect(0, blockY, blockW, blockH, RGB565_RED);
gfx->fillRect(40, blockY, blockW, blockH, RGB565_GREEN);
gfx->fillRect(80, blockY, blockW, blockH, RGB565_BLUE);
gfx->fillRect(120, blockY, blockW, blockH, RGB565_YELLOW);
gfx->fillRect(160, blockY, blockW, blockH, RGB565_CYAN);
gfx->fillRect(200, blockY, blockW, blockH, RGB565_MAGENTA);
}
void loop() {
}
编译并烧录后,屏幕显示黑底白字 "Hello, LCD!"、白色矩形边框,以及底部一排红、绿、蓝、黄、青、品红色块。
代码中的两个构造对象对应 显示基础与接口 中的分层:Arduino_DataBus 负责总线传输(更换接口只需替换该类),Arduino_ST7789 负责驱动 IC 的命令(更换驱动 IC 只需替换该类)。gfx->begin() 完成复位和初始化序列,之后的绘图 API 底层执行的均为设置窗口与写入像素两类操作。
对本库而言,与具体硬件相关的只有引脚定义和这两个构造对象。显示对象初始化完成后,绘图部分与硬件无关:既可以将这段配置代入库自带的其他示例(文件 > 示例 > GFX Library for Arduino)直接运行,也可以在此基础上调用绘图 API 编写自己的画面。
构造函数中的行偏移 20,来自 GRAM 与面板高度之差:ST7789V2 的 GRAM 为 240×320,面板实际显示区域只有 240×280,相差 40 行。面板在 GRAM 寻址范围内居中排布,因此上下各留出 40÷2=20 行未使用的区域,行偏移即为这部分空白。列偏移为 0,是因为宽度方向面板与 GRAM 均为 240,没有差值需要补偿。更换其他分辨率的面板或驱动 IC 时,可按同样的思路,用 GRAM 分辨率减去面板分辨率再折半,推算对应的 offset。两类构造对象支持的总线、驱动 IC 与完整参数说明见 Arduino_GFX 官方 wiki 的 Data Bus Class 与 Display Class 页面。
4.4 旋转显示方向
ST7789V2 可通过 MADCTL 寄存器调整行列扫描方向。Arduino_GFX 在 Arduino_ST7789 构造函数和 setRotation() 中均提供了方向参数,取值 0~3,表示四个相差 90° 的显示方向。旋转后,库会同步调整坐标系和 GRAM 偏移;横屏方向下,逻辑宽高也会互换。
| 旋转值 | 相对于方向 0 | 逻辑分辨率 |
|---|---|---|
0 | 0° | 240×280 |
1 | 90° | 280×240 |
2 | 180° | 240×280 |
3 | 270° | 280×240 |
创建 Arduino_ST7789 对象时,构造函数的第三个参数用于设置初始方向。例如,下面代码中的 0 /* 初始方向 */ 表示按方向 0 启动;若屏幕只需固定为横屏,可将其改为 1 或 3,无需在运行过程中再次调用 setRotation()。setRotation() 用于初始化后动态切换显示方向。
以下完整程序每隔 2 秒切换一次显示方向,边框和文字会按旋转后的坐标系重新绘制:
#include <Arduino_GFX_Library.h>
// 引脚定义(ESP32-S3-Touch-LCD-1.69 或 ESP32-S3-LCD-1.69)
#define LCD_DC 4
#define LCD_CS 5
#define LCD_CLK 6
#define LCD_DIN 7
#define LCD_RST 8
#define LCD_BL 15
// 创建 SPI 总线对象
Arduino_DataBus *bus = new Arduino_ESP32SPI(
LCD_DC, LCD_CS, LCD_CLK, LCD_DIN, GFX_NOT_DEFINED /* 无需 MISO */);
// 创建显示对象:ST7789,240×280,IPS 面板,GRAM 偏移 20 行
Arduino_GFX *gfx = new Arduino_ST7789(
bus, LCD_RST, 0 /* 初始方向 */, true /* IPS */,
240 /* 宽 */, 280 /* 高 */,
0 /* 列偏移1 */, 20 /* 行偏移1 */,
0 /* 列偏移2 */, 20 /* 行偏移2 */);
void drawRotationDemo(uint8_t rotation) {
gfx->setRotation(rotation);
gfx->fillScreen(RGB565_BLACK);
gfx->drawRect(0, 0, gfx->width(), gfx->height(), RGB565_WHITE);
gfx->setTextColor(RGB565_WHITE);
gfx->setTextSize(2);
gfx->setCursor(10, 10);
gfx->print("Rotation: ");
gfx->println(rotation);
gfx->setCursor(10, 40);
gfx->print(gfx->width());
gfx->print(" x ");
gfx->println(gfx->height());
}
void setup() {
// 初始化屏幕
gfx->begin();
// 打开背光
pinMode(LCD_BL, OUTPUT);
digitalWrite(LCD_BL, HIGH);
}
void loop() {
static uint8_t rotation = 0;
drawRotationDemo(rotation);
rotation = (rotation + 1) % 4;
delay(2000);
}
setRotation() 只改变后续绘图使用的坐标系。切换方向后,应清屏并重新绘制画面。
4.5 常见问题
| 现象 | 常见原因 | 处理 |
|---|---|---|
| 屏幕全白 / 无显示 | 接线或引脚定义错误;背光未打开 | 核对引脚表;确认 BL 已置高 |
| 画面偏移、边缘花屏 | GRAM 偏移量不对 | 按面板实际分辨率设置构造函数中的 offset 参数 |
| 颜色反转(黑白颠倒) | IPS 参数与面板不符 | 切换构造函数中的 ips 参数,或调用 invertDisplay() |
| 红蓝颠倒 | RGB / BGR 子像素顺序不符 | 多数驱动类提供颜色顺序参数,或查阅所用库的说明 |
| 显示正常但很慢 | SPI 频率过低 | 初始化时通过 gfx->begin(80000000) 指定更高的 SPI 时钟频率(显示类会将其转发给总线) |