STM32H7的LCD控制学习和应用

news2024/12/29 9:14:22

STM32H7的LCD控制

  • LTDC基础
    • 硬件框图
    • LTDC时钟源选择
    • LTDC的时序配置
    • LTDC背景层、图层1、图层2和Alpha混合
    • LTDC的水平消隐和垂直消隐
    • LCD的DE同步模式和HV同步模式的区别
    • 区分FPS帧率和刷新率
    • 避免LTDC刷新撕裂感的解决方法
  • 驱动示例
    • 分配栈的大小
    • MPU和Cache配置
    • 初始化SDRAM
    • 初始化LCD
    • 应用

仅供个人学习,参考armfly

LTDC基础

LTDC 的几个关键知识点放在开头说:
STM32H7 的 LTDC 最大支持 1024*768 分辨率,且支持硬件双图层。实际支持的分辨率可能比1024*768 要高一点,因为最终可以支持的最大分辨率是芯片后期定标的。支持 32 位色,24 位色,16 位色和 8 位色
可编程窗口位置和大小,可编程行同步,场同步和数据使能信号的极性。
查色表 (CLUT,Color look-up table),每个图层最高可记录 256 种 24 位色
支持如下 8 种颜色格式:
ARGB8888:
32 位颜色格式,一个像素点占用 4 字节,其中低位 3 字节用于颜色分量,高位字节用于 Alpha 混合。红、绿、蓝和 Alpha 通道(0x00 表示完全透明,0xFF 表示完全不透明)都是 8 位表示。
颜色格式:AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB。
RGB888:
24 位颜色格式,一个像素点占用 3 字节,分别用于红、绿、蓝。
颜色格式:RRRRRRRRGGGGGGGGBBBBBBBB。
RGB565:
16 位颜色格式,一个像素点占用 2 字节,分别用于红、绿、蓝。
颜色格式:RRRRRGGGGGGBBBBB。
ARGB1555:
16 位颜色格式,一个像素点占用 2 字节,Alpha 通道使用 1 个位表示,等于 0 的时候表示完全透明,等于 1 的时候表示完全不透明。红、绿、蓝都是用 5 个位表示。
颜色格式:ARRRRRGGGGGBBBBB。
ARGB4444:
16 位颜色格式,一个像素点占用 2 字节,Alpha 通道使用 2 个位表示(0x0 表示完全透明,0x3表示完全不透明)。红、绿、蓝都是用 4 个位表示。
颜色格式:ARRRRRGGGGGBBBBB。
L8 (8-bit luminance or CLUT)
8 位颜色格式,实际上仅仅是 8 位索引值,范围 0–255,而每个索引值的具体颜色值在查色表 CLUT里面存储。
AL44 (4-bit alpha + 4-bit luminance)
8 位颜色格式,实际上是 4 位 Alpha 通道(0x0 表示完全透明,0xF 表示完全不透明)和 4 位的索引值,索引范围 0–15,而每个索引值的具体颜色值在查色表 CLUT 里面存储。
AL88 (8-bit alpha + 8-bit luminance)
16 位颜色格式,实际上是 8 位 Alpha 通道(0x00 表示完全透明,0xFF 表示完全不透明)和 8位的索引值,索引范围 0–255,而每个索引值的具体颜色值在查色表 CLUT 里面存储。

硬件框图

在这里插入图片描述
通过这个框图,我们可以得到如下信息:
ltdc_aclk
为 LTDC 寄存器提供时钟,时钟来自 AXI 时钟域。
ltdc_pclk
LTDC 寄存器接口时钟。
ltdc_ker_ck
用于生成 LCD_CLK(像素时钟输出)的 LTDC 内核时钟。
ltdc_li_it
LTDC 行中断,用于触发 MDMA。
ltdc_it
LTDC 全局中断请求。
ltdc_err_it
LTDC 全局错误中断请求。
下面是 LCD 接口引脚,用于外接显示屏:
LCD_CLK
像素时钟输出。
LCD_HSYNC
水平同步信号。
LCD_VSYN
垂直同步信号。
LCD_DE
数据使能信号。
LCD_R[7:0]
8 位红色数据。
LCD_G[7:0]
8 位绿色数据。
LCD_B[7:0]
8 位蓝色数据。

LTDC时钟源选择

LTDC 仅有一个时钟源可供选择,即 PLL3R。
在这里插入图片描述

LTDC的时序配置

在这里插入图片描述
HSYNC width
水平同步宽度设置,以 LCD_CLK 的像素时钟输出为单位。
HBP(horizontal back porch period)
水平后沿周期设置,以 LCD_CLK 的像素时钟输出为单位。
Active width
有效宽度设置,以 LCD_CLK 的像素时钟输出为单位。以 800480 分辨率为例,Active width = 800。
HFP(horizontal front porch period)
水平前沿周期设置,以 LCD_CLK 的像素时钟输出为单位。
VSYNC width
垂直同步宽度设置,以 LCD_CLK 的像素时钟输出为单位。
VBP(vertical back porch period)
垂直后沿周期设置,以 LCD_CLK 的像素时钟输出为单位。
Active height
有效高度设置,以 LCD_CLK 的像素时钟输出为单位。以 800
480 分辨率为例,Active height = 480。
VFP(vertical front porch period)
垂直前沿周期设置,以 LCD_CLK 的像素时钟输出为单位。

LTDC背景层、图层1、图层2和Alpha混合

LTDC除了图层1和图层2两个以外,还有一个背景层,背景层的刷新不需要显存空间,所以可以用这个图层验证LTDC时序配置是否有问题。
在这里插入图片描述
对于背景层,只支持单色设置,固定颜色RGB888。
对于图层1和图层2来说,支持如下8种颜色格式:
– ARGB8888
– RGB888
– RGB565
– ARGB1555
– ARGB4444
– L8(8 位 Luminance 或 CLUT)
– AL44(4 位 alpha + 4 位 luminance)
– AL88(8 位 alpha + 8 位 luminance)
实现Alpha混合的关键是要有一个变量可以设置各种透明度,对此,STM32H7准备了两个Alpha供使用:
一个是常数 Alpha(0x00 表示完全透明,0xFF 表示完全不透明),所有颜色格式都可以使用。
另一个是像素 Alpha,也就是 ARGB8888,ARGB1555,ARGB4444 等颜色格式的 Alpha 通道数值,也就是我们为图层每个位置绘制的实际颜色值。
STM32H7也给出了具体的混合公式:

BC = BF1 x C + BF2 x Cs
混合后的颜色= 混合系数 1 x 当前层颜色 + 混合系数 2 x 底层混合后的颜色

混合系数 1 可以选择:
常数 Alpha
像素 Alpha x 常数 Alpha
混合系数 2 可以选择:
1 - 常数 Alpha
1 - 像素 Alpha x 常数 Alpha
底层混合后的颜色:
可以是背景层。
可以是背景层与图层 1 混合后的颜色。
那么公式就变成如下形式(主要是如下两种):
混合后的颜色 = 常数 Alpha x 当前层颜色 + (1 - 常数 Alpha) x 底层混合后的颜色。
混合后的颜色 = 像素 Alpha x 常数 Alpha x 当前层颜色 + (1 - 像素 Alpha x 常数 Alpha) x 底层混合后的颜色。

LTDC的水平消隐和垂直消隐

正常情况下,LCD 的刷新就是从左到右,从上到下进行逐个像素点刷新。但仅刷新有效的显示区是不够的,比如 800480 分辨率,我们不仅仅要刷 **800480** 这段有效区域,边界区也是要刷新的,即下图总宽度以内,有效宽度以外的区域。
水平消隐就是 LCD 扫描一行结束到另一行开始的时间,这段消失的时间就是水平消隐,即 HSYNC宽度+ HBP + HFP 这段消失的时间
垂直消隐就是 LCD 扫描最后一行结束到第一行开始的时间,这段消失的时间就是垂直消隐,即 VSYNC宽度+ VBP + VFP 这段消失的时间
在这里插入图片描述

LCD的DE同步模式和HV同步模式的区别

一般情况下,STM32H7都是用SDRAM作为LCD的显存,LTDC控制器会从SDRAM读取数据刷新到LCD显示屏上,具体如何刷新,涉及到了DE同步模式和HV同步模式。
具体支持哪种模式是由裸屏自带的DriverIC决定。
DE 同步模式
DE 模式需要 LCD_DE 和 LCD_CLK 信号来控制刷新。比如一个 800x480 分辨率的裸屏,在 DE 有效信号的时候(高电平或低电平),就有 800 个 LCD_CLK 输出时钟来确认行中 800 个点。每个时钟有效的时候,从显存读取一次 RGB 数据。因为存在回扫信号,所以 DE 是个方波。一个周期的 LCD_DE 信号,裸屏就扫描一行。扫描 480 行后,又从第一行扫描开始。这个规律由裸屏的驱动 IC 所决定的。
HV 同步模式
HV模式需要 LCD_CLK 时钟信号,行同步信号LCD_HSYNC 和场同步信号 LCD_VSYNC来控制刷新
比如一个 480x272 分辨率的裸屏,有一个行同步信号 LCD_HSYNC 产生时(高电平或者低电平脉冲),就有 480 个 LCD_CLK 输出时钟来确认行中 480 个点。每个时钟有效的时候,从显存读取一次 RGB 数据。
再来一个行同步信号 LCD_HSYNC 产生时(高电平或者低电平脉冲),切换到下一行,继续行同步和时钟输出,扫描 272 行后,发送一个场同步信号 LCD_VSYNC,又重新从第一行扫描开始。

区分FPS帧率和刷新率

FPS 帧率是对 STM32H7 刷到显存,也就是 SDRAM 里面来说的,而是刷新率是实际 LCD 显示的速度。

刷新率 = LTDC 输出时钟 /((Width + HSYNC_W + HBP + HFP )*(Height + VSYNC_W + VBP +VFP ))

一般情况下,帧率是远高于刷新率的,比如H7刷800*480的显示屏,帧数可以达到300多,但刷新率才108hz。我们可以使用emWin支持三缓冲,多余的帧数输出到其他的缓冲区,有效降低撕裂感和帧延迟,让多余的帧数有意义。

避免LTDC刷新撕裂感的解决方法

如果用户快速刷新颜色差异比较大的两种界面,会容易碰到这种撕裂问题。
原因:
用户更新显存数据期间,LTDC也在不断地读取显存的数据到显示屏上,如果用户才更新了部分界面,后面一部分还没有更新,就会出现撕裂感。
解决方法:
LTDC 刷新还在垂直消隐期间就将整个界面刷新完成,而我们如何只知道 LTDC 在垂直消隐期,通过函数 HAL_LTDC_ProgramLineEvent 设置刷新到指定行时进入中断即可,一般设置为第 0 行进入中断,然后设置个标志即可。
一旦检测到这个标志,就通过 DMA2D 快速将界面刷新好,这样就有效的避免了撕裂感。

驱动示例

分配栈的大小

在这里插入图片描述

MPU和Cache配置

数据 Cache 和指令 Cache 都开启。配置了 AXI SRAM 区(本例子未用到 AXI SRAM),FMC 的扩展 IO 区和 SDRAM。由于 SDRAM 要用于 LCD 的显存,方便起见,直接将其配置为 WT 模式。

static void MPU_Config( void )
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* 禁止 MPU */
HAL_MPU_Disable();
/* 配置 AXI SRAM 的 MPU 属性为 Write back, Read allocate,Write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置 FMC 扩展 IO 的 MPU 属性为 Device 或者 Strongly Ordered */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x60000000;
MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
 /* 配置 SDRAM 的 MPU 属性为 Write through, read allocate,no write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xC0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/*使能 MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

初始化SDRAM

/*
*********************************************************************************************************
*	函 数 名: SDRAM_GPIOConfig
*	功能说明: 配置连接外部SDRAM的GPIO
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
static void SDRAM_GPIOConfig(void)
{
	GPIO_InitTypeDef GPIO_Init_Structure;

    /*##-1- 使能FMC时钟和GPIO时钟 ##################################################*/
	__HAL_RCC_GPIOD_CLK_ENABLE();
	__HAL_RCC_GPIOE_CLK_ENABLE();
	__HAL_RCC_GPIOF_CLK_ENABLE();
	__HAL_RCC_GPIOG_CLK_ENABLE();
	__HAL_RCC_GPIOH_CLK_ENABLE();
	__HAL_RCC_GPIOI_CLK_ENABLE();

	  /* 使能FMC时钟 */
	  __HAL_RCC_FMC_CLK_ENABLE();
		
	/*-- 安富莱STM32-V7发板 SDRAM GPIO 定义 -----------------------------------------------------*/
	/*
	 +-------------------+--------------------+--------------------+--------------------+
	 +                       SDRAM pins assignment                                      +
	 +-------------------+--------------------+--------------------+--------------------+
	 | PD0  <-> FMC_D2   | PE0  <-> FMC_NBL0  | PF0  <-> FMC_A0    | PG0 <-> FMC_A10    |
	 | PD1  <-> FMC_D3   | PE1  <-> FMC_NBL1  | PF1  <-> FMC_A1    | PG1 <-> FMC_A11    |
	 | PD8  <-> FMC_D13  | PE7  <-> FMC_D4    | PF2  <-> FMC_A2    | PG4 <-> FMC_A14    |
	 | PD9  <-> FMC_D14  | PE8  <-> FMC_D5    | PF3  <-> FMC_A3    | PG5 <-> FMC_A15    |
	 | PD10 <-> FMC_D15  | PE9  <-> FMC_D6    | PF4  <-> FMC_A4    | PG8 <-> FC_SDCLK   |
	 | PD14 <-> FMC_D0   | PE10 <-> FMC_D7    | PF5  <-> FMC_A5    | PG15 <-> FMC_NCAS  |
	 | PD15 <-> FMC_D1   | PE11 <-> FMC_D8    | PF11 <-> FC_NRAS   |--------------------+
	 +-------------------| PE12 <-> FMC_D9    | PF12 <-> FMC_A6    | PG2  --- FMC_A12 (预留64M字节容量,和摇杆上键复用)
	                     | PE13 <-> FMC_D10   | PF13 <-> FMC_A7    |
	                     | PE14 <-> FMC_D11   | PF14 <-> FMC_A8    |
	                     | PE15 <-> FMC_D12   | PF15 <-> FMC_A9    |
	 +-------------------+--------------------+--------------------+
	 | PH2 <-> FMC_SDCKE0| PI4 <-> FMC_NBL2   |
	 | PH3 <-> FMC_SDNE0 | PI5 <-> FMC_NBL3   |
	 | PH5 <-> FMC_SDNW  |--------------------+
	 +-------------------+
	 +-------------------+------------------+
	 +   32-bits Mode: D31-D16              +
	 +-------------------+------------------+
	 | PH8 <-> FMC_D16   | PI0 <-> FMC_D24  |
	 | PH9 <-> FMC_D17   | PI1 <-> FMC_D25  |
	 | PH10 <-> FMC_D18  | PI2 <-> FMC_D26  |
	 | PH11 <-> FMC_D19  | PI3 <-> FMC_D27  |
	 | PH12 <-> FMC_D20  | PI6 <-> FMC_D28  |
	 | PH13 <-> FMC_D21  | PI7 <-> FMC_D29  |
	 | PH14 <-> FMC_D22  | PI9 <-> FMC_D30  |
	 | PH15 <-> FMC_D23  | PI10 <-> FMC_D31 |
	 +------------------+-------------------+

	 +-------------------+
	 +  Pins remapping   +
	 +-------------------+
	 | PC0 <-> FMC_SDNWE |
	 | PC2 <-> FMC_SDNE0 |
	 | PC3 <-> FMC_SDCKE0|
	 +-------------------+

	*/
	 /*##-2- 配置GPIO ##################################################*/
	GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
	GPIO_Init_Structure.Pull      = GPIO_PULLUP;
	GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_Init_Structure.Alternate = GPIO_AF12_FMC;

	/* GPIOD */
	GPIO_Init_Structure.Pin  = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8| GPIO_PIN_9 | GPIO_PIN_10 |\
							   GPIO_PIN_14 | GPIO_PIN_15;
	HAL_GPIO_Init(GPIOD, &GPIO_Init_Structure);

	/* GPIOE */  
	GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7| GPIO_PIN_8 | GPIO_PIN_9 |\
							  GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |\
							  GPIO_PIN_15;	  
	HAL_GPIO_Init(GPIOE, &GPIO_Init_Structure);

	/* GPIOF */  
	GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2| GPIO_PIN_3 | GPIO_PIN_4 |\
							  GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |\
							  GPIO_PIN_15;
	HAL_GPIO_Init(GPIOF, &GPIO_Init_Structure);

	/* GPIOG */  
	GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 |  
							  GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15;
	HAL_GPIO_Init(GPIOG, &GPIO_Init_Structure);

	/* GPIOH */  
	GPIO_Init_Structure.Pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_5 | GPIO_PIN_8 | 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_Structure); 

	/* GPIOI */  
	GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 |\
							  GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
	HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure);  
}
/*
*********************************************************************************************************
*	函 数 名: SDRAM初始化序列
*	功能说明: 完成SDRAM序列初始化
*	形    参: hsdram: SDRAM句柄
*			  Command: 命令结构体指针
*	返 回 值: None
*********************************************************************************************************
*/
static void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
{
	__IO uint32_t tmpmrd =0;
 
    /*##-1- 时钟使能命令 ##################################################*/
	Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
	Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;;
	Command->AutoRefreshNumber = 1;
	Command->ModeRegisterDefinition = 0;

	/* 发送命令 */
	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);

    /*##-2- 插入延迟,至少100us ##################################################*/
	HAL_Delay(1);

    /*##-3- 整个SDRAM预充电命令,PALL(precharge all) #############################*/
	Command->CommandMode = FMC_SDRAM_CMD_PALL;
	Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
	Command->AutoRefreshNumber = 1;
	Command->ModeRegisterDefinition = 0;

	/* 发送命令 */
	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);

    /*##-4- 自动刷新命令 #######################################################*/
	Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
	Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
	Command->AutoRefreshNumber = 8;
	Command->ModeRegisterDefinition = 0;

	/* 发送命令 */
	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);

    /*##-5- 配置SDRAM模式寄存器 ###############################################*/
	tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1          |
					 SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
					 SDRAM_MODEREG_CAS_LATENCY_3           |
					 SDRAM_MODEREG_OPERATING_MODE_STANDARD |
					 SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

	Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
	Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
	Command->AutoRefreshNumber = 1;
	Command->ModeRegisterDefinition = tmpmrd;

	/* 发送命令 */
	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);

    /*##-6- 设置自刷新率 ####################################################*/
    /*
        SDRAM refresh period / Number of rows)*SDRAM时钟速度 – 20
      = 64ms / 4096 *100MHz - 20
      = 1542.5 取值1543
    */
	HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT); 
}
/*
*********************************************************************************************************
*	函 数 名: bsp_InitExtSDRAM
*	功能说明: 配置连接外部SDRAM的GPIO和FMC
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitExtSDRAM(void)
{
    SDRAM_HandleTypeDef      hsdram = {0};
	FMC_SDRAM_TimingTypeDef  SDRAM_Timing = {0};
	FMC_SDRAM_CommandTypeDef command = {0};

    
	/* FMC SDRAM所涉及到GPIO配置 */
	SDRAM_GPIOConfig();

	/* SDRAM配置 */
	hsdram.Instance = FMC_SDRAM_DEVICE;

	/* 
       FMC使用的HCLK3时钟,200MHz,用于SDRAM的话,至少2分频,也就是100MHz,即1个SDRAM时钟周期是10ns
       下面参数单位均为10ns。
    */
	SDRAM_Timing.LoadToActiveDelay    = 2; /* 20ns, TMRD定义加载模式寄存器的命令与激活命令或刷新命令之间的延迟 */
	SDRAM_Timing.ExitSelfRefreshDelay = 7; /* 70ns, TXSR定义从发出自刷新命令到发出激活命令之间的延迟 */
	SDRAM_Timing.SelfRefreshTime      = 4; /* 50ns, TRAS定义最短的自刷新周期 */
	SDRAM_Timing.RowCycleDelay        = 7; /* 70ns, TRC定义刷新命令和激活命令之间的延迟 */
	SDRAM_Timing.WriteRecoveryTime    = 2; /* 20ns, TWR定义在写命令和预充电命令之间的延迟 */
	SDRAM_Timing.RPDelay              = 2; /* 20ns, TRP定义预充电命令与其它命令之间的延迟 */
	SDRAM_Timing.RCDDelay             = 2; /* 20ns, TRCD定义激活命令与读/写命令之间的延迟 */

	hsdram.Init.SDBank             = FMC_SDRAM_BANK1;               /* 硬件设计上用的BANK1 */
	hsdram.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_9;   /* 9列 */
	hsdram.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_12;     /* 12行 */
	hsdram.Init.MemoryDataWidth    = FMC_SDRAM_MEM_BUS_WIDTH_32;	/* 32位带宽 */
	hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;  /* SDRAM有4个BANK */
	hsdram.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_3;       /* CAS Latency可以设置Latency1,2和3,实际测试Latency3稳定 */
	hsdram.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE; /* 禁止写保护 */
	hsdram.Init.SDClockPeriod      = SDCLOCK_PERIOD;                /* FMC时钟200MHz,2分频后给SDRAM,即100MHz */
	hsdram.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE;       /* 使能读突发 */
	hsdram.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_0;       /* 此位定CAS延时后延后多少个SDRAM时钟周期读取数据,实际测此位可以设置无需延迟 */

	/* 配置SDRAM控制器基本参数 */
	if(HAL_SDRAM_Init(&hsdram, &SDRAM_Timing) != HAL_OK)
	{
		/* Initialization Error */
		Error_Handler(__FILE__, __LINE__);
	}

	/* 完成SDRAM序列初始化 */
	SDRAM_Initialization_Sequence(&hsdram, &command);
}

初始化LCD

/*
*********************************************************************************************************
*	函 数 名: LCD_HardReset
*	功能说明: 硬件复位. 针对复位口线由GPIO控制的产品。
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void LCD_HardReset(void)
{
#if 0	
	GPIO_InitTypeDef GPIO_InitStructure;

	/* 使能 GPIO时钟 */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	/* 配置背光GPIO为推挽输出模式 */
	GPIO_InitStructure.GPIO_Pin = GPIO_PIN_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_ResetBits(GPIOB, GPIO_PIN_1);
	bsp_DelayMS(20);
	GPIO_SetBits(GPIOB, GPIO_PIN_1);
#endif	
}
static void LCDH7_ConfigLTDC(void)
{
	/* 配置LCD相关的GPIO */
	{
		/* GPIOs Configuration */
		/*
		+------------------------+-----------------------+----------------------------+
		+                       LCD pins assignment                                   +
		+------------------------+-----------------------+----------------------------+
		|  LCDH7_TFT R0 <-> PI.15  |  LCDH7_TFT G0 <-> PJ.07 |  LCDH7_TFT B0 <-> PJ.12      |
		|  LCDH7_TFT R1 <-> PJ.00  |  LCDH7_TFT G1 <-> PJ.08 |  LCDH7_TFT B1 <-> PJ.13      |
		|  LCDH7_TFT R2 <-> PJ.01  |  LCDH7_TFT G2 <-> PJ.09 |  LCDH7_TFT B2 <-> PJ.14      |
		|  LCDH7_TFT R3 <-> PJ.02  |  LCDH7_TFT G3 <-> PJ.10 |  LCDH7_TFT B3 <-> PJ.15      |
		|  LCDH7_TFT R4 <-> PJ.03  |  LCDH7_TFT G4 <-> PJ.11 |  LCDH7_TFT B4 <-> PK.03      |
		|  LCDH7_TFT R5 <-> PJ.04  |  LCDH7_TFT G5 <-> PK.00 |  LCDH7_TFT B5 <-> PK.04      |
		|  LCDH7_TFT R6 <-> PJ.05  |  LCDH7_TFT G6 <-> PK.01 |  LCDH7_TFT B6 <-> PK.05      |
		|  LCDH7_TFT R7 <-> PJ.06  |  LCDH7_TFT G7 <-> PK.02 |  LCDH7_TFT B7 <-> PK.06      |
		-------------------------------------------------------------------------------
		|  LCDH7_TFT HSYNC <-> PI.12  | LCDTFT VSYNC <->  PI.13 |
		|  LCDH7_TFT CLK   <-> PI.14  | LCDH7_TFT DE   <->  PK.07 |
		-----------------------------------------------------
		*/		
		GPIO_InitTypeDef GPIO_Init_Structure;

		/*##-1- Enable peripherals and GPIO Clocks #################################*/  
		/* 使能LTDC时钟 */
		__HAL_RCC_LTDC_CLK_ENABLE();
		/* 使能GPIO时钟 */
		__HAL_RCC_GPIOI_CLK_ENABLE();
		__HAL_RCC_GPIOJ_CLK_ENABLE();
		__HAL_RCC_GPIOK_CLK_ENABLE();

		/* GPIOI 配置 */
		GPIO_Init_Structure.Pin       = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; 
		GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
		GPIO_Init_Structure.Pull      = GPIO_NOPULL;
		GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_HIGH;
		GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;  
		HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure);

		/* GPIOJ 配置 */  
		GPIO_Init_Structure.Pin       = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \
									  GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | \
									  GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \
									  GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; 
		GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
		GPIO_Init_Structure.Pull      = GPIO_NOPULL;
		GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_HIGH;
		GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;  
		HAL_GPIO_Init(GPIOJ, &GPIO_Init_Structure);  

		/* GPIOK 配置 */  
		GPIO_Init_Structure.Pin       = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \
									  GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
		GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
		GPIO_Init_Structure.Pull      = GPIO_NOPULL;
		GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_HIGH;
		GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;  
		HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure);  	
	}
	
	/*##-2- LTDC初始化 #############################################################*/  
	{	
		LTDC_LayerCfgTypeDef      pLayerCfg;
		uint16_t Width, Height, HSYNC_W, HBP, HFP, VSYNC_W, VBP, VFP;
		RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;

		/* 支持6种面板 */
		switch (g_LcdType)
		{
			case LCD_35_480X320:	/* 3.5寸 480 * 320 */	
				Width = 480;
				Height = 272;
				HSYNC_W = 10;
				HBP = 20;
				HFP = 20;
				VSYNC_W = 20;
				VBP = 20;
				VFP = 20;
				break;
			
			case LCD_43_480X272:		/* 4.3寸 480 * 272 */			
				Width = 480;
				Height = 272;

				HSYNC_W = 40;
				HBP = 2;
				HFP = 2;
				VSYNC_W = 9;
				VBP = 2;
				VFP = 2;
		
				/* LCD 时钟配置 */
				/* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */
				/* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 24 = 120MHz */
				/* PLLLCDCLK = PLL3_VCO Output/PLL3R = 120 / 10 = 12MHz */
				/* LTDC clock frequency = PLLLCDCLK = 24MHz */
				/*
					刷新率 = 12MHz /((Width + HSYNC_W  + HBP  + HFP)*(Height + VSYNC_W +  VBP  + VFP))
                   		   = 12000000/((480 + 40  + 2  + 2)*(272 + 9 +  2  + 2)) 
			               = 12000000/(524*285)
                           = 80Hz	

					当前这个配置方便用户使用PLL3Q输出的48MHz时钟供USB使用。
			    */
				PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
				PeriphClkInitStruct.PLL3.PLL3M = 5;
				PeriphClkInitStruct.PLL3.PLL3N = 36;
				PeriphClkInitStruct.PLL3.PLL3P = 2;
				PeriphClkInitStruct.PLL3.PLL3Q = 5;
				PeriphClkInitStruct.PLL3.PLL3R = 10;				
				HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);     			
				break;
			
			case LCD_50_480X272:		/* 5.0寸 480 * 272 */
				Width = 480;
				Height = 272;
			
				HSYNC_W = 40;
				HBP = 2;
				HFP = 2;
				VSYNC_W = 9;
				VBP = 2;
				VFP = 2;			
				break;
			
			case LCD_50_800X480:		/* 5.0寸 800 * 480 */
			case LCD_70_800X480:		/* 7.0寸 800 * 480 */					
				Width = 800;
				Height = 480;

				HSYNC_W = 96;	/* =10时,显示错位,20时部分屏可以的,80时全部OK */
				HBP = 10;
				HFP = 10;
				VSYNC_W = 2;
				VBP = 10;
				VFP = 10;			

				/* LCD 时钟配置 */
				/* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */
				/* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz */
				/* PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz */
				/* LTDC clock frequency = PLLLCDCLK = 24MHz */
				/*
					刷新率 = 24MHz /((Width + HSYNC_W  + HBP  + HFP)*(Height + VSYNC_W +  VBP  + VFP))
                   		   = 24000000/((800 + 96  + 10  + 10)*(480 + 2 +  10  + 10)) 
			               = 24000000/(916*502)
                           = 52Hz	
			
					根据需要可以加大,100Hz刷新率完全没问题,设置PeriphClkInitStruct.PLL3.PLL3N = 100即可
					此时的LTDC时钟是50MHz
					刷新率 = 50MHz /((Width + HSYNC_W  + HBP  + HFP )*(Height + VSYNC_W +  VBP  +VFP  )) 
					       = 5000000/(916*502) 
					       = 108.7Hz

					当前这个配置方便用户使用PLL3Q输出的48MHz时钟供USB使用。
			    */ 
				PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
				PeriphClkInitStruct.PLL3.PLL3M = 5;
				PeriphClkInitStruct.PLL3.PLL3N = 48;
				PeriphClkInitStruct.PLL3.PLL3P = 2;
				PeriphClkInitStruct.PLL3.PLL3Q = 5;
				PeriphClkInitStruct.PLL3.PLL3R = 10; 
				HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);     			
				break;
			
			case LCD_70_1024X600:		/* 7.0寸 1024 * 600 */
				/* 实测像素时钟 = 53.7M */
				Width = 1024;
				Height = 600;

				HSYNC_W = 2;	/* =10时,显示错位,20时部分屏可以的,80时全部OK */
				HBP = 157;
				HFP = 160;
				VSYNC_W = 2;
				VBP = 20;
				VFP = 12;		
			
				PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
				PeriphClkInitStruct.PLL3.PLL3M = 5;
				PeriphClkInitStruct.PLL3.PLL3N = 48;
				PeriphClkInitStruct.PLL3.PLL3P = 2;
				PeriphClkInitStruct.PLL3.PLL3Q = 5;
				PeriphClkInitStruct.PLL3.PLL3R = 10;
				HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); 			
				break;
			
			default:
				Width = 800;
				Height = 480;

				HSYNC_W = 80;	/* =10时,显示错位,20时部分屏可以的,80时全部OK */
				HBP = 10;
				HFP = 10;
				VSYNC_W = 10;
				VBP = 10;
				VFP = 10;		
			
				/* LCD 时钟配置 */
				/* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */
				/* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz */
				/* PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz */
				/* LTDC clock frequency = PLLLCDCLK = 24MHz */
				/*
					刷新率 = 24MHz /((Width + HSYNC_W  + HBP  + HFP)*(Height + VSYNC_W +  VBP  + VFP))
                   		   = 24000000/((800 + 96  + 10  + 10)*(480 + 2 +  10  + 10)) 
			               = 24000000/(916*502)
                           = 52Hz

					根据需要可以加大,100Hz刷新率完全没问题,设置PeriphClkInitStruct.PLL3.PLL3N = 100即可
					此时的LTDC时钟是50MHz
					刷新率 = 50MHz /((Width + HSYNC_W  + HBP  + HFP )*(Height + VSYNC_W +  VBP  +VFP  )) 
					       = 5000000/(916*502) 
					       = 108.7Hz

					当前这个配置方便用户使用PLL3Q输出的48MHz时钟供USB使用。
			    */ 
				PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
				PeriphClkInitStruct.PLL3.PLL3M = 5;
				PeriphClkInitStruct.PLL3.PLL3N = 48;
				PeriphClkInitStruct.PLL3.PLL3P = 2;
				PeriphClkInitStruct.PLL3.PLL3Q = 5;
				PeriphClkInitStruct.PLL3.PLL3R = 10;  
				HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); 			
				break;
		}		

		g_LcdHeight = Height;
		g_LcdWidth = Width;
		
		/* 配置信号极性 */	
		hltdc_F.Init.HSPolarity = LTDC_HSPOLARITY_AL;	/* HSYNC 低电平有效 */
		hltdc_F.Init.VSPolarity = LTDC_VSPOLARITY_AL; 	/* VSYNC 低电平有效 */
		hltdc_F.Init.DEPolarity = LTDC_DEPOLARITY_AL; 	/* DE 低电平有效 */
		hltdc_F.Init.PCPolarity = LTDC_PCPOLARITY_IPC;

		/* 时序配置 */    
		hltdc_F.Init.HorizontalSync = (HSYNC_W - 1);
		hltdc_F.Init.VerticalSync = (VSYNC_W - 1);
		hltdc_F.Init.AccumulatedHBP = (HSYNC_W + HBP - 1);
		hltdc_F.Init.AccumulatedVBP = (VSYNC_W + VBP - 1);  
		hltdc_F.Init.AccumulatedActiveH = (Height + VSYNC_W + VBP - 1);
		hltdc_F.Init.AccumulatedActiveW = (Width + HSYNC_W + HBP - 1);
		hltdc_F.Init.TotalHeigh = (Height + VSYNC_W + VBP + VFP - 1);
		hltdc_F.Init.TotalWidth = (Width + HSYNC_W + HBP + HFP - 1); 

		/* 配置背景层颜色 */
		hltdc_F.Init.Backcolor.Blue = 0;
		hltdc_F.Init.Backcolor.Green = 0;
		hltdc_F.Init.Backcolor.Red = 0;

		hltdc_F.Instance = LTDC;

		/* 开始配置图层 ------------------------------------------------------*/
		/* 窗口显示区设置 */ 
		pLayerCfg.WindowX0 = 0;
		pLayerCfg.WindowX1 = Width;
		pLayerCfg.WindowY0 = 0;
		pLayerCfg.WindowY1 = Height;

		/* 配置颜色格式 */ 
		pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;

		/* 显存地址 */
		pLayerCfg.FBStartAdress = LCDH7_FRAME_BUFFER;	

		/* Alpha常数 (255 表示完全不透明) */
		pLayerCfg.Alpha = 255;

		/* 无背景色 */
		pLayerCfg.Alpha0 = 0; 	/* 完全透明 */
		pLayerCfg.Backcolor.Blue = 0;
		pLayerCfg.Backcolor.Green = 0;
		pLayerCfg.Backcolor.Red = 0;

		/* 配置图层混合因数 */
		pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
		pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;

		/* 配置行列大小 */
		pLayerCfg.ImageWidth  = Width;
		pLayerCfg.ImageHeight = Height;

		/* 配置LTDC  */  
		if (HAL_LTDC_Init(&hltdc_F) != HAL_OK)
		{
			/* 初始化错误 */
			Error_Handler(__FILE__, __LINE__);
		}

		/* 配置图层1 */
		if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, LTDC_LAYER_1) != HAL_OK)
		{
			/* 初始化错误 */
			Error_Handler(__FILE__, __LINE__);
		}  
		
		#if 0
		/* 配置图层2 */
		if (HAL_LTDC_ConfigLayer(&hltdc_F, &pLayerCfg, LTDC_LAYER_2) != HAL_OK)
		{
			/* 初始化错误 */
			Error_Handler(__FILE__, __LINE__);
		} 
        #endif 		
	}  

#if	1
	HAL_NVIC_SetPriority(LTDC_IRQn, 0xE, 0);
	HAL_NVIC_EnableIRQ(LTDC_IRQn);
#endif	
}
/*
*********************************************************************************************************
*	函 数 名: LCDH7_InitDMA2D
*	功能说明: 配置DMA2D
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
static void LCDH7_InitDMA2D(void)
{
	/* 使能DMA2D时钟 */
	__HAL_RCC_DMA2D_CLK_ENABLE();   
	
	/* 配置默认模式 */ 
	hdma2d.Init.Mode         = DMA2D_R2M;
	hdma2d.Init.ColorMode    = DMA2D_INPUT_RGB565;
	hdma2d.Init.OutputOffset = 0x0;     

	hdma2d.Instance          = DMA2D; 

	if (HAL_DMA2D_Init(&hdma2d) != HAL_OK)
	{
		Error_Handler(__FILE__, __LINE__);
	}
 }
 /*
*********************************************************************************************************
*	函 数 名: LCDH7_SetLayer
*	功能说明: 切换层。只是更改程序变量,以便于后面的代码更改相关寄存器。硬件支持2层。
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void LCDH7_SetLayer(uint8_t _ucLayer)
{
	if (_ucLayer == LCD_LAYER_1)
	{
		s_CurrentFrameBuffer = LCDH7_FRAME_BUFFER;
		s_CurrentLayer = LCD_LAYER_1;
	}
	else if (_ucLayer == LCD_LAYER_2)
	{
		s_CurrentFrameBuffer = LCDH7_FRAME_BUFFER + BUFFER_OFFSET;
		s_CurrentLayer = LCD_LAYER_2;
	}
}
/*
*********************************************************************************************************
*	函 数 名: LCDH7_QuitWinMode
*	功能说明: 退出窗口显示模式,变为全屏显示模式
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void LCDH7_QuitWinMode(void)
{
	HAL_LTDC_SetWindowSize_NoReload(&hltdc_F, g_LcdWidth, g_LcdHeight, s_CurrentLayer);	/* 不立即更新 */
	HAL_LTDC_SetWindowPosition(&hltdc_F, 0, 0, s_CurrentLayer);		/* 立即更新 */	
}
/*
*********************************************************************************************************
*	函 数 名: LCDH7_InitHard
*	功能说明: 初始化LCD
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void LCDH7_InitHard(void)
{
	LCDH7_ConfigLTDC();			/* 配置429 CPU内部LTDC */
	LCDH7_InitDMA2D();          /* 使能DMA2D */
	LCDH7_SetLayer(LCD_LAYER_1);/* 使用的图层1 */	
	LCDH7_QuitWinMode();
}

/*
*********************************************************************************************************
*	函 数 名: LCDH7_SetDirection
*	功能说明: 设置显示屏显示方向(横屏 竖屏)
*	形    参: 显示方向代码 0 横屏正常, 1=横屏180度翻转, 2=竖屏, 3=竖屏180度翻转
*	返 回 值: 无
*********************************************************************************************************
*/
void LCDH7_SetDirection(uint8_t _dir)
{
	uint16_t temp;
	
	if (_dir == 0 || _dir == 1)		/* 横屏, 横屏180度 */
	{
		if (g_LcdWidth < g_LcdHeight)
		{
			temp = g_LcdWidth;
			g_LcdWidth = g_LcdHeight;
			g_LcdHeight = temp;			
		}
	}
	else if (_dir == 2 || _dir == 3)	/* 竖屏, 竖屏180°*/
	{
		if (g_LcdWidth > g_LcdHeight)
		{
			temp = g_LcdWidth;
			g_LcdWidth = g_LcdHeight;
			g_LcdHeight = temp;			
		}
	}
}

/*
*********************************************************************************************************
*	函 数 名: LCD_SetDirection
*	功能说明: 设置显示屏显示方向(横屏 竖屏)
*	形    参: 显示方向代码 0 横屏正常, 1=横屏180度翻转, 2=竖屏, 3=竖屏180度翻转
*	返 回 值: 无
*********************************************************************************************************
*/
void LCD_SetDirection(uint8_t _dir)
{
	g_LcdDirection =  _dir;		/* 保存在全局变量 */

	LCDH7_SetDirection(_dir);
}

*********************************************************************************************************
*	函 数 名: LCDH7_ClrScr
*	功能说明: 根据输入的颜色值清屏
*	形    参: _usColor : 背景色
*	返 回 值:*********************************************************************************************************
*/
void LCDH7_ClrScr(uint16_t _usColor)
{
	LCDH7_FillRect(0, 0, g_LcdHeight, g_LcdWidth, _usColor);
}
/*
*********************************************************************************************************
*	函 数 名: LCD_ClrScr
*	功能说明: 根据输入的颜色值清屏
*	形    参: _usColor : 背景色
*	返 回 值: 无
*********************************************************************************************************
*/
void LCD_ClrScr(uint16_t _usColor)
{
	LCDH7_ClrScr(_usColor);
}

void LCD_InitHard(void)
{
	LCD_HardReset();	/* 硬件复位 (STM32-V5, V6 无需),针对其他GPIO控制LCD复位的产品 */

	LCDH7_InitHard();
	
	LCD_SetDirection(0);

	LCD_ClrScr(CL_BLACK);	/* 清屏,显示全黑 */

//	LCD_SetBackLight(BRIGHT_DEFAULT);	 /* 打开背光,设置为缺省亮度 */
}

应用

	FONT_T tFont12;			/* 定义一个字体结构体变量,用于设置字体参数 */
	FONT_T tFont16;			/* 定义一个字体结构体变量,用于设置字体参数 */
    
	uint8_t buf[100], count = 0;

	/* 设置字体参数 */
	{
		tFont12.FontCode = FC_ST_12;	    /* 字体代码 12点阵 */
		tFont12.FrontColor = CL_WHITE;		/* 字体颜色 */
		tFont12.BackColor = CL_BLUE;	    /* 文字背景颜色 */
		tFont12.Space = 0;					/* 文字间距,单位 = 像素 */
	}
	
	/* 设置字体参数 */
	{
		tFont16.FontCode = FC_ST_16;	    /* 字体代码 16点阵 */
		tFont16.FrontColor = CL_WHITE;		/* 字体颜色 */
		tFont16.BackColor = CL_BLUE;	    /* 文字背景颜色 */
		tFont16.Space = 0;					/* 文字间距,单位 = 像素 */
	}
	
    
	bsp_Init();		/* 硬件初始化 */
	PrintfLogo();	/* 打印例程名称和版本等信息 */

	/* 延迟200ms再点亮背光,避免瞬间高亮 */
	bsp_DelayMS(200); 
	
	/* 清屏 */
	LCD_ClrScr(CL_BLACK);

	/* 显示汉字 */
	LCD_DispStr(5, 3, "故人西辞黄鹤楼,烟花三月下扬州。", &tFont12); 
	LCD_DispStr(5, 18, "孤帆远影碧空尽,唯见长江天际流。", &tFont12); 
	LCD_DispStr(5, 38, "故人西辞黄鹤楼,烟花三月下扬州。", &tFont16); 
	LCD_DispStr(10, 58, "孤帆远影碧空尽,唯见长江天际流。", &tFont16); 
	/* 绘制2D图形 */
	LCD_DrawLine(5, 120, 100, 220, CL_RED);
	LCD_DrawRect(120, 120, 100, 100, CL_RED);
	LCD_DrawCircle(280, 170, 50, CL_RED);
	LCD_Fill_Rect (340, 120, 100, 100, CL_BUTTON_GREY);

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

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

相关文章

鸿蒙 harmonyos 线程 并发 总结 async promise Taskpool woker(三)多线程并发 Worker

Worker Worker是与主线程并行的独立线程。创建Worker的线程称之为宿主线程&#xff0c;Worker自身的线程称之为Worker线程。创建Worker传入的url文件在Worker线程中执行&#xff0c;可以处理耗时操作但不可以直接操作UI。 Worker主要作用是为应用程序提供一个多线程的运行环境…

办公设备租赁行业内卷瞎扯

办公设备租赁行业内卷瞎扯 最近听到很多同行抱怨&#xff0c;现在市场太卷了&#xff0c;真的有点到了卷不死就往死里卷的节奏&#xff0c;让大家都开始想换地方&#xff0c;或者转行。但是今天&#xff0c;我想从另外一个角度聊一下这个问题&#xff0c;分析一下&#xff0c;…

苍穹外卖day9 (1)用户端历史订单

文章目录 前言用户端历史订单1. 查询历史订单1.1 业务规则1.2 接口设计1.3 代码实现 2. 查询历史订单详情2.1 接口设计2.2 代码实现 3. 取消订单3.1 业务规则3.2 接口设计3.3 代码设计 4. 再来一单4.1 业务规则4.2 接口设计4.3 代码实现 前言 用户端对历史订单的操作&#xff…

机器人系统开发ros2-基础学习16-使用 rosdep 管理依赖关系

1. what is rosdep? rosdep是一个依赖管理实用程序&#xff0c;可以与包和外部库一起使用。它是一个命令行实用程序&#xff0c;用于识别和安装依赖项以构建或安装包。 其本身rosdep并不是一个包管理器&#xff1b;它是一个元包管理器&#xff0c;它使用自己的系统知识和依赖…

Day10案例分页查询,条件查询

对要求进行逻辑分析,传递固定参数{page,pagesize}任意参数{name,gender,begin,end},返回总记录数以及当前页码的记录 不使用pagehelper插件,首先完成SQL语句 SQL语句 //固定头 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLI…

SQL-DML数据操纵语言(Oracle)

文章目录 DML数据操纵语言常见的字段属性字符型字段属性char(n)varchar2(n)/varchar(n) 数值型字段属性number([p],[s]int 日期型字段属性DATEtimestamp 如何查看字段属性增加数据INSERT快捷插入 删除数据DELETE修改数据UPDATE DML数据操纵语言 定义 是针对数据做处理&#xf…

JavaScript中的map()方法详解

1. map() 的返回值是一个新的数组&#xff0c;新数组中的元素为 “原数组调用函数处理过后的值” 2. 简单使用&#xff1a;遍历整个数组&#xff0c;将大于4的元素乘以2 const array [2, 3, 4, 4, 5, 6]console.log("array",array) const map array.map(x > {…

【THM】Linux Privilege Escalation(权限提升)-初级渗透测试

介绍 权限升级是一个旅程。没有灵丹妙药,很大程度上取决于目标系统的具体配置。内核版本、安装的应用程序、支持的编程语言、其他用户的密码是影响您通往 root shell 之路的几个关键要素。 该房间旨在涵盖主要的权限升级向量,并让您更好地了解该过程。无论您是参加 CTF、参加…

什么?双核A7双网口核心板只要49?

“性价比之王” 触觉智能IDO-SOM2D0X系列基于SigmaStar SSD201/202 SoC的超小SOM模组&#xff0c;双核A7 1.2GHz主频&#xff0c;1080P视频解码&#xff0c;支持MIPI/RGB显示接口&#xff0c;支持双以太网&#xff0c;支持SDIO/USB/SPI/I2C/UART/DMIC/I2S&#xff0c;集成音频C…

跨平台手机号:微信手机号授权登录、微信授权登录双登录实现账户生态融合,新时代的身份密钥

小程序厂商的多样性体现在开发工具、服务领域、商业模式等多方面&#xff0c;各厂商凭借独特的技术优势、行业解决方案和市场策略&#xff0c;满足不同企业和用户需求。与此同时&#xff0c;随着移动互联网发展&#xff0c;手机号统一登录成为提升用户体验、简化登录流程的关键…

CTFshow-PWN-栈溢出(pwn36)

存在后门函数&#xff0c;如何利用&#xff1f; 好好好&#xff0c;终于到了这种有后门函数的了 checksec 检查一下&#xff1a; 32 位程序&#xff0c;RELRO 保护部分开启 RWX: Has RWX segments 存在可读可写可执行的段 使用 ida32 看 main 函数 跟进 ctfshow 函数…

T2I-Adapter:学习适配器为文本到图像扩散模型挖掘更多可控能力

文章目录 一、研究动机二、T2I-Adapter的特点三、模型方法&#xff08;一&#xff09;关于stable diffusion&#xff08;二&#xff09;适配器设计1、结构控制2、空间调色板3、多适配器控制 &#xff08;三&#xff09;模型优化训练期间的非均匀时间步采样 一、研究动机 T2I模…

安卓手机如何改ip地址?探索方法与注意事项

在数字时代&#xff0c;IP地址成为了我们在线身份的重要标识。对于安卓手机用户而言&#xff0c;了解如何修改IP地址可能涉及多种场景&#xff0c;那么&#xff0c;如何安全、有效地进行这一操作呢&#xff1f;下面将为您提供相关方法&#xff0c;并探讨修改IP地址时的注意事项…

国外问卷调查如何做?需要借助海外住宅IP吗?

在数字化时代&#xff0c;国外问卷调查不仅是了解市场需求的重要手段&#xff0c;还成为了一项能够赚取额外收入的方式。随着全球范围内消费者行为的多样化&#xff0c;各类企业和机构越来越需要了解不同地区的用户观点和偏好&#xff0c;以优化产品和服务。 一、国外问卷调查…

接口测试和Mock学习路线(中)

1.什么是 swagger Swagger 是一个用于生成、描述和调用 RESTful 接口的 WEB 服务。 通俗的来讲&#xff0c;Swagger 就是将项目中所有想要暴露的接口展现在页面上&#xff0c;并且可以进行接口调用和测试的服务。 现在大部分的项目都使用了 swagger&#xff0c;因为这样后端…

基于STM32实现流水灯【Proteus仿真】

详情更多 wechat&#xff1a;嵌入式工程师成长日记 https://mp.weixin.qq.com/s?__bizMzg4Mzc3NDUxOQ&mid2247485624&idx1&sn4e553234c2624777409bd2067a07aad8&chksmcf430de0f83484f6189b119d9d83ea6e6f2a85d13afaa04d218483918231c38e6382d3007061&tok…

【大语言模型LLM】- Meta开源推出的新一代大语言模型 Llama 3

&#x1f525;博客主页&#xff1a;西瓜WiFi &#x1f3a5;系列专栏&#xff1a;《大语言模型》 很多非常有趣的模型&#xff0c;值得收藏&#xff0c;满足大家的收集癖&#xff01; 如果觉得有用&#xff0c;请三连&#x1f44d;⭐❤️&#xff0c;谢谢&#xff01; 长期不…

【图说】VMware Ubuntu22.04 详细安装教程

前言 无论是从事 Linux 开发工作&#xff0c;还是希望电脑运行双系统&#xff0c;VMware 虚拟机都是我们日常工作不可或缺的工具。本章将会重点介绍 VMware 安装流程&#xff0c;以及在 VMware 上如何运行、使用 Ubuntu22.04 系统。 一、VMware 下载安装 1.1 VMware 官网下载…

使用虚拟信用卡订阅Starlink教程

Starlink 是由 SpaceX 公司开发的卫星互联网服务平台。它旨在通过将成千上万的卫星部署到地球轨道上&#xff0c;为全球范围内的用户提供高速互联网接入。通过 Starlink&#xff0c;用户可以通过卫星连接接入互联网&#xff0c;无需依赖传统的地面基础设施&#xff0c;这对于偏…

软考高项(已通过,E类人才)-学习笔记材料梳理汇总

软考高项&#xff0c;即软考高级信息系统项目管理师&#xff0c;全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试中的高级水平测试。适用于从事计算机应用技术、软件、网络、信息系统和信息服务等领域的专业人员&#xff0c;以及各级企业管理人员和从事项目…