12864画点画线算法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
通过上一篇的实验,相信大家都掌握了显示字符的基本用法。 下面我们来看一下 12864 液晶更高级的用法。 首先是它的绘图功能。 让我们先来显示一整副的图片吧,也就是 128x64 大小。 在使用绘图功能时,先要打开扩充指令集,然后再打开绘图功能。接着就是送数据显示了。 这里我们首先要弄明白 ST7920 的显示坐标关系。其显示坐标如下。
Fra Baidu bibliotek
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 ) ; } 看看效果图片如下:显示一个人的图像
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 ;
| | 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 ) ;
} 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_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 ; }
下面来看看如何在任意一个位置显示或者是擦除一个点 对于 12864 这种二值显示屏来说,其显示状态无外乎显示和不显示一个点这两种状态。而 在任意位置画点,是我们随心所欲的画线,画圆,画矩形的等 GUI 函数的基础。 为了让这个位置有一个参考点,我们有必要定义一个坐标系 在这里,我定义的坐标系如下 0,0------------------------------------127,0 | | | | | |
从图中可以看出,X 方向共有 8 个字(16 个字节)Y 方向共有 0~31 行 分为上下两个屏。 弄懂了之后我们就可以依照此坐标来显示一整屏的图片了。 随便用一个图片的提取转换软件,讲一副 128X64 大小的图片转换成字节数据,总共字节大 小为 128*64/8 = 1024 个字节。 下面我们来看看这个显示整屏图像的函数 void v_Lcd12864DrawPicture_f( unsigned char code *pPicture ) { unsigned char i, j, k ; for( i = 0 ; i < 2 ; i++ )//分上下两屏写 {
Fra Baidu bibliotek
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 ) ; } 看看效果图片如下:显示一个人的图像
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 ;
| | 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 ) ;
} 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_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 ; }
下面来看看如何在任意一个位置显示或者是擦除一个点 对于 12864 这种二值显示屏来说,其显示状态无外乎显示和不显示一个点这两种状态。而 在任意位置画点,是我们随心所欲的画线,画圆,画矩形的等 GUI 函数的基础。 为了让这个位置有一个参考点,我们有必要定义一个坐标系 在这里,我定义的坐标系如下 0,0------------------------------------127,0 | | | | | |
从图中可以看出,X 方向共有 8 个字(16 个字节)Y 方向共有 0~31 行 分为上下两个屏。 弄懂了之后我们就可以依照此坐标来显示一整屏的图片了。 随便用一个图片的提取转换软件,讲一副 128X64 大小的图片转换成字节数据,总共字节大 小为 128*64/8 = 1024 个字节。 下面我们来看看这个显示整屏图像的函数 void v_Lcd12864DrawPicture_f( unsigned char code *pPicture ) { unsigned char i, j, k ; for( i = 0 ; i < 2 ; i++ )//分上下两屏写 {