LCD编程

LCD硬件原理

LCD基本的显示原理是由一个个像素组成的,而像素可以想象屏幕后面有一个电子枪,一边移动一边向像素发出红绿蓝这些颜色。
在这里插入图片描述
由此可以提出几个问题:
1.如何移动?
答:有一挑CLK线连接到LCD,每来一个CLK,移动一个像素。
2.颜色怎么确定?
答:有很多条线连接LCD,RGB三组线,由RGB确定颜色。
3.电子枪如何得知应该跳到下一行?
HSYNC(水平同步信号),接收到HSYNC脉冲时,跳到下一行。
4.电子枪如何得知应该跳到原点?
VSYNC(垂直同步信号),接收到这VSYNC这个脉冲时就会跳到最上面。
5.RGB线上的数据从和而来?
从显存中来(从内存中划分出的FrameBuffer),会有对应的数据存在FrameBuffer,LCD控制器会读取这些值通过RGB三组线传给电子枪,电子枪再打到各个像素上去。
在这里插入图片描述
6.每个像素在FrameBuffer中占据多少位?
这个值叫做BPP(bit per pixel),硬件上的LCD的BPP是确定的。假设LCD是24位,但是我们不一定需要全部的线都接,我们的arm板接的是16位的

这些都是芯片中的LCD控制器去发出这些信号的,下图是硬件接口原理图。

在这里插入图片描述

S3C2440_LCD控制器

LCD的功能:

1.取数据:把framebuffer的地址告诉LCD控制器,Bpp,分辨率。
2.发数据:把时序告诉LCD控制器、并且设置引脚的极性。

LCDCDMA会从framebuffer中取数据,然后我们需要设置黄色框中的时序。
在这里插入图片描述

设置RGB的格式

16位

可以通过设置BSWP=0,HWSWP=1来设置为排放方式。
在这里插入图片描述
对于16位的数据有两种格式:一种是RGB565,一种是RGB555在这里插入图片描述

8位

可以通过设置BSWP=0,HWSWP=1来设置为下面那一种排放方式。
在这里插入图片描述

我们需要根据外接的LCD手册来设置这些时序。

调色板

问题:本应该在FB中用16bit表示1像素,但是我们想节省空间,只用8bit,如何做呢?

这里就用到了调色板,我们LCD控制器从FB中取的八位的数据后,找到调色板的对应的16位数据,再把这16位的数据发送给我们的LCD,(用8bit做索引)。

调色板就是一块特殊的内存。

框架与准备

后续程序的框架

  • 首先我们的目的是:画线和画圆(geometry.c)、写字(font.c),其实最终是去画点,这一部分我们可以放在framebuffer.c文件中去实现,这一层面是纯软件的。
  • 我们需要根据不同的时间参数去设置不同的时序。lcd_4.3.c
  • 我们还需要设置LCD控制器。s3c2440_lcd_controller.c
  • 最后是操作硬件LCD。
  • lcd_test.c提供菜单

我们还需要用面向对象的思想进行编程,比如说我们如果要从4.3的显示屏换成3.5的显示屏,这时候该怎么做呢?我们需要抽象出他们的共同点。我们可以定义lcd的结构体:

struct lcd_opr lcd_3_5_opr{
	.init = lcd_3_5_init;
}

struct lcd_opr lcd_4_3_opr{
	.init = lcd_4_3_init;
}

然后我们可以定义一个lcd_opr的指针,让这个指针指向我们想使用的结构体。同理,lcd控制器也可以抽象出结构体方便给不同芯片的LCD控制器使用。

我们的使用者不需要关注到lcd_3_5.c等文件,它只需要关注lcd.c这个文件即可。

准备一个支持nor/nand启动的程序

抽象出重要结构体

创建一个lcd文件夹专门放lcd的代码。并在lcd文件夹中创建如下文件夹:lcd_test.c、geometry.c、font.c、framebuffer.c、lcd.c、lcd4.3.c、lcd_controller.c、s3c2440_lcd_controller.c

LCD参数(lcd.h)

我们创建lcd.h来构造一个结构体lcd_params表示这些参数。

typedef struct lcd_params{
    char *name;
	/* 引脚极性 */
	pins_polarity pins_pol;
	/* 时序 */
	time_sequence seq;
	/* 分辨率,bpp */
	int xres;
	int yres;
	int bpp;
	/* frambuffer的地址 */
	unsigned int fb_base;
}lcd_params,*p_lcd_params;

创建一个结构体来表示引脚极性:

enum{
	NORMAL = 0;
	INVERT = 1;
}

/**
* 0:正常极性
* 1:反转极性
*/
typedef struct pins_polarity{
	int de; /* normal :高电平时可以传输数据 */
	int pwren; /* normal :高电平有效 */
	int vckl; /* normal :在下降沿获取数据 */
	int rgb; /* normal : 高电平表示1*/
	int hsync;/* normal : 高脉冲 */
	int vsync;/* normal : 高脉冲 */
}pins_polarity,*p_pins_polarity;

创建一个结构体表示时序:

typedef struct time_sequence{
	/* 垂直方向 */
	int tvp; //vysnc脉冲宽度
	int tvb; //上边黑框
	int tvf; //下边黑框
	
	/* 水平方向 */
	int thp; //hysnc脉冲宽度 
	int thb; //左边黑框
	int thf; //右边黑框
	int vclk;
}time_sequence,*p_time_sequence;

LCD控制器(lcd_controller.c lcd_controller.h)

lcd_controller.c的作用是:

  • 向上:接收不同的LCD的参数
  • 向下:使用这些参数设置对应的LCD控制器

lcd_controller.h中定义lcd_controller函数:

typedef struct lcd_controller{
	char *name;
	void (*init)(p_lcd_params plcdparams);
	void (*enable)(void);
	void (*disable)(void);
}lcd_controller, *p_lcd_controller;

lcd_controller.c中添加如下函数:

void lcd_controller_init(p_lcd_params plcdparams)
{
	lcd_controller.init(plcdparams);
}

然后在s3c2440_lcd_controller.c中添加下面内容:

#define HCLK 100

void jz2440_lcd_pin_init(void)
{
	/* 初始化引脚 : 背光引脚 */
	GPBCON &= ~0x3;
	GPBCON |= 0x01;

	/* LCD专用引脚 */
	GPCCON = 0xaaaaaaaa;
	GPDCON = 0xaaaaaaaa;

	/* PWREN */
	GPGCON |= (3<<8);
}


struct lcd_controller s3c2440_lcd_controller = {
	  .name = "s3c2440";
	 .init = s3c2440_lcd_controller_init,
	 .enable = s3c2440_lcd_controller_enable,
	 .disable = s3c2440_lcd_controller_disable,
};

void s3c2440_lcd_controller_init(p_lcd_params plcdparams)
{
	int pixelplace;
	unsigned int addr;
	
	jz2440_lcd_pin_init();
	/* [17:8]: clkval, vclk = HCLK / [(CLKVAL+1) x 2]
	 *                   9   = 100M /[(CLKVAL+1) x 2], clkval = 4.5 = 5
	 *                 CLKVAL = 100/vclk/2-1
	 * [6:5]: 0b11, tft lcd
	 * [4:1]: bpp mode
	 * [0]  : LCD video output and the logic enable/disable
	 */
	 int clkval = (double)HCLK/plcdparams->time_seq.vclk/2-1+0.5;//四色五入向上去整
	 int bppmode = plcdparams->bpp == 8  ? 0xb :\
				  plcdparams->bpp == 16 ? 0xc :\
				  0xd;  /* 0xd: 24bpp */
	LCDCON1 = (clkval<<8) | (3<<5) | (bppmode<<1) ;
	
	/* [31:24] : VBPD    = tvb - 1
	 * [23:14] : LINEVAL = line - 1
	 * [13:6]  : VFPD    = tvf - 1
	 * [5:0]   : VSPW    = tvp - 1
	 */
	LCDCON2 = 	((plcdparams->time_seq.tvb - 1)<<24) | \
	            ((plcdparams->yres - 1)<<14)         | \
				((plcdparams->time_seq.tvf - 1)<<6)  | \
				((plcdparams->time_seq.tvp - 1)<<0);
				
	/* [25:19] : HBPD	 = thb - 1
	 * [18:8]  : HOZVAL  = 列 - 1
	 * [7:0]   : HFPD	 = thf - 1
	 */
	LCDCON3 =	((plcdparams->time_seq.thb - 1)<<19) | \
				((plcdparams->xres - 1)<<8)		      | \
				((plcdparams->time_seq.thf - 1)<<0);
	/* 
	 * [7:0]   : HSPW	 = thp - 1
	 */
	LCDCON4 =	((plcdparams->time_seq.thp - 1)<<0);

    /* 用来设置引脚极性, 设置16bpp, 设置内存中象素存放的格式
     * [12] : BPP24BL
	 * [11] : FRM565, 1-565
	 * [10] : INVVCLK, 0 = The video data is fetched at VCLK falling edge
	 * [9]  : HSYNC是否反转
	 * [8]  : VSYNC是否反转
	 * [7]  : INVVD, rgb是否反转
	 * [6]  : INVVDEN
	 * [5]  : INVPWREN
	 * [4]  : INVLEND
	 * [3]  : PWREN, LCD_PWREN output signal enable/disable
	 * [2]  : ENLEND
	 * [1]  : BSWP
	 * [0]  : HWSWP
	 */
	pixelplace = plcdparams->bpp == 24 ? (0) : |\
	             plcdparams->bpp == 16 ? (1) : |\
	             (1<<1);  /* 8bpp */
	LCDCON5 = (plcdparams->pins_pol.vclk<<10) |\
	          (plcdparams->pins_pol.rgb<<7)   |\
	          (plcdparams->pins_pol.hsync<<9) |\
	          (plcdparams->pins_pol.vsync<<8) |\
 			  (plcdparams->pins_pol.de<<6)    |\
			  (plcdparams->pins_pol.pwren<<5) |\
			  (1<<11) | pixelplace;
			  
	/* framebuffer地址 */
	/*
	 * [29:21] : LCDBANK, A[30:22] of fb
	 * [20:0]  : LCDBASEU, A[21:1] of fb
	 */
	addr = plcdparams->fb_base & ~(1<<31);
	LCDSADDR1 = (addr >> 1);

	/* 
	 * [20:0] : LCDBASEL, A[21:1] of end addr
	 */
	addr = plcdparams->fb_base + plcdparams->xres*plcdparams->yres*plcdparams->bpp/8;
	addr >>=1;
	addr &= 0x1fffff;
	LCDSADDR2 = addr;//
}

void s3c2440_lcd_controller_enable(void)
{
	/* 背光引脚 : GPB0 */
	GPBDAT |= (1<<0);
	
	/* pwren    : 给LCD提供AVDD  */
	LCDCON5 |= (1<<3);
	
	/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
	LCDCON1 |= (1<<0);
}

void s3c2440_lcd_controller_disable(void)
{
	/* 背光引脚 : GPB0 */
	GPBDAT &= ~(1<<0);

	/* pwren	: 给LCD提供AVDD  */
	LCDCON5 &= ~(1<<3);

	/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
	LCDCON1 &= ~(1<<0);
}

关于时序,对比LCD控制器时序与LCD时序,可以得出下列的结果:

VBPD    = tvb - 1
LINEVAL = line - 1
VFPD    = tvf - 1
VSPW    = tvp - 1

S3C2440
LCD

LCD设置(lcd_4.3.c lcd_controller.c)

lcd_4.3.c参数设置


#include "lcd.h"

#define LCD_FB_BASE 0x33c00000

lcd_params lcd_4_3_params = {
	.name = "lcd_4.3",
	.pins_pol = {
		.de    = NORMAL,	/* normal: 高电平时可以传输数据 */
		.pwren = NORMAL,    /* normal: 高电平有效 */
		.vclk  = NORMAL,	/* normal: 在下降沿获取数据 */
		.rgb   = NORMAL,	/* normal: 高电平表示1 */
		.hsync = INVERT,    /* normal: 高脉冲 */
		.vsync = INVERT, 	/* normal: 高脉冲 */
	},
	.time_seq = {
		/* 垂直方向 */
		.tvp=	10, /* vysnc脉冲宽度 */
		.tvb=	2,  /* 上边黑框, Vertical Back porch */
		.tvf=	2,  /* 下边黑框, Vertical Front porch */

		/* 水平方向 */
		.thp=	41, /* hsync脉冲宽度 */
		.thb=	2,  /* 左边黑框, Horizontal Back porch */
		.thf=	2,  /* 右边黑框, Horizontal Front porch */

		.vclk=	9,  /* MHz */
	},
	.xres = 480,
	.yres = 272,
	.bpp  = 8,  /* 16, no 24bpp */
	.fb_base = LCD_FB_BASE,
};


void lcd_4_3_add(void)
{
	register_lcd(&lcd_4_3_params);
}

lcd_controller.c设置

lcd_controller.c向下管理不同的LCD控制器,向上提供统一的结构选择某一款LCD控制器。

我们需要完成以下功能:

  • 用数组保存这些各种LCD控制器
  • 提供register_lcd_controller给下面的代码设置数组
  • 提供select_lcd_controller接口给上面的代码选择某个LCD控制器。

lcd_controller.c中添加下列代码:


#include "lcd_controller.h"

#define LCD_CONTROLLER_NUM 10

static p_lcd_controller p_array_lcd_controller[LCD_CONTROLLER_NUM];
static p_lcd_controller g_p_lcd_controller_selected;

int register_lcd_controller(p_lcd_controller plcdcon)
{
	int i;
	for (i = 0; i < LCD_CONTROLLER_NUM; i++)
	{
		if (!p_array_lcd_controller[i])
		{
			p_array_lcd_controller[i] = plcdcon;
			return i;
		}
	}
	return -1;		
}

int select_lcd_controller(char *name)
{
	int i;
	for (i = 0; i < LCD_CONTROLLER_NUM; i++)
	{
		if (p_array_lcd_controller[i] && !strcmp(p_array_lcd_controller[i]->name, name))
		{
			g_p_lcd_controller_selected = p_array_lcd_controller[i];
			return i;
		}
	}
	return -1;		
}


/* 向上: 接收不同LCD的参数
 * 向下: 使用这些参数设置对应的LCD控制器
 */

int lcd_controller_init(p_lcd_params plcdparams)
{
	/* 调用所选择的LCD控制器的初始化函数 */
	if (g_p_lcd_controller_selected)
	{
		g_p_lcd_controller_selected->init(plcdparams);
		g_p_lcd_controller_selected->init_palette();
		return 0;
	}
	return -1;
}

void lcd_controller_enable(void)
{
	if (g_p_lcd_controller_selected)
	{
		g_p_lcd_controller_selected->enable();
	}
}

void lcd_controller_disable(void)
{
	if (g_p_lcd_controller_selected)
	{
		g_p_lcd_controller_selected->disable();
	}
}


void lcd_contoller_add(void)
{
	s3c2440_lcd_contoller_add();
}


lcd.c设置

同样的,我们需要完成以下功能:

  • 用数组保存各类LCD的参数
  • 提供register_lcd给下面的LCD程序使用,用来设置数组
  • 提供select_lcd接口给上面的代码选择某款LCD。

lcd.c中添加下列代码:

#include "lcd.h"
#include "lcd_controller.h"

#define LCD_NUM 10

static p_lcd_params p_array_lcd[LCD_NUM];
static p_lcd_params g_p_lcd_selected;

int register_lcd(p_lcd_params plcd)
{
	int i;
	for (i = 0; i < LCD_NUM; i++)
	{
		if (!p_array_lcd[i])
		{
			p_array_lcd[i] = plcd;
			return i;
		}
	}
	return -1;		
}

int select_lcd(char *name)
{
	int i;
	for (i = 0; i < LCD_NUM; i++)
	{
		if (p_array_lcd[i] && !strcmp(p_array_lcd[i]->name, name))
		{
			g_p_lcd_selected = p_array_lcd[i];
			return i;
		}
	}
	return -1;		
}

void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp)
{
	*fb_base = g_p_lcd_selected->fb_base;
	*xres = g_p_lcd_selected->xres;
	*yres = g_p_lcd_selected->yres;
	*bpp = g_p_lcd_selected->bpp;
}

void lcd_enable(void)
{
	lcd_controller_enable();
}

void lcd_disable(void)
{
	lcd_controller_disable();
}

int lcd_init(void)
{
	/* 注册LCD */
	lcd_4_3_add();

	/* 注册LCD控制器 */
	lcd_contoller_add();
	
	/* 选择某款LCD */
	select_lcd("lcd_4.3");

	/* 选择某款LCD控制器 */
	select_lcd_controller("s3c2440");

	/* 使用LCD的参数, 初始化LCD控制器 */
	lcd_controller_init(g_p_lcd_selected);
}

简单测试

修改lcd_test.c 的代码

void lcd_test(void)
{
	unsigned int fb_base;
	int xres, yres, bpp;
	int x, y;
	unsigned short *p;
		
	/* 初始化LCD */
	lcd_init();

	/* 使能LCD */
	lcd_enable();

	/* 获得LCD的参数: fb_base, xres, yres, bpp */
	get_lcd_params(&fb_base, &xres, &yres, &bpp);
	
	/* 往framebuffer中写数据 */
	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;
		
	}
}


点线圆

计算某个像素的地址可以用下列公式:

pixel_base = fb_base + (xres * bpp / 8) * y + x * bpp / 8;

bpp / 8表示一个像素所占用的字节数。

实现画点函数其实实现的是往frambuffer中写入数据(需要注意要注意LCD的位数问题)。
framebuffer.c中添加下列函数:


#include "lcd.h"

/* 实现画点 */

/* 获得LCD参数 */
static unsigned int fb_base;
static int xres, yres, bpp;

void fb_get_lcd_params(void)
{
	get_lcd_params(&fb_base, &xres, &yres, &bpp);
}

/* 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));
}


/* color : 32bit, 0x00RRGGBB
 *
 */
void fb_put_pixel(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;
	}
}


画线函数其实是在画点的基础上进行扩展的,可参考网上的现成的函数更改画点的函数即可。


#include "framebuffer.h"

/*
 * http://blog.csdn.net/p1126500468/article/details/50428613
 */

 //-------------画圆函数。参数:圆心,半径,颜色----------	
 // 	   画1/8圆 然后其他7/8对称画  
 // 		 ---------------->X  
 // 		 |(0,0)   0  
 // 		 |	   7	 1	
 // 		 |	  6 	  2  
 // 		 |	   5	 3	
 // 	  (Y)V		  4  
 //  
 // 	 L = x^2 + y^2 - r^2  
 void draw_circle(int x, int y, int r, int color)  
 {	
	 int a, b, num;  
	 a = 0;  
	 b = r;  
	 while(22 * b * b >= r * r) 		 // 1/8圆即可  
	 {	
		 fb_put_pixel(x + a, y - b,color); // 0~1  
		 fb_put_pixel(x - a, y - b,color); // 0~7  
		 fb_put_pixel(x - a, y + b,color); // 4~5  
		 fb_put_pixel(x + a, y + b,color); // 4~3  
   
		 fb_put_pixel(x + b, y + a,color); // 2~3  
		 fb_put_pixel(x + b, y - a,color); // 2~1  
		 fb_put_pixel(x - b, y - a,color); // 6~7  
		 fb_put_pixel(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;  
	 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_put_pixel(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_put_pixel(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_put_pixel(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_put_pixel(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_put_pixel(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_put_pixel(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_put_pixel(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_put_pixel(x1,y1,color);  
					 if(e>0){x1-=1;e-=dy;}	   
					 y1-=1;  
					 e+=dx;  
				 }	
			 }	
		 }	   
	 }	
 }	


显示文字

显示文字的原理:根据字库文件中对应文字的字库信息,每一位代表一个像素(注意至少在本文中是这样子表示的),1代表显示,0代表不显示。

我们在显示字符的时候,需要将字库的信息解析出来,后进行判断是否显示。(支持多种颜色显示的)。

修改font.c的代码如下:

extern const unsigned char fontdata_8x16[];
/* 获得LCD参数 */
static unsigned int fb_base;
static int xres, yres, bpp;

void font_init(void)
{
	get_lcd_params(&fb_base, &xres, &yres, &bpp);
}

/* 根据字母的点阵在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++)
	{
		data = *dots++;
		bit = 7;
		for (i = x; i < x+8; i++)
		{
			/* 根据点阵的某位决定是否描颜色 */
			if (data & (1<<bit))
				fb_put_pixel(i, j, color);
			bit--;
		}
	}
}


/* "abc\n\r123" */
void fb_print_string(int x, int y, char* str, unsigned int color)
{
	int i = 0, j;
	
	while (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);
			x = x+8;
			if (x >= xres) /* 换行 */
			{
				x = 0;
				y = y+16;
			}
		}
		i++;
	}
}

调色板

更改lcd_controller.c下的lcd_controller结构体添加初始化调色板函数:


#ifndef _LCD_CONTROLLER_H
#define _LCD_CONTROLLER_H

#include "lcd.h"

typedef struct lcd_controller {
	char *name;
	void (*init)(p_lcd_params plcdparams);
	void (*enable)(void);
	void (*disable)(void);
	void (*init_palette)(void);
}lcd_controller, *p_lcd_controller;

#endif /* _LCD_CONTROLLER_H */


s3c2440_lcd_controller.c添加下列函数

/* 设置调色板之前, 先关闭lcd_controller */
void s3c2440_lcd_controller_init_palette(void)
{
	volatile unsigned int *palette_base =  (volatile unsigned int *)0x4D000400;
	int i;

	int bit = LCDCON1 & (1<<0);

	/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */
	if (bit)
		LCDCON1 &= ~(1<<0);

	for (i = 0; i < 256; i++)
	{
		/* 低16位 : rgb565 */	
		*palette_base++ = i;
	}

	if (bit)
		LCDCON1 |= (1<<0);
}

并在lcd_controller_init初始化函数中添加对调色板的初始化。

int lcd_controller_init(p_lcd_params plcdparams)
{
	/* 调用所选择的LCD控制器的初始化函数 */
	if (g_p_lcd_controller_selected)
	{
		g_p_lcd_controller_selected->init(plcdparams);
		g_p_lcd_controller_selected->init_palette();
		return 0;
	}
	return -1;
}

8bpp对应的直接是调色板,故在lcd_test.c 修改测试函数:

void lcd_test(void)
{
	...
	/* 往framebuffer中写数据 */
	if (bpp == 8)
	{
		/* 让LCD输出整屏的红色 */

		/* bpp: palette[12] */

		p0 = (unsigned char *)fb_base;
		for (x = 0; x < xres; x++)
			for (y = 0; y < yres; y++)
				*p0++ = 12;

		/* palette[47] */
		p0 = (unsigned char *)fb_base;
		for (x = 0; x < xres; x++)
			for (y = 0; y < yres; y++)
				*p0++ = 47;

		/* palette[88] */
		p0 = (unsigned char *)fb_base;
		for (x = 0; x < xres; x++)
			for (y = 0; y < yres; y++)
				*p0++ = 88;

		/* palette[0] */
		p0 = (unsigned char *)fb_base;
		for (x = 0; x < xres; x++)
			for (y = 0; y < yres; y++)
				*p0++ = 0;
			
	}
	else if (bpp == 16)
	{
		...	
	}
	else if (bpp == 32)
	{
		...
	}

	delay(1000000);
	
	/* 画线 */
	draw_line(0, 0, xres - 1, 0, 0x23ff77);
	draw_line(xres - 1, 0, xres - 1, yres - 1, 0xffff);
	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, 0xff45);
	draw_line(xres - 1, 0, 0, yres - 1, 0xff0780);

	delay(1000000);

	/* 画圆 */
	draw_circle(xres/2, yres/2, yres/4, 0xff);

	/* 输出文字 */
	fb_print_string(10, 10, "www.100ask.net\n\r100ask.taobao.com", 0xff);
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值