s5pv210——LCD的原理和实战

以下内容源于朱友鹏《物联网大讲堂》课程的学习,以及博客http://www.cnblogs.com/biaohc/p/6286946.html的学习,如有侵权,请告知删除。



s5pv210——LCD的原理和实战_第1张图片

一、S5PV210的LCD控制器

1、FIMD结构框图(P1162)

s5pv210——LCD的原理和实战_第2张图片

  • 210的LCD控制器叫FIMD;
  • FIMD是210内部和图像处理相关的一些部件,在摄像头等和图像处理有关的部分都可以有关联。
  • FIMD在内部与AHB总线等相连接,在外部提供RGB接口、I80接口、YUV接口与外部相连接,我们实际使用的是RGB接口。

2、虚拟屏幕叠加(数据手册P1194)

(1)虚拟屏幕

  • 屏幕显示的场景,实际是很多个屏幕显示叠加在一起的效果(譬如新闻图像、电视台台标、下方飘动的字幕新闻);
  • S5PV210的LCD控制器中有5个虚拟屏幕Window0到Window4,虚拟屏幕不存在于真实而存在于内存中。
  • LCD显示时,显示对应的内存中的显存区域的数值。
  • 虚拟屏幕其实就是一个内存中的显存区域,有几个显存区域就有几个虚拟屏幕。
  • 这些虚拟屏幕都被映射到一个真实的显示屏上面,实际效果是这几个虚拟屏幕的显示内容的叠加。
  • 叠加时,上面一层会覆盖下面一层,因此要注意谁在前谁在后,通过相关寄存器进行设置。
(2)使用虚拟屏幕,而非整个LCD只使用一个显存

  • 第一,可以保证不污染源图像,方便程序处理;
  • 第二,可以减少屏幕刷新,提高显示效率,减少CPU工作量。

3、虚拟显示(数据手册P1206)

s5pv210——LCD的原理和实战_第3张图片

(1)解决如何实现在小分辨率的屏幕上(真实地,即不允许修改分辨路)显示大分辨率的图像?

(2)按照以前的思想,当需要在屏幕上显示不同图像时,需要对整个显存区域进行刷新。

  • 即使是显示同一幅图像的不同区域(比如只需要屏幕显示移动一点点),整个屏幕对应的显存空间也需要整个重新刷新,工作量和完全重新显示一幅图像是一样的。
  • 这使得CPU刷新屏幕的工作量很大,效率很低。

(3)如何能够在显示一个大图片的不同区域时,让CPU刷新屏幕工作量减少?

  • 方法是,虚拟显示。
  • 在内存中建立显示缓存时,建立一个很大的区域,直接将大图像全部一次性加载入显示缓存区(这样就可以只加载一次);
  • LCD选择其中的一部分区域作为有效的显示区域;
  • 通过移动有效显示区域就可以显示大图像的不同区域了。

4、主要寄存器简介

(1)DISPAY_CONTROL寄存器:设置为10或11,即RGB模式可行。

s5pv210——LCD的原理和实战_第4张图片

(2)VIDCON0寄存器(Video Main Control 0 Register)

s5pv210——LCD的原理和实战_第5张图片

s5pv210——LCD的原理和实战_第6张图片

s5pv210——LCD的原理和实战_第7张图片

  • bit0,bit1:为使能控制信号都使能;
  • bit2:选择时钟源,选HCLK,连的是HCLC_DSYS 为166MHz;
  • bit4:开启分频;
  • bit13-6:设置时钟大小,时钟频率要小于控制器的最大时钟,也要小于LCD驱动器的最大时钟。
  • bit18:设置RGB数据传输为并行还是串行,因为有24根数据线所以为并行;
  • bit28-26:选择为RGB模式。
(3)VIDCON1寄存器(Video Main Control 1 Register)

s5pv210——LCD的原理和实战_第8张图片

  • bit5,bit6:设置HSYNC和VSYNC的极性,如果LCD的高低电平脉冲是相同的话,则Normal,如果极性相反则Invert。
(3)VIDTCONn(n=0,1,2,3)寄存器:设置时序,根据LCD数据手册中的时序来设置。

s5pv210——LCD的原理和实战_第9张图片

s5pv210——LCD的原理和实战_第10张图片

s5pv210——LCD的原理和实战_第11张图片

(4)WINCONn(n=0~4)寄存器

s5pv210——LCD的原理和实战_第12张图片s5pv210——LCD的原理和实战_第13张图片s5pv210——LCD的原理和实战_第14张图片s5pv210——LCD的原理和实战_第15张图片s5pv210——LCD的原理和实战_第16张图片

  • bit0:使能window0 ;
  • bit5-2:选择RGB888模式;
  • bit15:设置输出顺序为 red green blue还是 blue green red,设置为1时BGR,设置为0时RGB。
(5)VIDOSD0A、VIDOSD0B两个寄存器

s5pv210——LCD的原理和实战_第17张图片s5pv210——LCD的原理和实战_第18张图片

  • 用来设置内存中window0的大小;
  • 比如设置为LCD屏幕的尺寸,即左上坐标为(0,0),右下坐标为(1023,767)。

(6)VIDOSD0A寄存器

s5pv210——LCD的原理和实战_第19张图片

  • 也是设置内存中window0的大小,比如设置为LCD屏幕的尺寸=1024*768。
(7)VIDW0xADD0Bx寄存器、VIDW0xADD1Bx寄存器


  • VIDW0xADD0Bx寄存器,设置内存中window0的起始地址;
  • VIDW0xADD1Bx寄存器,设置内存中window0的结束地址。
(8)SHODOWCON寄存器:设置虚拟windows显示,以下位可以分别设置哪个windows显示

s5pv210——LCD的原理和实战_第20张图片

5、底板、核心板解读

s5pv210——LCD的原理和实战_第21张图片

s5pv210——LCD的原理和实战_第22张图片


二、代码实战

#define GPF0CON			(*(volatile unsigned long *)0xE0200120)
#define GPF1CON			(*(volatile unsigned long *)0xE0200140)
#define GPF2CON			(*(volatile unsigned long *)0xE0200160)
#define GPF3CON			(*(volatile unsigned long *)0xE0200180)

#define GPD0CON			(*(volatile unsigned long *)0xE02000A0)
#define GPD0DAT			(*(volatile unsigned long *)0xE02000A4)

#define CLK_SRC1		(*(volatile unsigned long *)0xe0100204)
#define CLK_DIV1		(*(volatile unsigned long *)0xe0100304)
#define DISPLAY_CONTROL	(*(volatile unsigned long *)0xe0107008)

#define VIDCON0			(*(volatile unsigned long *)0xF8000000)
#define VIDCON1			(*(volatile unsigned long *)0xF8000004)
#define VIDTCON2		(*(volatile unsigned long *)0xF8000018)
#define WINCON0 		(*(volatile unsigned long *)0xF8000020)
#define WINCON2 		(*(volatile unsigned long *)0xF8000028)
#define SHADOWCON 		(*(volatile unsigned long *)0xF8000034)
#define VIDOSD0A 		(*(volatile unsigned long *)0xF8000040)
#define VIDOSD0B 		(*(volatile unsigned long *)0xF8000044)
#define VIDOSD0C 		(*(volatile unsigned long *)0xF8000048)

#define VIDW00ADD0B0 	(*(volatile unsigned long *)0xF80000A0)
#define VIDW00ADD1B0 	(*(volatile unsigned long *)0xF80000D0)

#define VIDTCON0 		(*(volatile unsigned long *)0xF8000010)
#define VIDTCON1 		(*(volatile unsigned long *)0xF8000014)

#define HSPW 			(0)
#define HBPD			(40 - 1)
#define HFPD 			(5 - 1)
#define VSPW			(0)
#define VBPD 			(8 - 1)
#define VFPD 			(8 - 1)

// FB地址
#define FB_ADDR			(0x23000000)   //这个是任意的,只要对齐就好
#define ROW				(480)
#define COL				(800)
#define HOZVAL			(COL-1)  
#define LINEVAL			(ROW-1)

// 初始化LCD
void lcd_init(void)
{
	// 配置引脚用于LCD功能
	/*
	*LED、LCD、蜂鸣等,都是与gpio口有关的
	*因此,涉及这些器件的,都需要初始化gpio口
	*而初始化gpio,需要用到相关的配置寄存器,比如GPIOCON寄存器控制引脚的模式
	*而至于配置哪个GPIO,需要看原理图。
	*这里从原理图知LCD接到gpf0123,所以要到相应的配置寄存器中进行配置
	*/
	GPF0CON = 0x22222222;
	GPF1CON = 0x22222222;
	GPF2CON = 0x22222222;
	GPF3CON = 0x22222222;

	// 打开背光	GPD0_0(PWMTOUT0)
	/*
	*由原理图可知,当PWMTOUT0接低电平时,正5V电压加到背光上,也就打开了背光灯
	*而查阅PWMTOUT0可知,它就是GPD0_0这个引脚
	*把GPD0_0这个引脚配置为输出模式,通过GPD0CON寄存器配置
	*然后配置GPD0_0这个引脚输出值为0,通过GPD0DAT寄存器配置
	*/
	GPD0CON &= ~(0xf<<0);
	GPD0CON |= (1<<0);			// output mode
	GPD0DAT &= ~(1<<0);			// output 0 to enable backlight

	// 10: RGB=FIMD I80=FIMD ITU=FIMD
	//由上面寄存器的分析可知,这里可以设置为10
	DISPLAY_CONTROL = 2<<0;

	
	// bit[26~28]:使用RGB接口
	// bit[18]:RGB 并行
	// bit[2]:选择时钟源为HCLK_DSYS=166MHz
	VIDCON0 &= ~( (3<<26)|(1<<18)|(1<<2) );

	// bit[1]:使能lcd控制器
	// bit[0]:当前帧结束后使能lcd控制器
	VIDCON0 |= ( (1<<0)|(1<<1) );

	
	// bit[4]:选择需要分频
	// bit[6~13]:分频系数为15,即VCLK = 166M/(14+1) = 11M,那么bit[6~13]=14;
	VIDCON0 |= 14<<6 | 1<<4;

    //屏幕有的是高电平脉冲提示开始,有的是低脉冲提示开始
	// H43-HSD043I9W1.pdf(p13) 时序图:VSYNC和HSYNC都是低脉冲提示开始
	// s5pv210芯片手册(p1207) 时序图:VSYNC和HSYNC都是高脉冲提示开始,所以需要反转
	VIDCON1 |= 1<<5 | 1<<6;

	// 设置时序
	VIDTCON0 = VBPD<<16 | VFPD<<8 | VSPW<<0;
	VIDTCON1 = HBPD<<16 | HFPD<<8 | HSPW<<0;
	
	// 设置长宽(这里设置的是物理尺寸)
	VIDTCON2 = (LINEVAL << 11) | (HOZVAL << 0);

	// 设置windows0
	// bit[0]:使能
	// bit[2~5]:24bpp
	WINCON0 |= 1<<0;
	WINCON0 &= ~(0xf << 2);//先清零
	WINCON0 |= (0xB<<2) | (1<<15);//再设置

#define LeftTopX     0
#define LeftTopY     0
#define RightBotX   479
#define RightBotY   271

	// 设置windows1的上下左右
	//这里设置的是显存空间的大小,可以比屏幕大。
	//这里设置和物理屏幕一样大
	VIDOSD0A = (LeftTopX<<11) | (LeftTopY << 0);
	VIDOSD0B = (RightBotX<<11) | (RightBotY << 0);
	VIDOSD0C = (LINEVAL + 1) * (HOZVAL + 1);


	// 设置fb的地址
	VIDW00ADD0B0 = FB_ADDR;//这个可以任选的,但必须对齐(比如按照1M对齐)
	//每个像素点3个字节(24bit的bpp)就可以了的,但是为了对齐,用了4字节。
	VIDW00ADD1B0 = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);
	//上述两句表明了一个屏幕占用的空间大小

	// 使能channel 0传输数据
	SHADOWCON = 0x1;
}


// 描点
void lcd_draw_pixel(int row, int col, int color)
{
	unsigned long * pixel = (unsigned long  *)FB_ADDR;

	*(pixel + row * COL + col) = color;
	
	//假设像素点坐标为(row,col),则该像素点在内存中的位置计算方法如下
	//有一个基地址,pixel(这里是一个指针,注意它是指针,加1会加上4个字节)
	//偏移量=每行多少个像素*有多少行*每个像素占用多少个字节+col个像素*每个像素多少字节
    //             COL      * row    *    4                 +   col * 4
    //那么所处的位置应该是(int)pixel+COL*row*4 + col * 4
    //或者简单地写成 pixel + row * COL + col	

}

// 清屏
void lcd_clear_screen(int color)
{
	int i, j;

	for (i = 0; i < ROW; i++)
		for (j = 0; j < COL; j++)
			lcd_draw_pixel(i, j, color);

}

// 划横线
void lcd_draw_hline(int row, int col1, int col2, int color)
{
	int j;

	// 描第row行,第j列
	for (j = col1; j <= col2; j++)
		lcd_draw_pixel(row, j, color);

}

// 划竖线
void lcd_draw_vline(int col, int row1, int row2, int color)
{
	int i;
	// 描第i行,第col列
	for (i = row1; i <= row2; i++)
		lcd_draw_pixel(i, col, color);

}

// 划十字
void lcd_draw_cross(int row, int col, int halflen, int color)
{
	lcd_draw_hline(row, col-halflen, col+halflen, color);
	lcd_draw_vline(col, row-halflen, row+halflen, color);
}


三、显示内容

1、首先必须清屏;

2、显示点、线;

3、显示英文字符;

(1)每个英文字符,占用16*8个像素,如果单色显示,则每个像素占1bit,则每个英文字符是16Byte。
(2)字模。要想得到显示,必须先取“模”(知道该字符的字模),然后填充即可。
(3)代码示例
// 写字
// 写字的左上角坐标(x, y),字的颜色是color,字的字模信息存储在data中
static void show_8_16(unsigned int x, unsigned int y, unsigned int color, unsigned char *data)  
{  
// count记录当前正在绘制的像素的次序
    int i, j, count = 0;  
	  
    for (j=y; j<(y+16); j++)  
    {  
        for (i=x; i<(x+8); i++)  
        {  
            if (i= XSIZE)
		{
			x -= XSIZE;			// 回车
			y += 16;			// 换行
		}
    }  
}

4、显示中文字符;

5、显示图像

(1)之前都是单色的,每个像素点用一个bit(单色显示时),像素点不是0就是1;而图像是彩色的,每个像素点占用4字节(RGB888时),像素点情况很多。
(2)需要取模。使用软件image2LCD。知道RGB顺序的含义,知道生成的步骤即可(一般设置输出的和屏幕实际大小一样)。
(3)代码

// 画800×480的图,图像数据存储在pData所指向的数组中
void lcd_draw_picture(const unsigned char *pData)
{
	u32 x, y, color, p = 0;
	
	for (y=0; y<480; y++)
	{
		for (x=0; x<800; x++)
		{
			// 在这里将坐标点(x, y)的那个像素填充上相应的颜色值即可
			color = (pData[p+0] << 0) | (pData[p+1] << 8) | (pData[p+2] << 16);
			lcd_draw_pixel(x, y, color);
			p += 3;//注意这里的p不是指针,而是int数。由于RGB888,三个一组,所以下一个像素是p+3
		}
	}
}
(4)这样处理得到的bin大于16KB,所以需要分成BL1,BL2部分。
  • 引用chapter10的分割方法,详见chapter14\6.LCD目录下的内容。
  • 最后使用烧录脚本(./write2sd),不能使用windows里的烧录工具。
write2sd是一个脚本,内容如下:

#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45

(5)补充
  • bmp格式是原始图像,没有经过压缩?因此可以直接使用image2LCD软件解释成数据。
  • jpg、png、gif等压缩的不可以直接用image2LCD解释。需要先用对应的解压缩库对其解压缩,才能使用image2LCD软件?






你可能感兴趣的:(ARM裸机)