手把手教你玩转LCD1602:从基础显示到创意字符设计

张开发
2026/4/15 15:12:04 15 分钟阅读

分享文章

手把手教你玩转LCD1602:从基础显示到创意字符设计
1. LCD1602显示屏基础入门第一次拿到LCD1602这块小屏幕时你可能觉得它就是个简单的字符显示器。但别被它朴素的外表骗了这玩意儿玩起来可比想象中有趣得多。LCD1602的意思是能显示2行每行16个字符虽然现在看起来有点古董但在嵌入式开发中依然是个经典外设。这块屏幕最让我惊喜的是它内置了控制器我们只需要通过几根线就能控制它。通常用8根数据线D0-D7和3根控制线RS、RW、EN就能搞定。接线时有个小技巧VO引脚是用来调节对比度的接个电位器可以轻松调节显示清晰度我刚开始用的时候没注意这个显示效果总是不理想调了半天程序才发现是硬件问题。屏幕内部有两个重要存储区DDRAM和CGRAM。DDRAM相当于显示缓存我们往里面写什么屏幕就显示什么。有趣的是虽然屏幕只能显示32个字符2行×16列但DDRAM实际有80个位置2行×40列多出来的部分可以通过移屏命令来查看这个特性可以用来做简单的滚动字幕效果。2. 硬件连接与基础显示2.1 接线方案选择LCD1602支持4线和8线两种连接方式。新手我强烈建议用8线模式虽然多用了几根线但时序简单不容易出错。我当年第一次用4线模式调试了整整一天最后发现是时序没调好。接线时要注意开发板的IO口驱动能力有些单片机需要加上拉电阻。这里给出一个典型的51单片机连接方案D0-D7 → P0.0-P0.7RS → P2.0RW → P2.1EN → P2.22.2 基础显示函数实现显示字符的核心就是两个操作写命令和写数据。写命令用来设置显示模式、光标位置等写数据则是往DDRAM里写入要显示的字符。这两个操作的区别仅在于RS引脚的电平状态。下面这个延时函数很关键LCD1602对时序要求严格void LCD_Delay() { unsigned char i, j; i 11; j 190; do { while (--j); } while (--i); }写命令函数示例void LCD_WriteCommand(unsigned char Command) { LCD_RS0; // 命令模式 LCD_RW0; // 写操作 LCD_DataPortCommand; LCD_EN1; // 使能脉冲 LCD_Delay(); LCD_EN0; LCD_Delay(); }初始化时通常需要发送这几个命令void LCD_Init() { LCD_WriteCommand(0x38); // 8位数据接口2行显示5×8点阵 LCD_WriteCommand(0x0C); // 开显示关光标 LCD_WriteCommand(0x06); // 写入后地址指针自动加1 LCD_WriteCommand(0x01); // 清屏 }3. 高级显示技巧3.1 光标精确定位要在指定位置显示字符需要先设置光标位置。LCD1602的DDRAM地址第一行是0x00-0x27第二行是0x40-0x67。但屏幕只显示每行前16个字符。光标设置函数void LCD_SetCursor(unsigned char Line, unsigned char Column) { if(Line1) { LCD_WriteCommand(0x80|(Column-1)); } else if(Line2) { LCD_WriteCommand(0x80|(Column-10x40)); } }3.2 显示字符串基于前面的基础函数我们可以封装更高级的显示功能void LCD_ShowString(unsigned char Line, unsigned char Column, char *String) { unsigned char i; LCD_SetCursor(Line, Column); for(i0; String[i]!\0; i) { LCD_WriteData(String[i]); } }3.3 数字显示技巧显示数字时需要特别注意类型转换void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) { unsigned char i; LCD_SetCursor(Line, Column); for(iLength; i0; i--) { LCD_WriteData(Number/LCD_Pow(10,i-1)%100); } }4. 自定义字符设计4.1 CGRAM原理详解这才是LCD1602最好玩的部分屏幕内置的CGRAM允许我们创建8个5×8点阵的自定义字符。每个字符占用8字节空间每字节对应一行像素点只有低5位有效。CGRAM地址分配字符00x00-0x07字符10x08-0x0F...字符70x38-0x3F4.2 字符设计实战设计自定义字符需要用到取模软件。我推荐LCD Character Creator操作简单直观。设计时要注意每个字符实际是5×8点阵每个点用1位表示点亮为1熄灭为0每行对应一个字节只使用低5位取模示例// 笑脸字符 unsigned char smiley[] { 0x00,0x0A,0x0A,0x00,0x11,0x0E,0x00,0x00 };4.3 自定义字符写入与显示写入CGRAM的函数void LCD_SetCustomChar(unsigned char Position, char *Table) { unsigned char i; LCD_WriteCommand(0x40 (Position-1)*8); for(i0; i8; i) { LCD_WriteData(Table[i]); } }显示自定义字符void LCD_ShowCustomChar(unsigned char Line, unsigned char Column, unsigned char Num) { LCD_SetCursor(Line, Column); LCD_WriteData(Num-1); // 自定义字符的编码是0-7 }5. 创意应用实例5.1 简单动画效果利用CGRAM和延时可以实现简单动画。比如设计一个进度条动画unsigned char progress1[] {0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10}; unsigned char progress2[] {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}; // ... 设计更多帧 void show_progress() { LCD_SetCustomChar(1, progress1); LCD_ShowCustomChar(1, 1, 1); delay_ms(200); LCD_SetCustomChar(1, progress2); LCD_ShowCustomChar(1, 1, 1); // ... 更多帧 }5.2 自定义图标设计我们可以把多个自定义字符组合成更大的图标。比如设计一个温度计图标unsigned char temp_top[] {0x04,0x0E,0x0E,0x0E,0x1F,0x1F,0x0E,0x00}; unsigned char temp_mid[] {0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00}; unsigned char temp_bot[] {0x04,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00}; void show_temperature() { LCD_SetCustomChar(1, temp_top); LCD_SetCustomChar(2, temp_mid); LCD_SetCustomChar(3, temp_bot); LCD_ShowCustomChar(1, 1, 1); LCD_ShowCustomChar(1, 2, 2); LCD_ShowCustomChar(1, 3, 3); }5.3 移屏效果实现利用LCD1602的移屏命令可以实现滚动效果void scroll_text(char *text) { LCD_WriteCommand(0x01); // 清屏 LCD_ShowString(1, 1, text); for(int i0; i16; i) { LCD_WriteCommand(0x18); // 左移命令 delay_ms(300); } }6. 常见问题排查调试LCD1602时我踩过不少坑这里分享几个典型问题显示乱码检查初始化顺序是否正确确认总线模式4线/8线设置匹配检查延时函数是否足够显示对比度不佳调节VO引脚电压通常接10k电位器检查背光供电是否稳定自定义字符显示异常确认CGRAM地址计算正确检查取模数据是否符合5×8格式确保没有超出8个自定义字符限制光标位置错乱确认DDRAM地址计算正确检查行偏移量第二行0x40写入无反应检查使能信号(EN)的时序确认RW引脚电平写操作应为低电平测量各引脚电压是否正常记得我第一次用LCD1602时因为没加延时导致显示完全乱码调试了整整一天。后来发现是EN信号持续时间不够加上合适的延时就解决了。所以遇到问题时耐心检查时序和硬件连接往往能事半功倍。

更多文章