[讨论] 玩转12864液晶(2)--显示图片,画点,画任意直线

火龙果   2009-11-23 10:09 楼主
通过上一篇的实验,相信大家都掌握了显示字符的基本用法。

下面我们来看一下12864液晶更高级的用法。

首先是它的绘图功能

让我们先来显示一整副的图片吧,也就是128x64大小。
在使用绘图功能时,先要打开扩充指令集,然后再打开绘图功能。接着就是送数据显示了。这里我们首先要弄明白ST7920的显示坐标关系。其显示坐标如下。
11.jpg

从图中可以看出,X方向共有8个字(16个字节)Y方向共有0~31 行 分为上下两个屏。
弄懂了之后我们就可以依照此坐标来显示一整屏的图片了。
随便用一个图片的提取转换软件,讲一副126X64大小的图片转换成字节数据,总共字节大小为128*64/8 = 1024个字节。
下面我们来看看这个显示整屏图像的函数

  1. void v_Lcd12864DrawPicture_f( unsigned char code *pPicture )
  2. {
  3.     unsigned char i, j, k ;
  4.     for( i = 0 ; i < 2 ; i++ )//分上下两屏写
  5.     {
  6.         for( j = 0 ; j < 32 ; j++ )
  7.         {
  8.             v_Lcd12864SendCmd_f( 0x80 + j ) ;//写Y坐标
  9.             if( i == 0 )                    //写X坐标
  10.             {
  11.                 v_Lcd12864SendCmd_f( 0x80 ) ;
  12.             }
  13.             else
  14.             {
  15.                 v_Lcd12864SendCmd_f( 0x88 ) ;
  16.             }
  17.             for( k = 0 ; k < 16 ; k++ )      //写一整行数据
  18.             {
  19.                 v_Lcd12864SendData_f( *pPicture++ ) ;
  20.             }
  21.         }
  22.     }
  23.     v_Lcd12864SendCmd_f( 0x30 ) ;
  24. }



看看效果图片如下:显示一个人的图像
22.jpg

下面来看看如何在任意一个位置显示或者是擦除一个点
对于12864这种二值显示屏来说,其显示状态无外乎显示和不显示一个点这两种状态。而在任意位置画点,是我们随心所欲的画线,画圆,画矩形的等GUI函数的基础。
为了让这个位置有一个参考点,我们有必要定义一个坐标系
在这里,我定义的坐标系如下
0,0------------------------------------127,0
|                                                          |
|                                                          |
|                                                          |
|                                                          |
0,63----------------------------------127,63
0,0代表屏幕的左上角,127,63代表屏幕的右下角。
对于屏幕上面任意一个点,如果我们想要点亮它,必须先读出此点的状态,然后再修改该点,最后送出去,即 读----修改----写。按照这个步骤,然后再运用C语言中的位操作运算符 可以很方便的完成画点的函数。
由于画点函数涉及到读ST7920内部RAM的操作,因此,我们必须先要完成这个读数据的函数
具体实现过程如下:

  1. unsigned char u8_Lcd12864ReadByte_f( void )
  2. {
  3.     unsigned char byReturnValue ;
  4.     v_Lcd12864CheckBusy_f() ;
  5.     io_LCD12864_DATAPORT = 0xff ;
  6.     SET_DATA
  7.     SET_READ
  8.     CLR_EN
  9.     SET_EN
  10.     byReturnValue = io_LCD12864_DATAPORT ;
  11.     CLR_EN
  12.     return byReturnValue ;   
  13. }

然后是画点的函数,其实现过程如下:

  1. void v_Lcd12864DrawPoint_f( unsigned char X, unsigned char Y, unsigned char Color )
  2. {
  3.     unsigned char Row , Tier , Tier_bit    ;
  4.     unsigned char  ReadOldH, ReadOldL  ;
  5.     v_Lcd12864SendCmd_f( 0x34 ) ;
  6.     v_Lcd12864SendCmd_f( 0x36 ) ;
  7.     Tier = X >> 4 ;   
  8.     Tier_bit = X & 0x0f ;
  9.     if( Y < 32 )
  10.     {
  11.         Row = Y ;
  12.     }
  13.     else
  14.     {
  15.         Row = Y - 32 ;
  16.         Tier += 8 ;
  17.     }
  18.     v_Lcd12864SendCmd_f( Row + 0x80 ) ;
  19.     v_Lcd12864SendCmd_f( Tier + 0x80 ) ;
  20.     u8_Lcd12864ReadByte_f() ;
  21.     ReadOldH = u8_Lcd12864ReadByte_f() ;
  22.     ReadOldL = u8_Lcd12864ReadByte_f() ;
  23.     v_Lcd12864SendCmd_f( Row + 0x80 )    ;
  24.     v_Lcd12864SendCmd_f( Tier + 0x80 ) ;
  25.     if( Tier_bit < 8 )
  26.     {
  27.         switch( Color)
  28.         {
  29.             case 0 : ReadOldH &=( ~( 0x01 << ( 7 - Tier_bit ))) ; break ;
  30.             case 1 : ReadOldH |= ( 0x01 << ( 7 - Tier_bit ))  ;  break ;
  31.             case 2 : ReadOldH ^= ( 0x01 << ( 7 - Tier_bit ))    ; break ;
  32.             default : break ;   
  33.         }
  34.         v_Lcd12864SendData_f( ReadOldH ) ;
  35.         v_Lcd12864SendData_f( ReadOldL ) ;
  36.     }
  37.     else
  38.     {
  39.         switch(Color)
  40.         {
  41.             case 0 : ReadOldL &= (~( 0x01 << ( 15 - Tier_bit ))) ;  break ;
  42.             case 1 : ReadOldL |= ( 0x01 << ( 15 - Tier_bit ))    ;  break ;
  43.             case 2 : ReadOldL ^= ( 0x01 << ( 15 - Tier_bit ))  ;  break ;
  44.             default : break ;
  45.         }
  46.         v_Lcd12864SendData_f( ReadOldH ) ;
  47.         v_Lcd12864SendData_f( ReadOldL ) ;
  48.     }
  49.     v_Lcd12864SendCmd_f( 0x30 )    ;
  50. }



有了画点的函数之后,一切似乎都变得简单了,因为点是一切复杂图形的最基本的组成单位。
下面我们就在这个画点函数的基础上,实现画水平线和垂直线的两个函数。
画水平线:

  1. void v_Lcd12864DrawLineX_f( unsigned char X0, unsigned char X1, unsigned char Y, unsigned char Color )
  2. {    unsigned char Temp ;
  3.     if( X0 > X1 )
  4.     {
  5.         Temp = X1 ;
  6.         X1  = X0 ;
  7.         X0  = Temp ;
  8.     }
  9.     for( ; X0 <= X1 ; X0++ )
  10.     v_Lcd12864DrawPoint_f( X0, Y, Color ) ;   
  11. }


画垂直线:

  1. void v_Lcd12864DrawLineY_f( unsigned char X, unsigned char Y0, unsigned char Y1, unsigned char Color )
  2. {
  3.     unsigned char Temp ;
  4.     if( Y0 > Y1 )
  5.     {
  6.         Temp = Y1 ;
  7.         Y1  = Y0 ;
  8.         Y0  = Temp ;
  9.     }
  10.     for(; Y0 <= Y1 ; Y0++)
  11.     v_Lcd12864DrawPoint_f( X, Y0, Color)    ;
  12. }


下面我们就用以上两个画线函数,在液晶屏上面画一个表格出来

  1. v_Lcd12864DrawLineX_f( 0, 127 , 0, 1 ) ;
  2.   v_Lcd12864DrawLineX_f( 0, 127 , 7, 1 ) ;
  3.   v_Lcd12864DrawLineX_f( 0, 127 , 15, 1 ) ;
  4.   v_Lcd12864DrawLineX_f( 0, 127 , 23, 1 ) ;
  5.   v_Lcd12864DrawLineX_f( 0, 127 , 31, 1 ) ;
  6.   v_Lcd12864DrawLineX_f( 0, 127 , 39, 1 ) ;
  7.   v_Lcd12864DrawLineX_f( 0, 127 , 47, 1 ) ;
  8.   v_Lcd12864DrawLineX_f( 0, 127 , 55, 1 ) ;
  9.   v_Lcd12864DrawLineX_f( 0, 127 , 63, 1 ) ;
  10.   v_Lcd12864DrawLineY_f( 0, 0 , 63, 1 ) ;
  11.   v_Lcd12864DrawLineY_f( 15, 0 , 63, 1 ) ;
  12.   v_Lcd12864DrawLineY_f( 31, 0 , 63, 1 ) ;
  13.   v_Lcd12864DrawLineY_f( 47, 0 , 63, 1 ) ;
  14.   v_Lcd12864DrawLineY_f( 63, 0 , 63, 1 ) ;
  15.   v_Lcd12864DrawLineY_f( 79, 0 , 63, 1 ) ;
  16.   v_Lcd12864DrawLineY_f( 95, 0 , 63, 1 ) ;
  17.   v_Lcd12864DrawLineY_f( 111, 0 , 63, 1 ) ;
  18.   v_Lcd12864DrawLineY_f( 127, 0 , 63, 1 ) ;


看看显示效果
33.jpg

怎么样,你的实现了吗?
只能画水平线和垂直线似乎太简单和单调点了。
要是能在任意两点间画一条直线就好了,那样我们就可以做很多事情了。
下面就让我们去实现它!

在这里我们采用Bresenham画线算法,关于这个算法,网上有很多资料,请大家以它为关键字到网上去搜索,在这里就不啰嗦了。
下面是算法的具体实现过程:

  1. void v_Lcd12864DrawLine_f( unsigned char StartX, unsigned char StartY, unsigned char EndX, unsigned char EndY, unsigned char Color )
  2. {
  3.     int t, distance;      /*根据屏幕大小改变变量类型(如改为int型)*/
  4.     int x = 0 , y = 0 , delta_x, delta_y ;
  5.     char incx, incy ;
  6.     delta_x = EndX - StartX ;
  7.     delta_y = EndY - StartY ;
  8.     if( delta_x > 0 )
  9.     {
  10.         incx = 1;
  11.     }
  12.     else if( delta_x == 0 )
  13.     {
  14.         v_Lcd12864DrawLineY_f( StartX, StartY, EndY, Color ) ;
  15.         return ;
  16.     }
  17.     else
  18.     {
  19.         incx = -1 ;
  20.     }
  21.     if( delta_y > 0 )
  22.     {
  23.         incy = 1 ;
  24.     }
  25.     else if(delta_y == 0 )
  26.     {
  27.         v_Lcd12864DrawLineX_f( StartX, EndX, StartY, Color ) ;   
  28.         return ;
  29.     }
  30.     else
  31.     {
  32.         incy = -1 ;
  33.     }
  34.     delta_x = ABS( delta_x );   
  35.     delta_y = ABS( delta_y );
  36.     if( delta_x > delta_y )
  37.     {
  38.         distance = delta_x ;
  39.     }
  40.     else
  41.     {
  42.         distance = delta_y ;
  43.     }
  44.     v_Lcd12864DrawPoint_f( StartX, StartY, Color ) ;   
  45.     /* Draw Line*/
  46.     for( t = 0 ; t <= distance+1  ; t++ )
  47.     {
  48.         v_Lcd12864DrawPoint_f( StartX, StartY, Color ) ;
  49.         x += delta_x ;
  50.         y += delta_y ;
  51.         if( x > distance )
  52.         {
  53.             x -= distance ;
  54.             StartX += incx ;
  55.         }
  56.         if( y > distance )
  57.         {
  58.             y -= distance ;
  59.             StartY += incy ;
  60.         }
  61.     }
  62. }


老规矩,我们用这个函数随便画任意斜率的几条直线看看。

  1. v_Lcd12864DrawLine_f( 0, 0, 127, 63, 1 ) ;
  2.   v_Lcd12864DrawLine_f( 0, 63, 127, 0 , 1 ) ;
  3.   v_Lcd12864DrawLine_f( 12, 0, 127, 63, 1 ) ;
  4.   v_Lcd12864DrawLine_f( 52, 63, 127, 0 , 1 ) ;
  5.   v_Lcd12864DrawLine_f( 32, 63, 98, 0, 1 ) ;
  6.   v_Lcd12864DrawLine_f( 67, 0, 127, 63 , 1 ) ;


下面是具体的效果图:
44.jpg

至此,12864的用法就告一段落了,这篇文章仅仅希望是起到一个抛砖引玉的作用,希望各位工程师和同学能够灵活的运用12864液晶屏,并把自己的经验拿出来一起交流学习。

回复评论 (14)

顶一下!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
点赞  2009-11-25 08:53
我还停留在初级阶段!
点赞  2009-11-25 09:34
我还停留在初级阶段!
点赞  2010-1-18 11:16
非常感谢,跟着你分享的程序,我已经差不多掌握了。。
技术在交流中成长。。。。
点赞  2010-2-5 10:59
那个图像,这么像世界地图啊
点赞  2010-2-5 11:25
这种程序关键要和驱动结合,效率是第一位的,虽然算法能实现但是驱动很麻烦就不好了。
比如有些点屏可以连续写入数据,有些就要分左上左下有上有下,这些都是要考虑的问题。
点赞  2010-2-5 11:31
非常牛,长见识了……
点赞  2010-2-5 20:36
受教了
点赞  2010-4-21 11:29
顶……
点赞  2010-4-21 12:45
受到警告

真彩时代

提示: 该帖被管理员或版主屏蔽
点赞  2010-4-21 13:34
very good
点赞  2010-5-6 21:36
呵呵, 在新势力看过这个贴子。
Bresenham画线算法实现得很好,再这个基础上很容易就可以扩展成画圆的算法,网上很多资料,当时学的时候也是查找了很多资料才最后完成项目。
点赞  2010-5-8 14:04
我晕,新势力转过来的!
点赞  2012-3-24 04:17
LZ,我按照你的这样做,画出的横线是正常的,但是竖线太粗来了,九条竖线把整个液晶屏都占满了,苦思不得其解啊,求LZ赐教啊!!!!!
点赞  2013-8-12 16:12
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复