实验名称: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:
这次实验是我们和另外一个组合作的,说是合作,其实效率太低,这让我想到了《人月神话》,
资源就那么多,单纯的增加人员,收效不大反而更小,小小的团队如此,何况真正开发的团队。但这次实验还是了解了英文汉字在屏幕中是如何显示的,汉字和英文在计算机是以什么形式存储的,机内码和区位码转换,八分法画圆。而且比较深的感受是,对硬件的操作多数是直接对寄存器内存进行读写。