赞
踩
三、RGB屏基本驱动步骤
四、编程实战1
五、DMA2D介绍
嵌入式图形系统通常由微处理器、帧缓冲器、显示控制器和显示屏等组成,其工作流程如下:
微处理器(Microprocessor): 微处理器负责执行程序,并根据程序输出生成要显示的图像数据。这些图像数据通常以像素为单位,表示图像中每个像素的颜色和位置等信息。
帧缓冲器(Frame Buffer): 帧缓冲器是一块内存区域,用于存储要显示图像的像素数据。在帧缓冲器中,每个像素通常由几个字节表示,其中包括像素的颜色信息以及可能的其他属性,如透明度等。帧缓冲器也被称为图形随机存取存储器(GRAM)。
显示控制器(Display Controller): 显示控制器负责解析从帧缓冲器中读取的图像数据,并控制显示屏相应地显示这些图像数据。显示控制器通常包括一些硬件模块,如扫描控制器、时序控制器等,用于生成显示信号以及控制显示屏的刷新。
显示屏(Display Screen): 显示屏是实际用来显示图像的硬件设备,通常由液晶显示器(LCD)、有机发光二极管(OLED)或其他显示技术构成。显示屏接收来自显示控制器的信号,并根据这些信号将图像数据显示在屏幕上。
总的来说,微处理器生成图像数据并存储在帧缓冲器中,显示控制器负责从帧缓冲器中读取图像数据并控制显示屏进行显示,从而完成整个图形系统的工作。
RGB LCD(Red-Green-Blue Liquid Crystal Display)也称为TFT LCD(Thin Film Transistor Liquid Crystal Display),是一种常见的液晶显示技术。与传统的单色LCD相比,RGB LCD能够以全彩方式显示图像,因为它使用了红、绿、蓝三种基本颜色的组合来呈现各种颜色。
以下是RGB LCD的一些特点和工作原理:
接口类型: RGB LCD使用RGB接口,而不是传统的MCU接口。RGB接口通常包括24根颜色数据线,分别对应红、绿、蓝三种颜色的8根数据线,因此也称为RGB888接口。这种接口能够提供更丰富的颜色表现能力,支持1600万种颜色。
液晶材料: RGB LCD使用液晶材料作为显示介质,液晶分子的取向可通过电场控制,从而实现像素的开闭。每个像素由三个次像素(子像素)组成,分别对应红、绿、蓝三种颜色。通过控制每个子像素的亮度和色彩,可以呈现出各种颜色和图像。
TFT技术: RGB LCD通常采用TFT(Thin Film Transistor)技术。每个像素都配有一个薄膜晶体管,这些晶体管被用来控制液晶分子的取向,从而实现对像素的精确控制。TFT技术能够提高液晶显示器的刷新率和响应速度,同时也提升了图像的清晰度和色彩饱和度。
显示控制器: RGB LCD通常配有专用的显示控制器,用于驱动液晶面板并控制图像的显示。显示控制器会接收来自图形处理器或其他图像源的图像数据,并将其转换为液晶面板可识别的信号,从而实现图像的显示。
总的来说,RGB LCD以其丰富的色彩表现能力和高清晰度的图像显示效果,在电子产品中得到了广泛的应用,如智能手机、平板电脑、电视等。
RGB LCD的信号线包括颜色数据线和控制数据传输线,常见的RGB LCD信号线如下:
颜色数据线:
控制数据传输线:
这些信号线一起工作,确保图像数据能够按照正确的顺序和时序被传输到LCD面板上,从而正确显示图像。通过控制这些信号线的状态和时序,可以实现图像的显示和刷新,以及对图像的同步控制。
RGB LCD的驱动模式主要包括DE同步模式和HV同步模式:
DE同步模式(Data Enable 同步模式):
HV同步模式(Horizontal and Vertical 同步模式):
这两种驱动模式各有特点,可以根据具体的应用需求选择合适的模式来驱动RGB LCD。 DE同步模式相对简单,而HV同步模式则更加灵活,能够实现更多复杂的图像显示功能。
LTDC(LCD-TFT Display Controller)是STM32系列微控制器中的一种外设,用于驱动LCD-TFT显示器。它可以直接连接到RGB LCD屏幕,实现图形显示的功能,而无需额外的显示控制器和图形RAM。以下是LTDC的一些主要特点和功能:
成本效益:通过使用LTDC外设,可以节约成本,因为它不需要额外的显示控制器和图形RAM。
适用范围:LTDC具有灵活的并行RGB接口,可以连接低成本的LCD显示模块,从而扩展了其应用范围。
分辨率可编程:LTDC支持分辨率的编程,可以根据具体的应用需求设置不同的分辨率。其分辨率范围为宽度4096,高度2048,像素时钟高达83MHz。
颜色格式支持:LTDC支持多种颜色格式,包括RGB888、RGB565、ARGB8888和ARGB1555等,以满足不同应用场景的需求。
无需额外应用层:LTDC硬件能够完全管理数据读取、RGB输出和信号控制,因此不需要额外的应用层来管理显示操作。
多层显示数据混合:LTDC支持两层显示数据混合,包括前景层和背景层。通过LTDC混合后,可以将两层数据输出到LCD显示屏上,实现更丰富的显示效果。
总的来说,LTDC是一种强大的LCD-TFT显示控制器,能够为STM32系列微控制器提供高性能的图形显示功能,同时具有灵活的接口和丰富的特性,适用于各种嵌入式图形应用场景。
LTDC(LCD-TFT Display Controller)控制器是STM32微控制器中的一个重要外设,用于驱动LCD-TFT显示器。以下是LTDC控制器的主要组成部分和功能:
时钟域:LTDC控制器依赖于系统时钟提供时钟信号。其中,HCLK(AHB接口时钟SDRAM)和PCLK(APB接口时钟)用于控制控制器的工作时钟。
AHB主设备接口:LTDC控制器通过AHB总线与系统中的其他主设备进行通信。它包含配置和状态寄存器,用于生成同步时序。
图像处理单元:LTDC控制器具有图像处理单元,用于处理图像数据并生成相应的显示信号。它包括双层FIFO和PFC(Pixel Format Conversion)模块,用于处理像素数据的转换、混合和抖动等操作。
信号线:LTDC控制器通过一组信号线与外部LCD显示面板进行通信。这些信号线包括LCD_CLK(像素时钟)、LCD_HSYNC(水平同步信号)、LCD_VSYNC(垂直同步信号)、LCD_DE(数据使能信号)以及RGB数据线(LCD_R、LCD_G、LCD_B)等。
显示面板:LTDC控制器驱动的显示面板是LCD-TFT类型的显示器,它通过信号线接收来自LTDC控制器的信号,并将图像数据显示在屏幕上。
综上所述,LTDC控制器作为STM32微控制器中的一个外设,扮演着连接系统和LCD-TFT显示器之间的桥梁角色,负责生成适当的时序信号和像素数据,以实现图形数据在显示屏上的显示。
LTDC的图像处理单元是其功能强大的组成部分,主要负责对图像数据进行处理和转换,以便在LCD-TFT显示器上进行正确的显示。下面是图像处理单元的主要功能和流程:
AHB接口:图像数据从系统的存储器(比如SRAM或SDRAM)中通过AHB接口传输到LTDC的图像处理单元。
双层FIFO:LTDC的图像处理单元包含双层FIFO(First-In-First-Out)缓冲区,用于存储从存储器中读取的图像数据。这些FIFO缓冲区能够在数据传输过程中提供流畅的数据传输,并能够处理大量的图像数据。
像素格式转换器:图像处理单元还包括像素格式转换器,用于将图像数据从一种像素格式转换为另一种像素格式。例如,它可以将图像数据从RGB565格式转换为ARGB8888格式。
两层数据混合:LTDC支持两层图层,每一层都可以单独启用、禁用和配置。图层的显示顺序是固定的,由底层到顶层。这两层数据在像素级别进行混合,以生成最终的显示图像。在混合过程中,图像的透明度参数(例如ARGB8888格式中的透明通道)可以用来控制图层之间的透明度。
抖动色深处理:LTDC的图像处理单元还具有抖动色深处理功能,可以在显示过程中对色深进行处理,以提高图像的质量和准确性。抖动色深处理可以降低色彩的颜色深度,以适应LCD显示器的实际显示能力。
综上所述,LTDC的图像处理单元通过双层FIFO、像素格式转换器、两层数据混合和抖动色深处理等功能,能够对图像数据进行高效处理和转换,从而实现在LCD-TFT显示器上的清晰和逼真的图像显示。
配置和状态寄存器在LTDC控制器中起着关键作用,用于控制整个LTDC的工作参数以及监测LTDC的状态。下面是这些寄存器的主要功能和参数:
HSW(Horizontal Synchronization Width):水平同步脉冲的宽度,即水平同步信号的持续时间。
VSW(Vertical Synchronization Width):垂直同步脉冲的宽度,即垂直同步信号的持续时间。
HBP(Horizontal Back Porch):水平后廊,指水平同步信号之后到下一行像素数据开始之间的时间间隔。
HFP(Horizontal Front Porch):水平前廊,指一行像素数据结束后到水平同步信号开始之间的时间间隔。
VBP(Vertical Back Porch):垂直后廊,指垂直同步信号之后到下一帧像素数据开始之间的时间间隔。
VFP(Vertical Front Porch):垂直前廊,指一帧像素数据结束后到垂直同步信号开始之间的时间间隔。
这些参数用于配置LTDC控制器的时序和同步参数,以确保图像在LCD-TFT显示器上正确显示。通过调整这些参数,可以控制显示图像的水平和垂直同步信号的位置、持续时间以及像素数据的有效显示区域,从而实现对图像显示的精确控制。
在驱动RGBLCD时,确实需要大量的内存作为显存来存储图像数据。对于分辨率为800x480的屏幕,如果使用RGB565格式的图像数据,每个像素占用2字节,那么一帧图像数据所需的内存大小为:
800 × 480 × 2 = 768 K 字节 800 \times 480 \times 2 = 768 \text{K 字节} 800×480×2=768K 字节
为了高效地处理这么大量的图像数据,STM32系列的微控制器通常采用以下方法:
DMA2D:DMA2D(Direct Memory Access with 2D graphics accelerator)是STM32系列微控制器中的一种专门用于图形加速的DMA控制器。它可以在内部进行像素级别的图像处理,包括颜色转换、缩放、混合等操作,从而大大减轻了CPU的负担。
AHB接口到外部SDRAM:对于大量的图像数据,常常需要外部存储器来提供足够的存储空间。通过使用外部SDRAM(Synchronous Dynamic Random Access Memory),可以实现对大容量图像数据的存储和读取,同时通过AHB接口的高速数据传输,可以提高数据传输效率。
AHB接口到LTDC FIFO:LTDC控制器通过FIFO(First-In-First-Out)来缓存图像数据,以便在每个显示周期中按照指定的时序将图像数据发送到LCD显示器。通过AHB接口将图像数据传输到LTDC的FIFO中,可以确保数据的及时传输和显示。
综上所述,通过DMA2D加速处理图像数据,结合外部SDRAM和LTDC FIFO,可以有效地驱动RGBLCD,并实现高质量的图像显示。
在STM32中,LCD_CLK的来源和生成过程如下:
HSE(高速外部时钟):
PLL(锁相环):
PLLSAIN(PLL倍频因子):
PLLSAI分频因子(/R):
LCD_CLK(LCD控制器时钟):
通过以上步骤,LCD_CLK信号最终被产生,用于驱动LTDC控制器,控制LCD显示。这个过程中,可以根据具体的LCD显示器要求和系统的时钟设置,灵活地调整PLL的倍频和分频参数,以确保LCD_CLK的频率和时序符合LCD显示器的要求。
在STM32的LTDC控制器中,可以配置背景层和两个图层(图层1和图层2),并且可以进行Alpha混合设置。以下是相关配置的说明:
背景层设置(LTDC_BCCR):
图层设置(LTDC_LxCR):
Alpha混合设置:
通过配置这些寄存器,可以实现不同图层之间的混合效果,包括透明、半透明等,从而实现丰富的显示效果。
在混合操作中,通常会有两个参与混合的颜色层,即当前层的颜色和底层的颜色。这两个颜色层的混合结果将成为最终显示的颜色。混合操作的方式可以根据应用的需求和具体情况进行设置。
在你提供的示例中,有以下设置:
背景层设置:
图层设置:
混合公式:
通过这样的混合设置,可以实现不同层之间的颜色混合效果,从而呈现出丰富多彩的显示效果。
RGB 屏基本驱动步骤,这些步骤是配置和初始化 LTDC (LCD-TFT 显示控制器)以驱动 RGB 屏幕所需的关键步骤。这里我将简要解释每个步骤的作用:
配置 LTDC 底层操作:
设置 LCD_CLK 时钟:
初始化 LTDC:
设置 LTDC 层参数:
通过以上步骤,可以完成对 LTDC 的配置和初始化,使其能够驱动 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);
}
}
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, <dc_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(Direct Memory Access 2D)是STMicroelectronics(ST)提供的专用于图形处理的DMA引擎,有时也被称为“GPU”(图形处理器单元)。它主要用于处理图像数据,提供了 DMA 数据传输和 2D 图形加速功能。
DMA 数据传输功能:
2D 图形加速功能:
工作模式:
通过 DMA2D,开发人员可以实现高效的图形处理操作,加速图像数据的传输和处理,提高系统的性能和响应速度。
在DMA2D中,可以对前景层(Foreground)和背景层(Background)进行输入和处理,具体包括以下功能:
前景层输入:指定要处理的前景图像数据源,可以是存储器中的图像数据,也可以是寄存器中的数据。
背景层输入:指定背景图像数据源,同样可以是存储器中的数据或寄存器中的数据。
前景层颜色格式转换:DMA2D支持对前景层的颜色格式进行转换,例如从RGB888转换为RGB565等。
背景层颜色格式转换:同样地,DMA2D也支持对背景层的颜色格式进行转换,以满足不同的显示需求。
前景层和背景层的处理:DMA2D可以对前景层和背景层分别进行处理,也可以将它们进行混合处理。
在不同的工作模式下,DMA2D的处理方式略有不同:
寄存器到存储器:在这种模式下,仅输出阶段会被激活,前景层和背景层不需要输入,只需设置输出的目标存储器地址即可。
存储器到存储器:在这种模式下,前景层FIFO会被激活作为缓冲区,用于存储前景图像数据,而背景层则不被激活。
存储器到存储器并执行像素格式转换(PFC):这种模式下,虽然会执行像素格式转换,但不会激活背景层FIFO。
存储器到存储器并执行混合:在执行像素格式转换和混合时,前景层和背景层的数据都会被获取和处理,以进行混合操作。
以上是使用DMA2D进行颜色填充的基本步骤。下面是每个步骤的具体说明:
使能DMA2D时钟并停止DMA2D:
__HAL_RCC_DMA2D_CLK_ENABLE()
函数使能DMA2D时钟。DMA2D_CR
)中的START
位清零停止DMA2D传输。设置DMA2D工作模式:
设置DMA2D的相关参数:
启动DMA2D传输:
START
位,启动DMA2D传输。等待DMA2D传输完成,清除相关标志:
DMA2D_ISR
)中的传输完成标志位(DMA2D_FLAG_TC
),等待DMA2D传输完成。以上步骤完成后,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, <dc_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.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*) <dc_lcd_framebuf;
lcdltdc.pixsize = 4; /* 每个像素占4个字节 */
#else
lcdltdc.pixsize = 2; /* 每个像素占2个字节 */
ltdc_framebuf[0] = (uint32_t*)<dc_lcd_framebuf;
// ltdc_framebuf[1] = (uint32_t*)<dc_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秒
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。