1. 项目概述:一块1.47英寸LCD模块的深度探索
最近在捣鼓一个需要显示信息的小项目,手头正好有一块1.47英寸的LCD模块。这尺寸听起来有点“非主流”,比常见的0.96英寸大一圈,又比1.8英寸小一点,属于那种在特定空间限制下追求更大可视面积的折中选择。市面上这类模块通常基于ST7735S或ST7789这类驱动芯片,分辨率常见的是172x320(竖屏)或320x172(横屏),色彩深度能达到16位(RGB565),显示效果相当细腻。对于嵌入式开发者、电子爱好者或者想给树莓派Pico、Arduino、ESP32等主控板加个“眼睛”的朋友来说,这类小屏是绝佳的交互窗口。它能显示文本、图形、图标甚至简单的动画,应用场景从智能穿戴设备的副屏、微型游戏机、传感器数据显示终端到DIY的小型仪表盘,非常广泛。这篇文章,我就以手头这块屏为例,带你从硬件引脚、驱动原理、软件适配一路走到实际应用,把这块小屏玩透。
2. 硬件接口与电路连接详解
拿到一块裸屏模块,第一步永远是看引脚定义和接电路。别看它小,接口可能有好几种,接错了轻则不显示,重则烧芯片。
2.1 常见接口类型与引脚定义
1.47英寸LCD模块常见的接口是SPI(串行外设接口),因为SPI引脚少、速度快,非常适合驱动这种分辨率不算太高的彩屏。一块典型的SPI接口1.47寸屏,其引脚通常包括:
| 引脚名称 | 功能描述 | 连接注意事项 |
|---|---|---|
| VCC | 电源正极 | 通常为3.3V,务必确认模块工作电压,接5V可能永久损坏。 |
| GND | 电源地 | 与主控板共地。 |
| SCL / SCK | SPI时钟线 | 主控输出,屏幕输入。 |
| SDA / MOSI | SPI数据线(主出从入) | 主控输出,屏幕输入。 |
| RES / RST | 复位引脚 | 低电平有效,用于硬件复位屏幕控制器。 |
| DC / A0 | 数据/命令选择引脚 | 关键引脚!高电平时SDA上传的是显示数据(像素颜色),低电平时传的是命令(如设置扫描方向)。 |
| CS / CS | 片选引脚 | 低电平有效,选中该SPI从设备。 |
| BLK / LED | 背光控制 | 通常接高电平(3.3V)常亮,或接PWM引脚实现亮度调节。 |
注意:有些模块为了简化,可能将RES引脚内部上拉,外部只需接VCC即可,但为了可靠复位,建议还是连接到主控的GPIO上。背光引脚如果是LED+和LED-,则需要在中间串联一个限流电阻(如100欧姆)。
2.2 与主流主控板的连接示例
以最流行的ESP32开发板(如ESP32 DevKitC)和树莓派Pico为例,连接方式非常直观。关键在于利用硬件SPI接口以获得最佳性能。
连接ESP32:ESP32通常有两组硬件SPI(HSPI和VSPI)。我们可以任意选用一组,例如VSPI。
VCC-> ESP32的3.3V引脚GND-> ESP32的GNDSCL-> ESP32的GPIO 18(VSPI SCK)SDA-> ESP32的GPIO 23(VSPI MOSI)RES-> ESP32的GPIO 4(任意空闲GPIO)DC-> ESP32的GPIO 2(任意空闲GPIO)CS-> ESP32的GPIO 5(任意空闲GPIO,或接GND如果模块上只有一个SPI设备)BLK-> ESP32的3.3V或GPIO(用于PWM调光)
连接树莓派Pico(RP2040):RP2040也有两组硬件SPI(SPI0和SPI1)。我们使用SPI0。
VCC-> Pico的3.3V OUT(Pin 36)GND-> Pico的GND(Pin 38)SCL-> Pico的GPIO 18(SPI0 SCK)SDA-> Pico的GPIO 19(SPI0 MOSI)RES-> Pico的GPIO 20DC-> Pico的GPIO 21CS-> Pico的GPIO 17(SPI0 CSn)BLK-> Pico的3.3V OUT
实操心得:在面包板上连接时,最稳妥的方法是先用万用表蜂鸣档,逐一检查VCC和GND是否短路,再上电。第一次测试时,可以暂时不接背光(BLK),如果程序正确,你可能会在特定角度下看到非常暗淡的显示内容,这能证明驱动芯片和核心通信是正常的,只是背光没亮。
3. 驱动原理与初始化序列剖析
点亮屏幕不仅仅是通电和发数据那么简单,驱动芯片(如ST7735S)就像屏幕的“大脑”,需要一套精确的“开机指令”进行配置,才能正常工作和显示我们想要的画面。
3.1 驱动芯片如何工作
以ST7735S为例,它内部有一个显存(GRAM),对应着屏幕上的每一个像素点。我们的任务就是通过SPI总线,向这个GRAM写入颜色数据。但是,在写数据之前,必须告诉驱动芯片一系列参数:比如扫描方向(是从左到右还是从上到下)、颜色格式(是RGB565还是其他)、帧率、电源配置等等。这些配置都是通过发送“命令”(Command)和紧随其后的“参数”(Data)来实现的。
DC引脚在此扮演了至关重要的角色:当DC置为低电平,接下来通过SDA(MOSI)发送的一个字节就会被芯片解释为命令码;当DC置为高电平,接下来发送的字节则被解释为数据(可以是命令的参数,也可以是像素颜色值)。例如,设置扫描方向的命令可能是0x36,其后需要跟一个字节的数据来指定具体模式。
3.2 关键初始化命令解析
一个典型的初始化序列包含几十条命令。我们挑几条最核心的来理解:
- 软件复位(SWRESET, 0x01):发送此命令让驱动芯片内部复位,通常需要等待几毫秒让其稳定。
- 退出睡眠模式(SLPOUT, 0x11):屏幕刚上电可能处于睡眠省电模式,此命令唤醒它。发送后需要等待120ms以上。
- 颜色接口格式(COLMOD, 0x3A):这是最关键的命令之一。它告诉芯片我们后续发送像素数据所用的格式。对于16位色(65K色),我们通常发送参数
0x05,代表RGB565格式(即一个像素用2个字节表示:高5位红+6位绿+低5位蓝)。 - 内存访问控制(MADCTL, 0x36):这是另一个极其重要的命令。它控制显示方向、颜色顺序等。通过修改其参数,我们可以实现屏幕0°、90°、180°、270°旋转,以及镜像显示。例如,参数
0x00通常代表横屏模式,0xA0或0xC0可能代表竖屏模式,具体需要查阅具体屏的规格书或驱动库源码。 - 显示开启(DISPON, 0x29):完成所有配置后,发送此命令正式开启显示。
踩坑实录:最常遇到的问题就是“花屏”或颜色错乱。十有八九是
COLMOD命令设置的颜色格式与后续绘图函数发送的数据格式不匹配。比如库默认发送RGB888(3字节),但屏幕配置成了RGB565(2字节),必然错乱。另一个就是MADCTL设置的方向不对,导致绘图坐标计算全部错误,图形显示在奇怪的位置。
4. 软件驱动库选择与适配实战
我们不可能每次都从零开始写SPI底层和初始化序列,利用成熟的驱动库是最高效的方式。针对不同的主控和开发环境,选择也不同。
4.1 Arduino 环境下的 TFT_eSPI 库
对于Arduino IDE(兼容ESP32、ESP8266、STM32等),TFT_eSPI库是功能最强大、社区支持最好的选择之一。它支持数十种驱动芯片和屏幕。
适配步骤:
- 安装库:在Arduino库管理中搜索并安装
TFT_eSPI。 - 配置用户设置文件:这是最关键的一步。找到Arduino安装目录下的
libraries/TFT_eSPI/User_Setup.h文件(或User_Setup_Select.h),我们需要编辑它。 - 选择驱动和定义引脚:通常不直接修改
User_Setup.h,而是通过User_Setup_Select.h来选择或新建一个配置文件。更简单的方法是,在TFT_eSPI库的User_Setups文件夹下,找一个接近的配置(如Setup24_ST7735.h)复制并重命名为Setup47_ST7735.h,然后修改其中内容。// 示例片段 (Setup47_ST7735.h) #define ST7735_DRIVER // 声明使用ST7735驱动 #define TFT_WIDTH 172 // 设置屏幕宽度(像素) #define TFT_HEIGHT 320 // 设置屏幕高度(像素) #define TFT_RGB_ORDER TFT_RGB // 颜色顺序,如果红色蓝色反了可以改为TFT_BGR #define TFT_INVERSION_ON // 通常需要开启,否则颜色可能反相 // 定义引脚 #define TFT_CS PIN_D5 // 你的CS引脚对应Arduino引脚号 #define TFT_DC PIN_D3 // 你的DC引脚 #define TFT_RST PIN_D4 // 你的RST引脚,如果共用可设为-1 // 定义SPI频率 #define SPI_FREQUENCY 27000000 // 27MHz,在ESP32上可以尝试更高 - 在
User_Setup_Select.h中启用你的配置:找到一行类似#include <User_Setups/Setup47_ST7735.h>,取消它的注释。 - 编写测试代码:
#include <TFT_eSPI.h> TFT_eSPI tft = TFT_eSPI(); void setup() { tft.init(); tft.setRotation(1); // 根据你的屏幕方向调整,0-3 tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.drawString("Hello, 1.47!", 10, 10, 4); } void loop() {}
4.2 MicroPython / CircuitPython 环境
对于树莓派Pico、ESP32的MicroPython环境,常用st7789或st7735的库。由于1.47寸屏多用ST7735S,我们可以使用st7735库。
适配步骤(以RP2040 + MicroPython为例):
- 刷写MicroPython固件到Pico。
- 上传驱动库文件:将
st7735.py(或类似驱动文件)通过Thonny等IDE上传到Pico的文件系统。 - 连接硬件:如前文所述连接好硬件。
- 编写测试代码:
import machine import st7735 import time # 定义SPI和引脚 spi = machine.SPI(0, baudrate=20000000, polarity=0, phase=0, sck=machine.Pin(18), mosi=machine.Pin(19)) tft = st7735.ST7735(spi, rst=machine.Pin(20), dc=machine.Pin(21), cs=machine.Pin(17), width=172, height=320) # 初始化 tft.init() # 清屏并显示文字 tft.fill(st7735.BLACK) tft.text("MicroPython", 10, 10, st7735.WHITE) tft.show() # 有些库需要显式调用show,有些是立即生效
注意事项:MicroPython的SPI速度(baudrate)不要一开始就设得太高,先从10MHz开始测试,稳定后再逐步提高。另外,
st7735库可能需要根据你的屏幕具体型号修改初始化序列,这需要查看库文件的init函数部分。
5. 图形绘制与界面优化技巧
驱动点亮后,真正的乐趣在于绘制内容。无论是显示传感器数据还是制作小动画,都需要基本的图形学知识。
5.1 基础绘图函数与坐标系统
所有图形库都提供类似的基础函数:
fillScreen(color):用指定颜色清空整个屏幕。drawPixel(x, y, color):在坐标(x, y)画一个点。drawLine(x0, y0, x1, y1, color):画线。drawRect(x, y, w, h, color):画空心矩形。fillRect(x, y, w, h, color):画实心矩形。drawCircle(x, y, r, color):画空心圆。fillCircle(x, y, r, color):画实心圆。setTextColor(foreground, background):设置文本颜色(前景色和背景色)。drawString(string, x, y, font):在指定位置用指定字体绘制字符串。
坐标系统陷阱:原点(0,0)默认通常在屏幕的左上角,X轴向右递增,Y轴向下递增。但当你使用setRotation()函数旋转屏幕后,这个坐标系也会跟着旋转!例如,旋转90度后,原来的“宽度”变成了“高度”,绘图时必须根据新的宽高来调整坐标计算,否则内容会显示在屏幕外。
5.2 提升显示性能与流畅度
小屏资源有限,优化很重要。
- 局部刷新:如果只是更新部分区域(如一个数字),不要调用
fillScreen清全屏,只调用fillRect清除那个数字所在的区域,然后再绘制新内容。这能极大减少闪烁和提升速度。 - 使用帧缓冲区(Frame Buffer):高级玩法。在内存中开辟一块和屏幕分辨率一样大的缓冲区(对于172x320的RGB565,需要约107.5KB内存)。所有绘图操作先在内存缓冲区中进行,完成一整帧画面后,一次性调用
pushImage或swap函数将整个缓冲区数据发送到屏幕。这能完全消除闪烁,实现流畅动画。TFT_eSPI库通过setSwapBytes和pushImage函数支持此功能,但需要主控有足够内存(ESP32通常可以)。 - 双缓冲区(Double Buffering):在帧缓冲区的基础上,使用两个缓冲区。当一个缓冲区正在被后台绘图时,另一个缓冲区的内容被送到前台显示。绘制完成后交换缓冲区。这是实现复杂动画的终极方案,但对内存要求更高。
- 精简与压缩:
- 使用小字体,减少文本占用的像素数量。
- 对于图标、位图,使用取模软件将其转换为字节数组,并以
PROGMEM(Arduino)或const方式存储,减少RAM占用。绘制时使用drawBitmap函数。 - 如果颜色种类不多,可以考虑使用颜色索引模式(但大多数小屏库不支持,需自己实现)。
5.3 制作一个简单的数据仪表盘
结合上述知识,我们可以快速做一个显示温湿度传感器数据的界面。
// Arduino + TFT_eSPI 示例片段 #include <TFT_eSPI.h> TFT_eSPI tft = TFT_eSPI(); float temp = 25.6; float humidity = 60.3; void setup() { tft.init(); tft.setRotation(1); tft.fillScreen(TFT_BLACK); // 绘制静态标题和边框 tft.setTextColor(TFT_CYAN, TFT_BLACK); tft.drawString("Env Monitor", 20, 10, 4); tft.drawRect(10, 50, tft.width()-20, 100, TFT_WHITE); } void loop() { // 模拟读取传感器数据,实际应替换为真实传感器代码 // temp = readTemperature(); // humidity = readHumidity(); // 局部刷新:先清除旧数据区域 tft.fillRect(20, 70, 120, 60, TFT_BLACK); // 绘制新数据 tft.setTextColor(TFT_YELLOW, TFT_BLACK); tft.drawString("Temp:", 20, 70, 2); tft.drawFloat(temp, 1, 70, 70, 2); // 在(70,70)位置绘制浮点数,保留1位小数 tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.drawString("Humi:", 20, 90, 2); tft.drawFloat(humidity, 1, 70, 90, 2); tft.drawString("%", 110, 90, 2); delay(2000); // 每2秒更新一次 }这个例子展示了如何划分静态UI和动态数据区域,并通过局部刷新来高效更新。在实际项目中,你还可以添加进度条、趋势图等更丰富的元素。
6. 功耗管理与背光控制
对于电池供电的项目,屏幕往往是耗电大户。1.47寸LCD的功耗主要由背光LED和驱动芯片本身产生。
背光控制:这是省电的关键。如果背光引脚(BLK)直接接VCC,则背光常亮。更优的做法是将其连接到一个GPIO引脚上。
- 开关控制:需要显示时,将GPIO设为高电平打开背光;进入待机或不需要看屏幕时,设为低电平关闭背光。这能立刻节省大量电流。
- PWM调光:将背光引脚连接到支持PWM输出的GPIO。通过调节PWM的占空比(0-255),可以无级调节屏幕亮度。在环境光暗时降低亮度,既能省电又能保护眼睛。在Arduino中,使用
analogWrite(pin, brightness)即可实现。
驱动芯片功耗模式:通过发送命令,可以让ST7735S等驱动芯片进入睡眠模式(SLPIN, 0x10)。在此模式下,芯片内部大部分电路关闭,功耗可降至微安级别。唤醒则需要发送SLPOUT (0x11)命令并等待一段时间。在长时间不需要显示的场合,结合关闭背光和进入睡眠模式,可以将整体功耗降到最低。
实测经验:我测量过一款1.47寸屏,背光全亮(约3.3V供电)时,总电流约80mA。关闭背光后,电流降至约20mA。再让驱动芯片进入睡眠,电流可以降到1mA以下。对于使用小型锂聚合物电池(如500mAh)的项目,合理管理屏幕开关,能显著延长续航时间。
7. 常见问题排查与硬件调试
即使按照教程连接,第一次也难免遇到问题。这里梳理一个排查链路。
现象一:屏幕完全无反应,背光也不亮。
- 检查电源:用万用表测量屏幕VCC和GND之间的电压,确认是否为3.3V(或模块标称电压)。电流是否足够?尝试单独给屏幕供电。
- 检查背光:确认BLK引脚是否已正确接到高电平或PWM信号。可以尝试直接用杜邦线将BLK短接到VCC,看背光是否亮起。
- 检查复位:有些模块需要上电后一个正确的复位脉冲。确保RST引脚在初始化前有一个从高到低再到高的跳变过程。可以在代码开头手动控制该引脚:
digitalWrite(RST_PIN, HIGH); delay(10); digitalWrite(RST_PIN, LOW); delay(10); digitalWrite(RST_PIN, HIGH); delay(120);。
现象二:背光亮,但屏幕全白、全黑或有杂乱色块。
- 检查SPI通信:这是最常见的问题。首先确认SCK、MOSI、CS、DC引脚连接是否正确且牢固。特别是DC引脚,接错必然导致无法识别命令和数据。
- 降低SPI频率:在初始化代码中,将SPI时钟频率(如
SPI_FREQUENCY)从27MHz先降到1MHz或更低,排除因布线过长或干扰导致的数据错误。 - 检查初始化序列:确认使用的驱动库是否完全兼容你的屏幕型号。不同批次的1.47寸屏,其ST7735S的初始化参数可能有细微差别。尝试在库的配置文件中切换
TFT_INVERSION_ON/OFF,或调整TFT_RGB_ORDER。 - 逻辑分析仪抓包:如果以上都不行,终极武器是使用逻辑分析仪(如Saleae)连接SPI线,抓取上电初始化过程中的数据流。对照ST7735S数据手册的命令集,看发送的命令和参数是否正确。这是定位底层通信问题的金标准。
现象三:显示内容错位、镜像或颜色异常。
- 调整扫描方向(MADCTL):修改
setRotation()函数的参数(0,1,2,3)逐一尝试。如果库不支持或无效,直接查找并修改驱动库中MADCTL命令对应的参数值。 - 检查颜色格式(COLMOD):确认库中配置的颜色格式(如RGB565)与绘图函数输出的数据格式一致。
TFT_eSPI库中,drawPixel等函数会自动处理格式,但如果你直接操作帧缓冲区,就必须保证缓冲区数据的排列格式与COLMOD设置匹配。 - 检查字节序(Endianness):在ESP32等小端序(Little-Endian)处理器上,一个16位的RGB565颜色值(如0xF800代表红色)在内存中存储为
0x00 0xF8(低字节在前)。而SPI发送通常是先发高字节。TFT_eSPI库的setSwapBytes(true)函数就是用来解决这个问题的,它会在发送前交换字节顺序。如果颜色完全不对(比如红色显示成浅蓝色),可以尝试切换这个设置。
玩转一块小屏的乐趣,在于从硬件连接到软件驱动,从点亮第一颗像素到完成一个流畅界面的完整掌控感。1.47英寸这个尺寸在紧凑性和可视性之间取得了不错的平衡,希望这篇从硬件到软件、从原理到踩坑的详细梳理,能帮你省去大量摸索的时间。在实际项目中,更复杂的挑战可能来自于多任务下的刷新优化、低功耗设计以及与其它传感器的协同,但有了这些扎实的基础,那些挑战都将成为有趣的扩展。