绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)

前言:

近几天为了写LCD1602显示与运行的深度原理大量翻阅资料,研究到LCD1602指令集时发现碰到硬茬子了—

1.CSDN上关于LCD1602指令集的文章大多为水文,直接Pia的一手就是图一或图二加上一大堆臭长代码再随便和点“水泥”集成一块就草草上传,从中吸收的有用东西很少 。

2.网上所传的指令集(应该就图一二两种没跑了吧)中概念晦涩难懂—LCD的光标,AC等等概念是什么,长什么样,有什么用,操作描述不够清晰—光标,屏幕的移动是怎样移动,移动是什么原理实现的,光标,屏幕,AC之间有什么关系,它们的移动与DDRAM,CGRAM有什么联系?

在今天这篇文章,这些问题将得到一一解决,至于网传指令集上述问题是可以理解的,毕竟它们的来源是某些好心人或厂商对HD44780U芯片进行相应总结概括,从而流传到网上

所以:

我溯源求根,找到了控制LCD1602运行的核心芯片:HD44780U,自己手撕芯片手册,同时往该芯片方向查找资料,力求能对LCD1602指令集有深刻清晰的了解。

通过对LCD1602指令集的深入讲解,我们能进一步LCD1602显示及运行的原理,进而HD44780U这款芯片有一个初步的了解和认识,这样能更好的为阅读我的下一篇博客打下牢固的知识基础。

LCD1602指令集总览:

注意:本文章将基于图一进行讲解,现在你所看到的图一是有经过我修改的,修改的地方在此先点出,避免漏看指令集里面补充的内容:

1.清屏指令除了清除DDRAM和AC,光标复位还有把I/D(指令集第3个输入方式设置的DB1)变为1且进入S的模式不变的效果,同时若清屏前发生了移位,显示屏会恢复原始状态(即会显示消失,光标复位)

2.输入方式设置指令画面的左移还是右移已经标清

同时:图二的显示开关控制指令中光标闪烁的开关颠倒(应该是1闪0不闪

一般来说:网上指令集图就有以下三种没跑了吧?

图一:所有指令集成一张图

特点:写指令的十六进制值已标出,非常直观简单

但不足的是DB7~DB0对应的符号没给出,不利于交流沟通

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第1张图片

图二:普中开发资料中的指令集

特点:写指令的十六进制值没有给出,需自己计算

但DB7~DB0对应的符号有给出,有利于交流沟通

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第2张图片

 图三:HD44780U官方指令集

特点:绝对官方权威,真实可靠

 绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第3张图片

 以上就应该是网上流传的三种主流指令集图了,接下来就是进行各个指令的深入讲解了

LCD1602指令集深入讲解:

注意:我不会对所有指令集的所有指令进行讲解(因为没必要),我只挑一些重要且难以理解的指令进行解释(但放心,任何事物都是有联系的,更何况都在同一指令集中的,我在解释挑出来的指令时,会给出一些代码现象,里面的代码有涉及那些没挑出来到的指令,就足够加深你的这部分指令的理解了)

1.清屏指令

就是清除DDRAM和清除AC值,清除了之后光标就会复位。

那么什么是DDRAM,什么是AC,什么是光标,为什么把DDRAM和AC清除后屏幕就能实现清屏的效果呢?这三What一Why待我慢慢展开:

三个What:

1.DDRAM

显示数据随机存储器 (Display Data RAM,简称DDRAM)

通俗地讲:里面存放待显示字符在屏幕位置的信息(地址)(简单地说就是我们告诉DDRAM写入的字符要在屏幕的哪里显示,DDRAM就会根据我们告诉的信息在屏幕相应位置显示字符)

#   那么我们如何告诉DDRAM我们想要在屏幕哪里显示的信息呢?

答案就是指令集的第八个指令

 

#   如何告诉的问题我们已经解决,那么我们如何向DDRAM表达我们想要在屏幕哪里显示的信息呢?

是不是要在屏幕第一行第一个位置显示字符,就是往DDRAM里面写入0x00呢?按照常理来说确实如此,但是,因为不同指令集要区分的原因,实际并不是写入0x00(这句话怎么理解呢?)

有没有注意到DB7是固定为1,你再好好看看指令集(图一)中九条指令是不是肯定有一位固定为1?哎,恭喜你发现细节所在:LCD1602就是通过对某些位固定为1来区别我们给它的指令是哪一条。

所以,要在屏幕第一行第一个位置显示字符,就要向DDRAM中写入0x80(1000 0000)

要在屏幕第一行第八个位置显示字符,就要向DDRAM中写入0x87(1000 0111),以此类推。

但注意:这个类推仅能推到第一行的第40个位置(即0xa7(1010 0111)),更详细见下一篇博客。

这样你就学会如何告诉DDRAM我们想要在屏幕的哪里显示了。

2.AC

地址计数器(Address Counter,简称 AC)

 通俗地讲:里面存放DDRAM和CGRAM中要显示字符位置(地址)的信息,并且在我们告诉LCD1602指令后,AC会自动为地DDRAM和CGRAM分配地址(加一或减一)

至于加一还是减一,可以通过指令集的第三个输入方式设置进行设置。

3.光标

一般的光标想必大家都见过,就是在聊天框中那个一闪一闪的竖线,而LCD1602的光标长什么样呢?我们翻阅HD44780U可以寻得答案:

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第4张图片

从图中我们可以看出光标就是在点阵(就是图中方格组)最下面的一行全亮的方格组,光标的闪烁就是在光标上的方格组进行亮灭循环,而设置光标的显示与闪烁的指令就是指令集第四个显示开关控制。

一个Why:

首先,我再提一个更深的问题:是把DDRAM清除后,AC,光标跟着清除,还是说DDRAM的清除,AC的清除,光标的复位是三个同时进行的互不相干的过程?

下面给出一段代码现象,便于我们巩固上面的内容,引发我们的思考,进而了解DDRAM,AC,光标的关系,以便我们解决这个棘手的Why

代码:
#include "regx52.h"
#include "LCD1602.h"
#include "delay.h"

unsigned char ac_add;//接收AC值的变量
void main()
{
	//LCD1602的初始化
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0f);//显示开,光标开,闪烁开
	LCD_WriteCommand(0x01);//光标复位,清屏
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	while(1)
	{
			LCD_WriteCommand(0x80);//告诉DDRAM我们要在屏幕的第一行第一个位置显示
			LCD_WriteData(0x61);//告诉CGROM要显示的字符,根据ASCII码表,0x61为小写'a'
			ac_add=ac_add_read();//接收AC值
			LCD_ShowHexNum(1,6,ac_add,2);//显示AC值
			while(1);//把程序卡死,避免重复上面代码
		/*注:LCD写指令和显示十六进制代码不会的去看江科大
					也可以去看我的下一篇博客
					读取AC值代码内容下面有给出
		*/			
	}
}


/*读取AC值(代码原理可先不管,要了解原理的见下一篇博客)
unsigned char ac_add_read()
{
	unsigned char address;
	LCD_DataPort=0xff;//清除上一次写指令缓存的数据
	LCD_RS=0;
	LCD_RW=1;
	do{
		LCD_EN=1;
		LCD_Delay();
		address=LCD_DataPort;
		LCD_Delay();
			LCD_EN=0;
		}	while(address&0x80);//等到BF为0跳出
	return address;//返回读到的AC数据
}
*/

代码现象:

LCD1602指令集代码现象一

代码现象解析:

1.首先回忆一下,上面三个What中光标是出现在字符的哪里?是在字符本身的最下面是吧。而我们看到在第一段视频中,光标居然出现在字符a的后面,这究竟是怎么回事呢?

我们先来揭晓DDRAM,AC值和光标的三者关系:

在上面的介绍,我们已经得知:AC存放的值就是DDRAM中的地址信息(当然也有CGRAM的信息),那光标和DDRAM,AC有什么关系呢?

让我们再一次地翻阅HD44780U,从图中可得:

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第5张图片

由此我们可以得知:光标位置是于AC中存放的DDRAM的地址信息有关。

那么,按道理来说:我们向DDRAM中写入0x80,那光标位置不应该是在0x80(即第一行第一个字符a的位置)下面吗?怎么会出现在字符a的后一位(0x81)呢?

答案就是在LCD1602的初始化中!

请注意这条指令:LCD_WriteCommand(0x06);//数据读写操作后,AC自动加一,画面不动

而且还要注意:光标位置是由AC中的DDRAM地址信息决定,而不是由DDRAM中的地址信息决定

(这是一个很细微的差别)

所以:在写0x80指令后,AC自动加一,AC中DDRAM地址信息变为0x81,光标就自然而然地跑到字符a后一位了。

为了回答这个棘手的Why问题,我们还要注意:AC不仅能存放地址信息,还能分配地址信息。

这样:我们就可以回答这个棘手的Why问题:

清屏指令会先把AC里面DDRAM的地址信息置0,从而使DDRAM里面的地址信息为0,同时因为AC里面DDRAM的地址信息为0,光标也就会跟着复位。

顺便提一嘴:其实在HD44780U中,清屏指令并不是像最上面的图一这般描述,我们可以去看图三,官方的说法是:清除整个显示器并把AC中DDRAM的地址置0.刚才我只是回答了清屏函数如何使光标复位,还没有回答是怎么清除显示屏。(鉴于篇幅问题,我这里就简单的说:实现原理是对屏幕的每一个位置都显示空格(即告诉DDRAM所有位置都要显示,告诉CGRAM所有位置都显示空格(ASCII码值为0x20))

在HD44780U同样有相应介绍:

我们也可以来验证这三者的关系:

那么如何验证呢?那就要用到指令集的第九条读忙标志和AC值指令了。

那么该指令的AC读取是读取什么时候的AC值呢?翻阅HD44780U,我们可得:

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第6张图片

所以:读取的AC值是读取前的上一条指令执行后AC中DDRAM的地址信息。

根据第二段视频(加入了读取和显示AC值),我们可以得到DDRAM里面的值为0x00,写指令后AC自动加一,AC中DDRAM地址信息变为0x01,所以视频中显示出01,同时因为LCD_ShowHexNum函数有设置DDRAM地址再加上写完AC自动加一,光标最后就在01后面。

与归位指令的区别

这里注意:清屏和归位指令是存在差别的,前者是清除整个显示器并把AC中DDRAM的地址置0,后者是仅把AC中DDRAM的地址置0。

下面是在代码最后加上LCD_WriteCommand(0x02)效果:光标跑到字符a下面

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第7张图片

好了,终于终于把清屏指令相关细节到此就告一段落了!!!

2.输入方式模式指令

引入:

先别急着开始深入的讲解,让我们再看看指令集,有没有惊奇地发现:指令集第五个光标,画面位移指令好像也是对AC和画面进行操作,那么两者本质是否有相同呢?两者显示效果是否一样呢?

然后光标和移屏分别作用是什么样的?同时作用又是怎么样的?

为了解决这些问题,我们必须先了解屏幕和光标移动的原理。

屏幕和光标移动原理:

1.光标的移动原理上面都介绍过了,相必大家经过上面的学习,都了解了一二,简单地讲,AC中DDRAM的地址信息变化(加一减一)则光标就会相应地右移左移。

2.而关于屏幕的移动原理,都看到这里了,也应该知道要干嘛了吧—对,让我们再次翻阅HD44780U资料,从中我们可得:

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第8张图片

由图我们可以看出:本来屏幕第一二行的第一个位置是对应DDRAM的0x00和0x40的地址的,屏幕第一二行最后一个位置是对应DDRAM的0x27和0x67的地址的,但经过左移后,屏幕位置对应的DDRAM地址发生移位!由此我们可得:其屏幕移动原理是对屏幕对应的DDROM地址的固定位置进行相应的移动

步入正题:

由上一段代码的现象视频,我们已经初步了解AC自动加一的显示效果(AC自动减一的效果类推可得),那么屏幕的移动和AC自动加减一同时作用的显示效果究竟是怎么样的呢?

以下给出代码及其现象,我们可以从中了了解输入方式模式和清屏指令中的细节所在。(对,在此还涉及到清屏指令)

代码:
#include "regx52.h"
#include "LCD1602.h"
#include "delay.h"

unsigned char ac_add;//接收AC值的变量
void main()
{
	//LCD1602的初始化
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0f);//显示开,光标开,闪烁开
	LCD_WriteCommand(0x07);//数据读写操作后,AC自动加一,画面左移
    //LCD_WriteCommand(0x05);//数据读写操作后,AC自动减一,画面右移
    //第二段视频LCD_WriteCommand(0x05)注释取消,对LCD_WriteCommand(0x07)进行注释
	LCD_WriteCommand(0x01);//光标复位,清屏
	while(1)
	{
			LCD_WriteCommand(0x84);//告诉DDRAM我们要在屏幕的第一行第五个位置显示
			LCD_WriteData(0x61);//向DDRAM第五个位置写入'a'
			Delay(1000);
			LCD_WriteData(0x62);//向DDRAM第六个位置写入'b'
			Delay(1000);
			ac_add=ac_add_read();
			LCD_ShowHexNum(1,10,ac_add,2);
			while(1);//把程序卡死,避免重复上面代码			
	}
}
代码现象:

LCD1602指令集代码现象二

代码现象解析:

看完三段视频,我们可以得出屏幕的移动和AC自动加减一同时作用的显示效果就是:

光标"固定"不动而显示的内容相应移动。

运用原理回答现象:

让我们把目光放在第一段视频(输入方式为AC加一,画面左移)上,根据前面学习的知识,我们来探讨一下,为什么光标为保持"固定"不动呢?光标不动的话,其AC值是否会改变呢?

下面我给出一个场景,你可能就了解为什么光标不动了:

绝对的宝藏文章:深入讲解LCD1602指令集(附上代码演示及现象讲解)_第9张图片

站点就代表屏幕的位置,车厢序号相当于DDRAM中的地址信息,(一般站点和序号是相对应的)字符a相当于上车的a人,光标相当于一条狗,它会跟随在最后一个上车人的后一节车厢(这个跟随相当于AC自动加一),而在人上车后火车向前移动一节车厢的距离(相当于屏幕的移动

所以第一段视频可解读为a人在5车厢上车,狗就在6车厢,此时火车前移,a人(5车厢)就跑到站点4(屏幕第四位置),狗(6车厢)就跑到站点5(屏幕第五位置)了;然后b人就在6车厢上车,狗就跑到7车厢,火车再前移,a人(5车厢)就跑到站点3(屏幕第三位置),b人(6车厢)就跑到站点4(屏幕第四位置),狗(7车厢)就跑到站点5(屏幕第5位置),然后c人在7车厢上车,狗到8车厢,火车前移,最后发现狗依旧是呆在站点5的位置(即屏幕第5位置)

这就是为什么光标不动的原理,而且根据上面的解读·,我们可以知道虽然光标不动,但其AC值是在变化的。而且我们可以在读取AC值那里得到验证:视频中显示AC值为0x06(即在DDRAM的第七个位置,则对应7车厢)

清屏指令的隐藏效果:

让我们把目光放在第二段视频上,经过我们上面的学习,按道理来说,第二段视频的显示效果不应该是光标不动,而显示内容向右移动吗?怎么实际情况却是跟第一段视频一样呢?

我苦寻许久,终于发现问题出现在清屏指令的隐藏效果上:清屏指令除了清除DDRAM和AC,光标复位还有把I/D(指令集第3个输入方式设置的DB1)变为1,进入S的模式不变的效果(这个就是我上面指令集补充的内容)。

再看看之前的代码,你就会发现在LCD1602初始化中,输入方式模式代码执行是先于清屏指令的,这就会导致你设置完AC减一和画面右移后,被后执行的清屏指令重新设置成AC加一和画面左移!!!

所以解决方法是:把清屏指令放在输入方式模式前!就有了第三段视频所示现象。

3.光标画面位移

引入:

接下来就是光标画面位移部分了,了解完它,我们就才可以回答输入方式模式的"引入"中所提的问题。我们先来看看光标画面位移有什么效果:AC加减一(光标位移)以及移屏且光标不动。那么这里光标不动是否意味着就是AC值不变呢?经过上面的学习,我们已经知道光标就是于AC有关,光标不动AC就不变,但事实是否如我们推测的这般呢?本着大胆推测,小心求实的态度,那就让我们再来看一段代码和代码现象,从而解决这些问题,深入了解这一指令。

代码:

#include "regx52.h"
#include "LCD1602.h"
#include "delay.h"

unsigned char ac_add;//接收AC值的变量
void main()
{
	//LCD1602的初始化
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0e);//显示开,光标开,闪烁关
	LCD_WriteCommand(0x01);//光标复位,清屏
	LCD_WriteCommand(0x06);//数据读写操作后,AC自动加一,画面不动
	
	while(1)
	{
			LCD_WriteCommand(0x89);//告诉DDRAM我们要在屏幕的第一行第十个位置显示
			LCD_WriteData(0x61);//向DDRAM第五个位置写入'a'
			while(1)//把程序卡死,避免重复上面代码	
			{
				Delay(300);
				//LCD_WriteCommand(0x14);//光标右移
				LCD_WriteCommand(0x1c);//画面右移,光标不动
			}
            
            /*验证AC值部分代码
            LCD_WriteCommand(0x80);//告诉DDRAM我们要在屏幕的第一行第十个位置显示
			LCD_WriteData(0x61);//向DDRAM第五个位置写入'a'
			Delay(300);
			LCD_WriteCommand(0x1c);
			Delay(300);
			LCD_WriteCommand(0x1c);
			Delay(300);
			LCD_WriteCommand(0x1c);
			ac_add=ac_add_read();
			LCD_ShowHexNum(1,10,ac_add,2);
			while(1);//把程序卡死,避免重复上面代码*/			
	}
}

代码现象:

LCD1602指令集代码现象三

代码现象解析:

引入的答案:

由第一段视频,我们可以看出在画面连续移动三次后,AC的值依旧为0x01,这就说明我们的推测是正确的,光标不动则AC值不变(当然这里的不动是指AC中DDRAM地址不变),由视频可以看出移屏且光标不动指令的显示效果是显示内容和光标一块移动

隐藏的细节:

通过第二三段视频,你们有没有发现光标是移动到第一行的尽头就跳到第二行,而画面是两行同时移动,那么输入方式模式下的光标画面移动是在两行还是在所在行呢?我们可以看看第四段视频,发现怎么有点看不懂是怎么回事,而且翻阅HD44780U也没有相应说明,所以我就谈谈我的推测:

输入方式模式下的光标画面移动其实是同光标画面移位指令的,因为写入四个a后光标已经经过0x00,跳到下一行的0x40(这时候就是在屏幕的第五位置),然后在第二行开始写入a直到又跳到第一行。

如果有大佬知道相关的确切信息,恳请大佬赐教!

这样想必你已经对输入方式模式的引入中的问题已经有了答案,那么这篇文章就到处结束了。

笔者有话:

1.这篇文章肝了我四天时间,每每想到有些文章随便一水,就有不少的阅读量点赞量,而自己呕心沥血铸成的文章却无人问津,实属心痛!恳请大家不要吝啬手中的点赞(能点点关注就更好了)

2.写到现在8000字,我想引用我翻阅过的一篇文章末尾让我会有感触的一段话:

“我感觉网上很多资料介绍得不够详实,而且千篇一律,作者写文章仿佛有个前提,就是假设读者已经具备一定的基础知识和概念了,所以介绍得就不是特别细致,其实进行学习的人往往就是没什么基础才来看这些文章,导致最后稀里糊涂。看了很多国外的书籍,作者把读者看做“小白”,从基础讲起,使得读者的理解“渐入佳境”,达到自学而且学会的目的,希望咱们国内的同道有所借鉴”

http://news.eeworld.com.cn/mcu/ic622204.html

你可能感兴趣的:(51单片机,c语言,显示器)