一、LCD编程实战1 - LCD 控制器初始化
参考代码 lcd_init 函数详解
(1) 要想 LCD 工作,必须给 LCD 屏幕和显存之间建立一个映射(映射是在 CPU 初始化 LCD 控制器来完成的)。本部分就是在完成这个过程(这也是 LCD 显示的 2 个阶段的第一阶段,第二阶段中我们只需要给显存中丢入相应的数据,LCD 屏幕就会自动显示相应内容)。
1. 配置 GPIO 初始化,复用为 LCD 引脚。
2. 打开 LCD 屏幕的背光.
3. 显示路径的选择,FIMD 控制器使用 RGB 等接口.
4. 设置 FIMD 控制器使用 RGB 接口,和设置时钟.
5. 使能 lcd 控制器,当前帧结束后使能lcd控制器。
6. 配置时钟分频,用于 LCD 控制器。
7. 选择 HSYNC 和 VSYNC 的时钟极性。
8. 设置时序和有效数据。
9. 使能(RGB888)。
10. 设置window0的上下左右,设置的是显存空间的大小。
11. 设置 frame_buffer 的地址。
12. 使能 channel 0 传输数据。
二、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) 斜线
四、LCD编程实战 4 -写英文中文字符
五、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。
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;
}
}
}
六、画图函数的显示效果测试
1、RGB的像素顺序调整
(1) 图片中红色的变成了蓝色、蓝色的变成了红色,这就说明 RGB 顺序反了。
(2) 解决方案一:重新使用 Image2LCD 来取模,取模时 RGB 顺序对调。
(3) 解决方案二:不重新取模,改代码,在 color 形成的时候调换 RGB 的顺序。
2、实验现象
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)
源自朱有鹏老师.