实验名称:LCD显示实验
实验目的:
1. 了解LCD显示的基本原理
2. 掌握LCD显示汉字和英文的方法原理
3. 掌握LCD显示图形的方法
实验过程(含步骤):
1. 过程:这次LCD显示实验是组内的燕同学提出的想法,起初大家感觉挺不错的,同时也想满足一下她,但是在实验中展示出来的效果并不是理想中的,并且我们也是和另外一个组合作的,但毕竟机器只有一台,实际参与的人并不多,也不可能多。在机器中还不能做到随心所欲,有些想实现的功能还没来得及实现,画面不够想象中唯美,但实验的基本要求是做到了。虽然可能没有其他某些组的好看啥的,但我们自始自终都是在为开始的设想努力,如此而已。
2. 代码如下:
/*Main.c */ #include "def.h" #include "2410lib.h" #include "option.h" #include "2410addr.h" #include "interrupt.h" #include "lcdlib.h" /*STN,反射式LCD器件,功耗小,在比较暗的环境中清晰度很差,需配备外部明光源*/ /*难做出高分辨率的产品,适于显示小尺寸黑白数字,字符,手表,时钟,电话,传真机*/ //#define STN_LCD /*TFT,“背透”与“反射”结合,耗电和成本较高,反应快,在液晶的背部设置特殊光管*/ /*显示品质好,适用于电脑,手机,液晶电视等*/ #define TFT_8_0 int length=800;//目标机屏幕长800像素 int width=600;/目标机屏幕宽600像素 void (*PutPixel)(U32,U32,U32); void Lcd_Disp_Char(void); void Lcd_Disp_Grap(void); void print_Circle(int r,int x,int y,int color); /******************************************************************** // Function name : Main // Description : JXARM9-2410 LCD显示实验主程序 // 实现功能:显示设计的画面 // Return type : void // Argument : void *********************************************************************/ void Main(void) { /* 配置系统时钟 */ ChangeClockDivider(2,1); U32 mpll_val = 0 ; mpll_val = (92<<12)|(1<<4)|(1); ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); /* 初始化端口 */ Port_Init(); /* 初始化串口 */ Uart_Init(0,115200); Uart_Select(0); /* 打印提示信息 */ PRINTF("\n---LCD测试程序---\n"); PRINTF("\n请将UART0与PC串口进行连接,然后启动超级终端程序(115200, 8, N, 1)\n"); /* LCD初始化 */ Lcd_Port_Init(); //#ifdef STN_LCD // Lcd_Init(MODE_CSTN_8BIT); // Glib_Init(MODE_CSTN_8BIT); // Lcd_CstnOnOff(1); // // Glib_ClearScr(0xff, MODE_CSTN_8BIT); //#else #ifdef TFT_8_0 rGPCCON &= ~(3<<8); rGPCCON |= (2<<8); /*主要:设置16bpp TFT模式,RGB5:6:5格式*/ Lcd_Init(MODE_TFT_16BIT_800600);//具体函数在末尾 Glib_Init(MODE_TFT_16BIT_800600);//根据所传参数确定函数指针PutPixel使用哪个函数 Glib_ClearScr(0xffff, MODE_TFT_16BIT_800600);//清屏 Lcd_PowerEnable(0, 1);/*STN-LCD/TFT-LCD的电源控制信号PWERN使能和控制信号极性*/ Lcd_EnvidOnOff(1); /*允许视频和LCD控制信号输出*/ #endif //#endif //#define LCD_DISP_CHAR #ifdef LCD_DISP_CHAR //改 #else Lcd_Disp_Grap();/*河流颜色渐变*/ Lcd_Disp_Char();/*“心怀沧月”的中文显示*/ Glib_disp_ascii16x8(3*length/4,width/10,"Our Picture",0x0);/*“Out Picture”英文显示*/ Glib_Line(length/14,(7*width)/10,length/5,width/4,0x0);/*山的线条1*/ Glib_Line(length/5,width/4,2*length/7,(7*width)/10,0x0);/*山的线条2*/ Glib_Line(33*length/140,(4*width)/10,2*length/7,width/5,0x0);/*山的线条3*/ Glib_Line(2*length/7,width/5,52*length/140,(7*width)/10,0x0);/*山的线条4*/ Glib_Line(87*length/140,(7*width)/10,105*length/140,width/5,0x0);/*山的线条5*/ Glib_Line(105*length/140,width/5,11*length/14,7*width/20,0x0);/*山的线条6*/ Glib_Line(10*length/14,(7*width)/10,12*length/14,15*width/100,0x0);/*山的线条7*/ Glib_Line(length,(7*width)/10,12*length/14,15*width/100,0x0);/*山的线条8*/ Glib_Line(0,(7*width)/10,length,7*width/10,0x0);/*山的线条9*/ print_Circle(3*width/25,length/2,(3*width)/20,0xFFB0);/**/ while(1); #endif // } } void Lcd_Disp_Char(void)//{ /* 显示字符串 */ Glib_disp_hzk16(30,10,"心", 0x0); Glib_disp_hzk16(30,60,"怀", 0x0); Glib_disp_hzk16(30,110,"沧", 0x0); Glib_disp_hzk16(30,160,"月", 0x0); } /*在屏幕纵轴方向颜色渐变*/ void Lcd_Disp_Grap(void) { int i,j; int a=0x0010; int color=0x07e0; for(j=600;j>7*width/10;j--){ //从600到420方向进行填色 for(i=0;i<800;i++){ //屏幕横轴为800像素 (*PutPixel)(i,j,color); } if(j%5==0){ //每5行进行一次颜色渐变 color=a; a=a+0x0020; } } } /* Delay (100); for(j=0;j<800;j++) for(i=0;i<600;i++) //刷屏参考 (*PutPixel)(j,i,0x07e0); Delay (100); */ /*八分法画圆*/ void print_Circle(int r,int x,int y,int color){ int a,b; a=0;b=r;//初始化起点为(0,r) while(2*b*b>=r*r-100){//以纵坐标b来限制变化范围为八分之一的弧*/ /*八个对称域,只要画出八分之一弧,利用对称性就可以画出整个圆*/ Glib_Line(x+a,y-b,x,y,color); Glib_Line(x-a,y-b,x,y,color); Glib_Line(x-a,y+b,x,y,color); Glib_Line(x+a,y+b,x,y,color); Glib_Line(x+b,y-a,x,y,color); Glib_Line(x-b,y-a,x,y,color); Glib_Line(x-b,y+a,x,y,color); Glib_Line(x+b,y+a,x,y,color); a++;//横坐标往前试探性+1 if(a*a+b*b-r*r>=0){//若在圆外 a--;b--;//回退到上一个点(a,b)的下方(a,b-1)处 } } } /*glib.c*/ #include "def.h" #include "lcdlib.h" #include "glib.h" #include "lcd.h" #include "hzk16.h" #include "ascii.h" void (*PutPixel)(U32,U32,U32); void Glib_Init(int type) { switch(type) { case MODE_STN_1BIT: PutPixel=_PutStn1Bit; break; case MODE_CSTN_12BIT: PutPixel=_PutCstn12Bit; break; case MODE_TFT_8BIT_240320: PutPixel=_PutTft8Bit_240320; break; case MODE_TFT_16BIT_240320: PutPixel=_PutTft16Bit_240320; break; case MODE_TFT_1BIT_640480: PutPixel=_PutTft1Bit_640480; break; //-------------------------------------- case MODE_TFT_1BIT_800600: PutPixel=_PutTft1Bit_800600; break; case MODE_TFT_8BIT_800600: PutPixel=_PutTft8Bit_800600; break; case MODE_TFT_16BIT_800600: PutPixel=_PutTft16Bit_800600; break; //-------------------------------------- default: break; } } void _PutTft8Bit_640480(U32 x,U32 y,U32 c) { if(x<SCR_XSIZE_TFT_640480 && y<SCR_YSIZE_TFT_640480) frameBuffer8BitTft640480[(y)][(x)/4]=( frameBuffer8BitTft640480[(y)][x/4] & ~(0xff000000>>((x)%4)*8) ) | ( (c&0x000000ff)<<((4-1-((x)%4))*8) ); } void _PutTft16Bit_640480(U32 x,U32 y,U32 c) { if(x<SCR_XSIZE_TFT_640480 && y<SCR_YSIZE_TFT_640480) frameBuffer16BitTft640480[(y)][(x)/2]=( frameBuffer16BitTft640480[(y)][x/2] & ~(0xffff0000>>((x)%2)*16) ) | ( (c&0x0000ffff)<<((2-1-((x)%2))*16) ); } void _PutTft24Bit_640480(U32 x,U32 y,U32 c) { if(x<SCR_XSIZE_TFT_640480 && y<SCR_YSIZE_TFT_640480) frameBuffer24BitTft640480[(y)][(x)]=( frameBuffer24BitTft640480[(y)][(x)] & (0x0) | ( c&0xffffff00)); // | ( c&0x00ffffff)); LSB } void _PutTft8Bit_800600(U32 x,U32 y,U32 c) { if(x<SCR_XSIZE_TFT_800600 && y<SCR_YSIZE_TFT_800600) frameBuffer8BitTft800600[(y)][(x)/4]=( frameBuffer8BitTft800600[(y)][x/4] & ~(0xff000000>>((x)%4)*8) ) | ( (c&0x000000ff)<<((4-1-((x)%4))*8) ); } /*此处为实验所使用的函数*/ void _PutTft16Bit_800600(U32 x,U32 y,U32 c) //需要16位存储一个点的状态(即颜色信息) { if(x<SCR_XSIZE_TFT_800600 && y<SCR_YSIZE_TFT_800600)//判断x,y是否在屏幕有效范围内 /*数组的一个单元为一个U32型(即unsigned int)(U32在Def.h中定义了),32位*/ /*因此保存整个屏幕像素点的信息,可以用一个单元保存两个点,存储空间压缩一半*/ /*以下每两个点存储在数组的一个单元位置中,如[y][4/2]和[y][5/2]同时存储在[y][2] ,以低16位和高16位区分这两个像素点信息*/ frameBuffer16BitTft800600[(y)][(x)/2]=( frameBuffer16BitTft800600[(y)][x/2] & ~(0xffff0000>>((x)%2)*16) ) | ( (c&0x0000ffff)<<((2-1-((x)%2))*16) ); /*先&是对即将要存储像素点信息的那16位进行清零(避免影响后来c值的正确性),位置(低16位还是高16位)根据模2的结果来定*/ /*c&0x0000ffff是保留c的低16位(使用的就是16位颜色模式),<<到的位置和前面清零的位置是一致的(前面是从左到右移位,后面是从右往左移位,要移到相同的位置,移的位数是互补的,所以有左移时的(2-1-((x)%2))*16))*/ } /*void Glib_FilledRectangle(int x1,int y1,int x2,int y2,int color) { int i; for(i=y1;i<=y2;i++) Glib_Line(x1,i,x2,i,color); } */ // LCD display is flipped vertically // But, think the algorithm by mathematics point. // --+-- <-8 octants mathematical cordinate // 八分法画直线 void Glib_Line(int x1,int y1,int x2,int y2,int color) { int dx,dy,e; dx=x2-x1; dy=y2-y1; if(dx>=0) { if(dy >= 0) // dy>=0 { if(dx>=dy) // 1/8 octant { e=dy-dx/2; while(x1<=x2) { PutPixel(x1,y1,color); if(e>0){y1+=1;e-=dx;} x1+=1; e+=dy; } } else // 2/8 octant { e=dx-dy/2; while(y1<=y2) { PutPixel(x1,y1,color); if(e>0){x1+=1;e-=dy;} y1+=1; e+=dx; } } } else // dy<0 { dy=-dy; // dy=abs(dy) if(dx>=dy) // 8/8 octant { e=dy-dx/2; while(x1<=x2) { PutPixel(x1,y1,color); if(e>0){y1-=1;e-=dx;} x1+=1; e+=dy; } } else // 7/8 octant { e=dx-dy/2; while(y1>=y2) { PutPixel(x1,y1,color); if(e>0){x1+=1;e-=dy;} y1-=1; e+=dx; } } } } else //dx<0 { dx=-dx; //dx=abs(dx) if(dy >= 0) // dy>=0 { if(dx>=dy) // 4/8 octant { e=dy-dx/2; while(x1>=x2) { PutPixel(x1,y1,color); if(e>0){y1+=1;e-=dx;} x1-=1; e+=dy; } } else // 3/8 octant { e=dx-dy/2; while(y1<=y2) { PutPixel(x1,y1,color); if(e>0){x1-=1;e-=dy;} y1+=1; e+=dx; } } } else // dy<0 { dy=-dy; // dy=abs(dy) if(dx>=dy) // 5/8 octant { e=dy-dx/2; while(x1>=x2) { PutPixel(x1,y1,color); if(e>0){y1-=1;e-=dx;} x1-=1; e+=dy; } } else // 6/8 octant { e=dx-dy/2; while(y1>=y2) { PutPixel(x1,y1,color); if(e>0){x1-=1;e-=dy;} y1-=1; e+=dx; } } } } } /*清除屏幕*/ void Glib_ClearScr(U32 c, int type) { //Very inefficient function. int i,j; if((type==MODE_TFT_1BIT_800600)|(type==MODE_TFT_8BIT_800600)|(type==MODE_TFT_16BIT_800600)) if((type&0x4000)&&(type&0x400)) for(j=0;j<SCR_YSIZE_TFT_800600;j++) for(i=0;i<SCR_XSIZE_TFT_800600;i++) PutPixel(i,j,c); //else } /***************************************************************************** // Function name : Glib_disp_hzk16 // Description : 在LCD的(x,y)坐标处以colour颜色显示s中的汉字 // Return type : void // Argument : int x : x坐标 // Argument : int y : y坐标 // Argument : char *s : 待显示字符串 // Argument : int colour : 显示颜色 *****************************************************************************/ void Glib_disp_hzk16(int x,int y,char *s,int colour) { char buffer[32]; /* 32字节的字模缓冲区 */ int i,j,k; unsigned char qh,wh; unsigned long location; while(*s) { /*计算机汉字信息是以机内码存储,区位码=机内码-0xa0a0*/ qh=*s-0xa0; /* 计算区码 */ wh=*(s+1)-0xa0; /* 计算位码 */ location=(94*(qh-1)+(wh-1))*32L; /* 计算字模在文件中的位置,区和位从1开始 */ /*一个区94个汉字,一个汉字字模32字节*/ memcpy(buffer, &__HZK16X16__[location], 32); // 获取汉字字模,存储在buffer for(i=0;i<16;i++) /* 每一行 */ { for(j=0;j<2;j++) /* 一行两个字节 */ { for(k=0;k<8;k++) /* 每个字节按位显示 */ { /*buffer是一维数组,一个单元一个字节即8位*/ if(((buffer[i*2+j]>>(7-k)) & 0x1) != 0)//对一个单元中的每一位进行判断 PutPixel(x+8*(j)+k,y+i,colour); /* 显示一位 */ } } }//到这里处理完一个中文字符的显示 s+=2; /*一个汉字需两个char,这里指下一个汉字 */ x+=16; /*定位在原x往后16个像素点的位置,指汉字间距 */ } } /***************************************************************************** // Function name : lcd_disp_ascii16x8 // Description : 在LCD的(x,y)坐标处以colour颜色显示s中的ASCII字符 // Return type : void // Argument : int x : x坐标 // Argument : int y : y坐标 // Argument : char *s : 待显示字符串 // Argument : int colour : 显示颜色 *****************************************************************************/ void Glib_disp_ascii16x8(int x,int y,char *s,int colour) { char buffer[16]; /* 16字节的字模缓冲区 */ int i,j,k; unsigned long location; unsigned char a; while(*s) { a=*s; location=(a)*16L; /*(a)是ascii表示的,ascii头文件中按ascii排序, 无需经历像汉字的转换,直接计算出位置即可*/ memcpy(buffer, &__ASCII8X16__[location], 16); /* 在ascii.h中获取英文字模*/ for(i=0;i<16;i++) /* 每一行 */ { for(k=0;k<8;k++) /* 每个字节按位显示 */ { if(((buffer[i]>>(7-k)) & 0x1) != 0)/*分别判断每一位*/ PutPixel(x+k,y+i,colour); /* 显示一位 */ } } s+=1; /* 下一个char */ x+=16; /* 字符间距,隔16个像素点再显示下一个char */ } } /*Lcd_Init函数*/ void Lcd_Init(int type) { switch(type) { case MODE_TFT_16BIT_800600: frameBuffer16BitTft800600=(U32 (*)[SCR_XSIZE_TFT_800600/2])LCDFRAMEBUFFER; rLCDCON1=(CLKVAL_TFT_800600<<8)|(MVAL_USED<<7)|(3<<5)|(12<<1)|0; // TFT LCD panel,16bpp TFT,ENVID=off//设置每像素位BPP模式为16bpp TFT rLCDCON2=(VBPD_800600<<24)|(LINEVAL_TFT_800600<<14)|(VFPD_800600<<6)|(VSPW_800600); rLCDCON3=(HBPD_800600<<19)|(HOZVAL_TFT_800600<<8)|(HFPD_800600); rLCDCON4=(MVAL<<8)|(HSPW_800600); rLCDCON5=(1<<11)|(1<<10)|(1<<9)|(1<<8);//设置RGB5:6:5模式 //BPP24BL:x,FRM565:o,INVVCLK:x,INVVLINE:o,INVVFRAME:o,INVVD:x, //INVVDEN:x,INVPWREN:x,INVLEND:x,PWREN:x,ENLEND:x,BSWP:x,HWSWP:x rLCDSADDR1=(((U32)frameBuffer16BitTft800600>>22)<<21)|M5D((U32)frameBuffer16BitTft800600>>1); rLCDSADDR2=M5D( ((U32)frameBuffer16BitTft800600+(SCR_XSIZE_TFT_800600*LCD_YSIZE_TFT_800600*2))>>1 ); rLCDSADDR3=(((SCR_XSIZE_TFT_800600-LCD_XSIZE_TFT_800600)/1)<<11)|(LCD_XSIZE_TFT_800600/1); rLCDINTMSK|=(3); // MASK LCD Sub Interrupt rLPCSEL&=(~7); // Disable LPC3600 rTPAL=0; // Disable Temp Palette break; default: break; } } /*Lcd_EnvidOnOff函数*/ void Lcd_EnvidOnOff(int onoff) { if(onoff==1) rLCDCON1|=1; // ENVID=ON else rLCDCON1 =rLCDCON1 & 0x3fffe; // ENVID Off } /*Lcd_PowerEnable函数*/ void Lcd_PowerEnable(int invpwren,int pwren) { //GPG4 is setted as LCD_PWREN rGPGUP=rGPGUP&(~(1<<4))|(1<<4); // Pull-up disable rGPGCON=rGPGCON&(~(3<<8))|(3<<8); //GPG4=LCD_PWREN //Enable LCD POWER ENABLE Function rLCDCON5=rLCDCON5&(~(1<<3))|(pwren<<3); // PWREN rLCDCON5=rLCDCON5&(~(1<<5))|(invpwren<<5); // INVPWREN }
实验总结:
wq:
这次led显示实验,我们组制作了一副简易的山水画,先还是老问题,机器连接是硬伤。然后我们就开始在机器上作图了,这次我们的图上文字(包括汉字和英文)是采取绝对定位的形式,直接给出像素坐标来显示的;而图形部分(三角形的山,原形的月亮)是利用屏幕尺寸和图形比例来相对定位显示的。
yy:
这次led显示实验,因为机器连接问题,所以我们是两个组共用一台机器来做的,因此每个人接触机器的时间变少了。所以在这次实验中,我做的更多的不是编写代码,而是阅读代码,尽管这次实验的代码阅读起来也比较有意思,但是我还是衷心希望学校的机器能不要再出这么多问题
zhy:
这次led显示实验,由我设计了一副简易的山水图——《心怀沧月》,因为没带自己的电脑,只有先用PPT设计,然后经过测量计算得出各个线条的连接点,最后带入代码画出图 。图中主要有三角形和矩形以及圆形,汉字与英文。其中三角形、矩形:给相对点画线;三角形为山,本意用泥土色,深绿色,白色逐层渐变,却未实现;大矩形为海,蓝色渐变,实现。圆形:八分法画圆,利用圆的对称特性提高画圆效率;淡黄色涂色,实现。最后,我发现有其他组非常聪明,用PS软件,先画一幅完整的图,PS上不仅提供了点的坐标还有颜色的十六进制值,用于设计和代码实现均方便许多。
cxy:
这次lcd实验让我学到了不少东西。以前觉得在电脑上画图片很容易,现在发现从底层实现画图其实根本不容易。尤其是在画月亮的时候,使用八分法画圆画出来没有什么问题,但是在填充完颜色之后,问题就出现了,圆圈中有缝隙没有被填充上颜色。尝试修改了很多次才去除了中间的缝隙。
lg:
这次实验是我们和另外一个组合作的,说是合作,其实效率太低,这让我想到了《人月神话》,
资源就那么多,单纯的增加人员,收效不大反而更小,小小的团队如此,何况真正开发的团队。但这次实验还是了解了英文汉字在屏幕中是如何显示的,汉字和英文在计算机是以什么形式存储的,机内码和区位码转换,八分法画圆。而且比较深的感受是,对硬件的操作多数是直接对寄存器内存进行读写。