RGBLCD显示实验————复习到这
Posted 行稳方能走远
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RGBLCD显示实验————复习到这相关的知识,希望对你有一定的参考价值。
LCD 液晶屏是常用到的外设,通过LCD 可以显示绚丽的图形、界面等,提高人机交互的效率。I.MX6U 提供了一个eLCDIF 接口用于连接RGB 接口的液晶屏。本章我们就学习如何驱动RGB 接口液晶屏,并且在屏幕上显示字符。
LCD 和eLCDIF 简介
LCD 简介
LCD 全称是Liquid Crystal Display,也就是液晶显示器,是现在最常用到的显示器,手机、电脑、各种人机交互设备等基本都用到了LCD,最常见就是手机和电脑显示器了。由于笔者不是LCD 从业人员,对于LCD 的具体原理不了解,百度百科对于LCD 的原理解释如下:
LCD 的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通过TFT 上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目的。
我们现在要在I.MX6U-ALPHA 开发板上使用LCD,所以不需要去研究LCD 的具体实现原理,我们只需要从使用的角度去关注LCD 的几个重要点:
1、分辨率
提起LCD 显示器,我们都会听到720P、1080P、2K 或4K 这样的字眼,这个就是LCD 显示器分辨率。LCD 显示器都是由一个一个的像素点组成,像素点就类似一个灯(在OLED 显示器中,像素点就是一个小灯),这个小灯是RGB 灯,也就是由R(红色)、G(绿色)和B(蓝色)这三种颜色组成的,而RGB 就是光的三原色。1080P 的意思就是一个LCD 屏幕上的像素数量是1920*1080 个,也就是这个屏幕一列1080 个像素点,一共1920 列,如图24.1.1.1 所示:
在图24.1.1.1 就是1080P 显示器的像素示意图,X 轴就是LCD 显示器的横轴,Y 轴就是显示器的竖轴。图中的小方块就是像素点,一共有19201080=2073600 个像素点。左上角的A 点是第一个像素点,右下角的C 点就是最后一个像素点。2K 就是25601440 个像素点,4K 是3840*2160 个像素点。很明显,在LCD 尺寸不变的情况下,分辨率越高越清晰。同样的,分辨率不变的情况下,LCD 尺寸越小越清晰。比如我们常用的24 寸显示器基本都是1080P 的,而我们现在使用的5 寸的手机基本也是1080P 的,但是手机显示细腻程度就要比24 寸的显示器要好很多!
由此可见,LCD 显示器的分辨率是一个很重要的参数,但是并不是分辨率越高的LCD 就越好。衡量一款LCD 的好坏,分辨率只是其中的一个参数,还有色彩还原程度、色彩偏离、亮度、可视角度、屏幕刷新率等其他参数。
2、像素格式
上面讲了,一个像素点就相当于一个RGB 小灯,通过控制R、G、B 这三种颜色的亮度就可以显示出各种各样的色彩。那该如何控制R、G、B 这三种颜色的显示亮度呢?一般一个R、G、B 这三部分分别使用8bit 的数据,那么一个像素点就是8bit*3=24bit,也就是说一个像素点3 个字节,这种像素格式称为RGB888。如果再加入8bit 的Alpha(透明)通道的话一个像素点就是32bit,也就是4 个字节,这种像素格式称为ARGB8888。如果学习过STM32 的话应该还听过RGB565 这种像素格式,在本章实验中我们使用ARGB8888 这种像素格式,一个像素占用4个字节的内存,这四个字节每个位的分配如图24.1.1.2 所示:
在图24.1.1.2 中,一个像素点是4 个字节,其中bit31~bit24 是Alpha 通道,bit23~bit16 是RED 通道,bit15~bit14 是GREEN 通道,bit7~bit0 是BLUE 通道。所以红色对应的值就是0X00FF0000,蓝色对应的值就是0X000000FF,绿色对应的值为0X0000FF00。通过调节R、G、B 的比例可以产生其它的颜色,比如0X00FFFF00 就是黄色,0X00000000 就是黑色,0X00FFFFFF
就是白色。大家可以打开电脑的“画图”工具,在里面使用调色板即可获取到想要的颜色对应的数值,如图24.1.1.3 所示:
3、LCD 屏幕接口
LCD 屏幕或者说显示器有很多种接口,比如在显示器上常见的VGA、HDMI、DP 等等,但是I.MX6U-ALPHA开发板不支持这些接口。I.MX6U-ALPHA 支持RGB 接口的LCD,RGBLCD接口的信号线如表24.1.1.1 所示:
信号线 | 描述 |
---|---|
R[7:0] | 8 根红色数据线。 |
G[7:0] | 8 根绿色数据线。 |
B[7:0] | 8 根蓝色数据线。 |
DE | 数据使能线。 |
VSYNC | 垂直同步信号线。 |
HSYNC | 水平同步信号线。 |
PCLK | 像素时钟信号线。 |
表24.1.1.1 就是RGBLCD 的信号线,R[7:0]、G[7:0]和B[7:0]这24 根是数据线,DE、VSYNC、HSYNC 和PCLK 这四根是控制信号线。RGB LCD 一般有两种驱动模式:DE 模式和HV 模式,这两个模式的区别是DE 模式需要用到DE 信号线,而HV 模式不需要用到DE 信号线,在DE模式下是可以不需要HSYNC 信号线的,即使不接HSYNC 信号线LCD 也可以正常工作。
ALIENTEK 一共有三款RGB LCD 屏幕,型号分别为:ATK-4342(4.3 寸,480272)、ATK-7084(7 寸,800480)和ATK-7016(7 寸,1024*600),本教程就以ATK-7016 这款屏幕为例讲解,ATK-7016 的屏幕接口原理图如图24.1.1.4 所示:
图中J1 就是对外接口,是一个40PIN 的FPC 座(0.5mm 间距),通过FPC 线,可以连接到I.MX6U-ALPHA 开发板上面,从而实现和I.MX6U 的连接。该接口十分完善,采用RGB888格式,并支持DE&HV 模式,还支持触摸屏和背光控制。右侧的几个电阻,并不是都焊接的,而是可以用户自己选择。默认情况,R1 和R6 焊接,设置LCD_LR 和LCD_UD,控制LCD 的扫描方向,是从左到右,从上到下(横屏看)。而LCD_R7/G7/B7 则用来设置LCD 的ID,由于RGBLCD 没有读写寄存器,也就没有所谓的ID,这里我们通过在模块上面,控制R7/G7/B7 的上/下拉,来自定义LCD 模块的ID,帮助MCU 判断当前LCD 面板的分辨率和相关参数,以提高程序兼容性。这几个位的设置关系如表24.1.1.2 所示:
M2 LCD_G7 | M1 LCD_G7 | M0 LCD_R7 | LCD ID | 说明 |
---|---|---|---|---|
0 | 0 | 0 | 4342 | ATK-4342 RGBLCD 模块,分辨率:480*272 |
0 | 0 | 1 | 7084 | ATK-7084 RGBLCD 模块,分辨率:800*480 |
0 | 1 | 0 | 7016 | ATK-7016,RGBLCD 模块,分辨率:1024 * 600 |
1 | 0 | 0 | 4384 | ATK-4384,RGBLCD 模块,分辨率:800*480 |
X | X | X | NC | 暂时未用到 |
ATK-7016 模块,就设置M2:M0=010 即可。这样,我们在程序里面,读取LCD_R7/G7/B7,得到M0:M2 的值,从而判断RGBLCD 模块的型号,并执行不同的配置,即可实现不同LCD 模块的兼容。
4、LCD 时间参数
如果将LCD 显示一帧图像的过程想象成绘画,那么在显示的过程中就是用一根“笔”在不同的像素点画上不同的颜色。这根笔按照从左至右、从上到下的顺序扫描每个像素点,并且在像素画上对应的颜色,当画到最后一个像素点的时候一幅图像就绘制好了。假如一个LCD 的分辨率为1024*600,那么其扫描如图24.1.1.5 所示:
结合图24.1.1.5 我们来看一下LCD 是怎么扫描显示一帧图像的。一帧图像也是由一行一行组成的。HSYNC 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行了,所以此信号都是在图24.1.1.5 的最左边。当VSYNC 信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了,所以此信号在图24.1.1.5 的左上角。
在图24.1.1.5 可以看到有一圈“黑边”,真正有效的显示区域是中间的白色部分。那这一圈“黑边”是什么东西呢?这就要从显示器的“祖先”CRT 显示器开始说起了,CRT 显示器就是以前很常见的那种大屁股显示器,在2019 年应该很少见了,如果在农村应该还是可以见到的。CRT 显示器屁股后面是个电子枪,这个电子枪就是我们上面说的“画笔”,电子枪打出的电子撞击到屏幕上的荧光物质使其发光。只要控制电子枪从左到右扫完一行(也就是扫描一行),然后从上到下扫描完所有行,这样一帧图像就显示出来了。也就是说,显示一帧图像电子枪是按照‘Z’形在运动,当扫描速度很快的时候看起来就是一幅完成的画面了。
当显示完一行以后会发出HSYNC 信号,此时电子枪就会关闭,然后迅速的移动到屏幕的左边,当HSYNC 信号结束以后就可以显示新的一行数据了,电子枪就会重新打开。在HSYNC信号结束到电子枪重新打开之间会插入一段延时,这段延时就图24.1.1.5 中的HBP。当显示完一行以后就会关闭电子枪等待HSYNC 信号产生,关闭电子枪到HSYNC 信号产生之间会插入一段延时,这段延时就是图24.1.1.5 中的HFP 信号。同理,当显示完一帧图像以后电子枪也会关闭,然后等到VSYNC 信号产生,期间也会加入一段延时,这段延时就是图24.1.1.5 中的VFP。VSYNC 信号产生,电子枪移动到左上角,当VSYNC 信号结束以后电子枪重新打开,中间也会加入一段延时,这段延时就是图24.1.1.5 中的VBP。
HBP、HFP、VBP 和VFP 就是导致图24.1.1.5 中黑边的原因,但是这是CRT 显示器存在黑边的原因,现在是LCD 显示器,不需要电子枪了,那么为何还会有黑边呢?这是因为RGB LCD屏幕内部是有一个IC 的,发送一行或者一帧数据给IC,IC 是需要反应时间的。通过这段反应时间可以让IC 识别到一行数据扫描完了,要换行了,或者一帧图像扫描完了,要开始下一帧图像显示了。因此,在LCD 屏幕中继续存在HBP、HFP、VPB 和VFP 这四个参数的主要目的是为了锁定有效的像素数据。这四个时间是LCD 重要的时间参数,后面编写LCD 驱动的时候要用到的,至于这四个时间参数具体值是多少,那要需要去查看所使用的LCD 数据手册了。
5、RGB LCD 屏幕时序
上面讲了行显示和帧显示,我们来看一下行显示对应的时序图,如图24.1.1.6 所示:
图24.1.1.6 就是RGB LCD 的行显示时序,我们来分析一下其中重要的几个参数:
HSYNC:行同步信号,当此信号有效的话就表示开始显示新的一行数据,查阅所使用的LCD 数据手册可以知道此信号是低电平有效还是高电平有效,假设此时是低电平有效。
HSPW:有些地方也叫做thp,是HSYNC 信号宽度,也就是HSYNC 信号持续时间。HSYNC信号不是一个脉冲,而是需要持续一段时间才是有效的,单位为CLK。
HBP:有些地方叫做thb,前面已经讲过了,术语叫做行同步信号后肩,单位是CLK。
HOZVAL:有些地方叫做thd,显示一行数据所需的时间,假如屏幕分辨率为1024*600,那么HOZVAL 就是1024,单位为CLK。
HFP:有些地方叫做thf,前面已经讲过了,术语叫做行同步信号前肩,单位是CLK。
当HSYNC 信号发出以后,需要等待HSPW+HBP 个CLK 时间才会接收到真正有效的像素数据。当显示完一行数据以后需要等待HFP 个CLK 时间才能发出下一个HSYNC 信号,所以显示一行所需要的时间就是:HSPW + HBP + HOZVAL + HFP。
一帧图像就是由很多个行组成的,RGB LCD 的帧显示时序如图24.1.1.7 所示:
图24.1.1.7 就是RGB LCD 的帧显示时序,我们来分析一下其中重要的几个参数:
VSYNC:帧同步信号,当此信号有效的话就表示开始显示新的一帧数据,查阅所使用的LCD 数据手册可以知道此信号是低电平有效还是高电平有效,假设此时是低电平有效。
VSPW:有些地方也叫做tvp,是VSYNC 信号宽度,也就是VSYNC 信号持续时间,单位为1 行的时间。
VBP:有些地方叫做tvb,前面已经讲过了,术语叫做帧同步信号后肩,单位为1 行的时间。
LINE:有些地方叫做tvd,显示一帧有效数据所需的时间,假如屏幕分辨率为1024*600,那么LINE 就是600 行的时间。
VFP:有些地方叫做tvf,前面已经讲过了,术语叫做帧同步信号前肩,单位为1 行的时间。
显示一帧所需要的时间就是:VSPW+VBP+LINE+VFP 个行时间,最终的计算公式:
T = (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
因此我们在配置一款RGB LCD 的时候需要知道这几个参数:HOZVAL(屏幕有效宽度)、LINE(屏幕有效高度)、HBP、HSPW、HFP、VSPW、VBP 和VFP。ALIENTEK 三款RGB LCD屏幕的参数如表24.1.1.3 所示:
6、像素时钟
像素时钟就是RGB LCD 的时钟信号,以ATK7016 这款屏幕为例,显示一帧图像所需要的时钟数就是:
= (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
= (3 + 20 + 600 + 12) * (20 + 140 + 1024 + 160)
= 635 * 1344
= 853440。
显示一帧图像需要853440 个时钟数,那么显示60 帧就是:853440 * 60 = 51206400≈51.2M,所以像素时钟就是51.2MHz。
I.MX6U 的eLCDIF 接口时钟图如图24.1.1.8 所示:
①、此部分是一个选择器,用于选择哪个PLL 可以作为LCDIF 时钟源,由寄存器CCM_CSCDR2 的位LCDIF1_PRE_CLK_SEL(bit17:15)来决定,LCDIF1_PRE_CLK_SEL 选择设置如表24.1.1.4 所示:
值 | 时钟源 |
---|---|
0 | PLL2 作为LCDIF 的时钟源。 |
1 | PLL3_PFD3 作为LCDIF 的时钟源。 |
2 | PLL5 作为LCDIF 的时钟源。 |
3 | PLL2_PFD0 作为LCDIF 的时钟源。 |
4 | PLL2_PFD1 作为LCDIF 的时钟源。 |
5 | PLL3_PFD1 作为LCDIF 的时钟源。 |
在第16 章讲解I.MX6U 时钟系统的时候说过有个专用的PLL5 给VIDEO 使用,所以LCDIF1_PRE_CLK_SEL 设置为2。
②、此部分是LCDIF 时钟的预分频器,由寄存器CCM_CSCDR2 的位LCDIF1_PRED 来决定预分频值。可设置值为0~ 7,分别对应1~8 分频。
③、此部分进一步分频,由寄存器CBCMR 的位LCDIF1_PODF 来决定分频值。可设置值为0~ 7,分别对应1~8 分频。
④、此部分是一个选择器,选择LCDIF 最终的根时钟,由寄存器CSCDR2 的位LCDIF1_CLK_SEL 决定,LCDIF1_CLK_SEL 选择设置如表24.1.1.5 所示:
值 | 时钟源 |
---|---|
0 | 前面复用器出来的时钟,也就是前面PLL5 出来的时钟作为LCDIF 的根时钟。 |
1 | ipp_di0_clk 作为LCDIF 的根时钟。 |
2 | ipp_di1_clk 作为LCDIF 的根时钟。 |
3 | ldb_di0_clk 作为LCDIF 的根时钟。 |
4 | ldb_di1_clk 作为LCDIF 的根时钟。 |
这里肯定选择PLL5 出来的那一路时钟作为LCDIF 的根时钟,因此LCDIF1_CLK_SEL 设置为0。LCDIF 既然选择了PLL5 作为时钟源,那么还需要初始化PLL5,LCDIF 的时钟是由PLL5 和图24.1.1.8 中的②、③这两个分频值决定的,所以需要对这三个进行合理的设置以搭配出所需的时钟值,我们就以ATK7016 屏幕所需的51.2MHz 为例,看看如何进行配置。
PLL5 频率设置涉及到四个寄存器:CCM_PLL_VIDEO 、CCM_PLL_VIDEO_NUM 、CCM_PLL_VIDEO_DENOM 、CCM_MISC2 。其中CCM_PLL_VIDEO_NUM 和CCM_PLL_VIDEO_DENOM 这两个寄存器是用于小数分频的,我们这里为了简单不使用小数分频,因此这两个寄存器设置为0。
PLL5 的时钟计算公式如下:
PLL5_CLK = OSC24M * (loopDivider + (denominator / numerator)) / postDivider
不使用小数分频的话PLL5 时钟计算公式就可以简化为:
PLL5_CLK = OSC24M * loopDivider / postDivider
OSC24M 就是24MHz 的有源晶振,现在的问题就是设置loopDivider 和postDivider。先来看一下寄存器CCM_PLL_VIDEO,此寄存器结构如图24.1.1.9 所示:
寄存器CCM_PLL_VIDEO 用到的重要的位如下:
POST_DIV_SLECT(bit20:19):此位和寄存器CCM_ANALOG_CCMSC2 的VIDEO_DIV 位共同决定了postDivider,为0 的话是4 分频,为1 的话是2 分频,为2 的话是1 分频。本章设置为2,也就是1 分频。
ENABLE(bit13):PLL5(PLL_VIDEO)使能位,为1 的话使能PLL5,为0 的话关闭PLL5。
DIV_SELECT(bit6:0):loopDivider 值,范围为27~54,本章设置为32。
寄存器CCM_ANALOG_MISC2 的位VIDEO_DIV(bit31:30)与寄存器CCM_PLL_VIDEO 的位POST_DIV_SLECT(bit20:19)共同决定了postDivider,通过这两个的配合可以获得2、4、8、16 分频。本章将VIDEO_DIV 设置为0,也就是1 分频,因此postDivider 就是1,loopDivider
设置为32,PLL5 的时钟频率就是:
PLL5_CLK = OSC24M * loopDivider / postDivider
= 24M * 32 / 1
= 768MHz。
PLL5 此时为768MHz,在经过图24.1.1.8 中的②和③进一步分频,设置②中为3 分频,也就是寄存器CCM_CSCDR2 的位LCDIF1_PRED(bit14:12)为2。设置③中为5 分频,就是寄存器CCM_CBCMR 的位LCDIF1_PODF(bit25:23)为4。设置好以后最终进入到LCDIF 的时钟频率就是:768/3/5 =51.2MHz,这就是我们需要的像素时钟频率。
7、显存
在讲像素格式的时候就已经说过了,如果采用ARGB8888 格式的话一个像素需要4 个字节的内存来存放像素数据,那么1024 * 600 分辨率就需要1024 * 600 * 4=2457600B ≈ 2.4MB 内存。但是RGB LCD 内部是没有内存的,所以就需要在开发板上的DDR3 中分出一段内存作为RGB LCD 屏幕的显存,我们如果要在屏幕上显示什么图像的话直接操作这部分显存即可。
eLCDIF 接口
eLCDIF 是I.MX6U 自带的液晶屏幕接口,用于连接RGB LCD 接口的屏幕,eLCDIF 接口特性如下:
①、支持RGB LCD 的DE 模式。
②、支持VSYNC 模式以实现高速数据传输。
③、支持ITU-R BT.656 格式的4:2:2 的YCbCr 数字视频,并且将其转换为模拟TV 信号。
④、支持8/16/18/24/32 位LCD。
eLCDIF 支持三种接口:MPU 接口、VSYNC 接口和DOTCLK 接口,这三种接口区别如下:
1、MPU 接口
MPU 接口用于在I.MX6U 和LCD 屏幕直接传输数据和命令,这个接口用于6080/8080 接口的LCD 屏幕,比如我们学习STM32 的时候常用到的MCU 屏幕。如果寄存器LCDIF_CTRL的位DOTCLK_MODE、DVI_MODE 和VSYNC_MODE 都为0 的话就表示LCDIF 工作在MPU接口模式。关于MPU 接口的详细信息以及时序参考《I.MX6ULL 参考手册》第2150 页的“34.4.6 MPU Interface”小节,本教程不使用MPU 接口。
2、VSYNC 接口
VSYNC 接口时序和MPU 接口时序基本一样,只是多了VSYNC 信号来作为帧同步,当LCDIF_CTRL 的位VSYNC_MODE 为1 的时候此接口使能。关于VSYNC 接口的详细信息请参考《I.MX6ULL 参考手册》第2152 页的“34.4.7 VSYNC Interface”小节,本教程不使用VSYNC接口。
3、DOTCLK 接口
DOTCLK 接口就是用来连接RGB LCD 接口屏幕的,它包括VSYNC、HSYNC、DOTCLK和ENABLE(可选的)这四个信号,这样的接口通常被称为RGB 接口。DOTCLK 接口时序如图24.1.2.1 所示:
图24.1.2.1 是不是和图24.1.1.6、图24.1.1.7 很类似,因为DOTCLK 接口就是连接RGB 屏幕的,本教程使用的就是DOTCLK 接口。
eLCDIF 要驱动起来RGB LCD 屏幕,重点是配置好上一小节讲解的那些时间参数即可,这个通过配置相应的寄存器就可以了,所以我们接下来看一下eLCDIF 接口的几个重要的寄存器,首先看一下LCDIF_CTRL 寄存器,此寄存器结构如图24.1.2.1 所示:
寄存器LCDIF_CTRL 用到的重要位如下:
SFTRST(bit31):eLCDIF 软复位控制位,当此位为1 的话就会强制复位LCD。
CLKGATE(bit30):正常运行模式下,此位必须为0!如果此位为1 的话时钟就不会进入到LCDIF。
BYPASS_COUNT(bit19):如果要工作在DOTCLK 模式的话就此位必须为1。
VSYNC_MODE(bit18):此位为1 的话LCDIF 工作在VSYNC 接口模式。
DOTCLK_MODE(bit17):此位为1 的话LCDIF 工作在DOTCLK 接口模式。
INPUT_DATA_SWIZZLE(bit15:14):输入数据字节交换设置,此位为0 的话不交换字节也就是小端模式;为1 的话交换所有字节,也就是大端模式;为2 的话半字交换;为3 的话在每个半字内进行字节交换。本章我们设置为0,也就是不使用字节交换。
CSC_DATA_SWIZZLE(bit13:12) :CSC 数据字节交换设置,交换方式和
INPUT_DATA_SWIZZLE 一样,本章设置为0,不使用字节交换。
LCD_DATABUS_WIDTH(bit11:10):LCD 数据总线宽度,为0 的话总线宽度为16 位;为1 的话总线宽度为8 位;为2 的话总线宽度为18 位;为3 的话总线宽度为24 位。本章我们使用24 位总线宽度。
WORD_LENGTH(bit9:8):输入的数据格式,也就是像素数据宽度,为0 的话每个像素16位;为1 的话每个像素8 位;为2 的话每个像素18 位;为3 的话每个像素24 位。
MASTER(bit5):为1 的话设置eLCDIF 工作在主模式。
DATA_FORMAT_16_BIT(bit3):当此位为1 并且WORD_LENGTH 为0 的时候像素格式为ARGB555,当此位为0 并且WORD_LENGTH 为0 的时候像素格式为RGB565。
DATA_FORMAT_18_BIT(bit2):只有当WORD_LENGTH 为2 的时候此位才有效,此位为0 的话低18 位有效,像素格式为RGB666,高14 位数据无效。当此位为1 的话高18 位有效,像素格式依旧是RGB666,但是低14 位数据无效。
DATA_FORMAT_24_BIT(bit1):只有当WORD_LENGTH 为3 的时候此位才有效,为0 的时候表示全部的24 位数据都有效。为1 的话实际输入的数据有效位只有18 位,虽然输入的是24 位数据,但是每个颜色通道的高2 位数据会被丢弃掉。
RUN(bit0):eLCDIF 接口运行控制位,当此位为1 的话eLCDIF 接口就开始传输数据,也就是eLCDIF 的使能位。
接下来看一下寄存器LCDIF_CTRL1 ,此寄存器我们只用到位
BYTE_PACKING_FORMAT(bit19:16),此位用来决定在32 位的数据中哪些字节的数据有效,默认值为0XF,也就是所有的字节有效,当为0 的话表示所有的字节都无效。如果显示的数据是24 位(ARGB 格式,但是A 通道不传输)的话就设置此位为0X7。
接下来看一下寄存器LCDIF_TRANSFER_COUNT,这个寄存器用来设置所连接的RGB LCD 屏幕分辨率大小,此寄存器结构如图24.1.2.2 所示:
寄存器LCDIF_TRANSFER_COUNT 分为两部分,高16 位和低16 位,高16 位是V_COUNT,是LCD 的垂直分辨率。低16 位是H_COUNT,是LCD 的水平分辨率。如果LCD 分辨率为1024*600 的话,那么V_COUNT 就是600,H_COUNT 就是1024。
接下来看一下寄存器LCDIF_VDCTRL0,这个寄存器是VSYNC 和DOTCLK 模式控制寄存器0,寄存器结构如图24.1.2.3 所示:
寄存器LCDIF_VDCTRL0 用到的重要位如下:
VSYNC_OEB(bit29):VSYNC 信号方向控制位,为0 的话VSYNC 是输出,为1 的话VSYNC 是输入。
ENABLE_PRESENT(bit28):EBABLE 数据线使能位,也就是DE 数据线。为1 的话使能ENABLE 数据线,为0 的话关闭ENABLE 数据线。
VSYNC_POL(bit27):VSYNC 数据线极性设置位,为0 的话VSYNC 低电平有效,为1 的话VSYNC 高电平有效,要根据所使用的LCD 数据手册来设置。
HSYNC_POL(bit26):HSYNC 数据线极性设置位,为0 的话HSYNC 低电平有效,为1 的话HSYNC 高电平有效,要根据所使用的LCD 数据手册来设置。
DOTCLK_POL(bit25):DOTCLK 数据线(像素时钟线CLK) 极性设置位,为0 的话下降沿锁存数据,上升沿捕获数据,为1 的话相反,要根据所使用的LCD 数据手册来设置。
ENABLE_POL(bit24):EANBLE 数据线极性设置位,为0 的话低电平有效,为1 的话高电平有效。
VSYNC_PERIOD_UNIT(bit21):VSYNC 信号周期单位,为0 的话VSYNC 周期单位为像素时钟。为1 的话VSYNC 周期单位是水平行,如果使用DOTCLK 模式话就要设置为1。
VSYNC_PULSE_WIDTH_UNIT(bit20) :VSYNC 信号脉冲宽度单位,和
VSYNC_PERIOD_UNUT 一样,如果使用DOTCLK 模式的话要设置为1。
VSYNC_PULSE_WIDTH(bit17:0):VSPW 参数设置位。
接下来看一下寄存器LCDIF_VDCTRL1,这个寄存器是VSYNC 和DOTCLK 模式控制寄存器1,此寄存器只有一个功能,用来设置VSYNC 总周期,就是:屏幕高度+VSPW+VBP+VFP。
接下来看一下寄存器LCDIF_VDCTRL2,这个寄存器分为高16 位和低16 位两部分,高16位是HSYNC_PULSE_WIDTH,用来设置HSYNC 信号宽度,也就是HSPW。低16 位是HSYNC_PERIOD,设置HSYNC 总周期,就是:屏幕宽度+HSPW+HBP+HFP。
接下来看一下寄存器LCDIF_VDCTRL3,此寄存器结构如图24.1.2.4 所示:
寄存器LCDIF_VDCTRL3 用到的重要位如下:
HORIZONTAL_WAIT_CNT(bit27:16):此位用于DOTCLK 模式,用于设置HSYNC 信号产生到有效数据产生之间的时间,也就是HSPW+HBP。
VERTICAL_WAIR_CNT(bit15:0):和HORIZONTAL_WAIT_CNT 一样,只是此位用于VSYNC 信号,也就是VSPW+VBP。
接下来看一下寄存器LCDIF_VDCTRL4,此寄存器结构如图24.1.2.5 所示:
寄存器LCDIF_VDCTRL4 用到的重要位如下:
SYNC_SIGNALS_ON(bit18):同步信号使能位,设置为1 的话使能VSYNC、HSYNC、DOTCLK 这些信号。
DOTCLK_H_VALID_DATA_CNT(bit15:0):设置LCD 的宽度,也就是水平像素数量。
最后在看一下寄存器LCDIF_CUR_BUF 和LCDIF_NEXT_BUF,这两个寄存器分别为当前帧和下一帧缓冲区,也就是LCD 显存。一般这两个寄存器保存同一个地址,也就是划分给LCD的显存首地址。
关于eLCDIF 接口的寄存器就介绍到这里,关于这些寄存器详细的描述,请参考《I.MX6ULL参考手册》第2165 页的34.6 小节。本章我们使用I.MX6U 的eLCDIF 接口来驱动ALIENTEK的ATK7016 这款屏幕,配置步骤如下:
1、初始化LCD 所使用的IO
首先肯定是初始化LCD 所示使用的IO,将其复用为eLCDIF 接口IO。
2、设置LCD 的像素时钟
查阅所使用的LCD 屏幕数据手册,或者自己计算出的时钟像素,然后设置CCM 相应的寄存器。
3、配置eLCDIF 接口
设置LCDIF 的寄存器CTRL、CTRL1、TRANSFER_COUNT、VDCTRL0~4、CUR_BUF 和NEXT_BUF。根据LCD 的数据手册设置相应的参数。
4、编写API 函数
驱动LCD 屏幕的目的就是显示内容,所以需要编写一些基本的API 函数,比如画点、画线、画圆函数,字符串显示函数等。
硬件原理分析
本试验用到的资源如下:
①、指示灯LED0。
②、RGB LCD 接口。
③、DDR3
④、eLCDIF
RGB LCD 接口在I.MX6U-ALPHA 开发板底板上,原理图如图24.2.1 所示:
图24.2.1 中三个SGM3157 的目的是在未使用RGBLCD 的时候将LCD_DATA7、LCD_DATA15 和LCD_DATA23 这三个线隔离开来,因为ALIENTEK 的屏幕的LCD_R7/G7/B7这几个线用来设置LCD 的ID,所以这几根线上有上拉/下拉电阻。但是I.MX6U 的BOOT 设置也用到了LCD_DATA7、LCD_DATA15 和LCD_DATA23 这三个引脚,所以接上屏幕以后屏幕上的ID 电阻就会影响到BOOT 设置,会导致代码无法运行,所以先将其隔离开来,如果要使用RGB LCD 屏幕的时候再通过LCD_DE 将其“连接”起来。我们需要40P 的FPC 线将ATK7016屏幕和I.MX6U-ALPHA 开发板连接起来,如图24.2.2 所示:
实验程序编写
本实验对应的例程路径为:开发板光盘-> 1、裸机例程-> 15_lcd。
本章实验在上一章例程的基础上完成,更改工程名字为“lcd”,然后在bsp 文件夹下创建名为“lcd”的文件夹,在bsp/lcd 中新建bsp_lcd.c、bsp_lcd.h、bsp_lcdapi.c、bsp_lcdapi.h 和font.h这五个文件。bsp_lcd.c 和bsp_lcd.h 是LCD 的驱动文件,bsp_lcdapi.c 和bsp_lcdapi.h 是LCD 的API 操作函数文件,font.h 是字符集点阵数据数组文件。在bsp_lcd.h 中输入如下内容:
1 #ifndef _BSP_LCD_H
2 #define _BSP_LCD_H
3 /***************************************************************
4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
5 文件名: bsp_lcd.h
6 作者: 左忠凯
7 版本: V1.0
8 描述: LCD驱动文件头文件。
9 其他: 无
10 论坛: www.openedv.com
11 日志: 初版V1.0 2019/1/3 左忠凯创建
12 ***************************************************************/
13 #include "imx6ul.h"
14
15 /* 颜色宏定义*/
16 #define LCD_BLUE 0x000000FF
17 #define LCD_GREEN 0x0000FF00
18 #define LCD_RED 0x00FF0000
19 /* 省略掉其它宏定义,完整的请参考实验例程*/
在文件bsp_lcd.h 中一开始定义了一些常用的颜色宏定义,颜色格式都是ARGB8888 的。
第23 行的宏LCD_FRAMEBUF_ADDR 是显存首地址,此处将显存首地址放到了0X89000000地址处。这个要根据所使用的LCD 屏幕大小和DDR 内存大小来确定的,前面我们说了ATK7016这款RGB 屏幕所需的显存大小为2.4MB,而I.MX6U-ALPHA 开发板配置的DDR 有256 和512MB 两种类型,内存地址范围分别为0X80000000~ 0X90000000 和0X80000000~0XA0000000。所以LCD 显存首地址选择为0X89000000 不管是256MB 还是512MB 的DDR 都可以使用。
第26 行的结构体tftlcd_typedef 是RGB LCD 的控制参数结构体,里面包含了跟LCD 配置有关的一些成员变量。最后就是一些变量和函数是声明。
在bsp_lcd.c 中输入如下内容:
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名: bsp_lcd.c
作者: 左忠凯
版本: V1.0
描述: LCD驱动文件。
其他: 无
论坛: www.openedv.com
日志: 初版V1.0 2019/1/3 左忠凯创建
***************************************************************/
1 #include "bsp_lcd.h"
2 #include "bsp_gpio.h"
3 #include "bsp_delay.h"
4 #include "stdio.h"
5
6 /* 液晶屏参数结构体*/
7 struct tftlcd_typedef tftlcd_dev;
8
9 /*
10 * @description : 始化LCD
11 * @param : 无
12 * @return : 无
13 */
14 void lcd_init(void)
15
16 lcdgpio_init(); /* 初始化IO */
17 lcdclk_init(32, 3, 5); /* 初始化LCD时钟*/
18
19 lcd_reset(); /* 复位LCD */
20 delayms(10); /* 延时10ms */
21 lcd_noreset(); /* 结束复位*/
22
23 /* RGB LCD参数结构体初始化*/
24 tftlcd_dev.height = 600; /* 屏幕高度*/
25 tftlcd_dev.width = 1024; /* 屏幕宽度*/
文件bsp_lcd.c 里面一共有10 个函数,第一个函数是lcd_init,这个是LCD 初始化函数,此函数先调用LCD 的IO 初始化函数、时钟初始化函数、复位函数等,然后会按照我们前面讲解的步骤初始化eLCDIF 相关的寄存器,最后使能eLCDIF。第二个函数是lcdgpio_init,这个是LCD 的IO 初始化函数。第三个函数lcdclk_init 是LCD 的时钟初始化函数。第四个函数lcd_reset和第五个函数lcd_noreset 分别为复位LCD 的停止LCD 复位函数。第六个函数lcd_enable 是eLCDIF 使能函数,用于使能eLCDIF。第七个和第八个是画点和读点函数,分别为lcd_drawpoint和lcd_readpoint,通过这两个函数就可以在LCD 的指定像素点上显示指定的颜色,或者读取指定像素点的颜色。第九个函数lcd_clear 是清屏函数,使用指定的颜色清除整个屏幕。最后一个
函数lcd_fill 是填充函数,使用此函数的时候需要指定矩形的起始坐标、终止坐标和填充颜色,这样就可以填充出一个矩形区域。
在bsp_lcdapi.h 中输入如下所示内容:
1 #ifndef BSP_LCDAPI_H
2 #define BSP_LCDAPI_H
3 /***************************************************************
4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
5 文件名: bsp_lcdapi.h
6 作者: 左忠凯
7 版本: V1.0
8 描述: LCD显示API函数。
9 其他: 无
10 论坛: www.openedv.com
11 日志: 初版V1.0 2019/3/18 左忠凯创建
12 ***************************************************************/
13 #include "imx6ul.h"
14 #include "bsp_lcd.h"
15
16 /* 函数声明*/
17 void lcd_drawline(unsigned short x1, unsigned short y1, unsigned
short x2, unsigned short y2);
18 void lcd_draw_rectangle(unsigned short x1, unsigned short y1,
unsigned short x2, unsigned short y2);
19 void lcd_draw_circle(unsigned short x0,unsigned short y0,
unsigned char r);
20 void lcd_showchar(unsigned short x,unsigned short y,
unsigned char num,unsigned char size,
unsigned char mode);
21 unsigned int lcd_pow(unsigned char m,unsigned char n);
22 void lcd_shownum(unsigned short x, unsigned short y,
unsigned int num, unsigned char len,
unsigned char size);
23 void lcd_showxnum(unsigned short x, unsigned short y,
unsigned int num, unsigned char len,
unsigned char size, unsigned char mode);
24 void lcd_show_string(unsigned short x,unsigned short y,
unsigned short width, unsigned short height,
unsigned char size, char *p);
25 #endif
文件bsp_lcdapi.h 内容很简单,就是函数声明。在bsp_lcdapi.c 中输入如下内容:
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名: bsp_lcdapi.c
作者: 左忠凯
版本: V1.0
描述: LCD API函数文件。
其他: 无
论坛: www.openedv.com
日志: 初版V1.0 2019/3/18 左忠凯创建
***************************************************************/
1 #include "bsp_lcdapi.h"
2 #include "font.h"
3
4 /*
5 * @description : 画线函数
6 * @param - x1 : 线起始点坐标X轴
7 * @param - y1 : 线起始点坐标Y轴
8 * @param - x2 : 线终止点坐标X轴
9 * @param - y2 : 线终止点坐标Y轴
10 * @return : 无
11 */
12 void lcd_drawline(unsigned short x1, unsigned short y1,
unsigned short x2, unsigned short y2)
13
14 u16 t;
15 int xerr = 0, yerr = 0, delta_x, delta_y, distance;
16 int incx, incy, uRow, uCol;
17 delta_x = x2 - x1; /* 计算坐标增量*/
18 delta_y = y2 - y1;
19 uRow = x1;
20 uCol = y1;
21 if(delta_x > 0) incx = 1; /* 设置单步方向*/
22 else if(delta_x==0) incx = 0; /* 垂直线*/
23 else
24
25 incx = -1;
26 delta_x = -delta_x;
27
28
文件bsp_lcdapi.h 里面都是一些LCD 的API 操作函数,比如画线、画矩形、画圆、显示数字、显示字符和字符串等函数。这些函数都是从STM32 例程里面移植过来的,如果学习过ALIENTEK 的STM32 教程的话就会很熟悉,都是一些纯软件的东西。
lcd_showchar 函数是字符显示函数,要理解这个函数就得先了解一下字符(ASCII 字符集)在LCD 上的显示原理。要显示字符,我们先要有字符的点阵数据,ASCII 常用的字符集总共有95 个,从空格符开始,分别为:!"#$%&’()*+,-0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz|~.
我们先要得到这个字符集的点阵数据,这里我们介绍一个款很好的字符提取软件:PCtoLCD2002 完美版。该软件可以提供各种字符,包括汉字(字体和大小都可以自己设置)阵提取,且取模方式可以设置好几种,常用的取模方式,该软件都支持。该软件还支持图形模式,也就是用户可以自己定义图片的大小,然后画图,根据所画的图形再生成点阵数据,这功能在制作图标或图片的时候很有用。
该软件的界面如图24.3.1 所示:
然后我们点击字模选项按钮进入字模选项设置界面。设置界面中点阵格式和取模方式等参数配置如图24.3.2 所示:
上图设置的取模方式,在右上角的取模说明里面有,即:从第一列开始向下每取8 个点作为一个字节,如果最后不足8 个点就补满8 位。取模顺序是从高到低,即第一个点作为最高位。如*-------取为10000000。其实就是按如图24.3.3 所示的这种方式:
从上到下,从左到右,高位在前。我们按这样的取模方式,然后把ASCII 字符集按12 * 6 大小、16 * 8、24 * 12 和32 * 16 大小取模出来(对应汉字大小为12 * 12、16 * 16、24 * 24 和32 * 32,字符的只有汉字的一半大!)。将取出的点阵数组保存在font.h 里面,每个12 * 6 的字符占用12 个字节,每个16 * 8 的字符占用16 个字节,每个24 * 12 的字符占用36 个字节,每个32 * 16 的字符占用64 个字节。font.h 中的字符集点阵数据数组asc2_1206、asc2_1608、asc2_2412 和asc2_3216就对应着这四个大小字符集,具体见font.h 部分代码(该部分我们不再这里列出来了,请大家参考光盘里面的代码)。
最后在main.c 中输入如下所示内容:
/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名: main.c
作者: 左忠凯
版本: V1.0
描述: I.MX6U开发板裸机实验16 LCD液晶屏实验
其他: 本实验学习如何在I.MX6U上驱动RGB LCD液晶屏幕,I.MX6U有个
ELCDIF接口,通过此接口可以连接一个RGB LCD液晶屏。
论坛: www.openedv.com
日志: 初版V1.0 2019/1/15 左忠凯创建
**************************************************************/
1 #include "bsp_clk.h"
2 #include "bsp_delay.h"
3 #include "bsp_led.h"
4 #include "bsp_beep.h"
5 #include "bsp_key.h"
6 #include "bsp_int.h"
7 #include "bsp_uart.h"
8 #include "stdio.h"
9 #include "bsp_lcd.h"
10 #include "bsp_lcdapi.h"
11
12
13 /* 背景颜色数组*/
第37 行调用函数lcd_init 初始化LCD。
第39 行设置前景色,也就是画笔颜色为红色。
第40~44 行都是调用bsp_lcdapi.c 中的API 函数在LCD 上绘制各种图形和显示字符串。
第46 行的while 循环中每隔1S 中就调用函数lcd_fill 填充指定的区域,并且显示index值。
main 函数很简单,重点就是初始化LCD,然后调用LCD 的API 函数进行一些常用的操作,比如画线、画矩形、显示字符串和数字等等。
编译下载验证
编写Makefile 和链接脚本
修改Makefile 中的TARGET 为lcd,然后在INCDIRS 和SRCDIRS 中加入“bsp/lcd”,修改后的Makefile 如下:
1 CROSS_COMPILE ?= arm-linux-gnueabihf-
2 TARGET ?= lcd
3
4 /* 省略掉其它代码...... */
5
6 INCDIRS := imx6ul \\
7 stdio/include \\
8 bsp/clk \\
9 bsp/led \\
10 bsp/delay \\
11 bsp/beep \\
12 bsp/gpio \\
13 bsp/key \\
14 bsp/exit \\
15 bsp/int \\
16 bsp/epittimer \\
17 bsp/keyfilter \\
18 bsp/uart \\
19 bsp/lcd
20
21 SRCDIRS := project \\
22 stdio安卓复习8