ESP32C3驱动小尺寸TFT屏:Arduino环境下的硬件连接、库配置与图形显示实战

📅 2026/7/29 13:12:14 👁️ 阅读次数 📝 编程学习
ESP32C3驱动小尺寸TFT屏:Arduino环境下的硬件连接、库配置与图形显示实战

1. 项目缘起:为什么选择ESP32C3驱动小尺寸TFT屏?

最近在捣鼓一个需要显示交互信息的小项目,核心需求是体积小、功耗低、开发快。市面上常见的ESP32开发板虽然功能强大,但引脚多、体积大,对于我这个紧凑型项目来说有点“杀鸡用牛刀”了。直到我注意到了合宙的ESP32C3核心板,它基于RISC-V架构,价格亲民,最关键的是它集成了USB转串口芯片,用一根Type-C线就能完成供电、下载和调试,简直是桌面级小项目的“天选之子”。

显示部分,我选择了1.8寸和1.44寸的TFT液晶屏。这类屏幕分辨率通常在128x160或128x128,色彩鲜艳(通常是65K色),接口简单(多为SPI),价格也非常便宜。它们非常适合显示图标、文字、简单的图表或者传感器数据,是物联网设备、小型仪表盘的绝配。但问题来了,如何让这块小巧的ESP32C3驱动起这块TFT屏呢?官方SDK直接操作寄存器?那太繁琐了。我第一时间想到的就是用Arduino框架,毕竟它的生态丰富,有大量现成的库可以调用,能让我快速实现想法,把精力集中在应用逻辑上,而不是底层驱动调试。

这个组合——Arduino IDE + 合宙ESP32C3 + 小尺寸SPI TFT屏——构成了一个极具性价比和便捷性的嵌入式显示解决方案。它避开了复杂的硬件设计,降低了软件开发的准入门槛,无论是学生做毕设、创客做原型,还是工程师做小型产品验证,都非常合适。接下来,我就把从环境搭建、库选择、接线到最终点亮屏幕并显示内容的完整过程,以及中间踩过的坑和总结的经验,详细分享出来。

2. 硬件准备与核心原理剖析

在动手写代码之前,我们必须先搞清楚手头的“兵器”以及它们是如何协同工作的。这能帮助你在后面遇到问题时,快速定位是硬件连接错误、库配置不对还是代码逻辑有误。

2.1 硬件清单与引脚功能解读

首先,请准备好以下硬件:

  1. 合宙ESP32C3开发板:我使用的是合宙的ESP32C3核心板(经典款),它板载了CH343 USB转串口芯片,自带Type-C接口。
  2. 1.8寸或1.44寸 TFT液晶屏模块:我以最常见的ST7735ST7789驱动芯片的屏幕为例。购买时一定要确认屏幕的驱动芯片型号,这决定了后续库的选择。通常卖家会提供资料,上面写着“Drive IC: ST7735S”之类的信息。
  3. 杜邦线若干(母对母或公对母,根据你的屏幕接口而定)。
  4. USB Type-C数据线一根,用于供电和程序下载。

核心引脚连接解析: 这类SPI TFT屏通常有7-8个引脚(不包括背光)。我们需要关注以下几个关键引脚,并将它们与ESP32C3的GPIO连接起来。这里有一个非常重要的前提:ESP32C3的硬件SPI引脚是固定的。

  • SPI总线引脚 (必须正确连接)

    • SCK (Serial Clock): SPI时钟线。ESP32C3的默认SPI (SPI2/HSPI) 的SCK引脚是GPIO6
    • MOSI (Master Out Slave In): 主设备输出,从设备输入,也就是数据线。ESP32C3的默认SPI的MOSI引脚是GPIO7
    • MISO (Master In Slave Out): 主设备输入,从设备输出。对于纯输出的TFT屏,这个引脚可以不接,但为了规范,我建议接上。ESP32C3的默认SPI的MISO引脚是GPIO2
    • 注意:合宙ESP32C3核心板的GPIO6和GPIO7在板子上可能没有直接引出,你需要查看板子的原理图。对于常见的合宙ESP32C3核心板,GPIO6和GPIO7是用于内部Flash通信的,强烈不建议挪用,否则可能导致程序无法下载或运行异常。因此,我们需要使用软件SPI其他硬件SPI引脚(如果支持)。
  • 控制引脚 (可以自定义,但需在代码中声明)

    • CS (Chip Select): 片选引脚,低电平有效。当总线上有多个SPI设备时,通过拉低对应设备的CS来选中它。我们可以将其连接到ESP32C3的任何空闲GPIO,例如GPIO10
    • DC (Data/Command): 数据/命令选择引脚。这是TFT屏驱动的一个关键引脚。拉高时,SPI总线上的数据被解释为要显示的像素数据;拉低时,则被解释为发送给驱动芯片的命令(如设置扫描方向、亮度等)。同样,连接至任意空闲GPIO,例如GPIO0(注意:GPIO0在启动时有特殊作用,但运行时可用)。
    • RST (Reset): 复位引脚,低电平复位。可以接GPIO,也可以直接接VCC(即不通过MCU控制,上电即复位)。为了代码可控,建议接一个GPIO,如GPIO1
    • BL (Backlight): 背光控制。可以接GPIO进行PWM调光,也可以直接接VCC或GND(常亮或常灭)。为了省电和体验,建议接一个支持PWM的GPIO,如GPIO3

我的连接方案(基于软件SPI,避免引脚冲突): 由于硬件SPI默认引脚可能冲突,我选择使用软件模拟SPI,这样我可以自由分配SCK和MOSI引脚,灵活性极高,且对性能影响在小屏上几乎可忽略。

  • TFT_SCK -> ESP32C3GPIO4(自定义时钟线)
  • TFT_MOSI -> ESP32C3GPIO5(自定义数据线)
  • TFT_CS -> ESP32C3GPIO10
  • TFT_DC -> ESP32C3GPIO0
  • TFT_RST -> ESP32C3GPIO1
  • TFT_BL -> ESP32C3GPIO3(接背光阳极,阴极接地)
  • VCC -> ESP32C33.3V
  • GND -> ESP32C3GND

2.2 TFT驱动芯片与SPI通信浅析

你不需要成为驱动芯片专家,但了解基本原理能让你更从容。ST7735/ST7789这类芯片内部有一个称为“显存”(GRAM)的区域,它对应着屏幕上的每一个像素点。我们的任务就是通过SPI接口,向这个显存写入颜色数据。

SPI通信是同步、全双工的,但在驱动屏幕时,我们通常只使用主设备发送模式。通信过程可以简化为:

  1. 拉低CS,选中屏幕。
  2. 拉低DC,表示接下来发送的是命令(Command)。然后通过MOSI线发送一个命令字节(如0x2A,设置列地址)。
  3. 拉高DC,表示接下来发送的是数据(Data)。然后发送该命令所需的参数数据(如起始列和结束列地址)。
  4. 发送显示数据命令(如0x2C)后,持续向DC为高的状态下发送像素颜色数据(每个像素通常为16位,RGB565格式),数据会按顺序填充到显存中。
  5. 拉高CS,结束本次通信。

高级的图形库(如TFT_eSPI)帮我们封装了所有这些底层命令,我们只需要调用tft.drawPixel(x, y, color)这样的函数,库就会自动完成上述复杂的序列。这就是使用Arduino生态的巨大优势。

3. 软件环境搭建与库的选型

硬件连接是骨架,软件环境就是灵魂。这一步走对了,后面事半功倍。

3.1 Arduino IDE配置ESP32C3开发环境

合宙ESP32C3在Arduino IDE中属于“第三方开发板”,我们需要手动添加开发板管理网址。

  1. 安装Arduino IDE:从官网下载并安装最新版(1.8.x或2.x均可)。我习惯用1.8.19,稳定。
  2. 添加ESP32开发板支持
    • 打开Arduino IDE,点击文件->首选项
    • 在“附加开发板管理器网址”中,填入以下网址(如果已有其他网址,用逗号隔开):
      https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
    • 点击确定
  3. 安装ESP32开发板包
    • 点击工具->开发板->开发板管理器...
    • 在搜索框中输入“esp32”。
    • 找到由“Espressif Systems”提供的“esp32”开发板包,选择最新版本(或稳定版本),点击“安装”。这个过程需要下载大量文件,请保持网络通畅。
  4. 选择正确的开发板和配置
    • 安装完成后,在工具->开发板中,选择“ESP32 Arduino”下的“ESP32C3 Dev Module”。
    • 关键配置
      • Upload Speed: 921600(下载速度,可以选最高,提高下载效率)
      • Flash Mode: DIO(默认)
      • Flash Size: 4MB (32Mb)(合宙C3通常是4MB Flash)
      • Partition Scheme: Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)Huge APP (3MB No OTA/1MB SPIFFS)。如果程序不大,用默认即可;如果程序较大,选Huge APP。
      • Core Debug Level: 无(默认,除非你需要调试)
      • Port: 插入ESP32C3后,这里会出现对应的串口(如COM3, /dev/ttyUSB0)。

3.2 TFT驱动库的抉择:TFT_eSPI vs. Adafruit库

Arduino生态中有几个主流的TFT库,我重点对比两个最常用的:

  • TFT_eSPI

    • 优点:性能极高,专为ESP32等32位MCU优化,支持硬件SPI加速、DMA传输(在ESP32上),刷新速度飞快。配置灵活,通过一个用户配置文件来适配上百种不同的屏幕,几乎支持所有常见的驱动芯片(ST7735, ST7789, ILI9341等)。社区活跃,例子多。
    • 缺点:初始配置需要修改文件,对新手有一点点门槛。
    • 结论强烈推荐。它是ESP32驱动TFT屏的“事实标准”,能充分发挥硬件性能。
  • Adafruit ST7735 and ST7789 Library配合Adafruit GFX Library

    • 优点:由Adafruit维护,文档规范,API统一,与Adafruit其他传感器库兼容性好。使用简单,安装后即可通过构造函数传入引脚编号使用。
    • 缺点:性能通常不如TFT_eSPI,特别是对于ESP32的硬件加速特性利用不足。
    • 结论:适合快速验证、跨平台项目,或者你非常熟悉Adafruit的生态。

我选择TFT_eSPI,因为它能让我手里的ESP32C3和小屏跑得最流畅。接下来,详细讲解如何配置它。

3.3 安装与配置TFT_eSPI库

  1. 安装库:在Arduino IDE中,点击项目->加载库->管理库...,搜索“TFT_eSPI”,找到“Bodmer’s TFT_eSPI”,点击安装。
  2. 找到并编辑用户配置文件:这是最关键的一步。库安装后,在Arduino的库文件夹中找到TFT_eSPI库。路径通常像文档\Arduino\libraries\TFT_eSPI。在该文件夹下,找到名为User_Setup.h的文件。不要直接修改它!
  3. 正确的配置方法:在TFT_eSPI库文件夹内,有一个User_Setup_Select.h文件。用文本编辑器打开它,你会看到很多#include语句,但都被注释了。我们的任务是启用适合我们屏幕的配置行,并注释掉其他所有行
    • 由于我们用的是1.8/1.44寸的ST7735/ST7789屏,通常需要找到类似下面的行并取消注释(删除行首的//):
      //#include <User_Setups/Setup25_ST7789.h> // Setup file for DSTIKE/ESP32/ESP8266 configured for ST7789 240 x 240
    • 但更通用的方法是,我们直接创建一个自定义配置。复制一份User_Setups文件夹下的Setup1_ESP32.hSetup135_ST7735.h到库根目录,重命名为My_ESP32C3_ST77xx.h,然后修改它。不过对于新手,我建议直接修改User_Setup.h的一个副本,但为了规范,我们还是利用现有机制。
    • 简化操作:对于大多数通用SPI屏,我们可以直接修改User_Setup.h。在修改前先备份原文件。然后根据我们的硬件连接进行设置:
      // 在User_Setup.h中找到并修改以下关键字段 #define ST7735_DRIVER // 如果你的屏是ST7735,启用这个;如果是ST7789,就启用 #define ST7789_DRIVER //#define ST7789_DRIVER #define TFT_WIDTH 128 // 你的屏幕宽度 #define TFT_HEIGHT 160 // 你的屏幕高度(1.8寸常见)或 128(1.44寸) // 定义引脚 —— 根据我们之前的接线方案(软件SPI) #define TFT_CS 10 // Chip select control pin #define TFT_DC 0 // Data Command control pin #define TFT_RST 1 // Reset pin (could connect to Arduino RESET pin if you don’t use it) #define TFT_BL 3 // LED back-light control pin // 因为我们使用了软件SPI,所以需要定义软件SPI的引脚 #define TFT_MOSI 5 #define TFT_SCLK 4 // MISO引脚可以不用定义,因为我们只写不读 // 注释掉硬件SPI的定义,强制使用软件SPI //#define SPI_FREQUENCY 27000000 // 硬件SPI频率设置,软件SPI时无效 //#define USE_HSPI_PORT // 注释掉,不使用ESP32的硬件SPI端口 // 对于ST7735,有时需要根据屏幕版本调整偏移量和颜色模式 #define ST7735_INITB //#define ST7735_GREENTAB //#define ST7735_REDTAB // 具体用哪个,需要尝试。通常INITB适用于很多1.8寸屏。 #define TFT_RGB_ORDER TFT_RGB // 颜色顺序,如果红色和蓝色反了,可以换成 TFT_BGR
    修改完成后,保存文件。现在,你的TFT_eSPI库就针对你的ESP32C3和屏幕配置好了。

4. 从点亮到绘图:代码实战与功能实现

环境配置妥当,是时候让屏幕亮起来了。我们将从最简单的“Hello World”开始,逐步实现图形绘制。

4.1 基础测试:清屏与显示文字

创建一个新的Arduino草图,输入以下代码。这个代码的目的是验证硬件连接和库配置是否正确。

#include <TFT_eSPI.h> // 包含图形和字体功能库 TFT_eSPI tft = TFT_eSPI(); // 创建TFT对象 void setup() { Serial.begin(115200); // 初始化串口,用于调试输出 Serial.println("TFT Test Start..."); tft.init(); // 初始化屏幕 tft.setRotation(1); // 设置屏幕方向 (0-3),根据你的安装方式调整 tft.fillScreen(TFT_BLACK); // 清屏为黑色 // 设置文本颜色和背景色 tft.setTextColor(TFT_WHITE, TFT_BLACK); // 白色字,黑色背景 tft.setTextSize(2); // 设置字体大小(1-8) // 在坐标 (10, 20) 处打印文字 tft.drawString("Hello ESP32-C3!", 10, 20); // 设置不同的颜色和大小再打印一行 tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextSize(1); tft.drawString("TFT_eSPI Library Works!", 10, 50); Serial.println("TFT Test Done."); } void loop() { // 主循环,这里暂时什么都不做 delay(1000); }

代码解析与注意事项

  • tft.init(): 这个函数会按照User_Setup.h中的配置初始化屏幕,发送一系列初始化命令。如果屏幕没有任何反应,首先检查这里是否执行(可以通过串口打印判断)。
  • tft.setRotation(1): 设置显示方向。参数0-3分别代表0度、90度、180度、270度旋转。如果你的文字是横着的但屏幕竖着放,就调整这个参数。这是一个常见的坑,如果显示内容方向不对或位置偏移,优先检查这个值和TFT_WIDTH/TFT_HEIGHT的定义是否与物理屏幕匹配。
  • tft.fillScreen(TFT_BLACK): 用颜色填充整个屏幕。TFT_BLACK,TFT_WHITE,TFT_RED,TFT_GREEN,TFT_BLUE等都是库定义好的16进制颜色常量。
  • tft.drawString(): 在指定坐标绘制字符串。坐标原点(0,0)默认在旋转后的屏幕左上角。
  • 上传代码:选择正确的端口和开发板,点击上传。如果一切顺利,你将看到屏幕点亮,并显示两行文字。

常见问题排查

  • 屏幕全白或全黑,无内容:检查背光BL引脚是否接好(接VCC或PWM输出高电平)。检查VCCGND。用万用表量一下屏幕供电电压是否为3.3V。
  • 屏幕有背光但无显示:最可能的原因是DCCS引脚接错,或者初始化失败。检查User_Setup.h中的引脚定义是否与实际接线一致。尝试将TFT_RST引脚在代码中手动复位一下(在tft.init()前加pinMode(TFT_RST, OUTPUT); digitalWrite(TFT_RST, LOW); delay(20); digitalWrite(TFT_RST, HIGH); delay(150);)。
  • 显示乱码或花屏:SPI通信速率可能过高(对于软件SPI)。尝试在setup()tft.init()之后加一句tft.setSPIClockDivider(SPI_CLOCK_DIV2);(如果库支持)或降低软件SPI的时钟频率(需要修改库底层,较复杂)。更可能的原因是驱动芯片型号初始化序列不对。确认你的屏幕是ST7735还是ST7789,并尝试User_Setup.h中不同的ST7735_*TAB定义(INITB,GREENTAB,REDTAB),它们对应屏幕不同的GRAM布局和偏移。
  • 颜色反了(红蓝互换):修改User_Setup.h中的#define TFT_RGB_ORDER TFT_RGBTFT_BGR

4.2 图形绘制与动画基础

文字显示正常后,我们来玩点图形。TFT_eSPI库提供了丰富的绘图函数。

#include <TFT_eSPI.h> TFT_eSPI tft = TFT_eSPI(); void setup() { tft.init(); tft.setRotation(1); tft.fillScreen(TFT_BLACK); // 1. 画点 for(int i=0; i<100; i++){ tft.drawPixel(random(tft.width()), random(tft.height()), TFT_YELLOW); delay(10); } delay(1000); tft.fillScreen(TFT_BLACK); // 2. 画线 tft.drawLine(0, 0, tft.width()-1, tft.height()-1, TFT_RED); tft.drawLine(tft.width()-1, 0, 0, tft.height()-1, TFT_BLUE); delay(1000); // 3. 画矩形(空心和实心) tft.drawRect(20, 20, 80, 40, TFT_GREEN); // 空心矩形 (x, y, w, h, color) tft.fillRect(25, 25, 70, 30, TFT_CYAN); // 实心矩形 delay(1000); // 4. 画圆 tft.drawCircle(64, 80, 20, TFT_MAGENTA); // 空心圆 (x, y, radius, color) tft.fillCircle(64, 80, 15, TFT_ORANGE); // 实心圆 delay(1000); // 5. 显示位图(需要先将图片转换为C数组) // 这里先注释掉,下一节专门讲 // testDrawBitmap(); } void loop() { // 创建一个简单的弹球动画 static int x = 64, y = 80; // 球心坐标 static int dx = 2, dy = 3; // 速度向量 static int radius = 5; // 用黑色覆盖上一帧的球(擦除) tft.fillCircle(x, y, radius, TFT_BLACK); // 更新位置 x += dx; y += dy; // 边界碰撞检测 if(x <= radius || x >= tft.width() - radius) dx = -dx; if(y <= radius || y >= tft.height() - radius) dy = -dy; // 在新位置画球 tft.fillCircle(x, y, radius, TFT_WHITE); delay(20); // 控制帧率,约50FPS }

这个loop()函数实现了一个简单的弹球动画。它演示了动画的基本原理:擦除旧图形 -> 计算新位置 -> 绘制新图形。在资源有限的MCU上,全屏刷新(fillScreen)太慢,这种局部擦除重绘的方式效率更高。

4.3 显示自定义图片与图标

在嵌入式设备上显示Logo或图标非常有用。由于内存有限,我们不能直接存储JPG或PNG,需要将图片转换为微控制器可以直接使用的像素数组(C语言数组)。通常我们使用二进制位图

步骤一:准备图片并转换

  1. 找到一张图片,用画图等工具将其调整为不超过屏幕分辨率(如128x128),并最好保存为BMP格式。
  2. 使用转换工具。这里推荐一个在线工具:LCD Image Converter或者Image2Lcd的在线版本。以LCD Image Converter为例:
    • 上传你的BMP图片。
    • 设置输出格式为C array
    • 扫描模式选择Vertical(垂直扫描,常见)或根据屏幕驱动要求选择。
    • 颜色格式选择RGB565(16位)。
    • 生成数组。你会得到一个巨大的const uint16_t myImage[] = { ... };数组。

步骤二:在代码中使用图片数组

  1. 在Arduino草图的标签页(Tab)中,创建一个新的头文件(如myImage.h),将生成的数组代码粘贴进去。
  2. 在主程序中包含这个头文件并显示。
// 在主程序文件中 #include "myImage.h" // 包含你的图片数组头文件 void testDrawBitmap() { // 方法1:使用drawBitmap函数(适用于与屏幕同向、未经特殊处理的数组) // tft.drawBitmap(x, y, myImage, width, height, color); // 此函数对数组格式有要求 // 方法2:更通用的方法,使用pushImage(推荐) // 假设你的图片宽高为80x60,数组名为myImage tft.pushImage( (tft.width()-80)/2, (tft.height()-60)/2, 80, 60, myImage); } void setup() { // ... 初始化代码 ... testDrawBitmap(); }

重要提示:图片数组会占用大量的程序存储空间(Flash)。一张128x128的RGB565图片需要128 * 128 * 2 bytes = 32768 bytes,即32KB!这对于只有4MB Flash的ESP32C3来说也需要谨慎使用。务必优化图片尺寸和颜色深度,或者考虑使用SPIFFS文件系统将图片存放到Flash中动态读取。

4.4 高级话题:优化刷新率与降低闪烁

当你尝试快速更新屏幕内容(如动画、实时数据曲线)时,可能会遇到屏幕闪烁或刷新慢的问题。这主要是因为在绘制过程中,屏幕内容发生了不完整的变化。

**双缓冲(Double Buffering)**是解决闪烁的经典方法,但在ESP32C3这种内存有限的MCU上,为整个屏幕分配两个帧缓冲区(128*160*2*2 ≈ 80KB)几乎不可能(ESP32C3的SRAM通常约400KB,但需要留给系统和其他变量)。

针对小屏的实用优化技巧

  1. 局部更新:只重绘屏幕上发生变化的部分,而不是整个屏幕。就像上面的弹球例子,我们只擦除和重绘小球所在的区域。
  2. 使用setAddrWindowpushColors:对于需要连续更新一大块区域的情况(如进度条、波形图),可以手动设置显存窗口,然后连续发送颜色数据,这比多次调用单个绘图函数效率高得多。
    // 示例:绘制一个水平进度条 void drawProgressBar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t percent, uint16_t color) { uint16_t fillWidth = (w * percent) / 100; tft.setAddrWindow(x, y, x + fillWidth - 1, y + h - 1); // 设置要填充的矩形窗口 for(uint16_t i = 0; i < fillWidth * h; i++) { tft.pushColor(color); // 连续推送颜色数据 } // 画边框 tft.drawRect(x, y, w, h, TFT_WHITE); }
  3. 调整SPI频率:如果你使用的是硬件SPI,可以在User_Setup.h中提高SPI_FREQUENCY(如40000000即40MHz),但前提是屏幕驱动芯片和布线能支持这么高的速率,否则会导致花屏。软件SPI的频率受代码执行速度限制,通常较低。
  4. 避免在loop()中使用fillScreen():全屏清屏是最耗时的操作之一,尽量避免在每帧动画中都使用。

5. 项目集成与实战心得

掌握了基本显示功能后,我们可以将其融入一个实际的项目中。假设我们要做一个环境监测显示器,显示温湿度和网络时间。

5.1 集成传感器与网络功能

我们需要:

  • DHT11/DHT22温湿度传感器(GPIO连接)
  • ESP32C3的Wi-Fi功能(连接网络获取时间)

接线

  • DHT数据线接GPIO8
  • TFT屏接线不变。

代码结构概览

#include <TFT_eSPI.h> #include <WiFi.h> #include <time.h> #include <DHT.h> TFT_eSPI tft; DHT dht(8, DHT22); // 假设使用DHT22 const char* ssid = "Your_SSID"; const char* password = "Your_PASSWORD"; const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 8 * 3600; // 东八区 const int daylightOffset_sec = 0; void setup() { Serial.begin(115200); tft.init(); tft.setRotation(1); tft.fillScreen(TFT_BLACK); dht.begin(); // 连接Wi-Fi tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.drawString("Connecting WiFi...", 10, 10); WiFi.begin(ssid, password); while(WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } tft.drawString("WiFi OK!", 10, 30); // 配置并从NTP服务器获取时间 configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // 等待时间同步 struct tm timeinfo; if(!getLocalTime(&timeinfo)){ tft.drawString("Failed to get time", 10, 50); return; } tft.drawString("Time Synced!", 10, 50); delay(2000); tft.fillScreen(TFT_BLACK); } void loop() { // 读取传感器数据 float h = dht.readHumidity(); float t = dht.readTemperature(); // 获取当前时间 struct tm timeinfo; if(!getLocalTime(&timeinfo)){ Serial.println("Failed to obtain time"); return; } char timeStr[20]; strftime(timeStr, sizeof(timeStr), "%H:%M:%S", &timeinfo); // 在屏幕上显示 tft.setTextSize(2); tft.setTextColor(TFT_CYAN, TFT_BLACK); tft.setCursor(10, 20); tft.printf("Temp: %.1fC", t); tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setCursor(10, 50); tft.printf("Humi: %.1f%%", h); tft.setTextSize(1); tft.setTextColor(TFT_YELLOW, TFT_BLACK); tft.setCursor(10, 90); tft.printf("Time: %s", timeStr); // 为了减少闪烁,我们只更新变化的部分。这里简单起见,每5秒全刷新一次。 // 更好的做法是判断数值或时间字符串是否改变,再局部更新。 delay(5000); // 5秒更新一次 // tft.fillScreen(TFT_BLACK); // 如果局部更新,就不需要这行 }

5.2 避坑总结与经验分享

在完成这个项目的过程中,我积累了一些宝贵的经验,特别是针对ESP32C3和TFT_eSPI库:

  1. GPIO分配陷阱:ESP32C3的GPIO6和GPIO7默认用于连接内部Flash。绝对不要将TFT的SCK和MOSI接到这两个引脚上,否则程序无法启动。仔细阅读合宙ESP32C3的原理图,优先使用其他空闲GPIO。使用软件SPI(定义TFT_MOSITFT_SCLK)是最安全灵活的选择。
  2. 电源一定要足:ESP32C3和TFT屏同时工作时,峰值电流可能超过200mA。确保你的USB线或电源适配器能提供足够的电流(500mA以上比较稳妥)。供电不足会导致屏幕显示异常、ESP32不断重启。
  3. 初始化序列的玄学:如果屏幕初始化失败(白屏、花屏),除了检查接线,最重要的就是User_Setup.h中的驱动芯片型号和TAB定义。多尝试几个不同的ST7735_*TAB。有时候卖家提供的资料也不准,需要一点耐心测试。
  4. 库的版本问题:Arduino库管理器中的TFT_eSPI可能不是最新版。如果你遇到奇怪的问题,可以尝试从GitHub(Bodmer/TFT_eSPI)下载最新的库,手动替换。
  5. 内存管理:ESP32C3的RAM相对紧张。避免在函数内定义大数组(如图片缓冲区),尽量使用全局或静态变量,或者使用psram(如果板子支持)。频繁的字符串操作(如String类)也容易产生内存碎片,对于要长期运行的项目,建议使用字符数组(char[])。
  6. 调试利器——串口打印:务必善用Serial.print()。将引脚配置、初始化状态、传感器读数、Wi-Fi连接状态等都打印出来,能极大提升排查效率。
  7. 背光控制:将背光BL引脚接到一个支持PWM的GPIO(如GPIO3),你就可以用analogWrite(TFT_BL, brightness)来调节亮度,brightness值从0(暗)到255(最亮)。这不仅能营造更好的视觉效果,还能在电池供电时节省电量。

这个由Arduino、合宙ESP32C3和小尺寸TFT屏组成的方案,以其极低的成本、便捷的开发方式和够用的性能,成为了我小型创意项目中的“常客”。从简单的信息显示到复杂的交互界面,它都能胜任。希望这篇详细的指南能帮你绕过我踩过的那些坑,顺利点亮你的屏幕,并创造出有趣的作品。