使用以下结构体的具体原理需要参考LTDC-DMA2D液晶显示(一)
一、LTDC初始化结构体
typedef struct
{
uint32_t LTDC_HSPolarity; //配置行同步信号HSYNC的极性
uint32_t LTDC_VSPolarity; //配置垂直同步信号VSYNC的极性
uint32_t LTDC_DEPolarity; //配置数据使能信号DE的极性
uint32_t LTDC_PCPolarity; //配置像素时钟信号CLK的极性
uint32_t LTDC_HorizontalSync; //配置行同步信号HSYNC的宽度(HSW-1)
uint32_t LTDC_VerticalSync; //配置垂直同步信号VSYNC的宽度(VSW-1)
uint32_t LTDC_AccumulatedHBP; //配置(HSW+HBP-1)的值
uint32_t LTDC_AccumulatedVBP; //配置(VSW+VBP-1)的值
uint32_t LTDC_AccumulatedActiveW; //配置(HSW+HBP+有效宽度-1)的值
uint32_t LTDC_AccumulatedActiveH; //配置(VSW+VBP+有效高度-1)的值
uint32_t LTDC_TotalWidth; //配置(HSW+HBP+有效宽度+HFP-1)的值
uint32_t LTDC_TotalHeigh; //配置(VSW+VBP+有效高度+HFP-1)的值
uint32_t LTDC_BackgroundRedValue; //配置红色背景值
uint32_t LTDC_BackgroundGreenValue; //配置绿色背景值
uint32_t LTDC_BackgroundBlueValue; //配置蓝色背景值
} LTDC_InitTypeDef;
-
LTDC_HSPolarity 本成员用于设置行同步信号HSYNC的极性,即HSYNC有效时的电平,该成员的值可设置为高电平(LTDC_HSPolarity_AH)或低电平(LTDC_HSPolarity_AL)。 -
LTDC_VSPolarity 本成员用于设置垂直同步信号VSYNC的极性,可设置为高电平(LTDC_VSPolarity_AH)或低电平(LTDC_VSPolarity_AL)。 -
LTDC_DEPolarity 本成员用于设置数据使能信号DE的极性,可设置为高电平(LTDC_DEPolarity_AH)或低电平(LTDC_DEPolarity_AL)。 -
LTDC_PCPolarity 本成员用于设置像素时钟信号CLK的极性,可设置为上升沿(LTDC_PCPolarity_IPC)或下降沿(LTDC_PCPolarity_IIPC),表示RGB数据信号在CLK的哪个时刻被采集。 -
LTDC_HorizontalSync 本成员设置行同步信号HSYNC的宽度HSW,它以像素时钟CLK的周期为单位,实际写入该参数时应写入(HSW-1),参数范围为0x000- 0xFFF。
??
?
?
本成员用于配置“垂直同步行VSW”加“垂直后沿行VBP”的累加值,实际写入该参数时应写入(VSW+VBP-1) ,参数范围为0x000- 0x7FF。
?
?配置所需要时间:
?
本成员用于配置“垂直同步行VSW”加“垂直后沿行VBP”加“有效行”的累加值,实际写入该参数时应写入(VSW+VBP+有效高度-1) ,参数范围为0x000- 0x7FF。
本成员用于配置“水平同步像素HSW”加“水平后沿像素HBP”加“有效像素”加“水平前沿像素HFP”的累加值,即总宽度,实际写入该参数时应写入(HSW+HBP+有效宽度+HFP-1) ,参数范围为0x000- 0xFFF。
本成员用于配置“垂直同步行VSW”加“垂直后沿行VBP”加“有效行”加“垂直前沿行VFP”的累加值,即总高度,实际写入该参数时应写入(HSW+HBP+有效高度+VFP-1) ,参数范围为0x000- 0x7FF
-
LTDC_BackgroundRedValue/GreenValue/ BlueValue 这三个结构体成员用于配置背景的颜色值,这里说的背景层与前面提到的“前景层/背景层”概念有点区别,它们对应下图中的“第2层/第1层”,而在这两层之外,还有一个最终的背景层,当第1第2层都透明时,这个背景层就会被显示,而这个背景层是一个纯色的矩形,它的颜色值就是由这三个结构体成员配置的,各成员的参数范围为0x00- 0xFF。
?
?
对这些LTDC初始化结构体成员赋值后,调用库函数LTDC_Init可把这些参数写入到LTDC的各个配置寄存器,LTDC外设根据这些配置控制时序。
二、LTDC层级初始化结构体
LTDC初始化结构体只是配置好了与液晶屏通讯的基本时序,还有像素格式、显存地址等诸多参数需要使用LTDC层级初始化结构体完成。
typedef struct
{
uint32_t LTDC_HorizontalStart; //配置窗口起始行位置
uint32_t LTDC_HorizontalStop; //配置窗口的行结束位置
uint32_t LTDC_VerticalStart; //配置窗口的垂直起始位置
uint32_t LTDC_VerticalStop; //配置窗口的垂直束位置
uint32_t LTDC_PixelFormat; //配置当前层的像素格式
uint32_t LTDC_ConstantAlpha; //配置当前层的透明度Alpha常量
uint32_t LTDC_DefaultColorBlue; //配置当前层的默认蓝色值
uint32_t LTDC_DefaultColorGreen; //配置当前层的默认绿色值
uint32_t LTDC_DefaultColorRed; //配置当前层的默认红色值
uint32_t LTDC_DefaultColorAlpha; //配置当前层的默认透明值
uint32_t LTDC_BlendingFactor_1; //配置混合因子
uint32_t LTDC_BlendingFactor_2;
uint32_t LTDC_CFBStartAdress; //配置当前层的显存起始位置
uint32_t LTDC_CFBLineLength; //配置当前层的行数据长度
uint32_t LTDC_CFBPitch; //配置从某行的起始到下一行像素起始处的增量
uint32_t LTDC_CFBLineNumber; //配置当前层的行数
} LTDC_Layer_InitTypeDef;
?
这些成员用于确定该层显示窗口的边界,分别表示行起始、行结束、垂直起始及垂直结束的位置,注意这些参数包含同步HSW/VSW、后沿大小HBP/VBP和有效数据区域的内部时序发生器的配置,表中的是各个窗口配置成员应写入的数值。
?
?
LTDC层级窗口配置成员 | 等效于LTDC时序参数配置成员的值 | 实际值 |
---|
LTDC_HorizontalStart | (LTDC_AccumulatedHBP+1) | HBP + HSW | LTDC_HorizontalStop | LTDC_AccumulatedActiveW | HSW+HBP+LCD_PIXEL_WIDTH-1 | LTDC_VerticalStart | (LTDC_AccumulatedVBP+1) | VBP + VSW | LTDC_VerticalStop | LTDC_AccumulatedActiveH | VSW+VBP+LCD_PIXEL_HEIGHT-1 |
-
LTDC_PixelFormat 本成员用于设置该层数据的像素格式,可以设置为LTDC_Pixelformat_ARGB8888/ RGB888/ RGB565/ ARGB1555/ ARGB4444/ L8/ AL44/ AL88格式。(不透明的话底层没有意义) -
LTDC_ConstantAlpha 本成员用于设置该层恒定的透明度常量Alpha,称为恒定Alpha,参数范围为0x00-0xFF,在图层混合时,可根据后面的BlendingFactor成员的配置,选择是只使用这个恒定Alpha进行混合运算还是把像素本身的Alpha值也加入到运算中。 -
LTDC_DefaultColorBlue/Green/Red/Alpha 这些成员用于配置该层的默认颜色值,分别为蓝、绿、红及透明分量,该颜色在定义的层窗口外或在层禁止时使用。 -
LTDC_BlendingFactor_1/2 本成员用于设置混合系数 BF1 和 BF2。每一层实际显示的颜色都需要使用透明度参与运算,计算出不包含透明度的直接RGB颜色值,然后才传输给液晶屏(因为液晶屏本身没有透明的概念)。混合的计算公式为: BC = BF1 x C + BF2 x Cs
参数 | 说明 | CA | PAxCA |
---|
BC | 混合后的颜色(混合结果) | - | - | C | 当前层颜色 | - | - | Cs | 底层混合后的颜色 | - | - | BF1 | 混合系数1 | 等于(恒定Alpha值) | 等于(恒定Alpha x 像素Alpha值) | BF2 | 混合系数2 | 等于(1-恒定Alpha) | 等于(1-恒定Alpha x 像素Alpha值) |
本结构体成员可以设置BF1/BF2参数使用CA配置(LTDC_BlendingFactor1/2_CA)还是PAxCA配置(LTDC_BlendingFactor1/2_PAxCA)。配置成CA表示混合系数中只包含恒定的Alpha值,即像素本身的Alpha不会影响混合效果(比如第一层与背景层,我们不希望看到背景层),若配置成PAxCA,则混合系数中包含有像素本身的Alpha值,即把像素本身的Alpha加入到混合运算中(比如第二层与第一层,我们希望第一层与第二层混合)。其中的恒定Alpha值即前面“LTDC_ConstantAlpha”结构体配置参数的透明度百分比:(配置的Alpha值/0xFF)。
Example:数据源混合时,由下至上,如果使用了2层,则先将第1层与LTDC背景混合,随后再使用该混合颜色与第2层混合得到最终结果。例如,当只使用第1层数据源时,且BF1及BF2都配置为使用恒定Alpha,该Alpha值在LTDC_ConstantAlpha结构体成员值中被配置为240(0xF0)。因此,恒定Alpha值为240/255=0.94。若当前层颜色C=128,背景色Cs=48,那么第1层与背景色的混合结果为:
BC=恒定Alpha x C + (1- 恒定Alpha) x Cs=0.94 x Cs +(1-0.94)x 48=123
-
LTDC_CFBStartAdress 本成员用于设置该层的显存首地址,该层的像素数据保存在从这个地址开始的存储空间内。 -
LTDC_CFBLineLength 本成员用于设置当前层的行数据长度,即每行的有效像素点个数x每个像素的字节数,实际配置该参数时应写入值(行有效像素个数x每个像素的字节数+3),每个像素的字节数跟像素格式有关,如RGB565为2字节,RGB888为3字节,ARGB8888为4字节。 -
LTDC_CFBPitch 本成员用于设置从某行的有效像素起始位置到下一行起始位置处的数据增量,无特殊情况的话,它一般就直接等于行的有效像素个数x每个像素的字节数。 -
LTDC_CFBLineNumber 本成员用于设置当前层的显示行数。
配置完LTDC_Layer_InitTypeDef层级初始化结构体后,调用库函数LTDC_LayerInit可把这些配置写入到LTDC的层级控制寄存器中,完成初始化。初始化完成后LTDC会不断把显存空间的数据传输到液晶屏进行显示,我们可以直接修改或使用DMA2D修改显存中的数据,从而改变显示的内容。
三、DMA2D结构体
typedef struct
{
uint32_t DMA2D_Mode; //配置DMA2D的传输模式
uint32_t DMA2D_CMode; //配置DMA2D的颜色模式
uint32_t DMA2D_OutputBlue; //配置输出图像的蓝色分量
uint32_t DMA2D_OutputGreen; //配置输出的绿色分量
uint32_t DMA2D_OutputRed; //配置输出的红色分量
uint32_t DMA2D_OutputAlpha; //配置输出的透明度分量
uint32_t DMA2D_OutputMemoryAdd; //配置显存地址
uint32_t DMA2D_OutputOffset; //配置输出地址的偏移
uint32_t DMA2D_NumberOfLine; //配置要传输多少行
uint32_t DMA2D_PixelPerLine; //配置每行有多少个像素
} DMA2D_InitTypeDef;
宏 | 说明 |
---|
DMA2D_M2M | 从存储器到存储器(仅限FG获取数据源) | DMA2D_M2M_PFC | 存储器到存储器并执行 PFC(仅限 FG PFC 激活时的 FG 获取) | DMA2D_M2M_BLEND | 存储器到存储器并执行混合(执行 PFC 和混合时的 FG 和 BG 获取) | DMA2D_R2M | 寄存器到存储器(无 FG 和 BG,仅输出阶段激活) |
这几种工作模式主要区分数据的来源、是否使能PFC以及是否使能混合器。
若使能了PFC,则存储器中的数据源会经过转换再传输到显存。若使能了混合器,DMA2D会把两个数据源中的数据混合后再输出到显存。
若使用存储器到存储器模式,需要调用库函数DMA2D_FGConfig,使用初始化结构体DMA2D_FG_InitTypeDef配置数据源的格式、地址等参数。(背景层使用函数DMA2D_BGConfig和结构体DMA2D_BG_InitTypeDef)。
-
DMA2D_CMode 本成员用于配置DMA2D的输出PFC颜色格式,即它将要传输给显存的格式。 -
DMA2D_OutputBlue/ Green/ Red/ Alpha 这几个成员用于配置DMA2D的寄存器颜色值,若DMA2D工作在“寄存器到存储器”(DMA2D_R2M)模式时,这个颜色值作为数据源,被DMA2D复制到显存空间,即目标空间都会被填入这一种色彩。 -
DMA2D_OutputMemoryAdd 本成员用于配置DMA2D的输出FIFO的地址, DMA2D的数据会被搬运到该空间,一般把它设置为本次传输显示位置的起始地址。(适用于传输行图像) -
DMA2D_OutputOffset 本成员用于配置行偏移(以像素为单位),行偏移会被添加到各行的结尾,用于确定下一行的起始地址。如下表中的黄色格子表示行偏移,绿色格子表示要显示的数据。表1中显示的是一条垂直的线,且线的宽度为1像素,所以行偏移的值=7-1=6,即“行偏移的值=行宽度-线的宽度”,表2中的线宽度为2像素,行偏移的值=7-2=5。(适合传输列图像)
?
?
配置完这些结构体成员,调用库函数DMA2D_Init即可把这些参数写入到DMA2D的控制寄存器中,然后再调用DMA2D_StartTransfer函数开启数据传输及转换。
四、实战
(1)驱动SDRAM。在FMC的那一节已经配置好了。
(2)初始化LCD驱动引脚
(3)使用LTDC:两个结构体
(4)直接操控显存,控制液晶显示图形
(5)使用DMA2D快速绘制直线及矩形
-
初始化GPIO:
由于太多引脚了,只能选择一个引脚作为示例。
?需要注意的是在配置引脚时需要好好的看一下数据手册。
#define LTDC_R0_GPIO_PORT GPIOH
#define LTDC_R0_GPIO_CLK RCC_AHB1Periph_GPIOH
#define LTDC_R0_GPIO_PIN GPIO_Pin_2
#define LTDC_R0_PINSOURCE GPIO_PinSource2
#define LTDC_R0_AF GPIO_AF_LTDC
//下面两个引脚不需要复用 只需要控制其io口的输入输出方式即可
/*液晶屏使能信号DISP,高电平使能*/
#define LTDC_DISP_GPIO_PORT GPIOD
#define LTDC_DISP_GPIO_CLK RCC_AHB1Periph_GPIOD
#define LTDC_DISP_GPIO_PIN GPIO_Pin_4
/*液晶屏背光信号,高电平使能*/
#define LTDC_BL_GPIO_PORT GPIOD
#define LTDC_BL_GPIO_CLK RCC_AHB1Periph_GPIOD
#define LTDC_BL_GPIO_PIN GPIO_Pin_7
static void LCD_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* 使能LCD使用到的引脚时钟 */
//红色数据线
RCC_AHB1PeriphClockCmd(LTDC_R0_GPIO_CLK | LTDC_R1_GPIO_CLK | LTDC_R2_GPIO_CLK|
LTDC_R3_GPIO_CLK | LTDC_R4_GPIO_CLK | LTDC_R5_GPIO_CLK|
LTDC_R6_GPIO_CLK | LTDC_R7_GPIO_CLK |
//绿色数据线
LTDC_G0_GPIO_CLK|LTDC_G1_GPIO_CLK|LTDC_G2_GPIO_CLK|
LTDC_G3_GPIO_CLK|LTDC_G4_GPIO_CLK|LTDC_G5_GPIO_CLK|
LTDC_G6_GPIO_CLK|LTDC_G7_GPIO_CLK|
//蓝色数据线
LTDC_B0_GPIO_CLK|LTDC_B1_GPIO_CLK|LTDC_B2_GPIO_CLK|
LTDC_B3_GPIO_CLK|LTDC_B4_GPIO_CLK|LTDC_B5_GPIO_CLK|
LTDC_B6_GPIO_CLK|LTDC_B7_GPIO_CLK|
//控制信号线
LTDC_CLK_GPIO_CLK | LTDC_HSYNC_GPIO_CLK |LTDC_VSYNC_GPIO_CLK|
LTDC_DE_GPIO_CLK | LTDC_BL_GPIO_CLK |LTDC_DISP_GPIO_CLK ,ENABLE);
/* GPIO配置 */
/* 红色数据线 */
GPIO_InitStruct.GPIO_Pin = LTDC_R0_GPIO_PIN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(LTDC_R0_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R0_GPIO_PORT, LTDC_R0_PINSOURCE, LTDC_R0_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_R1_GPIO_PIN;
GPIO_Init(LTDC_R1_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R1_GPIO_PORT, LTDC_R1_PINSOURCE, LTDC_R1_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_R2_GPIO_PIN;
GPIO_Init(LTDC_R2_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R2_GPIO_PORT, LTDC_R2_PINSOURCE, LTDC_R2_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_R3_GPIO_PIN;
GPIO_Init(LTDC_R3_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R3_GPIO_PORT, LTDC_R3_PINSOURCE, LTDC_R3_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_R4_GPIO_PIN;
GPIO_Init(LTDC_R4_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R4_GPIO_PORT, LTDC_R4_PINSOURCE, LTDC_R4_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_R5_GPIO_PIN;
GPIO_Init(LTDC_R5_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R5_GPIO_PORT, LTDC_R5_PINSOURCE, LTDC_R5_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_R6_GPIO_PIN;
GPIO_Init(LTDC_R6_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R6_GPIO_PORT, LTDC_R6_PINSOURCE, LTDC_R6_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_R7_GPIO_PIN;
GPIO_Init(LTDC_R7_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_R7_GPIO_PORT, LTDC_R7_PINSOURCE, LTDC_R7_AF);
//绿色数据线
GPIO_InitStruct.GPIO_Pin = LTDC_G0_GPIO_PIN;
GPIO_Init(LTDC_G0_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G0_GPIO_PORT, LTDC_G0_PINSOURCE, LTDC_G0_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_G1_GPIO_PIN;
GPIO_Init(LTDC_G1_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G1_GPIO_PORT, LTDC_G1_PINSOURCE, LTDC_G1_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_G2_GPIO_PIN;
GPIO_Init(LTDC_G2_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G2_GPIO_PORT, LTDC_G2_PINSOURCE, LTDC_G2_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_G3_GPIO_PIN;
GPIO_Init(LTDC_G3_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G3_GPIO_PORT, LTDC_G3_PINSOURCE, LTDC_G3_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_G4_GPIO_PIN;
GPIO_Init(LTDC_G4_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G4_GPIO_PORT, LTDC_G4_PINSOURCE, LTDC_G4_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_G5_GPIO_PIN;
GPIO_Init(LTDC_G5_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G5_GPIO_PORT, LTDC_G5_PINSOURCE, LTDC_G5_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_G6_GPIO_PIN;
GPIO_Init(LTDC_G6_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G6_GPIO_PORT, LTDC_G6_PINSOURCE, LTDC_G6_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_G7_GPIO_PIN;
GPIO_Init(LTDC_G7_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_G7_GPIO_PORT, LTDC_G7_PINSOURCE, LTDC_G7_AF);
//蓝色数据线
GPIO_InitStruct.GPIO_Pin = LTDC_B0_GPIO_PIN;
GPIO_Init(LTDC_B0_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B0_GPIO_PORT, LTDC_B0_PINSOURCE, LTDC_B0_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_B1_GPIO_PIN;
GPIO_Init(LTDC_B1_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B1_GPIO_PORT, LTDC_B1_PINSOURCE, LTDC_B1_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_B2_GPIO_PIN;
GPIO_Init(LTDC_B2_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B2_GPIO_PORT, LTDC_B2_PINSOURCE, LTDC_B2_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_B3_GPIO_PIN;
GPIO_Init(LTDC_B3_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B3_GPIO_PORT, LTDC_B3_PINSOURCE, LTDC_B3_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_B4_GPIO_PIN;
GPIO_Init(LTDC_B4_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B4_GPIO_PORT, LTDC_B4_PINSOURCE, LTDC_B4_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_B5_GPIO_PIN;
GPIO_Init(LTDC_B5_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B5_GPIO_PORT, LTDC_B5_PINSOURCE, LTDC_B5_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_B6_GPIO_PIN;
GPIO_Init(LTDC_B6_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B6_GPIO_PORT, LTDC_B6_PINSOURCE, LTDC_B6_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_B7_GPIO_PIN;
GPIO_Init(LTDC_B7_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_B7_GPIO_PORT, LTDC_B7_PINSOURCE, LTDC_B7_AF);
//控制信号线
GPIO_InitStruct.GPIO_Pin = LTDC_CLK_GPIO_PIN;
GPIO_Init(LTDC_CLK_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_CLK_GPIO_PORT, LTDC_CLK_PINSOURCE, LTDC_CLK_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_HSYNC_GPIO_PIN;
GPIO_Init(LTDC_HSYNC_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_HSYNC_GPIO_PORT, LTDC_HSYNC_PINSOURCE, LTDC_HSYNC_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_VSYNC_GPIO_PIN;
GPIO_Init(LTDC_VSYNC_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_VSYNC_GPIO_PORT, LTDC_VSYNC_PINSOURCE, LTDC_VSYNC_AF);
GPIO_InitStruct.GPIO_Pin = LTDC_DE_GPIO_PIN;
GPIO_Init(LTDC_DE_GPIO_PORT, &GPIO_InitStruct);
GPIO_PinAFConfig(LTDC_DE_GPIO_PORT, LTDC_DE_PINSOURCE, LTDC_DE_AF);
//BL DISP
GPIO_InitStruct.GPIO_Pin = LTDC_DISP_GPIO_PIN;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(LTDC_DISP_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LTDC_BL_GPIO_PIN;
GPIO_Init(LTDC_BL_GPIO_PORT, &GPIO_InitStruct);
//拉高使能lcd
GPIO_SetBits(LTDC_DISP_GPIO_PORT,LTDC_DISP_GPIO_PIN); //显示开关
GPIO_SetBits(LTDC_BL_GPIO_PORT,LTDC_BL_GPIO_PIN); //驱动背光
}
-
初始化LTDC结构体,配置液晶屏的控制参数:
?
由图可得,HSYNC在低电平有效,所以配置低电平。
由图可得,VSYNC也应该于低电平有效:
?
HSD宽度可由下图可得:
??同理,对于VSD:
?最后需要定义的参数如下:
/*根据液晶数据手册的参数配置*/
#define HBP 46 //HSYNC后的无效像素
#define VBP 23 //VSYNC后的无效行数
#define HSW 1 //HSYNC宽度
#define VSW 1 //VSYNC宽度
#define HFP 20 //HSYNC前的无效像素
#define VFP 22 //VSYNC前的无效行数
LTDC初始化所需要的时钟:
输出给LTDC的时钟为PLLSAI。
需要使用下面这个函数进行配置:
?
?
比如在这里我们需要配置24Mhz的时钟,则由24MHz * DIV * R = N,其中DIV我们设置为4分频。
LTDC结构体初始化:
void LCD_Init(void)
{
LTDC_InitTypeDef LTDC_InitStruct;
/* 使能LTDC外设时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_LTDC, ENABLE);
/* 使能DMA2D时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2D, ENABLE);
/* 初始化LCD的控制引脚 */
LCD_GPIO_Config();
/* 初始化SDRAM,以便使用SDRAM作显存 */
SDRAM_Init();
/* 配置 PLLSAI 分频器,它的输出作为像素同步时钟CLK*/
/* PLLSAI_VCO 输入时钟 = HSE_VALUE/PLL_M = 1 Mhz */
/* PLLSAI_VCO 输出时钟 = PLLSAI_VCO输入 * PLLSAI_N = 416 Mhz */
/* PLLLCDCLK = PLLSAI_VCO 输出/PLLSAI_R = 420/6 Mhz */
/* LTDC 时钟频率 = PLLLCDCLK / DIV = 420/6/8 = 8.75 Mhz */
/* LTDC时钟太高会导花屏,若对刷屏速度要求不高,降低时钟频率可减少花屏现象*/
/* 以下函数三个参数分别为:PLLSAIN,PLLSAIQ,PLLSAIR,其中PLLSAIQ与LTDC无关*/
RCC_PLLSAIConfig(420,7, 6);
/*以下函数的参数为DIV值*/
RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div8);
/* 使能 PLLSAI 时钟 */
RCC_PLLSAICmd(ENABLE);
/* 等待 PLLSAI 初始化完成 */
while(RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET)
{}
/* LTDC配置*********************************************************/
/*信号极性配置*/
/* 行同步信号极性 */
LTDC_InitStruct.LTDC_HSPolarity = LTDC_HSPolarity_AL;
/* 垂直同步信号极性 */
LTDC_InitStruct.LTDC_VSPolarity = LTDC_VSPolarity_AL;
/* 数据使能信号极性 */
LTDC_InitStruct.LTDC_DEPolarity = LTDC_DEPolarity_AL;
/* 像素同步时钟极性 */
LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IPC;
/* 配置LCD背景颜色 */
LTDC_InitStruct.LTDC_BackgroundRedValue = 0;
LTDC_InitStruct.LTDC_BackgroundGreenValue = 0;
LTDC_InitStruct.LTDC_BackgroundBlueValue = 0;
/* 时间参数配置 */
/* 配置行同步信号宽度(HSW-1) */
LTDC_InitStruct.LTDC_HorizontalSync =HSW-1;
/* 配置垂直同步信号宽度(VSW-1) */
LTDC_InitStruct.LTDC_VerticalSync = VSW-1;
/* 配置(HSW+HBP-1) */
LTDC_InitStruct.LTDC_AccumulatedHBP =HSW+HBP-1;
/* 配置(VSW+VBP-1) */
LTDC_InitStruct.LTDC_AccumulatedVBP = VSW+VBP-1;
/* 配置(HSW+HBP+有效像素宽度-1) */
LTDC_InitStruct.LTDC_AccumulatedActiveW = HSW+HBP+LCD_PIXEL_WIDTH-1;
/* 配置(VSW+VBP+有效像素高度-1) */
LTDC_InitStruct.LTDC_AccumulatedActiveH = VSW+VBP+LCD_PIXEL_HEIGHT-1;
/* 配置总宽度(HSW+HBP+有效像素宽度+HFP-1) */
LTDC_InitStruct.LTDC_TotalWidth =HSW+ HBP+LCD_PIXEL_WIDTH + HFP-1;
/* 配置总高度(VSW+VBP+有效像素高度+VFP-1) */
LTDC_InitStruct.LTDC_TotalHeigh =VSW+ VBP+LCD_PIXEL_HEIGHT + VFP-1;
LTDC_Init(<DC_InitStruct);
LTDC_Cmd(ENABLE);
}
-
初始化LTDC层级
初始化第一层(底层):
我们根据上面提到的表格进行配置:
?
在对Alpha进行配置的时候,要注意的是,要在配置寄存器LTDC_LxCARC中:
?
对于配置颜色:
假如我们的像素点的透明度全为透明,即Alpha=0,那么可以得到
BC = (1 * 0) * Layer2的颜色 + (1-1 * 0 )* layer1的颜色
最后就可以实现layer2层透明的颜色与layer1的背景色相结合。
void LCD_LayerInit(void)
{
LTDC_Layer_InitTypeDef LTDC_Layer_InitStruct;
/* 层窗口配置 */
/* 配置本层的窗口边界,注意这些参数是包含HBP HSW VBP VSW的 */
//一行的第一个起始像素,该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedHBP+1)的值
LTDC_Layer_InitStruct.LTDC_HorizontalStart = HBP + HSW;
//一行的最后一个像素,该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedActiveW)的值
LTDC_Layer_InitStruct.LTDC_HorizontalStop = HSW+HBP+LCD_PIXEL_WIDTH-1;
//一列的第一个起始像素,该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedVBP+1)的值
LTDC_Layer_InitStruct.LTDC_VerticalStart = VBP + VSW;
//一列的最后一个像素,该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedActiveH)的值
LTDC_Layer_InitStruct.LTDC_VerticalStop = VSW+VBP+LCD_PIXEL_HEIGHT-1;
/* 像素格式配置*/
LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB888;
/* 恒定Alpha值配置,0-255 */
LTDC_Layer_InitStruct.LTDC_ConstantAlpha = 255;
/* 默认背景颜色,该颜色在定义的层窗口外或在层禁止时使用。这里相当于底层 */
LTDC_Layer_InitStruct.LTDC_DefaultColorBlue = 0xFF;
LTDC_Layer_InitStruct.LTDC_DefaultColorGreen = 0xFF;
LTDC_Layer_InitStruct.LTDC_DefaultColorRed = 0xFF;
LTDC_Layer_InitStruct.LTDC_DefaultColorAlpha = 0xFF;
/* 配置混合因子 CA表示使用恒定Alpha值,PAxCA表示使用像素Alpha x 恒定Alpha值 */
LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_CA; //1
LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA;//2
/* 该成员应写入(一行像素数据占用的字节数+3)
Line Lenth = 行有效像素个数 x 每个像素的字节数 + 3
行有效像素个数 = LCD_PIXEL_WIDTH
每个像素的字节数 = 2(RGB565/RGB1555)/ 3 (RGB888)/ 4(ARGB8888)
*/
LTDC_Layer_InitStruct.LTDC_CFBLineLength = ((LCD_PIXEL_WIDTH * 3) + 3);
/* 从某行的起始位置到下一行起始位置处的像素增量
Pitch = 行有效像素个数 x 每个像素的字节数 */
LTDC_Layer_InitStruct.LTDC_CFBPitch = (LCD_PIXEL_WIDTH * 3);
/* 配置有效的行数 */
LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_PIXEL_HEIGHT;
/* 配置本层的显存首地址 */
LTDC_Layer_InitStruct.LTDC_CFBStartAdress = LCD_FRAME_BUFFER;
/* 以上面的配置初始化第 1 层*/
LTDC_LayerInit(LTDC_Layer1, <DC_Layer_InitStruct);
/*配置第 2 层,若没有重写某个成员的值,则该成员使用跟第1层一样的配置 */
/* 配置本层的显存首地址,这里配置它紧挨在第1层的后面*/
LTDC_Layer_InitStruct.LTDC_CFBStartAdress = LCD_FRAME_BUFFER + BUFFER_OFFSET;
/* 配置混合因子,使用像素Alpha参与混合 */
LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA;
LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA;
/* 初始化第2层 */
LTDC_LayerInit(LTDC_Layer2, <DC_Layer_InitStruct);
/* 立即重载配置 */
LTDC_ReloadConfig(LTDC_IMReload);
/*使能前景及背景层 */
LTDC_LayerCmd(LTDC_Layer1, ENABLE);
LTDC_LayerCmd(LTDC_Layer2, ENABLE);
/* 立即重载配置 */
LTDC_ReloadConfig(LTDC_IMReload);
/* 设定字体(英文) */
LCD_SetFont(&LCD_DEFAULT_FONT);
}
对于第一二层准备放入SDRAM内存中的地址,我们可以规定:
#define LCD_WIDTH 800
#define LCD_HEIGHT 480
//第一层显存首地址,SDRAM的首地址
#define LCD_LAYER1_START_ADDR 0xD0000000
#define LCD_LAYER1_BUFFER_SIZE LCD_WIDTH * LCD_HEIGHT * 2
//第二层显存首地址
#define LCD_LAYER2_START_ADDR (LCD_LAYER1_START_ADDR + LCD_LAYER1_BUFFER_SIZE)
#define LCD_LAYER2_BUFFER_SIZE LCD_WIDTH * LCD_HEIGHT * 4
写一个划线的函数:
void DIS_Line(void)
{
uint16_t i;
uint32_t *p = (uint32_t *)(LCD_LAYER2_START_ADDR + LCD_WIDTH * 20 * 4)
for(i=0;i<800;i++)
{
*p = 0xffff0000;//画红线
p++;
}
}
-
使用DMA2D(可以减轻CPU的负担)
在使用DMA2D时要记得初始化时钟:
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_DMA2D,ENABLE);
?使用DMA2D在第二层显示一幅纯色的图像:
void DMA2D_DIS(void)
{
DMA2D_InitTypeDef DMA2D_InitTypeStruct;
//寄存器到存储器
DMA2D_InitTypeStruct.DMA2D_Mode = DMA2D_R2M;
//第二层图像格式
DMA2D_InitTypeStruct.DMA2D_CMode = DMA2D_ARGB8888;
//想要传输的颜色值
DMA2D_InitTypeStruct.DMA2D_OutputRed = 0xff;
DMA2D_InitTypeStruct.DMA2D_OutputGreen = 0x00;
DMA2D_InitTypeStruct.DMA2D_OutputBlue = 0x00;
DMA2D_InitTypeStruct.DMA2D_OutputAlpha = 0xff;
//要传输到的显存地址
DMA2D_InitTypeStruct.DMA2D_OutputMemoryAdd = LCD_LAYER2_START_ADDR;
/*当需要显示在其他行的时候,可以这么修改:
DMA2D_InitTypeStruct.DMA2D_OutputMemoryAdd = LCD_LAYER2_START_ADDR + LCD_Width * 20 * 4 + 350 * 4;
//表示下移20行显示,因为每个像素点占四个字节,所以乘以4,后面的显示350是指显示在350列
*/
//不需要行偏移
DMA2D_InitTypeStruct.DMA2D_OutputOffset = 0;
//全屏显示
DMA2D_InitTypeStruct.DMA2D_PixelPerLine = 800;
DMA2D_InitTypeStruct.DMA2D_NumberOfLine = 480;
/*如果想要显示一个矩形
DMA2D_InitTypeStruct.DMA2D_OutputOffset = 800-300;
DMA2D_InitTypeStruct.DMA2D_PixelPerLine = 300; //行
DMA2D_InitTypeStruct.DMA2D_NumberOfLine = 200; //列
*/
DMA2D_Init(&DMA2D_InitTypeStruct);
DMA2D_StartTransfer();
while(DMA2D_GetFlagStatus(DMA2D_FLAG_TC)==RESET)
{
//这里可以回到CPU执行其他东西。
}
}
画横线的原理也是差不多的,基本就是在操作像素点:
//使用DMA2D画直线
void DIS_Line_DMA2D(void)
{
DMA2D_InitTypeDef DMA2D_InitTypeStruct;
DMA2D_InitTypeStruct.DMA2D_Mode = DMA2D_R2M ;
DMA2D_InitTypeStruct.DMA2D_CMode = DMA2D_ARGB8888;
//要传输的颜色值
DMA2D_InitTypeStruct.DMA2D_OutputRed = 0xff;
DMA2D_InitTypeStruct.DMA2D_OutputGreen = 0x00;
DMA2D_InitTypeStruct.DMA2D_OutputBlue = 0x00;
DMA2D_InitTypeStruct.DMA2D_OutputAlpha = 0xff;
//要传输到的显存地址
DMA2D_InitTypeStruct.DMA2D_OutputMemoryAdd = LCD_LAYER2_START_ADDR +LCD_WIDTH*20*4 + 350*4;
DMA2D_InitTypeStruct.DMA2D_OutputOffset = 800-1;
DMA2D_InitTypeStruct.DMA2D_PixelPerLine = 1;
DMA2D_InitTypeStruct.DMA2D_NumberOfLine = 200;
DMA2D_Init(&DMA2D_InitTypeStruct);
DMA2D_StartTransfer();
while( DMA2D_GetFlagStatus(DMA2D_FLAG_TC) ==RESET);
}
使用DMA2D处理第一层(底层):
第一层的图像正常为RGB565或RGB888,现在以RGB565为例:
void DIS_RECT_LAYER1(void)
{
DMA2D_InitTypeDef DMA2D_InitTypeStruct;
DMA2D_InitTypeStruct.DMA2D_Mode = DMA2D_R2M ;
DMA2D_InitTypeStruct.DMA2D_CMode = DMA2D_RGB565;
//要传输的颜色值
DMA2D_InitTypeStruct.DMA2D_OutputRed = (0xff >>3) & 0x1F ; //把8位的颜色值转成5位的颜色值
DMA2D_InitTypeStruct.DMA2D_OutputGreen = (0xFF>>2) & 0x3F ;//把8位的颜色值转成6位的颜色值
DMA2D_InitTypeStruct.DMA2D_OutputBlue = (0x00 >>3) & 0x1F; //把8位的颜色值转成5位的颜色值
DMA2D_InitTypeStruct.DMA2D_OutputAlpha = 0xff;
//要传输到的显存地址
DMA2D_InitTypeStruct.DMA2D_OutputMemoryAdd = LCD_LAYER1_START_ADDR +LCD_WIDTH*200*2 + 350*2;
DMA2D_InitTypeStruct.DMA2D_OutputOffset = 800-100;
DMA2D_InitTypeStruct.DMA2D_PixelPerLine = 100;
DMA2D_InitTypeStruct.DMA2D_NumberOfLine = 100;
DMA2D_Init(&DMA2D_InitTypeStruct);
DMA2D_StartTransfer();
while( DMA2D_GetFlagStatus(DMA2D_FLAG_TC) ==RESET);
}
?
|