Để có thể hiển thị các ký tự đặc biệt trên LCD 16×2 bạn nên đọc trước bài hướng dẫn MPLAB XC8 – Lập trình LCD. Như chúng ta đã biết IC HD44780 dùng để điều khiển LCD cho phép chúng ta khai báo 8 ký tự đặc biệt để hiển thị bên cạnh các ký tự nằm trong bảng mã ASCII. Bài hướng dẫn này sẽ giúp các bạn lập trình hiển thị một vài ký tự đặc biệt trên LCD 16×2 dùng PIC16F877A và MPLAB XC8.
DDRAM, CGROM và CGRAM
CGROM – Character Generator ROM
Bộ nhớ CGROM lưu trữ các ma trận điểm 5×8 hoặc 5×10, mỗi ma trận là một ký tự đặc biệt dùng để hiển thị trên LCD. Nó có thể lưu trữ 205 ký tự kiểu 5×8 và 32 ký tự kiểu 5×10
DDRAM – Display Data RAM
This is the memory which holds the character data which is currently displayed on the LCD screen. Its capacity is 80×8 bits, ie 80 characters.
Bộ nhớ DDRAM lưu trữ ký tự đang được hiển thị trên màn hình LCD, nó có thể lưu trữ 80×8 bit, tương đương với 80 ký tự
CGRAM – Character Generator RAM
Bộ nhớ CGRAM tương tự với bộ nhớ CGROM nhưng đây là bộ nhớ RAM, chúng ta có thể thay đổi dữ liệu lưu trữ trên bộ nhớ này. Vì vậy các ký tự đặc biệt muốn hiển thị trên LCD sẽ được lưu trữ trên đây. Chúng ta có thể lưu được 8 ký tự kiểu 5×8 và 4 ký tự kiểu 5×10 trên CGRAM.
Cách tạo ký tự đặc biệt
Một ký tự hiển thị trên LCD là một ma trận điểm 5×8 hoặc 5×10, thông thường là kiểu hiển thị 5×8. Chúng ta có thể dễ dàng tạo một ký tự đặc biệt bằng cách định nghĩa từng pixel trong ma trận. Ví dụ để tạo ký tự Ω theo kiểu ký tự 5×8:

MPLAB XC8 Code
lcd.h
/* * File: lcd.h * Author: TienAnh * * Created on January 3, 2016, 1:41 PM */ #ifndef LCD_H #define LCD_H #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif #endif /* LCD_H */
lcd.c
//LCD Functions void Lcd_Port(char a) { if(a & 1) D4 = 1; else D4 = 0; if(a & 2) D5 = 1; else D5 = 0; if(a & 4) D6 = 1; else D6 = 0; if(a & 8) D7 = 1; else D7 = 0; } void Lcd_Cmd(char a) { RS = 0; // => RS = 0 Lcd_Port(a); EN = 1; // => E = 1 __delay_ms(4); EN = 0; // => E = 0 } Lcd_Clear() { Lcd_Cmd(0); Lcd_Cmd(1); } void Lcd_Set_Cursor(char a, char b) { char temp,z,y; if(a == 1) { temp = 0x80 + b - 1; z = temp>>4; y = temp & 0x0F; Lcd_Cmd(z); Lcd_Cmd(y); } else if(a == 2) { temp = 0xC0 + b - 1; z = temp>>4; y = temp & 0x0F; Lcd_Cmd(z); Lcd_Cmd(y); } } void Lcd_Init() { Lcd_Port(0x00); __delay_ms(20); Lcd_Cmd(0x03); __delay_ms(5); Lcd_Cmd(0x03); __delay_ms(11); Lcd_Cmd(0x03); ///////////////////////////////////////////////////// Lcd_Cmd(0x02); Lcd_Cmd(0x02); Lcd_Cmd(0x08); Lcd_Cmd(0x00); Lcd_Cmd(0x0C); Lcd_Cmd(0x00); Lcd_Cmd(0x06); } void Lcd_Write_Char(char a) { char temp,y; temp = a&0x0F; y = a&0xF0; RS = 1; // => RS = 1 Lcd_Port(y>>4); //Data transfer EN = 1; __delay_us(40); EN = 0; Lcd_Port(temp); EN = 1; __delay_us(40); EN = 0; } void Lcd_Write_String(char *a) { int i; for(i=0;a[i]!='\0';i++) Lcd_Write_Char(a[i]); } void Lcd_Shift_Right() { Lcd_Cmd(0x01); Lcd_Cmd(0x0C); } void Lcd_Shift_Left() { Lcd_Cmd(0x01); Lcd_Cmd(0x08); }
main.c
#define _XTAL_FREQ 8000000 #define RS RD2 #define EN RD3 #define D4 RD4 #define D5 RD5 #define D6 RD6 #define D7 RD7 #include #include "lcd.h"; // BEGIN CONFIG #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT enabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #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) //END CONFIG const unsigned short MyChar5x8[] = { 0x00, 0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00, // Code for char num #0 0x0E, 0x1B, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x00, // Code for char num #1 0x0E, 0x1B, 0x11, 0x11, 0x11, 0x1F, 0x1F, 0x00, // Code for char num #2 0x0E, 0x1B, 0x11, 0x11, 0x1F, 0x1F, 0x1F, 0x00, // Code for char num #3 0x0E, 0x1B, 0x11, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // Code for char num #4 0x0E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // Code for char num #5 0x00, 0x04, 0x02, 0x1F, 0x02, 0x04, 0x00, 0x00, // Code for char num #6 0x00, 0x00, 0x0E, 0x11, 0x11, 0x0A, 0x1B, 0x00 // Code for char num #7 }; void InitCustomChars() { char i; Lcd_Cmd(0x04); // Set CGRAM Address Lcd_Cmd(0x00); // .. set CGRAM Address for (i = 0; i <= 63 ; i++) Lcd_Write_Char(MyChar5x8[i]); Lcd_Cmd(0); // Return to Home Lcd_Cmd(2); // .. return to Home } int main() { TRISD = 0x00; // PORTD as Output Lcd_Init(); InitCustomChars(); // Write custom characters to LCD memory while(1) { Lcd_Clear(); Lcd_Set_Cursor(1,1); Lcd_Write_Char(0); // Display Custom Character 0 Lcd_Write_Char(1); // Display Custom Character 1 Lcd_Write_Char(2); // Display Custom Character 2 Lcd_Write_Char(3); // Display Custom Character 3 Lcd_Write_Char(4); // Display Custom Character 4 Lcd_Write_Char(5); // Display Custom Character 5 Lcd_Write_Char(6); // Display Custom Character 6 Lcd_Write_Char(7); // Display Custom Character 7 __delay_ms(1000); } return 0; }
Sơ đồ mạch điện
