51单片机基础-LCD1602液晶显示

第二十三章 LCD1602液晶显示

1. 导入

LCD1602(16×2 字符型液晶)使用基于HD44780控制器的命令集,支持8位或4位数据总线,常用于菜单、状态与调试信息显示。本章以"4位总线,RW接地(只写)"方案实现稳定驱动,提供完整API:初始化、定位、打印、清屏、自定义字符(CGRAM)。

目标:

  • 掌握LCD1602引脚与对比度、电源接法
  • 理解4位时序与初始化流程
  • 实现常用API:定位、打印、清屏、滚动/移位
  • 扩展:显示时钟/温度、自定义字符"°"

2. 硬件设计

  • 供电与对比度
    • VSS→GND,VDD→+5V
    • V0(对比度)→10k电位器中点(两端接VCC/GND)
  • 背光(如有):A→+5V,K→GND(可串电阻限流)
  • 数据/控制引脚(4位方式,推荐)
    • RW→GND(只写,简单可靠)
    • RS:0=命令,1=数据
    • E(EN):上升沿锁存
    • D4~D7:数据高四位,4位模式仅用这四根

示例接线(可按需改口):

  • P2.2 → RSP2.3 → E
  • P2.4 → D4P2.5 → D5P2.6 → D6P2.7 → D7
  • RW → GND

注意:若用P0口需外接上拉;P2口免上拉更稳。


3. 指令与寻址要点

  • 主要命令(十六进制):
    • 0x01 清屏(>1.52ms)
    • 0x02 归位(>1.52ms)
    • 0x04/0x06 输入模式(增量/不移屏)
    • 0x08~0x0F 显示/光标/闪烁开关(如0x0C 显示开、无光标)
    • 0x10/0x14/0x18/0x1C 光标/整屏左/右移
    • 0x20/0x28 数据长度与行数(0x28=4位、两行、5×8点阵)
    • 0x80 | addr 设置DDRAM地址(行列定位)
  • DDRAM行首地址(1602):
    • 第1行:0x00
    • 第2行:0x40

4. 完整驱动(4位、RW接地)

c 复制代码
#include <reg52.h>
#include <intrins.h>

/* 引脚映射:按你的接线修改 */
sbit LCD_RS = P2^2;
sbit LCD_EN = P2^3;
sbit LCD_D4 = P2^4;
sbit LCD_D5 = P2^5;
sbit LCD_D6 = P2^6;
sbit LCD_D7 = P2^7;

/* 延时(粗略,不依赖忙标志) */
static void delay_us_inline() { _nop_(); _nop_(); _nop_(); _nop_(); }
void delay_ms(unsigned int ms){
    unsigned int i,j;
    for(i=0;i<ms;i++) for(j=0;j<125;j++);
}

/* 低级IO */
static void lcd_write4(unsigned char nib){   // 发送高4位或低4位
    LCD_D4 = (nib & 0x01) ? 1 : 0;
    LCD_D5 = (nib & 0x02) ? 1 : 0;
    LCD_D6 = (nib & 0x04) ? 1 : 0;
    LCD_D7 = (nib & 0x08) ? 1 : 0;
    delay_us_inline();
    LCD_EN = 1; delay_us_inline();
    LCD_EN = 0; delay_us_inline();
}

/* 发送一个字节(4位总线):rs=0命令,rs=1数据 */
static void lcd_send(unsigned char value, bit rs){
    LCD_RS = rs;
    // 先发高四位
    lcd_write4((value >> 4) & 0x0F);
    // 再发低四位
    lcd_write4(value & 0x0F);

    // 大多数命令37us足够,清屏/归位需更长
    if (!rs && (value==0x01 || value==0x02)) {
        delay_ms(2);
    } else {
        // 约40us
        _nop_(); _nop_(); _nop_(); _nop_();
    }
}
static void lcd_cmd(unsigned char cmd){ lcd_send(cmd, 0); }
static void lcd_data(unsigned char dat){ lcd_send(dat, 1); }

/* 初始化(4位,2行,5x8,显示开,光标关,自增) */
void lcd_init(void){
    LCD_RS = 0; LCD_EN = 0;

    delay_ms(40);          // 上电等待>30ms

    // 强制进入4位模式的固定序列(参考HD44780)
    lcd_write4(0x03); delay_ms(5);
    lcd_write4(0x03); delay_ms(5);
    lcd_write4(0x03); delay_ms(1);
    lcd_write4(0x02);      // 现在进入4位模式

    lcd_cmd(0x28);         // 功能设置:4位、2行、5x8点阵
    lcd_cmd(0x08);         // 显示关闭
    lcd_cmd(0x01);         // 清屏
    delay_ms(2);
    lcd_cmd(0x06);         // 输入模式:写入后地址+1,屏不移
    lcd_cmd(0x0C);         // 显示开,光标关,闪烁关
}

/* 常用API */
void lcd_clear(void){ lcd_cmd(0x01); delay_ms(2); }
void lcd_home(void){ lcd_cmd(0x02); delay_ms(2); }

/* 设置光标:行row=0/1,列col=0..15 */
void lcd_set_cursor(unsigned char row, unsigned char col){
    unsigned char addr = (row ? 0x40 : 0x00) + (col & 0x0F);
    lcd_cmd(0x80 | addr);
}

/* 打印字符串(以 '\0' 结束) */
void lcd_print(const char* s){
    while(*s) lcd_data((unsigned char)*s++);
}

/* 指定位置打印 */
void lcd_print_at(unsigned char row, unsigned char col, const char* s){
    lcd_set_cursor(row, col);
    lcd_print(s);
}

/* 自定义字符(CGRAM)。loc:0~7,对应字符码0..7;pattern[8]每行低5位有效 */
void lcd_define_char(unsigned char loc, const unsigned char pattern[8]){
    unsigned char i;
    loc &= 0x07;
    lcd_cmd(0x40 | (loc << 3));   // 设置CGRAM地址
    for(i=0;i<8;i++) lcd_data(pattern[i] & 0x1F);
    lcd_cmd(0x80);                // 返回DDRAM(可选)
}

/* 示例:主程序 */
void main(void){
    unsigned int cnt = 0;
    const unsigned char deg_sym[8] = {
        0x04,0x0A,0x04,0x00,0x00,0x00,0x00,0x00  // 简易"°"
    };

    lcd_init();
    lcd_define_char(0, deg_sym); // 自定义字符0号为"°"

    lcd_print_at(0, 0, "Hello, LCD1602!");
    lcd_print_at(1, 0, "Cnt: ");

    while(1){
        char buf[6];
        unsigned int v = cnt;

        // 将数字转字符串
        buf[0] = (v/10000)%10 + '0';
        buf[1] = (v/1000)%10 + '0';
        buf[2] = (v/100)%10 + '0';
        buf[3] = (v/10)%10 + '0';
        buf[4] = (v%10) + '0';
        buf[5] = '\0';

        lcd_print_at(1, 5, buf);      // 在第2行第6列更新计数
        // 示范自定义符号(在末尾显示"°C")
        lcd_set_cursor(1, 11);
        lcd_data(0);                   // 打印自定义"°"
        lcd_data('C');

        cnt++;
        delay_ms(500);
    }
}

要点:

  • 初始化中四次lcd_write4(...)的固定序列是切入4位模式的关键。
  • RW接地避开忙标志读取,使用"足够延时"保证可靠。
  • 清屏/归位需要>1.52ms,其余指令约37μs,代码已分别处理。

5. 常用拓展

  • 光标与移屏
    • lcd_cmd(0x0E):显示开、有光标
    • lcd_cmd(0x0F):显示开、光标闪烁
    • lcd_cmd(0x18):整屏左移;0x1C:右移
  • 快速覆盖行尾空白
    • 打印后用空格填满剩余列,避免残留字符
  • 数值/时间显示(与前章结合)
    • DS1302时间:lcd_print_at(0,0,"HH:MM:SS");
    • DS18B20温度:使用lcd_define_char自定义"°",显示"23.5°C"

6. 故障排查

  • 无显示/黑方块一行:对比度V0未调;初始化时序错误;E脚未正确翻转。
  • 乱码:4位高低半字节顺序错;数据线接错;延时不足。
  • 显示位置错乱:行首地址理解错误;lcd_set_cursor计算错误。
  • 闪烁严重:频繁清屏;改用覆盖更新局部字符。

7. 进阶(可选)

  • 8位总线:将D0~D7全部接入,初始化用0x38(8位、2行、5×8),写入一次8位即可(速度快,线多)。
  • 读忙标志:RW接MCU,切换D口方向,读BF(D7=1忙);但硬件与代码更复杂,通常延时法已足够。
  • I²C转接模块:很多1602带PCF8574 I/O扩展,I²C仅占两线(与第17章I²C兼容),命令需映射到PCF8574位序

相关推荐
南方的狮子先生5 小时前
【深度学习】60 分钟 PyTorch 极速入门:从 Tensor 到 CIFAR-10 分类
人工智能·pytorch·python·深度学习·算法·分类·1024程序员节
瑞禧生物ruixibio6 小时前
iFluor 594 Styramide,水溶性荧光探针
1024程序员节
金融小师妹6 小时前
OpenAI拟借AI估值重构浪潮冲击1.1万亿美元IPO——基于市场情绪因子与估值量化模型的深度分析
大数据·人工智能·深度学习·1024程序员节
jamesge20106 小时前
zookeeper学习笔记
笔记·学习·zookeeper·1024程序员节
Yupureki7 小时前
从零开始的C++学习生活 19:C++复习课(5.4w字全解析)
c语言·数据结构·c++·学习·1024程序员节
兆龙电子单片机设计8 小时前
【STM32项目开源】STM32单片机医疗点滴控制系统
stm32·单片机·开源·毕业设计·电子信息
dadaobusi9 小时前
看到一段SVA代码,让AI解释了一下
单片机·嵌入式硬件·fpga开发
门外的兔子10 小时前
【FPGA】Quartus Prime Lite 23.1 最新版 安装教程 ModelSim_18.1 下载安装 + 联调仿真教程 + 详细安装教程 2025最新
嵌入式硬件·fpga开发·1024程序员节
Hero_112710 小时前
Stm32 之 按键篇
stm32·单片机·嵌入式硬件