Phân tích cốt lõi vấn đề
Màn hình LCD1602 của bạn có đèn nền sáng bình thường nhưng không hiển thị ký tự, 90% khả năng là do giao tiếp I2C bất thường (sai địa chỉ/kết nối chân sai/không phản hồi), kế đến là lỗi về thời gian khởi tạo/độ trễ, hoặc thiếu nguồn/hạn chế kéo lên trên phần cứng. Dưới đây là hướng dẫn kiểm tra và khắc phục đầy đủ theo thứ tự ưu tiên.
Một、Kiểm tra phần cứng (giải quyết các vấn đề cơ bản trước)
1. Kiểm tra lại chân tín hiệu và dây nối
Sơ đồ mạch của bạn định nghĩa như sau:
- Giao diện LCD H2: 1=+5V, 2=GND, 3=SDA(PB7), 4=SCL(PB6)
- I2C1 của STM32: SCL phải nối với PB6, SDA phải nối với PB7 — Hãy xác minh lại ánh xạ chân trong CubeMX, tuyệt đối không được đảo ngược SDA/SCL.
2. Nguồn điện phải là 5V
LCD1602 kết hợp board chuyển đổi I2C là thiết bị hoạt động ở mức 5V. Nếu cấp nguồn 3.3V sẽ xuất hiện tình trạng “đèn nền sáng nhưng không giao tiếp I2C được”. Bạn phải nối vào chân 5V của STM32, không được dùng nguồn 3.3V.
3. Bus I2C bắt buộc phải có điện trở kéo lên (pull-up)
I2C là bus dạng drain hở (open-drain), bắt buộc phải có điện trở kéo lên:
- Phương án 1: Trong CubeMX, cấu hình chế độ GPIO cho PB6/PB7 của I2C1 thành Open Drain Pull-up
- Phương án 2: Thêm một điện trở 4.7KΩ giữa mỗi chân PB6, PB7 với 5V trên mạch
- Thiếu điện trở kéo lên sẽ khiến tín hiệu I2C bất thường, thiết bị không phản hồi.
Hai、Sửa lỗi phần mềm (theo thứ tự ưu tiên)
1. 【Lỗi phổ biến nhất】Sửa lại địa chỉ I2C
Trong code bạn đang dùng #define LCD_I2C_ADDRESS 0x4E, tuy nhiên địa chỉ này có thể không khớp với module của bạn:
- Module chuyển đổi I2C cho LCD1602 chủ yếu dùng chip PCF8574, địa chỉ 7 bit thường gặp là 0x27 hoặc 0x3F. Hàm
HAL_I2C_Master_Transmit của HAL Library yêu cầu truyền vào địa chỉ ghi 8 bit (địa chỉ 7 bit dịch trái 1 bit):
- Địa chỉ 7 bit 0x27 → Địa chỉ ghi 8 bit là
0x4E (đây là địa chỉ bạn đang dùng)
- Địa chỉ 7 bit 0x3F → Địa chỉ ghi 8 bit là
0x7E (địa chỉ phổ biến khác)
Xác minh nhanh địa chỉ đúng (bắt buộc thực hiện)
Ngay sau MX_I2C1_Init(), thêm đoạn quét địa chỉ I2C để tìm xem thiết bị nào phản hồi:
// Đặt ngay sau MX_I2C1_Init(), trước lcd_init()
uint8_t i2c_addr;
for(i2c_addr=0; i2c_addr<128; i2c_addr++)
{
if(HAL_I2C_IsDeviceReady(&hi2c1, i2c_addr<<1, 1, 100) == HAL_OK)
{
// Dừng tại đây bằng breakpoint/đèn báo, ghi nhận giá trị (i2c_addr<<1), rồi điền vào LCD_I2C_ADDRESS
break;
}
}
Nếu không quét thấy địa chỉ nào phản hồi, tức là có lỗi về dây nối hoặc khởi tạo I2C — hãy giải quyết phần cứng trước.
2. Kiểm tra hàm HAL_Delay có hoạt động hay không
Việc khởi tạo LCD rất phụ thuộc vào độ trễ. Nếu HAL_Delay không hoạt động, chuỗi lệnh khởi tạo sẽ sai hoàn toàn, dẫn đến không hiển thị.
- Cách kiểm tra: Thêm đoạn nháy LED trong vòng lặp chính để kiểm tra độ trễ:
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // Chân LED tích hợp trên bo mạch của bạn
HAL_Delay(500);
}
Nếu LED không nhấp nháy đúng 500ms, chứng tỏ cấu hình Clock/SysTick bị sai — cần sửa trước khi tiếp tục.
- Bạn đang dùng nội bộ HSI 8MHz, cấu hình này ổn, nhưng cần đảm bảo rằng SysTick Clock Source trong CubeMX đã chọn đúng và
HAL_Init() thực thi thành công.
3. Thêm kiểm tra lỗi giao tiếp I2C
Code hiện tại không kiểm tra xem I2C có gửi thành công hay không, nên không thể phát hiện lỗi. Hãy sửa hàm lcd_send_to_i2c để trả về trạng thái và xử lý lỗi:
// Hàm nội bộ: Gửi dữ liệu qua I2C, trả về trạng thái HAL
HAL_StatusTypeDef lcd_send_to_i2c(char data, int rs)
{
uint8_t data_t[4];
uint8_t upper_nibble, lower_nibble;
upper_nibble = data & 0xF0;
lower_nibble = (data << 4) & 0xF0;
uint8_t backlight = 0x08;
data_t[0] = upper_nibble | backlight | 0x04 | rs; // EN = 1
data_t[1] = upper_nibble | backlight | 0x00 | rs; // EN = 0
data_t[2] = lower_nibble | backlight | 0x04 | rs; // EN = 1
data_t[3] = lower_nibble | backlight | 0x00 | rs; // EN = 0
// Tăng thời gian chờ, trả về trạng thái gửi
return HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_ADDRESS, data_t, 4, 200);
}
// Gửi lệnh
void lcd_send_cmd(char cmd)
{
lcd_send_to_i2c(cmd, 0); // RS = 0: gửi lệnh
HAL_Delay(1); // Thêm độ trễ để tránh quá nhanh
}
// Gửi dữ liệu (ký tự)
void lcd_send_data(char data)
{
lcd_send_to_i2c(data, 1); // RS = 1: gửi dữ liệu
HAL_Delay(1);
}
4. Điều chỉnh chi tiết thời gian khởi tạo
Quy trình khởi tạo của bạn cơ bản đúng, nhưng nên tăng thêm thời gian chờ để an toàn hơn:
// Khởi tạo LCD1602
void lcd_init(void)
{
// Chuẩn bị khởi tạo chế độ 4-bit, tăng thêm thời gian chờ
HAL_Delay(100); // Chờ lâu hơn sau khi cấp nguồn, đảm bảo LCD ổn định
lcd_send_cmd(0x30);
HAL_Delay(10);
lcd_send_cmd(0x30);
HAL_Delay(5);
lcd_send_cmd(0x30);
HAL_Delay(10);
lcd_send_cmd(0x20); // Chuyển sang chế độ 4-bit
HAL_Delay(10);
// Thiết lập hiển thị, tăng thời gian chờ giữa các lệnh
lcd_send_cmd(0x28); // 4-bit, 2 hàng, font 5x8
HAL_Delay(2);
lcd_send_cmd(0x08); // Tắt hiển thị
HAL_Delay(2);
lcd_send_cmd(0x01); // Xóa màn hình
HAL_Delay(5);
lcd_send_cmd(0x06); // Di chuyển con trỏ sang phải, địa chỉ tự tăng
HAL_Delay(2);
lcd_send_cmd(0x0C); // Bật hiển thị, tắt con trỏ, không nhấp nháy
HAL_Delay(2);
}
Ba、Các bước gỡ lỗi toàn diện (thực hiện theo thứ tự)
- Kiểm tra lại dây nối: 5V/GND đúng, SDA → PB7, SCL → PB6, không bị đảo.
- Thực hiện quét địa chỉ I2C, xác nhận thiết bị phản hồi, điền địa chỉ 8 bit đúng vào
LCD_I2C_ADDRESS.
- Kiểm tra
HAL_Delay hoạt động bình thường.
- Thay thế driver bằng mã đã sửa, có thêm kiểm tra lỗi và thời gian chờ dự phòng.
- Chạy thử: nếu mọi thứ đúng, sau khi cấp nguồn sẽ hiển thị
Hello STM32!.
Bốn、Kiểm tra bổ sung nếu vẫn chưa hoạt động
Nếu đã làm hết các bước trên mà vẫn không hiển thị:
- Điều chỉnh biến trở độ tương phản (potentiometer màu xanh ở mặt sau LCD1602). Độ tương phản quá thấp sẽ khiến ký tự không nhìn thấy — từ từ xoay biến trở cho đến khi thấy các ô vuông/ký tự xuất hiện.
- Dùng máy phân tích logic hoặc dao động ký để kiểm tra tín hiệu I2C trên PB6/PB7, xác định MCU có phát tín hiệu hay không, hoặc thiết bị có phản hồi không.
- Dùng dây jumper nối trực tiếp PB6, PB7, 5V, GND từ STM32 sang module LCD — loại trừ khả năng tiếp xúc kém do giắc cắm.