ARM LCD 编程实战

时间:2022-12-28 01:20:39

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

参考代码 lcd_init 函数详解

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


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

ARM LCD 编程实战


ARM LCD 编程实战


2. 打开 LCD 屏幕的背光.

ARM LCD 编程实战


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

ARM LCD 编程实战


ARM LCD 编程实战


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

ARM LCD 编程实战


ARM LCD 编程实战


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

ARM LCD 编程实战


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

ARM LCD 编程实战


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

ARM LCD 编程实战


ARM LCD 编程实战


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

ARM LCD 编程实战


ARM LCD 编程实战


9. 使能(RGB888)。

ARM LCD 编程实战


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

ARM LCD 编程实战


11. 设置 frame_buffer 的地址。

ARM LCD 编程实战


12. 使能 channel 0 传输数据。

ARM LCD 编程实战


二、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) 斜线

ARM LCD 编程实战


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

ARM LCD 编程实战

ARM LCD 编程实战

ARM LCD 编程实战


五、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。

ARM LCD 编程实战


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;
		}
	}
}

ARM LCD 编程实战


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

1、RGB的像素顺序调整

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

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

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

ARM LCD 编程实战


2、实验现象

ARM LCD 编程实战


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)


源自朱有鹏老师.