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.

Nối LCD 1602 hay LCD 2004 I2C vào ESP32, Arduino, nạp chương trình xong mà màn hình chỉ sáng đèn, chỉ hiện một hàng ô vuông đen hoặc không thấy gì là lỗi gặp rất thường xuyên. Nguyên nhân hầu như luôn nằm ở một trong bốn chỗ: nguồn, biến trở tương phản, dây I2C và địa chỉ I2C. Bài này đi qua từng chỗ theo thứ tự, kèm một chương trình kiểm tra tự quét địa chỉ, tự chọn địa chỉ của LCD và hướng dẫn chỉnh tương phản qua Serial Monitor.

1. Nhìn màn hình trước khi sửa

Bốn hiện tượng của LCD 1602 I2C: đèn nền tắt không thấy gì, đèn sáng không thấy chữ, hàng trên toàn ô vuông đen, chữ hiện đúng
Mỗi hiện tượng chỉ ra một nhóm nguyên nhân (hình minh họa).
  • Đèn nền tắt: LCD chưa có nguồn, đứt dây GND, hoặc jumper LED phía sau đã bị tháo.
  • Đèn sáng nhưng màn hình trống: biến trở tương phản đang vặn quá nhạt, hoặc LCD đang được cấp 3,3 V.
  • Hàng trên toàn ô vuông đen, hàng dưới trống: đây là trạng thái của LCD khi có điện mà chưa nhận được lệnh khởi tạo nào. Tức là nguồn và tương phản đã ổn, lỗi nằm ở đường I2C: sai địa chỉ, sai dây SDA, SCL, hoặc chương trình chưa chạy tới lệnh khởi tạo.
  • Chữ hiện đúng: mọi thứ đã ổn.

2. Tìm lỗi từng bước

Lưu đồ tìm lỗi LCD I2C: kiểm tra đèn nền, tương phản, quét địa chỉ I2C, sửa địa chỉ và thư viện cho tới khi LCD hiện chữ đúng
Đi từ trên xuống; sửa xong nhánh bên phải thì quay lại bước đó (hình minh họa).
  1. Nguồn và đèn nền. LCD I2C cần 5 V. Với Arduino Nano, UNO lấy chân 5V; với ESP32 lấy chân VIN (là 5 V khi cắm USB). Đèn nền vẫn tắt thì kiểm tra dây GND và jumper LED ở mạch I2C phía sau.
  2. Tương phản. Dùng tua vít nhỏ xoay biến trở xanh phía sau, có khi phải xoay vài vòng, tới khi thấy ô vuông hoặc chữ rõ trên nền sáng. Cấp 3,3 V thì hầu như vặn thế nào cũng không thấy chữ.
  3. Dây I2C. Arduino Nano, UNO: SDA vào A4, SCL vào A5. ESP32: SDA vào GPIO21, SCL vào GPIO22, đi qua mạch chuyển mức 3,3 V – 5 V vì mạch I2C của LCD kéo SDA, SCL lên 5 V. GND của LCD phải nối chung với GND của mạch điều khiển. Dây nên ngắn dưới khoảng 30 cm.
  4. Địa chỉ I2C. Hai địa chỉ hay gặp là 0x27 và 0x3F, tùy chip trên mạch I2C. Chạy chương trình kiểm tra ở mục 4 để biết chắc, rồi sửa địa chỉ trong chương trình của bạn.
  5. Chương trình. Khai báo đúng số cột, số hàng (16, 2 hay 20, 4), gọi lcd.init() và lcd.backlight() trong setup(). Bài dùng thư viện LiquidCrystal I2C bản 1.1.2 của Frank de Brabander; các bản cũ hơn có lỗi chỉ in ra ký tự đầu tiên của chuỗi.

3. Mặt sau LCD: mạch I2C và địa chỉ

Ảnh mặt sau LCD 1602 có chú thích: jumper LED, đèn POWER, chip PCF8574, biến trở chỉnh tương phản, pad A0 A1 A2, bốn chân GND VCC SDA SCL, kèm bảng địa chỉ I2C 0x27 0x3F
Mạch I2C hàn sẵn phía sau LCD và bảng địa chỉ theo pad A0–A2.

Mạch I2C phía sau dùng chip PCF8574 để điều khiển 16 chân của LCD qua 2 dây. Có hai loại chip: PCF8574T có địa chỉ từ 0x20 đến 0x27, PCF8574AT có địa chỉ từ 0x38 đến 0x3F. Ba cặp pad A0, A1, A2 để hở thì địa chỉ cao nhất (0x27 hoặc 0x3F); hàn nối cặp nào thì bit địa chỉ tương ứng về 0. Nhờ vậy có thể nối tới 8 LCD trên cùng hai dây I2C nếu mỗi màn hình được hàn một địa chỉ riêng.

4. Chương trình kiểm tra LCD I2C

Chương trình quét toàn bộ bus I2C, in ra từng địa chỉ tìm thấy kèm loại chip, tự chọn địa chỉ giống mạch I2C của LCD rồi kiểm tra lần lượt: nháy đèn nền, lấp đầy ô đen để chỉnh tương phản, hiện chữ và bộ đếm giây. Chưa thấy LCD thì chương trình tự quét lại mỗi 3 giây, sửa dây xong không cần nạp lại.

Chuẩn bị: LCD 1602 hoặc LCD 2004 I2C, ESP32 DevKit V1 hoặc Arduino Nano, dây cắm. Dùng ESP32 thì cần thêm mạch chuyển mức logic hai chiều 3,3 V – 5 V.

Chân LCDArduino Nano, UNOESP32 DevKit V1
GNDGNDGND
VCC5VVIN (5 V khi cắm USB)
SDAA4GPIO21, qua mạch chuyển mức
SCLA5GPIO22, qua mạch chuyển mức

Cài thư viện: trong Arduino IDE mở Tools > Manage Libraries, tìm "LiquidCrystal I2C", cài bản của Frank de Brabander. Dùng LCD 2004 thì đổi LCD_COT thành 20 và LCD_HANG thành 4.

// PYWORLD.VN - Kiểm tra LCD I2C từng bước: quét bus I2C, đoán loại chip theo địa chỉ, tự chọn địa chỉ của LCD
// PYWORLD.VN - Nháy đèn nền, lấp đầy ô đen để chỉnh biến trở tương phản, rồi hiện chữ và bộ đếm thời gian
// PYWORLD.VN - Chạy trên ESP32 (SDA GPIO21, SCL GPIO22) và Arduino Nano, UNO (SDA A4, SCL A5); thư viện LiquidCrystal I2C 1.1.2

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

const uint8_t LCD_COT = 16;   // LCD 2004: đổi thành 20
const uint8_t LCD_HANG = 2;   // LCD 2004: đổi thành 4
const unsigned long CHU_KY_QUET_MS = 3000;   // chưa thấy LCD thì quét lại sau 3 giây

LiquidCrystal_I2C *lcd = nullptr;   // tạo sau khi biết địa chỉ
uint8_t diaChiLcd = 0;

void inHex(uint8_t so) {
  Serial.print(F("0x"));
  if (so < 0x10) {
    Serial.print('0');
  }
  Serial.print(so, HEX);
}

// Mạch I2C của LCD dùng PCF8574 (0x20–0x27) hoặc PCF8574A (0x38–0x3F); 0x3C, 0x3D thường là màn hình OLED
const __FlashStringHelper *loaiChip(uint8_t dc) {
  if (dc == 0x3C || dc == 0x3D) {
    return F("thuong la OLED (cung co the la PCF8574A)");
  }
  if (dc >= 0x20 && dc <= 0x27) {
    return F("PCF8574: mach I2C cua LCD");
  }
  if (dc >= 0x38 && dc <= 0x3F) {
    return F("PCF8574A: mach I2C cua LCD");
  }
  return F("thiet bi I2C khac (cam bien, RTC...)");
}

bool laDiaChiLcd(uint8_t dc) {
  return (dc >= 0x20 && dc <= 0x27) || (dc >= 0x38 && dc <= 0x3F && dc != 0x3C && dc != 0x3D);
}

// Quét địa chỉ 1–126, in từng thiết bị tìm thấy; trả về địa chỉ đầu tiên giống mạch I2C của LCD (0 nếu không có)
uint8_t quetI2C() {
  uint8_t soThietBi = 0, diaChi = 0;
  Serial.println(F("Quet bus I2C..."));
  for (uint8_t dc = 1; dc < 127; dc++) {
    Wire.beginTransmission(dc);
    if (Wire.endTransmission() == 0) {
      soThietBi++;
      Serial.print(F("  Tim thay "));
      inHex(dc);
      Serial.print(F(" -> "));
      Serial.println(loaiChip(dc));
      if (diaChi == 0 && laDiaChiLcd(dc)) {
        diaChi = dc;
      }
    }
  }
  if (soThietBi == 0) {
    Serial.println(F("Khong thay thiet bi I2C nao. Kiem tra GND chung, SDA/SCL co bi dao, LCD da co nguon 5V chua."));
  } else if (diaChi == 0) {
    Serial.println(F("Co thiet bi I2C nhung khong giong mach I2C cua LCD."));
  }
  return diaChi;
}

void lapDayOden() {
  for (uint8_t hang = 0; hang < LCD_HANG; hang++) {
    lcd->setCursor(0, hang);
    for (uint8_t cot = 0; cot < LCD_COT; cot++) {
      lcd->write(255);   // ký tự 255 của LCD là một ô đen đầy
    }
  }
}

void kiemTraLcd() {
  lcd = new LiquidCrystal_I2C(diaChiLcd, LCD_COT, LCD_HANG);
  lcd->init();
  lcd->backlight();
  Serial.print(F("Dung LCD o dia chi "));
  inHex(diaChiLcd);
  Serial.println();

  Serial.println(F("Buoc 1: den nen tat 1 giay roi bat lai. Den khong doi: kiem tra jumper LED sau mach I2C."));
  delay(1000);
  lcd->noBacklight();
  delay(1000);
  lcd->backlight();

  Serial.println(F("Buoc 2: lap day o den 5 giay. Xoay bien tro xanh den khi o den ro, nen sang."));
  lapDayOden();
  delay(5000);

  Serial.println(F("Buoc 3: hien chu. Chi thay ky tu dau tien: cap nhat thu vien LiquidCrystal I2C."));
  char dong[21];
  lcd->clear();
  snprintf(dong, sizeof(dong), "LCD OK 0x%02X", diaChiLcd);
  lcd->setCursor(0, 0);
  lcd->print(dong);
  if (LCD_HANG >= 4) {
    snprintf(dong, sizeof(dong), "Kich thuoc %u x %u", (unsigned)LCD_COT, (unsigned)LCD_HANG);
    lcd->setCursor(0, 1);
    lcd->print(dong);
    lcd->setCursor(0, 2);
    lcd->print(F("pyworld.vn"));
  }
}

void setup() {
  Serial.begin(115200);
  delay(500);
  Wire.begin();            // ESP32: SDA 21, SCL 22; Nano, UNO: SDA A4, SCL A5
  Wire.setClock(100000);   // 100 kHz cho dây dài vẫn ổn
  Serial.println();
  Serial.println(F("PYWORLD - Kiem tra LCD I2C"));
  diaChiLcd = quetI2C();
  if (diaChiLcd != 0) {
    kiemTraLcd();
  }
}

void loop() {
  static unsigned long lanTruoc = 0;
  if (lcd == nullptr) {
    // Chưa thấy LCD: sửa dây xong không cần nạp lại, chương trình tự quét lại
    if (millis() - lanTruoc >= CHU_KY_QUET_MS) {
      lanTruoc = millis();
      diaChiLcd = quetI2C();
      if (diaChiLcd != 0) {
        kiemTraLcd();
      }
    }
    return;
  }
  if (millis() - lanTruoc >= 1000) {
    lanTruoc = millis();
    char dong[21];
    snprintf(dong, sizeof(dong), "Dem: %lu s", (unsigned long)(millis() / 1000));
    lcd->setCursor(0, LCD_HANG - 1);
    lcd->print(dong);
  }
}

Các điểm chính của chương trình:

  • Wire.begin() không truyền chân nên dùng chân I2C mặc định: GPIO21, GPIO22 trên ESP32; A4, A5 trên Nano, UNO. Cùng một chương trình nạp được cho cả hai.
  • Địa chỉ 0x3C, 0x3D thường là màn hình OLED nên chương trình không chọn làm LCD, trừ khi bạn tự sửa.
  • Ký tự số 255 trong bảng ký tự của LCD là một ô đen đầy; lấp kín màn hình bằng ký tự này giúp chỉnh biến trở tương phản dễ hơn.
  • Đối tượng LCD được tạo sau khi đã biết địa chỉ (new LiquidCrystal_I2C(...)), nên không phải sửa địa chỉ bằng tay.
Serial Monitor của chương trình kiểm tra LCD I2C: tìm thấy LCD ở địa chỉ 0x27 và ba bước kiểm tra; trường hợp nối sai dây rồi sửa, chương trình tự tìm thấy LCD ở 0x3F
Kết quả ở Serial Monitor tốc độ 115200 (hình minh họa, địa chỉ là ví dụ).

5. Bảng lỗi thường gặp

Hiện tượngNguyên nhân hay gặpCách sửa
Hàng trên toàn ô đenLCD chưa được khởi tạo: sai địa chỉ, sai SDA, SCLChạy chương trình kiểm tra, sửa địa chỉ, kiểm tra dây
Đèn sáng, không thấy gìTương phản quá nhạt, nguồn 3,3 VXoay biến trở, cấp 5 V
Quét I2C không thấy thiết bị nàoĐảo SDA, SCL; thiếu GND chung; LCD chưa có nguồnKiểm tra lại 4 dây, đo 5 V ở chân VCC của LCD
Chỉ hiện ký tự đầu tiên của mỗi chuỗiThư viện LiquidCrystal I2C bản cũCài bản 1.1.2
LCD 2004 chữ nhảy sang dòng 3, dòng 4 trốngKhai báo 16 cột, 2 hàngKhai báo 20 cột, 4 hàng
Chữ tiếng Việt thành ký tự lạBảng ký tự của LCD không có chữ có dấuViết không dấu, hoặc tự tạo tối đa 8 ký tự bằng createChar()
Chạy một lúc thì chữ lộn xộnDây dài, nhiễu từ relay, động cơ, nguồn yếuDây ngắn, đi xa dây tải; gọi lại lcd.init() khi cần

6. Xem thêm

Cần hỗ trợ khi LCD vẫn không hiện chữ, liên hệ PYWORLD qua số 0915703319.


Tin tức liên quan

Hướng dẫn đo điện áp 16 bit bằng module ADS1115 với ESP32
Hướng dẫn đo điện áp 16 bit bằng module ADS1115 với ESP32

Hướng dẫn nối module ADC 16 bit ADS1115 với ESP32 DevKit V1 qua I2C, cài thư viện Adafruit ADS1X15, đọc 4 kênh ra vôn, đo vi sai và chọn gain an toàn.

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.

Hướng dẫn làm đồng hồ Internet với ESP32, NTP và màn hình OLED 0.96 inch
Hướng dẫn làm đồng hồ Internet với ESP32, NTP và màn hình OLED 0.96 inch

Hướng dẫn ESP32 lấy giờ Việt Nam từ Internet bằng NTP trên Arduino IDE, in ra Serial Monitor và làm đồng hồ hiển thị thứ, ngày, giờ trên màn hình OLED 0.96 inch SSD1306.

Xe tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 trên servo quét, tự chọn đường
Xe tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 trên servo quét, tự chọn đường

Làm xe robot tự tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 gắn trên servo quét trái phải, cầu phân áp cho chân ECHO, thuật toán chọn đường, lưu đồ và thí nghiệm STEM.

Cánh tay robot 4 servo với ESP32: điều khiển bằng biến trở, ghi nhớ và phát lại động tác
Cánh tay robot 4 servo với ESP32: điều khiển bằng biến trở, ghi nhớ và phát lại động tác

Làm cánh tay robot 4 servo với ESP32: điều khiển từng khớp bằng biến trở, nhấn nút ghi tư thế, phát lại chuỗi động tác mượt, lưu vào flash khi mất điện và gợi ý STEM.

Xe robot ESP32 điều khiển bằng điện thoại qua Wi-Fi: không cần router, không cần cài ứng dụng
Xe robot ESP32 điều khiển bằng điện thoại qua Wi-Fi: không cần router, không cần cài ứng dụng

Làm xe robot hai bánh với ESP32 và L298N, điều khiển bằng trình duyệt điện thoại qua Wi-Fi do ESP32 phát ra: bố trí, nối dây, code web, tự dừng khi mất sóng, lái vi sai, STEM.

Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, driver và nạp code
Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, driver và nạp code

Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, cài driver, chọn Board và Processor, nạp Blink, in ra Serial Monitor, quét I2C trên A4/A5, cấp nguồn và xử lý lỗi thường gặp.

Cài ESP32 vào Arduino IDE 2: thêm bo mạch, chọn ESP32 Dev Module và nạp chương trình đầu tiên
Cài ESP32 vào Arduino IDE 2: thêm bo mạch, chọn ESP32 Dev Module và nạp chương trình đầu tiên

Cài gói esp32 của Espressif vào Arduino IDE 2, chọn bo DOIT ESP32 DEVKIT V1 hoặc ESP32 Dev Module, chọn cổng COM, nạp chương trình nháy LED, in thông tin chip và sửa lỗi lần nạp đầu.


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