Cấu hình TFT_eSPI cho ESP32: User_Setup, chọn driver, khai báo chân và sửa lỗi màn trắng
Thư viện TFT_eSPI đọc cấu hình theo thứ tự nào, bốn cách cấu hình (User_Setup, User_Setups, cờ -D, tft_setup.h), chọn driver ILI9341, ST7789, ST7735, khai báo chân ESP32, sketch kiểm tra.
TFT_eSPI là thư viện màn hình TFT nhanh và phổ biến nhất cho ESP32, ESP8266, RP2040, nhưng khác các thư viện khác: chân nối và loại màn hình không khai báo trong sketch mà trong một file cấu hình nằm ở thư mục thư viện. Cấu hình sai là nguyên nhân của phần lớn lỗi "màn trắng", màu đảo, hình lệch. Bài này giải thích TFT_eSPI chọn file cấu hình theo thứ tự nào, bốn cách đưa cấu hình vào, khai báo driver và chân cho ILI9341, ST7789, ST7735, và một sketch kiểm tra in ra cấu hình thư viện thực sự đang dùng.
1. TFT_eSPI đọc cấu hình từ đâu

Khi sketch có dòng #include <TFT_eSPI.h>, thư viện lần lượt:
- tìm file
tft_setup.htrên đường dẫn include; có thì dùng file đó; - nếu đã có
USER_SETUP_LOADED(truyền bằng cờ-Dlúc biên dịch) thì dùng các cờ đó; - nếu không, đọc
User_Setup_Select.hvà dùng file được#includeở dòng duy nhất không có//, mặc định làUser_Setup.h.
Hai điều quan trọng: cấu hình nằm trong thư viện nên dùng chung cho mọi sketch trên máy, và User_Setup.h mặc định khai báo ILI9341 với chân PIN_D5, PIN_D8… của ESP8266. Biên dịch cho ESP32 mà chưa đổi cấu hình thì hoặc lỗi biên dịch, hoặc chạy mà màn trắng.
2. Bốn cách đưa cấu hình vào

Với Arduino IDE, cách gọn nhất là tạo file cấu hình riêng cho từng loại màn trong thư mục User_Setups và chọn nó ở User_Setup_Select.h:
- Mở thư mục thư viện:
Documents/Arduino/libraries/TFT_eSPI. - Chép file cấu hình (ví dụ file dưới đây) vào
User_Setups/. - Mở
User_Setup_Select.h, thêm//vào đầu dòng#include <User_Setup.h>, thêm dòng#include <User_Setups/Setup_PYWORLD_ILI9341_ESP32.h>. Chỉ được để một dòng#includecó hiệu lực. - Giữ bản sao file cấu hình và file Select ở chỗ khác: cập nhật thư viện có thể ghi đè.
// PYWORLD.VN - File cấu hình TFT_eSPI cho màn hình ILI9341 2,4 / 2,8 inch SPI với ESP32 DevKit V1 (dùng VSPI)
// PYWORLD.VN - Chép vào libraries/TFT_eSPI/User_Setups/ rồi trong User_Setup_Select.h chỉ để lại một dòng
// PYWORLD.VN - #include <User_Setups/Setup_PYWORLD_ILI9341_ESP32.h> (thêm // trước mọi dòng #include khác)
#define USER_SETUP_INFO "PYWORLD ILI9341 ESP32"
#define ILI9341_DRIVER // 240 x 320, không cần khai báo TFT_WIDTH, TFT_HEIGHT
#define TFT_MISO 19 // SDO(MISO) trên module; chỉ cần khi đọc từ màn hoặc dùng cảm ứng
#define TFT_MOSI 23 // SDI(MOSI)
#define TFT_SCLK 18 // SCK
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4 // nối chân RESET của module; nối chung EN của ESP32 thì đặt -1
#define TFT_BL 32 // chân LED đèn nền; module nối thẳng 3,3 V thì bỏ dòng này
#define TFT_BACKLIGHT_ON HIGH
#define TOUCH_CS 21 // chân T_CS của cảm ứng XPT2046 (GPIO21 cũng là SDA mặc định: đổi chân nếu dùng I2C)
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define SMOOTH_FONT
#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
Với PlatformIO hoặc arduino-cli, có thể bỏ hẳn bước sửa thư viện: truyền -DUSER_SETUP_LOADED=1 cùng các khai báo dạng -DILI9341_DRIVER=1 -DTFT_MOSI=23 -DTFT_SCLK=18 … vào build_flags (PlatformIO) hoặc --build-property compiler.cpp.extra_flags=… (arduino-cli). Mỗi dự án mang theo cấu hình của nó. File tft_setup.h chỉ có tác dụng khi công cụ biên dịch đưa thư mục chứa nó vào đường dẫn include của thư viện; Arduino IDE thường không làm vậy với thư mục sketch.
3. Chọn driver và kích thước

- Chỉ khai báo một driver. Hai dòng
…_DRIVERcùng có hiệu lực sẽ cho hình sai mà không báo lỗi. - ST7789, ST7735 có nhiều cỡ: phải khai báo
TFT_WIDTH,TFT_HEIGHTđúng độ phân giải của tấm màn. ST7735 còn cần chọn biến thể (ST7735_GREENTAB,ST7735_REDTAB,ST7735_BLACKTAB…) theo lô màn; sai biến thể thì hình lệch vài điểm ảnh hoặc có viền nhiễu. - Màu: tấm màn đảo thứ tự đỏ, xanh dương thì thêm
TFT_RGB_ORDER TFT_BGR; màn IPS hiện âm bản thì thêmTFT_INVERSION_ON(hoặc_OFF).
Ví dụ cấu hình cho ST7789 và cách nối có ở bài màn hình ST7789 với ESP32 cùng cụm chủ đề; ILI9341 dùng không cần thư viện có ở bài màn hình TFT SPI ILI9341 với ESP32.
4. Khai báo chân đúng cho ESP32
- Dùng chân SPI phần cứng cho tốc độ cao nhất: VSPI là MOSI 23, MISO 19, SCLK 18. Chân CS, DC, RST chọn tự do trong các GPIO ra được.
- Tên chân trên module khác tên trong thư viện: SDA hoặc SDI là
TFT_MOSI, SCL hoặc SCK làTFT_SCLK, RS hoặc A0 làTFT_DC, BLK hoặc LED là đèn nền. - Tránh chân strapping (GPIO0, 2, 12, 15) cho tín hiệu có điện trở kéo trên module nếu mạch khởi động chập chờn; GPIO2 và 15 vẫn dùng được cho DC, CS trong đa số trường hợp.
- RST: nối với một GPIO, hoặc nối chung chân EN của ESP32 và đặt
TFT_RST -1. - Cảm ứng XPT2046 dùng chung SPI, chỉ cần thêm
TOUCH_CS.
5. Sketch kiểm tra cấu hình
Khi màn không lên hình, việc đầu tiên là biết thư viện đang thật sự dùng cấu hình nào. Sketch dưới đây gọi getSetup() để in driver, kích thước, chân, tốc độ SPI ra Serial Monitor, rồi vẽ mẫu thử gồm ba dải màu có nhãn, khung viền và tam giác vàng ở góc trên bên trái, xoay qua 4 hướng. Cần bật LOAD_GLCD và LOAD_FONT2 trong file cấu hình.
// PYWORLD.VN - Kiểm tra cấu hình TFT_eSPI: in ra Serial cấu hình mà thư viện thực sự dùng (driver, kích thước, chân, tốc độ SPI)
// PYWORLD.VN - Vẽ ba dải màu đỏ, xanh lá, xanh dương, khung viền và nhãn góc để nhận ra lỗi đảo màu, âm bản, lệch hình, sai kích thước
// PYWORLD.VN - Xoay màn qua 4 hướng, mỗi hướng 4 giây; cần LOAD_GLCD và LOAD_FONT2 trong file cấu hình
#include <TFT_eSPI.h>
TFT_eSPI tft;
setup_t cauHinh;
uint8_t huong = 0;
void inChan(const char *ten, int8_t chan) {
if (chan < 0) {
Serial.printf(" %-5s khong dung\n", ten);
} else {
Serial.printf(" %-5s GPIO%d\n", ten, chan);
}
}
void inCauHinh() {
tft.getSetup(cauHinh);
Serial.printf("TFT_eSPI %s, cau hinh: %s\n", cauHinh.version.c_str(), cauHinh.setup_info.c_str());
Serial.printf("Driver 0x%04X, kich thuoc %d x %d\n", cauHinh.tft_driver, cauHinh.tft_width, cauHinh.tft_height);
inChan("MOSI", cauHinh.pin_tft_mosi);
inChan("MISO", cauHinh.pin_tft_miso);
inChan("SCLK", cauHinh.pin_tft_clk);
inChan("CS", cauHinh.pin_tft_cs);
inChan("DC", cauHinh.pin_tft_dc);
inChan("RST", cauHinh.pin_tft_rst);
inChan("BL", cauHinh.pin_tft_led);
Serial.printf("SPI ghi %.1f MHz\n", cauHinh.tft_spi_freq / 10.0);
}
void veMauThu() {
int w = tft.width();
int h = tft.height();
tft.fillScreen(TFT_BLACK);
int cao = h / 4;
tft.fillRect(0, cao, w, cao, TFT_RED);
tft.fillRect(0, cao * 2, w, cao, TFT_GREEN);
tft.fillRect(0, cao * 3, w, h - cao * 3, TFT_BLUE);
tft.setTextColor(TFT_WHITE);
tft.drawString("DO", 6, cao + 4, 2);
tft.setTextColor(TFT_BLACK);
tft.drawString("XANH LA", 6, cao * 2 + 4, 2);
tft.setTextColor(TFT_WHITE);
tft.drawString("XANH DUONG", 6, cao * 3 + 4, 2);
tft.drawString("Huong " + String(huong) + " " + String(w) + "x" + String(h), 6, 6, 2);
tft.drawRect(0, 0, w, h, TFT_WHITE); // khung phải chạm đủ 4 cạnh màn hình
tft.fillTriangle(0, 0, 18, 0, 0, 18, TFT_YELLOW); // tam giác vàng đánh dấu góc trên bên trái
}
void setup() {
Serial.begin(115200);
tft.init();
inCauHinh();
}
void loop() {
tft.setRotation(huong);
veMauThu();
Serial.printf("Huong %u: %d x %d\n", (unsigned)huong, tft.width(), tft.height());
huong = (huong + 1) % 4;
delay(4000);
}

Nếu tên cấu hình, driver hoặc chân in ra khác file vừa sửa, thư viện đang đọc file khác: kiểm tra lại User_Setup_Select.h, và kiểm tra máy có hai bản TFT_eSPI ở hai thư mục khác nhau không (cửa sổ biên dịch báo "Multiple libraries were found for TFT_eSPI.h").

6. Màn trắng: kiểm tra theo thứ tự
| Bước | Kiểm tra | Dấu hiệu |
|---|---|---|
| 1 | Sketch kiểm tra in đúng cấu hình? | sai thì sửa User_Setup_Select.h, bỏ bản thư viện thừa |
| 2 | Đúng driver? | đèn nền sáng, màn trắng hoặc đen hoàn toàn |
| 3 | Đúng chân DC, CS, RST? | màn trắng; đảo DC và CS là lỗi hay gặp |
| 4 | Nguồn, đèn nền | module 5 V có ổn áp: cấp VCC 5 V; module 3,3 V: cấp 3V3 |
| 5 | Tốc độ SPI | hình nhiễu, sọc: giảm SPI_FREQUENCY xuống 27 MHz hoặc 20 MHz, dây ngắn hơn |
Xem thêm