12864讲解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
辛勤学习了好几天,终于对12864液晶有了些初步了解~没有视频教程学起来真有些累,基本上内部程序写入顺序都是根据程序自我变动,然后逆向反推出原理……
芯片:YM12864R P-1 控制芯片:ST7920A 带中文字库
初步小结:
1、控制芯片不同,寄存器定义会不同
2、显示方式有并行和串行,程序不同
3、含字库芯片显示字符时不必对字符取模了
4、对芯片的结构地址一定要理解清楚
5、显示汉字时液晶芯片写入数据的顺序(即显示的顺序)要清楚
6、显示图片时液晶芯片写入数据的顺序(即显示的顺序)要清楚
7、显示汉字时的二级单元(一级为八位数据写入单元)要清楚
8、显示图片时的二级单元(一级为八位数据写入单元)要清楚
12864点阵液晶显示模块(LCM)就是由128*64个液晶显示点组成的一个128列*64行的阵列。
每个显示点对应一位二进制数,1表示亮,0表示灭。
存储这些点阵信息的RAM称为显示数据存储器。
要显示某个图形或汉字就是将相应的点阵信息写入到相应的存储单元中。
图形或汉字的点阵信息由自己设计,问题的关键就是显示点在液晶屏上的位置(行和列)与其在存储器中的地址之间的关系。
由于多数液晶显示模块的驱动电路是由一片行驱动器和两片列驱动器构成,所以12864液晶屏实际上是由左右两块独立的64*64液晶屏拼接而成,每半屏有一个512*8 bits显示数据RAM。
左右半屏驱动电路及存储器分别由片选信号CS1和CS2选择。
显示点在64*64液晶屏上的位置由行号(line,0~63)与列号(column,0~63)确定。
512*8 bits RAM中某个存储单元的地址由页地址(Xpage,0~7)和列地址(Yaddress,0~63)确定。
每个存储单元存储8个液晶点的显示信息。
为了使液晶点位置信息与存储地址的对应关系更直观关,将64*64液晶屏从上至下8等分为8个显示块,每块包括8行*64列个点阵。
每列中的8行点阵信息构成一个8bits二进制数,存储在一个存储单元中。
(注意:二进制的高低有效位顺序与行号对应关系因不同商家而不同)存放一个显示块的RAM区称为存储页。
即64*64液晶屏的点阵信息存储在8个存储页中,每页64个字节,每个字节存储一列(8行)点阵信息,每页包括64个存储单元。
因此存储单元地址包括页地址(Xpage,0~7)和列地址(Yaddress,0~63)。
例如点亮128*64的屏中(20,30)位置上的液晶点,因列地址30小于64,该点在左半屏第29列,所以CS1有效;行地址20除以8取整得2,取余得4,该点在RAM中页地址为2,在字节中的序号为4;所以将二进制数据00010000(也可能是00001000,高低顺序取决于制造商)写入Xpage=2,Yaddress=29的存储单元中即点亮(20,30)上的液晶点。
芯片的结构一定要清楚!
点阵LCD的显示原理
在数字电路中,所有的数据都是以0和1保存的,对LCD控制器进行不同的数据操作,可以得到不同的结果。
对于显示英文操作,由于英文字母种类很少,只需要8位(一字节)即可。
而对于中文,常用却有6000以上,于是我们的DOS前辈想了一个办法,就是将ASCII表的高128个很少用到的数值以两个为一组来表示汉字,即汉字的内码。
而剩下的低128位则留给英文字符使用,即英文的内码。
那么,得到了汉字的内码后,还仅是一组数字,那又如何在屏幕上去显示呢?这就涉及到文字的字模,字模虽然也是一组数字,但它的意义却与数字的意义有了根本的变化,它是用数字的各位信息来记载英文或汉字的形状,如英文的'A'在字模的记载方式如图1所示:
图1 “A”字模图
而中文的“你”在字模中的记载却如图2所示:
图2 “你”字模图
字符二级单元(图3中阴影部分)
一个汉字的二级单元是一个16*16的区域,因些128*64液晶可以显示4行8列共32个汉字(如图3)。
而它的一个二级单元如图4(在无字库时,对汉字的取模有横向跟纵向两种,要注意),对于并行含有子库芯片的显示,只要设定好这个二级单元的地址(如0X80+i,这样设定i的范围为0~31,这里注意第一行会直接跳到第三行;或者根据自己需要如第二行0X90+i,i范围为0~7;第三行0X88+i,i范围为0~7;),然后直接把汉字写入就OK了~(串行无字符库的后面再做分析)
垂直坐标:上半屏00~1F,总共为32
水平坐标:上半屏水平坐标分别为0X80+(00~07)
下半屏00~1F,总共为
32 下半屏水平坐标分别为0X88+(00~07)
图片显示芯片结构分块与汉字显示不一样
图象显示过程是这样的:首先设置垂直地址,再设水平地址(连续写入两个字节的资料来完成垂直与水平的坐标地址,然后在每个地址里写入16位数据)。
垂直地址范围 AC5...AC0
水平地址范围 AC3…AC0
绘图RAM 的地址计数器(AC)只会对水平地址(X 轴)自动加一,当水平地址=0FH 时会重新设为00H
但并不会对垂直地址做进位自动加一,故当连续写入多笔资料时,程序需自行判断垂直地址是否需重新设定。
GDRAM的坐标地址与资料排列顺序如图5:分上下屏写入。
for(i=0;i<32;i++) // 上半屏32个垂直地址
{
write_com(0x80 + i); // 垂直地址
write_com(0x80); // 水平地址
for(j=0;j<16;j++)
{
write_data(*adder);
adder++;
}
}
带中文字库的128X64显示模块时应注意以下几点:
①欲在某一个位置显示中文字符时,应先设定显示字符位置,即先设定显示地址,再写入中文字符编码。
②显示ASCII字符过程与显示中文字符过程相同。
不过在显示连续字符时,只须设定一次显示地址,由模块自动对地址加1指向下一个字符位置,否则,显示的字符中将会有一个空ASCII字符位置。
③当字符编码为2字节时,应先写入高位字节,再写入低位字节。
④模块在接收指令前,向处理器必须先确认模块内部处于非忙状态,即读取BF标志时BF需为“0”,方可接受新的指令。
如果在送出一个指令前不检查BF标志,则在前一个指令和这个指令中间必须延迟一段较长的时间,即等待前一个指令确定执行完成。
指令执行的时间请参考指令表中的指令执行时间说明。
⑤“RE”为基本指令集与扩充指令集的选择控制位。
当变更“RE”后,以后的指令集将维持在最后的状态,除非再次变更“RE”位,否则使用相同指令集时,无需每次均重设“RE”位。
程序———————并行(串行后面再分析)——————————————————————————
#include <stdio.h>
#include <reg52.h>
#include <intrins.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int
uchar code LCD_data1[];
uchar code LCD_data2[];
uchar code LCD_picture1[];
uchar code LCD_picture2[];
sbit RS = P2^4;
sbit RW = P2^5;
sbit EN = P2^6;
sbit PSB = P2^1;
sbit RES = P2^3;
sbit Dataport = P0;
sbit Busyport = P0^7;
////////////////////////////////////////////////////////////// void delay_ms(unsigned int n) //延时10×n毫秒程序 {
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<2000;j++);
}
void delay(unsigned int m) //1US延时程序
{
unsigned int i,j;
for(i=0;i<m;i++)
for(j=0;j<10;j++);
}
/////////////////////////////////////////////////////////////// //判LCM忙子函数
void check_LCD_busy (void)
{
Dataport = 0xff;
RS = 0;
RW = 1;
EN = 1;
while (Busyport);
EN = 0;
}
/////////////////////////////////////////////////////////////// //写命令子函数
void write_com(uchar Command)
{
check_LCD_busy();
RW=0;
RS=0;
delay(1);
P0=Command;
EN=1;
delay(1);
EN=0;
}
////////////////////////////////////////////////////////////////
//写数据子函数
void write_data(uchar Data)
{
check_LCD_busy();
RW=0;
RS=1;
delay(1);
P0=Data;
EN=1;
delay(1);
EN=0;
}
/////////////////////////////////////////////////////////////////
//LCM清屏函数
void lcdClear (void)
{
write_com(0x01);
}
////////////////////////////////////////////////////////////////
//LCM复位函数
void reset ()
{
RES=0; //复位
delay(1); //延时
RES=1; //复位置高
delay(10);
}
///////////////////////////////////////////////////////////////
//显示汉字
void dispString (uchar X, Y,uchar *msg) //X为哪一行,Y为哪一列。
msg 为汉字
{
if(X==0) X = 0x80; //第一行,汉字显示坐标
else if(X==1) X = 0x90; //第二行
else if(X==2) X = 0x88; //第三行
else X = 0x98; //第四行
Y = X + Y; //Y为1往右移一位
write_com(Y); //写入坐标
while (*msg)
{
write_data(*msg++); //显示汉字
}
}
/////////////////////////////////////////////////////////////// //显示图象
void disppicture(uchar code *adder)
{
uint i,j;
//*******显示上半屏内容设置
for(i=0;i<32;i++) // 上半屏32个垂直地址 {
write_com(0x80 + i); //SET 垂直地址 VERTICAL ADD
write_com(0x80); //SET 水平地址 HORIZONTAL ADD
for(j=0;j<16;j++)
{
write_data(*adder);
adder++;
}
}
//*******显示下半屏内容设置
for(i=0;i<32;i++) //
{
write_com(0x80 + i); //SET 垂直地址 VERTICAL ADD write_com(0x88); //SET 水平地址 HORIZONTAL ADD
for(j=0;j<16;j++)
{
write_data(*adder);
adder++;
}
}
}
/////////////////////////////////////////////////////////////// //LCD字库初始化函数
void lcdinit_str(void)
{
delay(40); //大于40MS的延时程序
PSB=1; //设置为8BIT并口工作模式
delay(1); //延时
reset(); //复位
write_com(0x30); //Extended Function Set :8BIT设置,RE=0: basic instruction set, G=0 :graphic display OFF
delay(100); //大于100uS的延时程序
write_com(0x30); //Function Set
delay(37); ////大于37uS的延时程序
write_com(0x08); //Display on Control
delay(100); //大于100uS的延时程序
write_com(0x10); //Cursor Display Control光标设置
delay(100); //大于100uS的延时程序
write_com(0x0C); //Display Control,D=1,显示开
delay(100); //大于100uS的延时程序
write_com(0x01); //Display Clear
delay(10); //大于10mS的延时程序
write_com(0x06); //Enry Mode Set,光标从右向左加1位移动
delay(100); //大于100uS的延时程序
}
//////////////////////////////////////////////////////////////////
//LCD图片(扩展)初始化函数
void lcdinit_pic(void)
{
delay(40); //大于40MS的延时程序
PSB=1; //设置为8BIT并口工作模式
delay(1); //延时
reset();
write_com(0x36); //Extended Function Set RE=1: extended instruction
delay(100); //大于100uS的延时程序
write_com(0x36); //Extended Function Set:RE=1: extended instruction set
delay(37); ////大于37uS的延时程序
write_com(0x3E); //EXFUNCTION(DL=8BITS,RE=1,G=1)
delay(100); //大于100uS的延时程序
write_com(0x01); //CLEAR SCREEN
delay(100); //大于100uS的延时程序
}
/////////////////////////////////////////////////////////////////
void main()
{
while(1)
{
lcdinit_str();
delay_ms(10); //此延时如果没有的话第一行会一直在第一列
dispString(0, 1,"祖国江山好");
delay_ms(10);
dispString(1, 1,"爱情少不了");
delay_ms(10);
dispString(2, 1,"为了下一代");
delay_ms(10);
dispString(3, 1,"赶紧谈恋爱");
delay_ms(200);
delay_ms(200);
lcdClear();
delay_ms(10);
dispString(0, 1,"大名吴建峰");
delay_ms(10);
dispString(1, 1,"性别为非女");
delay_ms(10);
dispString(2, 1,"芳龄二十二");
delay_ms(10);
dispString(3, 1,"海拔一百六");
delay_ms(200);
delay_ms(200);
lcdinit_pic();
lcdClear();
delay_ms(10);
disppicture(LCD_picture1);
delay_ms(300);
delay_ms(300);
}
}
图象代码库见最后!~
成果——————————————————————————————————
图形取模方法(转):
128*64的像素能显示的内容就有限,也无法要求它能多清楚,如果将一个彩色的图片转换为单色位图,效果就更差了,个人不建议用它来显示彩色的图片,如果真要用128*64的液晶显示,建议如下:
1.尽量选择颜色比较单一的图片,当然一种颜色的效果最好不过了;
2.图片不能选择的太大,要不缩小了就看不清楚了;
3.图片的调整可以这样(仅供参考):
1>调整图片的宽高比大致为2:1;
2>将图片缩小到128*64像素;
3>保存为单色位图;
图片的大小缩放不太好操作,我通常是这样做的:你用画图程序打开你要显示的图片后,首先要操作的查看属性(点击菜单栏的图像->属性,单位选择为
像素后,宽高值就出来了),比如:宽:603,高:444,这显然宽高比不是2:1,你就要调整了,444*2=888,现在为603,所以888/603=1.47,所以宽要放大为147%(点击菜单栏的图像->拉伸/扭曲,在拉伸里面的水平处改为147),现在就调整为2:1了;接下来就要将图片缩小到128*64像素,先计算缩放的比例,128/888=0.144,所以相同的操作(点击菜单栏的图像->拉伸/扭曲,在拉伸里面的水平处改为14,垂直里面也要改为14);最后就是保存为单色位图(文件->另存为->文件类型选择为:单色位图(.bmp))?试过颜色比较单一的,效果还可以,复杂的彩色图片效果就很不理想了...
说明:在调整图片的宽高比大致为2:1的过程中图片会被拉伸变形,不过缩小到128*64像素后也不是太明显...
图片取模
图片代码——————————————————————————————————
uchar code LCD_picture1[]=
{
0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0
0x00,0x00,0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0 0,0x18,0x00,
0x00,0x00,0x07,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0 0,0x18,0x00,
0x03,0x00,0x07,0xEC,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x0 0,0x37,0x80,
0x03,0x00,0x00,0xEC,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x1 8,0x3F,0xC0,
0x03,0x00,0x0F,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x1 8,0x79,0x80,
0x03,0x00,0x0F,0x6B,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x1 8,0x7F,0x00,
0x1F,0xF4,0x01,0xE9,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0xE0,0x1 8,0xFF,0x80,
0x1F,0xFC,0x00,0xFB,0x30,0x00,0x00,0x00,0x00,0x00,0x01,0xC6,0x70,0x1 B,0x1E,0xC0,
0x03,0x38,0x01,0xB3,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x86,0x38,0xD B,0xFF,0xE0,
0x03,0x70,0x07,0x9E,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x26,0x18,0xD B,0x8C,0x70,
0x7F,0xFF,0x87,0x27,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x01,0xF F,0xFF,0xB8,
0x7F,0xFF,0x80,0x3F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,0x00,0x0 0,0x0C,0x18,
0x07,0x80,0x00,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0 0,0xFF,0xE0,
0x0F,0x00,0x00,0xFB,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xF 0,0xFF,0xE0,
0x1F,0x30,0x01,0xCF,0xFF,0xFF,0x09,0x00,0x00,0x00,0x00,0x03,0xFF,0xF F,0x0C,0x00,
0x3B,0x36,0x03,0x07,0xFF,0xFF,0xE0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF F,0xCC,0x00,
0x73,0x76,0x03,0x3F,0xFF,0xFF,0xF7,0x00,0x00,0x00,0x00,0x1F,0xFF,0xF F,0xFC,0x00,
0x63,0xE6,0x00,0x3F,0xFF,0xFF,0xDC,0x80,0x00,0x00,0x00,0x7F,0xFF,0xF F,0xFC,0x00,
0x03,0xFE,0x00,0x7F,0xFF,0xFF,0xAF,0x60,0x00,0x00,0x00,0xFF,0xFF,0xF F,0xFC,0x00,
0x03,0xFE,0x00,0xFF,0xFF,0xFF,0xF5,0xC0,0x00,0x00,0x01,0xFF,0xFF,0xF F,0xFC,0x00,
0x00,0x00,0x01,0xFF,0xFF,0xFF,0x23,0x20,0x00,0x00,0x03,0xFF,0xFF,0xF F,0xFE,0x00,
0x00,0x00,0x03,0xFF,0x8F,0xE3,0xEC,0xA0,0x00,0x00,0x03,0xFF,0x1F,0xC
0x00,0x00,0x03,0xFF,0x07,0xC1,0x92,0xC0,0x00,0x00,0x07,0xFE,0x0F,0x8 3,0xFF,0x80,
0x00,0x00,0x07,0xFF,0x03,0xC0,0xE9,0xC0,0x00,0x00,0x0F,0xFE,0x07,0x8 1,0xFF,0x80,
0x00,0x00,0x07,0xFE,0x03,0x80,0xED,0xE0,0x00,0x00,0x0F,0xFC,0x07,0x0 1,0xFF,0xC0,
0x00,0x00,0x0F,0xFE,0x1B,0xB0,0xDD,0xE0,0x00,0x00,0x1F,0xFC,0x37,0x6 1,0xFF,0xC0,
0x00,0x00,0x0F,0xFE,0x1B,0xB0,0xFF,0xE0,0x00,0x00,0x1F,0xFC,0x37,0x6 1,0xFF,0xC0,
0x00,0x00,0x0F,0xFE,0x1B,0xB0,0xFF,0xF0,0x00,0x00,0x1F,0xFC,0x37,0x6 1,0xFF,0xE0,
0x00,0x00,0x1F,0xFE,0x1B,0xB0,0xFF,0xF0,0x00,0x00,0x3F,0xFC,0x37,0x6 1,0xFF,0xE0,
0x00,0x00,0x1F,0xFF,0x03,0xC0,0xFF,0xF0,0x00,0x00,0x3F,0xFE,0x07,0x8 1,0xFF,0xE0,
0x00,0x00,0x1F,0xFF,0x07,0xC1,0xFF,0xF0,0x00,0x00,0x3F,0xFE,0x0F,0x8 3,0xFF,0xE0,
0x00,0x00,0x1F,0xFF,0x8C,0x63,0xFF,0xF0,0x00,0x00,0x3F,0xFF,0x1F,0xC 7,0xFF,0xE0,
0x00,0x00,0x1F,0xDF,0xFC,0x7F,0xF7,0xF0,0x00,0x00,0x3F,0xFF,0xF0,0x7 F,0xFF,0xE0,
0x00,0x00,0x1F,0x07,0xFF,0xFF,0xD1,0xF0,0x00,0x00,0x3F,0xFF,0xE0,0x3 F,0xFF,0xE0,
0x00,0x00,0x1E,0x03,0xFF,0xFF,0x80,0xF0,0x00,0x00,0x3F,0xC0,0x00,0x0 0,0x07,0xE0,
0x00,0x00,0x1E,0x03,0xFE,0xFF,0xC0,0xF0,0x00,0x00,0x3F,0xE0,0x00,0x0 0,0x0F,0xE0,
0x00,0x00,0x0C,0x01,0xF8,0x3F,0x80,0xE0,0x00,0x00,0x1F,0xE0,0x00,0x0 0,0x1F,0xC0,
0x00,0x00,0x0E,0x03,0xF0,0x1F,0xC0,0xE0,0x00,0x00,0x1F,0xF0,0x0F,0xC 0,0x1F,0xC0,
0x00,0x00,0x1E,0x03,0xF0,0x1F,0x80,0xE0,0x00,0x00,0x1F,0xFC,0x0B,0x4 0,0x7F,0xC0,
0x00,0x00,0x7F,0x1F,0xF0,0x1F,0xC3,0xE0,0x00,0x00,0xFF,0xFF,0x0B,0x4 1,0xFF,0xC0,
0x00,0x00,0xFF,0xFF,0xF0,0x1F,0xF7,0xF0,0x00,0x01,0xFF,0xFF,0xC0,0x0 F,0xFF,0xF0,
0x00,0x01,0xFF,0xFF,0xF8,0x3F,0xFF,0xFC,0x00,0x03,0xFF,0xFF,0xF8,0x7 F,0xFF,0xF8,
0x00,0x03,0xFE,0x7F,0xFE,0xFF,0xFF,0xFE,0x00,0x07,0xFC,0xFF,0xFF,0xF F,0xFF,0xFC,
0x00,0x07,0xFC,0x1F,0xFF,0xFF,0xF8,0xFE,0x00,0x0F,0xF8,0x3F,0xFF,0xF
0x00,0x0F,0xF8,0x07,0xFF,0xFF,0xE0,0xFF,0x00,0x1F,0xF0,0x0F,0xFF,0xF F,0xC0,0xFE,
0x00,0x0F,0xF8,0x00,0xFF,0xFF,0x00,0x7F,0x80,0x1F,0xF0,0x01,0xFF,0xF E,0x00,0xFF,
0x00,0x1F,0xF0,0x00,0x02,0x00,0x00,0x7F,0x80,0x3F,0xE0,0x00,0x00,0x0 0,0x00,0x7F,
0x00,0x1F,0xF0,0x00,0x00,0x00,0x00,0x3F,0xC0,0x3F,0xE0,0x00,0x00,0x0 0,0x00,0x7F,
0x00,0x3F,0xF0,0x00,0x00,0x00,0x00,0x3F,0xC0,0x7F,0xE0,0x00,0x00,0x0 0,0x00,0x7F,
0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x1F,0xE0,0x7F,0xC0,0x00,0x00,0x0 0,0x00,0x3F,
0x00,0x3F,0xE0,0x00,0x00,0x00,0x00,0x1F,0xE0,0x7F,0xC0,0x00,0x00,0x0 0,0x00,0x3F,
0x00,0x7F,0xE0,0x01,0xC0,0xE0,0x00,0x1F,0xE0,0xFF,0xC0,0x00,0x70,0xE 0,0x00,0x3F,
0x00,0x7F,0xE0,0x03,0xF3,0xF0,0x00,0x1F,0xE0,0xFF,0xC0,0x00,0xF9,0xF 0,0x00,0x3F,
0x00,0x7F,0xE0,0x07,0xFF,0xF8,0x00,0x1F,0xE0,0xFF,0xC0,0x01,0xFF,0xF 8,0x00,0x3F,
0x00,0x7F,0xE0,0x07,0xFF,0xF8,0x00,0x1F,0xE0,0xFF,0xC0,0x01,0xFF,0xF 8,0x00,0x3F,
0x00,0x7C,0xE0,0x07,0xFF,0xF8,0x00,0x1B,0xE0,0xF9,0xC0,0x01,0xFF,0xF 8,0x00,0x37,
0x00,0x38,0xF0,0x03,0xFF,0xF0,0x00,0x39,0xE0,0xF1,0xE0,0x00,0xFF,0xF 0,0x00,0x73,
0x00,0x30,0x70,0x01,0xFF,0xE0,0x00,0x30,0xC0,0x60,0xE0,0x00,0x7F,0xE 0,0x00,0x61,
0x00,0x00,0x78,0x00,0xFF,0xC0,0x00,0x30,0x00,0x00,0xF0,0x00,0x3F,0xC 0,0x00,0x60,
0x00,0x00,0x38,0x00,0x7F,0x80,0x00,0x60,0x00,0x00,0x70,0x00,0x1F,0x8 0,0x00,0xC0,
0x00,0x00,0x1C,0x00,0x3F,0x00,0x00,0xC0,0x00,0x00,0x38,0x00,0x0F,0x0 0,0x01,0x80,
0x00,0x00,0x0E,0x00,0x1E,0x00,0x00,0x80,0x00,0x00,0x1C,0x00,0x06,0x0 0,0x01,0x00,
0x00,0x00,0x07,0x00,0x0C,0x00,0x01,0x80,0x00,0x00,0x0E,0x00,0x06,0x0 0,0x03,0x00,
};
三、文本(汉字,字符)输入
1、文本显示RAM(DDRAM)
文本显示RAM提供8个×4行的汉字空间,当写入文本显示RAM时,可以分别显示CGROM、HCGROM 与CGRAM的字型;
根据汉字显示坐标可以很容易地显示汉字以及其它字符~
四、图象输入
1、绘图RAM(GDRAM)
绘图显示RAM提供128×8个字节的记忆空间,在更改绘图RAM时,先连续写入水平与垂直的坐标值,再写入两个字节的数据到绘图RAM,而地址计数器(AC)会对水平地址(X地址)自动加一,当水平地址为0XFH时会重新设为00H;不会对垂直地址做进位自动加1.。
在写入绘图RAM的期间,绘图显示必须关闭,整个写入绘图RAM的步骤如下:
1、关闭绘图显示功能。
2、先将水平的位元组坐标(X)写入绘图RAM地址;
再将垂直的坐标(Y)写入绘图RAM地址;
将D15——D8写入到RAM中;
将D7——D0写入到RAM中;
打开绘图显示功能。
绘图显示的缓冲区对应分布请参考“GDRAM坐标”
GDRAM地址坐标对于图象显示,这个地址表才是王道
水平方向X—以字节单位(2字节16位)
垂直方向Y—以位为单位
屏幕分上下两屏,垂直坐标上下屏都为Y:00—1F(也即0X80+Y),以位为单位;
水平坐标上半屏为X1:00—07(也即0X80+X1);下半屏为X2:08—0F (也0X80+X2)
由图可以看到水平坐标一个单位是两字节(即16位D15~D0),X地址会自动加1,是直接加一个单位(即两字节16位),比如00—》01(也即0X80+00—》0X80+01),从第一行第一列跳到第一行第二列。
(1)、整屏图象显示
程序:
void disppicture(uchar code *adder)
{
uint i,j;
//*******显示上半屏内容设置
for(i=0;i<32;i++)
{
write_com(0x80 + i); // 垂直地址 VERTICAL ADD
write_com(0x80); // 水平地址 HORIZONTAL ADD
for(j=0;j<16;j++) //X坐标方向以2字节为单位,Y坐标方向以1位为单位,先连续写入垂直与水平坐标,
{ //再写入两字节数据到GDRAM。
这里是这样进行的:i=0时,j=0,1时,写入两字节到垂直(0X80+00)水平(0X80+00)这格(D15~D0)里;然后X 坐标地址自增1,地址变为垂直(0X80+00)水平(0X80+01)这格,在j=2,3时写入两字节,………一直到垂直(0X80+00)水平(0X80+07)这格,在j=14,15时写入两字节,此时循环
for(j=0;j<16;j++)结束跳出,刚好第一行128位写完数据;然后i++,开始写第二行…
write_data(*adder);
adder++;
}
}
//*******显示下半屏内容设置
for(i=0;i<32;i++) //
{
write_com(0x80 + i); //SET 垂直地址下半屏的垂直地址中上半屏一样是0X80+Y (Y:00~1F)
write_com(0x88); //SET 水平地址下半屏的水平地址是从0X80+08=0X88开始的 for(j=0;j<16;j++)
{
write_data(*adder);
adder++;
}
}
}
整屏图
形:……
(2)分块图形显示
程序:
void anti_nor_pic (uchar cpl_sign,uchar menu_num)
{
uchar i,j,x,y;
x=menu_num*3; // 计算图片显示坐标,由坐标图知图片0/1/2/3/4/5的X坐标分别
if(x>6) x--; //为0X80+(00,03, 06,08,0B,0E)
if(menu_num<3) y=16; //0、1、2号图片Y坐标起始为0X80+16(上半屏从上往下第16行) else y=8; //3、4、5号图片Y坐标起始为0X80+8(下半屏从上往下第8行)
if(cpl_sign==1) display_cnasc(0x82,4,menu_cn[menu_num]);
for(j=0;j<24;j++) //图片32*24(宽*高),即有24行
{
for(i=0;i<2;i++) //一张图片在X方向上占两个单位(一个单位即16位两字节)在写
{ //完一个单位后要向下一个单位写,因为这里不是全屏显示,要设定
wr_lcd(comm,0x34); //关闭绘图显示功能
wr_lcd(comm,0x80+y); //垂直地址
wr_lcd(comm,0x80+x+i); //水平地址,事实上这里i可以不要,因为水平地址会自动加1,
wr_lcd(comm,0x30); //但因为是分块,要有个
for(i=0;i<2;i++)的循环。
if(cpl_sign==1) // 图片反选
{
wr_lcd(dat,~menu_pic[menu_num][j*4+i*2]); //写入一字节
wr_lcd(dat,~menu_pic[menu_num][j*4+i*2+1]); //再写入一字节
}
else // 正常显示
{
wr_lcd(dat,menu_pic[menu_num][j*4+i*2]);
wr_lcd(dat,menu_pic[menu_num][j*4+i*2+1]);
}
}
y++; //垂直地址要由软件设定自动加1
if(y==32&&menu_num<3) {x+=8;y=0;} // 上下屏切换。
0/1/2三幅图Y坐标由16加了
} //16行,跳入下半屏,水平坐标X变成0X80+08,垂直坐标变成0X80+00
wr_lcd(comm,0x36);
}
分块图形(旁边还未处理)待续……
一、ST7920控制IC的LCD12864实现反白显示
从使用手册上可知,扩展指令里的0x03+行号即可实现反白对应行。
但是ST7920 控制器的128×64 点阵液晶其实原理上等同256×32 点阵,第三行对应的DDRAM 地址紧接第一行;第四行对应的DDRAM 地址紧接第二行。
所以128×64 点阵的液晶执行反白功能时实用意义不大,因为用户对第一行执行反白显示操作时,第三行必然也反白显示;第二行反白,第四行也必然反白。
其实还是有办法做到单行反白的,解决方法就是混用图形显示和字符显示。
其理论支持在于:在ST7920中,字符显示的DDRAM和图形的GDRAM是相互独立的,而最后显示到液晶上的结果,是两个RAM中数据的异或。
具体来说:假如某个点上,绘图RAM的没有绘图(数据为0),而字符RAM上有点阵(数据为1),那么异或的结果就是1,也就是说正常显示字符;当字符上RAM没有点阵的时候,异或的结果是0,自然也就不显示了。
假如该点上绘图RAM绘图了(数据为1),当字符RAM上有点阵(数据为1时),异或的结果为0,效果就是反白显示;如果字符RAM没有点阵(数据为0时),异或结果为1,效果就是显示绘图的背景。
所以,如果要在某个地方反白显示,那么就在该点绘图并且写字,如果要取消反白,就重新用全0擦掉那个地方的绘图!这样一来可以实现任何地方、任意大小的反白显示,反而比原指令中的单行反白的功能更好更强大。
二、对于整屏既有图象又有文本,则可以用两种方式实现:
1、首先文本DDRAM写入要写的字符,其余全部空格(即0X00),然后再在没有字符的地方(即非点亮的晶格中,0X00)绘入图象。
DDRAM与GDRAM异或后就可以整屏实现图象与文本。
参见程序实例1。
2、首先图象GDRAM绘入要绘的图象,其余全部用con_disp()反白(或显示图象)函数(见程序实例1)写入0X00,然后再在没有图象的地方(即0X00)处写入文本。
同样DDRAM与GDRAM异或后就可以整屏实现图象与文本。
参见程序实例2.
注意:根据最后显示到液晶上的结果,是两个RAM中数据的异或结果,我们可以知道,con_disp()反白(或显示图象)函数只能对文本字符进行反白,而不能对图形进行反白,因为con_disp()反白(或显示图象)函数本身是一个绘图函数,数据是写到GDRAM中去,只能与DDRAM(文本)异或,而不能与GDRAM (图象)异或。
那么图象如何反白呢?我们在绘入图象时可以用按位取反符号
“~”,如write_data(~date),这样就可以对你要绘入的图象进行取反了!如果用con_disp()反白(或显示图象)函数对GDRAM进行反白(写入0xff),就会出现重叠覆盖现象,(假设这个位置的DDRAM文本全为0x00);如下图:
第一幅图被反白函数覆盖了
//////////////////////////////////////////////////
程序实例1:(con_disp()反白(或显示图象)函数)
//反白或显示图片参数格式:(H)8bit数据,(L)8bit数据,X0坐标(16bit),
Y0坐标(1bit),X坐标(16bit),Y坐标(1bit)*/
void con_disp (uchar data1,uchar data2,uchar x0,uchar y0,uchar xl,uchar yl)
{
uchar i,j;
for(j=0;j<yl;j++)
{
for(i=0;i<xl;i++)
{
write_com(0x34); // 扩展功能
write_com(y0+j); // 写Y坐标
write_com(x0+i); // 写X坐标
write_com(0x30); // 普通功能
write_data(data1);
write_data(data2);
}
}
write_com(0x36); // 开绘图显示
}
1、这个函数,X0,Y0是左上角坐标,X1,Y1分别是列数(水平坐标)和行数(垂直坐标)。
因为水平坐标是以二字节(16位)为单位,一次性要写入两字
节(data1,data2),因此如果要反白则(data1,data2都为0XFF),如果要绘图(data1,data2可以为其它,此绘图函数主要用来绘制导航标头的)。
2、写入资料到内部RAM(包括DDRAM、GDRAM等)是基本操作指令,因此之前要写write_com(0X30);
写DDRAM地址是基本操作指令,因此之前要写write_com(0X30);
写GDRAM地址是扩展操作指令,因此之前要写write_com(0X34);(此时要关绘图显示),然后写完GDRAM数据,最后要开绘图显示write_com(0X36);
/////////////////////////////////////////////////////////
程序实例2:(先文本再绘图)
///////////////////////////////////////////////////////////
//图片菜单标头
void dis_title_pic()
{
display_cnasc(0x80,"〓〓");
display_cnasc(0x82,"调整");
display_cnasc(0x86,"〓〓"); //第一行为文本
display_cnasc(0x90," "); //第二行空格(即
0X00)
display_cnasc(0x88," "); //第三行空格(即
0X00)
display_cnasc(0x98," "); //第四行空格(即
0X00)
}
void main()
{
init_lcd();
dis_title_pic(); //首先显示图片菜单标头,即先全屏覆盖文本
for(d=0;d<6;d++) //然后再绘入六幅图片
{
if(d==0) disppicture(1,d); //disppicture()函数见个人资料12864分析2
else disppicture(0,d);
}
}
~~~
////////////////////////////////////////////
程序实例3:(先绘图再文本)
/////////////////////////////////////////////
//图片菜单标头
void dis_title_pic()
{
display_cnasc(0x80,"〓〓");
display_cnasc(0x82,"调整");
display_cnasc(0x86,"〓〓");
}
void main()
{
Init_lcd();
for(d=0;d<6;d++) //绘入图象{
if(d==0) disppicture(1,d); //第一幅图反白
else disppicture(0,d); //其余不反白}
con_disp(0x00,0x00,0X80,0x80,8,16);
con_disp(0x00,0x00,0X82,0x80+16,1,16);
con_disp(0x00,0x00,0X85,0x80+16,1,16);
con_disp(0x00,0x00,0X8a,0x80,1,32);
con_disp(0x00,0x00,0X8d,0x80,1,32);
dis_title_pic(); //写入文本
}
~~~
/////////////////////////////////////////////
程序实例4:对于for(d=0;d<6;d++),我想也可以一个一个拆出来写,试验下反白结果
void dis_title_pic()
{
display_cnasc(0x80,"〓〓");
display_cnasc(0x82,"调整");
display_cnasc(0x86,"〓〓");
}
void main()
{
init_lcd();
disppicture(1,0); //第一幅图反白显示
disppicture(0,1); //第二幅图正常显示
disppicture(1,2); //第三幅图反白显示
disppicture(0,3); //第四幅图正常显示
disppicture(1,4); //第五幅图反白显示
disppicture(0,5); //第六幅图正常显示
con_disp(0x00,0x00,0X80,0x80,8,16); //GDRAM其余地方写入0X00 con_disp(0x00,0x00,0X82,0x80+16,1,16);
con_disp(0x00,0x00,0X85,0x80+16,1,16);
con_disp(0x00,0x00,0X8a,0x80,1,32);
con_disp(0x00,0x00,0X8d,0x80,1,32);
dis_title_pic(); //写入文本
}
~~1、3、5图反白显示~
至此,通过以上绘图原理以、反白原理和四个例程,就可以在12864液晶上任何位置显示图象或文本,可以随意对液晶上任意块块进行反白显示,所以,可以通过12864液晶完成类似MP3显示屏上的各种操作:进入菜单页面,通过按键选择,被选择的模块反白,若MEU键按下刚进入此菜单(比如时钟)下面的页面,然后再进行各种设置……
当然,还有很多问题尚未解决,其中一个:就是单单写入文本(不占全屏),不出现花屏;而单单绘入图象(不占全屏)时,就会出现花屏,见下图……(可能是:绘图GDRAM要写入全屏数据,若只有部分数据,则其余地方芯片不知道写入什么就会随机写入,然后出现花屏;而文本DDRAM本身芯片已经有了一个分块控制。
而12864液晶不同的控制芯片具体情况不一样),当然我还不确定,有待以后继续分析……
那么右边这块没有花屏的显示是根据程序实例2,3作出来的,运用GDRAM 与DDRAM相异或原理,就是把左边花屏处用con_disp()反白(或显示图象)函数全部绘0X00.~~~
……好吧,暂时告一段落先……理解到这程度已经死了好大一批脑细胞了……。