欢迎访问杭州精显科技有限公司液晶显示屏网站! 设为首页 | 收藏本站| 网站地图
全国统一服务热线
19157925796


技术知识

您只需一个电话我们将推荐性价比高的液晶屏产品选型,让您花合理的价格,达到预期的效果

全国统一服务热线
19157925796

产品动态

联系我们

全国统一服务热线:

19157925796

客服QQ:3234659108

手机:19157925796

地址:浙江省杭州市西湖区文一西路830号蒋村商务中心B楼2-8007室

当前位置: 主页 > 资讯中心 > 技术知识 > >

在点阵屏上面绘图—LCD12864控制的详解

文章出处:原创 人气:发表时间:2017-08-28


怎样在点阵屏上绘图——基于LCD12864

LCD12864:12864液晶屏

   — 预备知识 — 
其实,本文应该算是计算机图形学中的一个具体分支,所以,计算机图形学的最基本要求就是本文的基本要求了,由于考虑到各位大兄弟的胃口,我就多啰嗦下。 

1、位操作 
像LCD12864这一种二值屏幕,我们往往习惯于使用1个字节表示连续的8个点,1对应的对应位被点亮,0则表示不亮,所以对于图形的操作最基本的手段那就是位操作了。 
复习一下常用的位操作,假设Dis表示是某一个现存地址的内容 
Dis = Dis~     黑白颠倒 
Dis &= ~(1<<n) 第n处被擦去, 
Dis |= (1<<n)   第n处被画了一个点 
Dis ^= (1<<n)   如果n处是亮的,就变被擦掉;如果n处是空白的,就被点亮了…… 
…… 差不多也就是这些 

2、作图的原理 
点是一切光栅显示设备最基本的要素,所有的操作也都是以点为基础的,所以要学会如何去利用点构成线、圆、填充就是必须要掌握的——几何不能够太差。 
还有,结合屏幕硬件的特点,对于算法进行优化的一些方法也是需要去掌握的。比方说:如何填充之类的……后面还会针对于LCD12864来作详细的介绍。 

3、人机交互学 
虽然说很多的人都没有去实实在在的学过这门功课,但是多多少少还是对于界面应该都有些许了解。如何去利用手中的基本操作函数来做出一些特效?如何安排窗体?如何绘制图形界面的一些基本元素,例如按钮,
甚至如何的显示汉字,都是人际交互学需要教会你的——总而言之,如果你是没有学过这一门课程,你的产品 只有你自己一个人用的话——那就跟着感觉走,没错的。 

4、最最重要的物质基础 
你需要掌握一种单片机,掌握到一种点阵屏幕。 

怎样在点阵屏上绘图——基于LCD12864
Chapter One  

— 从点滴开始 —  



前面就已经说过了,对于栅格的显示设备来说,点是一切图形的基础,说白了,不会在液晶屏幕的任意位置随心所欲的画点就不能够说你已经掌握了某一种液晶屏幕的使用了。事实上,根据笔者的一些经验,尝试画点
往往就能暴露出很多的问题。就拿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); 

}

12864图形屏
怎样在点阵屏上绘图——基于LCD12864
特别说明一下,关于贪食蛇范例的问题,这篇文章里面只会简单得提及一下。 

作为嵌入式系统开发的一个范例,我会另外开一个帖子详细说明开发过程。 
这个范例将作为介绍嵌入式系统开发方法的一个很好的例子,用于解释一个系统和一段表示您调通了某一个功能的代码之间有什么区别,同时也将介绍嵌入式开发系统的几种模式(超级循环、调度器),顺便侃一侃时间驱动的系统RTOS (Real Time Operation System实时操作系统)和RTS(Real Time System)实时系统。
怎样在点阵屏上绘图——基于LCD12864
[本章导读] 

直线由点构成,更精确的说,直线是由靠近这条线的像素构成。这就引出一个问题,究近那些点算是靠近一条直线;哪些点不算是靠近一条直线,这必须使用一种算法作为依据。实际上,图形学算法和纯几何算法还是有很大差别的,问题就出在一个离散化上面,说白了,你画出的直线很可能是一组波动厉害的锯齿象素群而不是一条看起来有规则变化的直线。 
当然,太过于理论的东西对我们是没有多少实际价值的。下面,我就介绍两种常见的画线思路,一种就是最容易被想到的直线方程计算的方法,另外一种则是被称为布兰森汉姆(Bresenham)的计算机图形学主流算法。 
   
在介绍完这两种算法以后,我们会针对LCD12864的硬件结构为例子,介绍,具体算法的实现和优化。
怎样在点阵屏上绘图——基于LCD12864
首先,我们从最基本的数学算法说起。 
如果我们使用公式y = kx + b来作为绘图的依据,那么就需要分3种情况:水平直线,斜率为0;垂直直线,斜率为五穷达(或者说k不存在);普通直线。 
假设我们已经知道直线的起始坐标点(Xbegin,Ybegin)和终点(Xend,Yend),x,y,是当前的坐标点,如果我们通过增加x反算出y的方法的话,这个公式就可以很容易转换为伪代码。 
LineMode 为直线的类型:水平,垂直,普通 
if Xbegin == Xend then LineMode = 水平 
elseif Ybegin = Yend   then LineMode = 垂直 
else k = (Yend - Ybegin) / (Xend - Xbegin) 

switch LineMode 
   case 水平 
      for x = Xbegin to Xend 
         在x,Ybegin处画点 
   case 垂直 
      for y = Ybegin to Yend 
         在Ebegin,y处画点 
   default: 
      for x = Xbegin to Xend 
      { 
         y = kx + b 
         在x,y处画点 
      } 

非常简单,不是么?注意,大部分情况下,这个算法存在很多乘法和除法,对单片机系统来说,可能不是那么合适哦。画出的点也基本令人满意。
怎样在点阵屏上绘图——基于LCD12864
有了上面的代码垫底,我想很多人都可以放心了,因为大不了跑一个高速晶振也能快速的画出直线,否则你还需要耐心的阅读下面的算法。 

首先,我们给出这个算法的伪代码。 
在(x1,y1)到(x2,y2)之间画一条直线 

dx 是x到终点横坐标的距离 
dy 是y到终点纵坐标的距离 
ix 是dx的绝对值 
iy 是dy的绝对值 
inc是dx和dy中较大的那个 
plot是是否要画一个点的标志位,boolean变量 

plotx 是当前点所在的横坐标 
ploty 是当前点所在的纵坐标 
  
plotx = x1 
ploty = y1 
x = 0 
y = 0 

在 plotx,ploty画一个点——起点 

for i = 0 to inc 增量1 
   x += ix 
   y += iy 
   plot = false 

   if x > inc then 
          plot = true 
          x -= inc 
            if dx > 0 then plotx ++ 
            if dx < 0 then plotx -- 
  
   if y > inc then 
          plot = true 
          y -= inc 
            if dy > 0 then ploty ++ 
            if dy < 0 then ploty -- 

   if plot == true then 在(plotx,ploty)处画点 
这就是计算机图形学中流行的布兰森汉姆(Bresenham)算法,他的意图就是采用离散的整数增量来代替斜率增量计算,学习这个算法,最好的方法不是看多少理论,而是按照上面的伪代码自己完成一条直线的绘制工作,你就能心领神会了——不是小弟我偷懒。 

所有的计算都是简单得整数计算,代码效率自然不用小弟我罗嗦哈。
怎样在点阵屏上绘图——基于LCD12864
俗语说,巧妇难为无米之炊,有了点再有了直线算法,距离窗体的绘制不远了,But Stop! “没有最好,但求更好”这是我们做系统开发应该谨记的一条准则。 
有了上面的算法还不够,毕竟,他们只是一些伪代码,针对不同的屏幕——准确地说,是针对不同的显存映射方式,有不同的算法优化方法。究竟有哪些优化方法暂且不论,其实他们都是一个原理,我想先解释一下,为什么我们需要优化算法,或者说,我们需要先弄清楚是什么地方产生了冗余。 

还记得黑白点阵屏幕的显存映射方式么?它简单的使用1个字节表示8个坐标点,同时这1个字节是沿着屏幕的短边方向映射的,所以当我们想画一条垂直的直线时,对于每一个牵涉到的字节都有可能要重复的操作8次之多,这种操作不是简单的画线,而是要先读取再计算最后再写这样的复合操作,重复8次只是为了把整个字节变黑显然是一种超级不可容忍的冗余——大家都知道,直接把这个字节读取一次,计算一次,再写一次就完成了。基于这种思想,我们需要把这种特殊情况单独提取出来,重新优化代码……具体优化代码就留给大家做作业了哈。 

同时补充一下,这样做,不是只对画线作了优化,事实上,在后面,矩形的填充里面,这种优化会极大地提高速度,因为填充的本质就是画线(点)…… 
 



群创液晶屏 友达液晶屏 京东方液晶屏 京瓷液晶屏 龙腾液晶屏 天马液晶屏 奇美液晶屏 三菱液晶屏