ST7920 12864液晶并口驱动实战:从点不亮到显示汉字
简介这份资源面向单片机与嵌入式开发初学者聚焦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 先分清你手里是哪块 1286412864 只是分辨率叫法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地接 GNDVDD逻辑电源接 5VV0对比度接可调电阻中间脚RS数据/指令选择接单片机 IORW读写选择接单片机 IO只写可接地E使能接单片机 IODB0-DB7数据总线接单片机 8 个 IOPSB并口/串口选择接高电平RST复位接复位电路或 IOBLA/BLK背光正负串限流电阻接 5V/GNDV0 对比度是新手最容易忽略的一脚。悬空或者接错屏幕要么全黑要么全白看起来像没驱动起来其实数据已经写进去了。我一般用 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 单片机为例给出可抄的代码框架。// 假设数据口接 P0RS 接 P2^0RW 接 P2^1E 接 P2^2 #define LCD_DATA P0 sbit RS P2^0; sbit RW P2^1; sbit E P2^2; // 写指令RS0RW0 void lcd_write_cmd(unsigned char cmd) { RS 0; RW 0; LCD_DATA cmd; E 1; // 空操作延时保证 E 高电平宽度 _nop_(); _nop_(); E 0; } // 写数据RS1RW0 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 换成 0x880x90 换成 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 验证显示是否正确的三个检查点调通之后建议做三个验证。第一显示一串连续汉字确认字库调用正确没有错位。第二在四行分别显示不同内容确认地址映射无误。第三快速刷新一个变化的数字确认没有残影或闪烁。这三个过了基本可以认为驱动稳定。从那以后我每次调新屏都会先跑这三个检查点再往项目里集成省得后面返工。希望帮到你。本文还有配套的精品资源点击获取