1. 项目概述
1.8寸TFT LCD显示模块是一种小型彩色显示屏,在嵌入式系统中广泛应用。ESP32作为一款功能强大的Wi-Fi/蓝牙双模微控制器,与TFT LCD的结合可以构建各种物联网显示终端。本文将详细介绍如何在ESP32平台上使用Arduino IDE驱动1.8寸TFT LCD显示模块。
这个组合特别适合需要小型彩色显示屏的物联网项目,比如智能家居控制面板、便携式数据监测设备、迷你游戏机等。相比OLED,TFT LCD在阳光下可视性更好,色彩表现更丰富;而相比大尺寸LCD,1.8寸的体积更小巧,功耗更低。
2. 硬件准备与连接
2.1 所需材料清单
要完成这个项目,你需要准备以下硬件:
- ESP32开发板(推荐使用ESP32 DevKitC)
- 1.8寸TFT LCD模块(通常基于ST7735或ILI9163驱动芯片)
- 杜邦线若干(建议使用母对母)
- 面包板(可选,用于临时连接)
- 5V/2A电源适配器(当需要长时间工作时)
注意:不同厂商的1.8寸TFT模块引脚定义可能不同,购买时务必确认具体型号和规格书。
2.2 模块引脚说明
典型的1.8寸TFT LCD模块引脚定义如下:
| 引脚名称 | 功能描述 |
|---|---|
| VCC | 电源正极(3.3V或5V) |
| GND | 电源地 |
| CS | 片选信号 |
| RESET | 复位信号 |
| DC | 数据/命令选择 |
| MOSI | SPI数据输入 |
| SCK | SPI时钟 |
| LED | 背光控制 |
2.3 ESP32连接方案
ESP32与TFT模块的推荐连接方式:
| TFT引脚 | ESP32 GPIO |
|---|---|
| VCC | 3.3V |
| GND | GND |
| CS | GPIO5 |
| RESET | GPIO17 |
| DC | GPIO16 |
| MOSI | GPIO23 |
| SCK | GPIO18 |
| LED | 3.3V(常开)或GPIO控制 |
提示:如果模块支持5V供电,也可以连接ESP32的5V引脚,但需要注意电平兼容性。
3. 软件环境配置
3.1 Arduino IDE设置
- 安装最新版Arduino IDE(建议1.8.15+)
- 添加ESP32支持:
- 打开首选项,在"附加开发板管理器网址"中添加:
code复制https://dl.espressif.com/dl/package_esp32_index.json - 通过开发板管理器安装"esp32"平台
- 打开首选项,在"附加开发板管理器网址"中添加:
- 安装必要的库:
- Adafruit ST7735 and ST7789 Library
- Adafruit GFX Library
安装完成后,在"工具"菜单中选择:
- 开发板:ESP32 Dev Module
- Flash Mode:QIO
- Flash Size:4MB
- Partition Scheme:Default
- Upload Speed:921600
3.2 库函数关键解析
Adafruit_ST7735库提供的主要功能类:
Adafruit_ST7735:显示屏驱动类Adafruit_GFX:图形基础功能类
关键方法包括:
cpp复制begin() // 初始化显示屏
setRotation() // 设置显示方向
fillScreen() // 填充整个屏幕
drawPixel() // 绘制单个像素
drawLine() // 绘制线条
drawRect() // 绘制矩形
fillRect() // 填充矩形
drawCircle() // 绘制圆形
fillCircle() // 填充圆形
setTextSize() // 设置文字大小
setTextColor() // 设置文字颜色
print() // 显示文本
4. 基础驱动实现
4.1 基本显示示例
创建一个最基本的显示示例:
cpp复制#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#define TFT_CS 5
#define TFT_RST 17
#define TFT_DC 16
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
void setup() {
tft.initR(INITR_BLACKTAB); // 初始化屏幕
tft.fillScreen(ST7735_BLACK); // 清屏为黑色
tft.setTextSize(1);
tft.setTextColor(ST7735_WHITE);
tft.setCursor(10, 10);
tft.println("Hello, ESP32!");
tft.drawRect(5, 5, 50, 30, ST7735_RED); // 绘制红色边框
}
void loop() {
// 主循环可以添加动态效果
}
4.2 显示优化技巧
- 双缓冲技术:减少屏幕闪烁
cpp复制// 在内存中创建缓冲区
uint16_t buffer[128][160]; // 假设分辨率128x160
// 先操作缓冲区
buffer[x][y] = color;
// 然后一次性刷新到屏幕
tft.drawRGBBitmap(0, 0, (uint16_t *)buffer, 128, 160);
- 局部刷新:只更新变化的部分
cpp复制// 只更新文本区域
void updateText(String text, int x, int y) {
tft.fillRect(x, y, text.length()*6*textSize, 8*textSize, ST7735_BLACK);
tft.setCursor(x, y);
tft.print(text);
}
- 自定义字体:使用更美观的字体
cpp复制#include <Fonts/FreeSans9pt7b.h>
tft.setFont(&FreeSans9pt7b);
tft.setTextSize(1); // 使用字体本身大小
5. 高级应用实例
5.1 物联网数据显示
实现一个简单的物联网数据仪表盘:
cpp复制void displaySensorData(float temp, float humidity, int pressure) {
tft.fillScreen(ST7735_BLACK);
// 温度显示
tft.setTextColor(ST7735_RED);
tft.setCursor(10, 20);
tft.print("Temp: ");
tft.print(temp);
tft.println(" C");
// 湿度显示
tft.setTextColor(ST7735_BLUE);
tft.setCursor(10, 40);
tft.print("Humidity: ");
tft.print(humidity);
tft.println(" %");
// 压力显示
tft.setTextColor(ST7735_GREEN);
tft.setCursor(10, 60);
tft.print("Pressure: ");
tft.print(pressure);
tft.println(" hPa");
// 绘制边框
tft.drawRoundRect(5, 5, 118, 70, 5, ST7735_WHITE);
}
5.2 简单UI界面
创建一个带按钮的交互界面:
cpp复制// 按钮结构体
struct Button {
int x, y, width, height;
String label;
uint16_t color;
};
// 绘制按钮
void drawButton(Button btn) {
tft.fillRoundRect(btn.x, btn.y, btn.width, btn.height, 5, btn.color);
tft.drawRoundRect(btn.x, btn.y, btn.width, btn.height, 5, ST7735_WHITE);
int textX = btn.x + (btn.width - btn.label.length()*6)/2;
int textY = btn.y + (btn.height - 8)/2;
tft.setTextColor(ST7735_WHITE);
tft.setCursor(textX, textY);
tft.print(btn.label);
}
// 检查触摸
bool isButtonPressed(Button btn, int touchX, int touchY) {
return (touchX > btn.x && touchX < btn.x + btn.width &&
touchY > btn.y && touchY < btn.y + btn.height);
}
6. 性能优化与问题排查
6.1 常见问题解决
-
屏幕无显示
- 检查电源连接是否正确
- 确认背光LED是否点亮
- 检查SPI引脚连接是否正确
- 尝试调整初始化参数
INITR_BLACKTAB/INITR_GREENTAB
-
显示颜色异常
- 检查颜色格式是否正确(RGB565)
- 确认初始化时指定的屏幕类型
- 尝试调整
setRotation()参数
-
显示内容错位
- 检查分辨率设置是否正确
- 确认显示方向设置
- 可能需要调整偏移量参数
6.2 性能优化建议
- SPI时钟优化
cpp复制// 在初始化后设置SPI时钟
SPI.beginTransaction(SPISettings(40000000, MSBFIRST, SPI_MODE0));
-
减少SPI传输量
- 使用
startWrite()和endWrite()包裹多次操作 - 批量绘制相似元素
- 使用
-
内存管理
- 对于大尺寸显示,考虑使用PROGMEM存储图像
- 动态分配内存时要小心内存泄漏
-
电源管理
cpp复制// 不需要显示时关闭背光
digitalWrite(TFT_LED, LOW);
// 深度睡眠时完全关闭显示
tft.enableDisplay(false);
7. 实际项目应用扩展
7.1 智能家居控制面板
结合Home Assistant API实现控制面板:
cpp复制void updateHAStatus(String entity, String state) {
// 清除旧状态
tft.fillRect(10, 30, 100, 20, ST7735_BLACK);
// 显示新状态
tft.setCursor(10, 30);
tft.print(entity + ": ");
tft.setTextColor(state == "on" ? ST7735_GREEN : ST7735_RED);
tft.print(state);
}
// 在loop中调用
void loop() {
if(millis() - lastUpdate > 5000) { // 每5秒更新
String lightState = getHAState("light.living_room");
updateHAStatus("Living Light", lightState);
lastUpdate = millis();
}
}
7.2 迷你天气站
获取并显示天气信息:
cpp复制void displayWeather(float temp, String conditions, int humidity) {
tft.fillScreen(ST7735_BLUE);
// 温度大字体显示
tft.setTextSize(3);
tft.setTextColor(ST7735_WHITE);
tft.setCursor(30, 30);
tft.print(temp, 1);
tft.println(" C");
// 天气状况
tft.setTextSize(1);
tft.setCursor(20, 70);
tft.println(conditions);
// 湿度
tft.setCursor(20, 90);
tft.print("Humidity: ");
tft.print(humidity);
tft.println("%");
// 根据天气绘制图标
if(conditions.indexOf("rain") >= 0) {
drawRainIcon(90, 80);
} else if(conditions.indexOf("cloud") >= 0) {
drawCloudIcon(90, 80);
} else {
drawSunIcon(90, 80);
}
}
8. 进阶开发技巧
8.1 自定义图形绘制
实现一个简单的绘图函数:
cpp复制void drawBattery(int x, int y, int percent) {
// 电池外框
tft.drawRect(x, y, 20, 10, ST7735_WHITE);
tft.fillRect(x+20, y+3, 2, 4, ST7735_WHITE);
// 电量填充
int fillWidth = map(percent, 0, 100, 0, 18);
uint16_t fillColor;
if(percent > 70) fillColor = ST7735_GREEN;
else if(percent > 30) fillColor = ST7735_YELLOW;
else fillColor = ST7735_RED;
tft.fillRect(x+1, y+1, fillWidth, 8, fillColor);
// 电量百分比文本
tft.setTextSize(1);
tft.setTextColor(ST7735_WHITE);
tft.setCursor(x+25, y+2);
tft.print(percent);
tft.print("%");
}
8.2 动画效果实现
创建一个平滑的进度条动画:
cpp复制void animateProgressBar(int x, int y, int width, int height, int duration) {
int steps = 100;
int delayTime = duration / steps;
for(int i = 0; i <= steps; i++) {
// 绘制背景
tft.drawRect(x, y, width, height, ST7735_WHITE);
// 计算填充宽度
int fillWidth = map(i, 0, steps, 0, width-2);
// 根据进度改变颜色
uint16_t color;
if(i < 30) color = ST7735_RED;
else if(i < 70) color = ST7735_YELLOW;
else color = ST7735_GREEN;
// 绘制填充
tft.fillRect(x+1, y+1, fillWidth, height-2, color);
// 显示百分比
tft.fillRect(x+width+5, y, 30, height, ST7735_BLACK);
tft.setCursor(x+width+5, y+2);
tft.print(i);
tft.print("%");
delay(delayTime);
}
}
9. 项目优化与电源管理
9.1 低功耗设计
对于电池供电的应用,功耗优化至关重要:
- 背光控制
cpp复制#define TFT_LED 4 // 假设背光控制接在GPIO4
void setBacklight(int brightness) {
// 使用PWM控制背光亮度
ledcSetup(0, 5000, 8); // 通道0,5kHz,8位分辨率
ledcAttachPin(TFT_LED, 0);
ledcWrite(0, brightness); // 0-255
}
- 动态刷新率
cpp复制unsigned long lastRefresh = 0;
int refreshInterval = 1000; // 初始1秒刷新一次
void loop() {
if(millis() - lastRefresh > refreshInterval) {
updateDisplay();
lastRefresh = millis();
// 根据内容重要性调整刷新间隔
if(isImportantData) {
refreshInterval = 200; // 重要数据200ms刷新
} else {
refreshInterval = 1000; // 普通数据1秒刷新
}
}
// 其他低功耗处理
esp_sleep_enable_timer_wakeup(10000); // 10ms睡眠
esp_light_sleep_start();
}
9.2 内存优化技巧
ESP32内存有限,需要特别注意:
- 使用PROGMEM存储大图像
cpp复制#include <avr/pgmspace.h>
const uint16_t logo[] PROGMEM = {
0x0000, 0x0000, 0x0000, // 图像数据
// ...
};
void drawLogo(int x, int y) {
tft.drawRGBBitmap(x, y, logo, 32, 32);
}
- 分段处理大图像
cpp复制void drawLargeImage(int x, int y, const uint16_t *image, int width, int height) {
uint16_t buffer[32]; // 小缓冲区
for(int row = 0; row < height; row++) {
for(int col = 0; col < width; col += 32) {
int chunk = min(32, width - col);
// 复制一块数据到缓冲区
for(int i = 0; i < chunk; i++) {
buffer[i] = pgm_read_word(&image[row * width + col + i]);
}
// 绘制这一块
tft.drawRGBBitmap(x + col, y + row, buffer, chunk, 1);
}
}
}
10. 综合项目示例:智能植物监测仪
结合多种传感器和TFT显示,创建一个完整的项目:
cpp复制#include <Adafruit_ST7735.h>
#include <DHT.h>
#define DHTPIN 13
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
void setup() {
Serial.begin(115200);
dht.begin();
tft.initR(INITR_BLACKTAB);
tft.fillScreen(ST7735_BLACK);
tft.setTextSize(1);
drawUI();
}
void loop() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if(isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
updateDisplay(t, h);
delay(2000);
}
void drawUI() {
tft.fillScreen(ST7735_BLACK);
// 标题
tft.setTextColor(ST7735_GREEN);
tft.setCursor(10, 5);
tft.println("Plant Monitor");
// 温度标签
tft.drawRect(5, 25, 60, 20, ST7735_BLUE);
tft.setCursor(10, 30);
tft.print("Temp:");
// 湿度标签
tft.drawRect(5, 50, 60, 20, ST7735_BLUE);
tft.setCursor(10, 55);
tft.print("Humid:");
// 状态区域
tft.drawRect(70, 25, 50, 45, ST7735_RED);
}
void updateDisplay(float temp, float humid) {
// 更新温度
tft.fillRect(70, 25, 50, 20, ST7735_BLACK);
tft.setTextColor(ST7735_WHITE);
tft.setCursor(75, 30);
tft.print(temp, 1);
tft.print("C");
// 更新湿度
tft.fillRect(70, 50, 50, 20, ST7735_BLACK);
tft.setCursor(75, 55);
tft.print(humid, 0);
tft.print("%");
// 状态提示
String status;
uint16_t color;
if(temp > 30 || humid < 40) {
status = "WARNING";
color = ST7735_RED;
} else if(temp > 25 || humid < 60) {
status = "NOTICE";
color = ST7735_YELLOW;
} else {
status = "GOOD";
color = ST7735_GREEN;
}
tft.fillRect(70, 70, 50, 20, ST7735_BLACK);
tft.setTextColor(color);
tft.setCursor(75, 75);
tft.print(status);
}
这个综合示例展示了如何将传感器数据与TFT显示结合,创建一个实用的物联网设备。你可以根据需要添加更多功能,比如:
- 土壤湿度传感器
- 光照传感器
- 历史数据图表
- WiFi连接和远程监控
- 报警阈值设置
在实际使用中,我发现1.8寸TFT LCD虽然小巧,但在合理设计UI的情况下,能够显示相当丰富的信息。关键是要注意信息的层次结构和重点突出,避免在小屏幕上堆砌过多内容。通过颜色编码和图标化表示,可以大大提高信息的可读性。
