Lập trình LCD và I2C với pic 12f1840
Lập trình LCD và I2C giúp giải quyết việc hiển thị dữ liệu lên LCD với các dòng vi điều khiển Pic có ít chân (pin) như 12fxx
Việc lập trình hiển thị dữ liệu lên LCD trên mikroc mình đã post nhiều project, nó khá đơn giản vì mikroc đã hỗ trợ tất cả các thư viện, chỉ cần bạn kết nối đúng và khai báo port đúng là thực hiện thành công. Tuy nhiên trong project này ta sử dụng vi điều khiển pic 12f1840, vi điều khiển này có rất ít chân (8 pin), chỉ có 1 port từ RA0 đến RA5. Như vậy để kết nối với LCD theo giao thức 4 bit thông thường cần tối thiểu 6 chân để điều khiển, vậy là không còn chân (pin) nào để làm việc khác. Một giải pháp đặt ra là ta kết hợp việc lập trình LCD và I2C để thực hiện việc này. Bằng cách này ta chỉ sử dụng 2 pin RA1, RA2 (SCL, SDA) để truyền dữ liệu bằng giao thức I2C đến IC ghi dịch PC8574, điều khiển LCD.
Vi điều khiển Pic12f1840
Như vậy với việc sử dụng giao thức I2C ta đã tiết kiệm được 4 pin của vi điều khiển, thêm vào đó ta học được cách truyền dữ liệu qua giao thức I2C, một công đôi việc nhé!
Sau đây là đoạn code mẫu:
/*
Project: LCD và I2C
Description: hien thi du lieu len LCD thong qua giao thuc i2C
voi cac dong vi dieu khien it pin
Author: Jayanth Devarayanadurga
Re-Edit: giaoan.link
*/
#define _LCD_FIRST_ROW 0x80 //Move cursor to the 1st row
#define _LCD_SECOND_ROW 0xC0 //Move cursor to the 2nd row
#define _LCD_THIRD_ROW 0x94 //Move cursor to the 3rd row
#define _LCD_FOURTH_ROW 0xD4 //Move cursor to the 4th row
#define _LCD_CLEAR 0x01 //Clear display
#define _LCD_RETURN_HOME 0x02 //Return cursor to home position, returns a shifted display to
//its original position. Display data RAM is unaffected.
#define _LCD_CURSOR_OFF 0x0C //Turn off cursor
#define _LCD_UNDERLINE_ON 0x0E //Underline cursor on
#define _LCD_BLINK_CURSOR_ON 0x0F //Blink cursor on
#define _LCD_MOVE_CURSOR_LEFT 0x10 //Move cursor left without changing display data RAM
#define _LCD_MOVE_CURSOR_RIGHT 0x14 //Move cursor right without changing display data RAM
#define _LCD_TURN_ON 0x0C //Turn Lcd display on
#define _LCD_TURN_OFF 0x08 //Turn Lcd display off
#define _LCD_SHIFT_LEFT 0x18 //Shift display left without changing display data RAM
#define _LCD_SHIFT_RIGHT 0x1E //Shift display right without changing display data RAM
// LCD Definitions
#define LCD_ADDR 0x4E
// Lcd constants
char txt1[] = "localhost/dientudieukhien.net";
char txt2[] = "I2C LCD";
char txt3[] = "PIC12F1840";
char txt4[] = "4 MHz";
void I2C_LCD_Cmd(char out_char) {
char byte;
byte = out_char & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
byte.F0 = 0;
byte.F2 = 1;
byte.F3 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte = (out_char << 4) & 0xF0;
byte.F0 = 0;
byte.F2 = 1;
byte.F3 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
I2C1_stop();
if(out_char == 0x01)Delay_ms(2);
}
void I2C_LCD_Chr(char row, char column, char out_char) {
char byte;
switch(row){
case 1:
I2C_LCD_Cmd(0x80 + (column - 1));
break;
case 2:
I2C_LCD_Cmd(0xC0 + (column - 1));
break;
case 3:
I2C_LCD_Cmd(0x94 + (column - 1));
break;
case 4:
I2C_LCD_Cmd(0xD4 + (column - 1));
break;
};
byte = out_char & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
byte.F0 = 1;
byte.F2 = 1;
byte.F3 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte = (out_char << 4) & 0xF0;
byte.F0 = 1;
byte.F2 = 1;
byte.F3 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
I2C1_stop();
}
void I2C_LCD_Chr_Cp(char out_char) {
char byte;
byte = out_char & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
byte.F0 = 1;
byte.F2 = 1;
byte.F3 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte = (out_char << 4) & 0xF0;
byte.F0 = 1;
byte.F2 = 1;
byte.F3 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
I2C1_stop();
}
void I2C_LCD_Init() {
char byte;
Delay_ms(150);
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
Delay_ms(30);
byte = 0x30;
byte.F3 = 1;
byte.F2 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
Delay_ms(30);
byte.F2 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
Delay_ms(30);
byte.F2 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
Delay_ms(20);
byte = 0x20;
byte.F3 = 1;
byte.F2 = 1;
I2C1_Wr(byte);
I2C1_Is_Idle();
byte.F2 = 0;
I2C1_Wr(byte);
I2C1_Is_Idle();
Delay_ms(10);
I2C_LCD_Cmd(0x28);
I2C_LCD_Cmd(0x06);
}
void I2C_LCD_Out(char row, char col, char *text) {
while(*text)
I2C_LCD_Chr(row, col++, *text++);
}
void I2C_LCD_Out_Cp(char *text) {
while(*text)
I2C_LCD_Chr_Cp(*text++);
}
void main() {
ANSELA = 0x00;
TRISA = 0x00;
PORTA = 0x00;
LATA = 0x00;
I2C1_Init(100000);
Delay_ms(200);
I2C_LCD_Init();
I2C_LCD_Cmd(_LCD_CURSOR_OFF);
I2C_LCD_Cmd(_LCD_CLEAR);
while(1) {
I2C_Lcd_Out(1,1,txt1); // Write text in first row
I2C_Lcd_Out(2,1,txt2); // Write text in second row
I2C_Lcd_Out(3,1,txt3); // Write text in third row
I2C_Lcd_Out(4,1,txt4); // Write text in fourth row
Delay_ms(2000);
}
}