51单片机基础-LCD12864液晶显示
第二十四章 LCD12864液晶显示(ST7920并口文本模式)
1. 导入
LCD12864 是一款 128×64 点阵的图形液晶,常见模块多采用 ST7920 控制器。它兼容“字符模式”(类似 HD44780 的 1602)与“图形模式”,并支持并口(8位/4位)或三线串行方式。为了快速稳定上手,本章采用最简洁、最通用的方案:
- ST7920 并行8位总线,RW接地(只写),仅做“文本模式”(16×4字符)驱动;
- 提供完整API:初始化、清屏、定位、打印字符串、自定义字符。
说明:图形模式(逐点画图、显示位图)涉及 GDRAM 坐标与页地址映射,放在进阶章节展开。本章先把“可读可写、稳定显示”的基础夯实。
2. 硬件设计
- 供电与对比度
- VSS→GND,VDD→+5V
- VO(对比度)→10k电位器中点(两端接VCC/GND)
- 背光(若有):LED+→+5V,LED−→GND(或串限流电阻)
- 控制与数据(并口8位,RW接地)
- PSB:置高(并口方式,PSB=1)
- RST:接MCU可控复位引脚(或上拉到VCC,经延时自动复位)
- RS(DI):0=命令,1=数据
- RW:接地(0,只写)
- E(EN):上升沿/脉冲锁存
- DB0~DB7:8位数据总线(建议接P0口,需上拉)
- 推荐接线(可按需修改端口):
- P0.0
P0.7 → DB0DB7(P0口需外接上拉10k×8,或用上拉阵列电阻) - P2.0 → RS
- P2.1 → E
- P2.2 → RST
- PSB 拉高到VCC,RW接地
- P0.0
注意:
- P0为开漏口,必须外接上拉电阻;用P2/P1则可省上拉。
- RW接地后,无法读忙标志,需用“足够延时”法保证时序。
3. 指令与地址映射(文本模式)
常用指令(与1602相近):
- 0x30:功能设置(基本指令集、8位数据)
- 0x0C:显示开、光标关、闪烁关
- 0x01:清屏(>1.6ms)
- 0x06:输入模式(写入后地址+1)
- 0x80 | addr:设置DDRAM地址
16×4 行地址映射(ST7920特性):
- 第1行:0x80 ~ 0x8F
- 第2行:0x90 ~ 0x9F
- 第3行:0x88 ~ 0x8F
- 第4行:0x98 ~ 0x9F
(行3、行4的地址并非紧邻,属该芯片内部映射规律)
4. 完整驱动代码(Keil C51,8位并口,RW接地)
#include <reg52.h>
#include <intrins.h>/* 引脚映射:按你的接线修改 */
#define LCD_DATA P0 // 并口8位数据总线(P0需外接上拉)
sbit LCD_RS = P2^0; // RS/DI:0=命令 1=数据
sbit LCD_EN = P2^1; // E/EN
sbit LCD_RST = P2^2; // 硬件复位(低有效)/* 基本延时 */
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++);
}/* 发送命令/数据(RW接地,仅写) */
static void lcd_pulse_enable(void){LCD_EN = 1; delay_us_inline();LCD_EN = 0; delay_us_inline();
}static void lcd_write_cmd(unsigned char cmd){LCD_RS = 0;LCD_DATA = cmd;lcd_pulse_enable();// 指令典型执行时间:大多数~40us,清屏/归位>1.6msif (cmd == 0x01 || cmd == 0x02) delay_ms(2);else { delay_us_inline(); delay_us_inline(); delay_us_inline(); }
}static void lcd_write_data(unsigned char dat){LCD_RS = 1;LCD_DATA = dat;lcd_pulse_enable();// 数据写入约40usdelay_us_inline(); delay_us_inline(); delay_us_inline();
}/* 复位与初始化(文本模式,8位) */
void lcd12864_init(void){// 先拉低复位,再拉高LCD_RST = 0; delay_ms(5);LCD_RST = 1; delay_ms(10);// 基本指令集、8位lcd_write_cmd(0x30); // Function setdelay_ms(2);lcd_write_cmd(0x30);delay_ms(2);lcd_write_cmd(0x0C); // 显示开,光标关lcd_write_cmd(0x01); // 清屏delay_ms(2);lcd_write_cmd(0x06); // 写入后地址+1
}/* 清屏、归位 */
void lcd12864_clear(void){ lcd_write_cmd(0x01); delay_ms(2); }
void lcd12864_home(void){ lcd_write_cmd(0x02); delay_ms(2); }/* 设置光标:row=0..3,col=0..15(16列×4行) */
static unsigned char row_base_addr[4] = {0x80, 0x90, 0x88, 0x98};
void lcd12864_set_cursor(unsigned char row, unsigned char col){unsigned char addr;if (row > 3) row = 3;if (col > 15) col = 15;addr = row_base_addr[row] + col;lcd_write_cmd(addr);
}/* 打印字符串(ASCII/半角字符) */
void lcd12864_print(const char* s){while(*s){lcd_write_data((unsigned char)*s++);}
}/* 指定位置打印 */
void lcd12864_print_at(unsigned char row, unsigned char col, const char* s){lcd12864_set_cursor(row, col);lcd12864_print(s);
}/* 自定义字符(CGRAM,0..7,每字形8行×5点,低5位有效)注:ST7920文本模式兼容HD44780的CGRAM机制 */
void lcd12864_define_char(unsigned char loc, const unsigned char pattern[8]){unsigned char i;loc &= 0x07;lcd_write_cmd(0x40 | (loc << 3)); // CGRAM地址:0x40 + loc*8for(i=0;i<8;i++){lcd_write_data(pattern[i] & 0x1F);}// 返回DDRAM(任何一次设置地址到0x80..会自动回到DDRAM)lcd_write_cmd(0x80);
}/* 示例:主程序 */
void main(void){unsigned int cnt = 0;char buf[6];const unsigned char deg_sym[8] = {0x04,0x0A,0x04,0x00,0x00,0x00,0x00,0x00 // “°”简易点阵};// 上电默认电平LCD_EN = 0;LCD_RS = 0;LCD_RST = 1;LCD_DATA = 0x00;lcd12864_init();lcd12864_define_char(0, deg_sym); // 自定义字符编号0 = “°”lcd12864_clear();lcd12864_print_at(0, 0, "LCD12864 ST7920");lcd12864_print_at(1, 0, "Text Mode 16x4");lcd12864_print_at(2, 0, "Temp: 23");lcd12864_set_cursor(2, 8);lcd_write_data(0); // 打印自定义“°”lcd_write_data('C');while(1){// 简单计数显示:第4行刷新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';lcd12864_print_at(3, 0, "Count: ");lcd12864_print(buf);cnt++;// 粗略500ms{unsigned int i,j;for(i=0;i<500;i++) for(j=0;j<125;j++);}}
}
要点与小贴士:
- RW接地后,所有“等待忙标志”的流程都改用“足够延时”,关键指令(清屏/归位)已专门延时2ms。
- P0口必须有上拉电阻;若用P2或P1当数据口可省略上拉。
- 16×4行地址的“第3/4行”并非线性增长,需按映射表计算地址。
5. 常见问题与排查
- 仅一行黑块或完全无字:
- 对比度VO未调到合适电位;RST脚未正确复位;PSB未拉高到并口模式。
- 乱码/错位:
- 数据线顺序接错;地址映射未按表设置;延时不足(尤其清屏/归位)。
- 上电偶发异常:
- 增加上电延时与复位脉冲(RST低≥5ms再拉高,已在初始化中处理)。
- P0总线乱跳:
- 未加上拉;或外部上拉阻值过大导致时序边沿过慢(10k常见,必要时可降至4.7k)。
6. 进阶与扩展
- 4位总线模式:可仿照1602的“先高后低半字节”方式改写底层发送,节省4根数据线(时序更严格)。
- 串行三线模式:PSB=0 时 ST7920 进入串行接口,需按“首字节功能位 + 两个半字节”的协议发送命令/数据(位bang实现简单、占线少),适合IO紧张的场合。
- 图形模式(GDRAM):
- 切换到扩展指令集(0x34),开启图形显示(0x36),之后通过设置X/Y地址并连续写数据可逐点显示。
- 涉及页面/半屏映射与位序,建议配合帧缓冲或专用绘图API,后续章节单独展开。
- 与前章联动:将 DS1302 时间、DS18B20 温度输出到 12864,更直观;或做简单菜单系统(上下键选择、回车执行)。
7. 小结
- 本章完成了 ST7920 型 LCD12864 的并口文本模式驱动:初始化、定位、打印与自定义字符。
- 采用 RW 接地 + 充足延时的简化方案,可靠易调试,适合多数入门与教学项目。
- 图形模式与串行模式将作为进阶内容,在掌握文本模式后再拓展更丰富的显示能力。
