news 2026/6/10 15:07:41

LCD12864地址计数器与显示位置关系图解说明

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LCD12864地址计数器与显示位置关系图解说明

深入理解 LCD12864 显存布局:地址计数器如何精准控制显示位置

你有没有遇到过这样的情况?明明写好了字模数据,也调用了显示函数,结果汉字却“飞”到了屏幕底部,或者图像错位、重叠、闪烁……在使用LCD12864这类图形液晶屏时,很多初学者甚至有经验的工程师都会被“显示偏移”问题困扰。而罪魁祸首,往往不是硬件故障,也不是字库出错——而是对一个关键机制的理解不足:地址计数器(Address Counter, AC)与显存映射的关系

今天我们就来彻底讲清楚这个问题。不绕弯子,不堆术语,从底层结构出发,一步步带你搞懂:

为什么写入的数据没有出现在预期的位置?
地址计数器到底是怎么工作的?
怎样才能让每一个像素都落在它该在的地方?


一、LCD12864 的真实“内存地图”长什么样?

我们习惯性地认为:“屏幕是从左到右、从上到下线性排列的”。但遗憾的是,LCD12864 并不这么想

它的内部显存(GDRAM)并不是按扫描顺序连续存储的,而是被切成了8个“页”(Page),每页管理 8 行像素高度,共 64 行(8×8=64),横向则有 128 列。

这就像一本书:
- 整本书是整个屏幕;
- 每一页纸对应 LCD 中的一个“Page”;
- 每页纸上能画一条高 8 像素的横带;
- 要画满整屏,你需要翻 8 页纸。

显存组织结构一览(以 ST7920 控制器为例)

页号Y 像素范围数据长度功能说明
Page 0Y = 0 ~ 7128 字节第一横条区域
Page 1Y = 8 ~ 15128 字节第二横条区域
Page 2Y = 16 ~ 23128 字节——
——
Page 7Y = 56 ~ 63128 字节最下面一条

每一列(X方向)上的一个字节控制纵向8个像素点,也就是说:

向 Page 0, Column 10 写入 0x80(即二进制 1000_0000) → 在坐标 (10, 0) 处点亮一个点

因为最高位对应的是当前页内的第0行(Y%8 == 0)。
同理,写入0x01就会在该列的最下方(Y=7)点亮一个点。

✅ 简单记法:每个字节垂直分布,高位在上,低位在下

所以如果你想绘制一个完整的 16×16 汉字,它会跨越两个页(比如 Page 0 和 Page 1),每页写入 16 字节数据。


二、地址计数器 AC:你的“自动笔尖”

当你往 LCD 写数据的时候,LCD 怎么知道该把这笔数据放在哪里?答案就是:地址计数器(AC)

你可以把它想象成一支正在写字的笔尖,它始终指向下一个要写的位置。

地址计数器的核心行为

  • 它只记录当前列地址(Column Address),范围是 0~127;
  • 它不会自动跨页!哪怕你一直写到第127列,也不会跳去下一页;
  • 每次写入一个字节后,AC 自动 +1(默认递增模式);
  • 如果你想换页或跳转位置,必须手动发送指令设置新的页和列地址;
  • AC 的值不能直接写入,只能通过“读忙状态+地址”命令读取。

这就带来一个重要结论:

🔴地址计数器 ≠ 屏幕坐标指针,它是受控于页地址和列地址设置的动态偏移量

举个例子:
假设你现在设置了 Page 0,然后写了 10 个字节,此时 AC = 10。
接下来如果你不做任何操作就切换到 Page 1,AC 仍然会从 10 开始继续递增!

这意味着:只要你不重置列地址,地址计数器就会继承之前的偏移!

这也是为什么很多人发现:“第二次写内容总是比第一次靠右几个字符”的根本原因。


三、图解地址移动路径:别再误以为它是“自动翻页”的

让我们模拟一次连续写入过程,看看地址计数器的真实轨迹。

初始状态:Page 0, AC = 0

执行以下动作:

set_page(0); // 进入第0页 set_column(0); // 设置起始列为0 → AC=0 write_data(0xFF); // 写一字节 → AC=1 write_data(0xFF); // → AC=2 ... write_data(0xFF); // 第128次 → AC=127 write_data(0xFF); // 继续写?→ AC溢出!可能回绕为0或无效

现在问题来了:我能直接进入 Page 1 吗?

不可以!除非你重新设置列地址,否则即使你发了set_page(1),地址计数器依然停留在 127 或其他未知值。下一次写入还是会从 Column 127 开始,很可能只显示最后一个像素!

✅ 正确做法是:

set_page(1); set_column(0); // 强制将AC归零,确保从左边开始

这才是安全可靠的跨页操作方式。

📌 所以记住一句话:

换页必设列!否则笔尖还在原来的位置,新页照样错位!


四、实战代码解析:如何精准定位并高效刷新

下面我们用 C 语言封装几个实用函数,帮助你在项目中避免这些坑。

1. 设置光标位置(即设置页 + 列)

/** * 设置显示起始位置(逻辑坐标转物理页/列) * @param page: 0~7 对应 Page 0~7 * @param col: 0~127 对应 X 坐标 */ void lcd12864_set_cursor(uint8_t page, uint8_t col) { // 设置页地址: 命令 0xB0 ~ 0xB7 send_command(0xB0 + (page & 0x07)); // 设置列地址高4位: 0x10 ~ 0x1F send_command(0x10 | ((col >> 4) & 0x0F)); // 设置列地址低4位: 0x00 ~ 0x0F send_command(0x00 | (col & 0x0F)); }

📌 关键点:
-0xB0 + page是标准指令格式;
- 列地址分高低字节发送,这是 KS0108/ST7920 系列的通用设计;
- 调用此函数后,地址计数器会被强制同步到指定列位置。


2. 快速批量写入(利用 AC 自动递增)

/** * 连续写入多个字节(适用于填充一行或一块图像) * @param data: 数据缓冲区 * @param len: 字节数(建议 ≤128,防止越界) */ void lcd12864_write_bytes(const uint8_t *data, uint8_t len) { for (uint8_t i = 0; i < len; i++) { write_data(data[i]); // 每次写入后 AC 自动+1 } }

💡 应用场景:
- 显示一行水平线条;
- 刷新某个图标区域;
- 写入汉字上半部分(16字节);

⚠️ 注意事项:
- 必须提前调用lcd12864_set_cursor()设置好起始位置;
- 不要超过当前页的边界(128列),否则可能溢出;
- 若需跨页,中间必须重新设置页和列。


3. 高级封装:draw_pixel 实现任意点绘制

虽然 LCD12864 不适合逐点操作(效率低),但在调试或绘制曲线时仍有必要实现。

/** * 在指定坐标点亮一个像素 * @param x: X坐标 (0~127) * @param y: Y坐标 (0~63) */ void draw_pixel(uint8_t x, uint8_t y) { uint8_t page = y / 8; // 计算所属页 uint8_t bit = y % 8; // 在字节中的位位置 uint8_t mask = 1 << (7 - bit); // 高位对应Y较小值 // 读出现有数据(需要支持读操作且有读写引脚) lcd12864_set_cursor(page, x); uint8_t old_data = read_data(); // 注意:需启用读模式 uint8_t new_data = old_data | mask; // 重新设置地址(读操作也会使AC+1,需重置) lcd12864_set_cursor(page, x); write_data(new_data); }

🔧 提示:
- 实际应用中尽量避免频繁读写,可考虑建立 RAM 缓冲区镜像;
- 多数模块默认只写不读,如需读取 GDRAM 需外接 R/W 引脚并修改驱动。


五、常见“翻车”现场与避坑指南

❌ 问题1:文字显示错位、漂移

现象:每次开机显示的内容位置不一样,或者越来越靠右。

根源:未初始化地址计数器,依赖“上次残留状态”。

解决方案
- 每次重要写入前都调用lcd12864_set_cursor(page, col)
- 上电后执行一次全屏清零或复位初始化流程;
- 使用调试工具读取 BF 和 AC 状态辅助排查。


❌ 问题2:图像上下颠倒或断层

现象:汉字上半身在 Page 0,下半身却出现在 Page 2。

根源:页地址设置错误,或忘记切换页。

正确流程(显示一个16×16汉字)

// 显示汉字 "中" 起始于 X=10, Y=0 const uint8_t *font = get_chinese_font('中'); // 写前8行(Page 0) lcd12864_set_cursor(0, 10); lcd12864_write_bytes(font, 16); // 上半部分 // 写后8行(Page 1) lcd12864_set_cursor(1, 10); lcd12864_write_bytes(font+16, 16); // 下半部分

✅ 每次换页都要重新设置起始列!


❌ 问题3:刷新慢、界面卡顿

原因:每写一个字节都重复设置地址。

反例

for (int i = 0; i < 128; i++) { lcd12864_set_cursor(0, i); // 错!频繁设地址 write_data(buffer[i]); }

正解

lcd12864_set_cursor(0, 0); // 只设一次 lcd12864_write_bytes(buffer, 128); // 利用AC自动递增

⏱ 效率提升可达5~10倍以上


六、最佳实践建议:写出稳定高效的 LCD 驱动

  1. 永远不要假设 AC 处于某个状态
    → 每次关键操作前显式设置页和列。

  2. 批量操作优先于单字节写入
    → 减少指令通信次数,提高刷新率。

  3. 建立显存镜像缓冲区(Frame Buffer)
    → 在 RAM 中维护一份 GDRAM 副本,局部更新后再刷入,减少闪烁。

  4. 封装高级绘图接口
    c void lcd_draw_char(x, y, ch); void lcd_draw_string(x, y, str); void lcd_fill_rect(x, y, w, h, color);
    底层自动处理页切换与地址同步。

  5. 监控忙信号(Busy Flag)
    在每次发送命令或数据前,检测 BF 是否为0,防止控制器未准备好导致数据丢失。


写在最后:掌握本质,才能游刃有余

LCD12864 看似简单,但其“页-列”分离的显存结构和地址计数器的行为逻辑,常常成为嵌入式开发中的“隐形陷阱”。

一旦你真正理解了:
- 地址计数器只是一个列指针;
- 页地址必须单独设置;
- 跨页操作必须重置列地址;
- 批量写入依赖 AC 的自动递增特性;

你会发现,无论是显示汉字、绘制图表,还是实现滚动菜单、动画效果,都能做到精准控制、高效运行。

对于从事工业控制、智能仪表、家电面板等领域的开发者来说,这种底层掌控力,远比调用现成库更有价值。

如果你也在用 LCD12864,欢迎分享你在实际项目中遇到的“神奇bug”以及解决方法。一起交流,少走弯路!


技术无捷径,唯有深挖原理,方能从容应对千变万化的工程挑战。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 14:22:39

百度网盘分享被封?转移至私有化部署的DDColor系统

百度网盘分享被封&#xff1f;转移至私有化部署的DDColor系统 你有没有遇到过这样的情况&#xff1a;辛辛苦苦修复好的老照片&#xff0c;通过百度网盘分享给家人&#xff0c;结果链接突然失效&#xff0c;提示“因涉及违规内容无法查看”&#xff1f;更离谱的是&#xff0c;一…

作者头像 李华
网站建设 2026/6/10 14:53:55

print driver host在x86平台的系统学习教程

print driver host 在 x86/x64 混合环境下的真实工作原理与实战解析 你有没有遇到过这种情况&#xff1a;一台老旧但仍在使用的打印机&#xff0c;驱动只有32位版本&#xff0c;却要部署在全新的64位 Windows 服务器上&#xff1f;系统居然能正常打印——这背后功臣之一就是 P…

作者头像 李华
网站建设 2026/6/10 15:26:48

GitHub镜像推荐:DDColor黑白照片修复模型快速部署教程

GitHub镜像推荐&#xff1a;DDColor黑白照片修复模型快速部署教程 在家庭相册的角落里&#xff0c;泛黄的老照片静静躺着——祖辈穿着旧式制服站在老屋门前&#xff0c;父母年轻时的笑容定格在胶片上。这些画面承载着记忆&#xff0c;却因岁月褪去了色彩。如今&#xff0c;AI正…

作者头像 李华
网站建设 2026/6/9 18:39:50

设计模式学习(7) 23-5 建造者模式

文章目录0.个人感悟建造者模式1. 概念2. 适配场景(什么场景下使用)3. 实现方法3.1 实现思路3.2 UML类图4. 代码示例5. 优缺点6. 源码分析-JDK中的StringBuilder实现分析7. vs抽象工厂模式0.个人感悟 建造者模式也是很典型的创建型设计模式。主要目的是将对象的构建和表示分离&…

作者头像 李华
网站建设 2026/6/10 16:21:15

【年度总结】只是一个简单的年度总结而已

本来想着1月17号要发生日文章就不发年度总结的&#xff0c;但是真到了跨年夜这一天内心还是感慨良多。稍加构思后博主打算将年度总结在功能定位上与生日文章错开&#xff0c;前者无情展示一些直观的成果&#xff0c;后者再深挖这一年的心路历程。关于博客 博主这一年取得的最重…

作者头像 李华
网站建设 2026/6/10 17:11:08

elasticsearch设置密码操作指南:基础环境下的密码配置

Elasticsearch 设置密码实战指南&#xff1a;从零构建安全访问体系你有没有遇到过这样的场景&#xff1f;刚部署好的 Elasticsearch 节点&#xff0c;对外暴露了 9200 端口&#xff0c;结果第二天就被扫描到、数据被清空&#xff0c;甚至收到勒索信息&#xff1a;“你的数据已被…

作者头像 李华