☰
ST7920 12864液晶并口驱动实战:从点不亮到显示汉字
2026/10/15 16:54:14 网站建设 项目流程

简介:这份资源面向单片机与嵌入式开发初学者,聚焦128×64点阵液晶屏同时显示汉字、字母与数字的完整实现。包内提供C语言源程序与Proteus仿真工程,可直接在Keil环境下编译调试,帮助读者理解点阵驱动、字模取模与硬件接口控制等关键环节,适用于智能家居、工业控制及教学实验等场景。压缩包共17个文件,约60KB,包含C源码、A51启动代码、HEX烧录文件、DSN仿真原理图、UV2工程配置及LST、OBJ等编译中间文件,覆盖从源码到仿真的完整链路。目前已有2904人学习下载,适合希望动手实践液晶驱动、积累嵌入式底层控制经验的读者参考借鉴。

1. 点一块 12864 液晶,为什么有人一次点亮有人三天翻车

手头有一批老式 12864 液晶模块,想让它显示汉字、字母和数字,结果有人半小时出画面,有人调了三天还是白屏或者满屏乱码。这块屏在工控面板、仪器仪表、旧设备改造里出镜率极高,ST7920 主控带中文字库的版本尤其常见,能直接显示 GB2312 汉字,不用自己取模。它解决的核心问题就一个:用最低的硬件开销,把中文信息稳定地显示出来。适合做单片机入门练手、老设备显示模块替换、以及需要低成本人机界面的场景。下面按硬件接线、并口驱动、字库调用、避坑排查、进阶技巧的顺序,把这块屏从点不亮到显示工整的完整路径拆一遍。

2. 12864 的硬件底子:ST7920 并口接线与供电边界

2.1 先分清你手里是哪块 12864

12864 只是分辨率叫法,128×64 点阵,但主控和接口差别很大。常见三类:ST7920 带中文字库、KS0108 不带字库、以及各种串口/并口兼容片。带字库的 ST7920 是本文重点,因为它内置 GB2312 字库,显示汉字只需要发两个字节的编码,不用自己建字模数组。判断方法很简单:看模块背面丝印,有 ST7920 字样、引脚里有 PSB 的,基本就是它。PSB 接高电平走并口,接低电平走串口。并口又分 8 位和 4 位模式,8 位接线多但时序简单,新手建议先上 8 位并口。

供电上,模块标称 5V,但背光 LED 往往要单独限流。我一般会在背光正极串一个 100Ω 到 220Ω 的电阻,具体看亮度需求。直接把背光接 5V 不加限流,短期能亮,长期容易把背光二极管烧暗。逻辑电平方面,5V 单片机可以直接连,3.3V 单片机建议加电平转换,否则高电平识别可能不稳,表现为偶尔能写进去偶尔写不进,这种玄学问题多半出在电平上。

2.2 并口引脚接线表与最小系统

以 8 位并口为例,模块侧关键引脚如下。不同厂家丝印略有差异,但功能一致。

引脚名功能接法
VSS地接 GND
VDD逻辑电源接 5V
V0对比度接可调电阻中间脚
RS数据/指令选择接单片机 IO
RW读写选择接单片机 IO,只写可接地
E使能接单片机 IO
DB0-DB7数据总线接单片机 8 个 IO
PSB并口/串口选择接高电平
RST复位接复位电路或 IO
BLA/BLK背光正负串限流电阻接 5V/GND

V0 对比度是新手最容易忽略的一脚。悬空或者接错,屏幕要么全黑要么全白,看起来像没驱动起来,其实数据已经写进去了。我一般用 10k 可调电阻,一端接 VDD 一端接 GND,中间脚接 V0,上电后慢慢调,直到字迹清晰。这一步没调好,后面所有代码都白搭。

2.3 上电时序与复位要求

ST7920 对上电顺序有要求。常见做法是 VDD 先上,等电源稳定后再拉高 RST 保持至少 10ms,然后拉低释放。如果 RST 直接接高电平不复位,部分模块上电后状态机停在未知状态,表现为花屏。稳妥做法是用一个 IO 控制 RST,初始化时先拉低延时再拉高。另外,E 使能信号的高电平宽度要够,ST7920 手册给的是最小 450ns,但实际用 1MHz 以内的 IO 翻转速度基本都能满足,不用刻意加延时,除非你的单片机主频很高,那就要在 E 拉高后插几个空指令。

3. 并口驱动怎么写:从初始化到显示一个汉字

3.1 底层写指令与写数据函数

并口驱动的核心就两个函数:写指令和写数据。区别只在 RS 电平。下面以常见的 51 或类 51 单片机为例,给出可抄的代码框架。

// 假设数据口接 P0,RS 接 P2^0,RW 接 P2^1,E 接 P2^2 #define LCD_DATA P0 sbit RS = P2^0; sbit RW = P2^1; sbit E = P2^2; // 写指令:RS=0,RW=0 void lcd_write_cmd(unsigned char cmd) { RS = 0; RW = 0; LCD_DATA = cmd; E = 1; // 空操作延时,保证 E 高电平宽度 _nop_(); _nop_(); E = 0; } // 写数据:RS=1,RW=0 void lcd_write_data(unsigned char dat) { RS = 1; RW = 0; LCD_DATA = dat; E = 1; _nop_(); _nop_(); E = 0; }

逻辑说明:ST7920 在 E 的下降沿采样数据,所以数据要先放好再拉高 E,然后拉低。RW 固定为写,如果只写不读,RW 可以直接接地省一个 IO。参数上,cmd 和 dat 都是 8 位,命令字后面会用到。注意 LCD_DATA 赋值前最好先读一下忙标志,但 ST7920 并口模式下忙标志读取需要把 RW 拉高,很多简化驱动直接靠延时等待,只要延时够长也能稳定。我一般会在每条指令后加 1ms 左右的延时,保守但省心。

3.2 初始化序列与显示模式设置

初始化顺序不能乱,常见序列是:功能设置、显示开关、清屏、进入设置模式、设置地址。下面这段是经过验证的稳定序列。

void lcd_init(void) { delay_ms(50); // 上电等待 lcd_write_cmd(0x30); // 基本指令集,8 位并口 delay_ms(1); lcd_write_cmd(0x30); // 再发一次,确保状态机同步 delay_ms(1); lcd_write_cmd(0x0C); // 显示开,光标关,不闪烁 delay_ms(1); lcd_write_cmd(0x01); // 清屏 delay_ms(10); // 清屏需要较长时间 lcd_write_cmd(0x06); // 光标右移,画面不移动 delay_ms(1); }

参数说明:0x30 是基本指令集且 8 位接口,发两次是血泪经验,部分模块第一次上电收不到,发两次能显著提高成功率。0x0C 对应显示开、光标关,如果你要光标就改成 0x0E 或 0x0F。0x01 清屏后必须给足 10ms,否则后续写入会被吃掉。0x06 设置写入后地址自动加一,这是最常用的模式。

3.3 显示汉字:直接发 GB2312 编码

带字库的 ST7920 显示汉字,不需要取模,直接把汉字的 GB2312 编码两个字节依次写入数据即可。比如“中”的 GB2312 编码是 0xD6 0xD0。

void lcd_show_char(unsigned char high, unsigned char low) { lcd_write_data(high); lcd_write_data(low); } // 显示“中”字 lcd_show_char(0xD6, 0xD0);

逻辑说明:ST7920 收到两个字节后自动查内部字库,把对应点阵打到当前地址。字母和数字更简单,直接发 ASCII 码,一个字节就行。比如显示字符 'A',调用 lcd_write_data('A')。这里有个细节:汉字和 ASCII 混排时,地址会自动按字节推进,一个汉字占两个字节位置,一个 ASCII 占一个,所以排版时要注意对齐。如果要在指定位置显示,先用 0x80 加地址设置 DDRAM 地址,再写数据。

3.4 定位与清屏的地址计算

12864 的显示 RAM 分上下两半屏,地址不是连续的。常见地址映射:上半屏第一行 0x80 到 0x87,第二行 0x90 到 0x97;下半屏第一行 0x88 到 0x8F,第二行 0x98 到 0x9F。每个地址对应 16 个点,也就是两个字节。要定位到第 x 行第 y 列,需要先算地址再发设置命令。

// 定位到上半屏第 row 行(0 或 1),第 col 列(0 到 7) void lcd_set_pos(unsigned char row, unsigned char col) { unsigned char addr; if (row == 0) addr = 0x80 + col; else addr = 0x90 + col; lcd_write_cmd(addr); }

参数说明:col 以 16 点为单位,0 到 7 共 8 列,对应 128 点宽。下半屏把 0x80 换成 0x88,0x90 换成 0x98。这个地址表建议直接抄进代码注释,调位置时对着看,比反复试错快得多。

4. 避坑排查:白屏、乱码、只显示一半的常见原因

4.1 现象:上电全白或全黑,调对比度也没用

原因:V0 对比度电压不对,或者 PSB 没接高电平导致模块在串口模式,并口写不进去。还有一种可能是 RST 没复位,状态机没起来。

解决:先确认 PSB 接高,再确认 RST 有低电平复位过程,最后调 V0。用万用表量 V0 对地电压,正常在 0.5V 到 2V 之间随调节变化。如果 V0 始终是 0V 或 5V,检查可调电阻接线。

4.2 现象:能显示但全是乱码方块

原因:数据口接线顺序错了,比如 DB0 和 DB7 接反,或者某根线虚焊。也可能是初始化序列没发两次 0x30,状态机没同步。

解决:用万用表逐根量数据线通断,重点查高位和低位有没有接反。然后确认初始化里 0x30 发了两次。如果还乱,把写指令后的延时加到 5ms 试一次,排除时序太快。

4.3 现象:上半屏正常,下半屏不显示或显示错位

原因:地址算错了。下半屏的地址不是上半屏加 0x08 那么简单,第一行是 0x88,第二行是 0x98,和上半屏的 0x80、0x90 不是连续关系。

解决:对照地址表重新检查定位函数。另外,ST7920 在扩展指令集下才有绘图模式,基本指令集下只能按上述地址操作,别混用。

4.4 现象:显示几个字后后面全是空白

原因:DDRAM 地址越界或者没有在写满一行后换行。12864 一行只有 8 个汉字位,写第 9 个汉字会跑到下一行或者无效区域。

解决:在写数据前判断列计数,到 8 就手动换行。或者用地址设置命令显式定位,不要依赖自动地址递增跨行。

4.5 现象:背光亮但屏幕没反应,换一块模块就好了

原因:模块本身坏了,或者排线接触不良。这种概率不低,尤其是拆机模块。

解决:换一块确认好的模块交叉验证。如果换模块就好,别在代码上死磕。我一般会备一块确认能用的模块作为基准,调不通时先换模块排除硬件问题。

5. 进阶技巧:自定义字符与显示缓冲区管理

5.1 用 CGRAM 造几个自定义符号

ST7920 除了 GB2312 字库,还支持自定义字符,存在 CGRAM 里,一共 4 个自定义字符位,地址是 0x00 到 0x07。每个自定义字符是 16 字节的点阵数据,按上下两半各 8 字节写入。下面是一个自定义电池图标的例子。

// 自定义字符:电池图标,存入 CGRAM 地址 0 void lcd_load_custom(void) { unsigned char battery[16] = { 0x3E, 0x22, 0x2A, 0x22, 0x2A, 0x22, 0x3E, 0x00, // 上半部分 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 下半部分 }; lcd_write_cmd(0x40); // 设置 CGRAM 地址为 0 for (int i = 0; i < 16; i++) { lcd_write_data(battery[i]); } lcd_write_cmd(0x80); // 回到 DDRAM }

逻辑说明:0x40 是 CGRAM 起始地址,连续写 16 字节后自动指向下一个自定义字符位。写完后用 0x00 到 0x03 的编码调用显示。参数上,点阵数据按字节从高位到低位对应每一行,1 亮 0 灭。这个技巧适合做图标、进度条边框、特殊符号,比外挂字库灵活。

5.2 用缓冲区做局部刷新

直接往屏幕写数据,每次刷新都要重写整屏,闪烁明显。常见做法是在单片机内存里开一块 128×64 位的缓冲区,所有绘制操作先改缓冲区,再统一刷到屏幕。缓冲区大小是 1024 字节,对资源紧张的单片机要权衡。如果内存不够,可以只缓冲一行或者半屏。

unsigned char lcd_buf[8][64]; // 8 页,每页 64 字节 void lcd_flush(void) { for (int page = 0; page < 8; page++) { // 设置页地址,具体命令依模块而定 lcd_write_cmd(0xB0 + page); lcd_write_cmd(0x80); // 列地址 0 for (int col = 0; col < 64; col++) { lcd_write_data(lcd_buf[page][col]); } } }

参数说明:这里用的是页地址模式,和前面基本指令集的地址模式不同,实际使用时需要确认模块支持的指令集。如果模块只支持基本指令集,缓冲区方案要按 DDRAM 地址映射来组织。这个技巧的价值在于:改一个数字不用重画整屏,减少闪烁,也方便做动画。

5.3 验证显示是否正确的三个检查点

调通之后,建议做三个验证。第一,显示一串连续汉字,确认字库调用正确,没有错位。第二,在四行分别显示不同内容,确认地址映射无误。第三,快速刷新一个变化的数字,确认没有残影或闪烁。这三个过了,基本可以认为驱动稳定。从那以后我每次调新屏,都会先跑这三个检查点,再往项目里集成,省得后面返工。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询