LTDC-DMA2D液晶显示 代码详解(二)
使用以下结构体的具体原理需要参考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。
LTDC_VerticalSync
本成员设置垂直同步信号VSYNC的宽度VSW,它以“行”为位,实际写入该参数时应写入(VSW-1) ,参数范围为0x000- 0x7FF。
LTDC_AccumulatedHBP
本成员用于配置“水平同步像素HSW”加“水平后沿像素HBP”的累加值,实际写入该参数时应写入(HSW+HBP-1) ,参数范围为0x000- 0xFFF。
LTDC_AccumulatedVBP
本成员用于配置“垂直同步行VSW”加“垂直后沿行VBP”的累加值,实际写入该参数时应写入(VSW+VBP-1) ,参数范围为0x000- 0x7FF。
配置所需要时间:
LTDC_AccumulatedActiveW
本成员用于配置“水平同步像素HSW”加“水平后沿像素HBP”加“有效像素”的累加值,实际写入该参数时应写入(HSW+HBP+有效宽度-1) ,参数范围为0x000- 0xFFF。
LTDC_AccumulatedActiveH
本成员用于配置“垂直同步行VSW”加“垂直后沿行VBP”加“有效行”的累加值,实际写入该参数时应写入(VSW+VBP+有效高度-1) ,参数范围为0x000- 0x7FF。
LTDC_TotalWidth
本成员用于配置“水平同步像素HSW”加“水平后沿像素HBP”加“有效像素”加“水平前沿像素HFP”的累加值,即总宽度,实际写入该参数时应写入(HSW+HBP+有效宽度+HFP-1) ,参数范围为0x000- 0xFFF。
LTDC_TotalHeigh
本成员用于配置“垂直同步行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;
LTDC_ HorizontalStart /HorizontalStop/VerticalStart/VerticalStop
这些成员用于确定该层显示窗口的边界,分别表示行起始、行结束、垂直起始及垂直结束的位置,注意这些参数包含同步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_Mode
用于配置DMA2D的工作模式,它可以被设置为下表中的值:
宏 | 说明 |
---|---|
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_NumberOfLine
本成员用于配置DMA2D一共要传输多少行数据,表1中绿色部分一共有5行数据。
DMA2D_PixelPerLine
本成员用于配置每行有多少个像素点,如表2示每行有2个像素点。
配置完这些结构体成员,调用库函数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 DISPGPIO_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);//拉高使能lcdGPIO_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); }
LTDC-DMA2D液晶显示 代码详解(二)相关推荐
- Pytorch|YOWO原理及代码详解(二)
Pytorch|YOWO原理及代码详解(二) 本博客上接,Pytorch|YOWO原理及代码详解(一),阅前可看. 1.正式训练 if opt.evaluate:logging('evaluating ...
- ELMo代码详解(二)
ELMo代码解读笔记2:模型代码 2.模型代码介绍 模型代码主要包括以下几个部分:1.构建word embedding; 2.构建word_char embedding的准备; 3.语言模型介绍( ...
- linux 进程间通信 dbus-glib【实例】详解二(下) 消息和消息总线(ListActivatableNames和服务器的自动启动)(附代码)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
- linux 进程间通信 dbus-glib【实例】详解二(上) 消息和消息总线(附代码)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
- 微信公众平台万能代码详解-php语言(二)
1.基础知识在上一篇地址有讲解和图片,内容大致包括微信开发者模式后台配置.微信公众开发者文档代码详解. 2.本篇将粘贴出包括所有类型在内的消息处理办法,在开发者模式下用代码完成所有编辑模式的基础内容. ...
- 微信小程序系列(4)如何用微信小程序写一个论坛?贴心代码详解(二)评论页
源代码已开源,如果对你有帮助可以点个星:https://github.com/linkaimin/xdzs 写论坛不难,重点是各页面之间的信息传递! 先放成品图,虽然有点单调....但是麻雀虽小五脏俱 ...
- Qt开发技术:Q3D图表开发笔记(二):Q3DBar三维柱状图介绍、Demo以及代码详解
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/130150728 各位读者,知识无穷而人力有穷,要么改需 ...
- 天津理工大学《操作系统》实验二,存储器的分配与回收算法实现,代码详解,保姆式注释讲解
天津理工大学<操作系统>实验二,存储器的分配与回收算法实现,代码详解,保姆式注释讲解 实验内容 1. 本实验是模拟操作系统的主存分配,运用可变分区的存储管理算法设计主存分配和回收程序,并不 ...
- 数学建模二:TOPSIS法(优劣解距离法) 附代码详解
数学建模二:TOPSIS法(优劣解距离法)附代码详解 TOPSIS法(优劣解距离法)用于评价类问题. 层次分析法因为受限于一致性检验指标的数量,最多只能选择15个准则或方案.同时层次分析法也难以处理已 ...
最新文章
- Python中的继承和多态
- 为什么linux进程名匹配最多只支持15个字符?
- Django的APP,视图,url和模板
- 深度学总结:weight_initialization
- [BUUCTF-pwn]——wustctf2020_getshell
- BeanUtils.copyProperties() 用法
- 象棋子 设计模式_通过设计国际象棋游戏了解策略模式
- android url 快捷方式,Android向桌面添加快捷方式,使其指向特定的(URL)网页
- 美女被偷之后.....
- acl 影响因子_适合理论、计算化学投稿的期刊及其2016年影响因子(2017年公布)...
- 计算机打文档的技巧,电脑word文档下划线怎么打(word文档编辑小技巧)
- 在局域网内主动封堵BT下载使用
- 【计算机网络】ARP协议工作原理
- SPICE鼠标报点率降低问题分析
- 《智能控制导论》读书报告(课程作业)--常规控制的智能化
- Codeforces C. Ehab and Path-etic MEXs (树 / 构造 / MEX)
- 全志v3s学习笔记(8)——TF卡分区及烧录
- 计算机三级网络技术第一轮(第二章)
- 三极管PNP与NPN控制区别
- java 网校 代码_JAVA代码审计 | 因酷网校在线教育系统