【正点原子STM32】LTDC LCD(RGB屏)实验(TFT LCD、RGB LCD信号线、驱动模式、LTDC驱动LCD-TFT显示器、LTDC控制器、DMA2D-GPU、DMA2D颜色填充步骤)

一、RGB LCD简介
二、LTDC介绍

三、RGB屏基本驱动步骤
四、编程实战1
五、DMA2D介绍

六、DMA2D颜色填充的具体步骤
七、编程实战2
八、总结


嵌入式图形系统

在这里插入图片描述
在这里插入图片描述
嵌入式图形系统通常由微处理器、帧缓冲器、显示控制器和显示屏等组成,其工作流程如下:

  1. 微处理器(Microprocessor): 微处理器负责执行程序,并根据程序输出生成要显示的图像数据。这些图像数据通常以像素为单位,表示图像中每个像素的颜色和位置等信息。

  2. 帧缓冲器(Frame Buffer): 帧缓冲器是一块内存区域,用于存储要显示图像的像素数据。在帧缓冲器中,每个像素通常由几个字节表示,其中包括像素的颜色信息以及可能的其他属性,如透明度等。帧缓冲器也被称为图形随机存取存储器(GRAM)。

  3. 显示控制器(Display Controller): 显示控制器负责解析从帧缓冲器中读取的图像数据,并控制显示屏相应地显示这些图像数据。显示控制器通常包括一些硬件模块,如扫描控制器、时序控制器等,用于生成显示信号以及控制显示屏的刷新。

  4. 显示屏(Display Screen): 显示屏是实际用来显示图像的硬件设备,通常由液晶显示器(LCD)、有机发光二极管(OLED)或其他显示技术构成。显示屏接收来自显示控制器的信号,并根据这些信号将图像数据显示在屏幕上。

总的来说,微处理器生成图像数据并存储在帧缓冲器中,显示控制器负责从帧缓冲器中读取图像数据并控制显示屏进行显示,从而完成整个图形系统的工作。

一、RGB LCD简介

在这里插入图片描述
RGB LCD(Red-Green-Blue Liquid Crystal Display)也称为TFT LCD(Thin Film Transistor Liquid Crystal Display),是一种常见的液晶显示技术。与传统的单色LCD相比,RGB LCD能够以全彩方式显示图像,因为它使用了红、绿、蓝三种基本颜色的组合来呈现各种颜色。

以下是RGB LCD的一些特点和工作原理:

  1. 接口类型: RGB LCD使用RGB接口,而不是传统的MCU接口。RGB接口通常包括24根颜色数据线,分别对应红、绿、蓝三种颜色的8根数据线,因此也称为RGB888接口。这种接口能够提供更丰富的颜色表现能力,支持1600万种颜色。

  2. 液晶材料: RGB LCD使用液晶材料作为显示介质,液晶分子的取向可通过电场控制,从而实现像素的开闭。每个像素由三个次像素(子像素)组成,分别对应红、绿、蓝三种颜色。通过控制每个子像素的亮度和色彩,可以呈现出各种颜色和图像。

  3. TFT技术: RGB LCD通常采用TFT(Thin Film Transistor)技术。每个像素都配有一个薄膜晶体管,这些晶体管被用来控制液晶分子的取向,从而实现对像素的精确控制。TFT技术能够提高液晶显示器的刷新率和响应速度,同时也提升了图像的清晰度和色彩饱和度。

  4. 显示控制器: RGB LCD通常配有专用的显示控制器,用于驱动液晶面板并控制图像的显示。显示控制器会接收来自图形处理器或其他图像源的图像数据,并将其转换为液晶面板可识别的信号,从而实现图像的显示。

总的来说,RGB LCD以其丰富的色彩表现能力和高清晰度的图像显示效果,在电子产品中得到了广泛的应用,如智能手机、平板电脑、电视等。
在这里插入图片描述
RGB LCD的信号线包括颜色数据线和控制数据传输线,常见的RGB LCD信号线如下:

  1. 颜色数据线:

    • R [0 : 7]:红色数据线,一般为8位,用于传输像素中红色分量的数据。
    • G [0 : 7]:绿色数据线,一般也为8位,用于传输像素中绿色分量的数据。
    • B [0 : 7]:蓝色数据线,同样为8位,用于传输像素中蓝色分量的数据。
      这三组数据线共同组成了RGB信号,决定了像素点的颜色。
  2. 控制数据传输线:

    • VSYNC:垂直同步信号线,用于同步图像的垂直扫描,通常用于指示图像的一帧开始和结束。
    • HSYNC:水平同步信号线,用于同步图像的水平扫描,通常用于指示图像的每行的开始和结束。
    • DCLK:像素时钟信号线,用于指示像素数据传输的时钟信号,控制数据的传输速率和同步。

这些信号线一起工作,确保图像数据能够按照正确的顺序和时序被传输到LCD面板上,从而正确显示图像。通过控制这些信号线的状态和时序,可以实现图像的显示和刷新,以及对图像的同步控制。
在这里插入图片描述
RGB LCD的驱动模式主要包括DE同步模式和HV同步模式:

  1. DE同步模式(Data Enable 同步模式):

    • 在DE同步模式下,图像数据的有效区域由DE(Data Enable)信号来确定。
    • DE信号指示了像素数据的有效传输时间段,在DE信号有效期间,RGB数据被传输到LCD面板上以显示图像。
    • DE同步模式简单、直观,适用于一些简单的图形显示应用。
  2. HV同步模式(Horizontal and Vertical 同步模式):

    • 在HV同步模式下,图像数据的传输和扫描由水平同步信号(HSYNC)和垂直同步信号(VSYNC)来控制。
    • HSYNC信号用于指示图像的每一行的开始和结束,VSYNC信号用于指示图像的一帧的开始和结束。
    • HV同步模式需要行和场同步信号来表示扫描的行和列,可以更精确地控制图像的扫描和显示,适用于要求更高的图形显示应用。

这两种驱动模式各有特点,可以根据具体的应用需求选择合适的模式来驱动RGB LCD。 DE同步模式相对简单,而HV同步模式则更加灵活,能够实现更多复杂的图像显示功能。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、LTDC介绍

2.1、LTDC简介

在这里插入图片描述
LTDC(LCD-TFT Display Controller)是STM32系列微控制器中的一种外设,用于驱动LCD-TFT显示器。它可以直接连接到RGB LCD屏幕,实现图形显示的功能,而无需额外的显示控制器和图形RAM。以下是LTDC的一些主要特点和功能:

  1. 成本效益:通过使用LTDC外设,可以节约成本,因为它不需要额外的显示控制器和图形RAM。

  2. 适用范围:LTDC具有灵活的并行RGB接口,可以连接低成本的LCD显示模块,从而扩展了其应用范围。

  3. 分辨率可编程:LTDC支持分辨率的编程,可以根据具体的应用需求设置不同的分辨率。其分辨率范围为宽度4096,高度2048,像素时钟高达83MHz。

  4. 颜色格式支持:LTDC支持多种颜色格式,包括RGB888、RGB565、ARGB8888和ARGB1555等,以满足不同应用场景的需求。

  5. 无需额外应用层:LTDC硬件能够完全管理数据读取、RGB输出和信号控制,因此不需要额外的应用层来管理显示操作。

  6. 多层显示数据混合:LTDC支持两层显示数据混合,包括前景层和背景层。通过LTDC混合后,可以将两层数据输出到LCD显示屏上,实现更丰富的显示效果。

总的来说,LTDC是一种强大的LCD-TFT显示控制器,能够为STM32系列微控制器提供高性能的图形显示功能,同时具有灵活的接口和丰富的特性,适用于各种嵌入式图形应用场景。

2.2、LTDC控制器框图介绍

在这里插入图片描述
LTDC(LCD-TFT Display Controller)控制器是STM32微控制器中的一个重要外设,用于驱动LCD-TFT显示器。以下是LTDC控制器的主要组成部分和功能:

  1. 时钟域:LTDC控制器依赖于系统时钟提供时钟信号。其中,HCLK(AHB接口时钟SDRAM)和PCLK(APB接口时钟)用于控制控制器的工作时钟。

  2. AHB主设备接口:LTDC控制器通过AHB总线与系统中的其他主设备进行通信。它包含配置和状态寄存器,用于生成同步时序。

  3. 图像处理单元:LTDC控制器具有图像处理单元,用于处理图像数据并生成相应的显示信号。它包括双层FIFO和PFC(Pixel Format Conversion)模块,用于处理像素数据的转换、混合和抖动等操作。

  4. 信号线:LTDC控制器通过一组信号线与外部LCD显示面板进行通信。这些信号线包括LCD_CLK(像素时钟)、LCD_HSYNC(水平同步信号)、LCD_VSYNC(垂直同步信号)、LCD_DE(数据使能信号)以及RGB数据线(LCD_R、LCD_G、LCD_B)等。

  5. 显示面板:LTDC控制器驱动的显示面板是LCD-TFT类型的显示器,它通过信号线接收来自LTDC控制器的信号,并将图像数据显示在屏幕上。

综上所述,LTDC控制器作为STM32微控制器中的一个外设,扮演着连接系统和LCD-TFT显示器之间的桥梁角色,负责生成适当的时序信号和像素数据,以实现图形数据在显示屏上的显示。
在这里插入图片描述

图像处理单元

在这里插入图片描述
LTDC的图像处理单元是其功能强大的组成部分,主要负责对图像数据进行处理和转换,以便在LCD-TFT显示器上进行正确的显示。下面是图像处理单元的主要功能和流程:

  1. AHB接口:图像数据从系统的存储器(比如SRAM或SDRAM)中通过AHB接口传输到LTDC的图像处理单元。

  2. 双层FIFO:LTDC的图像处理单元包含双层FIFO(First-In-First-Out)缓冲区,用于存储从存储器中读取的图像数据。这些FIFO缓冲区能够在数据传输过程中提供流畅的数据传输,并能够处理大量的图像数据。

  3. 像素格式转换器:图像处理单元还包括像素格式转换器,用于将图像数据从一种像素格式转换为另一种像素格式。例如,它可以将图像数据从RGB565格式转换为ARGB8888格式。

  4. 两层数据混合:LTDC支持两层图层,每一层都可以单独启用、禁用和配置。图层的显示顺序是固定的,由底层到顶层。这两层数据在像素级别进行混合,以生成最终的显示图像。在混合过程中,图像的透明度参数(例如ARGB8888格式中的透明通道)可以用来控制图层之间的透明度。

  5. 抖动色深处理:LTDC的图像处理单元还具有抖动色深处理功能,可以在显示过程中对色深进行处理,以提高图像的质量和准确性。抖动色深处理可以降低色彩的颜色深度,以适应LCD显示器的实际显示能力。

综上所述,LTDC的图像处理单元通过双层FIFO、像素格式转换器、两层数据混合和抖动色深处理等功能,能够对图像数据进行高效处理和转换,从而实现在LCD-TFT显示器上的清晰和逼真的图像显示。

配置和状态寄存器

在这里插入图片描述
配置和状态寄存器在LTDC控制器中起着关键作用,用于控制整个LTDC的工作参数以及监测LTDC的状态。下面是这些寄存器的主要功能和参数:

  1. HSW(Horizontal Synchronization Width):水平同步脉冲的宽度,即水平同步信号的持续时间。

  2. VSW(Vertical Synchronization Width):垂直同步脉冲的宽度,即垂直同步信号的持续时间。

  3. HBP(Horizontal Back Porch):水平后廊,指水平同步信号之后到下一行像素数据开始之间的时间间隔。

  4. HFP(Horizontal Front Porch):水平前廊,指一行像素数据结束后到水平同步信号开始之间的时间间隔。

  5. VBP(Vertical Back Porch):垂直后廊,指垂直同步信号之后到下一帧像素数据开始之间的时间间隔。

  6. VFP(Vertical Front Porch):垂直前廊,指一帧像素数据结束后到垂直同步信号开始之间的时间间隔。

这些参数用于配置LTDC控制器的时序和同步参数,以确保图像在LCD-TFT显示器上正确显示。通过调整这些参数,可以控制显示图像的水平和垂直同步信号的位置、持续时间以及像素数据的有效显示区域,从而实现对图像显示的精确控制。

AHB接口

在这里插入图片描述
在驱动RGBLCD时,确实需要大量的内存作为显存来存储图像数据。对于分辨率为800x480的屏幕,如果使用RGB565格式的图像数据,每个像素占用2字节,那么一帧图像数据所需的内存大小为:

800 × 480 × 2 = 768 K 字节 800 \times 480 \times 2 = 768 \text{K 字节} 800×480×2=768字节

为了高效地处理这么大量的图像数据,STM32系列的微控制器通常采用以下方法:

  1. DMA2D:DMA2D(Direct Memory Access with 2D graphics accelerator)是STM32系列微控制器中的一种专门用于图形加速的DMA控制器。它可以在内部进行像素级别的图像处理,包括颜色转换、缩放、混合等操作,从而大大减轻了CPU的负担。

  2. AHB接口到外部SDRAM:对于大量的图像数据,常常需要外部存储器来提供足够的存储空间。通过使用外部SDRAM(Synchronous Dynamic Random Access Memory),可以实现对大容量图像数据的存储和读取,同时通过AHB接口的高速数据传输,可以提高数据传输效率。

  3. AHB接口到LTDC FIFO:LTDC控制器通过FIFO(First-In-First-Out)来缓存图像数据,以便在每个显示周期中按照指定的时序将图像数据发送到LCD显示器。通过AHB接口将图像数据传输到LTDC的FIFO中,可以确保数据的及时传输和显示。

综上所述,通过DMA2D加速处理图像数据,结合外部SDRAM和LTDC FIFO,可以有效地驱动RGBLCD,并实现高质量的图像显示。

时钟域

在这里插入图片描述

LCD_CLK来源

在这里插入图片描述
在STM32中,LCD_CLK的来源和生成过程如下:

  1. HSE(高速外部时钟)

    • HSE是微控制器的外部时钟源,它通常连接到晶体振荡器或外部时钟源。
  2. PLL(锁相环)

    • PLLSAI(PLL专用于系统和音频)是通过HSE提供的时钟信号进行倍频操作得到的。
  3. PLLSAIN(PLL倍频因子)

    • PLLSAI的倍频因子(xN)用于调节PLL的输出频率。
  4. PLLSAI分频因子(/R)

    • PLLSAI的分频因子(/R)进一步调整PLL的输出频率,以生成与LCD控制器时序要求相匹配的时钟信号。
  5. LCD_CLK(LCD控制器时钟)

    • 经过上述的倍频和分频调节之后,得到的PLL输出信号经过DIV因子的控制,生成LCD控制器所需的LCD_CLK信号。

通过以上步骤,LCD_CLK信号最终被产生,用于驱动LTDC控制器,控制LCD显示。这个过程中,可以根据具体的LCD显示器要求和系统的时钟设置,灵活地调整PLL的倍频和分频参数,以确保LCD_CLK的频率和时序符合LCD显示器的要求。

2.3、LTDC相关寄存器介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在STM32的LTDC控制器中,可以配置背景层和两个图层(图层1和图层2),并且可以进行Alpha混合设置。以下是相关配置的说明:

  1. 背景层设置(LTDC_BCCR)

    • 背景层用于显示纯色背景,配置寄存器为LTDC_BCCR。
    • 可以设置背景层的颜色格式和背景色。
  2. 图层设置(LTDC_LxCR)

    • 图层1和图层2用于显示具体的图像信息,分别对应寄存器LTDC_L1CR和LTDC_L2CR。
    • 需要配置图层的颜色格式、使能状态、像素Alpha值等。
  3. Alpha混合设置

    • 在图层设置中,可以配置Alpha值。Alpha值用于控制图层的透明度。
    • 有两种Alpha设置方式:恒定Alpha和像素Alpha。
      • 恒定Alpha表示整个图层的透明度,取值范围为0x00到0xFF,其中0x00表示完全透明,0xFF表示完全不透明。
      • 像素Alpha表示每个像素的透明度,通常在颜色格式为ARGB8888、ARGB1555、ARGB4444等格式时可用。这些格式中的Alpha通道存储每个像素的透明度信息。

通过配置这些寄存器,可以实现不同图层之间的混合效果,包括透明、半透明等,从而实现丰富的显示效果。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4、LTDC相关HAL库驱动介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

混合简单说明

在这里插入图片描述
在混合操作中,通常会有两个参与混合的颜色层,即当前层的颜色和底层的颜色。这两个颜色层的混合结果将成为最终显示的颜色。混合操作的方式可以根据应用的需求和具体情况进行设置。

在你提供的示例中,有以下设置:

  1. 背景层设置

    • 背景层的颜色为红色(RGB为0xFF, 0x00, 0x00)。
    • 未被图层覆盖的区域,背景层的混合因子为CA(恒定Alpha),Alpha值为255(完全不透明)。
  2. 图层设置

    • 图层的背景色为蓝色(RGB为0x00, 0x00, 0xFF)。
    • 图层未覆盖区域的混合因子为CA(恒定Alpha),Alpha值为127(具有一定透明度)。
  3. 混合公式

    • 混合操作根据混合系数进行计算。在这里,混合系数BF1表示当前层的颜色混合系数,BF2表示底层混合后的颜色混合系数。
    • 混合后的颜色计算公式为:BC = BF1 * C + BF2 * Cs,其中C为当前层的颜色,Cs为底层混合后的颜色。

通过这样的混合设置,可以实现不同层之间的颜色混合效果,从而呈现出丰富多彩的显示效果。

三、RGB屏基本驱动步骤

在这里插入图片描述 RGB 屏基本驱动步骤,这些步骤是配置和初始化 LTDC (LCD-TFT 显示控制器)以驱动 RGB 屏幕所需的关键步骤。这里我将简要解释每个步骤的作用:

  1. 配置 LTDC 底层操作

    • 这一步涉及使能 LTDC 时钟,并配置 LTDC 相关的 IO 接口。LTDC 驱动 RGB 屏幕需要一定的时钟信号以及正确的 IO 接口配置,以便正确发送和接收数据。通过使能 LTDC 时钟和配置 IO 接口,为 LTDC 提供所需的硬件支持。
  2. 设置 LCD_CLK 时钟

    • LCD_CLK 是 LTDC 中的像素时钟,它决定了图像数据的传输速率和时序。在这一步中,通过调用相应的函数(如 HAL_RCCEx_PeriphCLKConfig())来设置 LCD_CLK 的时钟源和分频等参数,确保其满足 RGB 屏幕的工作要求。
  3. 初始化 LTDC

    • 这一步是对 LTDC 进行初始化,即配置 RGB LCD 的相关参数,包括信号极性和时序等。通过调用 HAL_LTDC_Init() 函数来完成 LTDC 的初始化,使其处于可用状态。
  4. 设置 LTDC 层参数

    • 最后一步是设置 LTDC 的图层参数,即配置 LTDC 所需的层的参数。这些参数可能包括图像的分辨率、颜色格式、显示区域等。通过调用 HAL_LTDC_ConfigLayer() 函数来设置 LTDC 的图层参数,确保 LTDC 能够正确地显示图像数据。

通过以上步骤,可以完成对 LTDC 的配置和初始化,使其能够驱动 RGB 屏幕显示图像数据。

四、编程实战1

在这里插入图片描述
在这里插入图片描述

最简点亮RGB屏

ltdc.c

#include "./BSP/LCD/ltdc.h"
#include "./BSP/LCD/lcd.h"

LTDC_HandleTypeDef  g_ltdc_handle;  /* LTDC句柄 */
_ltdc_dev lcdltdc;                  /* 管理LCD LTDC的重要参数 */

/* 4.3 800480 RGB屏 */
void ltdc_init(void)
{
    RCC_PeriphCLKInitTypeDef periphclk_init_struct;
    
    /* 裸屏数据写入到lcdltdc */
    lcdltdc.pwidth = 800;   /* LCD面板的宽度 */
    lcdltdc.pheight = 480;  /* LCD面板的高度 */
    lcdltdc.hsw = 48;       /* 水平同步宽度 */
    lcdltdc.vsw = 3;        /* 垂直同步宽度 */
    lcdltdc.hbp = 88;       /* 水平后廊 */
    lcdltdc.vbp = 32;       /* 垂直后廊 */
    lcdltdc.hfp = 40;       /* 水平前廊 */
    lcdltdc.vfp = 13;       /* 垂直前廊  */
    
    /* 设置LCD CLK ,获得33MHz*/
    periphclk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
    periphclk_init_struct.PLLSAI.PLLSAIN = 396;
    periphclk_init_struct.PLLSAI.PLLSAIR = 3;
    periphclk_init_struct.PLLSAIDivR = RCC_PLLSAIDIVR_4;
    HAL_RCCEx_PeriphCLKConfig(&periphclk_init_struct);
    
    /* LTDC配置 */
    g_ltdc_handle.Instance = LTDC;
    /* 信号极性设置 */
    g_ltdc_handle.Init.HSPolarity = LTDC_HSPOLARITY_AL;     /* 水平同步极性 */
    g_ltdc_handle.Init.VSPolarity = LTDC_VSPOLARITY_AL;     /* 垂直同步极性 */
    g_ltdc_handle.Init.DEPolarity = LTDC_DEPOLARITY_AL;     /* 数据使能极性 */
    g_ltdc_handle.Init.PCPolarity = LTDC_PCPOLARITY_IPC;    /* 像素时钟极性 */
    
    /* 时序参数设置 */
    g_ltdc_handle.Init.HorizontalSync       = lcdltdc.hsw - 1;  /* 水平同步宽度 */
    g_ltdc_handle.Init.VerticalSync         = lcdltdc.vsw - 1;    /* 垂直同步高度 */
    g_ltdc_handle.Init.AccumulatedHBP       = lcdltdc.hsw + lcdltdc.hbp - 1;  /* 水平同步后沿宽度 */
    g_ltdc_handle.Init.AccumulatedVBP       = lcdltdc.vsw + lcdltdc.vbp - 1;  /* 垂直同步后沿高度 */
    g_ltdc_handle.Init.AccumulatedActiveW   = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth - 1;  /* 累加有效宽度 */
    g_ltdc_handle.Init.AccumulatedActiveH   = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight  - 1;  /* 累加有效高度 */
    g_ltdc_handle.Init.TotalWidth           = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth + lcdltdc.hfp - 1;      /* 总宽度 */
    g_ltdc_handle.Init.TotalHeigh           = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight + lcdltdc.vfp - 1;      /* 总高度 */
    
    /* 背景层颜色设置 RGB888*/
    g_ltdc_handle.Init.Backcolor.Red = 0;
    g_ltdc_handle.Init.Backcolor.Green = 0xFF;
    g_ltdc_handle.Init.Backcolor.Blue = 0;

    HAL_LTDC_Init(&g_ltdc_handle);  /* 初始化LTDC */

    LCD_BL(1);  /* 点亮背光 */
}

/* 底层初始化工作 */
void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    __HAL_RCC_LTDC_CLK_ENABLE();                /* 使能LTDC时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();               /* 使能GPIOB时钟 */
    __HAL_RCC_GPIOF_CLK_ENABLE();               /* 使能GPIOF时钟 */
    __HAL_RCC_GPIOG_CLK_ENABLE();               /* 使能GPIOG时钟 */
    __HAL_RCC_GPIOH_CLK_ENABLE();               /* 使能GPIOH时钟 */
    __HAL_RCC_GPIOI_CLK_ENABLE();               /* 使能GPIOI时钟 */
    
    /* 初始化PB5,背光引脚 */
    gpio_init_struct.Pin = GPIO_PIN_5;                /* PB5推挽输出,控制背光 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;      /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;              /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    HAL_GPIO_Init(GPIOB, &gpio_init_struct);
    
    /* 初始化PF10 */
    gpio_init_struct.Pin = GPIO_PIN_10; 
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;          /* 复用 */
    gpio_init_struct.Pull = GPIO_NOPULL;              
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    gpio_init_struct.Alternate = GPIO_AF14_LTDC;      /* 复用为LTDC */
    HAL_GPIO_Init(GPIOF, &gpio_init_struct);
    
    /* 初始化PG6,7,11 */
    gpio_init_struct.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_11;
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);
    
    /* 初始化PH9,10,11,12,13,14,15 */
    gpio_init_struct.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \
                     GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOH, &gpio_init_struct);
    
    /* 初始化PI0,1,2,4,5,6,7,9,10 */
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | \
                     GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOI, &gpio_init_struct); 
}

ltdc.h

#ifndef __LTDC_H
#define __LTDC_H

#include "./SYSTEM/sys/sys.h"

/* LCD LTDC重要参数集 */
typedef struct  
{
    uint32_t pwidth;        /* LCD面板的宽度,固定参数,不随显示方向改变,如果为0,说明没有任何RGB屏接入 */
    uint32_t pheight;       /* LCD面板的高度,固定参数,不随显示方向改变 */
    uint16_t hsw;           /* 水平同步宽度 */
    uint16_t vsw;           /* 垂直同步宽度 */
    uint16_t hbp;           /* 水平后廊 */
    uint16_t vbp;           /* 垂直后廊 */
    uint16_t hfp;           /* 水平前廊 */
    uint16_t vfp;           /* 垂直前廊  */
    uint8_t  activelayer;   /* 当前层编号:0/1 */
    uint8_t  dir;           /* 0,竖屏;1,横屏; */
    uint16_t width;         /* LCD宽度 */
    uint16_t height;        /* LCD高度 */
    uint32_t pixsize;       /* 每个像素所占字节数 */
}_ltdc_dev;

/* 函数声明 */
void ltdc_init(void);

#endif

lcd.c
sdram.c
main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SDRAM/sdram.h"
#include "./BSP/LCD/ltdc.h"

int main(void)
{
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(360, 25, 2, 8);    /* 设置时钟,180Mhz */
    delay_init(180);                        /* 延时初始化 */
    usart_init(115200);                     /* 初始化USART */
    led_init();                             /* 初始化LED */
    key_init();                             /* 初始化KEY */
    sdram_init();                           /* 初始化SDRAM */
    ltdc_init();
    
    while (1)
    {
        LED0_TOGGLE();
        delay_ms(1000);
    }
}

在这里插入图片描述

RGB屏画点和读点

在这里插入图片描述
ltdc.c

#include "./BSP/LCD/ltdc.h"
#include "./BSP/LCD/lcd.h"

LTDC_HandleTypeDef  g_ltdc_handle;  /* LTDC句柄 */
_ltdc_dev lcdltdc;                  /* 管理LCD LTDC的重要参数 */

uint16_t framebuf[1280][800] __attribute__((at (0xC0000000)));

void ltdc_draw_point (uint16_t x, uint16_t y, uint32_t color) 
{
    if (lcdltdc.dir) 
    {   /* 横屏 */
        *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) = color;
    }
    else 
    {   /* 竖屏 */
        *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)) = color; 
    }
}

uint32_t ltdc_read_point (uint16_t x, uint16_t y) 
{
    if (lcdltdc.dir) 
    {   /* 横屏 */
        return *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) ;
    }
    else 
    {  /* 竖屏 */
        return *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)); 
    }
}


void ltdc_layer1_config(void)
{
    LTDC_LayerCfgTypeDef ltdc_layer_cfg;
    
    /* 层显示区域位置设置 */
    ltdc_layer_cfg.WindowX0 = 0;                /* 配置窗口的行起始位置 */ 
    ltdc_layer_cfg.WindowX1 = lcdltdc.pwidth;   /* 配置窗口的行结束位置 */
    ltdc_layer_cfg.WindowY0 = 0;                /* 配置窗口的垂直起始位置 */
    ltdc_layer_cfg.WindowY1 = lcdltdc.pheight;  /* 配置窗口的垂直结束位置*/
    
    /* 层像素格式设置 */
    ltdc_layer_cfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; /* 配置当前层的像素格式 */
    
    /* 层混合因数设置 */
    ltdc_layer_cfg.Alpha = 255;     /* 255完全不透明 配置当前层的恒定Alpha值(透明度) */
    ltdc_layer_cfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;  /* 配置混合因子 */
    ltdc_layer_cfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;  /* 配置混合因子 */
    
    /* 层帧缓冲器设置 */
    ltdc_layer_cfg.FBStartAdress = (uint32_t)framebuf;  /* 配置当前层的显存起始位置 */
    ltdc_layer_cfg.ImageWidth = lcdltdc.pwidth;         /* 配置当前层的图像宽度 */
    ltdc_layer_cfg.ImageHeight = lcdltdc.pheight;       /* 配置当前层的图像高度 */
    
    /* 层背景层颜色设置 */
    ltdc_layer_cfg.Alpha0 = 0;          /* 配置当前层的默认透明值 */
    ltdc_layer_cfg.Backcolor.Red = 0;
    ltdc_layer_cfg.Backcolor.Blue = 0;
    ltdc_layer_cfg.Backcolor.Green = 0;

    HAL_LTDC_ConfigLayer(&g_ltdc_handle, &ltdc_layer_cfg, LTDC_LAYER_1);
}

/* 4.3 800480 RGB屏 */
void ltdc_init(void)
{
    RCC_PeriphCLKInitTypeDef periphclk_init_struct;
    
    /* 裸屏数据写入到lcdltdc */
    lcdltdc.pwidth = 800;   /* LCD面板的宽度 */
    lcdltdc.pheight = 480;  /* LCD面板的高度 */
    lcdltdc.hsw = 48;       /* 水平同步宽度 */
    lcdltdc.vsw = 3;        /* 垂直同步宽度 */
    lcdltdc.hbp = 88;       /* 水平后廊 */
    lcdltdc.vbp = 32;       /* 垂直后廊 */
    lcdltdc.hfp = 40;       /* 水平前廊 */
    lcdltdc.vfp = 13;       /* 垂直前廊  */
    
    lcdltdc.dir = 1;
    lcdltdc.pixsize = 2;
    
    lcddev.width = lcdltdc.pwidth;
    lcddev.height = lcdltdc.pheight;
    
    /* 设置LCD CLK ,获得33MHz*/
    periphclk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
    periphclk_init_struct.PLLSAI.PLLSAIN = 396;
    periphclk_init_struct.PLLSAI.PLLSAIR = 3;
    periphclk_init_struct.PLLSAIDivR = RCC_PLLSAIDIVR_4;
    HAL_RCCEx_PeriphCLKConfig(&periphclk_init_struct);
    
    /* LTDC配置 */
    g_ltdc_handle.Instance = LTDC;
    /* 信号极性设置 */
    g_ltdc_handle.Init.HSPolarity = LTDC_HSPOLARITY_AL;     /* 水平同步极性 */
    g_ltdc_handle.Init.VSPolarity = LTDC_VSPOLARITY_AL;     /* 垂直同步极性 */
    g_ltdc_handle.Init.DEPolarity = LTDC_DEPOLARITY_AL;     /* 数据使能极性 */
    g_ltdc_handle.Init.PCPolarity = LTDC_PCPOLARITY_IPC;    /* 像素时钟极性 */
    
    /* 时序参数设置 */
    g_ltdc_handle.Init.HorizontalSync       = lcdltdc.hsw - 1;  /* 水平同步宽度 */
    g_ltdc_handle.Init.VerticalSync         = lcdltdc.vsw - 1;    /* 垂直同步高度 */
    g_ltdc_handle.Init.AccumulatedHBP       = lcdltdc.hsw + lcdltdc.hbp - 1;  /* 水平同步后沿宽度 */
    g_ltdc_handle.Init.AccumulatedVBP       = lcdltdc.vsw + lcdltdc.vbp - 1;  /* 垂直同步后沿高度 */
    g_ltdc_handle.Init.AccumulatedActiveW   = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth - 1;  /* 累加有效宽度 */
    g_ltdc_handle.Init.AccumulatedActiveH   = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight  - 1;  /* 累加有效高度 */
    g_ltdc_handle.Init.TotalWidth           = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth + lcdltdc.hfp - 1;      /* 总宽度 */
    g_ltdc_handle.Init.TotalHeigh           = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight + lcdltdc.vfp - 1;      /* 总高度 */
    
    /* 背景层颜色设置 RGB888*/
    g_ltdc_handle.Init.Backcolor.Red = 0;
    g_ltdc_handle.Init.Backcolor.Green = 0xFF;
    g_ltdc_handle.Init.Backcolor.Blue = 0;

    HAL_LTDC_Init(&g_ltdc_handle);  /* 初始化LTDC */

    ltdc_layer1_config();
    
    LCD_BL(1);  /* 点亮背光 */
}

/* 底层初始化工作 */
void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    __HAL_RCC_LTDC_CLK_ENABLE();                /* 使能LTDC时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();               /* 使能GPIOB时钟 */
    __HAL_RCC_GPIOF_CLK_ENABLE();               /* 使能GPIOF时钟 */
    __HAL_RCC_GPIOG_CLK_ENABLE();               /* 使能GPIOG时钟 */
    __HAL_RCC_GPIOH_CLK_ENABLE();               /* 使能GPIOH时钟 */
    __HAL_RCC_GPIOI_CLK_ENABLE();               /* 使能GPIOI时钟 */
    
    /* 初始化PB5,背光引脚 */
    gpio_init_struct.Pin = GPIO_PIN_5;                /* PB5推挽输出,控制背光 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;      /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;              /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    HAL_GPIO_Init(GPIOB, &gpio_init_struct);
    
    /* 初始化PF10 */
    gpio_init_struct.Pin = GPIO_PIN_10; 
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;          /* 复用 */
    gpio_init_struct.Pull = GPIO_NOPULL;              
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    gpio_init_struct.Alternate = GPIO_AF14_LTDC;      /* 复用为LTDC */
    HAL_GPIO_Init(GPIOF, &gpio_init_struct);
    
    /* 初始化PG6,7,11 */
    gpio_init_struct.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_11;
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);
    
    /* 初始化PH9,10,11,12,13,14,15 */
    gpio_init_struct.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \
                     GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOH, &gpio_init_struct);
    
    /* 初始化PI0,1,2,4,5,6,7,9,10 */
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | \
                     GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOI, &gpio_init_struct); 
}

ltdc.h

#ifndef __LTDC_H
#define __LTDC_H

#include "./SYSTEM/sys/sys.h"



/* LCD LTDC重要参数集 */
typedef struct  
{
    uint32_t pwidth;        /* LCD面板的宽度,固定参数,不随显示方向改变,如果为0,说明没有任何RGB屏接入 */
    uint32_t pheight;       /* LCD面板的高度,固定参数,不随显示方向改变 */
    uint16_t hsw;           /* 水平同步宽度 */
    uint16_t vsw;           /* 垂直同步宽度 */
    uint16_t hbp;           /* 水平后廊 */
    uint16_t vbp;           /* 垂直后廊 */
    uint16_t hfp;           /* 水平前廊 */
    uint16_t vfp;           /* 垂直前廊  */
    uint8_t  activelayer;   /* 当前层编号:0/1 */
    uint8_t  dir;           /* 0,竖屏;1,横屏; */
    uint16_t width;         /* LCD宽度 */
    uint16_t height;        /* LCD高度 */
    uint32_t pixsize;       /* 每个像素所占字节数 */
}_ltdc_dev;

extern _ltdc_dev lcdltdc;                  /* 管理LCD LTDC的重要参数 */


/* 函数声明 */
void ltdc_init(void);
void ltdc_layer1_config(void);
void ltdc_draw_point (uint16_t x, uint16_t y, uint32_t color) ;
uint32_t ltdc_read_point (uint16_t x, uint16_t y) ;

#endif

lcd.c
sdram.c
main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SDRAM/sdram.h"
#include "./BSP/LCD/ltdc.h"

int main(void)
{
    uint16_t pix_color = 0;
    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(360, 25, 2, 8);    /* 设置时钟,180Mhz */
    delay_init(180);                        /* 延时初始化 */
    usart_init(115200);                     /* 初始化USART */
    led_init();                             /* 初始化LED */
    key_init();                             /* 初始化KEY */
    sdram_init();                           /* 初始化SDRAM */
    ltdc_init();

    ltdc_draw_point (100, 100, RED);
    ltdc_draw_point (110, 110, RED);
    ltdc_draw_point (120, 120, RED);
    ltdc_draw_point (130, 130, RED);
    ltdc_draw_point (140, 140, RED);
    ltdc_draw_point (150, 150, RED);
    ltdc_draw_point (101, 101, RED);
    ltdc_draw_point (111, 111, RED);
    ltdc_draw_point (121, 121, RED);
    ltdc_draw_point (131, 131, RED);
    ltdc_draw_point (141, 141, RED);
    ltdc_draw_point (151, 151, RED);
    
    pix_color = ltdc_read_point (151, 151);
    printf("pix_color:%#x \r\n",pix_color);
    
    lcd_show_char(100, 100, 'A', 16, 1, 0xF81F);

    while (1)
    {
        LED0_TOGGLE();
        delay_ms(1000);
    }
}

在这里插入图片描述

五、DMA2D介绍

5.1、DMA2D简介

在这里插入图片描述
DMA2D(Direct Memory Access 2D)是STMicroelectronics(ST)提供的专用于图形处理的DMA引擎,有时也被称为“GPU”(图形处理器单元)。它主要用于处理图像数据,提供了 DMA 数据传输和 2D 图形加速功能。

  1. DMA 数据传输功能

    • DMA2D 提供了灵活的 DMA 数据传输功能,包括从寄存器到存储器和从存储器到存储器的数据传输。它可以用于颜色填充(如矩形区域)、图像复制等操作,大大提高了数据传输的效率。
  2. 2D 图形加速功能

    • DMA2D 还提供了针对图形处理的硬件加速功能。其中,最重要的功能包括硬件颜色格式转换和 Alpha 混合效果。这些功能使得 DMA2D 可以在硬件级别上执行颜色格式的转换(例如,YCbCr 转换为 RGB 或 RGB888 转换为 RGB565)以及透明度混合等操作,大大提高了图形处理的效率和性能。
  3. 工作模式

    • DMA2D 可以在多种工作模式下运行,包括:
      • 寄存器到存储器模式:用于快速单色填充,例如清屏操作。
      • 存储器到存储器模式:用于快速图像填充,例如图像的复制。
      • 存储器到存储器并执行像素颜色格式转换:支持像素数据的颜色格式转换。
      • 存储器到存储器且支持像素颜色格式转换和透明度混合:在数据传输的同时,支持颜色格式转换和透明度混合操作,提高了图形处理的灵活性和效率。

通过 DMA2D,开发人员可以实现高效的图形处理操作,加速图像数据的传输和处理,提高系统的性能和响应速度。

5.2、DMA2D框图介绍

在这里插入图片描述
在这里插入图片描述
在DMA2D中,可以对前景层(Foreground)和背景层(Background)进行输入和处理,具体包括以下功能:

  1. 前景层输入:指定要处理的前景图像数据源,可以是存储器中的图像数据,也可以是寄存器中的数据。

  2. 背景层输入:指定背景图像数据源,同样可以是存储器中的数据或寄存器中的数据。

  3. 前景层颜色格式转换:DMA2D支持对前景层的颜色格式进行转换,例如从RGB888转换为RGB565等。

  4. 背景层颜色格式转换:同样地,DMA2D也支持对背景层的颜色格式进行转换,以满足不同的显示需求。

  5. 前景层和背景层的处理:DMA2D可以对前景层和背景层分别进行处理,也可以将它们进行混合处理。

在不同的工作模式下,DMA2D的处理方式略有不同:

  • 寄存器到存储器:在这种模式下,仅输出阶段会被激活,前景层和背景层不需要输入,只需设置输出的目标存储器地址即可。

  • 存储器到存储器:在这种模式下,前景层FIFO会被激活作为缓冲区,用于存储前景图像数据,而背景层则不被激活。

  • 存储器到存储器并执行像素格式转换(PFC):这种模式下,虽然会执行像素格式转换,但不会激活背景层FIFO。

  • 存储器到存储器并执行混合:在执行像素格式转换和混合时,前景层和背景层的数据都会被获取和处理,以进行混合操作。
    在这里插入图片描述

5.3、DMA2D相关寄存器介绍

在这里插入图片描述
STM32的“GPU”——DMA2D实例详解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、DMA2D颜色填充的具体步骤

在这里插入图片描述
以上是使用DMA2D进行颜色填充的基本步骤。下面是每个步骤的具体说明:

  1. 使能DMA2D时钟并停止DMA2D

    • 首先,通过__HAL_RCC_DMA2D_CLK_ENABLE()函数使能DMA2D时钟。
    • 然后,通过将DMA2D控制寄存器(DMA2D_CR)中的START位清零停止DMA2D传输。
  2. 设置DMA2D工作模式

    • 在这一步中,根据具体的需求设置DMA2D的工作模式。可以是寄存器到存储器(R2M)模式,也可以是存储器到存储器(M2M)模式。
  3. 设置DMA2D的相关参数

    • 设置颜色格式,例如RGB565、RGB888等。
    • 设置输出窗口,即要填充的区域的大小和位置。
    • 设置输出存储器地址,指定填充后的数据存储的地址。
    • 如果是存储器到存储器模式(M2M),则需要设置前景层地址;如果是寄存器到存储器模式(R2M),则需要设置颜色寄存器。
  4. 启动DMA2D传输

    • 设置DMA2D控制寄存器中的START位,启动DMA2D传输。
  5. 等待DMA2D传输完成,清除相关标志

    • 在这一步中,通过轮询DMA2D状态寄存器(DMA2D_ISR)中的传输完成标志位(DMA2D_FLAG_TC),等待DMA2D传输完成。
    • 一旦传输完成,清除传输完成标志位,可以通过将相应的位写为1来清除。

以上步骤完成后,DMA2D就会按照设置的参数进行颜色填充操作。

七、编程实战2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

RGB屏DMA2D-单色填充

ltdc.c

#include "./BSP/LCD/ltdc.h"
#include "./BSP/LCD/lcd.h"

LTDC_HandleTypeDef  g_ltdc_handle;  /* LTDC句柄 */
_ltdc_dev lcdltdc;                  /* 管理LCD LTDC的重要参数 */

uint16_t framebuf[1280][800] __attribute__((at (0xC0000000)));

void dma2d_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
{
    uint32_t addr;
    uint32_t offline = 0;

    uint32_t psx, psy, pex, pey;   /* 以LCD面板为基准的坐标系,不随横竖屏变化而变化 */

    /* 坐标系转换 */
    if (lcdltdc.dir)               /* 横屏 */
    {
        psx = sx;
        psy = sy;
        pex = ex;
        pey = ey;
    }
    else                          /* 竖屏 */
    {
        psx = sy;
        psy = lcdltdc.pheight - ex - 1;
        pex = ey;
        pey = lcdltdc.pheight - sx - 1;
    }
    
    /* 横屏 */
    addr = (uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * psy + psx);
    offline = lcdltdc.pwidth - (pex - psx + 1);
    
    /* 使能DMA2D时钟并停止DMA2D */
    __HAL_RCC_DMA2D_CLK_ENABLE();
    DMA2D->CR &= ~DMA2D_CR_START;
    
    /* 设置DMA2D工作模式 */
    DMA2D->CR = DMA2D_R2M;  /* 寄存器到存储器 */
    
    /* 设置DMA2D的相关参数 */
    /* 颜色格式设置(OPFCCR) */
    DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
    
    /* 输出存储器地址(OMAR) */
    DMA2D->OMAR = addr;
    
    /* 输出窗口(OOR NLR) */
    DMA2D->OOR = offline;
    DMA2D->NLR = ((pex - psx + 1) << 16) | (pey - psy + 1); /* 像素数 */
    
    /* 颜色寄存器(仅R2M时设置) OCOLR */
    DMA2D->OCOLR = color;
    
    /* 启动DMA2D传输 */
    DMA2D->CR |= DMA2D_CR_START;    /* 启动DMA2D */

    /* 等待DMA2D传输完成,清除相关标志 */
    while((DMA2D->ISR & DMA2D_FLAG_TC) == 0);
    DMA2D->IFCR |= DMA2D_FLAG_TC;
}

void dma2d_fill_color(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t *color)
{
    uint32_t addr;
    uint32_t offline = 0;

    uint32_t psx, psy, pex, pey;   /* 以LCD面板为基准的坐标系,不随横竖屏变化而变化 */

    /* 坐标系转换 */
    if (lcdltdc.dir)               /* 横屏 */
    {
        psx = sx;
        psy = sy;
        pex = ex;
        pey = ey;
    }
    else                          /* 竖屏 */
    {
        psx = sy;
        psy = lcdltdc.pheight - ex - 1;
        pex = ey;
        pey = lcdltdc.pheight - sx - 1;
    }
    
    /* 横屏 */
    addr = (uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * psy + psx);
    offline = lcdltdc.pwidth - (pex - psx + 1);
    
    /* 使能DMA2D时钟并停止DMA2D */
    __HAL_RCC_DMA2D_CLK_ENABLE();
    DMA2D->CR &= ~DMA2D_CR_START;
    
    /* 设置DMA2D工作模式 */
    DMA2D->CR = DMA2D_M2M;  /* 存储器到存储器 */
    
    /* 设置DMA2D的相关参数 */
    /* 颜色格式设置(FGPFCCR) */
    DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565;
    
    /* 输入 & 输出存储器地址(FGMAR & OMAR) */
    DMA2D->FGMAR = (uint32_t)color;
    DMA2D->OMAR = addr;
    
    /* 输出窗口(FGOR OOR NLR) */
    DMA2D->FGOR = 0;
    DMA2D->OOR = offline;
    DMA2D->NLR = ((pex - psx + 1) << 16) | (pey - psy + 1); /* 像素数 */
    
    /* 启动DMA2D传输 */
    DMA2D->CR |= DMA2D_CR_START;    /* 启动DMA2D */

    /* 等待DMA2D传输完成,清除相关标志 */
    while((DMA2D->ISR & DMA2D_FLAG_TC) == 0);
    DMA2D->IFCR |= DMA2D_FLAG_TC;
}



void ltdc_draw_point (uint16_t x, uint16_t y, uint32_t color) 
{
    if (lcdltdc.dir) 
    {   /* 横屏 */
        *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) = color;
    }
    else 
    {   /* 竖屏 */
        *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)) = color; 
    }
}

uint32_t ltdc_read_point (uint16_t x, uint16_t y) 
{
    if (lcdltdc.dir) 
    {   /* 横屏 */
        return *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) ;
    }
    else 
    {  /* 竖屏 */
        return *(uint16_t *)((uint32_t)framebuf + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)); 
    }
}


void ltdc_layer1_config(void)
{
    LTDC_LayerCfgTypeDef ltdc_layer_cfg;
    
    /* 层显示区域位置设置 */
    ltdc_layer_cfg.WindowX0 = 0;                /* 配置窗口的行起始位置 */ 
    ltdc_layer_cfg.WindowX1 = lcdltdc.pwidth;   /* 配置窗口的行结束位置 */
    ltdc_layer_cfg.WindowY0 = 0;                /* 配置窗口的垂直起始位置 */
    ltdc_layer_cfg.WindowY1 = lcdltdc.pheight;  /* 配置窗口的垂直结束位置*/
    
    /* 层像素格式设置 */
    ltdc_layer_cfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; /* 配置当前层的像素格式 */
    
    /* 层混合因数设置 */
    ltdc_layer_cfg.Alpha = 255;     /* 255完全不透明 配置当前层的恒定Alpha值(透明度) */
    ltdc_layer_cfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;  /* 配置混合因子 */
    ltdc_layer_cfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;  /* 配置混合因子 */
    
    /* 层帧缓冲器设置 */
    ltdc_layer_cfg.FBStartAdress = (uint32_t)framebuf;  /* 配置当前层的显存起始位置 */
    ltdc_layer_cfg.ImageWidth = lcdltdc.pwidth;         /* 配置当前层的图像宽度 */
    ltdc_layer_cfg.ImageHeight = lcdltdc.pheight;       /* 配置当前层的图像高度 */
    
    /* 层背景层颜色设置 */
    ltdc_layer_cfg.Alpha0 = 0;          /* 配置当前层的默认透明值 */
    ltdc_layer_cfg.Backcolor.Red = 0;
    ltdc_layer_cfg.Backcolor.Blue = 0;
    ltdc_layer_cfg.Backcolor.Green = 0;

    HAL_LTDC_ConfigLayer(&g_ltdc_handle, &ltdc_layer_cfg, LTDC_LAYER_1);
}

/* 4.3 800480 RGB屏 */
void ltdc_init(void)
{
    RCC_PeriphCLKInitTypeDef periphclk_init_struct;
    
    /* 裸屏数据写入到lcdltdc */
    lcdltdc.pwidth = 800;   /* LCD面板的宽度 */
    lcdltdc.pheight = 480;  /* LCD面板的高度 */
    lcdltdc.hsw = 48;       /* 水平同步宽度 */
    lcdltdc.vsw = 3;        /* 垂直同步宽度 */
    lcdltdc.hbp = 88;       /* 水平后廊 */
    lcdltdc.vbp = 32;       /* 垂直后廊 */
    lcdltdc.hfp = 40;       /* 水平前廊 */
    lcdltdc.vfp = 13;       /* 垂直前廊  */
    
    lcdltdc.dir = 1;
    lcdltdc.pixsize = 2;
    
    lcddev.width = lcdltdc.pwidth;
    lcddev.height = lcdltdc.pheight;
    
    /* 设置LCD CLK ,获得33MHz*/
    periphclk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
    periphclk_init_struct.PLLSAI.PLLSAIN = 396;
    periphclk_init_struct.PLLSAI.PLLSAIR = 3;
    periphclk_init_struct.PLLSAIDivR = RCC_PLLSAIDIVR_4;
    HAL_RCCEx_PeriphCLKConfig(&periphclk_init_struct);
    
    /* LTDC配置 */
    g_ltdc_handle.Instance = LTDC;
    /* 信号极性设置 */
    g_ltdc_handle.Init.HSPolarity = LTDC_HSPOLARITY_AL;     /* 水平同步极性 */
    g_ltdc_handle.Init.VSPolarity = LTDC_VSPOLARITY_AL;     /* 垂直同步极性 */
    g_ltdc_handle.Init.DEPolarity = LTDC_DEPOLARITY_AL;     /* 数据使能极性 */
    g_ltdc_handle.Init.PCPolarity = LTDC_PCPOLARITY_IPC;    /* 像素时钟极性 */
    
    /* 时序参数设置 */
    g_ltdc_handle.Init.HorizontalSync       = lcdltdc.hsw - 1;  /* 水平同步宽度 */
    g_ltdc_handle.Init.VerticalSync         = lcdltdc.vsw - 1;    /* 垂直同步高度 */
    g_ltdc_handle.Init.AccumulatedHBP       = lcdltdc.hsw + lcdltdc.hbp - 1;  /* 水平同步后沿宽度 */
    g_ltdc_handle.Init.AccumulatedVBP       = lcdltdc.vsw + lcdltdc.vbp - 1;  /* 垂直同步后沿高度 */
    g_ltdc_handle.Init.AccumulatedActiveW   = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth - 1;  /* 累加有效宽度 */
    g_ltdc_handle.Init.AccumulatedActiveH   = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight  - 1;  /* 累加有效高度 */
    g_ltdc_handle.Init.TotalWidth           = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth + lcdltdc.hfp - 1;      /* 总宽度 */
    g_ltdc_handle.Init.TotalHeigh           = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight + lcdltdc.vfp - 1;      /* 总高度 */
    
    /* 背景层颜色设置 RGB888*/
    g_ltdc_handle.Init.Backcolor.Red = 0;
    g_ltdc_handle.Init.Backcolor.Green = 0xFF;
    g_ltdc_handle.Init.Backcolor.Blue = 0;

    HAL_LTDC_Init(&g_ltdc_handle);  /* 初始化LTDC */

    ltdc_layer1_config();
    
    LCD_BL(1);  /* 点亮背光 */
}

/* 底层初始化工作 */
void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    __HAL_RCC_LTDC_CLK_ENABLE();                /* 使能LTDC时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();               /* 使能GPIOB时钟 */
    __HAL_RCC_GPIOF_CLK_ENABLE();               /* 使能GPIOF时钟 */
    __HAL_RCC_GPIOG_CLK_ENABLE();               /* 使能GPIOG时钟 */
    __HAL_RCC_GPIOH_CLK_ENABLE();               /* 使能GPIOH时钟 */
    __HAL_RCC_GPIOI_CLK_ENABLE();               /* 使能GPIOI时钟 */
    
    /* 初始化PB5,背光引脚 */
    gpio_init_struct.Pin = GPIO_PIN_5;                /* PB5推挽输出,控制背光 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;      /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;              /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    HAL_GPIO_Init(GPIOB, &gpio_init_struct);
    
    /* 初始化PF10 */
    gpio_init_struct.Pin = GPIO_PIN_10; 
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;          /* 复用 */
    gpio_init_struct.Pull = GPIO_NOPULL;              
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    gpio_init_struct.Alternate = GPIO_AF14_LTDC;      /* 复用为LTDC */
    HAL_GPIO_Init(GPIOF, &gpio_init_struct);
    
    /* 初始化PG6,7,11 */
    gpio_init_struct.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_11;
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);
    
    /* 初始化PH9,10,11,12,13,14,15 */
    gpio_init_struct.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \
                     GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOH, &gpio_init_struct);
    
    /* 初始化PI0,1,2,4,5,6,7,9,10 */
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | \
                     GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOI, &gpio_init_struct); 
}

ltdc.h

#ifndef __LTDC_H
#define __LTDC_H

#include "./SYSTEM/sys/sys.h"



/* LCD LTDC重要参数集 */
typedef struct  
{
    uint32_t pwidth;        /* LCD面板的宽度,固定参数,不随显示方向改变,如果为0,说明没有任何RGB屏接入 */
    uint32_t pheight;       /* LCD面板的高度,固定参数,不随显示方向改变 */
    uint16_t hsw;           /* 水平同步宽度 */
    uint16_t vsw;           /* 垂直同步宽度 */
    uint16_t hbp;           /* 水平后廊 */
    uint16_t vbp;           /* 垂直后廊 */
    uint16_t hfp;           /* 水平前廊 */
    uint16_t vfp;           /* 垂直前廊  */
    uint8_t  activelayer;   /* 当前层编号:0/1 */
    uint8_t  dir;           /* 0,竖屏;1,横屏; */
    uint16_t width;         /* LCD宽度 */
    uint16_t height;        /* LCD高度 */
    uint32_t pixsize;       /* 每个像素所占字节数 */
}_ltdc_dev;

extern _ltdc_dev lcdltdc;                  /* 管理LCD LTDC的重要参数 */


/* 函数声明 */
void ltdc_init(void);
void ltdc_layer1_config(void);
void ltdc_draw_point (uint16_t x, uint16_t y, uint32_t color) ;
uint32_t ltdc_read_point (uint16_t x, uint16_t y) ;

#endif

lcd.c
sdram.c
main.c

#include "./SYSTEM/sys/sys.h"        // 包含系统初始化相关头文件
#include "./SYSTEM/usart/usart.h"    // 包含串口初始化相关头文件
#include "./SYSTEM/delay/delay.h"    // 包含延时函数相关头文件
#include "./BSP/LED/led.h"           // 包含LED初始化相关头文件
#include "./BSP/LCD/lcd.h"           // 包含LCD初始化相关头文件
#include "./BSP/KEY/key.h"           // 包含按键初始化相关头文件
#include "./BSP/SDRAM/sdram.h"       // 包含SDRAM初始化相关头文件
#include "./BSP/LCD/ltdc.h"          // 包含LTDC初始化相关头文件
#include "st_logo.c"                 // 包含ST标志图案数据文件

int main(void)
{
    uint16_t pix_color = 0;  // 定义像素颜色变量

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(360, 25, 2, 8);    /* 设置时钟,180Mhz */
    delay_init(180);                        /* 延时初始化 */
    usart_init(115200);                     /* 初始化USART */
    led_init();                             /* 初始化LED */
    key_init();                             /* 初始化KEY */
    sdram_init();                           /* 初始化SDRAM */
    ltdc_init();                            /* 初始化LTDC */

    // 画点示例
    ltdc_draw_point(100, 100, RED);
    ltdc_draw_point(110, 110, RED);
    ltdc_draw_point(120, 120, RED);
    ltdc_draw_point(130, 130, RED);
    ltdc_draw_point(140, 140, RED);
    ltdc_draw_point(150, 150, RED);
    ltdc_draw_point(101, 101, RED);
    ltdc_draw_point(111, 111, RED);
    ltdc_draw_point(121, 121, RED);
    ltdc_draw_point(131, 131, RED);
    ltdc_draw_point(141, 141, RED);
    ltdc_draw_point(151, 151, RED);
    
    // 读取像素颜色示例
    pix_color = ltdc_read_point(151, 151);
    printf("pix_color:%#x \r\n", pix_color);
    
    // 在LCD上显示字符示例
    lcd_show_char(100, 100, 'A', 16, 1, 0xF81F);
    
    // DMA2D填充颜色示例
    dma2d_fill(0, 0, 200, 200, BLUE);
    
    // DMA2D填充图片示例
    dma2d_fill_color(0, 0, 479, 136, gImage_st_logo);
    
    while (1)
    {
        LED0_TOGGLE();  // LED0翻转
        delay_ms(1000); // 延时1秒
    }
}

在这里插入图片描述

LTDC LCD(RGB屏)源码

ltdc.c

#include "./BSP/LCD/ltdc.h"
#include "./BSP/LCD/lcd.h"

LTDC_HandleTypeDef  g_ltdc_handle;       /* LTDC句柄 */
DMA2D_HandleTypeDef g_dma2d_handle;      /* DMA2D句柄 */

#if !(__ARMCC_VERSION >= 6010050)                                                    /* 不是AC6编译器,即使用AC5编译器时 */

/* 根据不同的颜色格式,定义帧缓存数组 */
#if LCD_PIXFORMAT == LCD_PIXFORMAT_ARGB8888 || LCD_PIXFORMAT == LCD_PIXFORMAT_RGB888
    uint32_t ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR)));   /* 定义最大屏分辨率时,LCD所需的帧缓存数组大小 */
#else
    uint16_t ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR)));   /* 定义最大屏分辨率时,LCD所需的帧缓存数组大小 */
//    uint16_t ltdc_lcd_framebuf1[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR + 1280 * 800 * 2)));   /* 使能LTDC层2时使用(默认使用LTDC层1) */
#endif

#else      /* 使用AC6编译器时 */

/* 根据不同的颜色格式,定义帧缓存数组 */
#if LCD_PIXFORMAT == LCD_PIXFORMAT_ARGB8888 || LCD_PIXFORMAT == LCD_PIXFORMAT_RGB888
    uint32_t ltdc_lcd_framebuf[1280][800] __attribute__((section(".bss.ARM.__at_0XC0000000")));  /* 定义最大屏分辨率时,LCD所需的帧缓存数组大小 */
#else
    uint16_t ltdc_lcd_framebuf[1280][800] __attribute__((section(".bss.ARM.__at_0XC0000000")));  /* 定义最大屏分辨率时,LCD所需的帧缓存数组大小 */
#endif

#endif

uint32_t *ltdc_framebuf[2];                /* LTDC LCD帧缓存数组指针,必须指向对应大小的内存区域 */
_ltdc_dev lcdltdc;                         /* 管理LCD LTDC的重要参数 */

/**
 * @brief       打开LCD开关
 * @param       lcd_switch:1 打开,0,关闭
 * @retval      无
 */
void ltdc_switch(uint8_t sw)
{
    if (sw == 1)
    {
        __HAL_LTDC_ENABLE(&g_ltdc_handle);
    }
    else if (sw == 0)
    {
        __HAL_LTDC_DISABLE(&g_ltdc_handle);
    }
}

/**
 * @brief       开关指定层
 * @param       layerx:层号,0,第一层; 1,第二层
 * @param       sw:1 打开;0关闭
 * @retval      无
 */
void ltdc_layer_switch(uint8_t layerx, uint8_t sw)
{
    if (sw == 1) 
    {
        __HAL_LTDC_LAYER_ENABLE(&g_ltdc_handle, layerx);
    }
    else if (sw == 0) 
    {
        __HAL_LTDC_LAYER_DISABLE(&g_ltdc_handle, layerx);
    }
    
    __HAL_LTDC_RELOAD_CONFIG(&g_ltdc_handle);
}

/**
 * @brief       选择层
 * @param       layerx:层号;0,第一层;1,第二层;
 * @retval      无
 */
void ltdc_select_layer(uint8_t layerx)
{
    lcdltdc.activelayer = layerx;
}

/**
 * @brief       设置LCD显示方向
 * @param       dir:0,竖屏;1,横屏
 * @retval      无
 */
void ltdc_display_dir(uint8_t dir)
{
    lcdltdc.dir = dir;    /* 显示方向 */
    
    if (dir == 0)         /* 竖屏 */
    {
        lcdltdc.width = lcdltdc.pheight;
        lcdltdc.height = lcdltdc.pwidth;
    }
    else if (dir == 1)    /* 横屏 */
    {
        lcdltdc.width = lcdltdc.pwidth;
        lcdltdc.height = lcdltdc.pheight;
    }
}

/**
 * @brief       画点函数
 * @param       x,y:写入坐标
 * @param       color:颜色值
 * @retval      无
 */
void ltdc_draw_point(uint16_t x, uint16_t y, uint32_t color)
{ 
#if LCD_PIXFORMAT == LCD_PIXFORMAT_ARGB8888 || LCD_PIXFORMAT == LCD_PIXFORMAT_RGB888
    if (lcdltdc.dir)   /* 横屏 */
    {
        *(uint32_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) = color;
    }
    else               /* 竖屏 */
    {
        *(uint32_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x) + y)) = color; 
    }
#else
    if (lcdltdc.dir)   /* 横屏 */
    {
        *(uint16_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) = color;
    }
    else              /* 竖屏 */
    {
        *(uint16_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)) = color; 
    }
#endif
}

/**
 * @brief       读点函数
 * @param       x,y:读取点的坐标
 * @param       返回值:颜色值
 * @retval      无
 */
uint32_t ltdc_read_point(uint16_t x, uint16_t y)
{ 
#if LCD_PIXFORMAT == LCD_PIXFORMAT_ARGB8888 || LCD_PIXFORMAT == LCD_PIXFORMAT_RGB888
    if (lcdltdc.dir)   /* 横屏 */
    {
        return *(uint32_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x));
    }
    else               /* 竖屏 */
    {
        return *(uint32_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x) + y)); 
    }
#else
    if (lcdltdc.dir)   /* 横屏 */
    {
        return *(uint16_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x));
    }
    else               /* 竖屏 */
    {
        return *(uint16_t *)((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)); 
    }
#endif 
}

/**
 * @brief       LTDC填充矩形,DMA2D填充
 * @note        有时候需要频繁的调用填充函数,所以为了速度,填充函数采用寄存器版本,
 * @note        不过下面有对应的库函数版本的代码。
 * @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
 * @param       color:要填充的颜色
 * @retval      无
 */
void ltdc_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
{ 
    uint32_t psx, psy, pex, pey;   /* 以LCD面板为基准的坐标系,不随横竖屏变化而变化 */
    uint32_t timeout = 0; 
    uint16_t offline;
    uint32_t addr; 

    /* 坐标系转换 */
    if (lcdltdc.dir)               /* 横屏 */
    {
        psx = sx;
        psy = sy;
        pex = ex;
        pey = ey;
    }
    else                          /* 竖屏 */
    {
        psx = sy;
        psy = lcdltdc.pheight - ex - 1;
        pex = ey;
        pey = lcdltdc.pheight - sx - 1;
    }

    offline = lcdltdc.pwidth - (pex - psx + 1);
    addr = ((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * psy + psx));

    __HAL_RCC_DMA2D_CLK_ENABLE();                              /* 使能DM2D时钟 */

    DMA2D->CR &= ~(DMA2D_CR_START);                            /* 先停止DMA2D */
    DMA2D->CR = DMA2D_R2M;                                     /* 寄存器到存储器模式 */
    DMA2D->OPFCCR = LCD_PIXFORMAT;                             /* 设置颜色格式 */
    DMA2D->OOR = offline;                                      /* 设置行偏移  */

    DMA2D->OMAR = addr;                                        /* 输出存储器地址 */
    DMA2D->NLR = (pey - psy + 1) | ((pex - psx + 1) << 16);    /* 设定行数寄存器 */
    DMA2D->OCOLR = color;                                      /* 设定输出颜色寄存器 */
    DMA2D->CR |= DMA2D_CR_START;                               /* 启动DMA2D */

    while ((DMA2D->ISR & (DMA2D_FLAG_TC)) == 0)                /* 等待传输完成 */
    {
        timeout++;
        if (timeout > 0X1FFFFF)break;                          /* 超时退出 */
    } 
    DMA2D->IFCR |= DMA2D_FLAG_TC;                              /* 清除传输完成标志 */
}

///* 使用DMA2D相关的HAL函数使用DMA2D外设(不推荐) */
///**
// * @brief       在指定区域内填充单个颜色
// * @param        (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
// * @param        color:要填充的颜色
// * @retval      无
// */
//void ltdc_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
//{
//    uint32_t psx, psy, pex, pey;   /* 以LCD面板为基准的坐标系,不随横竖屏变化而变化 */
//    uint32_t timeout = 0; 
//    uint16_t offline;
//    uint32_t addr;
//    
//    if (ex >= lcdltdc.width)
//    {
//        ex = lcdltdc.width - 1;
//    }
//    
//    if (ey >= lcdltdc.height)
//    {
//        ey = lcdltdc.height - 1;
//    }
//    
//    /* 坐标系转换 */
//    if (lcdltdc.dir)   /* 横屏 */
//    {
//        psx = sx; 
//        psy = sy;
//        pex = ex;
//        pey = ey;
//    }
//    else             /* 竖屏 */
//    {
//        psx = sy;
//        psy = lcdltdc.pheight - ex - 1;
//        pex = ey;
//        pey = lcdltdc.pheight - sx - 1;
//    }

//    offline = lcdltdc.pwidth - (pex - psx + 1);
//    addr = ((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * psy + psx));
//    
//    if (LCD_PIXFORMAT == LCD_PIXEL_FORMAT_RGB565)  /* 如果是RGB565格式的话需要进行颜色转换,将16bit转换为32bit的 */
//    {
//        color = ((color & 0X0000F800) << 8) | ((color & 0X000007E0) << 5 ) | ((color & 0X0000001F) << 3);
//    }
//    
//    /* 配置DMA2D的模式 */
//    g_dma2d_handle.Instance = DMA2D;
//    g_dma2d_handle.Init.Mode = DMA2D_R2M;               /* 内存到存储器模式 */
//    g_dma2d_handle.Init.ColorMode = LCD_PIXFORMAT;      /* 设置颜色格式 */
//    g_dma2d_handle.Init.OutputOffset = offline;         /* 输出偏移  */
//    HAL_DMA2D_Init(&g_dma2d_handle);                    /* 初始化DMA2D */
//    
//    HAL_DMA2D_ConfigLayer(&g_dma2d_handle, lcdltdc.activelayer);                            /* 层配置 */
//    HAL_DMA2D_Start(&g_dma2d_handle, color, (uint32_t)addr, pex - psx + 1, pey - psy + 1);  /* 开启传输 */
//    HAL_DMA2D_PollForTransfer(&g_dma2d_handle, 1000);                                       /* 传输数据 */
//    
//    while((__HAL_DMA2D_GET_FLAG(&g_dma2d_handle, DMA2D_FLAG_TC) == 0) && (timeout < 0X5000)) /* 等待DMA2D完成 */
//    {
//        timeout++;
//    }
//    __HAL_DMA2D_CLEAR_FLAG(&g_dma2d_handle,DMA2D_FLAG_TC);                                   /* 清除传输完成标志 */
//}

/**
 * @brief       在指定区域内填充指定颜色块,DMA2D填充
 * @param       此函数仅支持u16,RGB565格式的颜色数组填充.
 * @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
 * @param       注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!
 * @param       color:要填充的颜色数组首地址
 * @retval      无
 */
void ltdc_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color)
{
    uint32_t psx, psy, pex, pey;   /* 以LCD面板为基准的坐标系,不随横竖屏变化而变化 */
    uint32_t timeout = 0; 
    uint16_t offline;
    uint32_t addr;
  
    /* 坐标系转换 */
    if (lcdltdc.dir)               /* 横屏 */
    {
        psx = sx;
        psy = sy;
        pex = ex;
        pey = ey;
    }
    else                          /* 竖屏 */
    {
        psx = sy;
        psy = lcdltdc.pheight - ex - 1;
        pex = ey;
        pey = lcdltdc.pheight - sx - 1;
    }
    
    offline = lcdltdc.pwidth - (pex - psx + 1);
    addr = ((uint32_t)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * psy + psx));

    __HAL_RCC_DMA2D_CLK_ENABLE();                             /* 使能DM2D时钟 */

    DMA2D->CR &= ~(DMA2D_CR_START);                           /* 先停止DMA2D */
    DMA2D->CR = DMA2D_M2M;                                    /* 存储器到存储器模式 */
    DMA2D->FGPFCCR = LCD_PIXFORMAT;                           /* 设置颜色格式 */
    DMA2D->FGOR = 0;                                          /* 前景层行偏移为0 */
    DMA2D->OOR = offline;                                     /* 设置行偏移 */

    DMA2D->FGMAR = (uint32_t)color;                           /* 源地址 */
    DMA2D->OMAR = addr;                                       /* 输出存储器地址 */
    DMA2D->NLR = (pey - psy + 1) | ((pex - psx + 1) << 16);   /* 设定行数寄存器 */
    DMA2D->CR |= DMA2D_CR_START;                              /* 启动DMA2D */
    
    while((DMA2D->ISR & (DMA2D_FLAG_TC)) == 0)                /* 等待传输完成 */
    {
        timeout++;
        if (timeout > 0X1FFFFF)break;                         /* 超时退出 */
    } 
    DMA2D->IFCR |= DMA2D_FLAG_TC;                             /* 清除传输完成标志 */
}  

/**
 * @brief       LCD清屏
 * @param       color:颜色值
 * @retval      无
 */
void ltdc_clear(uint32_t color)
{
//    uint16_t x,y;
//    
//    for (y = 0; y < 800; y++)
//        for (x = 0; x < 480; x++)
//            ltdc_draw_point(x, y, color);
    
    ltdc_fill(0, 0, lcdltdc.width - 1, lcdltdc.height - 1, color);
}

/**
 * @brief       LTDC时钟(Fdclk)设置函数
 * @param       Fvco=Fin*pllsain; 
 * @param       Fdclk=Fvco/pllsair/2*2^pllsaidivr=Fin*pllsain/pllsair/2*2^pllsaidivr;
 *
 *              Fvco: VCO频率
 *              Fin:输入时钟频率一般为1Mhz(来自系统时钟PLLM分频后的时钟,见时钟树图)
 *              pllsain:SAI时钟倍频系数N,取值范围:50~432.  
 *              pllsair:SAI时钟的分频系数R,取值范围:2~7
 *              pllsaidivr:LCD时钟分频系数,取值范围:RCC_PLLSAIDIVR_2/4/8/16,对应分频2~16 
 *              假设:外部晶振为25M,pllm=25的时候,Fin=1Mhz.
 *              例如:要得到20M的LTDC时钟,则可以设置:pllsain=400,pllsair=5,pllsaidivr=RCC_PLLSAIDIVR_4
 *              Fdclk=1*400/5/4=400/20=20Mhz
 *              返回值:0,成功;1,失败。
*/
uint8_t ltdc_clk_set(uint32_t pllsain, uint32_t pllsair, uint32_t pllsaidivr)
{
    RCC_PeriphCLKInitTypeDef periphclk_initure;

    /* LTDC输出像素时钟,需要根据自己所使用的LCD数据手册来配置! */
    periphclk_initure.PeriphClockSelection = RCC_PERIPHCLK_LTDC;     /* LTDC时钟 */
    periphclk_initure.PLLSAI.PLLSAIN = pllsain;
    periphclk_initure.PLLSAI.PLLSAIR = pllsair;
    periphclk_initure.PLLSAIDivR = pllsaidivr;

    if (HAL_RCCEx_PeriphCLKConfig(&periphclk_initure) == HAL_OK)     /* 配置像素时钟 */
    {
        return 0;                                                    /* 成功 */
    }
    else return 1;                                                   /* 失败 */
}

/**
 * @brief       LTDC,层窗口设置,窗口以LCD面板坐标系为基准
 * @param       注意:此函数必须在LTDC_Layer_Parameter_Config之后再设置.
 * @param       layerx:层值,0/1.
 * @param       sx,sy:起始坐标
 * @param       width,height:宽度和高度
 * @retval      无
 */
void ltdc_layer_window_config(uint8_t layerx, uint16_t sx, uint16_t sy, uint16_t width, uint16_t height)
{
    HAL_LTDC_SetWindowPosition(&g_ltdc_handle, sx, sy, layerx);   /* 设置窗口的位置 */
    HAL_LTDC_SetWindowSize(&g_ltdc_handle, width, height, layerx);/* 设置窗口大小 */
}

/**
 * @brief       LTDC,基本参数设置.
 * @note        此函数,必须在LTDC_Layer_Window_Config之前设置.
 * @param       layerx:层值,0/1.
 * @param       bufaddr:层颜色帧缓存起始地址
 * @param       pixformat:颜色格式.0,ARGB8888;1,RGB888;2,RGB565;3,ARGB1555;4,ARGB4444;5,L8;6;AL44;7;AL88
 * @param       alpha:层颜色Alpha值,0,全透明;255,不透明
 * @param       alpha0:默认颜色Alpha值,0,全透明;255,不透明
 * @param       bfac1:混合系数1,4(100),恒定的Alpha;6(101),像素Alpha*恒定Alpha
 * @param       bfac2:混合系数2,5(101),恒定的Alpha;7(111),像素Alpha*恒定Alpha
 * @param       bkcolor:层默认颜色,32位,低24位有效,RGB888格式
 * @retval      返回值:无
*/
void ltdc_layer_parameter_config(uint8_t layerx, uint32_t bufaddr, uint8_t pixformat, uint8_t alpha, uint8_t alpha0, uint8_t bfac1, uint8_t bfac2, uint32_t bkcolor)
{
    LTDC_LayerCfgTypeDef pLayerCfg;

    pLayerCfg.WindowX0 = 0;                                            /* 窗口起始X坐标 */
    pLayerCfg.WindowY0 = 0;                                            /* 窗口起始Y坐标 */
    pLayerCfg.WindowX1 = lcdltdc.pwidth;                               /* 窗口终止X坐标 */
    pLayerCfg.WindowY1 = lcdltdc.pheight;                              /* 窗口终止Y坐标 */
    pLayerCfg.PixelFormat = pixformat;                                 /* 像素格式 */
    pLayerCfg.Alpha = alpha;                                           /* Alpha值设置,0~255,255为完全不透明 */
    pLayerCfg.Alpha0 = alpha0;                                         /* 默认Alpha值 */
    pLayerCfg.BlendingFactor1 = (uint32_t)bfac1 << 8;                  /* 设置层混合系数 */
    pLayerCfg.BlendingFactor2 = (uint32_t)bfac2 << 8;                  /* 设置层混合系数 */
    pLayerCfg.FBStartAdress = bufaddr;                                 /* 设置层颜色帧缓存起始地址 */
    pLayerCfg.ImageWidth = lcdltdc.pwidth;                             /* 设置颜色帧缓冲区的宽度 */
    pLayerCfg.ImageHeight = lcdltdc.pheight;                           /* 设置颜色帧缓冲区的高度 */
    pLayerCfg.Backcolor.Red = (uint8_t)(bkcolor & 0X00FF0000) >> 16;   /* 背景颜色红色部分 */
    pLayerCfg.Backcolor.Green = (uint8_t)(bkcolor & 0X0000FF00) >> 8;  /* 背景颜色绿色部分 */
    pLayerCfg.Backcolor.Blue = (uint8_t)bkcolor & 0X000000FF;          /* 背景颜色蓝色部分 */
    HAL_LTDC_ConfigLayer(&g_ltdc_handle, &pLayerCfg, layerx);          /* 设置所选中的层 */
}  

/**
 * @brief       读取面板参数
 * @param        PG6=R7(M0);PI2=G7(M1);PI7=B7(M2);
 * @param        M2:M1:M0
 * @param        0 :0 :0    4.3寸 480*272 RGB屏,ID=0X4342
 * @param        0 :0 :1    7  寸 800*480 RGB屏,ID=0X7084
 * @param        0 :1 :0    7  寸1024*600 RGB屏,ID=0X7016
 * @param        0 :1 :1    7  寸1280*800 RGB屏,ID=0X7018
 * @param        1 :0 :0    4.3寸 800*480 RGB屏,ID=0X4384
 * @param        1 :0 :1   10.1寸1280*800 RGB屏,ID=0X1018 
 * @param        返回值:LCD ID:0,非法;其他值,ID;
 * @retval      无
 */
uint16_t ltdc_panelid_read(void)
{
    uint8_t idx = 0;
    GPIO_InitTypeDef gpio_init_struct;
    
    __HAL_RCC_GPIOG_CLK_ENABLE();                           /* 使能GPIOG时钟 */
    __HAL_RCC_GPIOI_CLK_ENABLE();                           /* 使能GPIOI时钟 */
    
    gpio_init_struct.Pin = GPIO_PIN_6;                      /* PG6 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;                /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;               /* 高速 */
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);                /* 初始化 */
    
    gpio_init_struct.Pin = GPIO_PIN_2 | GPIO_PIN_7;         /* PI2,7 */
    HAL_GPIO_Init(GPIOI, &gpio_init_struct);                /* 初始化 */
    
    idx = (uint8_t)HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_6);     /* 读取M0 */
    idx|= (uint8_t)HAL_GPIO_ReadPin(GPIOI, GPIO_PIN_2) << 1;/* 读取M1 */
    idx|= (uint8_t)HAL_GPIO_ReadPin(GPIOI, GPIO_PIN_7) << 2;/* 读取M2 */
    
    switch (idx)
    {
        case 0 : 
            return 0X4342;     /*  4.3寸屏, 480*272分辨率 */

        case 1 : 
            return 0X7084;     /*  7  寸屏, 800*480分辨率 */

        case 2 : 
            return 0X7016;     /*  7  寸屏,1024*600分辨率 */

        case 3 : 
            return 0X7018;     /*  7  寸屏,1280*800分辨率 */

        case 4 : 
            return 0X4384;     /*  4.3寸屏, 800*480分辨率 */

        case 5 : 
            return 0X1018;     /* 10.1寸屏,1280*800分辨率 */

        default : 
            return 0;
    }
}

/**
 * @brief       LCD初始化函数
 * @param       无
 * @retval      无
 */
void ltdc_init(void)
{
    uint16_t lcdid = 0;

    lcdid = ltdc_panelid_read();                  /* 读取LCD面板ID */
    
    if (lcdid == 0X4342)
    {
        lcdltdc.pwidth = 480;                     /* 面板宽度,单位:像素 */
        lcdltdc.pheight = 272;                    /* 面板高度,单位:像素 */
        lcdltdc.hsw = 1;                          /* 水平同步宽度 */
        lcdltdc.vsw = 1;                          /* 垂直同步宽度 */
        lcdltdc.hbp = 40;                         /* 水平后廊 */
        lcdltdc.vbp = 8;                          /* 垂直后廊 */
        lcdltdc.hfp = 5;                          /* 水平前廊 */
        lcdltdc.vfp = 8;                          /* 垂直前廊 */
        ltdc_clk_set(288, 4, RCC_PLLSAIDIVR_8);   /* 设置像素时钟 9Mhz */
        /* 其他参数待定. */
    }
    else if (lcdid == 0X7084)
    {
        lcdltdc.pwidth = 800;                   /* 面板宽度,单位:像素 */
        lcdltdc.pheight = 480;                  /* 面板高度,单位:像素 */
        lcdltdc.hsw = 1;                        /* 水平同步宽度 */
        lcdltdc.vsw = 1;                        /* 垂直同步宽度 */
        lcdltdc.hbp = 46;                       /* 水平后廊 */
        lcdltdc.vbp = 23;                       /* 垂直后廊 */
        lcdltdc.hfp = 210;                      /* 水平前廊 */
        lcdltdc.vfp = 22;                       /* 垂直前廊 */
        ltdc_clk_set(396, 3, RCC_PLLSAIDIVR_4); /* 设置像素时钟 33M(如果开双显,需要降低DCLK到:18.75Mhz  300/4/4,才会比较好) */
    }
    else if (lcdid == 0X7016)
    {
        lcdltdc.pwidth = 1024;                 /* 面板宽度,单位:像素 */
        lcdltdc.pheight = 600;                 /* 面板高度,单位:像素 */
        lcdltdc.hsw = 20;                      /* 水平同步宽度 */
        lcdltdc.vsw = 3;                       /* 垂直同步宽度 */
        lcdltdc.hbp = 140;                     /* 水平后廊 */
        lcdltdc.vbp = 20;                      /* 垂直后廊 */
        lcdltdc.hfp = 160;                     /* 水平前廊 */
        lcdltdc.vfp = 12;                      /* 垂直前廊 */
        ltdc_clk_set(360, 2, RCC_PLLSAIDIVR_4);/* 设置像素时钟  45Mhz  */
        /* 其他参数待定.*/
    }
    else if (lcdid == 0X7018)
    {
        lcdltdc.pwidth = 1280;                  /* 面板宽度,单位:像素 */
        lcdltdc.pheight = 800;                  /* 面板高度,单位:像素 */
        /* 其他参数待定.*/
    }
    else if (lcdid == 0X4384)
    {
        lcdltdc.pwidth = 800;                   /* 面板宽度,单位:像素 */
        lcdltdc.pheight = 480;                  /* 面板高度,单位:像素 */
        lcdltdc.hbp = 88;                       /* 水平后廊 */
        lcdltdc.hfp = 40;                       /* 水平前廊 */
        lcdltdc.hsw = 48;                       /* 水平同步宽度 */
        lcdltdc.vbp = 32;                       /* 垂直后廊 */
        lcdltdc.vfp = 13;                       /* 垂直前廊 */
        lcdltdc.vsw = 3;                        /* 垂直同步宽度 */
        ltdc_clk_set(396, 3, RCC_PLLSAIDIVR_4); /* 设置像素时钟 33M */
        /* 其他参数待定. */
    }
    else if (lcdid == 0X1018)                   /* 10.1寸1280*800 RGB屏 */
    {
        lcdltdc.pwidth = 1280;                 /* 面板宽度,单位:像素 */
        lcdltdc.pheight = 800;                 /* 面板高度,单位:像素 */
        lcdltdc.hbp = 140;                     /* 水平后廊 */
        lcdltdc.hfp = 10;                      /* 水平前廊 */
        lcdltdc.hsw = 10;                      /* 水平同步宽度 */
        lcdltdc.vbp = 10;                      /* 垂直后廊 */
        lcdltdc.vfp = 10;                      /* 垂直前廊 */
        lcdltdc.vsw = 3;                       /* 垂直同步宽度 */
        ltdc_clk_set(360, 2, RCC_PLLSAIDIVR_4);/* 设置像素时钟  45Mhz */
    } 

    lcddev.width = lcdltdc.pwidth;
    lcddev.height = lcdltdc.pheight;
    
#if LCD_PIXFORMAT == LCD_PIXFORMAT_ARGB8888 || LCD_PIXFORMAT == LCD_PIXFORMAT_RGB888 
    ltdc_framebuf[0] = (uint32_t*) &ltdc_lcd_framebuf;
    lcdltdc.pixsize = 4;                        /* 每个像素占4个字节 */
#else 
    lcdltdc.pixsize = 2;                        /* 每个像素占2个字节 */
    ltdc_framebuf[0] = (uint32_t*)&ltdc_lcd_framebuf;
//    ltdc_framebuf[1] = (uint32_t*)&ltdc_lcd_framebuf1;
#endif 
    /* LTDC配置 */
    g_ltdc_handle.Instance = LTDC;
    g_ltdc_handle.Init.HSPolarity = LTDC_HSPOLARITY_AL;         /* 水平同步极性 */
    g_ltdc_handle.Init.VSPolarity = LTDC_VSPOLARITY_AL;         /* 垂直同步极性 */
    g_ltdc_handle.Init.DEPolarity = LTDC_DEPOLARITY_AL;         /* 数据使能极性 */
    
    if (lcdid == 0X1018)
    {
        g_ltdc_handle.Init.PCPolarity = LTDC_PCPOLARITY_IIPC;   /* 像素时钟极性 */
    }
    else 
    {
        g_ltdc_handle.Init.PCPolarity = LTDC_PCPOLARITY_IPC;    /* 像素时钟极性 */
    }
    
    g_ltdc_handle.Init.HorizontalSync = lcdltdc.hsw - 1;                                            /* 水平同步宽度 */
    g_ltdc_handle.Init.VerticalSync = lcdltdc.vsw - 1;                                              /* 垂直同步宽度 */
    g_ltdc_handle.Init.AccumulatedHBP = lcdltdc.hsw + lcdltdc.hbp - 1;                              /* 水平同步后沿宽度 */
    g_ltdc_handle.Init.AccumulatedVBP = lcdltdc.vsw + lcdltdc.vbp - 1;                              /* 垂直同步后沿高度 */
    g_ltdc_handle.Init.AccumulatedActiveW = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth - 1;         /* 有效宽度 */
    g_ltdc_handle.Init.AccumulatedActiveH = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight - 1;        /* 有效高度 */
    g_ltdc_handle.Init.TotalWidth = lcdltdc.hsw + lcdltdc.hbp + lcdltdc.pwidth + lcdltdc.hfp - 1;   /* 总宽度 */
    g_ltdc_handle.Init.TotalHeigh = lcdltdc.vsw + lcdltdc.vbp + lcdltdc.pheight + lcdltdc.vfp - 1;  /* 总高度 */
    g_ltdc_handle.Init.Backcolor.Red = 0xFF;                                                           /* 屏幕背景层红色部分 */
    g_ltdc_handle.Init.Backcolor.Green = 0;                                                         /* 屏幕背景层绿色部分 */
    g_ltdc_handle.Init.Backcolor.Blue = 0;                                                          /* 屏幕背景色蓝色部分 */
    HAL_LTDC_Init(&g_ltdc_handle);

    /* 层配置 */
    ltdc_layer_parameter_config(0, (uint32_t)ltdc_framebuf[0], LCD_PIXFORMAT, 255, 0, 6, 7, 0X000000);/* 第一层参数配置 */
//    ltdc_layer_parameter_config(1, (uint32_t)ltdc_framebuf[1], LCD_PIXFORMAT, 127, 0, 6, 7, 0X000000);/* 第二层参数配置 */

//    ltdc_display_dir(0);      /* 默认竖屏,在lcd_init函数里面设置 */
    ltdc_select_layer(0);       /* 选择第1层 */
    LCD_BL(1);                  /* 点亮背光 */
    ltdc_clear(0XFFFFFFFF);     /* 清屏 */
}

/**
 * @brief       LTDC底层IO初始化和时钟使能
 * @note        此函数会被HAL_LTDC_Init()调用
 * @param       hltdc:LTDC句柄
 * @retval      无
 */
void HAL_LTDC_MspInit(LTDC_HandleTypeDef* hltdc)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    __HAL_RCC_LTDC_CLK_ENABLE();                      /* 使能LTDC时钟 */
    __HAL_RCC_DMA2D_CLK_ENABLE();                     /* 使能DMA2D时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();                     /* 使能GPIOB时钟 */
    __HAL_RCC_GPIOF_CLK_ENABLE();                     /* 使能GPIOF时钟 */
    __HAL_RCC_GPIOG_CLK_ENABLE();                     /* 使能GPIOG时钟 */
    __HAL_RCC_GPIOH_CLK_ENABLE();                     /* 使能GPIOH时钟 */
    __HAL_RCC_GPIOI_CLK_ENABLE();                     /* 使能GPIOI时钟 */
    
    /* 初始化PB5,背光引脚 */
    gpio_init_struct.Pin = GPIO_PIN_5;                /* PB5推挽输出,控制背光 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;      /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;              /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    HAL_GPIO_Init(GPIOB, &gpio_init_struct);
    
    /* 初始化PF10 */
    gpio_init_struct.Pin = GPIO_PIN_10; 
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;          /* 复用 */
    gpio_init_struct.Pull = GPIO_NOPULL;              
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;         /* 高速 */
    gpio_init_struct.Alternate = GPIO_AF14_LTDC;      /* 复用为LTDC */
    HAL_GPIO_Init(GPIOF, &gpio_init_struct);
    
    /* 初始化PG6,7,11 */
    gpio_init_struct.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_11;
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);
    
    /* 初始化PH9,10,11,12,13,14,15 */
    gpio_init_struct.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \
                     GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOH, &gpio_init_struct);
    
    /* 初始化PI0,1,2,4,5,6,7,9,10 */
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5| \
                     GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOI, &gpio_init_struct); 
}

ltdc.h

#ifndef _LCD_H
#define _LCD_H

#include "./SYSTEM/sys/sys.h"

/* LCD LTDC重要参数集 */
typedef struct  
{
    uint32_t pwidth;      /* LCD面板的宽度,固定参数,不随显示方向改变,如果为0,说明没有任何RGB屏接入 */
    uint32_t pheight;     /* LCD面板的高度,固定参数,不随显示方向改变 */
    uint16_t hsw;         /* 水平同步宽度 */
    uint16_t vsw;         /* 垂直同步宽度 */
    uint16_t hbp;         /* 水平后廊 */
    uint16_t vbp;         /* 垂直后廊 */
    uint16_t hfp;         /* 水平前廊 */
    uint16_t vfp;         /* 垂直前廊  */
    uint8_t activelayer;  /* 当前层编号:0/1 */
    uint8_t dir;          /* 0,竖屏;1,横屏; */
    uint16_t width;       /* LCD宽度 */
    uint16_t height;      /* LCD高度 */
    uint32_t pixsize;     /* 每个像素所占字节数 */
}_ltdc_dev; 

extern _ltdc_dev lcdltdc;                   /* 管理LCD LTDC参数 */
extern LTDC_HandleTypeDef g_ltdc_handle;    /* LTDC句柄 */
extern DMA2D_HandleTypeDef g_dma2d_handle;  /* DMA2D句柄 */

#define LCD_PIXEL_FORMAT_ARGB8888       0X00
#define LCD_PIXEL_FORMAT_RGB888         0X01
#define LCD_PIXEL_FORMAT_RGB565         0X02
#define LCD_PIXEL_FORMAT_ARGB1555       0X03
#define LCD_PIXEL_FORMAT_ARGB4444       0X04
#define LCD_PIXEL_FORMAT_L8             0X05
#define LCD_PIXEL_FORMAT_AL44           0X06
#define LCD_PIXEL_FORMAT_AL88           0X07

/******************************************************************************************/
/*用户修改配置部分:

 * 定义颜色像素格式,一般用RGB565 */
#define LCD_PIXFORMAT       LCD_PIXEL_FORMAT_RGB565
/* 定义默认背景层颜色 */
#define LTDC_BACKLAYERCOLOR           0X00000000
/* LCD帧缓冲区首地址,这里定义在SDRAM里面. */
#define LCD_FRAME_BUF_ADDR            0XC0000000  

void ltdc_switch(uint8_t sw);                                                                                                                                        /* LTDC开关 */
void ltdc_layer_switch(uint8_t layerx, uint8_t sw);                                                                                                                  /* 层开关 */
void ltdc_select_layer(uint8_t layerx);                                                                                                                              /* 层选择 */
void ltdc_display_dir(uint8_t dir);                                                                                                                                  /* 显示方向控制 */
void ltdc_draw_point(uint16_t x, uint16_t y, uint32_t color);                                                                                                        /* 画点函数 */
uint32_t ltdc_read_point(uint16_t x, uint16_t y);                                                                                                                    /* 读点函数 */
void ltdc_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color);                                                                                  /* 矩形单色填充函数 */
void ltdc_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color);                                                                           /* 矩形彩色填充函数 */
void ltdc_clear(uint32_t color);                                                                                                                                     /* 清屏函数 */
uint8_t ltdc_clk_set(uint32_t pllsain, uint32_t pllsair, uint32_t pllsaidivr);                                                                                       /* LTDC时钟配置 */
void ltdc_layer_window_config(uint8_t layerx, uint16_t sx, uint16_t sy, uint16_t width, uint16_t height);                                                            /* LTDC层窗口设置 */
void ltdc_layer_parameter_config(uint8_t layerx, uint32_t bufaddr, uint8_t pixformat, uint8_t alpha, uint8_t alpha0, uint8_t bfac1, uint8_t bfac2, uint32_t bkcolor);/* LTDC基本参数设置 */
uint16_t ltdc_panelid_read(void);                                                                                                                                    /* LCD ID读取函数 */
void ltdc_init(void);                                                                                                                                                /* LTDC初始化函数 */


#endif

lcd.c
sdram.c
main.c

#include "./SYSTEM/sys/sys.h"        // 包含系统初始化相关头文件
#include "./SYSTEM/usart/usart.h"    // 包含串口初始化相关头文件
#include "./SYSTEM/delay/delay.h"    // 包含延时函数相关头文件
#include "./BSP/LED/led.h"           // 包含LED初始化相关头文件
#include "./BSP/LCD/lcd.h"           // 包含LCD初始化相关头文件
#include "./BSP/KEY/key.h"           // 包含按键初始化相关头文件
#include "./BSP/SDRAM/sdram.h"       // 包含SDRAM初始化相关头文件

int main(void)
{
    uint8_t x = 0;         // 定义循环计数器
    uint8_t lcd_id[12];    // 定义存储LCD ID的数组

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(360, 25, 2, 8);    /* 设置时钟,180Mhz */
    delay_init(180);                        /* 延时初始化 */
    usart_init(115200);                     /* 初始化USART */
    led_init();                             /* 初始化LED */
    key_init();                             /* 初始化KEY */
    sdram_init();                           /* 初始化SDRAM */
    lcd_init();                             /* 初始化LCD */
  
    g_point_color = RED;    // 设置画笔颜色为红色
    sprintf((char *)lcd_id, "LCD ID:%04X", lcddev.id);  /* 将LCD ID打印到lcd_id数组 */

    while (1)
    {
        switch (x)
        {
            case 0:
                lcd_clear(WHITE);    // 清屏并填充白色
                break;

            case 1:
                lcd_clear(BLACK);    // 清屏并填充黑色
                break;

            case 2:
                lcd_clear(BLUE);     // 清屏并填充蓝色
                break;

            case 3:
                lcd_clear(RED);      // 清屏并填充红色
                break;

            case 4:
                lcd_clear(MAGENTA);  // 清屏并填充洋红色
                break;

            case 5:
                lcd_clear(GREEN);    // 清屏并填充绿色
                break;

            case 6:
                lcd_clear(CYAN);     // 清屏并填充青色
                break;

            case 7:
                lcd_clear(YELLOW);   // 清屏并填充黄色
                break;

            case 8:
                lcd_clear(BRRED);    // 清屏并填充棕红色
                break;

            case 9:
                lcd_clear(GRAY);     // 清屏并填充灰色
                break;

            case 10:
                lcd_clear(LGRAY);    // 清屏并填充浅灰色
                break;

            case 11:
                lcd_clear(BROWN);    // 清屏并填充棕色
                break;
        }

        lcd_show_string(10, 40, 240, 32, 32, "STM32", RED);           // 在LCD上显示字符串
        lcd_show_string(10, 80, 240, 24, 24, "LTDC TEST", RED);       // 在LCD上显示字符串
        lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED);  // 在LCD上显示字符串
        lcd_show_string(10, 130, 240, 16, 16, (char *)lcd_id, RED);   // 在LCD上显示LCD ID
        x++;    // 循环计数器加1

        if (x == 12)    // 如果循环计数器达到12,重置为0
            x = 0;

        LED0_TOGGLE();  // 翻转LED0状态
        delay_ms(1000); // 延时1秒
    }
}

在这里插入图片描述
在这里插入图片描述

八、总结

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 36
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖喱年糕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值