news 2026/3/2 11:28:51

LCD1602液晶显示屏程序自定义字符实现详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LCD1602液晶显示屏程序自定义字符实现详解

玩转LCD1602:如何用代码“画”出自定义图标?

你有没有在电子秤上看到过一个小小的“砝码”符号?或者在温控器的屏幕上见过闪烁的温度计图标?这些可不是标准键盘能打出来的字符——它们是程序员一笔一划“画”出来的图形,而实现这一切的核心,就是那块看起来普普通通的LCD1602液晶屏

别看它只能显示两行、每行16个字符,一旦你掌握了它的“隐藏技能”,这块黑白小屏就能变成你的迷你图形画布。今天我们就来揭开这个秘密:如何通过编程,在LCD1602上自定义显示箭头、电池、心形甚至动画帧?


为什么还在用LCD1602?它不早就过时了吗?

坦率说,现在满大街都是彩色TFT触摸屏,动辄几百上千分辨率,LCD1602确实显得有点“复古”。但它依然活跃在无数设备中——从工厂里的PLC控制面板,到家里的智能电表、空气净化器,甚至一些高端示波器的状态栏。

原因很简单:省资源、够稳定、成本低

  • 它不需要操作系统;
  • 不消耗MCU太多算力;
  • 静态显示几乎不耗电;
  • 接线简单,4根数据线+3根控制线就能跑起来;
  • 最关键的是:便宜!几块钱一片。

尤其是在STM8、51单片机这类资源紧张的小系统里,给用户一点直观反馈,LCD1602仍是首选。

但问题来了:ASCII字符集就那么几十个字母数字和标点,怎么表达“当前正在加热”、“信号弱”这种状态呢?

答案就是:自定义字符(Custom Character)


自定义字符是怎么“画”出来的?点阵背后的逻辑

LCD1602每个字符的位置其实是一个5×8 像素的小网格。你可以把它想象成一张 tiny 的像素画板,每一列宽5个点,高8行。

比如你想画一个向上的箭头 ↑,大概是这样:

● ●●● ● ● ● ● ● ● ●

我们把“亮”的点记为1,“灭”的点记为0,每一行转换成一个字节的二进制数,只取低5位(因为宽度只有5列),高位补0。于是上面这个图案就变成了8个字节的数据:

const uint8_t up_arrow[8] = { 0b00100, // 第1行:中间亮 0b01110, // 第2行:中间三个亮 0b10101, // 第3行:两边和中间亮 0b00100, 0b00100, 0b00100, 0b00100, 0b00000 // 最后一行留空,避免粘连 };

这8个字节,就是一个完整的自定义字符的“图纸”。


图纸画好了,往哪儿存?CG RAM揭秘

LCD1602内部有个叫CG RAM(Character Generator RAM)的地方,专门用来存放这些“图纸”。它总共有64字节,分成8组,每组8字节,刚好容纳8个自定义字符。

也就是说,你最多可以同时定义8个不同的图标。

这些字符编号从0到7,不是ASCII码,而是内部索引。当你后续往屏幕写入数据时,只要写入0,就会显示第一个自定义图形;写入1,就显示第二个……

⚠️ 注意:CG RAM 是易失性内存,断电即清空。所以每次上电后,必须重新把这8组数据写一遍。

那怎么告诉LCD:“我现在要往CG RAM里写东西了”?

这就涉及到LCD控制器的一个关键机制:地址指针切换


控制核心:HD44780是如何管理内存的?

几乎所有字符型LCD都基于HD44780 或其兼容芯片(如ST7066U)。它内部有两块主要的RAM区域:

名称作用
DD RAMDisplay Data RAM —— 存放屏幕上实际显示的字符编码
CG RAMCharacter Generator RAM —— 存放自定义字符的点阵数据

还有一个地址计数器(AC),决定了当前操作的是哪块内存。

要写入自定义字符,流程非常清晰:

  1. 发送命令0x40 | (n << 3),设置CG RAM地址指针(n为字符编号)
  2. 连续写入8字节点阵数据
  3. 切回DD RAM模式
  4. 向DD RAM写入字符编号 n,即可显示对应图形

举个例子:想把上面的箭头存为第0号字符,起始地址就是0x40 + 0*8 = 0x40。发送命令0x40后,接下来的8次数据写入都会进入CG RAM。


实战代码:封装一个可复用的创建函数

下面这段C语言代码适用于STM32、AVR、51等常见MCU平台,假设你已经有了基本的IO驱动函数(如LCD_WriteCommand,LCD_WriteData)。

/** * @brief 创建一个自定义字符 * @param location: 字符编号 (0~7) * @param char_map: 指向8字节数组的指针 */ void LCD_CreateChar(uint8_t location, const uint8_t* char_map) { location &= 0x07; // 限制范围,防止越界 // 设置CG RAM地址:0x40 + location * 8 LCD_WriteCommand(0x40 | (location << 3)); for (int i = 0; i < 8; i++) { LCD_WriteData(char_map[i]); } }

是不是很简洁?只需要传入编号和数组,一行命令加一个循环搞定。

再来看怎么使用它:

// 定义几个实用图标 const uint8_t icon_temp[8] = { /* 温度计 */ }; const uint8_t icon_battery_full[8] = { /* 满电量 */ }; const uint8_t icon_arrow_up[8] = { /* 上箭头 */ }; void LCD_InitWithIcons(void) { LCD_Init(); // 先初始化LCD // 一次性加载所有需要的图标 LCD_CreateChar(0, icon_temp); LCD_CreateChar(1, icon_battery_full); LCD_CreateChar(2, icon_arrow_up); // 清屏 LCD_WriteCommand(0x01); Delay_ms(2); } // 显示示例:"Temp: 25°C ↑" void ShowTemperature(float temp) { LCD_GotoXY(0, 0); LCD_Print("Temp: "); LCD_PrintFloat(temp, 1); // 显示带一位小数 LCD_WriteData(0xDF); // 输出 ° 符号(部分型号支持) LCD_WriteData('C'); LCD_WriteData(2); // 显示↑图标(编号2) }

注意最后这句LCD_WriteData(2)—— 我们并没有发送字符’B’或’X’,而是直接发了个数字2。LCD会自动查找CG RAM中编号为2的图形并显示出来。


小技巧:还能玩出什么花样?

✅ 动态效果模拟

虽然LCD1602不能真正“刷新”局部画面,但你可以利用多个自定义字符实现简单的动画。

例如,定义三个风扇叶片旋转的不同姿态,编号分别为1、2、3:

const uint8_t fan_frame1[8] = { ..., 0b01110, ... }; // 叶片朝右 const uint8_t fan_frame2[8] = { ..., 0b00100, ... }; // 垂直 const uint8_t fan_frame3[8] = { ..., 0b00010, ... }; // 斜向

然后在主循环中轮询切换:

while (1) { LCD_GotoXY(10, 1); LCD_WriteData((frame++ % 3) + 1); // 循环显示三帧 Delay_ms(300); }

视觉上就有了一种“转动”的感觉。

✅ 电量条/进度条

可以用不同填充程度的方块表示电量:

编号图形含义
00%
125%
250%
375%
4100%

然后根据ADC读数选择对应的字符输出,比打印“Battery: 75%”更直观。


踩坑提醒:新手最容易忽略的几个细节

  1. 写完CG RAM记得切回DD RAM
    - 如果忘记切换,后续所有写入都会继续修改CG RAM,导致显示异常。
    - 建议在LCD_CreateChar末尾加一句LCD_WriteCommand(0x80);回到DD RAM首地址。

  2. 对比度调节Vo千万别接错
    - Vo引脚控制液晶偏压,直接影响是否能看清内容。
    - 正确做法是接一个10kΩ电位器,两端分别接Vcc和GND,滑动端接Vo。
    - 接错了可能全黑或全白。

  3. 避免频繁重载CG RAM
    - 每次写入都有延迟,建议只在初始化阶段集中写入一次。
    - 若需动态更换图标,提前规划好编号分配。

  4. 背光节能设计
    - LED+可通过NPN三极管由MCU控制,长时间无操作自动关闭背光。
    - 对电池供电设备尤为重要。

  5. 不同厂家略有差异
    - 有些LCD对时序要求更严格,延时不够会导致初始化失败。
    - 建议关键步骤后加入适当Delay(如2ms),或查询忙标志位。


为什么这项“老技术”还值得学?

也许你会问:现在都2025年了,谁还用手敲LCD1602驱动?

但正是这种“原始”的外设,教会我们最本质的东西:

  • 内存映射思想:DD RAM vs CG RAM 的分离设计,是现代显存架构的缩影;
  • 位操作实践:每一位代表一个像素,强化了对二进制的理解;
  • 硬件时序掌控:E脉冲、RS选择、建立保持时间……这些都是嵌入式开发的基本功;
  • 资源优化思维:在仅有几百字节RAM的系统中,如何高效利用每一个bit。

掌握LCD1602的自定义字符,不只是为了做一个温度图标。它是通往更复杂GUI系统的第一级台阶。当你以后面对TFT屏幕+TouchGFX框架时,你会发现很多概念一脉相承:显存管理、字体渲染、图层合成……

而这一切,都可以从这8个字节开始。


如果你也在做某个小项目,正愁怎么优雅地提示“运行中”状态,不妨试试用自定义字符画个小齿轮或者心跳动画。你会发现,哪怕是最简单的硬件,也能讲出动人的交互故事。

你在项目中用过哪些有趣的自定义字符?欢迎在评论区晒出你的“像素艺术”作品!

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

51单片机蜂鸣器唱歌在儿童电子玩具中的应用

让玩具“开口唱歌”&#xff1a;51单片机驱动蜂鸣器的实战艺术你有没有注意过&#xff0c;孩子手里那个会发光、会叫的小鸭子&#xff0c;按下按钮就“叮咚叮咚”地唱起《小星星》&#xff1f;它没有喇叭&#xff0c;也没有MP3芯片&#xff0c;却能奏出旋律。这背后藏着一个看似…

作者头像 李华
网站建设 2026/3/2 2:03:58

LCD显示屏并行接口设计:完整指南(8位数据总线)

LCD显示屏并行接口设计&#xff1a;从原理到实战的完整实践你有没有遇到过这样的场景&#xff1f;一个基于SPI驱动的TFT屏&#xff0c;在显示复杂图形或动画时卡得像幻灯片&#xff1b;MCU的CPU占用率飙到80%以上&#xff0c;连基本的任务调度都变得迟缓。这时候&#xff0c;很…

作者头像 李华
网站建设 2026/3/2 1:18:05

免费解锁Spotify高级版:BlockTheSpot完整使用指南

免费解锁Spotify高级版&#xff1a;BlockTheSpot完整使用指南 【免费下载链接】BlockTheSpot Video, audio & banner adblock/skip for Spotify 项目地址: https://gitcode.com/gh_mirrors/bl/BlockTheSpot &#x1f3b5; 你是否厌倦了音乐播放时频繁出现的广告打断…

作者头像 李华
网站建设 2026/2/28 17:26:48

Path of Building PoE2完全指南:终极流放之路2角色构建工具

Path of Building PoE2完全指南&#xff1a;终极流放之路2角色构建工具 【免费下载链接】PathOfBuilding-PoE2 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding-PoE2 Path of Building PoE2&#xff08;简称PoB2&#xff09;是专为《流放之路2》设计…

作者头像 李华
网站建设 2026/2/28 7:51:52

Dify平台透明度报告首次发布

Dify平台透明度报告首次发布&#xff1a;构建可信赖AI的实践路径 在大模型技术加速渗透各行各业的今天&#xff0c;企业不再只是“是否要用AI”的选择题&#xff0c;而是面临“如何安全、高效地用好AI”的现实挑战。尽管LLM&#xff08;大语言模型&#xff09;展现出强大的生成…

作者头像 李华
网站建设 2026/3/1 16:38:50

百度网盘秒传工具:从零基础到高效使用的完整指南

百度网盘秒传工具&#xff1a;从零基础到高效使用的完整指南 【免费下载链接】baidupan-rapidupload 百度网盘秒传链接转存/生成/转换 网页工具 (全平台可用) 项目地址: https://gitcode.com/gh_mirrors/bai/baidupan-rapidupload 还在为百度网盘下载速度慢而烦恼吗&…

作者头像 李华