Adafruit 1.44寸TFT屏SPI驱动与图形显示实战指南
1. 项目概述与核心价值如果你正在为一个嵌入式项目寻找一块小巧、色彩鲜艳且易于驱动的显示屏那么Adafruit这块1.44英寸的彩色TFT屏很可能就是你的理想选择。我手头这块屏幕已经陪我完成了好几个项目从便携式游戏机到智能家居控制面板它的表现一直很稳定。这块屏幕的核心魅力在于它不仅仅是一块128x128像素的真彩TFT面板更是一个高度集成的解决方案板载了3.3V稳压器、电平转换器甚至还贴心地集成了一个MicroSD卡槽。这意味着你只需要几根线就能让一块单片机拥有显示彩色图形甚至读取图片文件的能力极大地简化了外围电路设计。SPI接口是这一切得以实现的关键。对于资源有限的微控制器来说SPI就像一条高效的数据高速公路。它只需要时钟SCLK、主机输出从机输入MOSI、主机输入从机输出MISO和片选CS这四根基本线有时加上复位和命令/数据线就能实现全双工高速通信。相比于并口驱动方式动辄需要8根甚至16根数据线SPI在节省宝贵的I/O引脚方面优势巨大。这块Adafruit TFT屏正是利用了SPI的简洁性使得即便是像ATtiny85这样只有少数引脚的单片机也有机会驱动彩色显示。而板载的MicroSD卡槽共享了SPI总线让你可以在不增加额外连线的情况下为项目添加存储扩展功能用于存放字体、图片或配置文件这在实际项目中非常实用。本文的目标读者是那些已经熟悉基础电子概念和某种微控制器平台如Arduino或CircuitPython并希望为自己的项目添加图形化显示功能的开发者或爱好者。我将从最基础的硬件连接讲起涵盖Arduino和CircuitPython两种主流环境的软件驱动并深入探讨如何利用SD卡功能显示图片。无论你是想制作一个迷你气象站、一个自定义的游戏掌机还是一个带界面的智能设备控制器这篇指南都将为你提供从零到一实现的完整路径和我在实践中积累的细节经验。2. 硬件深度解析与连接方案2.1 引脚功能全解与电源设计拿到这块显示屏首先得弄清楚每一个引脚是干什么的。虽然引脚不多但理解其功能是正确连接和后续排错的基础。Vin (3-5V)电源输入引脚。这是整个模块的命脉。它内部连接到一个超低压差LDO稳压器可以将3V到5V的输入电压稳定到3.3V供屏幕核心电路使用。板上的反接保护二极管可以防止电源接反烧毁模块但养成良好习惯第一次连接时务必核对正负极。3.3Vout这是板载LDO稳压器输出的3.3V。一个重要提示这个引脚只能用于输出为外部低功耗传感器供电例如一个I2C温湿度传感器绝不能作为整个显示屏的输入电源。给屏幕供电必须使用Vin引脚。GND电源和信号的共同地线。所有接地必须连接在一起形成一个稳定的参考零电位这是通信稳定的前提。SCLK (CLK)SPI时钟线。由主设备单片机产生用于同步数据位传输的节奏。时钟频率决定了通信速度。MOSI主机输出从机输入数据线。单片机通过这根线向显示屏或SD卡发送命令和数据。MISO主机输入从机输出数据线。特别注意显示屏控制器ST7735R是只写的它不会通过MISO向单片机发送数据。因此这根线是专属于MicroSD卡槽的。如果你不打算使用SD卡功能这根线可以不接。TCS (TFT_CS)TFT屏幕的片选引脚。SPI总线可以挂载多个设备每个设备都有一个片选引脚。当单片机将某个设备的片选引脚拉低通常为低电平有效时表示要与之通信。驱动屏幕时你需要通过代码控制这个引脚。D/C数据/命令选择引脚。这是控制ST7735R芯片的关键引脚。当你需要发送一个命令如设置显示方向、开显示等时将此引脚拉低当你需要发送显示数据如图像像素的RGB值时将此引脚拉高。库函数会自动处理这个时序。RST复位引脚。拉低此引脚可以强制显示屏硬件复位。最佳实践是由单片机的一个GPIO引脚来控制它而不是直接接地或接Vcc。这样你可以在程序开始运行时执行一次干净的复位确保屏幕从一个已知的状态启动。虽然可以将其连接到单片机的复位引脚这样上电时屏幕会和单片机一起复位但有些复杂的初始化序列可能需要在单片机启动后再单独复位屏幕因此独立控制更为灵活可靠。CCS (Card CS)MicroSD卡的片选引脚。功能同TCS但对象是SD卡。当你要读写SD卡时需要拉低此引脚同时确保TCS为高不选中屏幕。Lite背光控制引脚。模块内部通过一个上拉电阻将此引脚拉高因此如果不连接背光默认是常亮的。你可以将此引脚连接到一个单片机的PWM输出引脚通过调节PWM占空比来无级调节背光亮度甚至实现呼吸灯效果。如果直接接地则会关闭背光。实操心得电源噪声与电容在实际焊接和布线时一个容易被忽视的细节是电源去耦。虽然模块板载了稳压器但在Vin和GND引脚附近尤其是使用长导线或面包板连接时最好并联一个10μF的电解电容和一个0.1μF的陶瓷电容。电解电容应对低频噪声陶瓷电容滤除高频噪声。这能显著提高屏幕在动态刷新时的稳定性避免出现闪烁或条纹干扰。2.2 两种连接方式传统焊接与EYESPI接口Adafruit为这块屏幕提供了两种连接方式适应不同的项目阶段和需求。方案一传统排针焊接这是最通用、成本最低的方式。你需要准备一排直角排针焊接在屏幕背面的焊盘上。焊接时建议先将排针插入面包板固定再将屏幕模块插在排针上这样焊接起来更稳当。确保焊点饱满圆润没有虚焊或桥接。这种方式适合在开发板、面包板上进行原型开发引脚定义清晰方便用杜邦线连接。方案二EYESPI FPC连接器新版本新版本的屏幕引入了一个名为EYESPI的18针FPC柔性印刷电路连接器。这是一个非常优雅的解决方案特别适合产品化或需要将屏幕与主板分离安装的场景。你需要什么一块EYESPI转接板将FPC接口转换为标准排针和一根合适长度的EYESPI FPC软排线有50mm、100mm、200mm等规格。连接步骤将EYESPI转接板通过排针焊接或插接到你的主控板上。关键操作抬起EYESPI连接器上的黑色翻盖锁扣将FPC软排线蓝色条纹面朝上轻轻插入插座底部然后压下锁扣固定。务必确保蓝色条纹面即引脚接触面朝上插反可能导致短路损坏。屏幕端重复此操作。优势连接牢固可靠线束整洁能有效减少因连接松动导致的问题并且方便屏幕的安装和布局。无论采用哪种方式最终的电气连接关系是一致的。下面的表格对比了在两种最流行的主控平台Arduino Uno和树莓派Pico上的推荐连接方式显示屏引脚Arduino Uno (ATmega328P)树莓派 Pico (RP2040)功能说明Vin5VVSYS 或 5V电源输入 (3-5V)GNDGNDGND电源地SCLKD13 (SCK)GP2 (SPI0 SCK)SPI时钟MOSID11 (MOSI)GP3 (SPI0 TX)SPI主机输出数据MISOD12 (MISO)GP4 (SPI0 RX)仅用于SD卡TCSD10 (可自定义)GP5 (可自定义)TFT片选D/CD9 (可自定义)GP6 (可自定义)数据/命令选择RSTD8 (可自定义)GP7 (可自定义)复位建议独立控制CCSD4 (可自定义)GP8 (可自定义)SD卡片选Lite3.3V 或 PWM引脚 (如D3)3.3V 或 PWM引脚 (如GP1)背光控制注意事项引脚自定义的权衡表中标注“可自定义”的引脚意味着你可以根据自己主控板的空闲情况灵活分配。但有两个原则第一SCLK和MOSI如果使用硬件SPI则通常是固定的如Arduino Uno的D13D11使用硬件SPI能获得最高速度和最低CPU占用。第二如果你需要极高的刷新率TCS、D/C、RST也应尽量选择单片机上操作速度快的GPIO。如果引脚紧张可以考虑使用软件模拟SPI任何数字引脚都可但这会牺牲一些速度。3. 软件驱动与图形库实战硬件连接妥当后下一步就是让屏幕亮起来并显示内容。这里我们分ArduinoC/C和CircuitPythonPython两大生态来讲解。3.1 Arduino环境Adafruit_GFX生态链Adafruit为他们的显示屏产品线构建了一个强大而统一的软件生态核心是Adafruit_GFX库。这是一个纯图形算法库它定义了画点、线、矩形、圆、文字等基本图形元素的绘制函数。而针对具体的屏幕驱动芯片如这里的ST7735R则有对应的“硬件层”驱动库如Adafruit_ST7735。这种架构让你学一次GFX库的API就能驱动几乎所有Adafruit的显示屏移植性极佳。3.1.1 库安装与基础测试安装库打开Arduino IDE依次点击工具 - 管理库...。在搜索框中首先搜索Adafruit GFX安装Adafruit GFX Library。然后搜索Adafruit ST7735安装Adafruit ST7735 and ST7789 Library。为了兼容性建议也搜索安装Adafruit BusIO库。运行示例安装后在文件 - 示例 - Adafruit ST7735菜单下找到graphicstest示例。在代码开头你需要根据屏幕型号修改初始化参数。找到如下代码段// Use this initializer if youre using a 1.8 TFT tft.initR(INITR_BLACKTAB); // Use this initializer (uncomment) if youre using a 1.44 TFT //tft.initR(INITR_144GREENTAB);我们的1.44英寸屏对应的是INITR_144GREENTAB。因此注释掉第一行取消第二行的注释修改后如下// Use this initializer if youre using a 1.8 TFT //tft.initR(INITR_BLACKTAB); // Use this initializer (uncomment) if youre using a 1.44 TFT tft.initR(INITR_144GREENTAB);INITR_144GREENTAB这个参数告诉驱动库屏幕的尺寸、驱动芯片型号以及内部RAM的偏移量我们的屏幕有2列和1行的非显示区域。用错参数会导致显示错位或颜色异常。引脚配置在同一示例文件中找到引脚定义部分。默认通常是#define TFT_CS 10 #define TFT_RST 8 #define TFT_DC 9请根据你实际的硬件连接参考上一章的表格修改这些#define语句。例如如果你将TCS接在了Arduino的D10脚RST接D8D/C接D9那么就无需修改。上传与验证将代码上传到Arduino。如果一切顺利你将看到屏幕依次执行一系列图形测试全屏颜色填充、画线、画矩形和圆、显示文字等。这个测试程序完美展示了Adafruit_GFX库的核心功能。3.1.2 核心图形API速览通过graphicstest你已经看到了库的能力。下面是一些最常用函数的解析tft.begin(): 初始化屏幕在graphicstest中由initR完成。tft.fillScreen(color): 用指定颜色填充整个屏幕。颜色是16位的RGB565格式常用颜色如ST77XX_BLACK,ST77XX_WHITE,ST77XX_RED等已预定义。tft.drawPixel(x, y, color): 在坐标(x, y)处画一个点。tft.drawLine(x0, y0, x1, y1, color): 画一条线。tft.drawRect(x, y, w, h, color): 画空心矩形。drawFillRect则是画实心矩形。tft.drawCircle(x, y, r, color): 画空心圆。tft.setCursor(x, y): 设置文本起始坐标左上角。tft.setTextColor(color),tft.setTextSize(size): 设置文本颜色和大小。tft.print(“Hello”)或tft.println(“World”): 输出文字。避坑指南颜色格式RGB565这块屏幕使用16位色深即RGB565格式红色5位绿色6位蓝色5位。Adafruit_GFX库提供了tft.color565(r, g, b)函数可以将8位0-255的R、G、B值转换为16位颜色值。例如tft.color565(255, 0, 0)得到纯红色。直接使用预定义的颜色常量更方便但自定义颜色时这个函数必不可少。3.2 CircuitPython环境displayio的现代范式对于使用CircuitPython的用户常见于Adafruit的Feather M4、RP2040等开发板驱动方式更加现代化和统一基于displayio核心模块。displayio采用“显示总线 - 显示驱动 - 图层组 - 图块网格”的对象模型概念上稍复杂但功能强大且一致。3.2.1 库安装与基础显示准备固件与库确保你的开发板已刷入最新版本的CircuitPython。将板子通过USB连接电脑会出现一个名为CIRCUITPY的U盘。安装驱动库从Adafruit的CircuitPython库包中找到adafruit_st7735r.mpy文件将其复制到CIRCUITPY盘符下的lib文件夹中。如果还需要显示文本请一并复制adafruit_display_text文件夹。代码解析以下是一个精简版的“Hello World”示例我添加了详细注释import board import displayio import terminalio from adafruit_display_text import label from adafruit_st7735r import ST7735R # 释放可能被占用的显示资源这是一个好习惯 displayio.release_displays() # 1. 定义引脚根据你的实际连接修改 tft_cs board.D5 # TFT片选 tft_dc board.D6 # 数据/命令选择 tft_rst board.D9 # 复位 # 2. 创建并配置SPI总线使用硬件SPI0 spi board.SPI() # 创建FourWire总线对象绑定SPI和命令/片选/复位引脚 display_bus displayio.FourWire(spi, commandtft_dc, chip_selecttft_cs, resettft_rst) # 3. 创建显示驱动对象指定分辨率并设置偏移colstart2, rowstart1针对1.44寸屏 display ST7735R(display_bus, width128, height128, colstart2, rowstart1) # 4. 创建一个“组”(Group)作为所有显示元素的容器 main_group displayio.Group() # 将这个组设置为显示的根内容 display.root_group main_group # 5. 创建一个纯绿色背景的位图(Bitmap)和调色板(Palette) color_bitmap displayio.Bitmap(display.width, display.height, 1) # 1色位图 color_palette displayio.Palette(1) # 1个颜色的调色板 color_palette[0] 0x00FF00 # 调色板索引0设置为绿色 (RGB565格式: 0x00FF00) # 6. 创建一个“图块网格”(TileGrid)将位图和调色板关联起来并放置在(0,0)位置 bg_sprite displayio.TileGrid(color_bitmap, pixel_shadercolor_palette, x0, y0) main_group.append(bg_sprite) # 将背景添加到主组 # 7. 创建并添加一个文本标签 text “Hello CircuitPython!” text_area label.Label(terminalio.FONT, texttext, color0xFFFF00, x20, y64) main_group.append(text_area) # 保持程序运行防止退出 while True: pass将代码保存为CIRCUITPY盘根目录下的code.py板子会自动运行。你应该看到绿色背景上显示黄色文字。3.2.2 displayio核心概念与性能优化Bitmap位图与Palette调色板displayio中图像数据存储在Bitmap对象里它像一个像素值的二维数组。Palette则定义了这些像素值对应的实际颜色。例如一个1位深度的Bitmap每个像素只能是0或1那么Palette[0]就定义索引0的颜色Palette[1]定义索引1的颜色。这种索引色的方式节省内存。TileGrid图块网格它是将Bitmap和Palette绑定并定位到屏幕特定位置的“精灵”。你可以创建多个TileGrid来显示不同的图像元素。Group组用于管理多个TileGrid或其他Group的容器。你可以改变整个Group的位置、缩放比例从而实现图层管理和复杂动画。自动刷新一旦你将Group赋值给display.root_groupdisplayio会在后台自动处理屏幕刷新你无需手动调用刷新函数。性能心得减少动态创建对象在循环中频繁创建新的Bitmap、Palette、TileGrid对象例如每秒更新多次的动画会迅速产生内存碎片可能导致程序崩溃。最佳实践是在循环外初始化好所有需要的图形对象。在循环内只更新这些对象的数据如修改Bitmap的像素值、改变TileGrid的x,y坐标或Palette的颜色。例如要做一个移动的小球预先创建好一个画着小球的Bitmap和TileGrid然后在循环里更新这个TileGrid的坐标即可。4. 高级应用从MicroSD卡读取并显示图片这块屏幕集成的MicroSD卡槽是其一大亮点让你可以轻松显示全彩图片而无需将图片数据硬编码在程序里极大丰富了项目可能性。4.1 硬件连接补充要使用SD卡功能除了之前连接的基本SPI线SCLK, MOSI和电源必须额外连接两根线MISO连接到单片机SPI的MISO引脚如Arduino Uno的D12。CCS (Card CS)连接到单片机的一个空闲数字引脚如Arduino Uno的D4。4.2 Arduino环境使用Adafruit_ImageReader库安装库在Arduino库管理中搜索并安装Adafruit_ImageReader。准备图片图片尺寸不能超过128x128像素。必须保存为24位BMP格式Windows画图工具“另存为”时可选择。即使图片颜色数少也须用24位格式保存。将处理好的BMP文件例如cat.bmp复制到MicroSD卡的根目录。使用示例代码打开文件 - 示例 - Adafruit ImageReader Library - BreakoutST7735。修改代码在示例中找到reader.drawBMP()函数调用将其中的文件名改为你的图片名并指定显示位置。// 例如在坐标(0,0)处显示cat.bmp stat reader.drawBMP(“/cat.bmp”, tft, 0, 0);关键配置确保在代码开头正确设置了SD卡的片选引脚SD_CS并与你的硬件连接一致。#define SD_CS 4 // 与CCS引脚连接的Arduino引脚号4.3 CircuitPython环境使用adafruit_imageload库CircuitPython下显示图片更为灵活通常使用adafruit_imageload库。安装库将adafruit_imageload库文件夹复制到CIRCUITPY的lib目录下。准备图片同样需要128x128像素以内但CircuitPython支持更多格式推荐使用BMP或PNG格式。对于PNG还需要adafruit_png库。示例代码片段import board import displayio from adafruit_st7735r import ST7735R import sdcardio import storage import adafruit_imageload # ... 初始化display总线、display对象和main_group的代码同上 ... # 挂载SD卡 spi board.SPI() cs board.D10 # SD卡片选引脚 sdcard sdcardio.SDCard(spi, cs) vfs storage.VfsFat(sdcard) storage.mount(vfs, “/sd”) # 加载并显示图片 image, palette adafruit_imageload.load(“/sd/cat.bmp”) # 如果图片是索引色palette有用如果是真彩色image直接是Bitmap对象 if palette: tile_grid displayio.TileGrid(image, pixel_shaderpalette) else: # 对于真彩色BMPimageload返回的image本身就是Bitmap # 需要创建一个调色板虽然真彩色图片内部不依赖它但TileGrid需要 # 更简单的方式是使用OnDiskBitmap pass # 推荐使用OnDiskBitmap更节省内存 from adafruit_bitmap_font import bitmap_font # 这个方法更简单直接从文件系统创建位图对象 # 需要先将图片转换为CircuitPython可用的格式或使用内置支持 # 显示图片通常用以下方式 group displayio.Group(scale1) bitmap displayio.OnDiskBitmap(“/sd/cat.bmp”) tile_grid displayio.TileGrid(bitmap, pixel_shaderbitmap.pixel_shader) group.append(tile_grid) display.root_group group注意CircuitPython中图片显示有多种方法OnDiskBitmap适用于BMP格式且最为简单高效因为它不需要将整个图片文件加载到RAM中。4.4 图片显示的性能与内存优化在资源受限的单片机上显示图片需要权衡速度、内存和功能。Arduino (AVR) 内存限制在Uno这样的ATmega328P板子上仅2KB RAM加载一张128x128的24位BMP到内存中几乎是不可能的。Adafruit_ImageReader库采用流式读取和逐行解码的方式极大地降低了对RAM的占用。但即便如此复杂的解码过程仍会较慢。色彩深度转换24位BMP约50KB需要被转换为屏幕所需的16位RGB565格式。这个转换过程由库在读取时实时完成。预转换图片为了获得最快的显示速度可以在电脑上提前将图片转换为RGB565格式的原始数据数组.c或.h文件然后通过PROGMEM存储在Arduino的Flash中。这样显示时只需要简单的内存拷贝速度极快但会占用大量程序存储空间。Adafruit提供了Img2Code等在线工具辅助完成这个转换。CircuitPython 的权衡CircuitPython开发板如RP2040、SAMD51通常有更多RAM几十到几百KB可以更灵活地处理图片。但同样需要注意使用OnDiskBitmap流式读取比用imageload.load()全部载入内存更节省资源。5. 常见问题排查与调试技巧即使按照指南操作也可能会遇到屏幕不亮、花屏、颜色不对等问题。下面是我在多次项目中总结的排查清单和技巧。5.1 屏幕完全无显示背光也不亮电源检查首要检查用万用表测量屏幕Vin和GND之间的电压是否在3.3V-5V之间电流是否足够通常需要200mA以上峰值尝试使用独立的5V/2A电源适配器为开发板供电而非USB口。背光控制检查Lite引脚是否被意外拉低如果悬空它应该被内部上拉至高电平背光亮。尝试直接将Lite引脚连接到3.3V或Vin。接线检查顺序核对按照接线表逐一核对每根线是否连接到了正确的引脚特别是SCLK、MOSI、D/C、CS这几根SPI线。接触不良面包板项目中最常见的问题。用力按压每个连接点或改用焊接连接。软件初始化复位序列确保代码中执行了正确的复位操作。在setup()函数中尝试手动控制RST引脚先拉低至少10ms再拉高然后延迟至少120ms再开始初始化。初始化函数确认调用了正确的初始化函数如tft.initR(INITR_144GREENTAB)且参数无误。5.2 屏幕有背光但无内容白屏或乱码SPI通信问题时钟极性/相位ST7735驱动芯片对SPI模式有特定要求通常是Mode 0或Mode 3。Adafruit库已正确配置。但如果使用其他非标准库需检查SPI模式设置。速度过快尝试降低SPI时钟频率。在Arduino中可以在初始化后调用tft.setSPISpeed(4000000)将速度设为4MHz试试默认可能更高。引脚冲突确认你定义的SPI引脚没有被其他功能占用例如Arduino Uno上的D10D11D12D13通常专用于硬件SPI。偏移参数Colstart/Rowstart这是1.44寸屏特有的问题。如果显示内容在屏幕上的位置明显偏移例如内容只显示在右下角一块区域说明colstart和rowstart参数设置错误。对于这块屏正确的参数是colstart2,rowstart1在CircuitPython的ST7735R初始化中设置或在Arduino库的底层配置中体现为INITR_144GREENTAB宏。如果使用其他驱动库可能需要显式设置这些值。5.3 显示内容错乱、颜色异常或闪烁电源噪声这是导致闪烁或雪花噪点的最常见原因。确保电源线尽可能短并在屏幕的Vin和GND之间就近并联一个100nF0.1uF的陶瓷电容和一个10uF的电解电容。地线回路确保所有设备单片机、屏幕、电脑USB共地。一个缺失的地线连接会导致奇怪的信号问题。内存不足在Arduino Uno上绘制复杂图形或使用大字体时可能导致内存不足而程序行为异常。使用Serial.println(freeMemory())函数需要MemoryFree库来监控剩余内存。软件刷新过快在loop()中不加延迟地连续执行fillScreen()和大量绘图操作可能会使SPI通信缓冲区溢出。在绘图操作间添加微小延迟delay(1)或使用非阻塞的定时逻辑。5.4 MicroSD卡无法读取卡片格式确保MicroSD卡格式化为FAT16或FAT32文件系统。exFAT或NTFS格式不被大多数嵌入式库支持。接线再次确认MISO和CCS两根线已正确连接。卡片兼容性某些品牌或超大容量的SDHC/SDXC卡可能兼容性不佳。尝试使用容量较小如4GB或8GB的知名品牌Class4或Class10卡。库初始化在Arduino中使用SD.begin(SD_CS)初始化SD卡。如果失败可以通过Serial打印错误信息排查。在CircuitPython中检查sdcardio的挂载操作是否抛出异常。电源读卡瞬间电流较大劣质电源或长导线可能导致电压骤降使读卡失败。确保电源能力充足。最后一个非常有效的调试方法是分步测试先抛开SD卡功能只连接最基本的屏幕电源、地、SCLK、MOSI、D/C、CS、RST线运行最简单的图形测试程序如全屏填充单色。确保基础显示正常后再逐一添加背光控制、SD卡等功能并在每个步骤后进行测试。这样能最快地定位问题所在。嵌入式开发就是这样耐心和系统性的排查是成功的关键。希望这份详细的指南和这些实战中踩过的坑能帮助你顺利点亮这块小巧而强大的彩色世界。