一丶引脚说明
Lcd12864(带字库版本,ST7920为主芯片驱动)
(正面)
?
(反面)
由于是带字库版本,与不带字库版本有出入,其部分引脚有不同
二丶接口时序
Mpu从模块写入资料
(时序图很重要,能够保证命令正确写入ST7920芯片,让12864屏幕正常使用)
?
??
这里我们采用并行模式,PSB,RS全部置高(PSB,RS所对应的引脚置高电平)
(
设
P2.6
为
RS
对应口,
P2.5 RW
,
P2.7 E
,
D0-D7
接在
P0
串口,
PSB P3.2,RST P3.3??vcc
接
5v?
下面统一采取这种接法)
写入指令:
CLR P2.7(使能信号先置低电平) CLR P2.6(RS低电平) CLR P2.5(RW低电平) LCALL DELAY1(延时一段时间 对应时序图TAS) CLR P2.7(使使能信号置低电平) SETB P2.7(使能信号置换高电平) MOV P0,#08H(输入08#对应指令) LCALL DELAY1(延时一段时间,对应TC) CLR P2.7 LCALL DELAY1 SETB P2.7 LCALL DELAY(此处延时大于60ms相当于读忙了) 这个程序段可以看作一个指令 写入数据: 同上? ???把CLR P2.6,改为SETB P2.6即可,其余相同 其中延时时间如上图,只要超过最小值即可,最好不要过长了。 ? 三丶对12864的所有操作概括起来有4种: 1)、读忙状态(同时读出指针地址内容),初始化之后每次对12864的读写均要进行忙检测。(我每次写一次完整指令都延时一段时间,相当于读忙了,下面就不再赘述) 2)、写命令:所有的命令可以查看指令表,后续讲解指令的详细用法。写地址也是写指令。 3)、写数据:操作对象有DDRAM、CGRAM、GDRAM。 4)、读数据:操作对象也是DDRAM、CGRAM、GDRAM。
忙标志:BF
BF标志提供内部工作情况。BF=1表示模块在内部操作,此时模块不接受外部指令和数据。BF=0时,模块为准备状态,随时可接受外部指令和数据。
利用STATUS READ指令,可以将BF读到DB7总线,从检验模块之工作状态。
那我们要如何在12864上面显示图像或者字符呢? 下面就来介绍一下DDRAM 上图所对应的四个区是DDRAM所对应的在ST7920上面的地址,其中能够显示的只有壹贰区,那么我们只需要把字符所对应的数据写进DDRAM中,那么就可以让12864显示出我们想要的字符。 其中80H到87H为第一行 90H到97H为第二行(上半屏) 88H到8FH为第三行 98H到9FH 为第四行(下半屏) 下面我们来试着编写一下程序 (这几个步骤很重要,学会了步骤就学会了12864的使用) *网上有带字库12864详细指令集,这里我就不打出来了,注意,我编写的输入指令的指令码是只对应DB0到DB7的,和下面绿框那种一样是没有RW,RS,E位的,那些位我在编指令的时候就设定好了。 (1)功能设定
?
(2)清屏指令
?
(3)进入设定点
?
(4)显示状态开关设置
?
?
上面为初始化程序
(
绿色框代表指令码,可以输入二进制也可以输入
16
进制,只要保证
p0
口电平信号相对应就行了)
?
? 对应指令1000 0000也就是给p0输入#80H DDRAM从80H开始输入指令,12864的DDRAM横向地址会自动读取表中的数据,每输入两个16位的数据如#41H,#41H,地址会自动从80H跳到81H以此类推,网上有详细的运作方式,可以上网查阅。 ? ? 上图很明显,我输入了三个#41H进80H中,12864就自动将第三个41#也就是大写A写进81H中,这里提一下,带字库DDRAM12864一个地址可以写一个汉字两个字母(厂家规定的,无法修改) ?
? 这里可以用查表的方式,一直往DDRAM写入数据,但是写进去的数据是这样的,先填满第一行,再填满第三行,再填满第二行,再填满第四行 ,那我这里就采用查不同表的方式,设DDRAM起始地址为80H的时候只查16个数据,再设90H再查16个数据,以此类推。 ? 下面我给仿真图和指令 (注意,这里仿真要带字库的12864,不带字库的不能成功仿真,带字库的模型上网搜) ?
? 我设置的引脚和开发板的设置引脚一致,直接插上去使用了,大家使用的时候可以看看开发板的原理图,一般都是一样的,不行就改引脚 ? 那普通的字符可以了,那怎么画图呢,广告的需求肯定不能只有字符,还要有图案,那怎么定制呢。下面就来介绍另一个RAM,CGRAM,也叫绘图RAM。 ? ? 这些都是点阵,绘图RAM就是给这些点阵置1或置0,可以看到其实它本来是32行×256列的,但是分成了上下两屏显示,每个点对应了屏幕上的一个点。要使用绘图功能需要开启扩展指令。然后写地址,再读写数据。 ? 这幅图就是CDRAM对应的地址,其实也是12864实际的样子,水平位址对应着DDRAM的80H到8FH,一共16格,每8格对应一个半屏,共两个半屏。一格一行一共对应了16位的二进制数字那么半个屏幕就是16*8(共8格,一格16列)*32(32行),一个半屏32行,一个整屏就是16*8*32*2=128*64所以称之为12864屏幕(很重要,一定要理解了) ? 那看到上图bit0和bit15,可以知道,一个地址是先从高位读到低位置,看起来很麻烦。但是后面取字模软件可以帮你修改,那我这里向CGRAM输入还是像DDRAM一样,当然,CGRAM和DDRAM的地址是一样的,一旦读够16个二进制数字,也就是两个16进制数,也就是两个字节,就会自动加一,DDRAM从80H到81H,那CGRAM对应的地址也是一样下面会提到。这是x坐标,但是y坐标不会,y坐标要你输入了16个数值后自己修改,也就是写完一行后要自己改。 (我这里写得不太清楚,LCD12864点阵型液晶显示器总结 - 吃不了就兜着走 可以看一下这个作者写的他写的很详细) ? ??GDRAM的读写: ??首先说明对GDRAM的操作基本单位是一个字,也就是2个字节,就是说读写GDRAM时一次最少写2个字节,一次最少读2个字节。 ? ??写数据:先开启扩展指令集(0x36),然后送地址,这里的地址与DDRAM中的略有不同,DDRAM中的地址只有一个,那就是字地址。而GDRAM中的 地址有2个,分别是字地址(列地址/水平地址X)和位地址(行地址/垂直地址Y),上图中的垂直地址就是00H~31H,水平地址就是00H~15H,写 地址时先写垂直地址(行地址)再写水平地址(列地址),也就是连续写入两个地址,然后再连续写入2个字节的数据。这里列举个写地址的例子:写GDRAM地址指令是0x80+地址。被加上的地址就是上面列举的X和Y,假设我们要写 第一行的2个字节,那么写入地址就是0x00H(写行地址)然后写0x80H(列地址),之后才连续写入2个字节的数据(先高字节后低字节)。 下面说说指令,其实和DDRAM基本一致,就是写写入CGRAM指令要先写x坐标,再写y坐标,其余步骤基本一样,但是查表要注意了,因为我编写的程序RA寄存器只能读到256位置,那我要分成4个表才能读完整个屏 ? 下面给出指令和仿真。 ? 下面拉屏和闪烁我就简要说一下思路 ? 闪烁我是通过不停地睡眠唤醒来实现,这里睡眠指令要开拓展指令,然后写一个循环和跳出循环的指令,通过两个按钮或者一个开关实现,也可以通过反白来实现,其实会写步骤就完成大部分了,剩余的其实都是不断地调试和检查错误。 ? 拉屏(仿真跑不了卷屏) 功能:SR=1: AC5~AC0 为垂直卷动地址,SR=0:。 那这里要说一下了,怎么实现拉屏呢。其实12864里自带了一个指令叫做卷动地址设置,它规定了只会向上滚动,而因为一共是有64列的但是上面CGRAM为什么只有32行呢,那是因为其中 A0到A7对应第一个屏中实际的第三行,其实它们也是可以输入数据的,只是没使用卷动指令时无法看到,那我假设 ? 设置拉屏的行数为16吧,那80H就到了原来的B0哪里,一整行都替换了,然后A0就到90H一行,90H就到80H,其实就是一行推着整个半屏都在运动,但是12864的一行实际上是80H到8FH,上下半屏都在动,但不会相互影响 ? 实际的一行:
| ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? |
|
| ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? |
|
| ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? |
|
| ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? |
|
所以一整个屏都会移动 ? ? (下图是半个屏,下面因为我已经实现全屏不方便改了,就只看上半屏吧) ? 那我要怎么实现全屏滚动呢,那么A0和B0行就开始有用了,那我只要将80H 对应的行,假设是CGRAM中的y??00H,x80H向上移,那我就设置成看起来从下半屏的最下面开始往上面升起,那就是Y 32H??X??88H,这一行。那这就开始有思路了,那我吧80H到88H的数据一起输入到A8到AFH区域不就行了吗,以此类推90H区到98H的就输入到B8到BF中,88H的输入到A0,98H的输入到B0就ok了。 下面试一下 成功了(此处要说明一下,CGRAM的y坐标是到0到63,是可以设置到A0和B0位置的,网上教程说的不清不楚的,花费了我很多精力,上面说y是0到31是因为不设置卷动看不见,所以不显示) ? 说明书心得: 说实话,这次我选的课题确实不算难度大的那一批了,网上也有很多的教程,但是由于要全部用汇编实现,工作量大了很多,我以为很快就能成功,没想到过程真的是千辛万苦,一个是仿真,可以说网上带字库的仿真几乎没有,那个Proteus的带字库模型还要自己在网上买,不断调试,不断测试才终于和开发板的一致。 不断在网上搜索教程,看了半天居然是不带字库的,差点晕倒在桌子上。后面终于在网上找到ST7920的说明书,一看和天书一样晦涩难懂,没办法,一个个不会的知识点慢慢学,实在不行就先看了1602的,这里很感谢知乎的北鸮,他写的一篇文章超详细讲解,LCD1602电路与驱动 真的很详细清楚 然后是烧录的问题,烧录还好,因为网上的教程很详尽明白,没什么问题。然后就是不断地写程序,调仿真一直反复。然后不断地看网上作者们写的程序,吐槽一下,网上都是c语言的程序,汇编几乎没有,少有的几个甚至有语法错误,看的时候都有点头痛,那只好看着c语言思路来写汇编了,感谢网络上各位热心的作者,他们无偿地分享知识,而且教的很棒,真的非常感谢他们。 ? ?
单片机源程序如下:
- AJMP MAIN
- TAB1:
- DB #41H,#41H,#41H,#41H,#41H,#41H,#41H,#41H,#41H,#41H,#41H,#41H,#41H,#41H
- ,#41H,#41H
- TAB2: DB "元旦快乐大家好"
- //带字库的很方便,直接按这个格式输入汉字就行 一行7汉字,没有字的地方记得输入空格,空格占半个地址位
- TAB3: DB "我你不合格 ",07FH,05FH,06FH
- TAB4: DB "哦哦哦哦哦 ",07FH,05FH,06FH? ?? ?? ?? ?
- //想改字符汉字直接改表就行,汉字要打在引号里面
- MAIN:
- SETB P3.2
- LCALL DELAY1
- CLR??P3.3
- LCALL DELAY1
- SETB P3.3
- LCALL DELAY1
- //初始化
- CLR P2.6
- CLR P2.5
- MOV P0,#30H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //写入#30H对应的指令
- CLR P2.7
- CLR P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,#08H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //写入#08H的指令
- CLR P2.7
- CLR P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,#0CH
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //开屏
- CLR P2.7
- CLR P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,#01H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //清屏指令
- CLR P2.7
- CLR P2.6
- ACALL DELAY1
- CLR P2.5
- MOV P0,#80H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //光标设置
- CLR P2.7
- CLR P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,#80H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //设置DDRAM地址
- CLR P2.7
- SETB P2.6
- ACALL DELAY1
- CLR P2.5
- MOV R2,#15D
- MOV DPTR,#TAB1
- LOOP:MOV A,R0
- INC R0
- MOVC A,@A+DPTR
- SETB P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,A
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- DJNZ R2,LOOP
- //查表指令
- CLR P2.6
- ACALL DELAY1
- CLR P2.5
- MOV P0,#90H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //设置DDRAM地址
- MOV R2,#15D
- MOV DPTR,#TAB2
- MOV R0,#00H
- LOOP1:MOV A,R0
- INC R0
- MOVC A,@A+DPTR
- SETB P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,A
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- DJNZ R2,LOOP1
- //查表指令
- CLR P2.6
- ACALL DELAY1
- CLR P2.5
- MOV P0,#88H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //设置DDRAM地址
- MOV R2,#15D
- MOV DPTR,#TAB3
- MOV R0,#00H
- LOOP2:MOV A,R0
- INC R0
- MOVC A,@A+DPTR
- SETB P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,A
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- DJNZ R2,LOOP2
- //查表指令
- CLR P2.6
- ACALL DELAY1
- CLR P2.5
- MOV P0,#98H
- LCALL DELAY1
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- //设置DDRAM地址
- MOV R2,#15D
- MOV DPTR,#TAB4
- MOV R0,#00H
- LOOP3:MOV A,R0
- INC R0
- MOVC A,@A+DPTR
- SETB P2.6
- LCALL DELAY1
- CLR P2.5
- MOV P0,A
- SETB P2.7
- LCALL DELAY1
- CLR P2.7
- LCALL DELAY1
- DJNZ R2,LOOP3
- //查表指令
- ? ?? ?? ?? ?
- DELAY1:
- MOV??R7,#100? ?;单周期指令(1us)
- D1:??
- ??MOV??R6,#100??;单周期指令(1us)? ?? ???
- ??DJNZ R6,[? ?? ???DISCUZ_CODE_3? ?? ???]nbsp;? ???;双周期指令(2us)//该指令自身执行R6次? ?? ???
- ??DJNZ R7,D1? ???;双周期指令(2us)//D1执行R7次??
- RET
- END
复制代码
|