Đây là hướng dẫn thứ sáu của chúng tôi trong Loạt bài Hướng dẫn về PIC, trong hướng dẫn này, chúng tôi tìm hiểu Giao diện của LCD 16x2 với Vi điều khiển PIC. Trong các hướng dẫn trước đây của chúng tôi, chúng tôi đã học những kiến thức cơ bản về PIC bằng cách sử dụng một số Chương trình nhấp nháy đèn LED và cũng đã học Cách sử dụng Bộ hẹn giờ trong Vi điều khiển PIC. Bạn có thể xem ở đây tất cả các hướng dẫn về Học vi điều khiển PIC bằng trình biên dịch MPLABX và XC8.
Hướng dẫn này sẽ rất thú vị vì chúng ta sẽ học Cách giao diện màn hình LCD 16 × 2 với PIC16F877A, hãy xem Video chi tiết ở cuối hướng dẫn này. Đã qua rồi cái thời chúng ta sử dụng đèn LED cho các chỉ dẫn của người dùng. Hãy để chúng tôi xem cách chúng tôi có thể làm cho các dự án của mình trông thú vị và hữu ích hơn bằng cách sử dụng màn hình LCD. Ngoài ra, hãy xem các bài viết trước của chúng tôi về Giao diện LCD với 8051, với Arduino, với Raspberry Pi, với AVR.
Functions for Interfacing LCD with PIC Microcontroller:
void Lcd_Start() { Lcd_SetBit(0x00); for(int i=1065244; i<=0; i--) NOP(); Lcd_Cmd(0x03); __delay_ms(5); Lcd_Cmd(0x03); __delay_ms(11); Lcd_Cmd(0x03); Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x08); //Select Row 1 Lcd_Cmd(0x00); //Clear Row 1 Display Lcd_Cmd(0x0C); //Select Row 2 Lcd_Cmd(0x00); //Clear Row 2 Display Lcd_Cmd(0x06); }
Lcd_Clear (): Hàm này xóa màn hình LCD và có thể được sử dụng bên trong các vòng lặp để xóa sự xuất hiện của dữ liệu trước đó.
Lcd_Clear() { Lcd_Cmd(0); //Clear the LCD Lcd_Cmd(1); //Move the cursor to first position }
void Lcd_Set_Cursor (x pos, y pos): Sau khi khởi động, màn hình LCD của chúng ta đã sẵn sàng thực hiện các lệnh, chúng ta có thể hướng dẫn màn hình LCD đặt con trỏ của nó ở vị trí ưa thích của bạn bằng cách sử dụng chức năng này. Giả sử nếu, chúng ta cần con trỏ ở ký tự thứ 5 của hàng thứ nhất. Sau đó, hàm sẽ là void Lcd_Set_Cursor (1, 5)
void Lcd_Set_Cursor(char a, char b) { char temp,z,y; if(a== 1) { temp = 0x80 + b - 1; //80H is used to move the cursor z = temp>>4; //Lower 8-bits y = temp & 0x0F; //Upper 8-bits Lcd_Cmd(z); //Set Row Lcd_Cmd(y); //Set Column } else if(a== 2) { temp = 0xC0 + b - 1; z = temp>>4; //Lower 8-bits y = temp & 0x0F; //Upper 8-bits Lcd_Cmd(z); //Set Row Lcd_Cmd(y); //Set Column } }
void Lcd_Print_Char (char data): Sau khi con trỏ được đặt, chúng ta có thể viết một ký tự vào vị trí của nó bằng cách gọi hàm này một cách đơn giản.
void Lcd_Print_Char(char data) //Send 8-bits through 4-bit mode { char Lower_Nibble,Upper_Nibble; Lower_Nibble = data&0x0F; Upper_Nibble = data&0xF0; RS = 1; // => RS = 1 Lcd_SetBit(Upper_Nibble>>4); //Send upper half by shifting by 4 EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; Lcd_SetBit(Lower_Nibble); //Send Lower half EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; }
void Lcd_Print_String (char * a): Nếu một nhóm ký tự được hiển thị, thì hàm chuỗi có thể được sử dụng.
void Lcd_Print_String(char *a) { int i; for(i=0;a[i]!='\0';i++) Lcd_Print_Char(a[i]); //Split the string using pointers and call the Char function }
Mỗi khi Lcd_Print_Char (dữ liệu ký tự) được gọi, các giá trị ký tự tương ứng của nó được gửi đến các dòng dữ liệu của màn hình LCD. Các ký tự này đạt đến HD44780U ở dạng bit. Bây giờ vi mạch này liên hệ các bit với ký tự được hiển thị bằng cách sử dụng bộ nhớ ROM của nó như bảng dưới đây. Bạn có thể tìm thấy các bit cho tất cả các ký tự trong biểu dữ liệu của Bộ điều khiển LCD HD44780U.
Bây giờ, vì chúng tôi hài lòng với tệp tiêu đề của mình, chúng ta hãy xây dựng mạch và thử nghiệm chương trình. Cũng kiểm tra tệp tiêu đề hoàn chỉnh được cung cấp trong liên kết được cung cấp ở trên.
Circuit Diagram and Testing:
Tôi đã không hiển thị nguồn điện hoặc kết nối ICSP trong mạch trên, vì chúng tôi đang sử dụng cùng một bảng mà chúng tôi đã sử dụng trong hướng dẫn trước, hãy kiểm tra tại đây.
Một điều quan trọng cần lưu ý trong chương trình là định nghĩa chân của LCD:
#define RS RD2 #define EN RD3 #define D4 RD4 #define D5 RD5 #define D6 RD6 #define D7 RD7
Các định nghĩa chân này có thể được thay đổi tùy theo thiết lập phần cứng của người lập trình. Hãy nhớ thay đổi cấu hình cổng được tôn trọng trong chức năng chính nếu bạn thay đổi ở đây.
Phần cứng cho dự án này rất đơn giản. Chúng tôi sẽ sử dụng lại cùng một mô-đun PIC mà chúng tôi đã sử dụng lần trước và kết nối mô-đun LCD với PIC của chúng tôi bằng dây nhảy.
Kết nối có thể được hiểu theo bảng sau:
LCD Pin No. | LCD Pin Name | MCU Pin Name | MCU Pin No. |
1 | Ground | Ground | 12 |
2 | VCC | +5V | 11 |
3 | VEE | Ground | 12 |
4 | Register Select | RD2 | 21 |
5 | Read/Write | Ground | 12 |
6 | Enable | RD3 | 22 |
7 | Data Bit 0 | NC | - |
8 | Data Bit 1 | NC | - |
9 | Data Bit 2 | NC | - |
10 | Data Bit 3 | NC | - |
11 | Data Bit 4 | RD4 | 27 |
12 | Data Bit 5 | RD5 | 28 |
13 | Data Bit 6 | RD6 | 29 |
14 | Data Bit 7 | RD7 | 30 |
15 | LED Positive | +5V | 11 |
16 | LED Negative | Ground | 12 |
Bây giờ chúng ta chỉ cần tạo kết nối, kết xuất mã vào MCU của chúng ta và xác minh kết quả đầu ra.
#define _XTAL_FREQ 20000000
#define RS RD2
#define EN RD3
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7
#include <xc.h>
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
//LCD Functions Developed by Circuit Digest.
void Lcd_SetBit(char data_bit) //Based on the Hex value Set the Bits of the Data Lines
{
if(data_bit& 1)
D4 = 1;
else
D4 = 0;
if(data_bit& 2)
D5 = 1;
else
D5 = 0;
if(data_bit& 4)
D6 = 1;
else
D6 = 0;
if(data_bit& 8)
D7 = 1;
else
D7 = 0;
}
void Lcd_Cmd(char a)
{
RS = 0;
Lcd_SetBit(a); //Incoming Hex value
EN = 1;
__delay_ms(4);
EN = 0;
}
Lcd_Clear()
{
Lcd_Cmd(0); //Clear the LCD
Lcd_Cmd(1); //Move the curser to first position
}
void Lcd_Set_Cursor(char a, char b)
{
char temp,z,y;
if(a== 1)
{
temp = 0x80 + b - 1; //80H is used to move the curser
z = temp>>4; //Lower 8-bits
y = temp & 0x0F; //Upper 8-bits
Lcd_Cmd(z); //Set Row
Lcd_Cmd(y); //Set Column
}
else if(a== 2)
{
temp = 0xC0 + b - 1;
z = temp>>4; //Lower 8-bits
y = temp & 0x0F; //Upper 8-bits
Lcd_Cmd(z); //Set Row
Lcd_Cmd(y); //Set Column
}
}
void Lcd_Start()
{
Lcd_SetBit(0x00);
for(int i=1065244; i<=0; i--) NOP();
Lcd_Cmd(0x03);
__delay_ms(5);
Lcd_Cmd(0x03);
__delay_ms(11);
Lcd_Cmd(0x03);
Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD
Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD
Lcd_Cmd(0x08); //Select Row 1
Lcd_Cmd(0x00); //Clear Row 1 Display
Lcd_Cmd(0x0C); //Select Row 2
Lcd_Cmd(0x00); //Clear Row 2 Display
Lcd_Cmd(0x06);
}
void Lcd_Print_Char(char data) //Send 8-bits through 4-bit mode
{
char Lower_Nibble,Upper_Nibble;
Lower_Nibble = data&0x0F;
Upper_Nibble = data&0xF0;
RS = 1; // => RS = 1
Lcd_SetBit(Upper_Nibble>>4); //Send upper half by shifting by 4
EN = 1;
for(int i=2130483; i<=0; i--) NOP();
EN = 0;
Lcd_SetBit(Lower_Nibble); //Send Lower half
EN = 1;
for(int i=2130483; i<=0; i--) NOP();
EN = 0;
}
void Lcd_Print_String(char *a)
{
int i;
for(i=0;a[i]!='\0';i++)
Lcd_Print_Char(a[i]); //Split the string using pointers and call the Char function
}
int main()
{
unsigned int a;
TRISD = 0x00;
Lcd_Start();
while(1)
{
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Print_String("Circuit Digest");
Lcd_Set_Cursor(2,1);
Lcd_Print_String("WORKING!!");
__delay_ms(2000);
}
return 0;
}