128图形显示原理分析c程序.docx
《128图形显示原理分析c程序.docx》由会员分享,可在线阅读,更多相关《128图形显示原理分析c程序.docx(35页珍藏版)》请在冰豆网上搜索。
128图形显示原理分析c程序
51单片机综合学习
12864液晶原理分析1
辛勤学习了好几天,终于对12864液晶有了些初步了解~没有视频教程学起来真有些累,基本上内部程序写入顺序都是根据程序自我变动,然后逆向反推出原理……
芯片:
YM12864RP-1控制芯片:
ST7920A 带中文字库
初步小结:
1、 控制芯片不同,寄存器定义会不同
2、 显示方式有并行和串行,程序不同
3、 含字库芯片显示字符时不必对字符取模了
4、 对芯片的结构地址一定要理解清楚
5、 显示汉字时液晶芯片写入数据的顺序(即显示的顺序)要清楚
6、 显示图片时液晶芯片写入数据的顺序(即显示的顺序)要清楚
7、 显示汉字时的二级单元(一级为八位数据写入单元)要清楚
8、 显示图片时的二级单元(一级为八位数据写入单元)要清楚
12864点阵液晶显示模块(LCM)就是由128*64个液晶显示点组成的一个128列*64行的阵列。
每个显示点对应一位二进制数,1表示亮,0表示灭。
存储这些点阵信息的RAM称为显示数据存储器。
要显示某个图形或汉字就是将相应的点阵信息写入到相应的存储单元中。
图形或汉字的点阵信息由自己设计,问题的关键就是显示点在液晶屏上的位置(行和列)与其在存储器中的地址之间的关系。
由于多数液晶显示模块的驱动电路是由一片行驱动器和两片列驱动器构成,所以12864液晶屏实际上是由左右两块独立的64*64液晶屏拼接而成,每半屏有一个512*8bits显示数据RAM。
左右半屏驱动电路及存储器分别由片选信号CS1和CS2选择。
显示点在64*64液晶屏上的位置由行号(line,0~63)与列号(column,0~63)确定。
512*8bitsRAM中某个存储单元的地址由页地址(Xpage,0~7)和列地址(Yaddress,0~63)确定。
每个存储单元存储8个液晶点的显示信息。
为了使液晶点位置信息与存储地址的对应关系更直观关,将64*64液晶屏从上至下8等分为8个显示块,每块包括8行*64列个点阵。
每列中的8行点阵信息构成一个8bits二进制数,存储在一个存储单元中。
(注意:
二进制的高低有效位顺序与行号对应关系因不同商家而不同)存放一个显示块的RAM区称为存储页。
即64*64液晶屏的点阵信息存储在8个存储页中,每页64个字节,每个字节存储一列(8行)点阵信息。
因此存储单元地址包括页地址(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 图4字符二级单元(图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了~(串行无字符库的后面再做分析)
图5:
垂直坐标:
上半屏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
#include
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
ucharcodeLCD_data1[];
ucharcodeLCD_data2[];
ucharcodeLCD_picture1[];
ucharcodeLCD_picture2[];
sbitRS=P2^4;
sbitRW=P2^5;
sbitEN=P2^6;
sbitPSB=P2^1;
sbitRES=P2^3;
sbitDataport=P0;
sbitBusyport=P0^7;
//////////////////////////////////////////////////////////////
void delay_ms(unsignedintn) //延时10×n毫秒程序
{
unsignedinti,j;
for(i=0;i for(j=0;j<2000;j++);
}
void delay(unsignedintm) //1US延时程序
{
unsignedinti,j;
for(i=0;i for(j=0;j<10;j++);
}
///////////////////////////////////////////////////////////////
//判LCM忙子函数
voidcheck_LCD_busy(void)
{
Dataport=0xff;
RS=0;
RW=1;
EN=1;
while(Busyport);
EN=0;
}
///////////////////////////////////////////////////////////////
//写命令子函数
voidwrite_com(ucharCommand)
{
check_LCD_busy();
RW=0;
RS=0;
delay
(1);
P0=Command;
EN=1;
delay
(1);
EN=0;
}
////////////////////////////////////////////////////////////////
//写数据子函数
voidwrite_data(ucharData)
{
check_LCD_busy();
RW=0;
RS=1;
delay
(1);
P0=Data;
EN=1;
delay
(1);
EN=0;
}
/////////////////////////////////////////////////////////////////
//LCM清屏函数
voidlcdClear(void)
{
write_com(0x01);
}
////////////////////////////////////////////////////////////////
//LCM复位函数
voidreset()
{
RES=0; //复位
delay
(1); //延时
RES=1; //复位置高
delay(10);
}
///////////////////////////////////////////////////////////////
//显示汉字
voiddispString(ucharX,Y,uchar*msg) //X为哪一行,Y为哪一列。
msg为汉字
{
if(X==0) X=0x80; //第一行,汉字显示坐标
elseif(X==1)X=0x90; //第二行
elseif(X==2)X=0x88; //第三行
else X=0x98; //第四行
Y=X+Y; //Y为1往右移一位
write_com(Y); //写入坐标
while(*msg)
{
write_data(*msg++);//显示汉字
}
}
///////////////////////////////////////////////////////////////
//显示图象
voiddisppicture(ucharcode*adder)
{
uinti,j;
//*******显示上半屏内容设置
for(i=0;i<32;i++) //上半屏32个列地址
{
write_com(0x80+i); //SET 垂直地址VERTICALADD
write_com(0x80); //SET 水平地址HORIZONTALADD
for(j=0;j<16;j++)
{
write_data(*adder);
adder++;
}
}
//*******显示下半屏内容设置
for(i=0;i<32;i++) //
{
write_com(0x80+i); //SET垂直地址VERTICALADD
write_com(0x88); //SET水平地址HORIZONTALADD
for(j=0;j<16;j++)
{
write_data(*adder);
adder++;
}
}
}
///////////////////////////////////////////////////////////////
//LCD字库初始化函数
voidlcdinit_str(void)
{
delay(40); //大于40MS的延时程序
PSB=1; //设置为8BIT并口工作模式
delay
(1); //延时
reset(); //复位
write_com(0x30); //ExtendedFunctionSet:
8BIT设置,RE=0:
basicinstructionset,G=0:
graphicdisplayOFF
delay(100); //大于100uS的延时程序
write_com(0x30); //FunctionSet
delay(37); ////大于37uS的延时程序
write_com(0x08); //DisplayonControl
delay(100); //大于100uS的延时程序
write_com(0x10); //CursorDisplayControl光标设置
delay(100); //大于100uS的延时程序
write_com(0x0C); //DisplayControl,D=1,显示开
delay(100); //大于100uS的延时程序
write_com(0x01); //DisplayClear
delay(10); //大于10mS的延时程序
write_com(0x06); //EnryModeSet,光标从右向左加1位移动
delay(100); //大于100uS的延时程序
}
//////////////////////////////////////////////////////////////////
//LCD图片(扩展)初始化函数
voidlcdinit_pic(void)
{
delay(40); //大于40MS的延时程序
PSB=1; //设置为8BIT并口工作模式
delay
(1); //延时
reset();
write_com(0x36); //ExtendedFunctionSetRE=1:
extendedinstruction
delay(100); //大于100uS的延时程序
write_com(0x36); //ExtendedFunctionSet:
RE=1:
extendedinstructionset
delay(37); ////大于37uS的延时程序
write_com(0x3E); //EXFUNCTION(DL=8BITS,RE=1,G=1)
delay(100); //大于100uS的延时程序
write_com(0x01); //CLEARSCREEN
delay(100); //大于100uS的延时程序
}
/////////////////////////////////////////////////////////////////
voidmain()
{
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像素后也不是太明显...
图片取模
图片代码———————