12864图形显示函数要点
- 格式:doc
- 大小:146.00 KB
- 文档页数:24
// 程序开发时间:2014年5月22日星期四姓名:杨言安地点:山西大学//------------------------------------------------------------------------------------------------------------------------------ // 名称:128X64液晶显示sin函数的图像//------------------------------------------------------------------------------------------------------------------------------ // 说明:在液晶上显示sin函数图像后通过四个按键控制sin函数的频率幅度,单片机// STC89C52,液晶QC12864B//------------------------------------------------------------------------------------------------------------------------------ #include <reg52.h>#include <intrins.h>#include <math.h>#define uint unsigned int#define uchar unsigned char#define LCMdata P0//液晶的8位I/O口与单片机的P0口相连#define Busy 0x80 //用于检测液晶忙状态#define pi 3.1415926uchar f=16;//sin函数频率的初始值uchar u=8;//sin函数幅值的初始值sbit RS=P3^5;// 数据/命令选择模式端sbit RW=P3^6;// 读/写操作选择模式端sbit E=P3^4;// 读/写操作使能端sbit PSB=P3^7;//并口/串口选择模式端sbit dula=P2^6;// 数码管段选控制端sbit wela=P2^7;// 数码管位选控制端sbit f_sub=P1^0;//频率减小按键sbit f_add=P1^1;//频率增大按键sbit vpp_add=P1^2;//幅值增大按键sbit vpp_sub=P1^3;//幅值减小按键//------------------------------------------------------------------------------------------------------------------------------ // 延时函数//------------------------------------------------------------------------------------------------------------------------------ void Delay(uchar i){while(i--);}//------------------------------------------------------------------------------------------------------------------------------ // 读取忙状态函数//------------------------------------------------------------------------------------------------------------------------------ void Read_busy(){LCMdata=0xff;RS=0;RW=1;E=1;while(LCMdata&Busy);E=0;}//------------------------------------------------------------------------------------------------------------------------------ // 写数据函数//------------------------------------------------------------------------------------------------------------------------------ void Write_data(uchar Data){Read_busy();RS=1;RW=0;LCMdata=Data;E=1;_nop_();_nop_();E=0;}//------------------------------------------------------------------------------------------------------------------------------ //写指令函数//------------------------------------------------------------------------------------------------------------------------------ void Write_command(uchar Command){Read_busy();RS=0;RW=0;LCMdata=Command;E=1;_nop_();_nop_();E=0;}//------------------------------------------------------------------------------------------------------------------------------ // 读数据函数//------------------------------------------------------------------------------------------------------------------------------ Read_data(){uchar Data;Read_busy();LCMdata=0xff;RS=1;RW=1;E=0;E=1;Data=LCMdata;E=0;return Data;}//------------------------------------------------------------------------------------------------------------------------------ // 液晶初始化函数//------------------------------------------------------------------------------------------------------------------------------ void LCM_init(){PSB=1;P3=0xff;Write_command(0x30);//8BIT设置,RE=0(使用基本指令集)Delay(100);Write_command(0x3E);//显示设定:整体显示、游标关、不反白Delay(100);Write_command(0x0C);//设定CGRAM字符的位置Delay(100);Write_command(0x01);//清屏指令Delay(100);Write_command(0x06);//进入点设置,指针加1Delay(100);}//------------------------------------------------------------------------------------------------------------------------------ //清屏函数:在液晶上打点之前先将原来的数据填写为0,这样启动液晶后液晶上没有任何亮点//------------------------------------------------------------------------------------------------------------------------------ void clear_screen(){uchar i,j;for(i=0;i<32;i++)//将上半屏清零{Write_command(0x80+i);//向GDRAM写入垂直地址Write_command(0x80);//向GDRAM写入水平地址,自动加一for(j=0;j<16;j++)//每个地址含两个字节,一行八个地址,故有16个字节,需写16次{Write_data(0x00);}}for(i=0;i<32;i++)//将下半屏清零{Write_command(0x80+i);Write_command(0x88);//下半屏初始地址for(j=0;j<16;j++){Write_data(0x00);}}}//------------------------------------------------------------------------------------------------------------------------------ // 在液晶任意位置进行打点:该函数带有三个参数分别是X轴坐标值,Y轴坐标值,color 为点设置位,点亮或熄灭//------------------------------------------------------------------------------------------------------------------------------ void Draw_dot(uchar x,uchar y,color){uchar Row,xlabel,xlabel_bit;uchar Read_H,Read_L;Write_command(0x34);//扩充指令Write_command(0x36);//打开绘图指令xlabel=x>>4;//左移4位相当于除以16,以此来确定水平地址xlabel_bit=x & 0x0f;//进一步确定在两个字节中第几位if(y<32)//该点位于上半屏{Row=y;}else//该点位于下半屏{Row=y-32;xlabel+=8;}Write_command(Row+0x80);//写入液晶纵坐标的地址Write_command(xlabel+0x80);//写入液晶横坐标的地址Read_data();Read_H=Read_data();//读取该地址的第一个字节数据Read_L=Read_data();//读取该地址的第二个字节的数据Write_command(Row+0x80);//再次向液晶写入纵坐标的地址Write_command(xlabel+0x80);//再次向液晶写入横坐标的地址if(xlabel_bit<8)//该点位于第一个字节{switch(color)//对该点进行设置{case 0:Read_H&=(~(0x01<<(7-xlabel_bit)));//若变白break;case 1:Read_H|=(0x01<<(7-xlabel_bit));//若变黑break;case 2:Read_H^=(0x01<<(7-xlabel_bit));//反转break;default:break;}Write_data(Read_H);//根据地址及相应的操作向液晶发送数据Write_data(Read_L);}else//该点位于第二个字节{switch(color)//对该点进行设置{case 0:Read_L&=(~(0x01<<(15-xlabel_bit)));//若变白break;case 1:Read_L|=(0x01<<(15-xlabel_bit));//若变黑break;case 2:Read_L^=(0x01<<(15-xlabel_bit));//反转break;default:break;}Write_data(Read_H);Write_data(Read_L);}Write_command(0x30);}//------------------------------------------------------------------------------------------------------------------------------ //画水平直线:X0和X1分别为直线的起点横坐标和终点横坐标,Y为纵坐标//------------------------------------------------------------------------------------------------------------------------------ void Draw_xlabel_line(uchar X0,uchar X1,uchar Y,uchar color){uchar temp;if(X0>X1){temp=X1;X1=X0;X0=temp;}for(;X0<=X1;X0++) Draw_dot(X0,Y,color);}//------------------------------------------------------------------------------------------------------------------------------ //画垂直线:X为横坐标值,Y0为纵坐标起始值,Y1为纵坐标终点值//------------------------------------------------------------------------------------------------------------------------------ void Draw_row_line(uchar X,uchar Y0,uchar Y1,uchar color){uchar temp;if(Y0>Y1){temp=Y1;Y1=Y0;Y0=temp;}for(;Y0<=Y1;Y0++) Draw_dot(X,Y0,color);}//------------------------------------------------------------------------------------------------------------------------------ //正弦函数显示//------------------------------------------------------------------------------------------------------------------------------ void sin_display(){uchar i,j;LCM_init();clear_screen();//画Y轴箭头Draw_dot(1,1,1);Draw_dot(3,1,1);Draw_dot(0,2,1);Draw_dot(4,2,1);Draw_row_line(2,0,60,1);//画Y轴直线//画X轴箭头Draw_dot(126,60,1);Draw_dot(126,62,1);Draw_dot(125,59,1);Draw_dot(125,63,1);Draw_xlabel_line(2,127,61,1);//画X轴直线for(i=3;i<=127;i++)//画sin函数,i的起始值可自行修改,i值的不同表现在sin图像起始点的位置不同{j=u*sin(pi*i/f)+30;//30可以改为其他值,显示效果是sin函数在幅度、频率不变的情况下图像上下移动Draw_dot(i,j,1);}}//------------------------------------------------------------------------------------------------------------------------------ //按键扫描函数:扫描完后立刻显示//------------------------------------------------------------------------------------------------------------------------------ void Key_scan(){P1=0xff;if(f_sub==0)//判断是否有键按下{Delay(1000);//消抖if(f_sub==0){f+=4;//频率减小的幅度,可自行修改}while(!f_sub==1);//判断按键操作是否结束Delay(100);while(!f_sub==1);//再次判断按键操作是否结束sin_display();//显示频率减小后的sin函数图像}if(f_add==0){Delay(1000);if(f_add==0){f-=4;//频率增大的幅度,可自行修改}while(!f_add==1);Delay(100);while(!f_add==1);sin_display();}if(vpp_add==0){if(vpp_add==0){u+=4;//幅值增大的幅度,可自行修改if(u>28)u=8;//超过32时,超出了液晶的显示范围,从新赋初始值}while(!vpp_add==1);Delay(100);while(!vpp_add==1);sin_display();}if(vpp_sub==0){if(vpp_sub==0){u-=4;//幅值减小的幅度,可自行修改}while(!vpp_sub==1);Delay(100);while(!vpp_sub==1);sin_display();}}//------------------------------------------------------------------------------------------------------------------------------// 主函数//------------------------------------------------------------------------------------------------------------------------------ void main(){P1=0xff;dula=1;P0=0xff;dula=0;//关闭数码管段选wela=1;P0=0xff;wela=0;//关闭数码管位选sin_display();//初始化后显示一个标准的sin函数图像while(1){Key_scan();//判断是否有键按下并显示出图像}}。
12864液晶画点和画任意两点间直线原理、算法及程序原码12864液晶画点和画任意直线的原理和算法程序原码经验证可行12864实际上是256x64二维显示空间,整个液晶屏分上下两个半屏。
整个屏一共有256列,64行。
可以把它分成16大列,每一大列包含16列。
图形RAM的起始址址为0x80,设置读或写的地址时,要先写Y坐标,再写X坐标。
要使用画图功能,就要设置扩允指令集。
画点原理:先确定坐标->读出数据->修改数据->数据写回原处。
程序原码://画点函数void Draw_Point(uchar x,uchar y,uchar color){uchar row,tier,row_bit;uchar ReadOldH,ReadOldL;tier=x>>4; //把256列分成16大列,每大列包含16列row_bit=x&0x0f; //计算所给坐标在某一大列中的哪一列if(y<32) //分上下半屏显示row=y; //上半屏else{row=y-32; //下半屏tier+=8;}WriteCommand(0x34); //8Bit扩充指令集,即使是36H也要写两次WriteCommand(0x36); //绘图ON,基本指令集里面36H不能开绘图WriteCommand(0x80+row); // 行位置WriteCommand(0x80+tier); // 列位置ReadData();ReadOldH=ReadData();//某大列的前8列数据,低位在前,高位在后ReadOldL=ReadData();//某大列的后8列数据if( row_bit < 8 ) //修改读出的数据{switch( color){case 0 : ReadOldH &=( ~( 0x01 << ( 7 - row_bit ))) ;break ;case 1 : ReadOldH |= ( 0x01 << ( 7 - row_bit )) ;break ;case 2 : ReadOldH ^= ( 0x01 << ( 7 - row_bit )) ;break ;default : break ;}}else{switch(color){case 0 : ReadOldL &= (~( 0x01 << ( 15 - row_bit ))) ;break ;case 1 : ReadOldL |= ( 0x01 << ( 15 - row_bit )) ;break ;case 2 : ReadOldL ^= ( 0x01 << ( 15 - row_bit )) ;break ;default : break ;}}WriteCommand(0x80+row); // 行位置WriteCommand(0x80+tier); // 列位置WriteData( ReadOldH ) ;//把修改后的数据写回原地址WriteData( ReadOldL ) ;}画任意两点间直线的原理和算法:采用Bresenham画线算法。
一.初始化函数!/**********************************1.初始化函数!//**********************************void init(){writecmd(0x30);delayms(1);writecmd(0x30);delayms(1);writecmd(0x0c);delayms(1);writecmd(0x01);delayms(1);writecmd(0x06);delayms(1);}2.初始化LCDvoid init(){write_com(0x30); //基本指令, 扩充指令为34Hwrite_com(0x0c); //显示开, 关光标write_com(0x01); //清屏delaym(10);}二.判忙函数1.检测LCD是否处于忙状态, 若忙返回1, 空闲返回0bit checkBusy(){bit busy;RS = 0;RW = 1;E = 1;delayu();busy = (bit)(LCD_DATA&0x80);E = 0;return busy ;}2.等待LCD到空闲void wait(){while(checkBusy());}三.数据读写函数1.写命令void write_com(uchar cmd){wait();RS = 0;RW = 0;E = 0;LCD_DATA = cmd;delayu();E = 1;E = 0;}2.写数据void write_data(uchar dat){wait();RS = 1;RW = 0;E = 0;LCD_DATA = dat;delayu();E = 1;E = 0;}3.读数据uchar read_data(void){uchar dsp_data;wait();LCD_DATA=0xFF;RS = 1;RW = 1;E=0;E=1;delayu();dsp_data=LCD_DATA;E = 0;return(dsp_data);}四.填充液晶DDRAM全为空格//***********************************//填充液晶DDRAM全为空格//**********************************void clrram_lcd (void){wr_i_lcd(0x30);wr_i_lcd(0x01);}五.指定要显示字符的坐标//********************************//指定要显示字符的坐标//*******************************void gotoxy(unsigned char y, unsigned char x){if(y==1)writecmd(0x80|x);if(y==2)writecmd(0x90|x);if(y==3)writecmd((0x80|x)+8);if(y==4)writecmd((0x90|x)+8);}六.液晶显示字符串程序/**********************************//液晶显示字符串程序//**********************************void print(uchar *str){while(*str!='\0'){writedate(*str);str++;}}七.指定要显示字符的坐标和字!或字节!/**********************************//指定要显示字符的坐标和字!或字节!//**********************************void disoneword( uchar x,uchar y,uint word) //此word如disoneword(2,0,0xb5a5); 0xb5a5为一个“单”字!!{uchar ah,al;if(x==0)ah=0x80;else if(x==1)ah=0x90;else if(x==2)ah=0x88;else ah=0x98;al=ah+y;writecmd(al);writedate((word&0xff00)>>8);writedate(word&0xff);}/**********************************//指定要显示字符的坐标和字!或字节!222222//**********************************void write_sfm(uchar x,uchar y,uchar shu) //shu经处理后见函数!!!!{uchar d1,d2,add,ah;d1=shu/10;d2=shu%10;if(x==0)ah=0x80;else if(x==1)ah=0x90;else if(x==2)ah=0x88;else ah=0x98;add=ah+y; //相当于ah+alwritecmd(add);writedate( d1+'0');writedate( d2+'0');}八.控制液晶显示汉字/*******************************************函数名称:Disp_HZ功能:控制液晶显示汉字参数:addr--显示位置的首地址pt--指向显示数据的指针num--显示字符个数返回值:无********************************************/void Disp_HZ(uchar addr,const uchar * pt,uchar num){uchar i;writecmd(addr);for(i = 0;i < (num*2);i++)writedata(*(pt++));}1.显示一个16*16大小的图形/*******************************************函数名称:Draw_TX功能:显示一个16*16大小的图形参数:Yaddr--Y地址Xaddr--X地址dp--指向图形数据存放地址的指针返回值:无********************************************/void Draw_TX(uchar Yaddr,uchar Xaddr,const uchar * dp) {uchar j;uchar k=0;// Write_Cmd(0x01); //清屏,只能清除DDRAMWrite_Cmd(0x34); //使用扩展指令集,关闭绘图显示for(j=0;j<16;j++){Write_Cmd(Yaddr++); //Y地址Write_Cmd(Xaddr); //X地址Write_Data(dp[k++]);Write_Data(dp[k++]);}Write_Cmd(0x36); //打开绘图显示// Write_Cmd(0x30); //回到基本指令集模式}#include<LCD12864_12.h>void delayu(){_nop_();}//延时a * 1msvoid delaym(uint a){uint i, j;for(i = a; i > 0; i--)for(j = 110; j > 0; j--);}//初始化LCDvoid init(){write_com(0x30); //基本指令, 扩充指令为34Hwrite_com(0x0c); //显示开, 关光标write_com(0x01); //清屏delaym(10);}void setPosition(uchar x, uchar y){uchar p;switch(y%4){case 0: p = 0x80; break; //第一行开始地址case 1: p = 0x90; break; //第二行case 2: p = 0x88; break; //第三行case 3: p = 0x98; break; //第四行}p += x;write_com(p);}void writestring(uchar * str){uchar i = 0;while(str[i] != '\0'){write_data(str[i++]);}}void lcd_DisplayImage(unsigned char *str){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++) // 在绘图GDRAM中写满屏图片数据{for(j = 0; j < 32; j++){for(k = 0; k < 8; k++){write_com(0x34);write_com(bGDRAMAddrY + j);//垂直地址write_com(bGDRAMAddrX + k);//水平地址write_com(0x30);write_data(*str++);write_data(*str++);}}bGDRAMAddrX = 0x88;}write_com(0x36);write_com(0x30);}2.任意坐标写汉字void write_sfm(uchar x,uchar y,uchar date){uchar shi,ge;shi=date/10;ge=date%10;switch(y){case 0:{write_com(0x80+x);write_data(0x30+shi);write_data(0x30+ge);}break;case 1:{write_com(0x90+x);write_data(0x30+shi);write_data(0x30+ge);}break;case 2:{write_com(0x88+x);write_data(0x30+shi);write_data(0x30+ge);}break;case 3:{write_com(0x98+x);write_data(0x30+shi);write_data(0x30+ge);}break;}}3.点显示子程序//*************点显示子程序************************//*** 0<=x<=127表示横坐标,0<=y<=63表示纵坐标colour=0表示删除点colour=1表示现实点void lcd_DisplayDot(uchar x,uchar y,uchar colour){uchar i,j,l,h,h1,l1;unsigned char bGDRAMAddrX = 0x80; //GDRAM水平地址unsigned char bGDRAMAddrY = 0x80;if(y>31){y-=32;bGDRAMAddrX = 0x88;}i=x/16;j=15-(x%16);k=k<<j;l=0xff&k;h=0xff&(k>>8);write_com(0x34);write_com(bGDRAMAddrY + y);//垂直地址write_com(bGDRAMAddrX + i);//水平地址write_com(0x30);read_data();h1=read_data();l1=read_data();write_com(0x34);write_com(bGDRAMAddrY + y);//垂直地址write_com(bGDRAMAddrX + i);//水平地址write_com(0x30);if(colour==0){write_data((~h)&h1);write_data((~l)|l1);}else{write_data(h|h1);write_data(l|l1);}write_com(0x36);write_com(0x30);}4.画横直线void lcd_DisplayHLine(unsigned char x0, unsigned char x1, unsigned char y, unsigned char color) {unsigned char bak;if(x0>x1) // 对x0、x1大小进行排列,以便画图{x1 = x0;x0 = bak;}do{lcd_DisplayDot(x0, y, color); // 从左到右逐点显示,描出垂直线x0++;}while(x1>=x0);}5.画纵直线void lcd_DisplayRLine(unsigned char x, unsigned char y0, unsigned char y1, unsigned char color) {unsigned char bak;if(y0>y1) // 对y0、y1大小进行排列,以便画图{bak = y1;y1 = y0;y0 = bak;}do{lcd_DisplayDot(x, y0, color); // 从上到下逐点显示,描出垂直线y0++;}while(y1>=y0);}6.画任意一直线void lcd_DisplayLine(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char color){unsigned char bak;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; // 决策变量dx = x1-x0; // 求取两点之间的差值dy = y1-y0;if(x0==x1) //判断是否为垂直线{lcd_DisplayRLine(x0,y0,y1,color); //画垂直线return;}if(y0==y1) //判断是否为水平线{lcd_DisplayHLine(x0,x1,y0,color); //画水平线return;}dx = x1-x0; // 求取两点之间的差值dy = y1-y0;/* 判断增长方向,或是否为水平线、垂直线、点*/ 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,画垂直线,或一点lcd_DisplayRLine(x0, y0, y1, color);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,画水平线,或一点lcd_DisplayHLine(x0, y0, x1, color);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){lcd_DisplayDot(x0, y0, color);x0 += dx_sym;if(di<0){di += dy_x2; // 计算出下一步的决策值}else{di += dy_x2 - dx_x2;y0 += dy_sym;}}lcd_DisplayDot(x0, y0, color); // 显示最后一点}else // 对于dx<dy,则使用y轴为基准{di = dx_x2 - dy;while(y0!=y1){lcd_DisplayDot(x0, y0, color);y0 += dy_sym;if(di<0){di += dx_x2;}else{di += dx_x2 - dy_x2;x0 += dx_sym;}}lcd_DisplayDot(x0, y0, color); // 显示最后一点}}7.填充矩形函数void lcd_DisplayRectangleFill(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char color){unsigned char i;if(x0>x1) // 若x0>x1,则x0与x1交换{i = x0;x0 = x1;x1 = i;}if(y0>y1) // 若y0>y1,则y0与y1交换{i = y0;y0 = y1;y1 = i;}//判断是否只是直线if(y0==y1){lcd_DisplayHLine(x0,x1,y0,color);return;}if(x0==x1){lcd_DisplayRLine(x0,y0,y1,color);return;}while(y0<=y1){lcd_DisplayHLine(x0,x1,y0,color); // 当前画水平线y0++; // 下一行}}8.画圆函数void lcd_DisplayCircle(unsigned char x0,unsigned char y0,unsigned char r,unsigned char color) {char a,b;char di;if(r>31 ||r==0) return; //参数过滤,次液晶显示的最大圆半径为31a=0;b=r;di=3-2*r; //判断下个点位置的标志while(a<=b){lcd_DisplayDot(x0-b,y0-a,color); //3lcd_DisplayDot(x0+b,y0-a,color); //0lcd_DisplayDot(x0-a,y0+b,color); //1lcd_DisplayDot(x0-b,y0-a,color); //7lcd_DisplayDot(x0-a,y0-b,color); //2lcd_DisplayDot(x0+b,y0+a,color); //4lcd_DisplayDot(x0+a,y0-b,color); //5lcd_DisplayDot(x0+a,y0+b,color); //6lcd_DisplayDot(x0-b,y0+a,color);a++;/***使用Bresenham算法画圆**/if(di<0)di +=4*a+6;else{di +=10+4*(a-b);b--;}lcd_DisplayDot(x0+a,y0+b,color);}}。
学习笔记:LCD12864的画点功能本人学习的LCD12864的驱动控制器是ST7920,本文档主要记述了本人学习LCD12864画点原理的一些心得及细节。
但只限于画点功能,其他功能将在其他文档中记述。
首先,先介绍与画点功能有关的存储器:GDRAM:(Graphic Display RAM):图形显示RAM,这一块区域用于绘图,往里面写啥,屏幕就会显示啥,它与DDRAM的区别在于,往DDRAM中写的数据是字符的编码,字符的显示先是在CGROM中找到字模,然后映射到屏幕上,而往GDRAM中写的数据是图形的点阵信息,每个点用1bit来保存其显示与否。
GDRAM的空间结构图如下图所示。
可以看出,它可以看作是32行*256列的屏幕,只不过分成上下半屏来显示,所以GDRAM 的地址是上下半屏连续循环的,例如点亮上半屏的第0行的第0列像素点,之后点亮第0行第2列、第0行第2列、第0行第4列......一直到第0行第127列,此时上半屏第0行像素已经全部点亮,当点亮第0行第128列时,点亮的像素点会是下半屏的第0行第0列,点亮第0行第129列会是下半屏的第0行第1列像素点亮,但要注意地址只是在当前行循环。
比如上面讲的例子,地址始终是在第0行循环的。
另外重要的是,在对GDRAM进行数据读/写时,当读/写完毕后,在当前的行上地址自动增加一个单位,这一个单位的大小为十六个格子,也就是16bit,一个字。
而读/写数据的最小单位也是一个字。
每一个方格代表一个像素点,在GDRAM空间结构中,以十六个方格为一个数据单位,即字(两个字节)。
在对GDRAM读写操作中,也是以字为单位的。
而又以16列*32行为一个单位,共分为16个单位。
分别为图中的0~15,为了方便叙述下面我将这些单位称之为模块(16列*32行,即32字)。
这些模块在存储中是有地址的,0~15分别对应地址80H~8FH。
上半屏为80H~87H,下半屏为88H~8FH。
看到工具箱旁边那个LCD12864很久没用了(当初买回来用的时候只是简单地测试了一下),于是萌生了重新写一次接口程序的想法(而且这次要给它加个图片显示的功能),好,说做就做,就用Atmega16和ICCAVR来做吧,最近这MCU和平台用得比较熟练。
马上从书堆里把当初打印出来的中文datasheet给翻了出来,依葫芦画瓢地写了个初始化程序。
好,OK。
编译通过。
于是又写了一个可以自定义从XY坐标值开始输出显示的函数,再次编译,也通过,OK。
于是呼马上写了四行简单的字符烧到单片机上试了一下,嘿嘿,一次通过。
如下图:后来在进一步测试的时候也出了点小问题。
就是我是使用USBISP烧写器把程序烧写进AVR的(此时实验板由USBISP烧写器供电),想要实现从第一行的第一个字符开始连续显示"0123456789"。
刚烧写完程序后能看到LCD12864上正常显示"0123456789",但是把烧写器从实验板上断开连接,单独用USB给实验板供电的时候,LCD的第一行只是显示"123456789",第一个字符消失了……,左思右想地弄了一个多小时后,终于把问题给解决了,就是把初始化程序的延时适当增加了些,真是奇怪。
刚开始一直想不通为什么在烧写器供电的情况下就正常显示,而换到USB供电后就出了问题。
后来再想想,估计是跟供电有关。
在使用USBISP烧写器供电的时候,LCD的背光灯明显比用USB供电的时候来得亮,而且对比度也高很多,看来是因为换到USB供电后,供电不怎么充足,以至于LCD在上电初始化的时候花上了更多的时间去初始化(因为供电低了,功率小了,跑起来有点力不从心,用的时间就久了嘛……我是觉得可以这样去理解的)接下来呢,就到了有点难度的画图了。
当初刚买到12864的时候只是简单测试了字符显示功能,除了因为画图还不需要用到,另外一个原因就是那datasheet上关于画图那部分的内容不怎么看得懂……。
/****************************************************************************** ******************************************************************************* *********///程序说明:本程序为12864(st7920)驱动程序,只实现了最简单的显示功能//端口设置:RS、RW、EN分别为P0口的0、1、2,数据口为P2口//用取模软件取的图形或汉字必须是逐行取的,因为本函数是在液晶上逐行打点的/****************************************************************************** ******************************************************************************* ********/#include<reg51.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 = P0^0;sbit LCD_RW = P0^1;sbit LCD_EN = P0^2;//************变量定义//uchar dis1[10];//读出缓存unsigned char tu[];//****************短延时void delay(uint k){uint i;uchar j;for(i = 0; i < k ;i ++)for(j = 0; j < 10 ;j ++);}//***********ms级延时函数/*void delay_1ms(uint x){uint i,j;for(j = 0;j < x; j++)for(i = 0;i < 110; i++);} *///***********12864写指令函数void write_com(uchar cmd){LCD_RS = 0;LCD_RW = 0;delay(5);LCD_EN = 1;P2 = cmd;delay(5);LCD_EN = 0;}//********12864写数据函数void write_dat(uchar dat){LCD_RS = 1;LCD_RW = 0;delay(5);LCD_EN = 1;P2 = dat;delay(5);LCD_EN = 0;}//****************从LCD中读数据uchar read_dat(void){uchar temp;P2 = 0XFF; //释放数据线LCD_RS = 1; //数据LCD_RW = 1; // 读模式LCD_EN = 1; //E为高电平进行读数据或指令delay(1);temp = P2;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(10);}}}write_com(DRAW_ON);//打开绘图显示write_com(BASIC_SET);//打开基本指令集}//************************************************************************////*********************清内部RAM************************//void clrgdram(){unsigned char x,y ;for(y=0;y<64;y++)for(x=0;x<16;x++){write_com(0x34);write_com(y+0x80);//行地址write_com(x+0x80);//列地址write_com(0x30);write_dat(0x00);write_dat(0x00);}}//***************************************************************************** ********************//***************有反白显示功能的打点函数**********************************************************//参数: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位数据中相应的点}elsewrite_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位数据中相应的点elsewrite_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);//3lcd_set_dot( x0 + b , y0 - a); //0lcd_set_dot( x0 - a , y0 + b); //1lcd_set_dot( x0 - b , y0 - a); //7lcd_set_dot( x0 - a , y0 - b); //2lcd_set_dot( x0 + b , y0 + a); //4lcd_set_dot( x0 + a , y0 - b); //5lcd_set_dot( x0 + a , y0 + b); //6lcd_set_dot( x0 - b , y0 + a);a ++;//***使用bresenham算法画圆********if(di < 0)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}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轴加1draw_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轴加1draw_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); //垂直地址Ywrite_com(bGDRAMAddrX+k); //水平地址Xwrite_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 elseGUI_Point(x,y,1); //将0反显}else{if(flag==0)GUI_Point(x,y,1);elseGUI_Point(x,y,0);}if( (++x)>127) return;//若显示超出了液晶屏则返回}}//***************************************************************************//*****************在自定义大小的区域内画图或画字函数****************************//参数说明:x、y指定显示区域的起始点坐标// dat 要输出显示的图形或汉字点阵数组。
#include<stdio.h>#include<reg52.h>#include<math.h>#include<intrins.h>sbit RS=P0^0;sbit RW=P0^1;sbit E=P0^2;sbit PSB=P0^3;sbit RES=P0^4;#define LCD12864_DatePort P2unsigned char lcd_x,lcd_y;unsigned char h[]={"aaaaaaaaaaaaaaaa"};unsigned char code image[1024]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0xC0,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0xFF,0xE0,0x00,0x00,0x00,0x1F,0xC1,0xC0,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x07,0xFF,0xF8,0x00,0x00,0x00,0xFF,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x1F,0xFF,0xFC,0x00,0x00,0x01,0xFF,0xFE,0x1F,0x80,0x00,0x00,0x00,0x00, 0x00,0x00,0x3F,0xFF,0xFF,0x00,0x00,0x07,0xFF,0xFF,0x9E,0xC0,0x00,0x00,0x00,0x00, 0x00,0x00,0x7F,0xFF,0xFF,0x80,0x00,0x0F,0xFF,0xFF,0xEC,0x60,0x00,0x00,0x00,0x00, 0x00,0x00,0xFF,0xFF,0xFF,0xC0,0x00,0x0F,0xFF,0xFF,0xE0,0x40,0x00,0x00,0x00,0x00, 0x00,0x01,0xFF,0x3E,0x3F,0xC0,0x00,0x1F,0xE3,0xE7,0xF0,0x80,0x00,0x00,0x00,0x00, 0x00,0x01,0xFE,0x1E,0x1F,0xE0,0x00,0x3F,0xE3,0xC3,0xF9,0x00,0x00,0x00,0x00,0x00, 0x00,0x03,0xFC,0x1C,0x1F,0xE0,0x00,0x3F,0xC1,0xC3,0xFC,0x00,0x00,0x00,0x00,0x00, 0x00,0x03,0xFC,0x6D,0x9F,0xF0,0x00,0x7F,0xC1,0x81,0xFE,0x00,0x00,0x00,0x00,0x00, 0x00,0x07,0xFC,0x6D,0x8F,0xF0,0x00,0x7F,0xCD,0xB1,0xFE,0x00,0x00,0x00,0x00,0x00, 0x00,0x07,0xFC,0x6D,0x9F,0xF0,0x00,0x7F,0xCF,0xB1,0xFF,0x00,0x00,0x00,0x00,0x00, 0x00,0x07,0xFC,0x1C,0x1F,0xF0,0x00,0xFF,0xC5,0xF3,0xFF,0x00,0x00,0x00,0x00,0x00, 0x00,0x07,0xFE,0x1E,0x1F,0xF8,0x00,0xFF,0xE3,0xC3,0xFF,0x00,0x00,0x00,0x00,0x00, 0x00,0x07,0xFF,0x3E,0x3F,0xF8,0x00,0xFF,0xE3,0xE7,0xFF,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0xFF,0xF3,0xFF,0xF8,0x00,0xFF,0xFE,0x7F,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x0F,0xF8,0xC1,0xCF,0xFC,0x00,0xFF,0x9C,0x38,0xFF,0x80,0x00,0x00,0x00,0x00, 0x00,0x0F,0xE0,0x7F,0x83,0xFC,0x01,0xFE,0x0F,0xF0,0x3F,0x80,0x00,0x00,0x00,0x00, 0x00,0x1F,0xFF,0x00,0x3F,0xFC,0x01,0xFF,0xE0,0x07,0xFF,0x80,0x00,0x00,0x00,0x00, 0x00,0x1F,0xFF,0xFB,0xFF,0xFC,0x01,0xBF,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00, 0x00,0x1F,0xFF,0xFF,0xFF,0xFC,0x01,0xDF,0xFF,0xFF,0xFB,0x80,0x00,0x00,0x00,0x00, 0x00,0x3F,0xFF,0xFF,0xFF,0xFC,0x03,0xC7,0xFF,0xFF,0xE7,0x80,0x00,0x00,0x00,0x00, 0x00,0x7F,0xFF,0xFF,0xFF,0xFC,0x07,0xC1,0xFF,0xFF,0x8F,0xC0,0x00,0x00,0x00,0x00, 0x00,0xFF,0xFF,0xFF,0xFF,0xFE,0x0F,0xE0,0x3F,0xFC,0x3F,0xC0,0x00,0x00,0x00,0x00, 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xF8,0x00,0x00,0x7F,0xE0,0x00,0x00,0x00,0x00, 0x01,0xFC,0x7F,0xFF,0xFF,0x3F,0x1F,0x8E,0x00,0x00,0xE3,0xF0,0x00,0x00,0x00,0x00, 0x03,0xF8,0x7F,0xFF,0xFC,0x1F,0xBF,0x87,0xC8,0x1F,0x83,0xF0,0x00,0x00,0x00,0x00, 0x03,0xF8,0x7F,0xFF,0xE0,0x1F,0xFF,0x87,0xFF,0xFC,0x01,0xF8,0x00,0x00,0x00,0x00, 0x03,0xF8,0x7F,0x00,0x00,0x1F,0xFF,0x04,0xE0,0x00,0x01,0xF8,0x00,0x00,0x00,0x00, 0x07,0xF8,0x7F,0x00,0x00,0x0F,0xFF,0x04,0x60,0x00,0x01,0xF8,0x00,0x00,0x00,0x00, 0x07,0xF8,0x7F,0x00,0x00,0x0F,0xFF,0x0C,0x60,0x00,0x01,0xFC,0x00,0x00,0x00,0x00, 0x07,0xF8,0x7F,0x00,0x00,0x0F,0xFF,0x0C,0x60,0x00,0x01,0xFC,0x00,0x00,0x00,0x00, 0x07,0xF8,0x7F,0x00,0x00,0x0F,0xFF,0x06,0x60,0x00,0x01,0xFC,0x00,0x00,0x00,0x00, 0x07,0xB8,0x3E,0x00,0x00,0x19,0xF3,0x87,0xE0,0x00,0x01,0xBC,0x00,0x00,0x00,0x00, 0x07,0x18,0x00,0x00,0x00,0x18,0xE3,0x80,0x00,0x00,0x01,0x18,0x00,0x00,0x00,0x00, 0x00,0x1C,0x00,0x00,0x00,0x10,0x01,0x80,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00, 0x00,0x0C,0x00,0x00,0x00,0x30,0x01,0xC0,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00, 0x00,0x06,0x00,0x00,0x00,0x60,0x00,0xE0,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00, 0x00,0x03,0x00,0x00,0x00,0x40,0x00,0x70,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00, 0x00,0x03,0x80,0x00,0x00,0xC0,0x00,0x78,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x00, 0x00,0x0D,0xE0,0x00,0x03,0xB8,0x01,0x9C,0x00,0x00,0x3B,0x00,0x00,0x00,0x00,0x00, 0x00,0x18,0x78,0x00,0x0F,0x7C,0x03,0xCF,0x00,0x00,0xE7,0x80,0x00,0x00,0x00,0x00, 0x00,0x10,0x1C,0x00,0x3E,0xFC,0x03,0xF3,0xE0,0x03,0xDF,0x80,0x00,0x00,0x00,0x00, 0x00,0x1A,0x0B,0xC1,0xE3,0xF8,0x01,0xF8,0xFC,0x3F,0x3F,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0xF1,0x80,0xEF,0xE0,0x00,0x7F,0xF0,0x1F,0xFC,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};void delay_ms(unsigned char z) //延时{unsigned i,j;for(i=0;i<z;i++)for(j=0;j<110;j++);}void LCD12864_WriteCmd(unsigned int cmd) //写命令{RS=0;RW=0;E=0;LCD12864_DatePort=cmd;delay_ms(5);E=1;delay_ms(5);E=0;}void LCD12864_WriteDate(unsigned char dat) //写数据{RS=1;RW=0;E=0;LCD12864_DatePort=dat;delay_ms(5);E=1;delay_ms(5);E=0;}void LCD12864_Init(){PSB=1; //1并行,0串行LCD12864_WriteCmd(0x30); //基本指令操作//delay_ms(5);LCD12864_WriteCmd(0x0c); //显示开,关光标//delay_ms(5);LCD12864_WriteCmd(0x01); //清除led的显示内容//delay_ms(5);LCD12864_WriteCmd(0x06);}/*====================================================函数功能:清除GCROM的内容,如果不清除会出现花屏现象清除方法:向GCROM中写入0x00来清除内========================================================*/ void LCD12864_ClearGDRAM(){unsigned char i,j,k;lcd_x=0x80;lcd_y=0x80;LCD12864_WriteCmd(0x34);for(i=0;i<2;i++){for(j=0;j<32;j++){LCD12864_WriteCmd(lcd_y+j);LCD12864_WriteCmd(lcd_x);for(k=0;k<16;k++){LCD12864_WriteDate(0x00);}}lcd_x=0x88;}LCD12864_WriteCmd(0x36);LCD12864_WriteCmd(0x30);}/*================================================================ 函数功能:向12864中写入一幅图片函数参数:lcd_x为水平显示位置,lcd_y为垂直显示位置,函数将12864屏分成上下两屏写入,上半屏LCD_x=0x80,下半屏lcd_x=0x88=================================================================*/ void LCD12864_WriteImage(unsigned char *p){unsigned char i,j,k;lcd_x=0x80;lcd_y=0x80;LCD12864_WriteCmd(0x34);for(i=0;i<2;i++) //分为上下两屏{for(j=0;j<32;j++){LCD12864_WriteCmd(lcd_y+j);LCD12864_WriteCmd(lcd_x);for(k=0;k<16;k++) //写入显示数据{LCD12864_WriteDate(*p++);}}lcd_x=0x88;}LCD12864_WriteCmd(0x36);LCD12864_WriteCmd(0x30);}bit LCD12864_CheckBusy() //读忙碌状态{bit i;RS=0;RW=1;E=1;LCD12864_DatePort=0xff;delay_ms(1);i=(bit)(LCD12864_DatePort&0x80);E=0;return(i);}unsigned char LCD12864_ReadByte( void ){unsigned char ReturnValue ;LCD12864_CheckBusy() ;LCD12864_DatePort = 0xff ;RS=1;RW=1;E=0;E=1;ReturnValue =LCD12864_DatePort;E=0;return ReturnValue ;}void LCD12864_DrawPoint( unsigned char X, unsigned char Y, unsigned char Color ) {unsigned char Row , Tier , Tier_bit ;unsigned char ReadOldH, ReadOldL ;LCD12864_WriteCmd( 0x34 ) ;LCD12864_WriteCmd( 0x36 ) ;Tier = X >> 4 ;Tier_bit = X & 0x0f ;if( Y < 32 ){Row = Y ;}else{Row = Y - 32 ;Tier += 8 ;}LCD12864_WriteCmd( Row + 0x80 ) ;LCD12864_WriteCmd( Tier + 0x80 ) ;LCD12864_ReadByte();ReadOldH = LCD12864_ReadByte() ;ReadOldL = LCD12864_ReadByte() ;LCD12864_WriteCmd( Row + 0x80 ) ;LCD12864_WriteCmd( 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 ;}LCD12864_WriteDate( ReadOldH ) ;LCD12864_WriteDate( 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 ;}LCD12864_WriteDate( ReadOldH ) ;LCD12864_WriteDate( ReadOldL ) ;}LCD12864_WriteCmd( 0x30 ) ;}void LCD12864_DrawLine_X( 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++ )LCD12864_DrawPoint( X0, Y, Color ) ;}void LCD12864_DrawLine_Y( 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++)LCD12864_DrawPoint( X, Y0, Color) ;}void LCD12864_DrawLine( 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 ){LCD12864_DrawLine_Y( StartX, StartY, EndY, Color ) ;return ;}else{incx = -1 ;}if( delta_y > 0 ){incy = 1 ;}else if(delta_y == 0 ){LCD12864_DrawLine_X( 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 ;}LCD12864_DrawPoint( StartX, StartY, Color ) ;/* Draw Line*/for( t = 0 ; t <= distance+1 ; t++ ){LCD12864_DrawPoint( StartX, StartY, Color ) ;x += delta_x ;y += delta_y ;if( x > distance ){x -= distance ;StartX += incx ;}if( y > distance ){y -= distance ;StartY += incy ;}}}void LCD12864_DrawCross(){LCD12864_DrawLine_X( 0, 127 , 0, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 7, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 15, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 23, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 31, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 39, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 47, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 55, 1 ) ;LCD12864_DrawLine_X( 0, 127 , 63, 1 ) ;LCD12864_DrawLine_Y( 0, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 15, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 31, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 47, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 63, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 79, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 95, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 111, 0 , 63, 1 ) ;LCD12864_DrawLine_Y( 127, 0 , 63, 1 ) ;}void LCD12864_Draw_ObliqueLine(){LCD12864_DrawLine( 0, 0, 127, 63, 1 ) ;LCD12864_DrawLine( 0, 63, 127, 0 , 1 ) ;LCD12864_DrawLine( 12, 0, 127, 63, 1 ) ;LCD12864_DrawLine( 52, 63, 127, 0 , 1 ) ;LCD12864_DrawLine( 32, 63, 98, 0, 1 ) ;LCD12864_DrawLine( 67, 0, 127, 63 , 1 ) ;}void LCD12864_DisplayOneChar(unsigned char X, unsigned char Y, unsigned char Data) {switch(Y){case 0:Y=0X80;break; //根据行数来选择相应地址case 1:Y=0X90;break;case 2:Y=0X88;break;case 3:Y=0X98;break;}LCD12864_WriteCmd( Y+X ); //这里不检测忙信号,发送地址码LCD12864_WriteDate(Data);}void LCD12864_DisplayListChar(unsigned char X, unsigned char Y, unsigned char *Data) {switch(Y){case 0:Y=0X80;break; //根据行数来选择相应地址case 1:Y=0X90;break;case 2:Y=0X88;break;case 3:Y=0X98;break;}LCD12864_WriteCmd( Y+X ); //这里不检测忙信号,发送地址码X = X*2;Y = 0;while( Data[Y] != 0 ) //若到达字串尾则退出{if (X <= 0x0F) //X坐标应小于0xF{LCD12864_WriteDate(Data[Y]);X++;Y++;}else break;}}void main(){LCD12864_Init();// write_com(0x1c);LCD12864_ClearGDRAM();//LCD12864_WriteImage(image);//LCD12864_Draw_ObliqueLine();LCD12864_DrawCross();//LCD12864_DisplayListChar(0,0,h);while(1);}。
/****************************************************************************** ******************************************************************************* *********///程序说明:本程序为12864(st7920)驱动程序,只实现了最简单的显示功能//端口设置:RS、RW、EN分别为P0口的0、1、2,数据口为P2口//用取模软件取的图形或汉字必须是逐行取的,因为本函数是在液晶上逐行打点的/****************************************************************************** ******************************************************************************* ********/#include<reg51.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 = P0^0;sbit LCD_RW = P0^1;sbit LCD_EN = P0^2;//************变量定义//uchar dis1[10];//读出缓存unsigned char tu[];//****************短延时void delay(uint k){uint i;uchar j;for(i = 0; i < k ;i ++)for(j = 0; j < 10 ;j ++);}//***********ms级延时函数/*void delay_1ms(uint x){uint i,j;for(j = 0;j < x; j++)for(i = 0;i < 110; i++);} *///***********12864写指令函数void write_com(uchar cmd){LCD_RS = 0;LCD_RW = 0;delay(5);LCD_EN = 1;P2 = cmd;delay(5);LCD_EN = 0;}//********12864写数据函数void write_dat(uchar dat){LCD_RS = 1;LCD_RW = 0;delay(5);LCD_EN = 1;P2 = dat;delay(5);LCD_EN = 0;}//****************从LCD中读数据uchar read_dat(void){uchar temp;P2 = 0XFF; //释放数据线LCD_RS = 1; //数据LCD_RW = 1; // 读模式LCD_EN = 1; //E为高电平进行读数据或指令delay(1);temp = P2;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(10);}}}write_com(DRAW_ON);//打开绘图显示write_com(BASIC_SET);//打开基本指令集}//************************************************************************////*********************清内部RAM************************//void clrgdram(){unsigned char x,y ;for(y=0;y<64;y++)for(x=0;x<16;x++){write_com(0x34);write_com(y+0x80);//行地址write_com(x+0x80);//列地址write_com(0x30);write_dat(0x00);write_dat(0x00);}}//***************************************************************************** ********************//***************有反白显示功能的打点函数**********************************************************//参数: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位数据中相应的点}elsewrite_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位数据中相应的点elsewrite_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);//3lcd_set_dot( x0 + b , y0 - a); //0lcd_set_dot( x0 - a , y0 + b); //1lcd_set_dot( x0 - b , y0 - a); //7lcd_set_dot( x0 - a , y0 - b); //2lcd_set_dot( x0 + b , y0 + a); //4lcd_set_dot( x0 + a , y0 - b); //5lcd_set_dot( x0 + a , y0 + b); //6lcd_set_dot( x0 - b , y0 + a);a ++;//***使用bresenham算法画圆********if(di < 0)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}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轴加1draw_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轴加1draw_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); //垂直地址Ywrite_com(bGDRAMAddrX+k); //水平地址Xwrite_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 elseGUI_Point(x,y,1); //将0反显}else{if(flag==0)GUI_Point(x,y,1);elseGUI_Point(x,y,0);}if( (++x)>127) return;//若显示超出了液晶屏则返回}}//***************************************************************************//*****************在自定义大小的区域内画图或画字函数****************************//参数说明:x、y指定显示区域的起始点坐标// dat 要输出显示的图形或汉字点阵数组。