IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 嵌入式linux学习笔记 1-11 LCD -> 正文阅读

[嵌入式]嵌入式linux学习笔记 1-11 LCD

目录

1. LCD硬件原理

2. LCD控制器

3. LCD编程——框架与准备

?

4. LCD编程——抽象出重要结构体

5. LCD编程——LCD控制器

补充C语言结构体中"."的使用

6. LCD编程——LCD设置


1. LCD硬件原理

??????? 屏幕上每个像素是由电子枪一边移动一边发出颜色的,每当来了一个pixel clock就移动一个像素。颜色是通过RGB三组线确定。当接收到HSYNC脉冲电子枪知道这行像素已经解决,跳到下一行。当接收到VSYNC脉冲,电子枪知道所有的像素都搞定了,重新回到原点。


这是一个关于LCD显示屏原理的gif图

?https://images0.cnblogs.com/blog2015/268182/201508/261557535783549.gif

更多更详细的原理参考这篇文章

http://www.cnblogs.com/shangdawei/p/4760933.html


????????

???????? 我们分析以下数据图,电子枪在CLK下降沿从数据线上得到数据(来自FrameBuffer中的数据),发射到像素上,最后移动到下一个像素,持续做到一行中的最后一个像素,打完最后一个像素后,会收到一个行脉冲信号Hsync(宽度不能太小,要给电子枪反应的时间thp和操作的时间thb),电子枪从最右移动到下一行的最左。当所有的像素都解决了,会收到一个Vsync信号,同样也需要持续一定的脉冲宽度tvp和预留移动时间tvb。

??????? thf为显示完最右像素后到下一个HSync来到之间的时间,tvf为显示完最下一行到下一个Vsync来到之间的时间。

?

?????

?? 综合上面两张图得到了以下这张图。

Q: 那每个像素在framebuffer中占据多少位BPP(Bit Per Pixel)?

??????? 板子上的LCD屏幕RGB各占8位,所以是3*8=24位。但是我们的arm开发板实际只用到16条线与LCD屏幕连接,R5G6B5,也就是每个像素占据16位的数据。

2. LCD控制器

主要有两个功能:

  • 从内存(framebuffer)里取出某个像素的数据:
    把framebuffer的地址告诉lcd控制器,bpp(bit per pixel), 分辨率。
    ?
  • 配合其他信号发送数据给LCD:
    把时序告诉Lcd控制器,并且设置引脚的极性。

??????? 常用的LCD有两种,STN LCD和TFT LCD,用得更多的是TFT LCD, 所以我们主要研究后者。可以通过配置BSWP 和HWSWP 来选择bpp(bit per pixel)为多少以及像素的排列方式。下图是bpp为16的两种形式,显然BSWP = 0, HWSWP = 1比较符合我们的编程习惯。

????????为了省framebuffer占用的空间,也可以采用8BPP, 另外添加一个调色板实际是一个256*16bits的一个内存空间,8bpp对应了这个调色板中的256种rgb颜色,每一种颜色都对应了一个16bit的值。所以这个8pp实际是伪彩色,只是用这8bit做索引,真正的颜色在调色板里,而之前的16bpp是真彩色,真正对应一种颜色。

?????????

3. LCD编程——框架与准备

??????? 我们需要引入面向对象的思想,因为我们会使用不同的lcd屏幕对应的底层的初始化配置都是不一样的,我们要保证即使我们使用了不同的lcd屏幕,顶层文件都是一样的不需要修改。

1、我们抽象出lcd_3.5.c和lcd_4.3.c的共同点,比如都有初始化函数init(),我们可以新建一个lcd.c,然后定义一个结构体:

struct lcd_opr{
    void (*init)(void);
};

2、在不同的下层文件里设置结构体
在lcd_3.5.c里:

struct lcd_opr lcd_3_5_opr{
   .init = lcd_3_5_init;
};

?

?

struct lcd_opr lcd_4_3_opr{
   .init = lcd_4_3_init;
};

3、用户不接触lcd_3.5.c和lcd_4.3.c,只需要在lcd.c里通过指针访问对应的结构体的函数,也就调用了不同init()。

?

4. LCD编程——抽象出重要结构体

??????? lcd.c是顶层,通过结构体调用底层的各种类型的lcd,比如lcd_3.5.c, lcd_4.3.c;lcd_controller也是顶层文件,通过结构体调用底层的各种芯片的lcd_controller, 比如s3c2440_led_controller.c。

?????? 所以分别头文件中定义结构体。抽象出不同类型的lcd,和lcd控制器共有的需要定义的参数放入结构体中,比如时序、引脚极性、分辨率Bpp等等。

??????? lcd.h

#ifndef _LCD_H
#define _LCD_H


enum {
	NORMAL = 0,
	INVERT = 1,
};

/* NORMAL : 正常极性
 * INVERT : 反转极性
 */
typedef struct pins_polarity {
	int vclk;  /* 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; /* 上边黑框, Vertical Back porch */
	int tvf; /* 下边黑框, Vertical Front porch */

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

	int vclk;
}time_sequence, *p_time_sequence;


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


#endif /* _LCD_H */

lcd_controller.h

#ifndef _LCD_CONTROLLER_H
#define _LCD_CONTROLLER_H

#include "lcd.h"

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

#endif /* _LCD_CONTROLLER_H */

5. LCD编程——LCD控制器

??????? 在头文件中定义完结构体以后,我们需要在底层文件中给结构体赋值。我们给结构体中三个成员分别赋值s3c2440_lcd_controller_init, s3c2440_lcd_controller_enalbe, s3c2440_lcd_controller_disable 这三个函数。之后我们要实现这三个函数的功能。

struct lcd_controller s3c2440_lcd_controller = {
	.init    = s3c2440_lcd_controller_init,
	.enalbe  = s3c2440_lcd_controller_enalbe,
	.disable = s3c2440_lcd_controller_disable,
};

补充C语言结构体中"."的使用

关于上面这个结构体中成员前面的".",主要是为了帮我们减少成员类型的定义,另外注意里面每一个成员后面不是“;”,而是“,”。 如果忘记了用法可以看一下这篇文章。

https://blog.csdn.net/qq_31869107/article/details/80988082

1.配置LCDCON1

分别设置TFT下的时钟、显示模式、bpp。

2.配置LCDCON2

??????? 这个寄存器是用来设置与垂直方向相关及时序频率相关的时序的。

?

3、设置LCDCON3
与水平方向相关的设置

4. 设置第四个寄存器LCDCON4

??????? 配置脉冲宽度。

?5、设置第五个寄存器

??????? 用来设置引脚极性、16bpp,、内存中像素存放的格式。

?

?5.设置LCDSADDR

??????? 设置framebuffer的地址 。

???????? 设置对于单行扫描来说的endaddress。

6. LCD编程——LCD设置

??????? 按照芯片手册,在lcd_4.3.c中的结构体中设置极性、时序等。

lcd_params lcd_4_3_params = {
	.name = "lcd_4.3"
	.pins_polarity = {
		.de    = NORMAL,	/* normal: 高电平时可以传输数据 */
		.pwren = NORMAL,    /* normal: 高电平有效 */
		.vclk  = NORMAL,	/* normal: 在下降沿获取数据 */
		.rgb   = NORMAL,	/* normal: 高电平表示1 */
		.hsync = INVERT,    /* normal: 高脉冲 */
		.vsync = INVERT, 	/* normal: 高脉冲 */
	},
	.time_sequence = {
		/* 垂直方向 */
		.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  = 16,
	.fb_base = LCD_FB_BASE,
};

????????另外,我们还要完成顶层文件lcd.c, lcd_controller.c,用数组保存下面各种lcd_controller,提供register_lcd_controller给下面的代码设置数组,提供select_lcd_controller给上面的代码使用,通过传入名字选择对应的lcd_controller。?

lcd.c

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

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_controller.c



#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);
		return 0;
	}
	return -1;
}

void lcd_contoller_add(void)
{
	s3c2440_lcd_contoller_add();
}

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 09:30:35  更:2021-08-28 09:31:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:46:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码