通过上一篇的实验,相信大家都掌握了显示字符的基本用法。
下面我们来看一下12864液晶更高级的用法。
首先是它的绘图功能
。
让我们先来显示一整副的图片吧,也就是128x64大小。
在使用绘图功能时,先要打开扩充指令集,然后再打开绘图功能。接着就是送数据显示了。这里我们首先要弄明白ST7920的显示坐标关系。其显示坐标如下。
从图中可以看出,X方向共有8个字(16个字节)Y方向共有0~31 行 分为上下两个屏。
弄懂了之后我们就可以依照此坐标来显示一整屏的图片了。
随便用一个图片的提取转换软件,讲一副126X64大小的图片转换成字节数据,总共字节大小为128*64/8 = 1024个字节。
下面我们来看看这个显示整屏图像的函数
- void v_Lcd12864DrawPicture_f( unsigned char code *pPicture )
- {
- unsigned char i, j, k ;
- for( i = 0 ; i < 2 ; i++ )//分上下两屏写
- {
- for( j = 0 ; j < 32 ; j++ )
- {
- v_Lcd12864SendCmd_f( 0x80 + j ) ;//写Y坐标
- if( i == 0 ) //写X坐标
- {
- v_Lcd12864SendCmd_f( 0x80 ) ;
- }
- else
- {
- v_Lcd12864SendCmd_f( 0x88 ) ;
- }
- for( k = 0 ; k < 16 ; k++ ) //写一整行数据
- {
- v_Lcd12864SendData_f( *pPicture++ ) ;
- }
- }
- }
- v_Lcd12864SendCmd_f( 0x30 ) ;
- }
看看效果图片如下:显示一个人的图像
下面来看看如何在任意一个位置显示或者是擦除一个点
对于12864这种二值显示屏来说,其显示状态无外乎显示和不显示一个点这两种状态。而在任意位置画点,是我们随心所欲的画线,画圆,画矩形的等GUI函数的基础。
为了让这个位置有一个参考点,我们有必要定义一个坐标系
在这里,我定义的坐标系如下
0,0------------------------------------127,0
| |
| |
| |
| |
0,63----------------------------------127,63
0,0代表屏幕的左上角,127,63代表屏幕的右下角。
对于屏幕上面任意一个点,如果我们想要点亮它,必须先读出此点的状态,然后再修改该点,最后送出去,即 读----修改----写。按照这个步骤,然后再运用C语言中的位操作运算符 可以很方便的完成画点的函数。
由于画点函数涉及到读ST7920内部RAM的操作,因此,我们必须先要完成这个读数据的函数
具体实现过程如下:
- unsigned char u8_Lcd12864ReadByte_f( void )
- {
- unsigned char byReturnValue ;
- v_Lcd12864CheckBusy_f() ;
- io_LCD12864_DATAPORT = 0xff ;
- SET_DATA
- SET_READ
- CLR_EN
- SET_EN
- byReturnValue = io_LCD12864_DATAPORT ;
- CLR_EN
- return byReturnValue ;
- }
然后是画点的函数,其实现过程如下:
- void v_Lcd12864DrawPoint_f( unsigned char X, unsigned char Y, unsigned char Color )
- {
- unsigned char Row , Tier , Tier_bit ;
- unsigned char ReadOldH, ReadOldL ;
- v_Lcd12864SendCmd_f( 0x34 ) ;
- v_Lcd12864SendCmd_f( 0x36 ) ;
- Tier = X >> 4 ;
- Tier_bit = X & 0x0f ;
- if( Y < 32 )
- {
- Row = Y ;
- }
- else
- {
- Row = Y - 32 ;
- Tier += 8 ;
- }
- v_Lcd12864SendCmd_f( Row + 0x80 ) ;
- v_Lcd12864SendCmd_f( Tier + 0x80 ) ;
- u8_Lcd12864ReadByte_f() ;
- ReadOldH = u8_Lcd12864ReadByte_f() ;
- ReadOldL = u8_Lcd12864ReadByte_f() ;
- v_Lcd12864SendCmd_f( Row + 0x80 ) ;
- v_Lcd12864SendCmd_f( Tier + 0x80 ) ;
- if( Tier_bit < 8 )
- {
- switch( Color)
- {
- case 0 : ReadOldH &=( ~( 0x01 << ( 7 - Tier_bit ))) ; break ;
- case 1 : ReadOldH |= ( 0x01 << ( 7 - Tier_bit )) ; break ;
- case 2 : ReadOldH ^= ( 0x01 << ( 7 - Tier_bit )) ; break ;
- default : break ;
- }
- v_Lcd12864SendData_f( ReadOldH ) ;
- v_Lcd12864SendData_f( ReadOldL ) ;
- }
- else
- {
- switch(Color)
- {
- case 0 : ReadOldL &= (~( 0x01 << ( 15 - Tier_bit ))) ; break ;
- case 1 : ReadOldL |= ( 0x01 << ( 15 - Tier_bit )) ; break ;
- case 2 : ReadOldL ^= ( 0x01 << ( 15 - Tier_bit )) ; break ;
- default : break ;
- }
- v_Lcd12864SendData_f( ReadOldH ) ;
- v_Lcd12864SendData_f( ReadOldL ) ;
- }
- v_Lcd12864SendCmd_f( 0x30 ) ;
- }
-
有了画点的函数之后,一切似乎都变得简单了,因为点是一切复杂图形的最基本的组成单位。
下面我们就在这个画点函数的基础上,实现画水平线和垂直线的两个函数。
画水平线:
- void v_Lcd12864DrawLineX_f( unsigned char X0, unsigned char X1, unsigned char Y, unsigned char Color )
- { unsigned char Temp ;
- if( X0 > X1 )
- {
- Temp = X1 ;
- X1 = X0 ;
- X0 = Temp ;
- }
- for( ; X0 <= X1 ; X0++ )
- v_Lcd12864DrawPoint_f( X0, Y, Color ) ;
- }
画垂直线:
- void v_Lcd12864DrawLineY_f( unsigned char X, unsigned char Y0, unsigned char Y1, unsigned char Color )
- {
- unsigned char Temp ;
- if( Y0 > Y1 )
- {
- Temp = Y1 ;
- Y1 = Y0 ;
- Y0 = Temp ;
- }
- for(; Y0 <= Y1 ; Y0++)
- v_Lcd12864DrawPoint_f( X, Y0, Color) ;
- }
下面我们就用以上两个画线函数,在液晶屏上面画一个表格出来
- v_Lcd12864DrawLineX_f( 0, 127 , 0, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 7, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 15, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 23, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 31, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 39, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 47, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 55, 1 ) ;
- v_Lcd12864DrawLineX_f( 0, 127 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 0, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 15, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 31, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 47, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 63, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 79, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 95, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 111, 0 , 63, 1 ) ;
- v_Lcd12864DrawLineY_f( 127, 0 , 63, 1 ) ;
看看显示效果
怎么样,你的实现了吗?
只能画水平线和垂直线似乎太简单和单调点了。
要是能在任意两点间画一条直线就好了,那样我们就可以做很多事情了。
下面就让我们去实现它!
在这里我们采用Bresenham画线算法,关于这个算法,网上有很多资料,请大家以它为关键字到网上去搜索,在这里就不啰嗦了。
下面是算法的具体实现过程:
- void v_Lcd12864DrawLine_f( unsigned char StartX, unsigned char StartY, unsigned char EndX, unsigned char EndY, unsigned char Color )
- {
- int t, distance; /*根据屏幕大小改变变量类型(如改为int型)*/
- int x = 0 , y = 0 , delta_x, delta_y ;
- char incx, incy ;
- delta_x = EndX - StartX ;
- delta_y = EndY - StartY ;
- if( delta_x > 0 )
- {
- incx = 1;
- }
- else if( delta_x == 0 )
- {
- v_Lcd12864DrawLineY_f( StartX, StartY, EndY, Color ) ;
- return ;
- }
- else
- {
- incx = -1 ;
- }
- if( delta_y > 0 )
- {
- incy = 1 ;
- }
- else if(delta_y == 0 )
- {
- v_Lcd12864DrawLineX_f( StartX, EndX, StartY, Color ) ;
- return ;
- }
- else
- {
- incy = -1 ;
- }
- delta_x = ABS( delta_x );
- delta_y = ABS( delta_y );
- if( delta_x > delta_y )
- {
- distance = delta_x ;
- }
- else
- {
- distance = delta_y ;
- }
- v_Lcd12864DrawPoint_f( StartX, StartY, Color ) ;
- /* Draw Line*/
- for( t = 0 ; t <= distance+1 ; t++ )
- {
- v_Lcd12864DrawPoint_f( StartX, StartY, Color ) ;
- x += delta_x ;
- y += delta_y ;
- if( x > distance )
- {
- x -= distance ;
- StartX += incx ;
- }
- if( y > distance )
- {
- y -= distance ;
- StartY += incy ;
- }
- }
- }
老规矩,我们用这个函数随便画任意斜率的几条直线看看。
- v_Lcd12864DrawLine_f( 0, 0, 127, 63, 1 ) ;
- v_Lcd12864DrawLine_f( 0, 63, 127, 0 , 1 ) ;
- v_Lcd12864DrawLine_f( 12, 0, 127, 63, 1 ) ;
- v_Lcd12864DrawLine_f( 52, 63, 127, 0 , 1 ) ;
- v_Lcd12864DrawLine_f( 32, 63, 98, 0, 1 ) ;
- v_Lcd12864DrawLine_f( 67, 0, 127, 63 , 1 ) ;
下面是具体的效果图:
至此,12864的用法就告一段落了,这篇文章仅仅希望是起到一个抛砖引玉的作用,希望各位工程师和同学能够灵活的运用12864液晶屏,并把自己的经验拿出来一起交流学习。