1. 程式人生 > >12864 顯示畫圓多種圖形

12864 顯示畫圓多種圖形

/*******************************************************************************************************/
//程式說明:本程式為12864(st7920)驅動程式,只實現了最簡單的顯示功能
/*******************************************************************************************************/
#include<reg52.h>
#include<intrins.h> //內含-NOP-函式
#include<stdlib.h> //內含rand()函式
#define uchar unsigned char
#define uint unsigned int
//**********巨集定義所需指令
#define BASIC_SET  0x30
#define EXTEND_SET 0x34
#define DRAW_ON    0x36
#define DRAW_OFF   0x34
//*************埠定義
sbit LCD_RS = P3^5;
sbit LCD_RW = P3^6;
sbit LCD_EN = P3^4;
sbit wela = P2^6;
sbit dula = P2^7;
//************變數定義

//****************短延時
void delay(uint k)
{
uint i;
uchar j;
for(i = 0; i < k ;i ++)
for(j = 0; j < 10 ;j ++);
}
//***********12864寫指令函式
void write_com(uchar cmd)
{
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 0;
P0 = cmd;
delay(5);
LCD_EN = 1;
delay(5);
LCD_EN = 0;
}
//********12864寫資料函式
void write_dat(uchar dat)
{
LCD_RS = 1;  
LCD_RW = 0;
LCD_EN = 0;
P0 = dat;
delay(5);
LCD_EN = 1;
delay(5);
LCD_EN = 0;
}
//****************從LCD中讀資料
uchar read_dat(void)
{
uchar temp;
P0 = 0XFF; //釋放資料線
LCD_RS = 1;   //資料
LCD_RW = 1;  //讀模式
LCD_EN = 1;  //為高電平進行讀資料或指令
delay(1);
temp = P0;
LCD_EN = 0;
return temp; 
}
//********************************************************
//設定游標(地址)函式
//引數說明:x---為行號,y為列號
//********************************************************
void set_cursor(unsigned char x, unsigned char y)
{
unsigned char i;
switch(x)    //確定行號
{
case 0x00: i=0x80; break; //第一行
case 0x01: i=0x90; break;  //第二行
case 0x02: i=0x88; break;  //第三行
case 0x03: i=0x98; break;  //第四行
default : break;
}
i = y+i;  //確定列號
write_com(i);
}
//********************************************************
//顯示字元函式
//********************************************************
void display_char(unsigned char Alphabet)
{
write_dat(Alphabet); //寫入需要顯示字元的顯示碼
}
//********************************************************
//指定位置顯示字串函式
//引數說明:x為行號,y為列號
//********************************************************
void display_string(unsigned char x,unsigned char y,unsigned char *Alphabet)
{
unsigned char i=0;
set_cursor(x,y); //設定顯示的起始地址
while(Alphabet[i]!='\0')
{
write_dat(Alphabet[i]); //寫入需要顯示字元的顯示碼
i++;
}
}
//***************************************************************************以下為GDRAM繪圖部分************************************************************************//
//*********************繪圖顯示的清屏函式(因清屏指令在畫圖時不能用)------------------------------------------------------------------------------注意!!!!!!!
void gui_clear()
{
uchar i , j , k;
write_com(EXTEND_SET);//擴充套件指令集,8位資料傳輸
write_com(DRAW_OFF);//繪圖顯示關閉
for(i = 0; i < 2; i ++)//分上下兩屏寫
{
for(j = 0; j < 32; j ++)
{
write_com(0x80 + j);//寫y座標
delay(1);
if(i == 0) //寫x座標
{
write_com(0x80);
delay(1);
}
else      //寫下半屏
{
write_com(0x88);
delay(1);
}
for(k = 0; k < 16; k ++)//寫一整行資料
{
write_dat(0x00);//寫高位元組
write_dat(0x00);//寫低位元組
delay(1);
}
}
}
write_com(DRAW_ON);//開啟繪圖顯示
write_com(BASIC_SET);//開啟基本指令集
}
//*************************************************************************************************
//***************有反白顯示功能的打點函式**********************************************************
//引數:color=1,該點填充1;color=0,該點填充白色0;
//*************************************************************************************************
void GUI_Point(unsigned char x,unsigned char y,unsigned char color)
{     
unsigned char x_Dyte,x_byte; //定義列地址的位元組位,及在位元組中的哪1位 
unsigned char y_Dyte,y_byte; //定義為上下兩個屏(取值為0,1),行地址(取值為0~31)
unsigned char GDRAM_hbit,GDRAM_lbit;
write_com(0x36); //擴充套件指令命令
/***X,Y座標互換,即普通的X,Y座標***/
x_Dyte=x/16; //計算在16個位元組中的哪一個
x_byte=x&0x0f; //計算在該位元組中的哪一位
y_Dyte=y/32; //0為上半屏,1為下半屏
y_byte=y&0x1f; //計算在0~31當中的哪一行
write_com(0x80+y_byte); //設定行地址(y座標),即是垂直地址
write_com(0x80+x_Dyte+8*y_Dyte); //設定列地址(x座標),並通過8*y_Dyte選定上下屏,即是水平地址
read_dat(); //預讀取資料
GDRAM_hbit= read_dat(); //讀取當前顯示高8位資料
GDRAM_lbit= read_dat(); //讀取當前顯示低8位資料
delay(1);
write_com(0x80+y_byte); //設定行地址(y座標)
write_com(0x80+x_Dyte+8*y_Dyte); //設定列地址(x座標),並通過8*y_Dyte選定上下屏
delay(1);
if(x_byte<8) //判斷其在高8位,還是在低8位
{
if(color==1)
{
write_dat(GDRAM_hbit|(0x01<<(7-x_byte))); //置位GDRAM區高8位資料中相應的點
}
else 
write_dat(GDRAM_hbit&(~(0x01<<(7-x_byte)))); //清除GDRAM區高8位資料中相應的點
write_dat(GDRAM_lbit); //顯示GDRAM區低8位資料 
}
else
{
write_dat(GDRAM_hbit);
if(color==1)
write_dat(GDRAM_lbit|(0x01<<(15-x_byte))); //置位GDRAM區高8位資料中相應的點
else 
write_dat(GDRAM_lbit&(~(0x01<<(15-x_byte))));//清除GDRAM區高8位資料中相應的點

write_com(0x30); //恢復到基本指令集
}
//***********(給定座標並打點的)任意位置打點函式
void lcd_set_dot(uchar x,uchar y)
{
uchar x_byte,x_bit;//確定在座標的那一位元組哪一位
uchar y_ping , y_bit;//確定在座標的哪一屏哪一行
uchar tmph , tmpl;//定義兩個臨時變數,用於存放讀出來的資料
write_com(EXTEND_SET);//擴充套件指令集
write_com(DRAW_OFF);//繪圖顯示關閉
x_byte = x / 16;//算出在哪一位元組,注意一個地址是16位的
x_bit = x % 16;//& 0x0f;//算出在哪一位
y_ping = y / 32;//確定在上半屏還是下半屏,0代表上半屏,1代表下半屏
y_bit = y % 32;//& 0x1f;//確定在第幾行
write_com(0X80 + y_bit);//先寫垂直地址(最高位必須)
write_com(0x80 + x_byte + 8 * y_ping);//水平座標,下半屏座標起始地址為0x88,(+8*y_ping)就是用來確定上半屏還是下半屏
read_dat();//預讀取資料
tmph = read_dat();//讀取當前顯示高8位資料
tmpl = read_dat();//讀取當前顯示低8位資料
delay(1);
write_com(0x80 + y_bit);//讀操作會改變AC,所以重新設定一下
write_com(0x80 + x_byte + 8 * y_ping);
delay(1);
if(x_bit < 8)
{
write_dat(tmph | (0x01 << (7 - x_bit)));//寫高位元組,因為座標是從左向右的,GDRAM高位在昨,低位在右
write_dat(tmpl);//原低位資料送回
}
else
{
write_dat(tmph);//原高位資料送回
write_dat(tmpl | (0x01 << (15 - x_bit)));
}
write_com(DRAW_ON); //開啟繪圖顯示
write_com(BASIC_SET);//回到基本指令集
}
//************畫水平線函式**********************************//
//x0、x1為起始點和終點的水平座標,y為垂直座標***************//
//**********************************************************//
void gui_hline(uchar x0, uchar x1, uchar y)
{
uchar bak;//用於對兩個數互換的中間變數,使x1為大值
if(x0 > x1)
{
bak = x1;
x1 = x0;
x0 = bak;
}
do
{
lcd_set_dot(x0 , y);//從左到右逐點顯示
x0 ++;
}
while(x1 >= x0);
}
//***********畫豎直線函式***********************************//
//x為起始點和終點的水平座標,y0、y1為垂直座標***************//
//**********************************************************//
void gui_rline(uchar x, uchar y0, uchar y1)
{
uchar bak;//用於對兩個數互換的中間變數,使y1為大值
if(y0 > y1)
{
bak = y1;
y1 = y0;
y0 = bak;
}
do
{
lcd_set_dot(x , y0);//從上到下逐點顯示
y0 ++;
}
while(y1 >= y0);
}
//*********任意兩點間畫直線*********************************//
//x0、y0為起始點座標,x1、y1為終點座標**********************//
//**********************************************************//
void gui_line(uchar x0 , uchar y0 , uchar x1 , uchar y1)
{
char dx;//直線x軸差值
char dy;//直線y軸差值
char dx_sym;//x軸增長方向,為-1時減值方向,為1時增值方向
char dy_sym;//y軸增長方向,為-1時減值方向,為1時增值方向
char dx_x2;//dx*2值變數,用於加快運算速度
char dy_x2;//dy*2值變數,用於加快運算速度
char di;   //決策變數
if(x0 == x1)//判斷是否為垂直線
{
gui_rline(x0 , y0 , y1);//畫垂直線
return;
}
if(y0 == y1)//判斷是否為水平線
{
gui_hline(x0 , x1 , y0);//畫水平線
return;
}
dx = x1 - x0;//求取兩點之間的差值
dy = y1 - y0;
//****判斷增長方向,或是否為水平線、垂直線、點*//
if(dx > 0)//判斷x軸方向
dx_sym = 1;
else
{
if(dx < 0)
dx_sym = -1;
else
{
gui_rline(x0 , y0 , y1);
return;
}
}
if(dy > 0)//判斷y軸方向
dy_sym = 1;
else
{
if(dy < 0)
dy_sym = -1;
else
{
gui_hline(x0 , x1 , y0);
return;
}
}
/*將dx、dy取絕對值***********/
dx = dx_sym * dx;
dy = dy_sym * dy;
/****計算2倍的dx、dy值*******/
dx_x2 = dx * 1;//我改為了一倍,這樣才跟真實的兩點對應
dy_x2 = dy * 1;
/***使用bresenham法進行畫直線***/
if(dx >= dy)//對於dx>=dy,使用x軸為基準
{
di = dy_x2 - dx;
while(x0 != x1)
{
lcd_set_dot(x0,y0);
x0 +=dx_sym;
if(di < 0)
di += dy_x2;//計算出下一步的決策值
else
{
di += dy_x2 - dx_x2;
y0 += dy_sym;
}
}
lcd_set_dot(x0, y0);//顯示最後一點
}
else  //對於dx<dy使用y軸為基準
{
di = dx_x2 - dy;
while(y0 != y1)
{
lcd_set_dot(x0, y0);
y0 += dy_sym;
if(di < 0)
di += dx_x2;
else
{
di += dx_x2 - dy_x2;
x0 += dx_sym;
}
}
lcd_set_dot(x0, y0);//顯示最後一點
}
}
//***************************************************************************//
//*******************畫指定寬度的任意兩點之間的直線**************************//
//引數說明:x0、y0為起始點座標,x1、y1為終點座標,with為線寬*****************//
//***************************************************************************//
void gui_linewith(uchar x0 , uchar y0 , uchar x1 , uchar y1 , uchar with)
{  
char dx; // 直線x軸差值變數
char dy;           // 直線y軸差值變數
char dx_sym; // x軸增長方向,為-1時減值方向,為1時增值方向
char dy_sym; // y軸增長方向,為-1時減值方向,為1時增值方向
char dx_x2; // dx*2值變數,用於加快運算速度
char dy_x2; // dy*2值變數,用於加快運算速度
char di; // 決策變數
char   wx, wy; // 線寬變數
char   draw_a, draw_b;
// 引數過濾
if(with==0) return;
if(with>50) with = 50;
dx = x1-x0; // 求取兩點之間的差值
dy = y1-y0;
wx = with/2;
wy = with-wx-1;
//判斷增長方向,或是否為水平線、垂直線、點 
if(dx>0) // 判斷x軸方向
{  
dx_sym = 1; // dx>0,設定dx_sym=1
}
else
{  
if(dx<0)
{  
dx_sym = -1; // dx<0,設定dx_sym=-1
}
else
{  
//dx==0,畫垂直線,或一點
wx = x0-wx;
if(wx<0) wx = 0;
wy = x0+wy;
while(1)
{  
x0 = wx;
gui_rline(x0, y0, y1);
if(wx>=wy) break;
wx++;
}
return;
}
}
if(dy>0) // 判斷y軸方向
{  
dy_sym = 1; // dy>0,設定dy_sym=1
}
else
{  
if(dy<0)
{  
dy_sym = -1; // dy<0,設定dy_sym=-1
}
else
{  
//dy==0,畫水平線,或一點
wx = y0-wx;
if(wx<0) wx = 0;
wy = y0+wy;
while(1)
{  
y0 = wx;
gui_hline(x0, x1, y1);
if(wx>=wy) break;
wx++;
}
return;
}
}
// 將dx、dy取絕對值
dx = dx_sym * dx;
dy = dy_sym * dy;
//計算2倍的dx及dy值
dx_x2 = dx*2;
dy_x2 = dy*2;
//使用Bresenham法進行畫直線
if(dx>=dy) // 對於dx>=dy,則使用x軸為基準
{  
di = dy_x2 - dx;
while(x0!=x1)
{  
//x軸向增長,則寬度在y方向,即畫垂直線
draw_a = y0-wx;
if(draw_a<0) draw_a = 0;
draw_b = y0+wy;
gui_rline(x0, draw_a, draw_b);
x0 += dx_sym;
if(di<0)
{  
di += dy_x2; // 計算出下一步的決策值
}
else
{  
di += dy_x2 - dx_x2;
y0 += dy_sym;
}
}
draw_a = y0-wx;
if(draw_a<0) draw_a = 0;
draw_b = y0+wy;
gui_rline(x0, draw_a, draw_b);
}
else // 對於dx<dy,則使用y軸為基準
{  
di = dx_x2 - dy;
while(y0!=y1)
{  
//y軸向增長,則寬度在x方向,即畫水平線
draw_a = x0-wx;
if(draw_a<0) draw_a = 0;
draw_b = x0+wy;
gui_hline(draw_a, y0, draw_b);
y0 += dy_sym;
if(di<0)
{  
di += dx_x2;
}
else
{  
di += dx_x2 - dy_x2;
x0 += dx_sym;
}
}
draw_a = x0-wx;
if(draw_a<0) draw_a = 0;
draw_b = x0+wy;
gui_hline(draw_a, y0, draw_b);

}
//***********畫矩形函式*************************************//
//x0、y0為矩形左上角座標值,x1、y1為矩形右下角座標值********//
//**********************************************************//
void gui_rectangle(uchar x0 , uchar y0 , uchar x1 , uchar y1)
{
gui_hline(x0 , x1 , y0);
gui_rline(x0 , y0 , y1);
gui_rline(x1 , y0 , y1);
gui_hline(x0 , x1 , y1);
}
//****************畫填充矩形函式****************************//
//x0、y0為矩形左上角座標值,x1、y1為矩形右下角座標值********//
//**********************************************************//
/*void gui_rectangle_fill(uchar x0 , uchar y0 , uchar x1 , uchar y1)
{
uchar i;//轉換資料的中間變數,使x1、y1大
if(x0 > x1)
{
i = x0;
x0 = x1;
x1 = i;
}
if(y0 > y1)
{
i = y0;
y0 = y1;
y1 = i;
}
//***判斷是否是直線***/
/* if(y0 == y1)//畫水平線
{
gui_hline(x0 , x1 , y0);
return;
}
if(x0 == x1)//畫豎直線
{
gui_rline(x0 , y0 , y1);
return;
}
while(y0 <= y1)//畫填充矩形
{
gui_hline(x0 , x1 , y0);
y0 ++;
}
} */
//*******************畫正方形函式*************************//
//x0、y0為正方形左上角座標,with正方形邊長****************//
//********************************************************//
/*void gui_square(uchar x0 , uchar y0 , uchar with)
{
if(with == 0)
return;
if((x0 + with) > 127)//橫軸超出液晶邊界
return;
if((y0 + with) > 63)
return;
gui_rectangle(x0 , y0 , x0 + with , y0 + with);
} */
//****************畫填充正方形函式*************************//
//x0、y0為正方形左上角座標,with正方形邊長*****************//
//*********************************************************//
/*void gui_square_fill(uchar x0 , uchar y0 , uchar with)
{
if(with == 0)
return;
if((x0 + with) > 127)//橫軸超出液晶邊界
return;
if((y0 + with) > 63)
return;
gui_rectangle_fill(x0 , y0 , x0 + with , y0 + with);
} */
//****************畫圓函式*********************************//
//x0、y0為圓心座標,r為圓的半徑****************************//
//*********************************************************//
void gui_circle(uchar x0 , uchar y0 , uchar r)
{
char a , b;
char di;
if(r > 31 || r == 0)//圓大於液晶屏或者沒半徑則返回
return;
a = 0;
b = r;
di = 3 - 2 * r;//判斷下個點位置的標誌
while(a <= b)
{
lcd_set_dot( x0 - b , y0 - a);//3
lcd_set_dot( x0 + b , y0 - a); //0
lcd_set_dot( x0 - a , y0 + b); //1
lcd_set_dot( x0 - b , y0 - a); //7
lcd_set_dot( x0 - a , y0 - b); //2
lcd_set_dot( x0 + b , y0 + a); //4
lcd_set_dot( x0 + a , y0 - b); //5
lcd_set_dot( x0 + a , y0 + b); //6
lcd_set_dot( x0 - b , y0 + a);
a ++;
//***使用bresenham演算法畫圓********/
if(di < 0)
di += 4 * a + 6;
else
{
di += 10 + 4 * (a - b);
b --;
}
lcd_set_dot( x0 + a , y0 + b);
}

//***************************************************************************//
//***************************畫正橢圓函式************************************//
//說明:給定橢圓的四個點的引數,最左、最右點的x軸座標值為x0、x1,最上、最下點
//      的y軸座標為y0、y1.
//說明:----------------------------顯示效果不好
//***************************************************************************//
void gui_ellipse(char x0, char x1, char y0, char y1)
{  
char  draw_x0, draw_y0; // 劊圖點座標變數
char  draw_x1, draw_y1;
char  draw_x2, draw_y2;
char  draw_x3, draw_y3;
char  xx, yy; // 畫圖控制變數 
char  center_x, center_y; // 橢圓中心點座標變數
char  radius_x, radius_y; // 橢圓的半徑,x軸半徑和y軸半徑
int  radius_xx, radius_yy; // 半徑乘平方值
int  radius_xx2, radius_yy2; // 半徑乘平方值的兩倍
char  di; // 定義決策變數
  /* 引數過濾 */
  if( (x0==x1) || (y0==y1) ) return; 
   /* 計算出橢圓中心點座標 */
  center_x = (x0 + x1) >> 1;
  center_y = (y0 + y1) >> 1;  
  /*計算出橢圓的半徑,x軸半徑和y軸半徑 */
if(x0 > x1)
   { 
radius_x = (x0 - x1) >> 1;
   }
   else
   { 
radius_x = (x1 - x0) >> 1;
   }
   if(y0 > y1)
   { 
radius_y = (y0 - y1) >> 1;
   }
   else
   {
radius_y = (y1 - y0) >> 1;
   }   
   /* 計算半徑平方值 */
   radius_xx = radius_x * radius_x;
   radius_yy = radius_y * radius_y; 
   /* 計算半徑平方值乘2值 */
   radius_xx2 = radius_xx<<1;
   radius_yy2 = radius_yy<<1;  
   /* 初始化畫圖變數 */
 xx = 0;
   yy = radius_y;
   di = radius_yy2 + radius_xx - radius_xx2*radius_y ; // 初始化決策變數 
   /* 計算出橢圓y軸上的兩個端點座標,作為作圖起點 */
   draw_x0 = draw_x1 = draw_x2 = draw_x3 = center_x;
   draw_y0 = draw_y1 = center_y + radius_y;
   draw_y2 = draw_y3 = center_y - radius_y;
   lcd_set_dot(draw_x0, draw_y0); // 畫y軸上的兩個端點 
   lcd_set_dot(draw_x2, draw_y2);
   while( (radius_yy*xx) < (radius_xx*yy)) 
   {  
if(di<0)
{  
di+= radius_yy2*(2*xx+3);
}
else
{  
di += radius_yy2*(2*xx+3) + 4*radius_xx - 4*radius_xx*yy;
yy--;
draw_y0--;
draw_y1--;
draw_y2++;
draw_y3++;  
}
xx ++; // x軸加1
draw_x0++;
draw_x1--;
draw_x2++;
draw_x3--;
lcd_set_dot(draw_x0, draw_y0);
lcd_set_dot(draw_x1, draw_y1);
lcd_set_dot(draw_x2, draw_y2);
lcd_set_dot(draw_x3, draw_y3);
}
   di = radius_xx2*(yy-1)*(yy-1) + radius_yy2*xx*xx + radius_yy + radius_yy2*xx - radius_xx2*radius_yy;
   while(yy>=0) 
   { 
if(di<0)

di+= radius_xx2*3 + 4*radius_yy*xx + 4*radius_yy - 2*radius_xx2*yy;
xx ++; // x軸加1  
draw_x0++;
draw_x1--;
draw_x2++;
draw_x3--;  
}
else

di += radius_xx2*3 - 2*radius_xx2*yy;            
}
yy--;
draw_y0--;
draw_y1--;
draw_y2++;
draw_y3++;
lcd_set_dot(draw_x0, draw_y0);
lcd_set_dot(draw_x1, draw_y1);
lcd_set_dot(draw_x2, draw_y2);
lcd_set_dot(draw_x3, draw_y3);
}    

//*******************************************************************
//*******畫滿屏圖片-----本程式為逐行寫,因此圖片資料也應該是逐行取的
//引數:dat為填充的資料------用本程式時需要滿屏圖的陣列
//*******************************************************************
void gui_draw_full_picture (unsigned char *dat)

unsigned char i;                 
unsigned char j; 
unsigned char k; 
unsigned char bGDRAMAddrX = 0x80; //GDRAM水平地址 
unsigned char bGDRAMAddrY = 0x80; //GDRAM垂直地址 
for(i = 0; i < 2; i++)                                                                                          

for(j = 0; j < 32; j++)                                                                 

for(k = 0; k < 8; k++)                                                         

write_com(0x34); //設定為8位MPU介面,擴充指令集,關閉繪圖顯示
write_com(bGDRAMAddrY+j); //垂直地址Y                                                                         
write_com(bGDRAMAddrX+k); //水平地址X 
write_dat(*dat++); //寫資料高位元組
write_dat(*dat++); //寫資料低位元組


bGDRAMAddrX = 0x88; //寫下半螢幕

write_com(0x36); //開啟繪圖模式
write_com(0x30); //恢復基本指令集,關閉繪圖模式   
}  
unsigned char code  DCB2HEX_TAB[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
//****************************輸出一行資料函式,此行可任意長,不必非得是8的倍數**和下個函式合用畫任意大小(矩形)的圖形或漢字
//引數:flag反顯標誌,1為反顯 ,x、y為指定顯示位置的起始點,*dat要輸出的點陣陣列,no顯示此行所需的點個數,即圖形一行的點數
void gui_loadline(unsigned char x,unsigned char y,unsigned char *dat,unsigned char no,unsigned char flag)
{  
unsigned char bit_dat;
unsigned char i;
/* 引數過濾,若指定顯示位置超出液晶屏則返回 */
if(x>127) return;
if(y>63) return;
for(i=0; i<no; i++)//超出本行所規定的點數則本行顯示完成
{  
/* 判斷是否要讀取點陣資料,每位元組的開始讀取一次點陣陣列即i為8的倍數時 */
if( (i%8)==0 ) bit_dat = *dat++;
/* 對相應的點打1或打0, i&0x07意思是對8求餘*/
if( (bit_dat&DCB2HEX_TAB[i&0x07])==0 )//取出i對應的位,並判斷是否為0 
{
if(flag==0) //判斷是否反顯,該位取出的資料為0,反顯要打為1,flag==0代表不反顯
GUI_Point(x,y,0);  //正常顯示0,GUI_Point(x,y,0)代表在x、y處打0
else
GUI_Point(x,y,1);  //將0反顯
}
else  
{
if(flag==0)
GUI_Point(x,y,1); 
else
GUI_Point(x,y,0); 
}        
if( (++x)>127) return;//若顯示超出了液晶屏則返回
   }
}
//***************************************************************************
//*****************在自定義大小的區域內畫圖或畫字函式****************************
//引數說明: x、y指定顯示區域的起始點座標
//          dat 要輸出顯示的圖形或漢字點陣陣列。
//          hno 要顯示區域的長度
//          lno 要顯示區域的高度
//          flag反顯標誌,1為反顯
//****************************************************************************
void  GUI_Put_Area(unsigned char x,unsigned char y,unsigned char *dat,unsigned char hno,unsigned char lno,unsigned char flag)
{  
unsigned char i;
for(i=0;i<lno;i++)//逐行打點,打點行數要小於區域高度
{  
gui_loadline(x,y,dat,hno,flag); // 打一行資料
y++; //使指標指向下一行
dat += (hno>>3); //比如說上一行打了2位元組,此處的意思是使資料向後推進2位元組,(hno>>3)意思是算出上一行有幾個位元組資料
if((hno&0x07)!=0) //hno&0x07意思是對8求餘,因一行不滿整位元組時,上一句的意思就是算出上一行的整位元組數,但實際上在取模時不滿一個
dat++;   //位元組也按一個位元組取,所以上一句少計算了一個位元組,這裡加上
}
}
//*********************************************************************************以上為GDRAM繪圖部分************************************************************************//
//**********************************************************************************以下為CGRAM自定義字型檔部分******************************************************************//
//********************************************************
//設定CGRAM字型檔
//ST7920 CGRAM(使用者自定義圖示)空間分佈
//空間1地址:40H~4FH共16個地址,一個地址對應兩個位元組資料;對應呼叫碼:0000H
//空間2地址:50H~5FH共16個地址,一個地址對應兩個位元組資料;對應呼叫碼:0002H
//空間3地址:60H~6FH共16個地址,一個地址對應兩個位元組資料;對應呼叫碼:0004H
//空間4地址:70H~7FH共16個地址,一個地址對應兩個位元組資料;對應呼叫碼:0006H
//引數說明:num為空間編號,取1、2、3、4,CGRAM_ZIKU為地址指標
void SET_CGRAM(unsigned char num,unsigned char *CGRAM_ZIKU)
{
unsigned char i,add;
write_com(0x34); //再次設定為8位並行口,擴充套件指令集
write_com(0x02); //SR=0,允許設定CGRAM地址
write_com(0x30); //恢復設定為8位並行口,基本指令集
add=(num<<4)|0x40; //計算CGRAM的首地址
for(i=0;i<16;i++)
{
write_com(add+i); //設定CGRAM的首地址
write_dat(CGRAM_ZIKU[i*2]); //寫入高8位資料
write_dat(CGRAM_ZIKU[i*2+1]);//寫入低8位資料
}

//********************************************************
//指定位置顯示CGRAM自造字函式
//引數說明:x為行號,y為列號,num為編號
//********************************************************
void display_CGRAM(unsigned char x,unsigned char y,unsigned char num)
{
set_cursor(x,y); //設定顯示的起始地址
write_dat(0x00); //寫入需要顯示漢字的高八位資料
write_dat(num*2); //寫入需要顯示字元的低八位資料
}
uchar code CGRAM_ZIKU[] = {0X03,0X80,0X00,0X80,0XF8,0X8E,0X23,0X91,0X22,0X21,0X22,0X20,0X23,0XA0,0X20,0X20,
0X20,0X20,0X20,0X20,0X20,0X20,0X20,0X21,0X20,0X11,0XF8,0X0E,0X00,0X00,0X00,0X00};//I2C
//**************設定CGRAM字型檔並顯示函式
void set_and_display()
{
SET_CGRAM( 1,CGRAM_ZIKU);//把自定義字型檔寫入CGRAM,且寫入空間地址1
display_CGRAM(0 , 1 , 1);//在指定的位置顯示空間地址1中自定義的字元

//***************************************************************************以上為自定義字型檔部分**************************************************************//
//****************12864初始化函式
void lcd_init()
{
write_com(0x30);//基本指令操作,8位並口
delay(1);
write_com(0x06);//設定為遊標右移,DDRAM地址加一,畫面不動
delay(1);
write_com(0x0c);//顯示開,關游標
delay(1);
write_com(0x01);//清除lcd顯示內容
delay(1);
}
//*****************************主函式
void main()
{
wela=0;
dula=0;
delay(1);
lcd_init();//12864初始化函式
gui_clear();//畫圖時清屏函式
delay(10);
set_and_display();//顯示自定義字型檔
  gui_circle( 86 , 32 , 30); //畫圓
gui_linewith( 5 , 5 , 120 , 60 , 3);//畫指定線寬直線
  gui_ellipse(10 , 120 , 5 , 60);//畫正橢圓
while(1);
}