news 2026/3/13 3:32:14

LCD12864多页面显示切换的STM32实现示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LCD12864多页面显示切换的STM32实现示例

如何用STM32玩转LCD12864:从驱动到多页菜单的实战设计

你有没有遇到过这样的场景?手头有个温控器、数据采集仪或者智能仪表,想给它加个“脸”——一个能显示中文、操作直观的界面。但OLED太贵,TFT又太复杂,代码跑不起来还耗电严重。

这时候,LCD12864就该登场了。

这块黑乎乎的小屏,分辨率128×64,自带汉字库,5块钱搞定,接上STM32就能输出“系统正常”、“温度:25℃”这种接地气的信息。更关键的是——它支持多页面切换。你可以像翻菜单一样,在主页、设置页、关于页之间自由跳转。

今天我们就来干一件实在事:不用图形库、不依赖RTOS,用最基础的GPIO模拟SPI,让STM32F103驱动LCD12864实现稳定流畅的多页显示


为什么是LCD12864?不是OLED也不是TFT?

先说清楚:我们不是在怀旧,而是在做工程权衡。

特性LCD12864OLEDTFT-LCD
成本<¥10¥30~80¥50起
功耗(静态)极低(背光主导)自发光超省电背光常亮,功耗高
中文支持✅ 内置GB2312字库❌ 需烧录字体✅ 可加载任意字体
接口难度3线SPI可驱动I2C/SPI简单FSMC/DMA较复杂
开发门槛低,资料丰富中等

如果你做的设备要批量生产几百台,成本敏感;或是学生项目预算有限;又或者只是想快速验证一个想法——那LCD12864依然是那个“够用就好”的黄金选择。

而且它的优势很明确:
-能显示中文,用户一看就懂;
-无需显存管理,ST7920控制器自带显存;
-支持串行模式,三根IO线就能点亮;
-工业级稳定性,阳光下也能看清。


搞懂LCD12864的核心机制:地址怎么映射?数据怎么写?

别急着写代码,先搞明白这块屏是怎么组织画面的。

它不是像素阵列,而是“页-列”结构

LCD12864的分辨率是128×64,但它内部并不是按“行×列”直接存储的。它被划分为8页(Page 0 ~ Page 7),每页对应8行(共64行),每一列是一个字节(8位),代表垂直方向上的8个点。

也就是说:
- 每一页有128列 × 8行 = 1024字节;
- 整个显存共 8 × 1024 = 1KB;
- 写入一个字节时,实际上是向某一列写入8个纵向排列的点。

这个结构决定了我们必须按页操作。比如你要画一条横线跨多行,就得拆成8次写入,分别写到不同的Page中。

支持两种工作模式:文本 vs 图形

这是ST7920控制器的一大特色:

  • 文本模式(Basic Instruction Set, cmd=0x30)
  • 直接发送汉字或ASCII字符,自动查内置字库存储位置;
  • 光标定位使用goto_xy(x, y),x为列(0~15),y为行(0~7);
  • 最适合菜单、状态信息等静态文本。

  • 图形模式(Extended Instruction Set, cmd=0x34 + 0x36)

  • 手动控制每个像素点,可用于绘制图标、波形、进度条;
  • 需手动设置Page和Column地址;
  • 更灵活,但也更麻烦。

我们在多页系统中通常采用混合模式:标题用文本模式快速输出,图标或动态图用图形模式补充。


STM32怎么控制它?三根线搞定串行通信

虽然LCD12864支持并行8位接口,但对资源紧张的STM32F103C8T6来说,串行模式才是最优解

只需要3个GPIO:
-SCL(时钟)→ PA5
-SID(数据)→ PA7
-CS(片选)→ PA6

注意:电源建议5V供电(模块逻辑电平兼容3.3V输入),若MCU为3.3V系统,可在信号线上串联1kΩ电阻防反灌。

串行协议怎么玩?

ST7920的串行通信其实是一种“伪SPI”,时序如下:

  1. CS拉低开始帧;
  2. 发送控制字节(0xF8表示指令,0xFA表示数据);
  3. 分两次发送高4位和低4位;
  4. CS拉高结束。

下面是核心函数实现:

static void lcd12864_write_byte(uint8_t data) { for (int i = 7; i >= 0; i--) { HAL_GPIO_WritePin(LCD_PORT, LCD_SCL_PIN, GPIO_PIN_RESET); if (data & (1 << i)) HAL_GPIO_WritePin(LCD_PORT, LCD_SID_PIN, GPIO_PIN_SET); else HAL_GPIO_WritePin(LCD_PORT, LCD_SID_PIN, GPIO_PIN_RESET); __NOP(); __NOP(); // 延时约1μs HAL_GPIO_WritePin(LCD_PORT, LCD_SCL_PIN, GPIO_PIN_SET); } } void lcd12864_send_cmd(uint8_t cmd) { HAL_GPIO_WritePin(LCD_PORT, LCD_CS_PIN, GPIO_PIN_RESET); lcd12864_write_byte(0xF8); // 指令标识 lcd12864_write_byte(cmd & 0xF0); // 高4位 lcd12864_write_byte((cmd << 4) & 0xF0); // 低4位 HAL_GPIO_WritePin(LCD_PORT, LCD_CS_PIN, GPIO_PIN_SET); }

初始化时调用这些命令进入基本模式:

void lcd12864_init(void) { HAL_Delay(50); lcd12864_send_cmd(0x30); // 基本指令集 HAL_Delay(5); lcd12864_send_cmd(0x0C); // 开显示,关光标 HAL_Delay(5); lcd12864_send_cmd(0x01); // 清屏 HAL_Delay(5); }

现在屏幕已经准备好了,接下来就是让它“会说话”。


多页面切换的本质:状态机 + 按键中断

很多人一开始都会犯一个错误:在主循环里轮询按键,发现按下就翻页。结果呢?卡顿、误触发、响应慢。

真正的做法是:用外部中断处理按键,用状态机管理页面,用标志位协调刷新

硬件连接很简单

  • KEY1 接 PA0,接地,上升沿触发EXTI;
  • 上拉电阻确保默认高电平;
  • 按下时产生下降沿,触发中断。

中断服务程序只做一件事:标记需要翻页

#define PAGE_COUNT 3 uint8_t current_page = 0; volatile uint8_t page_needs_update = 1; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { HAL_Delay(20); // 简单消抖 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) return; // 不是有效按下 current_page = (current_page + 1) % PAGE_COUNT; page_needs_update = 1; // 标记刷新 } }

注意这里用了软件延时消抖。实际项目中推荐用定时器+状态机实现更可靠的去抖算法,但初学者这样足够。

主循环负责渲染,不阻塞也不忙等

while (1) { if (page_needs_update) { switch (current_page) { case 0: render_page_home(); break; case 1: render_page_settings(); break; case 2: render_page_about(); break; } page_needs_update = 0; } HAL_Delay(50); // 给CPU喘口气 }

这种前后台分离的设计,保证了按键响应快、显示更新及时,还能留出时间做其他任务(比如采集传感器数据)。


页面内容怎么画?封装你的render_xxx()函数

每个页面独立绘制,互不影响,后期扩展也方便。

举个例子,首页长这样:

【系统主页】 时间: 14:23 状态: 正常 IP: 192.168.1.100

对应的绘制函数:

void render_page_home(void) { lcd12864_clear(); lcd12864_goto_xy(0, 0); lcd12864_puts("系统主页"); lcd12864_goto_xy(0, 2); lcd12864_puts("时间: 14:23"); lcd12864_goto_xy(0, 4); lcd12864_puts("状态: 正常"); lcd12864_goto_xy(0, 6); lcd12864_puts("IP: 192.168.1.100"); }

其中lcd12864_goto_xy(x, y)是关键,它把“第y行、第x列”转换成正确的显存地址:

void lcd12864_goto_xy(uint8_t x, uint8_t y) { uint8_t addr = 0x80 + x; // 第x列 if (y < 4) { addr |= 0x00; // 左半屏 } else { addr |= 0x10; // 右半屏 y -= 4; } lcd12864_send_cmd(0x80 | (y << 6) | (addr & 0x0F)); }

注:ST7920将屏幕分为左右两个64列区域,Y坐标需根据左/右半屏调整。


实战中的坑与避坑指南

1. 屏幕闪烁得像频闪灯?

原因:每次切换都lcd12864_clear()全屏清空再重绘。

解决方案
- 改成局部擦除 + 局部刷新;
- 或者只在首次进入页面时清屏,后续增量更新。

例如:

if (current_page != previous_page) { lcd12864_clear(); previous_page = current_page; } // 然后只绘制变化的内容

2. 按键一按连跳两页?

机械按键弹跳导致多次触发。

除了加HAL_Delay(20),更好的方式是记录上次触发时间,判断是否小于20ms则忽略:

static uint32_t last_press_time = 0; uint32_t now = HAL_GetTick(); if ((now - last_press_time) < 20) return; last_press_time = now;

3. 中文变成“锟斤拷”?

编码不对!LCD12864只认GB2312编码的中文。

解决办法
- 在Keil或VS Code中将源文件保存为GB2312编码;
- 或使用数组形式定义字符串:

const char* title = "\xB7\xC7\xD1\xF9"; // “系统”二字的GB2312编码

工具网站可以帮你查汉字编码,搜索“GB2312编码查询”即可。


进阶技巧:如何让它更聪明?

掌握了基础之后,还可以加些实用功能:

✅ 自动轮播(无人操作时展示信息)

加个定时器,每10秒自动翻一页:

if (HAL_GetTick() - last_input_time > 10000) { current_page = (current_page + 1) % PAGE_COUNT; page_needs_update = 1; }

用户一操作就重置计时器。

✅ 节能背光控制

长时间无操作关闭背光:

lcd12864_send_cmd(0x08); // 关显示 // 按键唤醒后再开 lcd12864_send_cmd(0x0C); // 开显示

显存内容不会丢失,瞬间恢复。

✅ 加个返回键 or 旋钮编码器

两个按键:UP / DOWN,实现上下翻页:

if (up_pressed) current_page = (current_page - 1 + PAGE_COUNT) % PAGE_COUNT; if (down_pressed) current_page = (current_page + 1) % PAGE_COUNT;

甚至可以用旋转编码器替代按键,手感更好。


写在最后:经典组合为何依然值得学?

也许你会问:都2025年了,为啥还要折腾这种“古董级”液晶?

因为技术的价值不在新旧,而在适用

  • 学生练手?它便宜、资料全、调试简单;
  • 产品原型?它够稳、够省、够直观;
  • 工业现场?它耐高温、抗干扰、寿命长。

更重要的是,通过LCD12864 + STM32这个组合,你能真正理解:
- GPIO如何模拟时序;
- 显存如何映射画面;
- 中断如何提升交互体验;
- 状态机如何管理UI流程。

这些底层能力,才是嵌入式开发的立身之本。

下次当你面对一块新屏、一个新的HMI框架时,你会发现:原来它们不过是把今天我们亲手实现的东西,封装得更漂亮而已

所以,不妨拿起你的STM32最小系统板,接上那块吃灰已久的LCD12864,点亮第一个“Hello World”吧。

如果你在实现过程中遇到了其他挑战,欢迎在评论区分享讨论。

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

SMT工艺下防止贴片LED反向贴装的识别方案:实战案例

如何让SMT产线“一眼识破”贴片LED反向&#xff1f;实战防错方案全解析你有没有遇到过这样的情况&#xff1a;产品下线测试时&#xff0c;某个指示灯怎么都不亮。排查半天&#xff0c;最后发现——那颗小小的0603 LED贴反了。不是芯片坏了&#xff0c;也不是电路不通&#xff0…

作者头像 李华
网站建设 2026/3/12 17:39:56

BAAI/bge-m3降本部署案例:CPU版高性能推理,节省GPU成本60%

BAAI/bge-m3降本部署案例&#xff1a;CPU版高性能推理&#xff0c;节省GPU成本60% 1. 背景与挑战&#xff1a;语义相似度在AI应用中的核心地位 随着大模型和检索增强生成&#xff08;RAG&#xff09;架构的广泛应用&#xff0c;语义相似度计算已成为构建智能问答、知识检索和…

作者头像 李华
网站建设 2026/3/9 12:29:06

163MusicLyrics:智能歌词提取工具全方位解析

163MusicLyrics&#xff1a;智能歌词提取工具全方位解析 【免费下载链接】163MusicLyrics Windows 云音乐歌词获取【网易云、QQ音乐】 项目地址: https://gitcode.com/GitHub_Trending/16/163MusicLyrics 还在为寻找合适的音乐歌词而烦恼&#xff1f;163MusicLyrics作为…

作者头像 李华
网站建设 2026/3/2 22:59:11

开源AI编程助手OpenCode深度解析:从技术架构到实战部署完整指南

开源AI编程助手OpenCode深度解析&#xff1a;从技术架构到实战部署完整指南 【免费下载链接】opencode 一个专为终端打造的开源AI编程助手&#xff0c;模型灵活可选&#xff0c;可远程驱动。 项目地址: https://gitcode.com/GitHub_Trending/openc/opencode 在AI编程工具…

作者头像 李华
网站建设 2026/3/12 20:26:26

AtlasOS主题定制终极指南:打造你的专属视觉体验

AtlasOS主题定制终极指南&#xff1a;打造你的专属视觉体验 【免费下载链接】Atlas &#x1f680; An open and lightweight modification to Windows, designed to optimize performance, privacy and security. 项目地址: https://gitcode.com/GitHub_Trending/atlas1/Atla…

作者头像 李华