/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
LCDCON1 &= ~(1<<0);
}
/*======================================================================================*/
4.6.从结构体变量获取LCD重要参数
代码如下:
/*获得结构体LCD参数函数*/
void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp)
{
*fb_base = lcd_4_3_params.fb_base;
*xres = lcd_4_3_params.xres;
*yres = lcd_4_3_params.yres;
*bpp = lcd_4_3_params.bpp;
}
4.7.写测试函数
有了上面的准备工作,从现在开始就显示简单一点了,刚开始写测试代码,为了显得简单一点,让整个屏幕显示出同一种颜色、
如何做呢?
直接往frambuffer填入颜色数值就行了。且设置bpp = 16,那么一个颜色的数据就占用两个字节。
代码如下:
/*LCD测试函数*/
void lcd_test(void)
{
unsigned int fb_base;
int xres, yres, bpp;
int x, y;
unsigned short *p;
/*初始化LCD*/
lcd_init();
/*获得参数*/
//这个函数获取得到的数值是在lcd_test里面使用的
get_lcd_params(&fb_base,&xres,&yres,&bpp);
/*判断bpp的数值*/
if (bpp == 16)
{
/* 让LCD输出整屏的红色 */
/* 565: 0xf800 */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0xf800;
/* green */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x7e0;
/* blue */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x1f;
/* black */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0;
}
}
接着在主函数中调用这个测试函数,如下:
int main()
{
while(1)
{
/*lcd测试函数*/
lcd_test();
}
return 0;
}
4.8.修改Makefile
在这里增加了lcd.c和lcd.h需要把它添加到编译的项目,如下:
all: start.o sdram_init.o interrupt.o nand_flash.o main.o led.o uart.o execption.o timer.o lib1funcs.o my_printf.o string_utils.o lcd.o
arm-linux-ld -T lcd.lds $^ libgcc.a -o lcd.elf
arm-linux-objcopy -O binary -S lcd.elf lcd.bin
arm-linux-objdump -D lcd.elf > lcd.dis
%.o : %.c
arm-linux-gcc -march=armv4 -c -o $@ $<
%.o : %.S
arm-linux-gcc -march=armv4 -c -o $@ $<
clean:
rm *.bin *.o *.elf *.dis
4.9.编译下载
把代码上传到Linux系统编译,下载lcd.bin文件到开发版,复位之后,屏幕依次红绿蓝刷屏。
备注:此部分代码命令为:LCD显示01
5.画点函数实现
5.1.新建两个函数,drawpoint.c和drawpoint.h
1).编写drawpoint.c函数
在这个文件中需要用到一些,参数比如,分辨率,bpp,以及frambuffer的地址,因此,写一个函数来获取,然后保存在这个文件的全局变量中,以便这个文件调用,如下:
/* 获得LCD参数,static表示对外界这些变量是不可见的*/
static unsigned int fb_base;
static int xres, yres, bpp;
void drawpoint_get_lcd_params(void)
{
/*获得的参数保存在drawpoint.c的局部变量中,对外界不可见*/
get_lcd_params(&fb_base, &xres, &yres, &bpp);
}
在这里需要注意一个问题,RGB颜色都是32位的
但是如果使用16bpp的话,颜色数据才两个字节,因此需要把32位的颜色数据转换为16位,且16bpp时数据组成格式是RGB565
把32bpp的颜色数据转换为16bpp
函数如下:
/* rgb: 0x00RRGGBB */
unsigned short convert32bppto16bpp(unsigned int rgb)
{
int r = (rgb >> 16)& 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
/* rgb565 */
r = r >> 3;
g = g >> 2;
b = b >> 3;
return ((r<<11) | (g<<5) | (b));
}
接着实现画点函数,这个函数有三个参数,x,y代表坐标,第三个参数,代表这个点的颜色color
代码如下:
/* color : 32bit, 0x00RRGGBB
*
*/
void fb_drawpoint(int x, int y, unsigned int color)
{
unsigned char *pc; /* 8bpp */
unsigned short *pw; /* 16bpp */
unsigned int *pdw; /* 32bpp */
unsigned int pixel_base = fb_base + (xres * bpp / 8) * y + x * bpp / 8;
switch (bpp)
{
case 8:
pc = (unsigned char *) pixel_base;
*pc = color;
break;
case 16:
pw = (unsigned short *) pixel_base;
*pw = convert32bppto16bpp(color);
break;
case 32:
pdw = (unsigned int *) pixel_base;
*pdw = color;
break;
}
}
在这里需要清楚画点的原理,从上面可知,画点,其实就是往frambuffer特定的地址,填入颜色数据而已。那如何去计算这个值呢?
有一个LCD的屏幕如下面简图所示:
这个点如何计算呢?
算出y轴的偏移,即: 先算出一行所占的字节数,然后乘于 y0行 ===》 (xres * bpp /8) *y0,此时的点就是(0,y0)点的颜色数据
加上x轴的偏移,即:(xres * bpp /8) *y0 + x0*bpp/8 ,此时的点就是(x0,y0)的颜色数据
最后加上基地址就是在内存frambuffer对应颜色数据的值了。
还有一点需要注意:决定了每个像素所占字节的大小。
6.画圆画线函数
画圆画线的基础都是画点,圆和线都是由一个个点组成的,只要有了画点的基础,实现这个功能就是算法的问题了。
代码如下:
void draw_circle(int x, int y, int r, int color)
{
int a, b, num;
a = 0;
b = r;
/*画点之前先获得LCD参数*/
drawpoint_get_lcd_params();
while(22 * b * b >= r * r) // 1/8圆即可
{
fb_drawpoint(x + a, y - b,color); // 0~1
fb_drawpoint(x - a, y - b,color); // 0~7
fb_drawpoint(x - a, y + b,color); // 4~5
fb_drawpoint(x + a, y + b,color); // 4~3
fb_drawpoint(x + b, y + a,color); // 2~3
fb_drawpoint(x + b, y - a,color); // 2~1
fb_drawpoint(x - b, y - a,color); // 6~7
fb_drawpoint(x - b, y + a,color); // 6~5
a++;
num = (a * a + b * b) - r*r;
if(num > 0)
{
b--;
a--;
}
}
}
//-----------画线。参数:起始坐标,终点坐标,颜色--------
void draw_line(int x1,int y1,int x2,int y2,int color)
{
int dx,dy,e;
/*画点之前先获得LCD参数*/
drawpoint_get_lcd_params();
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)
{
fb_drawpoint(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)
{
fb_drawpoint(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)
{
fb_drawpoint(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)
{
fb_drawpoint(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)
{
fb_drawpoint(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)
{
fb_drawpoint(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)
{
fb_drawpoint(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)
{
fb_drawpoint(x1,y1,color);
if(e>0){x1-=1;e-=dy;}
y1-=1;
e+=dx;
}
}
}
}
}
修改lcd_test函数,调用画点画圆函数,如下:
/*LCD测试函数*/
void lcd_test(void)
{
unsigned int fb_base;
int xres, yres, bpp;
int x, y;
unsigned short *p;
/*初始化LCD*/
lcd_init();
/*获得参数*/
//这个函数获取得到的数值是在lcd_test里面使用的
get_lcd_params(&fb_base,&xres,&yres,&bpp);
//这个函数中获取的值是在画点函数中用的,
//fb_get_lcd_params();
/*获得字体需要用的LCD参数*/
//font_init();
/*判断bpp的数值*/
if (bpp == 16)
{
/* 让LCD输出整屏的红色 */
/* 565: 0xf800 */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0xf800;
/* green */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x7e0;
/* blue */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0x1f;
/* black */
p = (unsigned short *)fb_base;
for (x = 0; x < xres; x++)
for (y = 0; y < yres; y++)
*p++ = 0;
}
delay(1000000);
/* 画线 */
draw_line(0, 0, xres - 1, 0, 0xff0000);
draw_line(xres - 1, 0, xres - 1, yres - 1, 0xffff00);
draw_line(0, yres - 1, xres - 1, yres - 1, 0xff00aa);
draw_line(0, 0, 0, yres - 1, 0xff00ef);
draw_line(0, 0, xres - 1, yres - 1, 0xff4500);
draw_line(xres - 1, 0, 0, yres - 1, 0xff0780);
delay(1000000);
/* 画圆 */
draw_circle(xres/2, yres/2, yres/4, 0xff00);
delay(1000000);
}
下载烧录,就可以看到画线和画圆的效果了。
备注:这个部分的代码命名为:LCD画线画圆
7.LCD输出字符串
字符串的显示的基础也是画点,字符都是由一个个点组成了,所以在输入字符之前,我们首先需要知道字体多大。
在这里使用 8 * 16 为例(宽为8,长为16,一个字符占用16个字节的数据),那么一个字符就需要占据128像素。
查看一下点阵文件中A字符的表示方法:
总共有16个字节数据,从左至右,从上至下,如果哪个字节的位为1,则画点,A字符由16字节组成的点阵字符
同理,B的点阵字符集如下:
由这样的一个一个字符的点阵集组成一个数组,就构成了LCD输出字符串的基础
备注:这些点阵字符在fontdata_8x16[ ] 数组中的排序方式是以ASCII码的方式。
如果想取出字符‘A’ 点阵字符集,直接使用 fontdata_8x16[ A * 16 ]
字符A的ascii码是:65
有了这个点阵数组,和这些基础知识就可以开始写程序了。
先实现一个最简单的程序,在指定的坐标显示指定的单个字符。
1)、新建文件font.c和font.h文件
写font.c文件,实现输出字符函数
思路:
比如现在想打印出 'A'字符,那我们必须在刚才那个点阵数组中,把这个字符的16字节点阵数据取出来,然后读取这16字节数据的每一个点,根据数据来决定是否画点。
代码如下
/* 根据字母的点阵在LCD上描画文字 */
void fb_print_char(int x, int y, char c, unsigned int color)
{
int i, j;
/* 根据c的ascii码在fontdata_8x16中得到点阵数据的首地址 */
unsigned char *dots = &fontdata_8x16[c * 16];
unsigned char data;
int bit;
/* 根据点阵来设置对应象素的颜色 */
for (j = y; j < y+16; j++) //总共需要写16个字节,循环16次
{
//先取出首字节数据,然后取出下一字节数据,
data = *dots++;
//先写最高位
bit = 7;
for (i = x; i < x+8; i++)//先第一行数据,循环8次
/* 点阵字节为1,则画点,否则不画点 */
if (data & (1
bit--;
}
}
}
输出字符串函数
字符串的输出的基础是单个字符的输出。
思路:把需要输出的字符串保存在一个数组中,然后依次读取出这个字符串数组的字符,如果不为空则打印出来,碰到 "nr"就换行并回到下一行的起始位置,继续打印数据。
代码如下:
void fb_print_string(int x, int y, char* str, unsigned int color)
{
int i = 0, j;
while (str[i]) //但str[i] 不为空
{
if (str[i] == 'n')
y = y+16;
else if (str[i] == 'r')
x = 0;
else
{
fb_print_char(x, y, str[i], color); //在指定的位置输出一个字符
[1] [2] [3]