第二十三章 LCD1602液晶顯示
1. 導入
LCD1602(16×2 字符型液晶)使用基於HD44780控制器的命令集,支持8位或4位數據總線,常用於菜單、狀態與調試信息顯示。本章以“4位總線,RW接地(只寫)”方案實現穩定驅動,提供完整API:初始化、定位、打印、清屏、自定義字符(CGRAM)。
目標:
- 掌握LCD1602引腳與對比度、電源接法
- 理解4位時序與初始化流程
- 實現常用API:定位、打印、清屏、滾動/移位
- 擴展:顯示時鐘/温度、自定義字符“°”
2. 硬件設計
- 供電與對比度
- VSS→GND,VDD→+5V
- V0(對比度)→10k電位器中點(兩端接VCC/GND)
- 背光(如有):A→+5V,K→GND(可串電阻限流)
- 數據/控制引腳(4位方式,推薦)
- RW→GND(只寫,簡單可靠)
- RS:0=命令,1=數據
- E(EN):上升沿鎖存
- D4~D7:數據高四位,4位模式僅用這四根
示例接線(可按需改口):
P2.2 → RS,P2.3 → EP2.4 → D4,P2.5 → D5,P2.6 → D6,P2.7 → D7RW → GND
注意:若用P0口需外接上拉;P2口免上拉更穩。
3. 指令與尋址要點
- 主要命令(十六進制):
0x01清屏(>1.52ms)0x02歸位(>1.52ms)0x04/0x06輸入模式(增量/不移屏)0x08~0x0F顯示/光標/閃爍開關(如0x0C 顯示開、無光標)0x10/0x14/0x18/0x1C光標/整屏左/右移0x20/0x28數據長度與行數(0x28=4位、兩行、5×8點陣)0x80 | addr設置DDRAM地址(行列定位)
- DDRAM行首地址(1602):
- 第1行:0x00
- 第2行:0x40
4. 完整驅動(4位、RW接地)
#include <reg52.h>
#include <intrins.h>
/* 引腳映射:按你的接線修改 */
sbit LCD_RS = P2^2;
sbit LCD_EN = P2^3;
sbit LCD_D4 = P2^4;
sbit LCD_D5 = P2^5;
sbit LCD_D6 = P2^6;
sbit LCD_D7 = P2^7;
/* 延時(粗略,不依賴忙標誌) */
static void delay_us_inline() { _nop_(); _nop_(); _nop_(); _nop_(); }
void delay_ms(unsigned int ms){
unsigned int i,j;
for(i=0;i<ms;i++) for(j=0;j<125;j++);
}
/* 低級IO */
static void lcd_write4(unsigned char nib){ // 發送高4位或低4位
LCD_D4 = (nib & 0x01) ? 1 : 0;
LCD_D5 = (nib & 0x02) ? 1 : 0;
LCD_D6 = (nib & 0x04) ? 1 : 0;
LCD_D7 = (nib & 0x08) ? 1 : 0;
delay_us_inline();
LCD_EN = 1; delay_us_inline();
LCD_EN = 0; delay_us_inline();
}
/* 發送一個字節(4位總線):rs=0命令,rs=1數據 */
static void lcd_send(unsigned char value, bit rs){
LCD_RS = rs;
// 先發高四位
lcd_write4((value >> 4) & 0x0F);
// 再發低四位
lcd_write4(value & 0x0F);
// 大多數命令37us足夠,清屏/歸位需更長
if (!rs && (value==0x01 || value==0x02)) {
delay_ms(2);
} else {
// 約40us
_nop_(); _nop_(); _nop_(); _nop_();
}
}
static void lcd_cmd(unsigned char cmd){ lcd_send(cmd, 0); }
static void lcd_data(unsigned char dat){ lcd_send(dat, 1); }
/* 初始化(4位,2行,5x8,顯示開,光標關,自增) */
void lcd_init(void){
LCD_RS = 0; LCD_EN = 0;
delay_ms(40); // 上電等待>30ms
// 強制進入4位模式的固定序列(參考HD44780)
lcd_write4(0x03); delay_ms(5);
lcd_write4(0x03); delay_ms(5);
lcd_write4(0x03); delay_ms(1);
lcd_write4(0x02); // 現在進入4位模式
lcd_cmd(0x28); // 功能設置:4位、2行、5x8點陣
lcd_cmd(0x08); // 顯示關閉
lcd_cmd(0x01); // 清屏
delay_ms(2);
lcd_cmd(0x06); // 輸入模式:寫入後地址+1,屏不移
lcd_cmd(0x0C); // 顯示開,光標關,閃爍關
}
/* 常用API */
void lcd_clear(void){ lcd_cmd(0x01); delay_ms(2); }
void lcd_home(void){ lcd_cmd(0x02); delay_ms(2); }
/* 設置光標:行row=0/1,列col=0..15 */
void lcd_set_cursor(unsigned char row, unsigned char col){
unsigned char addr = (row ? 0x40 : 0x00) + (col & 0x0F);
lcd_cmd(0x80 | addr);
}
/* 打印字符串(以 '\0' 結束) */
void lcd_print(const char* s){
while(*s) lcd_data((unsigned char)*s++);
}
/* 指定位置打印 */
void lcd_print_at(unsigned char row, unsigned char col, const char* s){
lcd_set_cursor(row, col);
lcd_print(s);
}
/* 自定義字符(CGRAM)。loc:0~7,對應字符碼0..7;pattern[8]每行低5位有效 */
void lcd_define_char(unsigned char loc, const unsigned char pattern[8]){
unsigned char i;
loc &= 0x07;
lcd_cmd(0x40 | (loc << 3)); // 設置CGRAM地址
for(i=0;i<8;i++) lcd_data(pattern[i] & 0x1F);
lcd_cmd(0x80); // 返回DDRAM(可選)
}
/* 示例:主程序 */
void main(void){
unsigned int cnt = 0;
const unsigned char deg_sym[8] = {
0x04,0x0A,0x04,0x00,0x00,0x00,0x00,0x00 // 簡易“°”
};
lcd_init();
lcd_define_char(0, deg_sym); // 自定義字符0號為“°”
lcd_print_at(0, 0, "Hello, LCD1602!");
lcd_print_at(1, 0, "Cnt: ");
while(1){
char buf[6];
unsigned int v = cnt;
// 將數字轉字符串
buf[0] = (v/10000)%10 + '0';
buf[1] = (v/1000)%10 + '0';
buf[2] = (v/100)%10 + '0';
buf[3] = (v/10)%10 + '0';
buf[4] = (v%10) + '0';
buf[5] = '\0';
lcd_print_at(1, 5, buf); // 在第2行第6列更新計數
// 示範自定義符號(在末尾顯示“°C”)
lcd_set_cursor(1, 11);
lcd_data(0); // 打印自定義“°”
lcd_data('C');
cnt++;
delay_ms(500);
}
}
要點:
- 初始化中四次
lcd_write4(...)的固定序列是切入4位模式的關鍵。 RW接地避開忙標誌讀取,使用“足夠延時”保證可靠。- 清屏/歸位需要>1.52ms,其餘指令約37μs,代碼已分別處理。
5. 常用拓展
- 光標與移屏
lcd_cmd(0x0E):顯示開、有光標lcd_cmd(0x0F):顯示開、光標閃爍lcd_cmd(0x18):整屏左移;0x1C:右移
- 快速覆蓋行尾空白
- 打印後用空格填滿剩餘列,避免殘留字符
- 數值/時間顯示(與前章結合)
- DS1302時間:
lcd_print_at(0,0,"HH:MM:SS"); - DS18B20温度:使用
lcd_define_char自定義“°”,顯示“23.5°C”
6. 故障排查
- 無顯示/黑方塊一行:對比度V0未調;初始化時序錯誤;E腳未正確翻轉。
- 亂碼:4位高低半字節順序錯;數據線接錯;延時不足。
- 顯示位置錯亂:行首地址理解錯誤;
lcd_set_cursor計算錯誤。 - 閃爍嚴重:頻繁清屏;改用覆蓋更新局部字符。
7. 進階(可選)
- 8位總線:將D0~D7全部接入,初始化用
0x38(8位、2行、5×8),寫入一次8位即可(速度快,線多)。 - 讀忙標誌:RW接MCU,切換D口方向,讀BF(D7=1忙);但硬件與代碼更復雜,通常延時法已足夠。
- I²C轉接模塊:很多1602帶PCF8574 I/O擴展,I²C僅佔兩線(與第17章I²C兼容),命令需映射到PCF8574位序
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。