一、LCD编程实战1 - LCD 控制器初始化

参考代码 lcd_init 函数详解

(1) 要想 LCD 工作,必须给 LCD 屏幕和显存之间建立一个映射(映射是在 CPU 初始化 LCD 控制器来完成的)。本部分就是在完成这个过程(这也是 LCD 显示的 2 个阶段的第一阶段,第二阶段中我们只需要给显存中丢入相应的数据,LCD 屏幕就会自动显示相应内容)。


1. 配置 GPIO 初始化,复用为 LCD 引脚。

Android工控板可以连硬件_s5pv210


Android工控板可以连硬件_Android工控板可以连硬件_02


2. 打开 LCD 屏幕的背光.

Android工控板可以连硬件_linux_03


3. 显示路径的选择,FIMD 控制器使用 RGB 等接口.

Android工控板可以连硬件_Android工控板可以连硬件_04


Android工控板可以连硬件_s5pv210_05


4. 设置 FIMD 控制器使用 RGB 接口,和设置时钟.

Android工控板可以连硬件_arm开发_06


Android工控板可以连硬件_ARM_07


5. 使能 lcd 控制器,当前帧结束后使能lcd控制器。

Android工控板可以连硬件_Android工控板可以连硬件_08


6. 配置时钟分频,用于 LCD 控制器。

Android工控板可以连硬件_Android工控板可以连硬件_09


7. 选择 HSYNC 和 VSYNC 的时钟极性。

Android工控板可以连硬件_Android工控板可以连硬件_10


Android工控板可以连硬件_ARM_11


8. 设置时序和有效数据。

Android工控板可以连硬件_ARM_12


Android工控板可以连硬件_s5pv210_13


9. 使能(RGB888)。

Android工控板可以连硬件_ARM_14


10. 设置window0的上下左右,设置的是显存空间的大小。

Android工控板可以连硬件_s5pv210_15


11. 设置 frame_buffer 的地址。

Android工控板可以连硬件_Android工控板可以连硬件_16


12. 使能 channel 0 传输数据。

Android工控板可以连硬件_Android工控板可以连硬件_17


二、LCD编程实战2-显示像素&刷背景

(1) 显示像素

(2) 刷背景色

#include "main.h"

#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 			(40)				// 1~40 DCLK
#define HBPD			(10 - 1)			// 46
#define HFPD 			(240 - 1)			// 16 210 354
#define VSPW			(20)				// 1~20 DCLK
#define VBPD 			(10 - 1)			// 23
#define VFPD 			(30 - 1)			// 7 22 147



// FB地址1024*600
#define FB_ADDR			(0x23000000)
#define ROW			(600)//(480)
#define COL			(1024)//(800)
#define HOZVAL			(COL-1)
#define LINEVAL			(ROW-1)

#define XSIZE			COL
#define YSIZE			ROW

u32 *pfb = (u32 *)FB_ADDR;


// 常用颜色定义
#define BLACK	0x000000
#define BLUE	0x0000FF
#define RED	0xFF0000
#define GREEN	0x00FF00
#define WHITE	0xFFFFFF
#define  LightPink       0xFFB6C1        //浅粉红
#define  Pink            0xFFC0CB        //粉红
#define  Crimson         0xDC143C        //猩红
#define  LavenderBlush   0xFFF0F5        //脸红的淡紫色
#define  PaleVioletRed   0xDB7093        //苍白的紫罗兰红色
#define  HotPink         0xFF69B4        //热情的粉红
#define  DeepPink        0xFF1493        //深粉色
#define  MediumVioletRed 0xC71585        //适中的紫罗兰红色
#define  Orchid          0xDA70D6        //兰花的紫色
#define  Thistle         0xD8BFD8        //蓟
#define  Plum            0xDDA0DD        //李子
#define  Violet          0xEE82EE        //紫罗兰
#define  Magenta         0xFF00FF        //洋红
#define  Fuchsia         0xFF00FF        //灯笼海棠(紫红色)
#define  DarkMagenta     0x8B008B        //深洋红色
#define  Purple          0x800080        //紫色
#define  MediumOrchid    0xBA55D3        //适中的兰花紫
#define  DarkViolet      0x9400D3        //深紫罗兰色
#define  DarkOrchid      0x9932CC        //深兰花紫
#define  Indigo          0x4B0082        //靛青
#define  BlueViolet      0x8A2BE2        //深紫罗兰的蓝色
#define  MediumPurple    0x9370DB        //适中的紫色
#define  MediumSlateBlue 0x7B68EE        //适中的板岩暗蓝灰色
#define  SlateBlue       0x6A5ACD        //板岩暗蓝灰色
#define  DarkSlateBlue   0x483D8B        //深板岩暗蓝灰色
#define  Lavender        0xE6E6FA        //薰衣草花的淡紫色
#define  GhostWhite      0xF8F8FF        //幽灵的白色
#define  Blue            0x0000FF        //纯蓝
#define  IndigoBlue      0x0000FF        //靛蓝
#define  MediumBlue      0x0000CD        //适中的蓝色
#define  MidnightBlue    0x191970        //午夜的蓝色
#define  DarkBlue        0x00008B        //深蓝色
#define  Navy            0x000080        //海军蓝,深靛蓝
#define  RoyalBlue       0x4169E1        //皇家蓝
#define  CornflowerBlue  0x6495ED        //矢车菊的蓝色
#define  LightSteelBlue  0xB0C4DE        //淡钢蓝
#define  LightSlateGray  0x778899        //浅石板灰
#define  SlateGray       0x708090        //石板灰
#define  DodgerBlue      0x1E90FF        //道奇蓝
#define  AliceBlue       0xF0F8FF        //爱丽丝蓝
#define  SteelBlue       0x4682B4        //钢蓝


// 初始化LCD
static void lcd_init(void)
{
	// 配置引脚用于LCD功能
	GPF0CON = 0x22222222;
	GPF1CON = 0x22222222;
	GPF2CON = 0x22222222;
	GPF3CON = 0x22222222;

	// 打开背光	GPD0_0(PWMTOUT0)
	GPD0CON &= ~(0xf<<0);
	GPD0CON |= (1<<0);			// output mode
	GPD0DAT &= ~(1<<0);			// output 0 to enable backlight

	// 10: RGB=FIMD I80=FIMD ITU=FIMD
	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[6]:选择需要分频
	// bit[6~13]:分频系数为5,即VCLK = 166M/(4+1) = 33M
	VIDCON0 |= 4<<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);

	// 设置window0
	// bit[0]:使能
	// bit[2~5]:24bpp(RGB888)
	WINCON0 |= 1<<0;
	WINCON0 &= ~(0xf << 2);
	WINCON0 |= (0xB<<2) | (1<<15);

#define LeftTopX     0
#define LeftTopY     0
#define RightBotX   1023//799
#define RightBotY   599//479

	// 设置window0的上下左右
	// 设置的是显存空间的大小
	VIDOSD0A = (LeftTopX<<11) | (LeftTopY << 0);
	VIDOSD0B = (RightBotX<<11) | (RightBotY << 0);
	VIDOSD0C = (LINEVAL + 1) * (HOZVAL + 1);


	// 设置fb的地址
	VIDW00ADD0B0 = FB_ADDR;
	VIDW00ADD1B0 = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);

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

// 在像素点(x, y)处填充为color颜色
static inline void lcd_draw_pixel(u32 x, u32 y, u32 color)
{
	*(pfb + COL * y + x) = color;
}

// 把整个屏幕全部填充成一个颜色color
static void lcd_draw_background(u32 color)
{
	u32 i, j;
	
	for (j=0; j<ROW; j++)
	{
		for (i=0; i<COL; i++)
		{
			lcd_draw_pixel(i, j, color);
		}
	}
}

static void delay(void)
{
	volatile u32 i, j;
	for (i=0; i<4000; i++)
		for (j=0; j<100; j++);
}


void lcd_test(void)
{
	lcd_init();
	

    // 测试绘制背景色,成功
	while (1)
	{
		lcd_draw_background(MidnightBlue);
                delay();

                lcd_draw_background(DarkViolet);
                delay();

                lcd_draw_background(Fuchsia);
		delay();
	}

}

三、LCD编程实战3 - 横线竖线斜线&画圆

(1) 画横线&竖线

(2) 斜线

Android工控板可以连硬件_Android工控板可以连硬件_18


四、LCD编程实战 4 -写英文中文字符

Android工控板可以连硬件_s5pv210_19

Android工控板可以连硬件_arm开发_20

Android工控板可以连硬件_ARM_21


五、LCD编程实战 5 -画图

1、图片显示分析

(1) 图像是彩色的,而之前的文字、图形都是单色的。之前的图形文字绘制函数都有个 color 参数,就是传给显存,告诉它这个像素的显示颜色。

(2) 一副分辨率是 1024 × 600,BPP 是 24 的图片,实际上就是 1024 × 600 × 3 字节的数据。将来写代码将图片显示到 LCD 中时,图片将会以 unsigned char pic_data[1024 × 600 × 3]的形式出现。


2、Image2LCD 使用简介

(1) 如何由一副图片得到它对应的数据的数组?要用取模工具,如 Image2LCD。

Android工控板可以连硬件_Android工控板可以连硬件_22


3、画图函数的编写

// 画 1024 × 600 的图,图像数据存储在 pData 所指向的数组中
void lcd_draw_picture(const unsigned char *pData)
{
	u32 x, y, color, p = 0;

	for (y=0; y < 600 ; y++)
	{
		for (x=0; x < 1024 ; x++)
		{
			// 在这里将坐标点(x, y)的那个像素填充上相应的颜色值即可
			color = (pData[p+0] << 0) | (pData[p+1] << 8) | (pData[p+2] << 16);
			lcd_draw_pixel(x, y, color);
			p += 3;
		}
	}
}

Android工控板可以连硬件_ARM_23


六、画图函数的显示效果测试

1、RGB的像素顺序调整

(1) 图片中红色的变成了蓝色、蓝色的变成了红色,这就说明 RGB 顺序反了。

(2) 解决方案一:重新使用 Image2LCD 来取模,取模时 RGB 顺序对调。

(3) 解决方案二:不重新取模,改代码,在 color 形成的时候调换 RGB 的顺序。

Android工控板可以连硬件_Android工控板可以连硬件_24


2、实验现象

Android工控板可以连硬件_linux_25


3、小分辨率图片显示

(1) 本质上大小分辨率的图像显示都是一样的,都是:在图像的每个像素点对应的显存中,填入相应的颜色值即可。


七、未完成事项展望

1、RGB565 和 RGB888

(1) 单片机等性能和资源有限的平台会用 RGB565,嵌入式平台习惯用 RGB888.

(2) 有时候手头只有 RGB888 的颜色值,但是显示部分却只接受 RGB565 的(有时候反过来),这时候就需要在2种颜色之间做转换。


2、不同分辨率、不同起点坐标下的显示

(1) 屏幕分辨率和图像分辨率不同时的显示。我们现在写的代码都是没有考虑的。

(2) 图像起点坐标(左上角坐标)不一定在屏幕左上角,也可能在屏幕的任何其他区域,这种情况下画图的函数要做处理来支持。

(3) 分辨率不同而且起始坐标不同下,这两个问题组合起来也对画图函数提出了更高要求。


3、bmp 图片格式解析及显示

(1) 我们当前的图像数据是用 Image2LCD 工具转过来的。实践中是不可能手工用工具来转的(你想一下 Image2LCD 也是一个软件,既然它能转,我们也应该可以用软件来转)

(2) bmp 格式的图片本身遵照一定的格式来存储的,我们只需按照 bmp 的解析格式来读取文件就可得到这幅图片的图像数据的二进制,直接拿去显示。(这就是图片文件的本质)


4、jgp、png、gif等压缩图片的解码和显示

(1) 对于以上压缩格式的图片是不能直接解析的,必须用相应的库来解压缩图片之后才能拿来显示。

(2) 不同的格式的图片其压缩算法不同,解压缩算法自然也不同。开源社区中每种压缩格式都有个对应的库,库里提供了这种格式的压缩和解压缩函数,我们只需要移植这个库并且调用它来压缩或解压缩即可。


5、图片缩小和放大显示

(1) 图片数据源本来是 400×200 的,但是显示时我希望显示成 100×50 的,这时候就要显示函数后台对源图像进行先缩小,然后再显示。这时候就需要一个缩小算法。(譬如最简单的算法就是抽点采样)

(2) 图片数据源是100×50 的,但是显示时希望全屏显示(800×480),这时候就需要对源图像进行放大然后再显示(当然了显示出来图像清晰度肯定比较低),这时候就需要一种放大算法(譬如插点方式)


6、显示动画

(1) 动态刷屏形成动画。

(2) 立体3D图像显示(伪3D)


源自朱有鹏老师.