09【模块学习】LCD1602(一):使用学习

1、LCD1602简介

1.1、简要介绍

LCD1602是一种工业字符型液晶显示屏幕,能够同时显示16×2即32字符(16列2行)。每个字符是由 5X8 的点阵组成。如下图所示:
在这里插入图片描述

😀VSS:电源接地引脚
😀VDD:电源正极引脚,接+5V
😀VO:对比度调节电压
😀RS:数据/指令选择引脚。0表示指令,1表示数据
😀RW:读/写选择引脚。0表示写,1表示读
😀E:模块使能引脚
😀D0~D7:数据传输引脚

1.2、显示原理

LCD1602的每个显示字符位置是由 5X8 的点阵组成,如下图所示:
在这里插入图片描述

如图:字符"A"字模由数据0x0E,0x11,0x11,0x11,0x1F,0x11,0x11,0x00这8个数据组成。
而在LCD1602将这些字模数据进行固化保存在 CGROM 和CGRAM 中。如下图所示:

在这里插入图片描述
如上图所示:字符“A”的字模数据保存在CGROM中地址为的0x41的位置,我们需要LCD1602屏幕上显示字符"A",即向模块写入0x41即可。模块接收到数据0x41后,就去找到0x41位置数据将其显示在屏幕上。
【注意】CGROM中的字模与ASCII码表是一一对应的。

LCD1602由2行16列个小屏幕组成,每个小屏幕都有相应对应的地址。在要屏幕显示数据的时候,需要发送指令,指定位置小屏幕显示。
而这些地址数据是存储在 DDRAM中, DDRAM是一个40x2的存储数据的存储区,它前面16列存储对应的屏幕地址数据,后面的位置用于存储,发送来的指令/显示的数据。
在这里插入图片描述
在这里插入图片描述

1.3、指令集

LCD1602在使用前需要发送指令对其进行配置。其指令集如下所示:
在这里插入图片描述
在这里插入图片描述

2、时序

  • ①读操作
    在这里插入图片描述

读取数据:再E = 0时,将RS = 0表示读指令(读取忙状态D7位BF)/将RS = 1表示读数据。然后将RW = 1(表示读操作)
然后延时tsp1时间,然后将E = 1,开始读取D0~D7线上的数据,延时tpw时间后将E = 0即可。

/**
 * LCD1602忙检测
 */
void LCD_BusyCheck(void)
{
	unsigned char temp = 0;
	unsigned char Count = 0;
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 0;				//RS = 0代表指令
	LCD_RW = 1;				//RW = 1代表读操作
	
	do{
		LCD_EN = 1;		  // 拉高
		temp = LCD_Data;  // 将 LCD 状态保存在temp中,用于判忙
		LCD_EN = 0;		  // 负跳变使能 
		if(Count++ >= 40)
		{
			break;					//若死循环则通过break跳出循环
		}
	}while(temp & 0x80);  // D7位BF结果为1,LCD 忙,继续循环;结果为0,LCD 不忙,可以进行后面的操作
}	
  • ②写操作
    在这里插入图片描述

写入数据/指令:再E = 0时,将R/W = 0(表示写操作),将RS = 0表示写指令/将RS = 1表示写数据。
然后将需要写入的指令/数据写入到D0~D7数据线上,延时tsp2时间,然后将E = 1,延时tpw时间后将E = 0即可

/**
 * 向LCD1602写指令
 */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_BusyCheck();		//判忙
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 0;				//RS = 0代表指令
	LCD_RW = 0;				//RW = 0代表写操作
	
	LCD_Data = Command;		//写入指令
	_nop_();				//等待tsp1 + tsp2
	LCD_EN = 1;				//使能开启
	
	LCD_Delay1ms();	
	LCD_EN = 0;				//使能关闭
}

/**
 * 向LCD1602写入数据
 */
void LCD_WriteData(unsigned char Data)
{
	LCD_BusyCheck();		//判忙
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 1;				//RS = 1代表数据
	LCD_RW = 0;				//RW = 0代表写操作
	
	LCD_Data = Data;		//写入数据
	_nop_();				//等待tsp1 + tsp2
	LCD_EN = 1;				//使能开启
	
	LCD_Delay1ms();			//等待tR +tPW
	LCD_EN = 0;				//使能关闭

3、代码演示

在这里插入图片描述

使用要求:构造函数让LCD1602能够显示字符,字符串,无符号整数,有符号整数,有符号小数,16进制数,二进制数

①LCD1602.c文件的代码如下:

#include "LCD1602.h"
#include "intrins.h"

void LCD_Delay1ms(void)		//@11.0592MHz,1ms的延迟
{
	unsigned char i, j;
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

/**
 * 向LCD1602写指令
 */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_BusyCheck();		//判忙
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 0;				//RS = 0代表指令
	LCD_RW = 0;				//RW = 0代表写操作
	
	LCD_Data = Command;		//写入指令
	_nop_();				//等待tsp1 + tsp2
	LCD_EN = 1;				//使能开启
	
	LCD_Delay1ms();	
	LCD_EN = 0;				//使能关闭
}

/**
 * 向LCD1602写入数据
 */
void LCD_WriteData(unsigned char Data)
{
	LCD_BusyCheck();		//判忙
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 1;				//RS = 1代表数据
	LCD_RW = 0;				//RW = 0代表写操作
	
	LCD_Data = Data;		//写入数据
	_nop_();				//等待tsp1 + tsp2
	LCD_EN = 1;				//使能开启
	
	LCD_Delay1ms();			//等待tR +tPW
	LCD_EN = 0;				//使能关闭
}

/**
 * LCD1602忙检测
 */
void LCD_BusyCheck(void)
{
	unsigned char temp = 0;
	unsigned char Count = 0;
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 0;				//RS = 0代表指令
	LCD_RW = 1;				//RW = 1代表读操作
	
	do{
		LCD_EN = 1;		  // 拉高
		temp = LCD_Data;  // 将 LCD 状态保存在temp中,用于判忙
		LCD_EN = 0;		  // 负跳变使能 
		if(Count++ >= 40)
		{
			break;
		}
	}while(temp & 0x80);  // 结果为 1,LCD 忙,继续循环;结果为 0,LCD 不忙,可以进行后面的操作
}

/**
 * LCD1602的初始化
 */
void LCD_Init(void)
{
	LCD_WriteCommand(0x38);	//8位数据接口,2行,5*8点阵
	LCD_WriteCommand(0x0c);	//显示开,光标关,闪烁关 
	LCD_WriteCommand(0x06);	//光标向右加1,画面不动 
	LCD_WriteCommand(0x80);	//从第一行第一列开始显示
	LCD_WriteCommand(0x01);	//清屏
	LCD_Delay1ms();			//延时,等待初始化完成
}

/**
 * 确定行列
 * 第1行的地址为:0x00,0x01,0x02....0x27
 * 第2行的地址为:0x40,0x41,0x42....0x67	
 * Hang: 1~2
 * Lie:  1~40
 */
void LCD_Position(unsigned char Hang, unsigned char Lie)
{
	if(Hang == 1)
	{
		LCD_WriteCommand(0x80 | (Lie - 1));
	}
	else
	{
		LCD_WriteCommand(0x80 | (0x40 + (Lie - 1)));
	}
}

/**
 * LCD显示一个字符
 */
void LCD_ShowChar(unsigned char Hang, unsigned char Lie, char Byte)
{
	LCD_Position(Hang, Lie);
	LCD_WriteData(Byte);
}

/**
 * LCD显示字符串
 */
void LCD_ShowString(unsigned char Hang, unsigned char Lie, char *Str)
{
	LCD_Position(Hang, Lie);
	while(*Str != '\0')
	{
		LCD_WriteData(*Str++);
	}
}


/**
 * X的Y次方函数
 */
unsigned int Pow_LCD(unsigned char X,unsigned char Y)
{
	unsigned int Result = 1;
	while(Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
 * LCD显示无符号整数
 * Num:无符号整数0 到 4294967295。
 * Length:数值长度
 */
void LCD_ShowNum(unsigned char Hang, unsigned char Lie, unsigned long Num, unsigned int Length)
{
	unsigned int i;
	unsigned long Num1;
	LCD_Position(Hang, Lie);
	for(i = 1; i<= Length; i++)
	{
		Num1 = (Num / Pow_LCD(10,Length - i)) % 10;
		LCD_WriteData('0'+ Num1);
	}
}

/**
 * LCD显示有符号整数
 * Num:无符号整数0 到 4294967295。
 * Length:数值长度
 */
void LCD_ShowSignedNum(unsigned char Hang, unsigned char Lie, signed long Num, unsigned int Length)
{
	unsigned int i ;
	unsigned long Num1;
	LCD_Position(Hang, Lie);
	if(Num >= 0)
	{
		LCD_WriteData('+');
		for(i = 1; i<= Length; i++)
		{
			LCD_WriteData('0'+ (Num / Pow_LCD(10,Length - i)) % 10);
		}
	}
	else
	{
		LCD_WriteData('-');
		for(i = 1; i<= Length; i++)
		{
			Num1 = -Num;
			LCD_WriteData('0'+ (Num1 / Pow_LCD(10,Length - i)) % 10);
		}
	}
}

/**
 * LCD显示小数
 * Num:有符号小数
 * Length:小数点后长度
 */
void LCD_ShowFloat(unsigned char Hang, unsigned char Lie, float Num, unsigned char LenInt,unsigned char LenFloat)
{
	unsigned char j = 0;
	unsigned short Inte = 0;	//保存小数整数部分的变量
	
	if(Num < 0)
	{
		Num = -Num;				//转换为正数
		LCD_Position(Hang, Lie);
		LCD_WriteData('-');		//显示“-”
	}
	else
	{
		LCD_Position(Hang, Lie);
		LCD_WriteData('+');		//显示“+”
	}
	
	/* 显示整数 */
	LCD_ShowNum(Hang, Lie + 1, (unsigned short)Num, LenInt);
	
	/* 将小数计算为整数 */
	Num = Num - (unsigned short)Num;					//获取小数部分
	Inte = (unsigned short)(Num * Pow_LCD(10,LenFloat));//将小数计算为整数
		
	/* 显示小数部分  */
	LCD_ShowChar(Hang, Lie + 1 + LenInt, '.');			//显示小数点
	LCD_ShowNum(Hang, Lie + 2 + LenInt, Inte, LenFloat);//显示小数部分
}

/**
  * LCD显示16进制数字
  * Number范围:0~0xFFFF
  * Length长度:1~4
  */
void LCD_ShowHexNum(unsigned char Hang, unsigned char Lie,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_Position(Hang, Lie);
	for(i=Length;i>0;i--)
	{
		SingleNumber = Number / Pow_LCD(16,i-1) % 16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * 将数值转换为二进制数值显示
  * Number范围:0~1111 1111 1111 1111
  * Length范围:1~16
  */
void LCD_ShowBinNum(unsigned char Hang, unsigned char Lie,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_Position(Hang, Lie);
	for(i = Length; i > 0; i--)
	{
		LCD_WriteData(Number / Pow_LCD(2,i-1) % 2 + '0');
	}
}

②LCD1602.h文件的代码如下:

#ifndef __LCD1602_H
#define __LCD1602_H
#include <REGX52.H>		//包含51头文件,里面全是寄存器地址

#define LCD_Data P0
sbit LCD_RS = P2^5;
sbit LCD_RW = P2^6;
sbit LCD_EN = P2^7;

/* 底层驱动函数 */
void LCD_WriteCommand(unsigned char Command);
void LCD_WriteData(unsigned char Data);
void LCD_BusyCheck(void);
void LCD_Position(unsigned char Hang, unsigned char Lie);
unsigned int Pow_LCD(unsigned char X,unsigned char Y);

/* 用户调用函数 */
void LCD_Init(void);
void LCD_ShowChar(unsigned char Hang, unsigned char Lie, char Byte);
void LCD_ShowString(unsigned char Hang, unsigned char Lie, char *Str);
void LCD_ShowNum(unsigned char Hang, unsigned char Lie, unsigned long Num,unsigned int Length);
void LCD_ShowSignedNum(unsigned char Hang, unsigned char Lie, signed long Num, unsigned int Length);
void LCD_ShowFloat(unsigned char Hang, unsigned char Lie, float Num, unsigned char LenInt,unsigned char LenFloat);
void LCD_ShowHexNum(unsigned char Hang, unsigned char Lie,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Hang, unsigned char Lie,unsigned int Number,unsigned char Length);

#endif

③main.c文件的代码如下:

#include "LCD1602.h"

void main(void)
{
	LCD_Init();
	
	LCD_ShowChar(1, 1, 'A');
	LCD_ShowString(1, 3, "nihao");
	LCD_ShowNum(1,9,123,3);
	LCD_ShowSignedNum(1, 13, -21, 2);
	LCD_ShowFloat(2,1,-15.321,2,3);
	LCD_ShowHexNum(2, 9,15,2);		//将15转化为十六进制为F显示
	LCD_ShowBinNum(2, 13,4,4);		//将4转化为二进制为0100显示
	
	while(1)
	{
	
	}
}

在这里插入图片描述

4、自定义字符显示

LCD1602将固定的字模数据保存再CGROM 中,而LCD1602的CGRAM用于保存用户自定义字模的数据。其中通过发送下列的指令来确定字模数据存储在哪一个CGRAM中。

在这里插入图片描述
其中D5 ~ D3位确定哪一个CGRAM。由此可得一共由2^3 = 8个CGRAM。D2~D0位确定数据存放在CGRAM中哪个位置(一共有8个位置,即对应了8点阵,如下图所示)。一般D2~D0用户不用管他,只需要发送一个CGRAM的初始地址,然后发送8个字模数据,数据就会依次从上往下存放在CGRAM中。
在这里插入图片描述

例如:
指令00 0100 0000(0x40)表示存放在第1个CGRAM中,而第1个CGRAM又与CGROM中的0x00相关联。
指令00 0100 1000(0x48)表示存放在第2个CGRAM中,而第2个CGRAM又与CGROM中的0x01相关联。
指令00 0101 0000(0x50)表示存放在第3个CGRAM中,而第3个CGRAM又与CGROM中的0x02相关联。
指令00 0101 1000(0x58)表示存放在第4个CGRAM中,而第4个CGRAM又与CGROM中的0x03相关联。
指令00 0110 0000(0x60)表示存放在第5个CGRAM中,而第5个CGRAM又与CGROM中的0x04相关联。
指令00 0110 1000(0x68)表示存放在第6个CGRAM中,而第6个CGRAM又与CGROM中的0x05相关联。
指令00 0111 0000(0x70)表示存放在第7个CGRAM中,而第7个CGRAM又与CGROM中的0x06相关联。
指令00 0111 1000(0x78)表示存放在第8个CGRAM中,而第8个CGRAM又与CGROM中的0x07相关联。
在这里插入图片描述

/**
 * 用户自定义字符
 * CGRAM起始地址:0x40 0x48 0x50 0x58 0x60 0x68 0x70 0x78
 * CGROM对应代码:0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07
 * Character:字模数据的数组指针
 * Len:需要自动几个字符(1~8)
 */
void LCD_Customcharacter(unsigned char *Character, unsigned char Len)
{
	unsigned char i = 0;
	unsigned char j = 0;
	for(i = 0; i < Len; i++)
	{
		LCD_WriteCommand(0x40 + i * 8);	//指令:写入自定义字符的起始空间地址(从第一个CGRAM开始)
		for(j = 0; j < 8; j++)
		{
			LCD_WriteData(*(Character + (i * 8) + j));	//写入字模数据
		}	 
	}
}

③main.c文件的代码如下:

#include "LCD1602.h"

unsigned char code Chara[][8] = {
	{0x0f, 0x12, 0x0f, 0x0a, 0x1f, 0x02, 0x02, 0x02},	//字符年
	{0x0f, 0x09, 0x0f, 0x09, 0x0f, 0x09, 0x0b, 0x11},	//字符月
	{0x1f, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x1f, 0x00}	//字符日
};
void main(void)
{
	LCD_Init();
	
	LCD_Customcharacter((unsigned char* )Chara, 3);	//添加自定义字符"年月日"
	
	LCD_ShowChar(1, 1, 0x00);	//显示年
	LCD_ShowChar(1, 2, 0x01);	//显示月
	LCD_ShowChar(1, 3, 0x02);	//显示日
	
	while(1)
	{
	
	}
}

在这里插入图片描述

5、滚屏显示

在这里插入图片描述
③main.c文件的代码如下:

#include "LCD1602.h"


void Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main(void)
{
	LCD_Init();
	LCD_ShowString(1,1,"Hello World");
	
	while(1)
	{
		LCD_WriteCommand(0x18);	//整体向左移动
		Delay500ms();

	}
}

6、4路引脚显示

在这里插入图片描述

在这里插入图片描述

使用4路引脚来驱动LCD1602可以节约引脚的使用。
需要向数据线上发送数据时:先将需要发送的数据Data的高4位数据发送到D7~D4,然后再将需要发送的数据Data的低4位数据发送到D7 ~ D4上

只需要修改驱动函数,应用函数不需要改变,修改的代码如下。
①LCD1602.c文件的代码如下:

#include "LCD1602.h"
#include "intrins.h"

void LCD_Delay1ms(void)		//@11.0592MHz,1ms的延迟
{
	unsigned char i, j;
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

/**
 * 向LCD1602写指令——4路
 */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_BusyCheck();		//判忙
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 0;				//RS = 0代表指令
	LCD_RW = 0;				//RW = 0代表写操作
	
	/* 写入指令的高4位数据 */
	LCD_Data &= 0x0F;			//先让引脚的高4位清零
 	LCD_Data |= (Command & 0xF0);//写入指令
	_nop_();					//等待tsp1 + tsp2
	LCD_EN = 1;					//使能开启
	LCD_Delay1ms();				
	LCD_EN = 0;					//使能关闭
	
	/* 写入指令的低4位数据 */
	LCD_Data &= 0x0F;			//先让引脚的高4位清零
 	LCD_Data |= (Command << 4);	//写入指令
	_nop_();					//等待tsp1 + tsp2
	LCD_EN = 1;					//使能开启
	LCD_Delay1ms();				//等待tsp1 + tsp2
	LCD_EN = 0;					//使能关闭
}

/**
 * 向LCD1602写入数据——4路
 */
 void LCD_WriteData(unsigned char Data)
{
	LCD_BusyCheck();		//判忙
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 1;				//RS = 1代表数据
	LCD_RW = 0;				//RW = 0代表写操作
	
	/* 写入数据的高4位数据 */
	LCD_Data &= 0x0F;			//先让引脚的高4位清零
 	LCD_Data |= (Data & 0xF0);	//写入指令
	_nop_();					//等待tsp1 + tsp2
	LCD_EN = 1;					//使能开启
	LCD_Delay1ms();				
	LCD_EN = 0;					//使能关闭
	
	/* 写入数据的低4位数据 */
	LCD_Data &= 0x0F;			//先让引脚的高4位清零
 	LCD_Data |= (Data << 4);	//写入指令
	_nop_();					//等待tsp1 + tsp2
	LCD_EN = 1;					//使能开启
	LCD_Delay1ms();				//等待tsp1 + tsp2
	LCD_EN = 0;					//使能关闭
}

/**
 * LCD1602忙检测——4路
 */
void LCD_BusyCheck(void)
{
	unsigned char temp = 0;
	unsigned char Count = 0;
	
	LCD_EN = 0;				//使能关闭
	LCD_RS = 0;				//RS = 0代表指令
	LCD_RW = 1;				//RW = 1代表读操作
	
	do{
		/* 读取高4位 */
		LCD_EN = 1;		  			// 拉高
		temp |= (LCD_Data & 0xF0);  // 将LCD的高4位保存到temp,用于判忙
		LCD_EN = 0;		  			// 使能关闭
		
		/* 读取低4位 */
		LCD_EN = 1;		  			// 拉高
		temp |= ((LCD_Data & 0xF0) >> 4);  // 将LCD的低4位保存到temp,用于判忙
		LCD_EN = 0;		  			// 使能关闭
		if(Count++ >= 40)
		{
			break;
		}
	}while(temp & 0x80);  // 结果为 1,LCD 忙,继续循环;结果为 0,LCD 不忙,可以进行后面的操作
}	

/**
 * LCD1602的初始化——4路
 */
void LCD_Init(void)
{
	LCD_WriteCommand(0x33);
	LCD_WriteCommand(0x32); //注意:使用4路驱动时需要先发送0x33和0x32,然后再发送配置指令,具体参考参考手册
	LCD_WriteCommand(0x28);	//4位数据接口,2行,5*8点阵
	LCD_WriteCommand(0x0c);	//显示开,光标关,闪烁关 
	LCD_WriteCommand(0x06);	//光标向右加1,画面不动 
	LCD_WriteCommand(0x80);	//从第一行第一列开始显示
	LCD_WriteCommand(0x01);	//清屏
	LCD_Delay1ms();			//延时,等待初始化完成
}

②main.c文件的代码如下:

#include "LCD1602.h"

void main(void)
{
	LCD_Init();	
	LCD_ShowChar(1, 1, 'A');
	LCD_ShowString(1, 3, "nihao");
	LCD_ShowNum(1,9,123,3);
	LCD_ShowSignedNum(1, 13, -21, 2);
	LCD_ShowFloat(2,1,-15.321,2,3);
	LCD_ShowHexNum(2, 9,15,2);		//将15转化为十六进制为F显示
	LCD_ShowBinNum(2, 13,4,4);		//将4转化为二进制为0100显示
	
	while(1)
	{
		
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值