DDRAM:
笔者使用的这块 12864 内部有 4 行×32 字节的 DDRAM 空间。但
是某一时刻,屏幕只能显示 2 行×32 字节的空间,那么剩余的这些空间
呢?它们可以用于缓存,在实现卷屏显示时这些空间就派上用场了。
DDRAM 结构如下所示:
80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、8EH、8FH
90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、9EH、9FH
A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、ADH、AEH、AFH
B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、BDH、BEH、BFH
地址与屏幕显示对应关系如下:
第一行:80H、81H、82H、83H、84H、85H、86H、87H
第二行:90H、91H、92H、93H、94H、95H、96H、97H
第三行:88H、89H、8AH、8BH、8CH、8DH、8EH、8FH
第四行:98H、99H、9AH、9BH、9CH、9DH、9EH、9FH
说明:红色部分的数据归上半屏显示,绿色部分的数据归下半屏显示。
一般我们用于显示字符使用的是上面两行的空间,也就是 80H~8FH,
90H~9FH,每个地址的空间是 2 个字节,也就是 1 个字,所以可以用于
存储字符编码的空间总共是 128 字节。因为每个汉字的编码是 2 个字节,
所以每个地址需要使用 2 个字节来存储一个汉字。当然如果将 2 个字节
拆开来使用也可以,那就是显示 2 个半宽字符。
DDRAM 内部存储的数据是字符的编码,可以写入的编码有 ASCII
码、GB2312 码、BIG5 码。笔者使用的 12864 字库貌似不太全,字符“数”
都无法显示,而是显示其他字符。如果显示长篇汉字文章就不太适合吧。
DDRAM 数据读写:
所有的数据读写都是先送地址,然后进行读写。对 DDRAM 写数
据时,确保在基本指令集下(使用指令 0x30 开启),然后写入地址,
之后连续写入两个字节的数据。读数据时,在基本指令集下先写地址,
然后假读一次,之后再连续读 2 个字节的数据,读完之后地址指针自动
加一,跳到下一个字,若需要读下一个字的内容,只需再执行连续读 2
个字节的数据。这里的假读需要注意,不光是读 CGRAM 需要假读,读其
他的 GDRAM、DDRAM 都需要先假读一次,之后的读才是真读,假读就是
读一次数据,但不存储该数据,也就是说送地址之后第一次读的数据时
错误的,之后的数据才是正确的。(dummy 为假读)
关于编码在 DDRAM 中的存储需要说明事项如下:
1)、每次对 DDRAM 的操作单位是一个字,也就是 2 个字节,
当往 DDRAM 写入数据时,首先写地址,然后连续送入 2 个字节的数据,
先送高字节数据,再送低字节数据。读数据时也是如此,先写地址,然
后读出高字节数据,再读出低字节数据(读数据时注意先假读一次)。
2)、显示 ASCII 码半宽字符时,往每个地址送入 2 个字节的
ASCII 编码,对应屏幕上的位置就会显示 2 个半宽字符,左边的为高字
节字符,右边的为低字节字符。
3)、显示汉字时,汉字编码的 2 个字节必须存储在同一地址
空间中,不能分开放在 2 个地址存放,否则显示的就不是你想要的字符。
每个字中的 2 个字节自动结合查找字模并显示字符。所以,如果我们往
一个地址中写入的是一个汉字的 2 字节编码就会正确显示该字符,编码
高字节存放在前一地址低字节,编码低字节存放在后一地址高字节,显
然他们就不会结合查找字模,而是与各地址相应字节结合查找字模。
4)、因为控制器 ST7920 提供了 4 个自定义字符,所以这 4 个
自定义字符也是可以显示出来的,同样这 4 个自定义字符也是采用编码
的方式,但是这 4 个字符的编码是固定的,分别是
0000H,0002H,0004H,0006H。如下图所示:
上图只是把 2 个字符的 CGRAM 空间画出来,后续还有 2 个字符。可以看
到每个字符都有 16 行 16 列,每一行使用 2 个字节,因此一个字符占用
的空间是 32 字节,地址是 6 位的,4 个字符的地址分别是:00H~0FH、
10H~1FH、20H~2FH、30H~3FH。编码使用 2 个字节,可以看到有 2 个位
是任意的,说明其实这 4 个字符的编码可以有多个,只是我们常用前面
列举的 4 个编码。
3)、12864 全屏卷动的实现方法
首先需要明确的要点:
DDRAM 的结构如下:
80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、
8EH、8FH
90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、
9EH、9FH
A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、
ADH、AEH、AFH
B0H、B1H、B2H、B3H、B4H、B5H、B6H、B7H、B8H、B9H、BAH、BBH、BCH、
BDH、BEH、BFH
卷屏是分上下屏个各自卷动的,上半屏卷动左边红色区域的内容,下半屏卷动右边绿色区域
的内容。
为了实现全屏卷动显示,必须使用拼接的方法实现。
笔者花了几个小时研究了下算法,然后第二天实现了。现讲述如下:
细心观察 DDRAM 的结构发现,如果在卷动过程中,在同一时刻屏幕显示的内容最多涉及 3
行 DDRAM 的内容,而另一行是没有显示的,那么这一行就是用来缓存的数据的。
当屏幕显示如下 2 行时开始卷动(一):
80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、
8EH、8FH
90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、
9EH、9FH
则屏幕同时出现以下 3 行 DDRAM 内容(二):
80H、81H、82H、83H、84H、85H、86H、87H、88H、89H、8AH、8BH、8CH、8DH、
8EH、8FH
90H、91H、92H、93H、94H、95H、96H、97H、98H、99H、9AH、9BH、9CH、9DH、
9EH、9FH
A0H、A1H、A2H、A3H、A4H、A5H、A6H、A7H、A8H、A9H、AAH、ABH、ACH、
ADH、AEH、AFH
需要注意的是,左边是上半屏显示,右边是下半屏显示。
在程序的开始处往 DDRAM 对应区域填写如下内容:
第一行字符 第三行字符--> 开始显示
第二行字符 第四行字符
第三行字符 第五行字符--> 即将显示
第四行字符 第六行字符
这样在开始卷动之后,就可以实现拼接的效果了。当卷动了 16 次之后,
也就是第一行字符已经移出屏幕,屏幕显示的 DDRAM 如下:
第一行字符 第三行字符
第二行字符 第四行字符
第三行字符 第五行字符
第四行字符 第六行字符
此时,屏幕接着滚动,显示内容涉及 3 行的 DDRAM,如下:
第一行字符 第三行字符--> 已显示完毕
第二行字符 第四行字符
第三行字符 第五行字符
第四行字符 第六行字符--> 即将显示
第一行 DDRAM 是空余的,下次就该往第一行写数据,写完后 DDRAM
内容如下:
第五行字符 第七行字符
第二行字符 第四行字符
第三行字符 第五行字符
第四行字符 第六行字符
经过又一次的 16 次卷屏之后屏幕显示内容如下:
第五行字符 第七行字符--> 即将显示
第二行字符 第四行字符--> 显示完毕
第三行字符 第五行字符
第四行字符 第六行字符
然后接下来又卷动 16 次,笔者的算法是,在每一次卷动后写一个字到
显示完毕的那一行中,卷完 16 次,显示完毕的那一行也就写完了。然
后接下来的 16 次卷动又写刚刚显示完毕的那一行,而刚被写完的那一
行将在后面 16 次卷动中显示。
原理就是如此,然后从中提取出规律,设计出算法,并编程实现:
下面是程序实现:
void main(void){
unsigned char code ser[] = {"一一一一一一一一二二二二二二二二
叁叁叁叁叁叁叁叁四四四四四四四四中国中国中国中国"}; //这是要显
示的字符串
//没有检测换行符功能,只能显示一长串的汉字或一串 ASCII 码字符。
unsigned char i,addr,flag,hang,over,*ptdat;
//addr 用于存储写入地址
//flag 存储卷动地址,名字没取好!
//hang 存储下一行要写入数据的行号(1~4)
//over 记录写入的空字符数
//ptdat 存储字符串的指针
delay_12864(1000);
initial_12864();
ptdat = ser;
over = 0;
//写入空字符数
//这里先把前面 DDRAM 中的前 3 行的字符数据写入
//如果字符不足<=4 行,那么不卷动,之后字符>4 行才卷动
//一直到末行显示完毕则停止卷动
wrtcom_12864(0x80); //写屏幕第一行字符
for(i = 0; i < 16; i++){
if(*ptdat != '\0'){
wrtdat_12864(*ptdat++);
}else{