android 一个字符串分两行显示_项目七 1602液晶显示

96b36743ab7a9ce8608e1814ee71cf00.png

(一)项目任务

  • 任务1:点亮LED
    • 子任务1.1 L

任务2:LED流水灯功能实现

    • 子任务2.1 C语言---for等基本语句认识

(二)相关知识点

2.1、1602LCD基本概念

LCD1602是一种工业字符型液晶,如图1所示,它能同时显示两行,每行16个字符,做为一种常用的显示屏,要熟练掌握它的使用方法。硬件连接如图2所示。

0cfaa6ac0edae41112c6d943d133e4c8.png
图1 LCD1602实物图

ef17ead2d21dfe8da470f524407c8cf4.png
图2 LCD1602硬件连接图

bfd5ab2eb154007d7969a669f8617a50.png

2.1.1 LCD1602控制时序读写分析

结合图2和表1,1602液晶屏一共外接16个引脚,其中第2、15引脚接电源,第1、16引脚接地,第3引脚接滑动变阻器,通过改变电压来控制显示的字符和背景的对比度,第7至14引脚接P2口用于传送数据。

第4引脚RS用来表示发送的是数据还是命令(高电平为数据,低电平为命令),第5引脚R/W用来表示当前操作是读操作还是写操作(高电平为读操作,低电平为写操作),第5引脚E用来表示使能液晶屏。液晶屏主要是由这3个引脚来控制,如图3是单片机的写操作时序。

774c820a3caeb3ddff1338ec137aa3fb.png
图3 写操作时序

7f87e2fed35ada2981878f391c9a14c8.png

“时序”可以理解为我们与芯片之间的通信语言,因为单片机只有高低两种电平,不能像我们一样用很多词汇去表达意思,设计工程师想了一个办法,通过高低电平和持续的时间让我们跟芯片进行“对话”。图3左边显示4组引脚名字,虽然有上下之分,但是4组时序是同时进行,由于是写操作,在tsp1前不管R/W什么状态都变低电平,同时RS完成高低电平的变化。图4中可以看到tsp1的最小值为30us,没有最大值,也就是说至少持续(tsp1-tr)再拉高E端口电平, E端口电平至少持续tpw的时间才能把数据写到DB0-DB7,E端口再拉低电平。根据时序图写相应的部分程序,以写命令为例,如图5所示,将准备工作做好后使能LcdEN为1,再将LcdEN置0,由于51单片机运行的最小单位都是us级别,远大于几个ns级别,不需要延时即可,如读者在调试过程中有问题加延时函数即可,本例程序在配套开发板上未加延时尚未出现问题。

5e475c8ecde604ab7b11f94b453cfd60.png

细心的读者发现图5和图6区别在于LcdRS,那么在3.2小节展示如何将两个程序合而为一,读者可以自行先思考。

读操作时序图如图7所示,读者可以根据上面的讲解看下是否能看懂该时序图。

e72fcfca4eb75d8bf1b1f5214fc89f43.png
图7 读操作时序图

根据LCD1602数据手册可知,对控制器每次读写之前必须进行读写检测,LCD中有个状态字,如图8所示,它的最高位STA7用于判断液晶屏是否出于忙的状态,当STA7为1时,表示正在忙,当STA7为0时,表示空闲,在程序中采用“while(LcdDB&0x80);”语句来判断LCD当前状态是否忙。

90c88b86f012ae6831e2bc2e99a5767d.png
图8 状态字说明图

2.1.2 1602功能配置实现

1602液晶屏本质上是由HD44780芯片控制,所以学好1602需要了解该控制芯片,本节不做展开,具体可以留言讨论。强调下HD44780控制芯片共有80个字节(每行40个),但是实际上只显示32个字节(每行16个),这是因为被液晶屏大小限制,每行的24个字节只是没有显示出来实际是存在的,如图9所示,第一行首地址为0x00,第二行首地址为0x40,但在实际使用中还需要加0x80,很多读者在别的资料中看到过相关语句,但是都没很好的解释,实际上查看HD44780芯片手册就一目了然,这是硬件规定,只需要加上即可,如实例中“LcdWrCmd(addr|0x80)”语句,即把原来地址再加上0x80。

51081961c728ca7aa209f34a2e641b6b.png
图9 DDRAM地址分配图

初始化液晶屏命令较为固定,本节不做具体展开,把需要的命令列出,具体请参考HD44780芯片手册。

2.1.3 1602液晶的指令介绍

1602液晶有以下几个指令需要了解。

1、显示模式设置

写指令0x38表示的是设置16x2显示,5x7点阵,8位数据接口。这条指令对液晶来说是固定的,仔细看会发现液晶实际上内部点阵是5x8。

2、显示开/关以及光标设置指令

这里有2条指令,第一条指令,一个字节中8位,其中高5位是0b00001,低3位分别用DCB从高到低表示,D=1表示开显示,D=0表示关显示;C=1表示显示光标,C=0表示不显示光标;B=1表示光标闪烁,B=0表示光标不闪烁。第二条指令,高6位是0b000001,低2位分别用NS从高到低表示,其中N=1表示读或者写一个字符后,指针自动加1,光标自动加1,N=0表示读或者写一个字符后指针自动减1,光标自动减1;S=1 表示写一个字符后,整屏显示左移(N=1)或右移(N=0),以达到光标不移动而屏幕移动的效果,如同计算器输入一样的效果,而S=0表示写一个字符后,整屏显示不移动。

3、清屏指令

写入0x01表示显示清屏,其中包含了数据指针清零,所有的显示清零。写入0x02则仅仅是数据指针清零,显示不清零。

4、RAM 地址设置指令

该指令码的最高位为1,低7位为RAM的地址。通常,在读写数据之前都要先设置好地址,然后再进行数据的读写操作。

这几条总结如下:

“0x38”命令表示数据总线为8位,显示两行,每个字符是5*7点阵;

“0x0C”命令表示开显示,关闭光标;

“0x 06”命令表示写字符后地址自动+1;

“0x01”命令表示清屏。

(三)设计实施

3.1、显示两行字符串

#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define LcdDB  P2
uchar code table1[]="I love MCU";
uchar code table2[]="I love Zhoushan";
uchar *str1=table1;//数组的首地址赋值给指针str1
uchar *str2=table2;
sbit  LcdEN = P3^2;//根据硬件连接图定义引脚
sbit  LcdRW = P3^6; //根据硬件连接图定义引脚
sbit  LcdRS = P3^7; //根据硬件连接图定义引脚
void LcdBusy();
void LcdWrCmd(uchar cmd);
void LcdWrDat(uchar dat);
void Lcd_Init();
void LcdShow(uchar x,uchar y);
void main()
{
	Lcd_Init();
	LcdShow(2,0);//指定字符串1的首地址在屏幕的位置
	while(*str1 !='0')
	{
		LcdWrDat(*str1++);
	}
	LcdShow(1,1); //指定字符串1的首地址在屏幕的位置
	while(*str2 !='0')
	{
		LcdWrDat(*str2++);
	}
	while(1);
}
void LcdBusy()//判断液晶屏是否处于忙的状态
{
	LcdDB=0xFF;
	LcdRS = 0;
	LcdRW = 1;		
	LcdEN = 1;
	while(LcdDB&0x80); //判断液晶是否处于忙的状态
	LcdEN = 0;
}

void LcdWrCmd(uchar cmd)
{
	LcdBusy();
	LcdRS = 0;
	LcdRW = 0;
	LcdDB = cmd;
	LcdEN = 1;
	LcdEN = 0;	
}
void LcdWrDat(uchar dat)
{
	LcdBusy();
	LcdRS = 1;
	LcdRW = 0;
	LcdDB = dat;
	LcdEN = 1;
	LcdEN = 0;	
}
void Lcd_Init()
{
	LcdWrCmd(0x38);//显示两行,每个字符是5*7点阵
	LcdWrCmd(0x0C);//表示开显示,关闭光标
	LcdWrCmd(0x06);//表示写字符后地址自动+1
	LcdWrCmd(0x01);//表示清屏
}
void LcdShow(uchar x,uchar y)
{
	uchar addr;
	if(0==y)
	 	addr = 0x00+x;
	else
	 	addr = 0x40+x;
	LcdWrCmd(addr|0x80);//地址设置
}

①操作液晶屏思路:指定要显示的位置,将字符串首地址赋值给指针,指针依次取字符并显示在液晶屏,直至碰到结束符’0’。

②液晶操作指令较为固定,正确掌握。

3.2、发送指令在1602显示

/*****************************************************
串口助手发送1,在串口助手接收区和1602显示“I love MCU”,
串口助手发送2,在串口助手接收区和1602显示“I love Zhoushan”。
*********************************************************/
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define LcdDB  P2
uchar code table1[]="I love MCU";
uchar code table2[]="I love Zhoushan";
uchar *str1=table1;
uchar *str2=table2;
void LcdBusy();
void LcdWrCmdorDat(uchar cmdordat,uchar cmddat);
void Lcd_Init();
void LcdShow(uchar x,uchar y);
void LcdClear(uchar x,uchar y,uchar len);
sbit  LcdEN = P3^2;
sbit  LcdRW = P3^6;
sbit  LcdRS = P3^7;
bit UartFlag=0;
uchar CmdIndex = 0;
uchar *str1 ;
uchar *str2 ;
void UART_Init();
void main()
{
	Lcd_Init();
	UART_Init();
	while(1)
	{
		 if(UartFlag)
		 {
		  	UartFlag=0;
			switch (CmdIndex)
			{								
				case 1:			
				{
					LcdWrCmdorDat(0,0x01);
					LcdShow(2,0);
					str1 = table1;//table1的首地址赋值给指针变量str1
					while(*str1 !='0')
					{
						LcdWrCmdorDat(1,*str1++);
					}				
					str1 = table1;//指针复位
					TI =1;
					CmdIndex=0;
					break;			 				
				}
				case 2:
				{				
					LcdWrCmdorDat(0,0x01);
					LcdShow(2,1);
					str2 = table2;
					while(*str2 !='0')
					{
						LcdWrCmdorDat(1,*str2++);
					}
					str2 = table2;
					TI =1;
					CmdIndex=0;break;
				}		
				default:break;
			}	 
	 	 }
	 }
}
void LcdBusy()
{
	LcdDB=0xFF;
	LcdRS = 0;
	LcdRW = 1;		
	LcdEN = 1;
	while(LcdDB&0x80);
	LcdEN = 0;
}
//把写命令和写数据语句合二为一,第一个参数为0表示写命令,1表示写数据,
第二个参数表示相应的数值
void LcdWrCmdorDat(uchar cmdordat,uchar cmddat)
{
	LcdBusy();
	if(cmdordat==0)
		LcdRS = 0;
	else
	   LcdRS = 1;
	LcdRW = 0;
	LcdDB = cmddat;
	LcdEN = 1;
	LcdEN = 0;	
}
void Lcd_Init()
{
	LcdWrCmdorDat(0,0x38);
	LcdWrCmdorDat(0,0x0C);
	LcdWrCmdorDat(0,0x06);
	LcdWrCmdorDat(0,0x01);
}
void LcdShow(uchar x,uchar y)
{
	uchar addr;
	if(0==y)
	 	addr = 0x00+x;
	else
	 	addr = 0x40+x;
	LcdWrCmdorDat(0,addr|0x80);
}
void UART_Init()
{
	EA=1;
	SCON = 0x50;
	TMOD &=0x0F;
	TMOD |=0x20;
	TH1 = 256-(12000000/12/32)/2400;
	TL1 = TH1;
	ET1 =0;
	TR1 = 1;
	ES=1;	
}
void InterruptUART() interrupt 4
{
	if(RI)
	{
	 	RI = 0;
		CmdIndex = SBUF;
		UartFlag = 1;
	}
	if(TI)
	{
		TI=0;
		if(*str1 !='0')
		{
			SBUF=*str1++;		
		}
		if(*str2 !='0')
		{
			SBUF=*str2++;		
		}	
	}		
}
  1. 在case1语句中,有两句“str1 = table1;”,第一句表示table1的首地址赋值给指针变量str1,用于显示在1602,第二句表示table1的首地址再重新赋值给str1,用于显示在串口助手。第二句不能漏写,这是因为在第一次赋值操作后指针的位置指向了字符串的末尾,所以没有第二句“str1 = table1;”的 话无法在串口助手接收区准确显示,第二句相当于复位,读者可以自行屏蔽任意一句看实验现象。

2、本实例将串口知识和液晶屏操作结合起来,同时也看到将写命令函数和写数据函数合二为一,精简程序。

(四)小结

本节通过两个实例来说明液晶屏1602的使用方法,从项目二到本项目读者发现程序行数逐渐增多,甚至以后可以上千行,同时发现本项目中的串口程序跟上一个项目的串口程序基本一致,那是不是可以通过模块化将功能相同的程序放在一起,需要时加入文件进行调用就行,答案是肯定的,在下一项目中讲解程序模块化。

可以在B站观看相关视频!!

哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值