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

Lưu đồ: TFT_eSPI.h kiểm tra tft_setup.h trên đường dẫn include, rồi kiểm tra USER_SETUP_LOADED từ cờ -D, nếu không có thì đọc User_Setup_Select.h và file được include trong đó, mặc định là User_Setup.h
Thứ tự chọn cấu hình theo mã nguồn TFT_eSPI 2.5.43.

Khi sketch có dòng #include <TFT_eSPI.h>, thư viện lần lượt:

  1. tìm file tft_setup.h trên đường dẫn include; có thì dùng file đó;
  2. nếu đã có USER_SETUP_LOADED (truyền bằng cờ -D lúc biên dịch) thì dùng các cờ đó;
  3. nếu không, đọc User_Setup_Select.h và 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

Bảng bốn cách: sửa User_Setup.h; file riêng trong User_Setups và chọn ở User_Setup_Select.h; cờ -D khi biên dịch; file tft_setup.h; kèm ưu và nhược điểm
So sánh các cách cấu hình TFT_eSPI.

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:

  1. Mở thư mục thư viện: Documents/Arduino/libraries/TFT_eSPI.
  2. Chép file cấu hình (ví dụ file dưới đây) vào User_Setups/.
  3. 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 #include có hiệu lực.
  4. 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

Bảng driver TFT_eSPI: ILI9341 0x9341 240 nhân 320; ST7789 0x7789 nhiều kích thước phải khai báo TFT_WIDTH, TFT_HEIGHT; ST7735 0x7735 cần chọn TAB; GC9A01 màn tròn; ILI9488 320 nhân 480 chậm; ST7796 320 nhân 480
Driver hay gặp, mã driver và lưu ý khi khai báo.
  • Chỉ khai báo một driver. Hai dòng …_DRIVER cù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êm TFT_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);
}
Serial Monitor in TFT_eSPI 2.5.43, cấu hình PYWORLD ILI9341 ESP32, driver 0x9341 kích thước 240 nhân 320, các chân MOSI 23, MISO 19, SCLK 18, CS 15, DC 2, RST 4, BL 32, SPI 40 MHz, hướng 0 và 1
Kết quả mong đợi với file cấu hình ILI9341 ở trên.

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").

Bốn màn hình mẫu thử: đúng thì nhãn khớp màu và khung đủ bốn cạnh; đỏ và xanh dương đảo thì thêm TFT_RGB_ORDER TFT_BGR; âm bản thì thêm TFT_INVERSION_ON hoặc OFF; lệch thiếu cạnh do sai kích thước hoặc driver
Đọc mẫu thử để biết cần sửa dòng nào trong file cấu hình.

6. Màn trắng: kiểm tra theo thứ tự

BướcKiểm traDấu hiệu
1Sketch 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
4Nguồn, đèn nềnmodule 5 V có ổn áp: cấp VCC 5 V; module 3,3 V: cấp 3V3
5Tốc độ SPIhình nhiễu, sọc: giảm SPI_FREQUENCY xuống 27 MHz hoặc 20 MHz, dây ngắn hơn

7. Xem thêm


Tin tức liên quan

Mạch ESP32 tích hợp OLED 0.96 và pin 18650: sơ đồ chân, sạc pin, đo % pin và chương trình đầu tiên
Mạch ESP32 tích hợp OLED 0.96 và pin 18650: sơ đồ chân, sạc pin, đo % pin và chương trình đầu tiên

Mạch ESP32 tích hợp OLED 0.96 inch và đế pin 18650: chân OLED GPIO5, GPIO4, LED GPIO16, sạc pin, công tắc, gắn cầu phân áp đo phần trăm pin và chương trình hiện lên màn hình.

Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny
Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny

Hướng dẫn cài driver, cài Thonny và nạp firmware MicroPython cho mạch ESP32 Lite, dùng REPL, nháy LED, lưu main.py, đọc nút nhấn, quét I2C và xử lý lỗi thường gặp.

Đo tốc độ động cơ bằng encoder và bộ đếm xung PCNT của ESP32: vòng/phút và số vòng
Đo tốc độ động cơ bằng encoder và bộ đếm xung PCNT của ESP32: vòng/phút và số vòng

Hướng dẫn đọc encoder Hall hai kênh của động cơ DC bằng bộ đếm xung PCNT của ESP32: đếm 4 cạnh, tính vòng/phút và số vòng, lập bảng tốc độ theo duty trong giờ học STEM.

ESP32 đọc MPU-6050 tính góc nghiêng bằng bộ lọc bù: nền tảng để flycam tự cân bằng
ESP32 đọc MPU-6050 tính góc nghiêng bằng bộ lọc bù: nền tảng để flycam tự cân bằng

Nối MPU-6050 với ESP32, đọc thanh ghi không cần thư viện, tính góc roll, pitch từ gia tốc kế, ghép với con quay bằng bộ lọc bù và kiểm tra dấu trước khi dùng cho flycam.

LCD I2C không hiện chữ, chỉ hiện ô vuông, sai địa chỉ 0x27/0x3F: tìm lỗi từng bước
LCD I2C không hiện chữ, chỉ hiện ô vuông, sai địa chỉ 0x27/0x3F: tìm lỗi từng bước

LCD 1602, LCD 2004 I2C không hiện chữ, chỉ hiện hàng ô vuông đen hoặc sai địa chỉ 0x27/0x3F: tìm lỗi nguồn, tương phản, dây I2C, địa chỉ và chương trình kiểm tra tự động.

Hướng dẫn dùng Deep Sleep trên ESP32: tiết kiệm pin, đánh thức bằng timer và nút nhấn
Hướng dẫn dùng Deep Sleep trên ESP32: tiết kiệm pin, đánh thức bằng timer và nút nhấn

Hướng dẫn đưa ESP32 vào Deep Sleep trên Arduino IDE để tiết kiệm pin: các chế độ nguồn, đánh thức bằng timer và nút nhấn, giữ biến trong bộ nhớ RTC và ước tính thời lượng pin.

Đồng hồ và dự báo thời tiết trên OLED: ESP32 lấy dữ liệu Open-Meteo qua HTTP API
Đồng hồ và dự báo thời tiết trên OLED: ESP32 lấy dữ liệu Open-Meteo qua HTTP API

ESP32 lấy giờ NTP và thời tiết Open-Meteo qua HTTPS mỗi 15 phút, đọc JSON bằng ArduinoJson, hiện nhiệt độ, độ ẩm, gió và dự báo 3 ngày trên OLED 0.96 inch.

Phased array siêu âm 40 kHz với ESP32 (phần 1): nguyên lý lái búp sóng và tạo 8 kênh lệch pha
Phased array siêu âm 40 kHz với ESP32 (phần 1): nguyên lý lái búp sóng và tạo 8 kênh lệch pha

Nguyên lý phased array và cách dùng ESP32 tạo 8 kênh xung 40 kHz lệch pha để lái búp sóng siêu âm: công thức độ trễ, giới hạn do khoảng cách đầu phát, cấu hình LEDC với hpoint, sơ đồ nối và code.


Bình luận
Đã thêm vào giỏ hàng