一、RGB LCD驱动方式
首先RGB LCD 的驱动方式主要可以分为两种,一种是HV模式,一种是DE模式
这两种模式的区别:DE模式使用DE信号来确定有效数据,HV模式则需要行 (HSD信号线) 和场同步信号 (VSD信号线) 来表示扫描的行和列
DE模式下需要用到 DEN 信号线,HSD和VSD信号线可以不用接,此时没有用到
HV模式下用到 HSD 和 VSD 信号线,此时DEN信号线可以不接
上图为两个模式的区别
需要注意的是在DE模式下虽然没有用到 HSD 和 VSD 信号线,但是对应的水平前后廊即 thpw,thb , thfp这些参数还是需要设置的 (参数代表的意思可以看之后两张图) ,不设置的话,DEN有效信号的开始和结束就无法确定了,所以DE模式下,虽然没有了HSD 和 VSD 信号线,但是程序上还是需要对HSYNC和VSYNC的相应时序进行设置,而设置的参数就和HV模式下,HSYNC和VSYNC的时序配置一样就可以了,下图是我使用的屏幕使用DE模式的时序图
而HV模式下,HSD和VSD信号线就都有使用到了,所以在驱动 LTDC外设时,相应引脚的使能和参数配置就要按照规定设置上。下图是HV模式的时序图
当然还有一种模式,那就是SYNC-DE模式,顾名思义是把 HSD VSD 和 DEN信号线都用到的一种模式,时序图可以见下图
不同时序参数英文含义可以看下面两张图
二、LTDC 外设驱动参数设置
了解了模式区别后就是如何驱动起 LTDC外设了,而LTDC外设的参数又和驱动的屏幕有关,只有LTDC外设设置的参数与屏幕参数能对应上的时候,LTDC才能真正驱动屏幕进行显示嘛,我这里驱动的是正点原子的4.3寸RGB 屏幕(480 x 272分辨率的)
这是我所驱动的屏幕的时序参数,真正驱动自己的RGB屏幕时,需要用自己驱动屏幕的相关参数,可以看到对于我驱动的屏幕,DCLK的频率一般是建议9Mhz的
1、时钟选择和配置
LTDC 驱动 RGB LCD 首先需要配置好屏幕的时钟参数,这个时钟参数可以决定刷屏速度的,如果觉得屏幕刷屏刷的慢,后续可以把这个时钟频率设置大一点,不过一般太快可能也会导致一些问题,具体得根据实际情况进行调试,上图显示通过分屏配置后进入到 PLLSAI 的时钟是1M,我是基于这1M来进行后面的时钟配置的,1M通过 xN 和 /R 得到 PLLLCDCLK 再通过 DIV 分频 最终得到屏幕所需的驱动时钟 LCD-TFT clock
在程序上是如何配置RGB屏幕的时钟呢?可以看下图
需要先定义一个相关结构体配置 N R DIV这三个参数,使得最终通往LCD-TFT clock符合屏幕要求
具体计算如下:LCD-TFT clock = 1 * 288 / 4 / 8 = 9 mhz;
这样,RGB LCD的时钟频率就设置好了
2、信号极性
确定好了时钟,接下来就是信号的极性了,信号极性也决定了图像是否能正常传输,那怎么知道对应信号引脚是什么电平有效呢,这个也是需要看屏幕资料的
这是在HV模式下需要设置的时序图
可以看到要设置HV模式下,几个信号是高电平还是低电平有效可以通过设置VDPOL、HDPOL和DCLKPOL这几个寄存器来决定,比如VDPOL,如果将这个寄存器设置 “High” 即 1的时候,VSYNC就是低电平即下降沿的时候表示一帧数据的开始有效的意思,最后一句是指当没有使用到这个寄存器,我们也应该将这个寄存器连接到 “High”。
像素时钟极性同理,所以可以知道也是低电平有效,剩下的就是DE信号线了
因为我选择的是DE模式,所以实际设置参数可以看这个图进行设置,通过测试,虽然图片上显示DE是高电平有效,但实际需要把DE的极性设置为低电平,才能正常显示
接下来时钟和极性都确定了,之后就是对屏幕的HSYNC和VSYNC的时序进行配置了,具体设置什么可以参考这一节最开始的屏幕参数表
这是我驱动的屏幕的参数,就按照裸屏手册建议进行配置就可以了,上面的Backcolor参数是设置背景层颜色的,只要设置的时钟、极性和时序参数没问题,将Backcolor.Blue设置成0xFF再调用下面的HAL_LTDC_Init () 就可以看到现象(屏幕显示蓝色)了
好了,到这里,屏幕显示背景层颜色就证明LTDC外设的设置都没有问题了
3、图层设置
图层的设置最主要一个开窗大小,还有一个就是显示的缓存地址,我们显示就是把缓存地址下的数据传输到屏幕上进行显示的,还有一个就是混合系数,这个系数能设置和背景层颜色进行混合,混合效果计算可以参考手册,如下图
我这里就没用到这么高级的混合显示,所以使用恒定Alpha,没有使用到像素Alpha,分频直接设置为255
设置BF1为100即选择恒定Alpha,没有使用像素Alpha
而图层缓冲区我使用的是外部sdram,所以需要先申请sdram的数据空间,缓冲区的地址就可以选用刚刚我申请的外部sdram的地址,至于关于sdram的初始化,就不在这里说明了,可以看看其他sdram的教程
上面的attribute是关键字,表示申请一个数组,地址在0xC0000000里,framebuf是一个指针数组,数组大小为1,表示这个数组里存放了一个uint32_t 大小的一个指针
上面的BlendingFactor1需要左移8位,可以看看前面的BFCR寄存器,然后里面的WindowX1和WindowY1可以设置不同于LTDC显示的像素大小的,这个开窗大小只要设置小于等于LTDC初始化时的480和272这个像素宽和高参数就可以的,接下来是ImageWidth和ImageHeight,这两个参数只有在调用HAL_LTDC_SetWindowPosition(); 这个函数的时候才会真正赋值进去
不调用的话,初始化可以不设置的,不过为了保证后续能正常设置还是建议设置上
还有一个函数是和HAL_LTDC_SetWindowPosition() 结合一起使用的,那就是HAL_LTDC_SetWindowSize() 这个函数
可以看到这个函数其实就是设置ImageWidth和ImageHeight的,这个函数负责设置开窗的大小,而HAL_LTDC_SetWindowPosition()则是负责设置开窗的位置,这样关于图层的设置就可以了。
4、DMA2D搬运数据
图层在上一节也成功设置好了,之后就要进行数据搬运了,为了让图片能更快的显示,这时我们就可以又要用另一个外设 DMA2D,DMA2D可以非常方便的把我们需要的数据不经过CPU而直接搬运到目的地址,进而让图片能更快显示。
DMA2D主要分为下面几种模式
一般我们就用到两种模式,存储器到存储器,寄存器到存储器,其中存储器到存储器主要用于图片的搬运和传输,而寄存器到存储器则是主要负责单片填充,即只要显示单调的一个颜色时可以选寄存器到存储器的模式,而进行图片显示的时候就选择存储器到存储器的模式
DMA2D最主要设置的寄存器可以看下图
这里我们直接看程序是怎么配置寄存器的,其实也可以使用HAL库提供的函数来对DMA2D外设初始化和调用,但因为HAL库会有比较多断言和回调导致传输速率稍微慢一点,所以我们直接对寄存器进行设置和操作,另外一个重点就是定位,想让屏幕在特定的地方进行相应显示,那就要先确定需要在sdram的哪里进行画点即进行颜色填充。
可以看到当屏幕是以横轴进行显示的时候,原点是在左上角的,而当,切换为竖屏后,原点会变为左下角,当选择不同的显示方向时,定位到sdram的位置也是不一样的。需要注意的是切换成竖屏之后,坐标原点改变了,但是sdram的开始地址还是一样的,所以这时候在sdram上画点的坐标就需要进行一个计算,而不能直接用x和y坐标了,横竖屏的坐标系是不一样的,最终这些坐标参数都需要转换到对于sdram来说才可以,而sdram的起始坐标又和横屏显示的坐标是一样的。
坐标都以0作为起始点的
了解如何将对应坐标的数据转换为sdram的存储位置后,就可以开始编写程序了
我是以横屏进行显示的,所以这里根据x,y进行偏移并写入到sdram对应地址就是用横屏的计算公式
一开始需要先将DMA2D进行一个失能操作,3 << 16位就是把DMA2D的传输方式配置成寄存器到存储器的模式,OMAR寄存器是配置我们要传输到的目的地址,这里我们是把图像数据搬运到sdram的对应区域内,所以就用计算好的addr作为目的地址,接下来就是NLR寄存器
可以看到时设置行数和像素数的而行像素数实在16-29位,所以需要将数据进行16位的偏移
然后就是设置OOR寄存器,其实就是图中黄色标注的区域
之后的OCOLR和OPFCCR是设置输出的颜色和颜色格式的,配置好之后可以使能DMA2D,开始数据传输,再传输的时候我们可以通过TC标志位,确认传输完成,等待TC标志位置一,如果在显示多张纯色图片切换的时候出现只显示一张后就无法进行切换,那有可能就是卡在了这个判断TC标志位这里了,可以选择在这里加个计时变量,大于某一个数值就break退出。
像这样定义一个超时变量,然后在while里自增,防止函数卡在这里,导致显示也卡住,最后就是清除这个传输完成的标志位了,这个可以通过IFCR寄存器来做清除操作。
可以看到只要让这个寄存器的位1置一就能清除传输完成标志位。
这样就成功地用DMA2D将纯色的颜色搬运到了我们图层显示所用到的sdram里了,那sdram里有了数据,图像就能正常显示了。
对上面的函数进行封装调用,这样就能在屏幕上显示对应的颜色了
Tips:如果在对GPIO初始化的时候,漏掉了某一条数据线的初始化会出现下面的现象
上图为漏掉某个数据线GPIO口初始化,显示的效果
上图则为没有漏掉数据线GPIO口初始化,正常显示的图片