025 - STM32学习笔记 - 液晶屏控制(二) - 代码实现

news2024/11/30 12:51:38

025- STM32学习笔记 - 液晶屏控制(二) - 代码实现

好久没更新学习笔记了,最近工作上的事情太多了,趁着国庆中秋,多更新一点看看。
上节学习了关于LTDC与DMA2D以及显示屏的相关知识点,这节开始实操,首先了解一下LTDC的相关寄存器。

一、LTDC初始化结构体

使用LTDC需要配置很多寄存器,利用LTDC的初始化结构体可以快速的进行配置,结构体声明位于stm32f4xx_ltdc.h,LTDC初始化结构体如下:

/** 
  * @brief  LTDC初始化结构体
  * stm32f4xx_ltdc.h
  */
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+有效高度+VFP-1)的值 */    
	uint32_t LTDC_BackgroundRedValue;         /* 配置背景的红色值 */
	uint32_t LTDC_BackgroundGreenValue;       /* 配置背景的绿色值 */ 
	uint32_t LTDC_BackgroundBlueValue;        /* 配置背景的蓝色值 */
} LTDC_InitTypeDef;

LTDC初始化结构体成员大部分用于定义LTDC的时序参数,包括信号有效电平及各种事件参数的宽度,配合液晶数据传输时序图中的说明更容易理解,这里对各个成员进行介绍:

LTDC_HSPolarity

用于设置行同步信号HSYNC的极性,即HSYNC有效时的电平,可以设置为高电平(LTDC_HSPolarity_AH)或者低电平(LTDC_HSPolarity_AL)。

LTDC_VSPolarity

同LTDC_HSPolarity一样,用来设置VSYNC有效时的电平,可以设置为高电平(LTDC_VSPolarity_AH)或者低电平(LTDC_VSPolarity_AL)。

LTDC_DEPolarity

用于设置数据使能信号 DE 的极性,可设置为高电平(LTDC_DEPolarity_AH)或者低电平(LTDC_DEPolarity_AL)。

LTDC_PCPolarity

用于设置像素时钟信号CLK的极性,可设置为上升沿(LTDC_DEPolarity_AH)或下降沿(LTDC_DEPolarity_AL),表示 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/ LTDC_BackgroundGreenValue/ LTDC_BackgroundBlueValue

这三个结构体成员用于配置背景的颜色值,这里说的背景层与上节内容中的“前景层与背景层”概念不一样,如下图:

在这里插入图片描述

这里需要配置颜色指的背景层,指的是图中的“第2层/第1层”,而在这两层之外,还有一个最终的背景层,当第1层和第2层都透明时,这个背景层就会被显示,而这个背景层是一个纯色的矩形,其颜色值就是通过这三个结构体成员配置,各个参数的范围为0x00-0xFF。

除了LTDC初始化结构体外,还得配置LTDC层级初始化结构体。

二、LTDC层级初始化结构体

LTDC初始化结构体只是配置好与液晶屏通讯的基本时序,而LTDC层级初始化结构体则是用来配置像素格式、显存地址灯诸多参数,结构体声明位于stm32f4xx_ltdc.h,其结构如下:

/**
  * @brief LTDC Layer structure definition
  */
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; 	/* 配置混合因子BlendingFactor1*/
    uint32_t LTDC_BlendingFactor_2; 	/* 配置混合因子BlendingFactor2 */
    uint32_t LTDC_CFBStartAdress; 		/* 配置当前层的显存起始位置*/
    uint32_t LTDC_CFBLineLength; 		/* 配置当前层的行数据长度 */
    uint32_t LTDC_CFBPitch; 		    /* 配置从某行的起始到下一行像素起始处的增量*/
    uint32_t LTDC_CFBLineNumber; 		/* 配置当前层的行数*/
}LTDC_Layer_InitTypeDef;

LTDC_HorizontalStart /LTDC_HorizontalStop/ LTDC_VerticalStart/ LTDC_VerticalStop

这四个成员用于确定该层显示窗口的边界,分别表示行起始、行结束、垂直起始和垂直结束的位置。如下图:

在这里插入图片描述

这些参数包含同步HSW/VSW、后沿大小HBP/VBP和有效数据区域的内部时许发生器的配置,其可写入的值如下表:

LTDC 层级窗口配置 成员等效于 LTDC 时序参数 配置成员的值实际值
LTDC_HorizontalStart(LTDC_AccumulatedHBP+1)HBP + HSW
LTDC_HorizontalStopLTDC_AccumulatedActiveWHSW+HBP+LCD_PIXEL_WIDTH - 1
LTDC_VerticalStart(LTDC_AccumulatedVBP+1)VBP + VSW
LTDC_VerticalStopLTDC_AccumulatedActiveHVSW+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/LTDC_DefaultColorGreen/LTDC_DefaultColorRed/LTDC_DefaultColorAlpha

这些成员用于配置该层的默认颜色值,分别为蓝、绿、红及透明分量,该颜色在定义的层窗口外或在层禁止时使用。

LTDC_BlendingFactor_1/LTDC_BlendingFactor_2

本成员用于设置混合系数BF1和BF2。每一层实际显示的颜色都需要使用透明度参与运算,计算出不包含透明度的直接RGB颜色值,然后才传输给液晶屏(因为液晶屏本身没有透明的概念)。混合的计算公式为:
B C = B F 1 ∗ C + B F 2 ∗ C s BC = BF1 * C + BF2 * Cs BC=BF1C+BF2Cs

参数说明CAPAxCA
BC混合后的颜色 (混合结 果)--
C当前层颜色--
Cs底层混合后的颜色--
BF1混合系数 1等于(恒 定 Alpha值)等于(恒定Alpha x 像素Alpha 值)
BF2混合系数 2等于(1-恒定 Alpha)等于(1-恒定Alpha x 像素Alpha 值)

用于设置BF1/BF2参数使用CA配置还是PAxCA 配置。配置成CA表示混合系数中只包含恒定的Alpha值, 即像素本身的Alpha不会影响混合效果,若配置成PAxCA,则混合系数中包含有像素本身的Alpha值,即把像素本身的Alpha加入到混合运算中。其中的恒定Alpha值即前面“LTDC_ConstantAlpha” 结构体配置参数的透明度百分比: (配置的Alpha值/0xFF)。

配置值为:

CA:LTDC_BlendingFactor1_CA 和 LTDC_BlendingFactor2_CA

PAxCA:LTDC_BlendingFactor1_PAxCA 和 LTDC_BlendingFactor2_PAxCA

在这里插入图片描述

入上图,数据源混合时,如果数据源有两层,首先会将第1层与背景混合,然后再将第2层与前面混合的图层再次混合从而得到最终图像。

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初始化结构体

使用DMA2D之前,需要对DMA2D进行配置初始化,结构体声明位于stm32f4xx_dma2d.h中,结构如下:

/** 
  * @brief  DMA2D Init structure definition  
  */
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;                /* 配置显存地址 0x00000000 to 0xFFFFFFFF. */
    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以及是否使能混合器,使用DMA2D时,可以将数据从某个位置搬运到现存,源位置可以时DMA2D本身的寄存器,也可以是设置好的DMA2D前景地址、背景地址。如果是能了PFC,则存储器中的数据源会经过转换后在送到显存,如果使能混合器,DMA2D会把两个数据源中的数据混合后再送到显存。

若使用存储器到存储器模式,需要调用库函数DMA2D_FGConfig,使用初始化结构体DMA2D_FG_InitTypeDef配置数据源的格式、地址等参数。背景层使用函数DMA2D_BGConfig和结构体DMA2D_BG_InitTypeDef)

DMA2D_CMode

用于配置DMA2D的输出PFC颜色格式,也就是将要传输给显存的格式。

DMA2D_OutputBlue/DMA2D_OutputGreen/DMA2D_OutputRed/DMA2D_OutputAlpha

用于配置DMA2D的输出颜色值,如果DMA2D工作模式设置为寄存器到存储器模式,则此颜色值作为数据源,会被DA2D复制到显存空间,目标空间就会被填入这一种颜色。

DMA2D_OutputMemoryAdd

用于配置DMA2D的输出FIFO的地址,DMA2D的数据会被搬运到该空间,一般将此设置为传输显示位置的起始地址。

DMA2D_OutputOffset

用于配置行偏移,行偏移会被添加到各行的结尾,用于确定下一行的起始地址,如下,绿色表示要显示的像素列,黄色表示行偏移,,假如左边显示的是一条竖线,竖线的宽度为1像素,所以行偏移的值为7-1=6,右边的线宽为2像素,行偏移的值为7-2=5,这样可以总结
行偏移的值 = 行宽度 − 线宽 行偏移的值 = 行宽度 - 线宽 行偏移的值=行宽度线宽
在这里插入图片描述

DMA2D_NumberOfLine

用于配置 DMA2D 一共要传输多少行数,如上图中,一共有8行数据。

DMA2D_PixelPerLine

用于配置每行有多少个像素点,如上图左侧表示每行有1个像素点,右侧表示每行有2个像素点。

四、硬件实现

野火F429上使用的LCD屏幕型号为“STD800480”的5寸液晶触摸屏,其显示分辨率为800x480,支持RGB888格式。关于其供电部分这里不说明了,感兴趣的可以看看野火官方原理图,这里对液晶触摸屏的接口部分大致说明一下:

在这里插入图片描述

接口电路中,使用了I2C通讯获取触摸屏的触控信号,排线包含了I2C的通讯引脚SCL(1)、SDA(2),同时还有控制触摸屏芯片复位的RSTN(4)以及触摸中断信号INT(3)。

从引脚6到引脚35,就是上面我们学到的所有关于LCD的控制引脚了,里面包含了RGB信号线[35:12]、同步时钟信号[11]\垂直/水平同步[10:9]、使能信号[8]、显示器开关[7]、背光控制[6]。

液晶屏部分引脚(B[6]、B[7])两个引脚同时也是CAN通讯的引脚,所以再使用液晶屏的时候不能使用CAN通讯。

五、软件设计

上面内容学习完后,就可以开始设计控制代码了,这里跟以前一样,还是通过建立板级支持包(bsp_lcd.c和bsp_lcd.h)来对LCD屏幕控制,方便以后移植。

编程要点:

a、初始化LTDC时钟、DMA2D时钟、GPIO时钟;

b、初始化SDRAM,用作为显存;

c、根据液晶屏的参数配置LTDC外设的通讯时序;

d、配置LTDC层级控制参数,配置显存地址;

e、初始化DMA2D,使用DMA2D辅助显示;

f、编写测试程序,控制液晶输出。

1、LTDC硬件宏定义

本来想像之前做SDRAM的时候一样,把所有引脚都做个表列出来,但是上次引脚太多了,列个表太痛苦了。这次就不列表格了,直接把宏定义贴出来各位看一下吧。

/* 红色数据线 */
//R0 - PH2
#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_GPIO_PINSOURCE              GPIO_PinSource2
#define LTDC_R0_AF                          GPIO_AF_LTDC

//R1 - PH3
#define LTDC_R1_GPIO_PORT                   GPIOH
#define LTDC_R1_GPIO_CLK                    RCC_AHB1Periph_GPIOH
#define LTDC_R1_GPIO_PIN                    GPIO_Pin_3
#define LTDC_R1_GPIO_PINSOURCE              GPIO_PinSource3
#define LTDC_R1_AF                          GPIO_AF_LTDC

//R2 - PH8
#define LTDC_R2_GPIO_PORT                   GPIOH
#define LTDC_R2_GPIO_CLK                    RCC_AHB1Periph_GPIOH
#define LTDC_R2_GPIO_PIN                    GPIO_Pin_8
#define LTDC_R2_GPIO_PINSOURCE              GPIO_PinSource8
#define LTDC_R2_AF                          GPIO_AF_LTDC

//R3 - PB0
#define LTDC_R3_GPIO_PORT                   GPIOB
#define LTDC_R3_GPIO_CLK                    RCC_AHB1Periph_GPIOB
#define LTDC_R3_GPIO_PIN                    GPIO_Pin_0
#define LTDC_R3_GPIO_PINSOURCE              GPIO_PinSource0
#define LTDC_R3_AF                          GPIO_AF9_LTDC

//R4 - PA11
#define LTDC_R4_GPIO_PORT                   GPIOA
#define LTDC_R4_GPIO_CLK                    RCC_AHB1Periph_GPIOA
#define LTDC_R4_GPIO_PIN                    GPIO_Pin_11
#define LTDC_R4_GPIO_PINSOURCE              GPIO_PinSource11
#define LTDC_R4_AF                          GPIO_AF_LTDC

//R5 - A12
#define LTDC_R5_GPIO_PORT                   GPIOA
#define LTDC_R5_GPIO_CLK                    RCC_AHB1Periph_GPIOA
#define LTDC_R5_GPIO_PIN                    GPIO_Pin_12
#define LTDC_R5_GPIO_PINSOURCE              GPIO_PinSource12
#define LTDC_R5_AF                          GPIO_AF_LTDC

//R6 - PB1
#define LTDC_R6_GPIO_PORT                   GPIOB
#define LTDC_R6_GPIO_CLK                    RCC_AHB1Periph_GPIOB
#define LTDC_R6_GPIO_PIN                    GPIO_Pin_1
#define LTDC_R6_GPIO_PINSOURCE              GPIO_PinSource1
#define LTDC_R6_AF                          GPIO_AF9_LTDC

//R7 - PG6
#define LTDC_R7_GPIO_PORT                   GPIOG
#define LTDC_R7_GPIO_CLK                    RCC_AHB1Periph_GPIOG
#define LTDC_R7_GPIO_PIN                    GPIO_Pin_6
#define LTDC_R7_GPIO_PINSOURCE              GPIO_PinSource6
#define LTDC_R7_AF                          GPIO_AF_LTDC

/* 绿色数据线 */
//G0 - PE5
#define LTDC_G0_GPIO_PORT                   GPIOE
#define LTDC_G0_GPIO_CLK                    RCC_AHB1Periph_GPIOE
#define LTDC_G0_GPIO_PIN                    GPIO_Pin_5
#define LTDC_G0_GPIO_PINSOURCE              GPIO_PinSource5
#define LTDC_G0_AF                          GPIO_AF_LTDC

//G1 - PE6
#define LTDC_G1_GPIO_PORT                   GPIOE
#define LTDC_G1_GPIO_CLK                    RCC_AHB1Periph_GPIOE
#define LTDC_G1_GPIO_PIN                    GPIO_Pin_6
#define LTDC_G1_GPIO_PINSOURCE              GPIO_PinSource6
#define LTDC_G1_AF                          GPIO_AF_LTDC

//G2 - PH13
#define LTDC_G2_GPIO_PORT                   GPIOH
#define LTDC_G2_GPIO_CLK                    RCC_AHB1Periph_GPIOH
#define LTDC_G2_GPIO_PIN                    GPIO_Pin_13
#define LTDC_G2_GPIO_PINSOURCE              GPIO_PinSource13
#define LTDC_G2_AF                          GPIO_AF_LTDC

//G3 - PG10
#define LTDC_G3_GPIO_PORT                   GPIOG
#define LTDC_G3_GPIO_CLK                    RCC_AHB1Periph_GPIOG
#define LTDC_G3_GPIO_PIN                    GPIO_Pin_10
#define LTDC_G3_GPIO_PINSOURCE              GPIO_PinSource10
#define LTDC_G3_AF                          GPIO_AF9_LTDC

//G4 - PH15
#define LTDC_G4_GPIO_PORT                   GPIOH
#define LTDC_G4_GPIO_CLK                    RCC_AHB1Periph_GPIOH
#define LTDC_G4_GPIO_PIN                    GPIO_Pin_15
#define LTDC_G4_GPIO_PINSOURCE              GPIO_PinSource15
#define LTDC_G4_AF                          GPIO_AF_LTDC

//G5 - PI0
#define LTDC_G5_GPIO_PORT                   GPIOI
#define LTDC_G5_GPIO_CLK                    RCC_AHB1Periph_GPIOI
#define LTDC_G5_GPIO_PIN                    GPIO_Pin_0
#define LTDC_G5_GPIO_PINSOURCE              GPIO_PinSource0
#define LTDC_G5_AF                          GPIO_AF_LTDC

//G6 - PC7
#define LTDC_G6_GPIO_PORT                   GPIOC
#define LTDC_G6_GPIO_CLK                    RCC_AHB1Periph_GPIOC
#define LTDC_G6_GPIO_PIN                    GPIO_Pin_7
#define LTDC_G6_GPIO_PINSOURCE              GPIO_PinSource7
#define LTDC_G6_AF                          GPIO_AF_LTDC

//G7 - PI2
#define LTDC_G7_GPIO_PORT                   GPIOI
#define LTDC_G7_GPIO_CLK                    RCC_AHB1Periph_GPIOI
#define LTDC_G7_GPIO_PIN                    GPIO_Pin_2
#define LTDC_G7_GPIO_PINSOURCE              GPIO_PinSource2
#define LTDC_G7_AF                          GPIO_AF_LTDC

/* 蓝色数据线 */
//B0 - PE4
#define LTDC_B0_GPIO_PORT                   GPIOE
#define LTDC_B0_GPIO_CLK                    RCC_AHB1Periph_GPIOE
#define LTDC_B0_GPIO_PIN                    GPIO_Pin_4
#define LTDC_B0_GPIO_PINSOURCE              GPIO_PinSource4
#define LTDC_B0_AF                          GPIO_AF_LTDC

//B1 - PG12
#define LTDC_B1_GPIO_PORT                   GPIOG
#define LTDC_B1_GPIO_CLK                    RCC_AHB1Periph_GPIOG
#define LTDC_B1_GPIO_PIN                    GPIO_Pin_12
#define LTDC_B1_GPIO_PINSOURCE              GPIO_PinSource12
#define LTDC_B1_AF                          GPIO_AF_LTDC

//B2 - PD6
#define LTDC_B2_GPIO_PORT                   GPIOD
#define LTDC_B2_GPIO_CLK                    RCC_AHB1Periph_GPIOD
#define LTDC_B2_GPIO_PIN                    GPIO_Pin_6
#define LTDC_B2_GPIO_PINSOURCE              GPIO_PinSource6
#define LTDC_B2_AF                          GPIO_AF_LTDC

//B3 - PG11
#define LTDC_B3_GPIO_PORT                   GPIOG
#define LTDC_B3_GPIO_CLK                    RCC_AHB1Periph_GPIOG
#define LTDC_B3_GPIO_PIN                    GPIO_Pin_11
#define LTDC_B3_GPIO_PINSOURCE              GPIO_PinSource11
#define LTDC_B3_AF                          GPIO_AF_LTDC

//B4 - PI4
#define LTDC_B4_GPIO_PORT                   GPIOI
#define LTDC_B4_GPIO_CLK                    RCC_AHB1Periph_GPIOI
#define LTDC_B4_GPIO_PIN                    GPIO_Pin_4
#define LTDC_B4_GPIO_PINSOURCE              GPIO_PinSource4
#define LTDC_B4_AF                          GPIO_AF_LTDC

//B5 - PA3
#define LTDC_B5_GPIO_PORT                   GPIOA
#define LTDC_B5_GPIO_CLK                    RCC_AHB1Periph_GPIOA
#define LTDC_B5_GPIO_PIN                    GPIO_Pin_3
#define LTDC_B5_GPIO_PINSOURCE              GPIO_PinSource3
#define LTDC_B5_AF                          GPIO_AF_LTDC

//B6 - PB8   CAN1_RX
#define LTDC_B6_GPIO_PORT                   GPIOB
#define LTDC_B6_GPIO_CLK                    RCC_AHB1Periph_GPIOB
#define LTDC_B6_GPIO_PIN                    GPIO_Pin_8
#define LTDC_B6_GPIO_PINSOURCE              GPIO_PinSource8
#define LTDC_B6_AF                          GPIO_AF_LTDC

//B7 - PB9   CAN1_TX
#define LTDC_B7_GPIO_PORT                   GPIOB
#define LTDC_B7_GPIO_CLK                    RCC_AHB1Periph_GPIOB
#define LTDC_B7_GPIO_PIN                    GPIO_Pin_9
#define LTDC_B7_GPIO_PINSOURCE              GPIO_PinSource9
#define LTDC_B7_AF                          GPIO_AF_LTDC

//像素时钟信号CLK - PG7
#define LTDC_CLK_GPIO_PORT                   GPIOG
#define LTDC_CLK_GPIO_CLK                    RCC_AHB1Periph_GPIOG
#define LTDC_CLK_GPIO_PIN                    GPIO_Pin_7
#define LTDC_CLK_GPIO_PINSOURCE              GPIO_PinSource7
#define LTDC_CLK_AF                          GPIO_AF_LTDC

//水平同步信号HSYNC - PI10
#define LTDC_HSYNC_GPIO_PORT                   GPIOI
#define LTDC_HSYNC_GPIO_CLK                    RCC_AHB1Periph_GPIOI
#define LTDC_HSYNC_GPIO_PIN                    GPIO_Pin_10
#define LTDC_HSYNC_GPIO_PINSOURCE              GPIO_PinSource10
#define LTDC_HSYNC_AF                          GPIO_AF_LTDC

//垂直同步信号VSYNC - PI9
#define LTDC_VSYNC_GPIO_PORT                   GPIOI
#define LTDC_VSYNC_GPIO_CLK                    RCC_AHB1Periph_GPIOI
#define LTDC_VSYNC_GPIO_PIN                    GPIO_Pin_9
#define LTDC_VSYNC_GPIO_PINSOURCE              GPIO_PinSource9
#define LTDC_VSYNC_AF                          GPIO_AF_LTDC

//数据使能信号DE - PF10
#define LTDC_DE_GPIO_PORT                   GPIOF
#define LTDC_DE_GPIO_CLK                    RCC_AHB1Periph_GPIOF
#define LTDC_DE_GPIO_PIN                    GPIO_Pin_10
#define LTDC_DE_GPIO_PINSOURCE              GPIO_PinSource10
#define LTDC_DE_AF                          GPIO_AF_LTDC

//液晶屏使能信号,高电平使能,DISP - PD4
#define LCD_DISP_GPIO_PORT                   GPIOD
#define LCD_DISP_GPIO_CLK                    RCC_AHB1Periph_GPIOD
#define LCD_DISP_GPIO_PIN                    GPIO_Pin_4
#define LCD_DISP_GPIO_PINSOURCE              GPIO_PinSource4

//液晶屏背光信号高电平使能 BL - PD7
#define LCD_BL_GPIO_PORT                   GPIOD
#define LCD_BL_GPIO_CLK                    RCC_AHB1Periph_GPIOD
#define LCD_BL_GPIO_PIN                    GPIO_Pin_7
#define LCD_BL_GPIO_PINSOURCE              GPIO_PinSource7

这里需要注意,在引脚的复用功能选择上,都选择为LTDC,在标准库中,GPIO_AF_LTDC默认映射为AF14,而我们使用到的部分引脚映射为AF9,区别如下:

#define GPIO_AF_LTDC          ((uint8_t)0x0E)			//默认映射为AF14
#define GPIO_AF9_LTDC         ((uint8_t)0x09)			//部分映射为AF9

因此此处需要注意,要根据实际使用的引脚来做映射。

2、引脚初始化

在宏定义好这些GPIO引脚后,就可以编写引脚初始化函数了,此段代码基本就是GPIO的初始化代码,比较简单,这里就不讲解了,大家看一下就行,代码如下:

/**
  * @brief  初始化LCD GPIO引脚
  * @parm   无
  * @retval 无
  */
void LCD_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    /* 开启所有LCD相关GPIO的时钟,再次声明,只要是外设操作,一定要先开时钟 */
    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  | LCD_DISP_GPIO_CLK   | LCD_BL_GPIO_CLK
                          ,ENABLE);
    /* GPIO通用配置 */
    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;
    
    /* R0配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R0_GPIO_PIN;
    GPIO_Init(LTDC_R0_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R0_GPIO_PORT,LTDC_R0_GPIO_PINSOURCE,LTDC_R0_AF);

    /* R1配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R1_GPIO_PIN;
    GPIO_Init(LTDC_R1_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R1_GPIO_PORT,LTDC_R1_GPIO_PINSOURCE,LTDC_R1_AF);

    /* R2配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R2_GPIO_PIN;
    GPIO_Init(LTDC_R2_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R2_GPIO_PORT,LTDC_R2_GPIO_PINSOURCE,LTDC_R2_AF);

    /* R3配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R3_GPIO_PIN;
    GPIO_Init(LTDC_R3_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R3_GPIO_PORT,LTDC_R3_GPIO_PINSOURCE,LTDC_R3_AF);

    /* R4配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R4_GPIO_PIN;
    GPIO_Init(LTDC_R4_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R4_GPIO_PORT,LTDC_R4_GPIO_PINSOURCE,LTDC_R4_AF);

    /* R5配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R5_GPIO_PIN;
    GPIO_Init(LTDC_R5_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R5_GPIO_PORT,LTDC_R5_GPIO_PINSOURCE,LTDC_R5_AF);
    
    /* R6配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R6_GPIO_PIN;
    GPIO_Init(LTDC_R6_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R6_GPIO_PORT,LTDC_R6_GPIO_PINSOURCE,LTDC_R6_AF);
    
    /* R7配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_R7_GPIO_PIN;
    GPIO_Init(LTDC_R7_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_R7_GPIO_PORT,LTDC_R7_GPIO_PINSOURCE,LTDC_R7_AF);
    
    /* G0配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G0_GPIO_PIN;
    GPIO_Init(LTDC_G0_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G0_GPIO_PORT,LTDC_G0_GPIO_PINSOURCE,LTDC_G0_AF);
    
    /* G1配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G1_GPIO_PIN;
    GPIO_Init(LTDC_G1_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G1_GPIO_PORT,LTDC_G1_GPIO_PINSOURCE,LTDC_G1_AF);
    
    /* G2配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G2_GPIO_PIN;
    GPIO_Init(LTDC_G2_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G2_GPIO_PORT,LTDC_G2_GPIO_PINSOURCE,LTDC_G2_AF);
    
    /* G3配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G3_GPIO_PIN;
    GPIO_Init(LTDC_G3_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G3_GPIO_PORT,LTDC_G3_GPIO_PINSOURCE,LTDC_G3_AF);
    
    /* G4配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G4_GPIO_PIN;
    GPIO_Init(LTDC_G4_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G4_GPIO_PORT,LTDC_G4_GPIO_PINSOURCE,LTDC_G4_AF);
    
    /* G5配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G5_GPIO_PIN;
    GPIO_Init(LTDC_G5_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G5_GPIO_PORT,LTDC_G5_GPIO_PINSOURCE,LTDC_G5_AF);
    
    /* G6配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G6_GPIO_PIN;
    GPIO_Init(LTDC_G6_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G6_GPIO_PORT,LTDC_G6_GPIO_PINSOURCE,LTDC_G6_AF);
    
    /* G7配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_G7_GPIO_PIN;
    GPIO_Init(LTDC_G7_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_G7_GPIO_PORT,LTDC_G7_GPIO_PINSOURCE,LTDC_G7_AF);
    
    /* B0配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B0_GPIO_PIN;
    GPIO_Init(LTDC_B0_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B0_GPIO_PORT,LTDC_B0_GPIO_PINSOURCE,LTDC_B0_AF);
    
    /* B1配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B1_GPIO_PIN;
    GPIO_Init(LTDC_B1_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B1_GPIO_PORT,LTDC_B1_GPIO_PINSOURCE,LTDC_B1_AF);
    
    /* B2配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B2_GPIO_PIN;
    GPIO_Init(LTDC_B2_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B2_GPIO_PORT,LTDC_B2_GPIO_PINSOURCE,LTDC_B2_AF);
    
    /* B3配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B3_GPIO_PIN;
    GPIO_Init(LTDC_B3_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B3_GPIO_PORT,LTDC_B3_GPIO_PINSOURCE,LTDC_B3_AF);
    
    /* B4配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B4_GPIO_PIN;
    GPIO_Init(LTDC_B4_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B4_GPIO_PORT,LTDC_B4_GPIO_PINSOURCE,LTDC_B4_AF);
    
    /* B5配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B5_GPIO_PIN;
    GPIO_Init(LTDC_B5_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B5_GPIO_PORT,LTDC_B5_GPIO_PINSOURCE,LTDC_B5_AF);
    
    /* B6配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B6_GPIO_PIN;
    GPIO_Init(LTDC_B6_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B6_GPIO_PORT,LTDC_B6_GPIO_PINSOURCE,LTDC_B6_AF);
    
    /* B7配置,并连接到复用功能 */
    GPIO_InitStruct.GPIO_Pin = LTDC_B7_GPIO_PIN;
    GPIO_Init(LTDC_B7_GPIO_PORT,&GPIO_InitStruct);
    GPIO_PinAFConfig(LTDC_B7_GPIO_PORT,LTDC_B7_GPIO_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_GPIO_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_GPIO_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_GPIO_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_GPIO_PINSOURCE,LTDC_DE_AF);
    
    /* 液晶屏信号线 */
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    
    /* 液晶屏使能信号线 */
    GPIO_InitStruct.GPIO_Pin = LCD_DISP_GPIO_PIN;
    GPIO_Init(LCD_DISP_GPIO_PORT,&GPIO_InitStruct);
    
    /* 液晶屏背光信号线 */
    GPIO_InitStruct.GPIO_Pin = LCD_BL_GPIO_PIN;
    GPIO_Init(LCD_BL_GPIO_PORT,&GPIO_InitStruct);

    /* 拉高使能LCD */
    GPIO_SetBits(LCD_DISP_GPIO_PORT,LCD_DISP_GPIO_PIN);
    GPIO_SetBits(LCD_BL_GPIO_PORT,LCD_BL_GPIO_PIN);
}
3、LCD参数配置

首先看一下LCD的时钟树,在上节中我们也大概看过,这里针对性的说明一下:

在这里插入图片描述

从上图中可以看到,LCD的时钟控制是通过HSE,经过M分频因子输出到PLLSAI分频器,有PLLSAI中的倍频因子N得到”LCD-TFT clock“,这里的”LCD-TFT clock“就是通讯中的同步时钟LCD_CLK,通过LCD_CLK引脚输出,按照默认设置,分频因子会把HSE分频得到1MHz的时钟,这里HSE外部高速时钟为25MHz,把M设置为25,经过M分频得到的1MHz时钟输入到PLLSAI分频器后,使用倍频因子N倍频,在经过R因子分频,得到PLLLCDCLK时钟,再由DIV因子分频得到LTDC通讯的同步时钟LCD_CLK,公式如下:
f L C D C L K = f H S E / M ∗ N / R / D I V fLCDCLK = fHSE/M*N/R/DIV fLCDCLK=fHSE/MN/R/DIV
经过M分频后,得到的时钟为1MHz的时钟,所以上面公式等价为:
f L C D C L K = 1 ∗ N / R / D I V fLCDCLK = 1*N/R/DIV fLCDCLK=1N/R/DIV
利用库函数RCC_PLLSAIConfig及RCC_LTDCCLKDivConfig函数可以配置PLLSAI分频器的这些参数,其中库函数RCC_PLLSAIConfig的三个输入参数分别为倍频因子N、分频因子Q和分频因子R,其中Q因子是作为SAI接口的分频时钟,与LTDC无关,RCC_LTDCCLKDivConfig函数的输入参数为分频因子DIV,在配置后,需要调用RCC_PLLSAICmd使能PLLSAI的时钟,并在检测位等待时钟初始化完成,根据库函数中可知,PLLSAI_N设置值范围为[50,432],PLLSAI_R设置范围为[2,7],PLLSAI_R设置范围为[2,15],PLLSAI_DIV可选值为2、4、8、16,在野火提供的5寸屏关于时钟频率的要求上限为50MHz,典型值为40MHz,无下限要求,但是野火测试下来,实际频率应该在24.5MHz以下,太高容易出现花屏的情况,这里我们按照野火提供的数据来测试,设置N = 420,R = 6,Q值不参与LCD_CLK的运行,随意设置即可,DIV设置为8分频,因此计算下来最终LCD_CLK = 420 / 6 / 8 = 8.75。

在这里插入图片描述

RCC_PLLSAIConfig(420, 7, 6);
RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div8);               /* DIV选择8分频 */
RCC_PLLSAICmd(ENABLE);					               /* 使能PLLSAI时钟 */
while (RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET);  /* 等待PLLSAI初始化完成 */

时钟配置完成后,根据液晶屏的时序要求,配置LTCD与液晶屏通讯时序的信号极性,配置过程参照如下图:

在这里插入图片描述

液晶屏时时序有效电平

根据上图中可以看到,HSYNC、VSYNC、DE的有效信号均为低电平,同步时钟信号极性配置为上升沿,这里需要注意的是野火提到文档中DE的有效电平为高电平,但是实测DE低电平时屏幕才能正常工作,这里我实测了一下确实如此,因此按照野火的实测值来处理。

接下来需要配置时间参数

这里需要与上面内容,需要根据液晶屏手册给出的时间参数,配置HSW、VSW、HBP、HFP、VBP、VFP、有效像素宽度及有效行数,从下图中得出,HSW = 1,VSW = 1 , HBP = 46 , VBP = 23 , HFP = 16 , VFP = 22:

在这里插入图片描述

/* 相关参数可查询液晶屏数据手册 */
#define HBP                                 46  //HSYNC 后的无效像素
#define VBP                                 23  //VSYNC 后的无效行数
#define HSW                                 1   //HSYNC 宽度
#define VSW                                 1   //VSYNC 宽度
#define HFP                                 20  //HSYNC 前的无效像素
#define VFP                                 22  //VSYNC 前的无效行数
#define LCD_WIDTH                           800	// LCD宽度
#define LCD_HEIGHT                          480	// LCD高度

下来对LTDC外设进行配置初始化

static void LCD_Init(void)
{
    LTDC_InitTypeDef LTDC_InitStruct;
    /* 使能 LTDC 外设时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_LTDC, ENABLE);
    /* 使能DMA2D时钟,后面会用到 */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2D, ENABLE);
    /* 配置N = 420,R = 6,Q不参与运算,随意配置 */
    RCC_PLLSAIConfig(420, 7, 6);
    RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div8); //这个函数的参数值为 DIV
    /* 使能 PLLSAI 时钟 */
    RCC_PLLSAICmd(ENABLE);
    /* 等待 PLLSAI 初始化完成 */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET);
    
    /* 行同步信号极性 */
    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 = 0x00;		//背景红通道设置为 0
    LTDC_InitStruct.LTDC_BackgroundGreenValue = 0x00; 	//背景绿通道设置为 0
    LTDC_InitStruct.LTDC_BackgroundBlueValue = 0x00;	//背景蓝通道设置为 0
    
    /* 时间参数配置,此部分根据上节内容中关于LTDC初始化结构体说明来配置 */
    /* 配置行同步信号宽度(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_WIDTH-1;
    /* 配置(VSW+VBP+有效像素高度-1) */
    LTDC_InitStruct.LTDC_AccumulatedActiveH = VSW+VBP+LCD_HEIGHT-1;
    /* 配置总宽度(HSW+HBP+有效像素宽度+HFP-1) */
    LTDC_InitStruct.LTDC_TotalWidth =HSW+ HBP+LCD_WIDTH + HFP-1;
    /* 配置总高度(VSW+VBP+有效像素高度+VFP-1) */
    LTDC_InitStruct.LTDC_TotalHeigh =VSW+ VBP+LCD_HEIGHT + VFP-1;
    
    LTDC_Init(&LTDC_InitStruct);				//初始化LTDC结构体
    LTDC_Cmd(ENABLE);						   //使能LTDC
}

LTDC初始化完成后,需要进行层级初始化

/* 相关宏定义 */
// LCD实际显示宽度
#define LCD_WIDTH                           800
 // LCD实际显示高度
#define LCD_HEIGHT                          480
// 第一层首地址
#define LCD_LAYER1_START_ADDR               0xD0000000 										   
// 第一层液晶的数据量,像素格式选择为RHB565,则此处×2
#define LCD_LAYER1_BUFFER_SIZE              (LCD_WIDTH * LCD_HEIGHT * 2)
// 第二层首地址
#define LCD_LAYER2_START_ADDR               (LCD_LAYER1_START_ADDR + LCD_LAYER1_BUFFER_SIZE)
// 第二层液晶的数据量,像素格式选择为ARGB8888,则此处×4
#define LCD_LAYER2_BUFFER_SIZE              (LCD_WIDTH * LCD_HEIGHT * 4)

/**
  * @brief 初始化 LTD 的 层 参数
  */
static void LCD_LayerInit(void)
{
    LTDC_Layer_InitTypeDef LTDC_Layer_InitStruct;
    /* 层窗口配置 */
    //一行的第一个起始像素,该值应为 (LTDC_InitStruct.LTDC_AccumulatedHBP+1)的值
    LTDC_Layer_InitStruct.LTDC_HorizontalStart = HBP + HSW;
    //一行的最后一个像素,该值应为 (LTDC_InitStruct.LTDC_AccumulatedActiveW)的值
    LTDC_Layer_InitStruct.LTDC_HorizontalStop = HSW+HBP+LCD_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_HEIGHT-1;
    
    /* 像素格式配置为RGB565 */
    LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565;
    
    /* 恒定 Alpha 值配置, 0-255,0为透明,255为完全不透明 */
    LTDC_Layer_InitStruct.LTDC_ConstantAlpha = 0xFF;
    LTDC_Layer_InitStruct.LTDC_DefaultColorAlpha = 0x00;
    LTDC_Layer_InitStruct.LTDC_DefaultColorBlue = 0x00;
    LTDC_Layer_InitStruct.LTDC_DefaultColorGreen = 0x00;
    LTDC_Layer_InitStruct.LTDC_DefaultColorRed = 0x00;

    /* 配置混合因子 CA 表示使用恒定 Alpha 值, PAxCA 表示使用像素 Alpha x 恒定 Alpha值 */
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_CA;
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_CA;
    
    /* 配置本层的显存首地址,这里显存地址就定义为SDRAM的起始地址0xD0000000 */
    LTDC_Layer_InitStruct.LTDC_CFBStartAdress = LCD_LAYER1_START_ADDR;	
    //设置行宽实际占用的大小,如果像素格式选择为RGB565,则×2,RGB888则×3
    LTDC_Layer_InitStruct.LTDC_CFBLineLength = LCD_WIDTH * 2 + 3;		    
    LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_HEIGHT ;
    LTDC_Layer_InitStruct.LTDC_CFBPitch = LCD_WIDTH * 2;

    /* 以上面的配置初始化第 1 层*/
    LTDC_LayerInit(LTDC_Layer1, &LTDC_Layer_InitStruct);
    
    //设置像素格式为ARGB8888
    LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_ARGB8888;
    /* 配置混合因子,使用像素 Alpha 参与混合 */
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA;
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA;
    
    /* 配置本层的显存首地址,这里配置它紧挨在第 1 层的后面*/
    LTDC_Layer_InitStruct.LTDC_CFBStartAdress = LCD_LAYER2_START_ADDR;
    //设置行宽实际占用的大小,如果像素格式选择为ARGB8888,则×4
    LTDC_Layer_InitStruct.LTDC_CFBLineLength = LCD_WIDTH * 4 + 3;
    LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_HEIGHT ;
    LTDC_Layer_InitStruct.LTDC_CFBPitch = LCD_WIDTH * 4;
    
    /* 初始化第 2 层 */
    LTDC_LayerInit(LTDC_Layer2, &LTDC_Layer_InitStruct);
    /*使能前景及背景层 */
    LTDC_LayerCmd(LTDC_Layer1, ENABLE);
    LTDC_LayerCmd(LTDC_Layer2, ENABLE);
 
    /* 立即重载配置 */
    LTDC_ReloadConfig(LTDC_IMReload);
    //使能抖动单元
    LTDC_DitherCmd(ENABLE);
}

以上所有程序写好之后,就可以实现对液晶屏的初步操作了:

/**
  * @brief 显存初始化,就是将显存数据全部清为白色
  */
void VRAM_Init(void)
{
    uint16_t i = 0,j = 0;
    uint32_t *p = (uint32_t *)(LCD_LAYER2_START_ADDR);			//p指针指向第二层首地址,此处需要转换为地址
    for(i = 0;i<LCD_WIDTH;i++)								  //宽高都刷成不透明白色
        for(j = 0;j < LCD_HEIGHT;j++)
        {
            *p = 0xffffffff;
            p++;
        }
}
void Draw_Line(void)
{
    uint16_t i = 0,j = 0;
    uint32_t *p = (uint32_t *)(LCD_LAYER2_START_ADDR);
    for(i = 0;i<LCD_WIDTH;i++)
    for(j = 0;j < LCD_HEIGHT;j++)
    {
        if((i > 200 && i < 220))
            *p = 0xffff0000;
        p++;
    }
}
void LCD_UpLoad(void)
{
    LCD_GPIO_Config();
    LCD_Init();
    LCD_LayerInit();
    VRAM_Init();
    Draw_Line();
}

看一下最终实验效果,忽略屏幕最下面的那条细线(显示屏硬件问题)。

在这里插入图片描述

六、总结

大概总结一下这节的内容,通过LTDC操作液晶屏步骤如下:

  1. 初始化 LTDC 时钟、DMA2D 时钟(下节再学)、 GPIO 时钟;
  2. 初始化 SDRAM,以便用作显存;
  3. 根据液晶屏的参数配置 LTDC 外设的通讯时序;
  4. 配置 LTDC 层级控制参数,配置显存地址;
  5. 初始化 DMA2D,使用DMA2D辅助显示(下节再学);
  6. 编写测试程序,控制液晶输出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1051836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[谷粒商城笔记]08、环境-linux安装docker

1.查看是否已安装docker列表 yum list installed | grep docker 如果没有结果&#xff0c;则说明没有安装docker&#xff0c;我们就可以直接安装了。 2.安装docker: yum -y install docker 如果不是root账号&#xff0c;使用 sudo 以管理员身份运行 sudo yum -y install …

CentOS 7 安装 MySQL5.7

CentOS 7 安装 MySQL5.7 安装wget&#xff1a; yum -y install wget进入/usr/local/下&#xff1a; cd /usr/local/新建mysqlrpm文件夹&#xff1a; mkdir mysqlrpm进入mysqlrpm文件夹下&#xff1a; cd /usr/local/mysqlrpm/下载mysql包安装源&#xff1a; wget http://…

MySQL单表查询与多表查询

目录 一、单表查询 ​编辑 1、显示所有职工的基本信息。 ​编辑2、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 ​编辑3、求出所有职工的人数。 4、列出最高工和最低工资。 ​编辑5、列出职工的平均工资和总工资。 ​编辑6、创建一个只有职…

FreeRTOS两个死机原因(中断调用接口异常)【杂记】

1、中断回调函数中没有使用中断级API (xxFromISR) 函数 xSemaphoreGiveFromISR(uart_busy,&HighterTask);----正确 xSemaphoreGive(uart_busy);-----错误2、比configMAX_SYSCALL_INTERRUPT_PRIORITY优先级高的中断函数中使用了FreeRTOS的函数 3、临界代码保护后不可调用os…

安全基础 --- MySQL数据库的《锁》解析

MySQL的ACID &#xff08;1&#xff09;ACID是衡量事务的四个特性 原子性&#xff08;Atomicity&#xff0c;或称不可分割性&#xff09;一致性&#xff08;Consistency&#xff09;隔离性&#xff08;Isolation&#xff09;持久性&#xff08;Durability&#xff09; &…

(一)TinyWebServer的环境配置与运行

Linux下C轻量级Web服务器&#xff0c;项目来源于&#xff1a;TinyWebServer 配置环境&#xff08;为下载代码&#xff0c;编译运行做准备&#xff09; 1. 安装VMware VMware官网 选择产品&#xff0c;点击Workstation Pro 下载试用版&#xff08;注&#xff1a;需要在官网注册…

Java自学(三)面向对象编程

目录 什么是面向对象 举例 this关键字和构造器 实体类 电影小案例 什么是面向对象 我们日常生活中谈到一个事物&#xff0c;总会描述它的性质与行为&#xff0c;这个事物也就是 ”对象”。比如一个学生对象&#xff0c;他的属性有姓名、学号、成绩......他的行为有上课、…

【C++】C++模板进阶 —— 非类型模板参数、模板的特化以及模板的分离编译

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C学习 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【C】C多…

史玉柱复出一年:巨人股权第三次被冻结,力推AI+游戏

大数据产业创新服务媒体 ——聚焦数据 改变商业 一则股权冻结信息&#xff0c;又一次将复出一年的史玉柱推上风口浪尖。 天眼查APP显示&#xff0c;巨人投资近日新增一则股权冻结信息&#xff0c;被执行人为史玉柱&#xff0c;冻结股权数额1.14亿元&#xff0c;冻结期限为三年…

Visual Studio 中将TAB设置为空格

将TAB设置为空格的原因很多&#xff0c;其中一点是为了统一不同编译器对TAB的解释&#xff0c;防止代码风格在不同编译器下不一致等。 在菜单中选择: 工具-->选项-->文本编辑器--->所有语言-->制表符 在窗口中选择&#xff0c;制表符大小和缩进大小都选为4&#xf…

【从入门到起飞】JavaSE—Stream流

&#x1f38a;专栏【JavaSE】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f970;欢迎并且感谢大家指出我的问题 文章目录 &#x1f354;Stream流的作用&#x1f354;Stream流的使用步骤&#x1f384;获取Strea…

机器人过程自动化(RPA)入门 7. 处理用户事件和助手机器人

在UiPath中,有两种类型的Robot用于自动化任何流程。一个是后台机器人,它在后台工作。它独立工作,这意味着它不需要用户的输入或任何用户交互。另一个是前台机器人,也被称为助理机器人。 本章介绍前台机器人。在这里,我们将了解自动化过程中通过简单按键、单击鼠标等触发事…

代码随想录算法训练营第四十六天 | 动态规划 part 8 | 139.单词拆分、多重背包、背包问题总结

目录 139.单词拆分思路代码 多重背包背包问题总结 139.单词拆分 Leetcode 思路 dp[i] : 字符串长度为i的话&#xff0c;dp[i]为true&#xff0c;表示可以拆分为一个或多个在字典中出现的单词。如果确定dp[j] 是true&#xff0c;且 [j, i] 这个区间的子串出现在字典里&#xf…

仿真数据检查器如何比较数据

可以定制仿真数据检查器比较过程&#xff0c;以多种方式满足您的需求。在比较各运行时&#xff0c;仿真数据检查器会执行以下操作&#xff1a; 根据对齐设置&#xff0c;对齐基线运行和比较项运行中的信号对组。 仿真数据检查器不会比较无法对齐的信号。 根据指定的同步方法同…

Leetcode 剑指 Offer II 046. 二叉树的右视图

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底…

【中秋国庆不断更】OpenHarmony定义可动画属性:@AnimatableExtend装饰器

AnimatableExtend装饰器用于自定义可动画的属性方法&#xff0c;在这个属性方法中修改组件不可动画的属性。在动画执行过程时&#xff0c;通过逐帧回调函数修改不可动画属性值&#xff0c;让不可动画属性也能实现动画效果。 可动画属性&#xff1a;如果一个属性方法在animation…

批量剪辑视频软件,支持免费试用,合成视频,预览不限量不收费

几乎对于所有的短视频创作者来说&#xff0c;批量剪辑软件都是现在或者将来必备的办公软件。现如今市场上的批量剪辑软件也是层出不穷&#xff0c;质量也是良莠不齐。 今天给大家实实在在的推荐一款性价比非常高的批量剪辑软件——超级编导&#xff0c;适用于既想提升团队视频…

volatile关键字以及使用场景

在多线程环境下&#xff0c;如果编程不当&#xff0c;可能会出现程序运行结果混乱的问题。 出现这个原因主要是&#xff0c;JMM 中主内存和线程工作内存的数据不一致&#xff0c;以及多个线程执行时无序&#xff0c;共同导致的结果。 同时也提到引入synchronized同步锁&#x…

Android Studio插件版本与Gradle 版本对应关系

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、Gradle各版本对应关系3.1 Gradle 版…

杀掉进程但是fastapi程序还在运行

两个脚本&#xff0c;一个运行fastapi服务&#xff0c;一个重启服务&#xff1a; 启动服务先&#xff1a; 发现问题&#xff0c;杀掉 server.sh 后&#xff0c;依旧有&#xff1a; 不知道为什么会出现这个&#xff0c;直接kill吧&#xff1a; server.sh: #!/bin/bashparpath/…