Sửa lỗi giao tiếp LCD 16x2

Em bí rồi không biết sai chỗ nào nạp chạy thì không hiện lên màn hình. Nếu nó thông báo lỗi gì thì em còn fix đc còn vụ này thì em vô phương cứu chữa!
code:

#include"LcdTxt.h"

void WriteHighNibble(int8_t data){
	if(data &0x80){
		PIN_HIGH(LCD_PORT, D7_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D7_PIN);
	}

	if(data &0x40){
		PIN_HIGH(LCD_PORT, D6_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D6_PIN);
	}

	if(data &0x20){
		PIN_HIGH(LCD_PORT, D5_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D5_PIN);
	}

	if(data &0x10){
		PIN_HIGH(LCD_PORT, D4_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D4_PIN);
	}
}

void WriteLowNibble(int8_t data){
	if(data &0x08){
		PIN_HIGH(LCD_PORT, D7_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D7_PIN);
	}

	if(data &0x04){
		PIN_HIGH(LCD_PORT, D6_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D6_PIN);
	}

	if(data &0x02){
		PIN_HIGH(LCD_PORT, D5_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D5_PIN);
	}

	if(data &0x01){
		PIN_HIGH(LCD_PORT, D4_PIN);
	}
	else{
		PIN_LOW(LCD_PORT, D4_PIN);
	}
}

void LcdCMD(int8_t cmd){
	PIN_LOW(LCD_PORT, RS_PIN);

	WriteHighNibble(cmd);
	PIN_HIGH(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, EN_PIN);

	WriteLowNibble(cmd);
	PIN_HIGH(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, EN_PIN);

	HAL_Delay(2);
}

void LcdInit(void){
	PIN_LOW(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, RS_PIN);
	HAL_Delay(15);

	WriteLowNibble(0x03);
	PIN_HIGH(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, EN_PIN);
	HAL_Delay(5);

	WriteLowNibble(0x03);
	PIN_HIGH(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, EN_PIN);
	HAL_Delay(1);

	WriteLowNibble(0x02);
	PIN_HIGH(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, EN_PIN);
	HAL_Delay(1);

	LcdCMD(_LCD_4BIT_2LINE_5x7FONT);
	LcdCMD(_LCD_TURN_ON);
	LcdCMD(_LCD_CLEAR);
	LcdCMD(_LCD_ENTRY_MODE);
}

void ShowChar(int8_t data){
	PIN_HIGH(LCD_PORT, RS_PIN);
	
	WriteHighNibble(data);
	PIN_HIGH(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, EN_PIN);
	
	WriteLowNibble(data);
	PIN_HIGH(LCD_PORT, EN_PIN);
	PIN_LOW(LCD_PORT, EN_PIN);
	
	HAL_Delay(1);
}

Chú ý thời gian yêu cầu của từng lệnh và thời gian của xung trên RS.

Ổn định mình để như sau :

LCD_RS_PIN=1;
delay_us(5);
LCD_RS_PIN=0;
delay_us(100);

Code trên của bạn hoàn toàn không có delay trên RS. Xung RS quá ngắn làm LCD không nhận dạng được.

Một số lệnh đặc biệt yêu cầu có thời gian xử lý lâu hơn thì phải có delay để chờ lcd thực hiện xong mới thực hiện tiếp việc khác được.
Ví dụ lệnh xoá LCD (0x01) cần tối thiểu 1.64 ms.

PS: Hàm send nibble chỉ cần viết 1 hàm gửi 4 bit thấp hoặc cao của 1 byte. Còn lại dùng swap để đảo vị trí 2 nibble cho nhau.

1 Like

Em đang viết code trên kit STM32 thì làm sau anh! em phải phát triển hàm delay_us? Do con MCU nó xử lý nhanh quá nên bị trường hợp này hả anh?

Em đọc tài liệu 2 lệnh clear lcd vs return home thì nó dùng gần bằng 2ms còn lại tính = us. vậy nếu em cho tất cả delay 2ms thì có sao ko?

Bạn phải viết hàm delay.
Thường có 3 loại hàm :
delay_ms
delay_us
delay_cycle
So với MCU thì LCD là chậm hơn rất nhiều. Còn so với STM32 thì là quá chậm. Ở 72MHz thì 1 lệnh của STM32 có 0.014 us.

Được nhưng không ra gì.

Thứ nhất MCU phải chờ rất nhiều thời gian để giao tiếp LCD và sẽ còn ít thời gian để làm những cái khác. Trong khi yêu cầu của nhúng là phải nhanh, thật nhanh.

Thứ hai việc này làm cho việc viết lên LCD diễn ra chậm chạp. Khi yêu cầu update tần số cao lên LCD thì nó sẽ nháy tung chảo.

Chào anh! Nếu em phát triển thêm 3 hàm này thì em phải làm sau để tìm số vòng lặp để có thời gian chính xác! Anh có thể hướng dẫn em để em học tập 1 cách chính xác đc không?

Viết hàm delay có 2 phương pháp :
Cả 2 phương pháp chỉ tương đối ở một mức độ nhất định.
C1: Dùng vòng lặp.
Để căn chỉnh bạn phải đọc và đếm được mã ASM.
Hoặc cho nó chạy rồi đo thời gian và căn chỉnh.
C2: Dùng Timer.
Cách này bạn phải biết cách sử dụng timer và ngắt timer.

1 Like

Em cảm ơn anh nếu có gì thắc mắc em sẽ tiếp tục hỏi anh tiếp!

Em không hiểu ý nghĩa vòng lặp này lắm anh có thể giải thích và cho em ví dụ đc ko! cycle: chu trình ???

Nó đúng là delay bao nhiêu chu kỳ máy. Tương ứng bao nhiêu lệnh NOP.

1 Like

Cách này anh có thể giới thiệu cho em không lần đầu em nghe!

Một hàm delay đơn giản có thể như thế này :

void delay_ms(unsigned int time){
    unsigned int loop = time * k;
    for(unsigned int t=0; t<loop;t++);
}

Việc hiệu chỉnh thời gian chính là tìm k.

Cách 1, viết 1 chương trình led blink chu kỳ 1s sau đó chạy trên mô phỏng. Kiểm tra chu kỳ xung để hiệu chỉnh k.
Cách 2 tương tự cách 1 nhưng dùng máy hiện sóng với mạch thực tế.
Cách 3 là xem mã ASM nếu không thể thực hiện 2 cách trên. Quy trình biên dịch từ mã C ra hex file gồm nhiều bước. Trong đó cơ bản là C -> ASM -> HEX.
Một vòng for hay while tương ứng n lệnh ASM. Mỗi lệnh ASM tương tứng với 1 chu kỳ máy (cycle) = 1/ fcpu.
Khi xem mã ASM sẽ biết được n là bao nhiêu từ đó tính ra k.

k= 1000/fcpu/n. (Tính cho hàm delay_ms)

1 Like

Em thấy cách 3 ổn hơn nhiều còn 2 cách trên khó chơi qua! Nhưng muốn làm cách 3 thì căng!

anh ơi check dùm em code này có lỗi gì không anh!

#include "LCD16x2.h"

/****************************
Define pin and port used
	LCD_PORT GPIOE
	RS_PIN GPIO_PIN_0
	EN_PIN GPIO_PIN_1
	D4_PIN GPIO_PIN_2
	D5_PIN GPIO_PIN_3
	D6_PIN GPIO_PIN_4
	D7_PIN GPIO_PIN_5

Define function
	HIGH_PIN(PIN): turn on pin
	LOW_PIN(PIN): turn off pin

*****************************/
void ENEable(void) 
{
	HIGH_PIN(EN_PIN);
	DelayMicros(5);
	LOW_PIN(EN_PIN);
	DelayMicros(100);
}

void WriteNibble(int8_t data, int8_t flag)
{
	flag = 4 * flag;
	if(data & (0x80 >> flag))
	{
		HIGH_PIN(D7_PIN);
	}
	else
	{
		LOW_PIN(D7_PIN);
	}

	if(data & (0x40 >> flag))
	{
		HIGH_PIN(D6_PIN);
	}
	else
	{
		LOW_PIN(D6_PIN);
	}

	if(data & (0x20 >> flag))
	{
		HIGH_PIN(D5_PIN);
	}
	else
	{
		LOW_PIN(D5_PIN);
	}

	if(data & (0x10 >> flag))
	{
		HIGH_PIN(D4_PIN);
	}
	else
	{
		LOW_PIN(D4_PIN);
	}
}

void LCDCmd(int8_t cmd)
{
	LOW_PIN(RS_PIN);
	DelayMicros(100);
	
	WriteNibble(cmd, 0);
	ENEable();
	WriteNibble(cmd, 1);
	ENEable();

	switch(cmd){
		case 0x01:
		case 0x02: 
			DelayMilis(2);
			DelayMicros(250);
			break;
		default:
			DelayMicros(53);
			break;
	}
}

void LCDInit(void)
{
	DelayMilis(15);

	LCDCmd(0x03);
	ENEable();

	DelayMilis(4);
	DelayMicros(100);

	LCDCmd(0x03);
	ENEable();
	DelayMicros(100);

	LCDCmd(0x03);
	ENEable();
	DelayMicros(100);

	LCDCmd(0x38); // use control: 4 bit, 2 line, 5x10;
	LCDCmd(0x0F); // turn on display, setting, cusor blink
	LCDCmd(0x06); // set entry mode
	LCDCmd(0x01); // clear lcd
}

void SendData(int8_t data)
{
	HIGH_PIN(RS_PIN);
	DelayMicros(5);
	
	WriteNibble(data, 0);
	ENEable();
	WriteNibble(data, 1);
	ENEable();
}

Hàm delay

void DelayMicros(uint32_t timer)
{
	uint32_t count = 0;

	while(timer--)
	{
		count = SystemCoreClock / 1000 / 1000 / 4;
		while(count--);
	}

}

void DelayMilis(uint32_t timer)
{
	uint32_t count = 0;

	while(timer--)
	{
		count = SystemCoreClock / 1000 / 4;
		while(count--);
	}
}

Em chạy nó vẫn chưa hiện ra màn hình!

Em hơi rối não ở đây luôn! DL 8-bit / 4-bit -> 0 là 8 bit, 1 là 4-bit data sheet là vậy mà đọc các trang khác thì ngược lại vậy cái nào đúng? Hay tài liệu em bị sai!

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?