51单片机学习笔记6-LCD1602液晶显示屏

目录

6.1 基本概述

6.2 引脚定义

6.3 工作原理

6.4 51 单片机与 LCD1602 的连接方式

8 位并行连接(D0-D7 全用)

4 位并行连接(仅用 D4-D7)

6.5 核心指令集(必背)

6.6 51 单片机驱动 LCD1602 的基本流程

8 位数据接口模式

                ⭐ 1. 初始化流程(关键!必须按顺序执行,8 位模式专属,顺序不可乱)

                ⭐ 2. 写指令函数流程(8 位模式核心,无拆分!)

                ⭐ 3. 写数据函数流程(8 位模式核心,无拆分!)

                ⭐ 4. 显示字符 / 字符串(8 位 / 4 位模式通用,无差异)

4 位数据接口模式

                ⭐ 1、LCD1602 初始化流程(【核心关键】,顺序绝对不可调换、缺一不可)

                ⭐ 2、LCD1602 写指令函数流程(通用,所有指令发送均遵循此流程)

                ⭐ 3、LCD1602 写数据函数流程(通用,所有显示字符发送均遵循此流程)

                ⭐ 4、LCD1602 显示字符 / 字符串 流程

<6.7> 51 单片机 LCD1602 (8 位模式) 完整驱动代码

✅LCD1602.c

✅LCD1602.h

6.8 项目示例

✅项目架构:

✅main.c主函数:


6.1 基本概述

  1. 器件类型LCD1602(Liquid Crystal Display)液晶显示屏是一种字符型液晶显示模块(只能显示 ASCII 字符,如数字、字母、符号,无法显示图形
  2. 显示容量16×2个字符,可同时显示 32 个字符,每个字符为5*7点阵。

6.2 引脚定义

6.3 工作原理

LCD1602 的核心是 指令寄存器数据寄存器,通过 RS、RW、E 引脚的电平组合控制操作类型:
  • 两种工作模式

  • 忙标志位(BF)
    • 位于 LCD 内部状态寄存器的最高位(D7)
    • BF=1:LCD 正忙,无法接收新指令 / 数据
    • BF=0:LCD 空闲,可以执行新操作
    • 51 驱动时,可通过延时替代读 BF(简化编程)
  • 使能信号(E)
    • 必须给一个高→低的跳变,LCD 才会执行输入的指令 / 数据
    • 典型操作:E=1 → 写入指令 / 数据 → E=0

6.4 51 单片机与 LCD1602 的连接方式

51 单片机与 LCD1602 有 8 位并行4 位并行 两种连接方式,4 位模式更常用(节省 IO 口)。

8 位并行连接(D0-D7 全用)

  • 连接:D0-D7 接 51 单片机的 P0~P7(如 P0 口);RS、RW、E 接任意通用 IO 口(如 P2.0、P2.1、P2.2)。
  • 优点:数据传输快,指令 / 数据一次发送完成。
  • 缺点:占用 8 个数据 IO 口,资源消耗大。

4 位并行连接(仅用 D4-D7)

  • 连接:D4-D7 接 51 单片机的 4 个 IO 口(如 P0.4~P0.7);RS、RW、E 接通用 IO 口。
  • 核心原理:将 8 位指令 / 数据分两次发送,高 4 位(D4-D7)先发送,低 4 位(D0-D3)后发送,每次发送都需触发 E 引脚。
  • 优点:仅占用 4 个数据 IO 口,节省资源,51 单片机项目首选。
  • 缺点:数据传输速度略慢于 8 位模式(对字符显示无明显影响)。

6.5 核心指令集(必背)

LCD1602 的所有操作都需通过 指令配置,常用指令如下(8 位模式,4 位模式需拆分高 4 位和低 4 位):

地址码与屏幕位置对应关系
LCD1602 的每一行有固定的地址码,用于定位光标:
  • 第一行字符地址:0x00 ~ 0x0F(对应第 1~16 列)
  • 第二行字符地址:0x40 ~ 0x4F(对应第 1~16 列)
  • 例:设置光标到第二行第 3 列 → 指令码 = 0x80 + 0x42 = 0xC2

6.6 51 单片机驱动 LCD1602 的基本流程

8 位数据接口模式

1. 初始化流程(关键!必须按顺序执行,8 位模式专属,顺序不可乱)

  1. 延时 15ms(LCD 上电稳定时间,内部复位必备,缺一不可)。
  2. 发送功能设置指令(0x38) → 8 位数据接口,16×2 显示,5×8 点阵(一次性发送完整 8 位,无需拆分)。
  3. 延时 5ms(指令执行稳定时间,保证 LCD 正确识别指令)。
  4. 重复发送功能设置指令(0x38)→ 二次确认,防止上电指令识别失败,提高兼容性。
  5. 发送显示开关控制指令(0x0C) → 开显示,关闭光标(最常用,0x0E 开显示开光标,0x0F 开显示 + 光标 + 闪烁)。
  6. 发送输入模式设置指令(0x06) → 写数据时光标自动右移,显示内容不整体移位,书写顺序正常。
  7. 发送清屏指令(0x01),延时 2ms → 清除屏幕所有内容,光标归位到第一行第一列。

2. 写指令函数流程(8 位模式核心,无拆分!)

  1. RS = 0 (选择指令寄存器,告知 LCD:接下来发送的是指令不是显示数据)。
  2. RW = 0 (选择写操作,51 驱动 LCD1602 只用到写,读用不到)。
  3. 完整 8 位指令码直接送入 LCD 的 D0~D7 数据引脚。
  4. E 引脚置高 → 置低(利用 E 引脚的下降沿触发LCD 执行指令,这是 LCD 的硬件规则)。
  5. 延时一段时间(1~2ms,确保指令执行完成,LCD 内部处理指令需要时间)。

3. 写数据函数流程(8 位模式核心,无拆分!)

  1. RS = 1 (选择数据寄存器,告知 LCD:接下来发送的是要显示的字符数据)。
  2. RW = 0 (保持写操作模式不变)。
  3. 完整 8 位 ASCII 码数据直接送入 LCD 的 D0~D7 数据引脚(如显示 '1' 送 0x31、显示 'A' 送 0x41)。
  4. E 引脚置高 → 置低(下降沿触发,让 LCD 接收显示数据)。
  5. 延时一段时间(1~2ms,确保数据稳定写入,无乱码)。

4. 显示字符 / 字符串(8 位 / 4 位模式通用,无差异)

  1. 发送光标位置指令(确定显示起始位置)→ 指令格式 0x80 + 地址码
  2. 调用写数据函数,逐个发送字符的 ASCII 码,完成显示。
地址码规则(必记):第一行 1~16 列: 0x00 ~ 0x0F第二行 1~16 列: 0x40 ~ 0x4F例:显示到第二行第 5 列 → 指令码 = 0x80 + 0x44 = 0xC4

4 位数据接口模式

1、LCD1602 初始化流程(【核心关键】,顺序绝对不可调换、缺一不可)

  1. 上电后先延时 15ms,等待 LCD1602 内部电路上电稳定;
  2. 发送功能设置指令 0x28,该指令为 4 位模式专属,发送时需分 2 次:先送高 4 位,再送低 4 位;
  3. 发送指令后延时 5ms,保证 LCD 稳定识别该指令;
  4. 重复发送一次功能设置指令0x28,确保 LCD 准确识别 4 位工作模式;
  5. 发送显示开关控制指令 0x0C,实现「开显示、关闭光标」;
  6. 发送输入模式设置指令 0x06,实现「写数据时光标自动右移,显示内容不移位」;
  7. 发送清屏指令 0x01,完成后延时 2ms,清除屏幕残留数据,光标归位至第一行第一列。

2、LCD1602 写指令函数流程(通用,所有指令发送均遵循此流程)

  1. 置引脚 RS = 0 ,选择 LCD 的指令寄存器,告知 LCD 接下来接收的是配置指令;
  2. 置引脚 RW = 0 ,选择写操作模式(51 驱动 LCD 仅用到写,读操作几乎不用);
  3. 将需要发送的指令高 4 位,送入 LCD 的 D4~D7 引脚,随后将使能引脚 E「置高→置低」,利用下降沿触发 LCD 接收数据;
  4. 短暂延时一段时间,保证高 4 位数据稳定写入;
  5. 将需要发送的指令低 4 位,送入 LCD 的 D4~D7 引脚,再次将使能引脚 E「置高→置低」,触发接收;
  6. 延时一小段时间,确保指令完全执行完毕,再进行后续操作。
✔ 核心:4 位模式仅用 D4-D7,所有指令必须拆分高低 4 位分两次发送

3、LCD1602 写数据函数流程(通用,所有显示字符发送均遵循此流程)

  1. 置引脚 RS = 1 ,选择 LCD 的数据寄存器,告知 LCD 接下来接收的是显示的字符数据;
  2. 置引脚 RW = 0 ,保持写操作模式不变;
  3. 将需要显示的数据高 4 位,送入 LCD 的 D4~D7 引脚,使能引脚 E「置高→置低」,下降沿触发接收;
  4. 短暂延时一段时间,保证高 4 位数据稳定写入;
  5. 将需要显示的数据低 4 位,送入 LCD 的 D4~D7 引脚,使能引脚 E 再次「置高→置低」,触发接收;
  6. 延时一小段时间,确保字符数据完全写入 LCD 内部缓存。
✔ 核心:写数据和写指令的唯一区别,仅为 RS 引脚电平不同(数据 = 1,指令 = 0)

4、LCD1602 显示字符 / 字符串 流程

  1. 第一步:发送光标位置设置指令,指令格式为 0x80 + 地址码,确定字符的显示起始位置;
  • 第一行 1~16 列 地址码:0x00 ~ 0x0F
  • 第二行 1~16 列 地址码:0x40 ~ 0x4F
  1. 第二步:调用「写数据函数」,逐个发送字符对应的 ASCII 码,完成显示;
  2. 举例:显示数字1则发送0x31、显示字母A则发送0x41、显示符号-则发送0x2D
  3. 若显示字符串,循环执行第二步,直到发送完所有字符的 ASCII 码。

<6.7> 51 单片机 LCD1602 (8 位模式) 完整驱动代码

✅LCD1602.c

#include <REGX52.H>  //51单片机寄存器定义头文件,兼容REG52.H,包含所有IO口/寄存器地址定义

//===============LCD1602硬件引脚定义===============
sbit LCD_RS=P2^6;    //LCD寄存器引脚:RS=0写指令,RS=1写数据
sbit LCD_RW=P2^5;    //LCD读写引脚:RW=0写操作,RW=1读操作(本代码只用写,默认0)
sbit LCD_E=P2^7;     //LCD使能引脚:下降沿触发,高电平锁存数据,低电平执行指令
#define LCD_DataPort P0  //LCD的8位数据口D0~D7 直接接单片机P0口,8位并行模式核心定义

/**
  * @brief  LCD1602延时函数,晶振12MHz时,该函数精准延时1ms
  * @param  无参数
  * @retval 无返回值
  * @note   LCD1602的时序要求必须加延时,保证指令/数据稳定写入,不可删除
  */
void LCD_Delay()		//@12.000MHz 延时1ms标准函数
{
	unsigned char i, j;
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写指令函数:向LCD写入配置指令
  * @param  Command 要写入的指令码(如0x38、0x0C等)
  * @retval 无
  * @note   所有LCD配置操作都调用此函数,写指令固定RS=0、RW=0
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;        //RS置0:选择【指令寄存器】,告知LCD接下来接收的是指令
	LCD_RW=0;        //RW置0:选择【写操作】,51驱动LCD1602仅用写,读极少用
	LCD_DataPort=Command;  //将8位指令码一次性送入LCD的D0~D7数据口
	LCD_E=1;         //E置高电平:锁存数据端口的指令码
	 LCD_Delay();     //短暂延时,保证数据稳定锁存
	LCD_E=0;         //E置低电平:下降沿触发LCD执行指令,核心触发逻辑
	 LCD_Delay();     //延时等待指令执行完成,防止指令执行不完整
}

/**
  * @brief  LCD1602写数据函数:向LCD写入要显示的字符数据
  * @param  Data 要写入的字符ASCII码(如显示'A'写0x41,显示'1'写0x31)
  * @retval 无
  * @note   所有字符显示都调用此函数,写数据固定RS=1、RW=0,与写指令仅RS电平不同
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;        //RS置1:选择【数据寄存器】,告知LCD接下来接收的是显示数据
	LCD_RW=0;        //RW置0:保持写操作模式
	LCD_DataPort=Data;  //将字符的ASCII码送入LCD数据口
	LCD_E=1;         //E置高:锁存数据
	 LCD_Delay();     //延时稳定
	LCD_E=0;         //E置低:下降沿触发LCD接收显示数据
	 LCD_Delay();     //延时保证数据写入完成
}

/**
  * @brief  LCD1602初始化函数,上电必调用,初始化完成后才能使用所有显示功能
  * @param  无参数
  * @retval 无
  * @note   指令顺序不可调换,是LCD1602的标准初始化流程,8位模式固定指令组合
  */
void LCD_Init(void)
{
	LCD_WriteCommand(0x38);  //功能设置指令:8位数据接口、16行2列显示、5*8点阵字符
	LCD_WriteCommand(0x0C);  //显示开关控制:开显示、关闭光标 (0x0E开光标,0x0F光标闪烁)
	LCD_WriteCommand(0x06);  //输入模式设置:写数据时光标自动右移,屏幕整体不移动
	LCD_WriteCommand(0x01);  //清屏指令:清除屏幕所有显示内容,光标归位到第一行第一列
}

/**
  * @brief  LCD1602设置光标位置函数,指定字符显示的行列坐标
  * @param  Line 行位置,取值范围:1 - 2 (仅支持2行)
  * @param  Column 列位置,取值范围:1 - 16 (仅支持16列)
  * @retval 无
  * @note   LCD1602的DDRAM地址规则:第一行起始地址0x00,第二行起始地址0x40;0x80是地址指令前缀
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)  //如果选择第一行
	{
		LCD_WriteCommand(0x80|(Column-1));  //0x80+列偏移量,设置第一行指定列光标
	}
	else         //如果选择第二行
	{
		LCD_WriteCommand(0x80|(Column-1)+0x40);  //0x80+0x40+列偏移量,设置第二行指定列光标
	}
}

/**
  * @brief  在LCD1602指定位置显示单个字符
  * @param  Line 行位置:1或2
  * @param  Column 列位置:1~16
  * @param  Char 要显示的单个字符(如'A','5','-'等)
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
	LCD_SetCursor(Line,Column);  //第一步:先定位光标到指定位置
	LCD_WriteData(Char);         //第二步:在光标位置写入字符的ASCII码,完成显示
}

/**
  * @brief  在LCD1602指定位置开始显示字符串
  * @param  Line 起始行:1或2
  * @param  Column 起始列:1~16
  * @param  String 要显示的字符串(字符串以'\0'作为结束标志,如"Hello 51MCU")
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);  //定位光标到起始位置
	for(i=0;String[i]!='\0';i++) //循环读取字符串的每个字符,直到读到结束符'\0'
	{
		LCD_WriteData(String[i]);//逐个写入字符ASCII码,实现字符串连续显示
	}
}

/**
  * @brief  内部幂运算函数,供数字显示函数调用,不可单独调用
  * @param  X 底数
  * @param  Y 指数
  * @retval 返回 X的Y次方 计算结果
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;        //初始化结果为1
	for(i=0;i<Y;i++)     //循环累乘,计算幂值
	{
		Result*=X;
	}
	return Result;       //返回计算结果
}

/**
  * @brief  在LCD指定位置显示【无符号十进制数字】
  * @param  Line 起始行:1或2
  * @param  Column 起始列:1~16
  * @param  Number 要显示的数字,取值范围:0 ~ 65535
  * @param  Length 要显示的数字位数,取值范围:1 ~ 5
  * @retval 无
  * @note   高位在前,低位在后,如数字123,Length=3,显示"123"
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);  //定位光标起始位置
	for(i=Length;i>0;i--)        //从高位到低位,依次拆分数字的每一位
	{
		//取当前位数字 + '0' 转为ASCII码,写入LCD显示
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD指定位置显示【有符号十进制数字】
  * @param  Line 起始行:1或2
  * @param  Column 起始列:1~16
  * @param  Number 要显示的数字,取值范围:-32768 ~ 32767
  * @param  Length 要显示的数字位数,取值范围:1 ~ 5
  * @retval 无
  * @note   正数显示前缀'+',负数显示前缀'-',数字部分按绝对值显示
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)        //判断数字为正数
	{
		LCD_WriteData('+');//显示正号
		Number1=Number;    //正数直接赋值
	}
	else                 //判断数字为负数
	{
		LCD_WriteData('-');//显示负号
		Number1=-Number;   //取负数的绝对值,统一按正数拆分
	}
	for(i=Length;i>0;i--) //拆分数字每一位并显示
	{
		LCD_WriteData('0'+Number1/LCD_Pow(10,i-1)%10);
	}
}

/**
  * @brief  在LCD指定位置显示【十六进制数字】
  * @param  Line 起始行:1或2
  * @param  Column 起始列:1~16
  * @param  Number 要显示的数字,取值范围:0 ~ 0xFFFF
  * @param  Length 要显示的位数,取值范围:1 ~ 4
  * @retval 无
  * @note   十六进制数字0~9正常显示,10~15显示大写字母A~F
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	unsigned char SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)        //从高位到低位拆分每一位十六进制数
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData('0'+SingleNumber); //0~9 转ASCII码显示
		}
		else
		{
			LCD_WriteData('A'+SingleNumber-10); //10~15 转A~F显示
		}
	}
}

/**
  * @brief  在LCD指定位置显示【二进制数字】
  * @param  Line 起始行:1或2
  * @param  Column 起始列:1~16
  * @param  Number 要显示的数字,取值范围:0 ~ 0xFFFF
  * @param  Length 要显示的位数,取值范围:1 ~ 16
  * @retval 无
  * @note   二进制仅显示0和1,是单片机查看寄存器/引脚状态的常用功能
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)        //从高位到低位拆分每一位二进制数
	{
		LCD_WriteData('0'+Number/LCD_Pow(2,i-1)%2);
	}
}

✅LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

void LCD_Init(void);
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

6.8 项目示例

项目架构:

main.c主函数:

#include <REGX52.H>
#include "LCD1602.h"

void main()
{
    LCD_Init();					   //LCD初始化
    LCD_ShowChar(1,1,'A');			  //在1行1列显示字符A
    LCD_ShowString(1,3,"Hello");	  //在1行3列显示字符串Hello
    LCD_ShowNum(1,9,66,2);			  //在1行9列显示数字66,长度为2
    LCD_ShowSignedNum(1,12,-88,2);	  //在1行12列显示有符号数字-88,长度为2
    LCD_ShowHexNum(2,1,0xA5,2);	  //在2行1列显示十六进制数字0xA5,长度为2
    LCD_ShowBinNum(2,4,0xA5,8);	  //在2行4列显示二进制数字0xA5,长度为8
    LCD_ShowChar(2,13,0xDF);		  //在2行13列显示编码为0xDF的字符
    LCD_ShowChar(2,14,'C');		  //在2行14列显示字符C
    while(1)
    {
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值