您当前的位置: 首页 > 

韦东山

暂无认证

  • 0浏览

    0关注

    506博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

15_LCD编程

韦东山 发布时间:2022-01-13 16:05:16 ,浏览量:0

第十五章 LCD编程 15.1 LCD硬件原理 15.2.1 LCD硬件工作原理简介

​ [外链图片转存中…(img-lRChXkF3-1642060379771)]

​ 假设上图是一个LCD屏幕,屏幕中一个一个密密麻麻的黑点称之为像素点,每一行有若干个点,试想下有一个电子枪,电子枪位于某一个像素点的背后,然后向这个像素发射红,绿,蓝三种原色,这三种颜色不同比例的组合成任意一种颜色。电子枪在像素点的背后,一边移动一边发出各种颜色的光,电子枪从左往右移动,到右边边缘之后就跳到下一行的行首,继续从左往右移动,如此往复,一直移动到屏幕右下角的像素点,最后就跳回原点。

​ 问题1:电子枪如何移动?

​ 答: 有一条像素时钟信号线(DCLK),连接屏幕,每来一个像素时钟信号(DCLK),电子枪就移动一个像素。

​ 问题2:电子枪打出的颜色该如何确定?

​ 答:有三组红,绿,蓝信号线(RGB),连接屏幕,由这三组信号线(RGB)确定颜色

​ 问题3:电子枪移动到LCD屏幕右边边缘时,如何得知需要跳到下一行的行首?

​ 答:有一条水平同步信号线(HSYNC),连接屏幕,当接收到水平同步信号(HSYNC),电子枪就跳到下一行的最左边

​ 问题4:电子枪如何得知需要跳到原点?

​ 答:有一条垂直同步信号线(VSYNC),连接屏幕,当接收到垂直同步信号线(VSYNC),电子枪就由屏幕右下脚跳到左上角(原点)

​ 问题5:电子枪如何得知三组信号线(RGB)确定的颜色就是它是需要的呢?

​ 答:有一条RGB数据使能信号线(DE),连接屏幕,当接收到数据使能信号线(DE),电子枪就知道这时由这三组信号线(RGB)确定的颜色是有效的,可以发射到该像素点。

​ 下图是开发板,LCD控制器,LCD屏幕的框图

image-20220112145025755

​ 之前提到的像素时钟(DCLK), 三组红,绿,蓝信号线(RGB),水平同步信号线(HSYNC),垂直同步信号线(VSYNC),RGB数据使能信号线(DE)都是从LCD控制器发出的,只要开发板支持LCD显示,他肯定就会有一个LCD控制器。

​ 问题6:RGB三组信号线上的数据从何而来?

image-20220112145046895

​ 上图是RGB数据来源框图,内存中划出一部分区域,这块区域成为Framebuffer,在这个Framebuffer里面我们会构造好每一个颜色所对应的像素,Framebuffer中的值会被LCD控制器读出来,通过RGB三组线传给电子枪,电子枪再它转换成红绿蓝三种颜色打到屏幕上,在屏幕上的每一个像素,在我们的Frambuffer里面都有一个对应存储空间,里面存有屏幕上对应像素的颜色。

​ 我们的LCD控制器会周而复始的从Framebuffer中取出一个像素的颜色值,发给电子枪,同时需要和DCLK,VSYNC,HSYNC,DE这些信号配合好。

15.2.2 RGB接口的LCD硬件连接信号

​ 本次实验编程的屏幕属于RGB接口的显示屏,RGB接口的显示屏至少具备以下信号:

(1)像素时钟信号(DCLK)

​ 像素时钟信号,用于同步LCD上的DE,VS,HS,RGB信号线。

(2)RGB数据信号(R[0:7] ,G[0:7],B[0:7])

​ 三组信号线组成,分别代表R(红色),G(绿色),B(蓝色),这三组信号中的每一组都会有8根信号,三组共同组成24根线来控制颜色数据。

(3)RGB数据使能信号(DE)

​ RGB接口的 LCD 有两种驱动模式DE 模式和 HV 模式, 在HV模式下,需要用到HS与VS同伴RGB数据,在DE模式下,则只需要DE信号同伴RGB数据,但是一般做LCD显示程序,都会兼容两种模式,所以一般都要将数据使能信号(DE),垂直同步信号(HS),水平同步信号(VS)一起使用。

(4)水平同步信号,

​ 电路中常用HS或HSYNC表示,详细说明下一小节会说明。

(5)垂直同步信号(帧同步或场同步)

​ 电路中常用VS或VSYNC表示,相信说明下一小节会说明。

(6)LCD背光电源控制信号

​ 一般是由普通GPIO控制(利用高低电平控制背光),背光就是在在LCD显示屏的背部一大串的灯珠,用它们来照亮屏幕。

​ 例如100ASK_IMX6ULL开发板的LCD接口定义,就包含了上面所述的几种信号类型:

image-20220112145210913

15.2.3 TFT材质液晶屏接口简介(7寸1024600TN-RGB)

​ 嵌入式一般都采用TFT材质的液晶屏,如遇到别的材质的屏幕,操作方法也是雷同,可能稍微有些差异,针对差异去做修改即可,7寸1024600TN-RGB液晶屏幕接口引脚如下图,一些关键的引脚做了注释。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNh3YKOO-1642060379773)(https://cdn.jsdelivr.net/gh/DongshanPI/HomeSite-Photos@main/IMX6ULL-BareMetal/LCD_program_bare_metal_image-20220112145308136.png)]

15.2.4 LCD关键特性

①信号时序与信号的极性

​ 接下来我们查看下100ASK_7.0寸LCD手册时序图

image-20220112145317135

​ 从最小的像素开始分析,电子枪每次在CLK下降沿,如上图所示,该LCD在像素时钟下降沿采集数据,从数据线上得到数据,发射到显示屏上,然后移动到下一个位置。Dn0-Dn7上的数据来源就是前面介绍的FrameBuffer。就这样从一行的最左边,一直移动到一行的最右边,完成了一行的显示,假设为x。

​ 当打完一行的最后一个数据后,就会收到Hsync行同步信号,如上图可知该LCD的HSD有效脉冲为低脉冲,根据时序图,一个HSD周期可以大致分为五部分组成:thp、thb、thd、thf。thpw称为脉冲宽度,这个时间不能太短,太短电子枪可能识别不到。电子枪正确识别到thpw后,会从最右端移动最左端,这个移动的时间就是thb,称之为移动时间。thfp表示显示完最右像素,再过多久HSD才来,thd为数据有效区,th为打完一行所需要的时间。

image-20220112145335485

​ 同理,当电子枪一行一行的从上面移动到最下面时,VSD垂直同步信号,如上图可知该LCD的VSD有效脉冲为低脉冲。然后就让电子枪移动回最上边。VSD中的tvpw是脉冲宽度,tvb是移动时间,tvfp表示显示完最下一行像素,再过多久VSD才来,tvd为数据有效区,tv为打完一帧所需要的时间。假设一共有y行,则LCD的分辨率就是x*y。

[外链图片转存中…(img-EtHcBoO3-1642060379775)]

​ RGB数据有效信号(DEN),高电平表示数据有效。

根据以上信息大致了解几个关键信号的时序和极性,后面章节会详细介绍。

image-20220112145347564

​ 再根据上图,我们就可以确定像素时钟是51.2Mhz。

②RGB数据的存放形式

​ 前面的LCD硬件接口,R0-R7、G0-G7、B0-B7,每个像素是占据3*8=24位的,即硬件上LCD的BPP是确定的。虽然硬件引脚连接是固定的,但我们使用的时候,可以根据实际情况进行取舍,比如我们的IMX6ULL开发板,可以让他支持不同的像素格式,ARGB888,ARGB555,RGB565等等,

​ 本实验支持ARGB888和ARGB555。

ARGB888:每个像素就占据32位数据,其中最高字节A表示灰度透明度其余RGB数据8+8+8=24BPP。

ARGB555:每个像素就占据16位数据,其中最高位A表示灰度透明度其余RGB数据5+5+5=15BPP。

15.2 IMX6ULL LCD控制器操作及寄存器 15.2.1 LCD控制器模块介绍

​ IMX6ULL的LCD控制器名称为elcdif(增强型LCD接口)主要特性如下:

​ a.支持MPU模式,针对显示屏内部有显存的显示屏;

​ b.支持DOTCLK模式,针对RGB接口使用,本实验就是此模式;

​ c.VSYNC模式,针对高速数据传输(行场信号);

​ d. 8/16/18/24/32 bit 的bpp数据都支持,取决于IO的复用设置及寄存器配置;

​ e.MPU模式,VSYNC模式,DOTCLK模式,都具有配置的时序参数;

image-20220112145424746

​ 上图是IMX6ULL的LCD控制器框图,AXI是一种总线协议,通过此总线将显存中的RGB数据写入到FIFO,经过FIFO过度,到达LCD接口,LCD控制器分两个时钟域,一个是外设总线时钟域,一个是LCD像素时钟域,前者是用于让LCD控制器正常工作的时钟,后者是控制电子枪移动速度的时钟。Read_Data操作工作在MPU模式,我们采用的是DCLK模式,因此不予考虑。

​ 以上只是介绍了部分,如需要更加详细的了解需要查看IMX6ull芯片手册《Chapter 34

​ Enhanced LCD Interface (eLCDIF)》

15.2.2 LCD控制器寄存器简介

[外链图片转存中…(img-Qpqmd64m-1642060379778)]

​ 上图是我们将要使用到的寄存器,接着将会大致讲解下使用到的寄存器,更加详细说明会在后续的LCD控制编程实验中提及。

15.2.2.1 ①LCDIF_CTRL寄存器:

image-20220112145500174

SFTRST:软复位,用于修改像素时钟后,进行复位同步时钟;

BYPASS_COUNT:DOTCLK和DVI modes都需要设置为1;

DOTCLK_MODE:设置为1,进入DOTCLK模式;

LCD_DATABUS_WIDTH:RGB数据总线,跟进数据总线宽度设置;

WORD_LENGTH:输入的RBG数据格式,即多少位表示一个像素;

MASTER:LCD控制器主机模式设置;

DATA_FORMAT_16_BIT:当设置为16BPP的时候需要设置该位

15.2.2.2 ②LCDIF_CTRL1寄存器:

image-20220112145520234

BYTE_PACKING_FORMAT:用于表示4字节RGB数据中,那几个字节是属于有有效数据,因为其中有一个字节表示A(灰度,透明度)

15.2.2.3 ③LCDIF_TRANSFER_COUNT寄存器:

image-20220112145543927

V_COUNT:表示垂直方向上的像素个数,即分辨率中的x;

H_COUNT:表示水平方向上的像素个数,即分辨率中的y;

15.2.2.4 ④LCDIF_VDCTRL0寄存器

image-20220112145551594

VSYNC_PULSE_WIDTH:垂直同步信号的宽度;

VSYNC_PULSE_WIDTH_UNIT:根据不同模式下的计算时钟方式来决定垂直同步信号宽度;

VSYNC_PERIOD_UNIT:根据不同模式下的计算时钟方式来发垂直同步信号;

ENABLE_POL:DE数据有效信号的极性,即有效电平极性;

DOTCLK_POL:像素时钟信号的极性,即有效电平极性;

HSYNC_POL:水平同步信号的极性,即有效电平极性;

VSYNC_POL:垂直同步信号的极性,即有效电平极性;

ENABLE_PRESENT:在DOTCLK模式下,是否会硬件产生ENABLE使能数据信号;

VSYNC_OEB:VSYNC设置为输出还是输入模式,我们选择输出模式;

15.2.2.5 ⑤LCDIF_VDCTRL1寄存器

image-20220112145558654

VSYNC_PERIOD:两个垂直同步信号之间的总数,即垂直方向同步信号的总周期;

15.2.2.6 ⑥LCDIF_VDCTRL2寄存器

image-20220112145602359

HSYNC_PULSE_WIDTH:水平同步信号脉冲宽度;

HSYNC_PERIOD:两个水平同步信号之间的总数,即水平方向同步信号的总周期

15.2.2.7 ⑦LCDIF_VDCTRL3寄存器

image-20220112145625980

HRIZONTAL_WAIT_CNT:水平方向上的等待像素个数;

VERTICAL_WAIT_CNT:垂直方向上的等待像素个数;

15.2.2.8 ⑧LCDIF_VDCTRL4寄存器

image-20220112145632883

SYNC_SIGNALS_ON:工作在DOTCLK模式下,需要设置为1;

DOTCLK_H_VALID_DATA_CNT:DOTCLK模式下,水平方向上的有效像素点个数,即分辨率的y;

15.2.2.9 ⑨LCDIF_CUR_BUF寄存器

image-20220112145636601

ADDR:通过LCD控制器,发送的当前帧地址;

15.2.2.10 ⑩LCDIF_NEXT_BUF寄存器

image-20220112145651586

ADDR:通过LCD控制器,发送的下一帧地址;

15.3 编程_框架与准备

​ 本节文档对应的视频是《第003节_LCD编程_框架与准备_P》。

15.3.3 功能目的

​ 我们最终的目的是在LCD显示屏上画线、画圆和写字,此外还需要一个测试程序提供操作菜单,调用画线、画圆和写字操作,这些终究其核心是画点,我们需要实现画点才能实现其他功能,但是画点前也要让我们的LCD控制器正常工作起来才能实现它,最终总结:先让LCD控制器正常工作(配置寄存器),再编写画点的函数。

15.3.4 编程框架

​ 接着我们就需要实现画点,在实现画点之前想两个问题:

​ ①有两款尺寸大小的LCD显示屏,如何快速的在两个lcd上切换?

​ ②有两款不同的CPU都需要显示同一款LCD显示屏,如何快速的在两个cpu上切换?

​ 为了让程序更加好扩展,下面介绍“面向对象编程”的概念

​ 我们发现LCD显示屏虽然不同尺寸,参数不同,但是它们终究是LCD显示屏,我们可以把他们归一类,当需要使用某款LCD显示屏的参数时就提供该款的参数,其他的LCD显示屏参数不管他不就可以了吗?

​ 同理不同的CPU虽然LCD控制器地址不同,操作也不同,但是它们终究是LCD控制器,我们可以把他们归一类,当确定使用某个LCD控制器的时候就用这个LCD控制器的操作,其他的LCD控制器不管他。

​ 下图是LCD编程的框架,尽可能的“高内聚低耦合”,即类的内聚性是否高,耦合度是否低。目的是使程序模块的可重用性、移植性大大增强。通常程序结构中各模块的内聚程度越高,模块间的耦合程度就越低

image-20220112145758887

​ 根据不同的LCD控制器特性,来设置不同的LCD控制器,对于我们开发板,就是imx6ull_con.c,假如希望在其它 开发板上也实现LCD显示,只需添加相应的代码文件xxx_con即可。

​ 根据不同的LCD屏幕特性,来编写不同的LCD屏幕参数,对于我们的开发板,就是lcd_7_0.c,假如希望这个开发板支持别的LCD屏幕,只需添加相应的代码文件lcd_xxx.c即可。

15.4 编程_抽象出重要结构体

​ 本节代码在裸机Git仓库 NoosProgramProject/(15_LCD编程/01_simple_test/lcd_manager.h) 与**裸机Git仓库 NoosProgramProject/(15_LCD编程/01_simple_test/lcd_controller_manager.h)**头文件中,本节文档对应的视频是《第004节_LCD编程_抽象出重要结构体_P》。

15.4.1 抽象出LCD屏幕的结构体

​ 建立一个lcd_manager.h,将任意LCD都共有的参数(引脚的极性、时序、数据的格式bpp、分辨率等)使用面向对象的思维方式,将这些封装成结构体放在lcd_manager.h中

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

	/* NORMAL : 正常
	* INVERT : 取反
	*/
	typedef struct pins_polarity {
		int de;    /* normal: 高电平使能数据 */
		int vclk;  /* normal: 在下降沿获取数据 */
		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 {
	
		char *name;
		
		/*引脚极性参数*/
		pins_polarity pins_pol;
	
		/*时序参数*/
		time_sequence time_seq;
	
		/*分辨率*/
		int xres;
		int yres;
		int bpp;
	
		/*显存*/
		unsigned int fb_base;
	
	}lcd_params, *p_lcd_params;

​ 以后就使用lcd_params结构体来表示lcd参数 ,通过register_lcd函数注册某款LCD屏幕参数到一个lcd_params结构体数组,然后通过select_lcd函数在lcd_params结构体数组中选中指定的LCD屏幕参数保存起来,提供给其他函数用。

15.4.2 抽象出LCD控制器的结构体

​ 建立一个lcd_controller_manager.h,将任意LCD控制器都共有的函数(初始化函数,使能函数等)使用面向对象的思维方式,将这些封装成结构体放在lcd_controller_manager.h中

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

​ 以后就使用lcd_controller结构体来表示lcd控制器。

​ 建立一个lcd_controller_manager.c,提供一系列的LCD控制器的管理函数,用这些管理函数,通过register_lcd_controller函数注册新的LCD控制器到lcd_controller的结构体数组中,然后通过select_lcd_controller函数在lcd_controller结构体数组中选中指定的LCD控制器,提供给其他函数用,最终用户再调用光宇LCD控制器的操作时,就会通过选中的lcd_controller的结构体访问到对应的某款LCD控制器的函数。

15.5 编程_LCD控制器

​ 本节代码在**裸机Git仓库 NoosProgramProject/(15_LCD编程/01_simple_test/imx6ull_con.c)**源文件中, 本节文档对应的视频是《第005节_LCD编程_LCD控制器_P》。

15.5.1 LCD控制器相关引脚复用配置

image-20220112145945035

​ 根据前面硬件接口章节15.1.3和上图,我们知道要设置这30个引脚,设置引脚按两步走,第一步设置引脚复用功能,第二部设置引脚的硬件属性。查看相应的寄存器得知,复用功能寄存器均设置为0即可,接着硬件属性根据章节《4-1.3 GPIO操作方法》的内容,均设置为0xB9即可。

15.5.2 LCD控制器像素时钟配置

​ 根据IMX6ULL芯片手册的Chapter 18 Clock Controller Module (CCM),我们就可以设置像素时钟为我们需要的51.2Mhz

15.5.2.1 ①确定PLL

image-20220112150005692

​ 由上图可知LCD控制器的时钟来源是PLL5(video pll)

[外链图片转存中…(img-1hb2DTWp-1642060379786)]

​ 根据上图可得知VIDEO pll的公式,但是为了方便计算,我们不需要后面的小数运算即Video PLL output frequency(PLL5)= Fref * (DIV_SELECT + 0)

15.5.2.2 ②确定PLL后的分频系数

image-20220112150053310

​ 根据上图可知PLL5出来后经过两级分频,即PLL5_MAIN_CLK = PLL5 / POST_DIV_SELECT / VIDEO_DIV

15.5.2.3 ③PLL分频后进入LCDIF控制器前的分频系数

image-20220112150121312

​ 根据上图可知PLL5分频后到LCDIF控制器也有两级分频,即LCDIF1_CLK_ROOT = PLL5_MAIN_CLK /LCDIF1_PRED / LCDIF1_PODF,根据上面三个内容,我们可以采用以下这个配置来达到像素时钟51.2Mhz,DIV_SELECT = 32;NUM = 0;DENOM = 0;POST_DIV_SELECT = 1,VIDEO_DIV = 1LCDIF1_PRED = 3;LCDIF1_PODF = 5

​ 带入时钟公式得24*(32+0)/1/1/3/5 ≈ 51.2Mhz。下面开始编程:

15.5.3 LCD控制器时钟编程 15.5.3.1 ①取消小数分配器
  CCM_ANALOG->PLL_VIDEO_NUM   = 0; 	 
  CCM_ANALOG->PLL_VIDEO_DENOM = 0;

​ 清零表示取消小数分配器

15.5.3.2 ②设置CCM_ANALOG_PLL_VIDEOn寄存器

image-20220112150211473

CCM_ANALOG->PLL_VIDEO =  (2             
关注
打赏
1658827356
查看更多评论
0.0468s