— 从点滴开始 —
前面已经说过,对于栅格显示设备来说,点是一切图形的基础,说白了,不会在液晶屏幕的任意位置随心所欲的画点就不能说你已经掌握了某种液晶屏幕的使用了。事实上,根据笔者的经验,尝试画点往往能暴露出很多问题。就拿LCD12864来说吧,一段时间,笔者总是发现,在片切换的地方有一点点错误……
在说明如何画点之前先说明一个概念:显示缓冲区。
所谓显示缓冲区,顾名思义,就是显示数据的一个缓冲地带。一般情况下,我们绘图也是直接对这块缓冲区域进行操作的。
很多朋友就要提问了:我们为什么不直接对屏幕操作呢?
事实上,LCD本身存在一块显示存储器,也可以被认为是一个显示缓存,直接写在这个存储器的数据并非直接显示出来,而多半需要一个显示指令来影射一下。我们有时候也通过这种类似的技术来实现很大的图片的显示——先画好,再拿给大家看,让人以为你是一下就画好的,当然如果你刻意需要那种图片被“画”出来的效果,则不在讨论之列。问题就在于,LCD是片外资源,对其存储器的访问可以被认为是对片外存储器的访问,其速度显然没有对片内SRAM的操作速度要快。如果我们使用那种常用的串行方式来作图(所谓串行方式作图,就是绘图指令的执行和系统的其他操作时串行的,指令不完成,其它操作就不回被执行),那么对于一些实时性要求高的系统来说就会造成重大隐患——甚至是不符合要求的;如果开辟一段片内存储空间和LCD的存储器一一对应,在相同的时间段内,花费相同的资源来保持着两个存储空间的一致性,那么就可以保证实时系统的稳定和可靠,保证画面显示的正常(因为允许跳帧嘛^_^)。这就是我们为什么需要另外在片内选取一个显示缓冲区的原因。
以后,我们的操作都是针对显示缓冲区的。显示缓冲区将成为一个概念,无论这个缓冲区位于SRAM内还是LCD内部。比方说,我们需要一个低成本的游戏机——如做一个贪食蛇游戏机送给老师作为课程设计,或者送给小侄子,那么,M8甚至是M48成为首选,问题是,M8绝对无法开辟出一个8 * 128 = 1K大小的数组,所以,这个时候,认定LCD的片内存储器作为显示缓冲区就是不二的选择——好在贪食蛇速度太快了也没有办法玩哈。
绕了这么多的弯子,究竟如何画点呢?我还想多补充一个原因,关于,为什么需要显示缓冲区,由于点阵屏幕使用的是1个字节来表示8个点的,如果你想使得一个字节中的某一个点被操作而不影响别的点,你起码要知道原来这个位置是什么内容,对于有些使用595级联的LCD设备来说,无法直接从LCD读取现存数据,所以开辟一个缓冲区,从中获得信息加以加工以后再放回去,是这种设备处理显示图形的唯一方法——当然我们没有那么惨,但是从LCD中获取所需点所在字节的内容还是必须的,不然你画一个点就会破坏一条线上所有的数据。
好的,那么我们实际上已经明确了如何去画一个点。
首先,根据用户给出的坐标计算出所要画的点所在现存内的地址偏移量;
然后,我们读出该地址内的数据;
再次,根据用户所需画点的类型(擦除、画点、反相点)来进行相应的操作获得一个新的数据;
最后,将该数据写回原来的地址。
不是很难吧?
---------------------------------------------
LCD12864补充知识
1、关于坐标系。很多人,包括笔者,一开始都看不懂LCD12864的内存影射方式,感觉X、Y似乎不是那么回事。后来才发现,只要把屏幕竖着放一切就好懂了。X还是横轴,Y还是竖轴……但是这显然不符合我们的习惯,我们习惯于长的那个边作为横轴,所以需要一点点坐标之间的转换。
假设输入的是正常习惯的坐标 X,Y DX DY就是LCD上的坐标,那么转换关系是
char DX = (Y >> 3); //计算出属于哪个字节
char BX = Y - (DX << 3); //属于该字节的哪个位
char DY = X;
2、读取12864的数据的时候,一定要注意,E信号要在一个下降延之后持续拉高,然后才能正常独处数据;假设直接拉高,的确也能读出数据,但是,等着抓头皮,发帖子“[跪求]大侠帮忙关于12864——请使用明确的大标题……”^_^
---------------------------------------------
废话少说(说的不少了),看源代码:
# define LCD12864_Graphic_Draw 0x01
# define LCD12864_Graphic_Clear 0x00
# define LCD12864_Graphic_Not 0x02
……
void LCD12864Draw(char X,char Y,char Type)
{
char DX = (Y >> 3); //计算出属于哪个字节
char BX = Y - (DX << 3); //计算出属于字节哪一位
char TempData = 0;
LCD12864_ChooseBoth;
setX(DX);
if (X > 63)
{
LCD12864_ChooseCS2;
X -= 64;
}
else
{
LCD12864_ChooseCS1;
}
setY(X);
TempData = getLCD12864Data();
switch (Type)
{
case LCD12864_Graphic_Clear:
TempData &= ~(1<<BX);
break;
case LCD12864_Graphic_Not:
TempData ^= (1 << BX);
break;
default:
TempData |= (1 << BX);
}
setY(X);
sendDataToLCD(TempData);
}
|