完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
本章节为大家讲解LCD控制器LTDC(LCD-TFT display controller),实际项目中用到显示屏的地方全靠它了,而且性能也比较给力。
50.1 初学者重要提示
50.2 LTDC基础知识
50.3 LTDC的HAL库用法
50.4 源文件stm32h7xx_hal_ltdc.c
50.5 总结
LTDC的几个关键知识点放在开头说:
32位颜色格式,一个像素点占用4字节,其中低位 3 字节用于颜色分量,高位字节用于 Alpha 混合。红、绿、蓝和 Alpha通道(0x00表示完全透明,0xFF表示完全不透明)都是 8 位表示。颜色格式:AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB。
24位颜色格式,一个像素点占用3字节,分别用于红、绿、蓝。
颜色格式:RRRRRRRRGGGGGGGGBBBBBBBB。
16位颜色格式,一个像素点占用2字节,分别用于红、绿、蓝。
颜色格式:RRRRRGGGGGGBBBBB。
16位颜色格式,一个像素点占用2字节,Alpha通道使用1个位表示,等于0的时候表示完全透明,等于1的时候表示完全不透明。红、绿、蓝都是用5个位表示。
颜色格式:ARRRRRGGGGGBBBBB。
16位颜色格式,一个像素点占用2字节,Alpha通道使用2个位表示(0x0表示完全透明,0x3表示完全不透明)。红、绿、蓝都是用4个位表示。
颜色格式:ARRRRRGGGGGBBBBB。
8位颜色格式,实际上仅仅是8位索引值,范围0–255,而每个索引值的具体颜色值在查色表CLUT里面存储。
8位颜色格式,实际上是4位Alpha通道(0x0表示完全透明,0xF表示完全不透明)和4位的索引值,索引范围0–15,而每个索引值的具体颜色值在查色表CLUT里面存储。
16位颜色格式,实际上是8位Alpha通道(0x00表示完全透明,0xFF表示完全不透明)和8位的索引值,索引范围0–255,而每个索引值的具体颜色值在查色表CLUT里面存储。
认识一个外设,最好的方式就是看它的框图,方便我们快速地了解LTDC的基本功能,然后再看手册了解细节。框图如下所示:
通过这个框图,我们可以得到如下信息:
为LTDC寄存器提供时钟,时钟来自AXI时钟域。
LTDC寄存器接口时钟。
用于生成LCD_CLK(像素时钟输出)的LTDC内核时钟。
LTDC行中断,用于触发MDMA。
LTDC全局中断请求。
LTDC全局错误中断请求。
下面是LCD接口引脚,用于外接显示屏:
像素时钟输出。
水平同步信号。
垂直同步信号。
数据使能信号。
8位红色数据。
8 位绿色数据。
8位蓝色数据。
LTDC仅有一个时钟源可供选择,即PLL3R。
一般情况下,STM32H7都是用SDRAM作为LCD的显存,LTDC控制器会从SDRAM读取数据刷新到LCD显示屏上,具体如何刷新呢?这就涉及到DE同步模式和HV同步模式。
具体支持哪种模式是由裸屏自带的Driver IC决定,比如V7板子7寸裸屏的Source Driver IC OTA7001支持DE和HV两种模式。现在的大分辨率显示屏一般都是DE同步模式,小分辨率的HV同步模式多。
DE模式需要LCD_DE和LCD_CLK信号来控制刷新。比如一个800x480分辨率的裸屏,在DE有效信号的时候(高电平或低电平),就有800个LCD_CLK输出时钟来确认行中800个点。每个时钟有效的时候,从显存读取一次RGB数据。因为存在回扫信号,所以DE是个方波。一个周期的LCD_DE信号,裸屏就扫描一行。扫描480行后,又从第一行扫描开始。这个规律由裸屏的驱动IC所决定的。
HV模式需要LCD_CLK时钟信号,行同步信号LCD_HSYNC和场同步信号LCD_VSYNC来控制刷新。比如一个480x272分辨率的裸屏,有一个行同步信号LCD_HSYNC产生时(高电平或者低电平脉冲),就有480个LCD_CLK输出时钟来确认行中480个点。每个时钟有效的时候,从显存读取一次RGB数据。再来一个行同步信号LCD_HSYNC产生时(高电平或者低电平脉冲),切换到下一行,继续行同步和时钟输出,扫描272行后,发送一个场同步信号LCD_VSYNC,又重新从第一行扫描开始。
具体的时序效果可以看第51章的内容。
LTDC的时序控制就是下面几个参数的设置,这几个参数都可以通过寄存器进行配置。
水平同步宽度设置,以LCD_CLK的像素时钟输出为单位。
水平后沿周期设置,以LCD_CLK的像素时钟输出为单位。
有效宽度设置,以LCD_CLK的像素时钟输出为单位。以800*480分辨率为例,Active width = 800。
水平前沿周期设置,以LCD_CLK的像素时钟输出为单位。
垂直同步宽度设置,以LCD_CLK的像素时钟输出为单位。
垂直后沿周期设置,以LCD_CLK的像素时钟输出为单位。
有效高度设置,以LCD_CLK的像素时钟输出为单位。以800*480分辨率为例,Active height = 480。
垂直前沿周期设置,以LCD_CLK的像素时钟输出为单位。
LTDC除了图层1和图层2两个硬件图层以外,还有一个背景层。由于背景层的刷新不需要显存空间,所以可以用这个图层验证LTDC时序配置是否有问题。
hltdc_F.Init.Backcolor.Green = 0
hltdc_F.Init.Backcolor.Red = 0
– ARGB8888
– RGB888
– RGB565
– ARGB1555
– ARGB4444
– L8(8 位 Luminance 或 CLUT)
– AL44(4 位 alpha + 4 位 luminance)
– AL88(8 位 alpha + 8 位 luminance)
==================================
了解了这点后就是具体的实现了。STM32H7的参考手册给出了具体的混合公式
BC = BF1 x C + BF2 x Cs
混合后的颜色= 混合系数1 x 当前层颜色 + 混合系数2 x 底层混合后的颜色
(1)常数 Alpha
(2)像素 Alpha x 常数 Alpha
(1)1 - 常数 Alpha
(2)1 - 像素 Alpha x 常数 Alpha
(1)可以是背景层。
(2)可以是背景层与图层1混合后的颜色。
混合后的颜色 = 常数 Alpha x 当前层颜色 + (1 - 常数 Alpha) x 底层混合后的颜色。
混合后的颜色 = 像素 Alpha x 常数 Alpha x 当前层颜色 +(1 - 像素 Alpha x 常数 Alpha) x 底层混合后的颜色。
像素Alpha是ARGB8888,ARGB1555等颜色格式的Alpha数值,也就是我们为图层每个位置绘制的实际颜色值,我们这里用AlphaValue表示。
混合后的颜色 = (pLayerCfg.Alpha /255 ) x 当前层颜色 +(1 - pLayerCfg.Alpha /255 ) x 底层混合后的颜色。
混合后的颜色 = (AlphaValue/255)x (pLayerCfg.Alpha /255 ) x 当前层颜色 +(1 - (AlphaValue/255)x (pLayerCfg.Alpha /255 )) x 底层混合后的颜色。
注,Alpha值要做归一化,Alpha的范围是0 - 255,比如Alpha = 100,那么代入公式的时候就是100/255。
(1)混合因数1选择像素 Alpha x 常数 Alpha。
(2)混合因数2选择像素 Alpha x 常数 Alpha。
(3)使用图层1和背景层,图层1使用ARGB8888颜色格式。
(4)背景色 = 0xFF0000,图层1位置坐标(0,0)颜色值0x5500FF00。
(5)Alpha常数 = 0x77
混合后的颜色 = (AlphaValue/255)x (pLayerCfg.Alpha /255 ) x 当前层颜色 +(1 - (AlphaValue/255)x (pLayerCfg.Alpha /255 )) x 底层混合后的颜色。
= (0x55/255)x (0x77/255) x 0x00FF00 +(1 - (0x55/255)x (0x77/255)) x 0xFF0000
= 0xD7809C
如果大家设置的图层显示区没有显示满整个显示屏,且使用的ARGB颜色格式,那么未覆盖的区域会使用图层默认颜色。对此HAL库有专门的配置:
LTDC_LayerCfgTypeDef pLayerCfg
pLayerCfg.Alpha0 = 0;
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
正常情况下,LCD的刷新就是从左到右,从上到下进行逐个像素点刷新。但仅刷新有效的显示区是不够的,比如800*480分辨率,我们不仅仅要刷800*480这段有效区域,边界区也是要刷新的,即下图总宽度以内,有效宽度以外的区域。
水平消隐就是LCD扫描一行结束到另一行开始的时间,这段消失的时间就是水平消隐,即HSYNC宽度+ HBP + HFP这段消失的时间。
垂直消隐就是LCD扫描最后一行结束到第一行开始的时间,这段消失的时间就是垂直消隐,即VSYNC宽度+ VBP + VFP这段消失的时间。
FPS帧率是对STM32H7刷到显存,也就是SDRAM里面来说的,而是刷新率是实际LCD显示的速度。
刷新率 = LTDC输出时钟 /((Width + HSYNC_W + HBP + HFP )*(Height + VSYNC_W + VBP +VFP ))
一般情况下,帧率是远高于刷新率的,但帧数高于刷新率有没有意义?网上普遍的看法是高于刷新率的帧数都是浪费,比如H7刷800*480分辨率显示屏,帧数可以达到300多,而刷新率估计才108Hz,多出来的不是都浪费了吗(对于那种FPS类的游戏,我们不讨论)。
对于这种观点,在一定情况下是成立的。但是有一点,即使是帧数和刷新率都是108Hz,能保证显示没有撕裂感吗?能保证没有帧延迟吗?能保证流畅的画面吗?这个时候,我们使用emWin支持的三缓冲,多余的帧数完全可以输出到其它缓冲区,有效降低撕裂感和帧延迟,保证流畅输出。
如果用户快速刷新颜色差异比较大的两种界面,容易遇到这种撕裂问题。
用户更新显存数据期间,LTDC也在不断的读取显存的数据到显示屏上,如果用户才更新了部分界面数据,后面部分还没有更新,LTDC刷新到显示屏的界面效果出现撕裂感,即下面这种现象:
LTDC刷新还在垂直消隐期间就将整个界面刷新完成,而我们如何只知道LTDC在垂直消隐期,通过函数HAL_LTDC_ProgramLineEvent设置刷新到指定行时进入中断即可,一般设置为第0行进入中断,然后设置个标志即可。
一旦检测到这个标志,就通过DMA2D快速将界面刷新好,这样就有效的避免了撕裂感。
LTDC的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC、中断和DMA。下面我们逐一展开为大家做个说明。
LTDC相关的寄存器是通过HAL库中的结构体LTDC_TypeDef定义的,在stm32h743xx.h中可以找到它们的具体定义:
typedef struct
{
uint32_t RESERVED0[2];/*!< Reserved, 0x00-0x04 */
__IO uint32_t SSCR; /*!< LTDC Synchronization Size Configuration Register, Address offset: 0x08 */
__IO uint32_t BPCR; /*!< LTDC Back Porch Configuration Register, Address offset: 0x0C */
__IO uint32_t AWCR; /*!< LTDC Active Width Configuration Register, Address offset: 0x10 */
__IO uint32_t TWCR; /*!< LTDC Total Width Configuration Register, Address offset: 0x14 */
__IO uint32_t GCR; /*!< LTDC Global Control Register, Address offset: 0x18 */
uint32_t RESERVED1[2]; /*!< Reserved, 0x1C-0x20 */
__IO uint32_t SRCR; /*!< LTDC Shadow Reload Configuration Register, Address offset: 0x24 */
uint32_t RESERVED2[1]; /*!< Reserved, 0x28 */
__IO uint32_t BCCR; /*!< LTDC Background Color Configuration Register, Address offset: 0x2C */
uint32_t RESERVED3[1]; /*!< Reserved, 0x30 */
__IO uint32_t IER; /*!< LTDC Interrupt Enable Register, Address offset: 0x34 */
__IO uint32_t ISR; /*!< LTDC Interrupt Status Register, Address offset: 0x38 */
__IO uint32_t ICR; /*!< LTDC Interrupt Clear Register, Address offset: 0x3C */
__IO uint32_t LIPCR; /*!< LTDC Line Interrupt Position Configuration Register, Address offset: 0x40 */
__IO uint32_t CPSR; /*!< LTDC Current Position Status Register, Address offset: 0x44 */
__IO uint32_t CDSR; /*!< LTDC Current Display Status Register, Address offset: 0x48 */
} LTDC_TypeDef;
__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:
#define __O volatile /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
下面我们再看LTDC的定义,在stm32h743xx.h文件。
#define PERIPH_BASE ((uint32_t)0x40000000)
#define D1_APB1PERIPH_BASE (PERIPH_BASE + 0x10000000)
#define LTDC_BASE (D1_APB1PERIPH_BASE + 0x1000)
#define LTDC ((LTDC_TypeDef *)LTDC_BASE) <----- 展开这个宏,(LTDC_TypeDef *) 0x50001000
我们访问LTDC的ISR寄存器可以采用这种形式:LTDC->ISR = 0。
此结构体用于配置LTDC的基本参数,具体定义如下:
typedef struct
{
uint32_t HSPolarity;
uint32_t VSPolarity;
uint32_t DEPolarity;
uint32_t PCPolarity;
uint32_t HorizontalSync;
uint32_t VerticalSync;
uint32_t AccumulatedHBP;
uint32_t AccumulatedVBP;
uint32_t AccumulatedActiveW;
uint32_t AccumulatedActiveH;
uint32_t TotalWidth;
uint32_t TotalHeigh;
LTDC_ColorTypeDef Backcolor;
} LTDC_InitTypeDef;
下面将这几个参数逐一为大家做个说明:
此参数用于设置水平同步信号极性,具体支持的参数如下:
#define LTDC_HSPOLARITY_AL (0x00000000U) /* 水平同步极性低电平有效 */
#define LTDC_HSPOLARITY_AH LTDC_GCR_HSPOL /* 水平同步极性高电平有效 */
此参数用于设置垂直同步信号极性,具体支持的参数如下:
#define LTDC_VSPOLARITY_AL (0x00000000U) /* 垂直同步极性低电平有效 */
#define LTDC_VSPOLARITY_AH LTDC_GCR_VSPOL /* 垂直同步极性高电平有效 */
此参数用于设置数据使能极性,具体支持的参数如下:
#define LTDC_DEPOLARITY_AL (0x00000000U) /* 数据使能极性低电平有效 */
#define LTDC_DEPOLARITY_AH LTDC_GCR_DEPOL /* 数据使能极性高电平有效 */
此参数用于设置像素时钟极性,具体支持的参数如下:
#define LTDC_PCPOLARITY_IPC (0x00000000U) /* 像素时钟极性低电平有效 */
#define LTDC_PCPOLARITY_IIPC LTDC_GCR_PCPOL /* 像素时钟极性告电平有效 */
此参数用于设置水平同步宽度,范围0x000 – 0xFFF,单位像素时钟个数。
此参数用于设置垂直同步宽度,范围0x000 – 0x7FF,单位像素时钟个数。
此参数用于设置HSYNC水平同步宽度 + HBP水平后沿之和,范围HSYNC水平同步宽度到0xFFF,单位像素时钟个数。
此参数用于设置VSYNC垂直同步宽度 + VBP垂直后沿之和,范围VSYNC垂直同步宽度到0x7FF,单位像素时钟个数。
此参数用于设置HSYNC水平同步宽度 + HBP水平后沿 + 有效宽度之和,范围AccumulatedHBP到0xFFF,单位像素时钟个数。
此参数用于设置VSYNC垂直同步宽度 + VBP垂直后沿 + 有效高度之和,范围AccumulatedVBP到0x7FF,单位像素时钟个数。
此参数用于设置HSYNC水平同步宽度 + HBP水平后沿 + 有效宽度 + HFP水平前沿之和,范围AccumulatedActiveW到0xFFF,单位像素时钟个数。
此参数用于设置VSYNC垂直同步宽度 + VBP垂直后沿 + 有效高度 +VFP垂直前沿之和,范围AccumulatedActiveH到0x7FF,单位像素时钟个数。
此参数用于设置背景层颜色,结构体LTDC_ColorTypeDef的定义如下:
typedef struct
{
uint8_t Blue;
uint8_t Green;
uint8_t Red;
uint8_t Reserved;
} LTDC_ColorTypeDef;
Bule用于设置蓝色值,范围0x00到0xFF。
Green用于设置绿色值,范围0x00到0xFF。
Red用于设置红色值,范围0x00到0xFF。
此结构体用于配置LTDC的图层,图层1和图层2均通过此结构体配置。
typedef struct
{
uint32_t WindowX0;
uint32_t WindowX1;
uint32_t WindowY0;
uint32_t WindowY1;
uint32_t PixelFormat;
uint32_t Alpha;
uint32_t Alpha0;
uint32_t BlendingFactor1;
uint32_t BlendingFactor2;
uint32_t FBStartAdress;
uint32_t ImageWidth;
uint32_t ImageHeight;
LTDC_ColorTypeDef Backcolor;
} LTDC_LayerCfgTypeDef;
下面将这几个参数逐一为大家做个说明。
设置图层水平起始位置,范围0x000到0xFFF。
设置图层水平结束位置,范围0x000到0xFFF。
设置图层垂直起始位置,范围0x000到0x7FF。
设置图层垂直结束位置,范围0x000到0x7FF。
设置图层所使用的颜色格式。
设置常数Alpha,范围0x00 – 0xFF。
设置图层默认Alpha值,范围0x00 – 0xFF,与结构体成员Backcolor一起使用,组成ARGB颜色格式用于图层背景色。
设置混合因数1,具体支持的参数如下:
#define LTDC_BLENDING_FACTOR1_CA (0x00000400U) /* 常数Alpha */
#define LTDC_BLENDING_FACTOR1_PAxCA (0x00000600U) /* 常数Alpha * 像素Alpha */
设置混合因数2,具体支持的参数如下:
#define LTDC_BLENDING_FACTOR2_CA (0x00000400U) /* 常数Alpha */
#define LTDC_BLENDING_FACTOR2_PAxCA (0x00000600U) /* 常数Alpha * 像素Alpha */
设置颜色帧缓冲区地址,即图层的显存地址。
设置颜色帧缓冲区行长,即要从显存读取一行的长度,范围0x0000到0x1FFF。
设置颜色帧缓冲区行数,即要从显存读取的行数,范围0x000到0xFFF。
此参数用于设置图层默认色,结构体LTDC_ColorTypeDef的定义如下:
typedef struct
{
uint8_t Blue;
uint8_t Green;
uint8_t Red;
uint8_t Reserved;
} LTDC_ColorTypeDef;
Bule用于设置蓝色值,范围0x00到0xFF。
Green用于设置绿色值,范围0x00到0xFF。
Red用于设置红色值,范围0x00到0xFF。
注:如果大家设置的图层显示区没有显示满整个显示屏,且使用的ARGB颜色格式,那么未覆盖的区域会使用图层默认颜色。
HAL库在LTDC_TypeDef, LTDC_InitTypeDef和LTDC_LayerCfgTypeDef的基础上封装了一个结构体LTDC_HandleTypeDef,定义如下:
typedef struct
{
LTDC_TypeDef *Instance;
LTDC_InitTypeDef Init;
LTDC_LayerCfgTypeDef LayerCfg[MAX_LAYER];
HAL_LockTypeDef Lock;
__IO HAL_LTDC_StateTypeDef State;
__IO uint32_t ErrorCode;
} LTDC_HandleTypeDef;
下面将这几个参数逐一做个说明。
这个参数是寄存器的例化,方便操作寄存器,详见本章3.1小节。
这个参数是用户接触较多的,用于配置LTDC的时序参数配置,详见本章3.2小节。
这个参数用于LTDC的图层配置,对于STM32H7来说,支持双图层,MAX_LAYER=2。详见本章3.3小节。
__IO uint32_t State;
__IO uint32_t ErrorCode
这三个变量主要供函数内部使用。Lock用于设置锁状态,State用于设置LTDC通信状态,而ErrorCode用于配置代码错误。
在下章的51.4小节给出了详细的设计步骤。
这里把我们把如下几个常用到的函数做个说明:
LTDC的API函数主要分为两类:
带后缀_NoReload的函数没有对重装寄存器进行配置。如果要配置的话,可以通过函数HAL_LTDC_Reload来立即更新配置或者在垂直消隐期间更新。
不带后缀_NoReload的函数会立即更新配置。
===========================================================
能够实现这种操作的关键是LTDC外设有影子寄存器。如果选择不立即更新,可以将用户配置先放到影子寄存器,等垂直消隐期间再更新,这样做的好处是可以整体更新这些寄存器。下面是部分函数:
HAL_StatusTypeDef HAL_LTDC_ConfigLayer(LTDC_HandleTypeDef *hltdc, LTDC_LayerCfgTypeDef *pLayerCfg, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_ConfigLayer_NoReload(LTDC_HandleTypeDef *hltdc, LTDC_LayerCfgTypeDef *pLayerCfg, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetWindowSize(LTDC_HandleTypeDef *hltdc, uint32_t XSize, uint32_t YSize, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetWindowSize_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t XSize, uint32_t YSize, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetWindowPosition(LTDC_HandleTypeDef *hltdc, uint32_t X0, uint32_t Y0, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetWindowPosition_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t X0, uint32_t Y0, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetPixelFormat(LTDC_HandleTypeDef *hltdc, uint32_t Pixelformat, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetPixelFormat_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t Pixelformat, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetAlpha(LTDC_HandleTypeDef *hltdc, uint32_t Alpha, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetAlpha_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t Alpha, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetAddress(LTDC_HandleTypeDef *hltdc, uint32_t Address, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetAddress_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t Address, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetPitch(LTDC_HandleTypeDef *hltdc, uint32_t LinePitchInPixels, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_SetPitch_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t LinePitchInPixels, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_ConfigColorKeying(LTDC_HandleTypeDef *hltdc, uint32_t RGBValue, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_ConfigColorKeying_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t RGBValue, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_EnableColorKeying(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_EnableColorKeying_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_DisableColorKeying(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_DisableColorKeying_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_EnableCLUT(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_EnableCLUT_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_DisableCLUT(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
HAL_StatusTypeDef HAL_LTDC_DisableCLUT_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t LayerIdx);
函数原型:
HAL_StatusTypeDef HAL_LTDC_Init(LTDC_HandleTypeDef *hltdc)
{
uint32_t tmp = 0, tmp1 = 0;
/* 检测参数是否有效 */
if(hltdc == NULL)
{
return HAL_ERROR;
}
/* 检查函数参数 */
assert_param(IS_LTDC_ALL_INSTANCE(hltdc->Instance));
assert_param(IS_LTDC_HSYNC(hltdc->Init.HorizontalSync));
assert_param(IS_LTDC_VSYNC(hltdc->Init.VerticalSync));
assert_param(IS_LTDC_AHBP(hltdc->Init.AccumulatedHBP));
assert_param(IS_LTDC_AVBP(hltdc->Init.AccumulatedVBP));
assert_param(IS_LTDC_AAH(hltdc->Init.AccumulatedActiveH));
assert_param(IS_LTDC_AAW(hltdc->Init.AccumulatedActiveW));
assert_param(IS_LTDC_TOTALH(hltdc->Init.TotalHeigh));
assert_param(IS_LTDC_TOTALW(hltdc->Init.TotalWidth));
assert_param(IS_LTDC_HSPOL(hltdc->Init.HSPolarity));
assert_param(IS_LTDC_VSPOL(hltdc->Init.VSPolarity));
assert_param(IS_LTDC_DEPOL(hltdc->Init.DEPolarity));
assert_param(IS_LTDC_PCPOL(hltdc->Init.PCPolarity));
if(hltdc->State == HAL_LTDC_STATE_RESET)
{
hltdc->Lock = HAL_UNLOCKED;
/* 初始化GPIO,NVIC等 */
HAL_LTDC_MspInit(hltdc);
}
/* 设置LTDC外设状态 */
hltdc->State = HAL_LTDC_STATE_BUSY;
/* 配置HSE,VS,DE和PC极性 */
hltdc->Instance->GCR &= ~(LTDC_GCR_HSPOL | LTDC_GCR_VSPOL | LTDC_GCR_DEPOL | LTDC_GCR_PCPOL);
hltdc->Instance->GCR |= (uint32_t)(hltdc->Init.HSPolarity | hltdc->Init.VSPolarity | \
hltdc->Init.DEPolarity | hltdc->Init.PCPolarity);
/* 设置水平同步宽度和垂直同步宽度 */
hltdc->Instance->SSCR &= ~(LTDC_SSCR_VSH | LTDC_SSCR_HSW);
tmp = (hltdc->Init.HorizontalSync << 16);
hltdc->Instance->SSCR |= (tmp | hltdc->Init.VerticalSync);
/* 设置HSYNC水平同步宽度 + HBP水平后沿之和
设置VSYNC垂直同步宽度 + VBP垂直后沿之和 */
hltdc->Instance->BPCR &= ~(LTDC_BPCR_AVBP | LTDC_BPCR_AHBP);
tmp = (hltdc->Init.AccumulatedHBP << 16);
hltdc->Instance->BPCR |= (tmp | hltdc->Init.AccumulatedVBP);
/* 设置HSYNC水平同步宽度 + HBP水平后沿 + 有效宽度之和
设置VSYNC垂直同步宽度 + VBP垂直后沿 + 有效高度之和 */
hltdc->Instance->AWCR &= ~(LTDC_AWCR_AAH | LTDC_AWCR_AAW);
tmp = (hltdc->Init.AccumulatedActiveW << 16);
hltdc->Instance->AWCR |= (tmp | hltdc->Init.AccumulatedActiveH);
/* 设置总宽度 */
hltdc->Instance->TWCR &= ~(LTDC_TWCR_TOTALH | LTDC_TWCR_TOTALW);
tmp = (hltdc->Init.TotalWidth << 16);
hltdc->Instance->TWCR |= (tmp | hltdc->Init.TotalHeigh);
/* 设置背景层颜色 */
tmp = ((uint32_t)(hltdc->Init.Backcolor.Green) << 8);
tmp1 = ((uint32_t)(hltdc->Init.Backcolor.Red) << 16);
hltdc->Instance->BCCR &= ~(LTDC_BCCR_BCBLUE | LTDC_BCCR_BCGREEN | LTDC_BCCR_BCRED);
hltdc->Instance->BCCR |= (tmp1 | tmp | hltdc->Init.Backcolor.Blue);
/* 使能传输错误中断和FIFO下溢中断 */
__HAL_LTDC_ENABLE_IT(hltdc, LTDC_IT_TE | LTDC_IT_FU);
/* 使能LTDC */
__HAL_LTDC_ENABLE(hltdc);
/* 无错误 Initialize the error code */
hltdc->ErrorCode = HAL_LTDC_ERROR_NONE;
/* 设置LTDC状态 */
hltdc->State = HAL_LTDC_STATE_READY;
return HAL_OK;
}
函数描述:
此函数用于初始化LTDC的基本参数。
函数参数:
注意事项:
对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_LTDC_STATE_RESET = 0x00U。
解决办法有三
方法1:用户自己初始LTDC和涉及到的GPIO等。
方法2:定义LTDC_HandleTypeDef LtdcHandle为全局变量。
方法3:下面的方法
if(HAL_LTDC_DeInit(&LtdcHandle) != HAL_OK)
{
Error_Handler();
}
if(HAL_LTDC_Init(&LtdcHandle) != HAL_OK)
{
Error_Handler();
}
使用举例:
static LTDC_HandleTypeDef hltdc_F;
uint16_t Width, Height, HSYNC_W, HBP, HFP, VSYNC_W, VBP, VFP;
/* 配置信号极性 */
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;
/* 配置LTDC */
if (HAL_LTDC_Init(&hltdc_F) != HAL_OK)
{
/* 初始化错误 */
Error_Handler(__FILE__, __LINE__);
}
函数原型:
HAL_StatusTypeDef HAL_LTDC_ConfigLayer(LTDC_HandleTypeDef *hltdc, LTDC_LayerCfgTypeDef *pLayerCfg, uint32_t LayerIdx)
{
/* 检测参数 */
assert_param(IS_LTDC_LAYER(LayerIdx));
assert_param(IS_LTDC_HCONFIGST(pLayerCfg->WindowX0));
assert_param(IS_LTDC_HCONFIGSP(pLayerCfg->WindowX1));
assert_param(IS_LTDC_VCONFIGST(pLayerCfg->WindowY0));
assert_param(IS_LTDC_VCONFIGSP(pLayerCfg->WindowY1));
assert_param(IS_LTDC_PIXEL_FORMAT(pLayerCfg->PixelFormat));
assert_param(IS_LTDC_ALPHA(pLayerCfg->Alpha));
assert_param(IS_LTDC_ALPHA(pLayerCfg->Alpha0));
assert_param(IS_LTDC_BLENDING_FACTOR1(pLayerCfg->BlendingFactor1));
assert_param(IS_LTDC_BLENDING_FACTOR2(pLayerCfg->BlendingFactor2));
assert_param(IS_LTDC_CFBLL(pLayerCfg->ImageWidth));
assert_param(IS_LTDC_CFBLNBR(pLayerCfg->ImageHeight));
/* 上锁 */
__HAL_LOCK(hltdc);
/* 设置LTDC外设状态 */
hltdc->State = HAL_LTDC_STATE_BUSY;
/* 结构体之间直接赋值 */
hltdc->LayerCfg[LayerIdx] = *pLayerCfg;
/* 配置LTDC图层 */
LTDC_SetConfig(hltdc, pLayerCfg, LayerIdx);
/* 设置立即更新 */
hltdc->Instance->SRCR = LTDC_SRCR_IMR;
/* 设置LTDC就绪 */
hltdc->State = HAL_LTDC_STATE_READY;
/* 解锁 */
__HAL_UNLOCK(hltdc);
return HAL_OK;
}
函数描述:
此函数主要用于配置LTDC的图层。
函数参数:
使用举例:
static LTDC_HandleTypeDef hltdc_F;
uint16_t Width, Height, HSYNC_W, HBP, HFP, VSYNC_W, VBP, VFP;
LTDC_LayerCfgTypeDef pLayerCfg;
* 配置信号极性 */
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__);
}
函数原型:
HAL_StatusTypeDef HAL_LTDC_SetAlpha(LTDC_HandleTypeDef *hltdc, uint32_t Alpha, uint32_t LayerIdx)
{
LTDC_LayerCfgTypeDef *pLayerCfg;
/* 检查参数 */
assert_param(IS_LTDC_ALPHA(Alpha));
assert_param(IS_LTDC_LAYER(LayerIdx));
/* 上锁 */
__HAL_LOCK(hltdc);
/* 设置LTDC状态 */
hltdc->State = HAL_LTDC_STATE_BUSY;
/* 获取图层配置 */
pLayerCfg = &hltdc->LayerCfg[LayerIdx];
/* 配置图层Alpha值 */
pLayerCfg->Alpha = Alpha;
/* 重新配置LTDC */
LTDC_SetConfig(hltdc, pLayerCfg, LayerIdx);
/* 设置立即更新 */
hltdc->Instance->SRCR = LTDC_SRCR_IMR;
/* 设置LTDC状态就绪 */
hltdc->State = HAL_LTDC_STATE_READY;
/* 解锁 */
__HAL_UNLOCK(hltdc);
return HAL_OK;
}
函数描述:
此函数用于设置图层的常数Alpha。
函数参数:
使用举例:
此函数的使用相对比较简单,直接调用即可。
函数原型:
HAL_StatusTypeDef HAL_LTDC_Reload(LTDC_HandleTypeDef *hltdc, uint32_t ReloadType)
{
/* 检测参数 */
assert_param(IS_LTDC_RELOAD(ReloadType));
/* 上锁 */
__HAL_LOCK(hltdc);
/* 设置LTDC状态 Change LTDC peripheral state */
hltdc->State = HAL_LTDC_STATE_BUSY;
/* 使能LTDC重装中断 */
__HAL_LTDC_ENABLE_IT(hltdc, LTDC_IT_RR);
/* 设置立即更新或者下一个垂直消隐期更新Apply Reload type */
hltdc->Instance->SRCR = ReloadType;
/* 设置LTDC就绪 */
hltdc->State = HAL_LTDC_STATE_READY;
/* 解锁 */
__HAL_UNLOCK(hltdc);
return HAL_OK;
}
函数描述:
此函数主要用于配合其它以_NoReload结尾的函数,可以设置立即更新配置或者下一个垂直消隐期更新。
函数参数:
设置为LTDC_RELOAD_VERTICAL_BLANKING表示下一个垂直消隐期更新。
使用举例:
此函数的使用相对比较简单,直接调用即可。
函数原型:
HAL_StatusTypeDef HAL_LTDC_SetPixelFormat(LTDC_HandleTypeDef *hltdc, uint32_t Pixelformat, uint32_t LayerIdx)
{
LTDC_LayerCfgTypeDef *pLayerCfg;
/* 检测参数 */
assert_param(IS_LTDC_PIXEL_FORMAT(Pixelformat));
assert_param(IS_LTDC_LAYER(LayerIdx));
/* 上锁 */
__HAL_LOCK(hltdc);
/* 设置LTDC状态 */
hltdc->State = HAL_LTDC_STATE_BUSY;
/* 获取图层配置 */
pLayerCfg = &hltdc->LayerCfg[LayerIdx];
/* 重新配置图层颜色格式 */
pLayerCfg->PixelFormat = Pixelformat;
/* 配置LTDC */
LTDC_SetConfig(hltdc, pLayerCfg, LayerIdx);
/* 设置立即更新 */
hltdc->Instance->SRCR = LTDC_SRCR_IMR;
/* 设置LTDC就绪 */
hltdc->State = HAL_LTDC_STATE_READY;
/* 解锁 */
__HAL_UNLOCK(hltdc);
return HAL_OK;
}
函数描述:
此函数用于设置图层的颜色格式。
函数参数:
#define LTDC_PIXEL_FORMAT_ARGB8888 (0x00000000U) /*!< ARGB8888 LTDC pixel format */
#define LTDC_PIXEL_FORMAT_RGB888 (0x00000001U) /*!< RGB888 LTDC pixel format */
#define LTDC_PIXEL_FORMAT_RGB565 (0x00000002U) /*!< RGB565 LTDC pixel format */
#define LTDC_PIXEL_FORMAT_ARGB1555 (0x00000003U) /*!< ARGB1555 LTDC pixel format */
#define LTDC_PIXEL_FORMAT_ARGB4444 (0x00000004U) /*!< ARGB4444 LTDC pixel format */
#define LTDC_PIXEL_FORMAT_L8 (0x00000005U) /*!< L8 LTDC pixel format */
#define LTDC_PIXEL_FORMAT_AL44 (0x00000006U) /*!< AL44 LTDC pixel format */
#define LTDC_PIXEL_FORMAT_AL88 (0x00000007U) /*!< AL88 LTDC pixel format */
使用举例:
此函数的使用相对比较简单,直接调用即可。
函数原型:
HAL_StatusTypeDef HAL_LTDC_SetWindowPosition(LTDC_HandleTypeDef *hltdc, uint32_t X0, uint32_t Y0, uint32_t LayerIdx)
{
LTDC_LayerCfgTypeDef *pLayerCfg;
/* 检测参数 */
assert_param(IS_LTDC_LAYER(LayerIdx));
assert_param(IS_LTDC_CFBLL(X0));
assert_param(IS_LTDC_CFBLNBR(Y0));
/* 上锁 */
__HAL_LOCK(hltdc);
/* 设置LTDC状态 */
hltdc->State = HAL_LTDC_STATE_BUSY;
/* 获取图层配置 */
pLayerCfg = &hltdc->LayerCfg[LayerIdx];
/* 更新水平起始和结束位置 */
pLayerCfg->WindowX0 = X0;
pLayerCfg->WindowX1 = X0 + pLayerCfg->ImageWidth;
/* 更新垂直起始和结束位置 */
pLayerCfg->WindowY0 = Y0;
pLayerCfg->WindowY1 = Y0 + pLayerCfg->ImageHeight;
/* 设置LTDC参数 */
LTDC_SetConfig(hltdc, pLayerCfg, LayerIdx);
/* 设置立即更新 */
hltdc->Instance->SRCR = LTDC_SRCR_IMR;
/* 设置LTDC就绪 */
hltdc->State = HAL_LTDC_STATE_READY;
/* 解锁 */
__HAL_UNLOCK(hltdc);
return HAL_OK;
}
函数描述:
此函数用于设置显示区在图层中的起始位置。
函数参数:
使用举例:
此函数的使用相对比较简单,直接调用即可,注意设置的显示区最好不要超出图层范围。
函数原型:
HAL_StatusTypeDef HAL_LTDC_SetWindowSize_NoReload(LTDC_HandleTypeDef *hltdc, uint32_t XSize, uint32_t YSize, uint32_t LayerIdx)
{
LTDC_LayerCfgTypeDef *pLayerCfg;
/* 检测参数 */
assert_param(IS_LTDC_LAYER(LayerIdx));
assert_param(IS_LTDC_CFBLL(XSize));
assert_param(IS_LTDC_CFBLNBR(YSize));
/* 上锁 */
__HAL_LOCK(hltdc);
/* 设置LTDC状态 */
hltdc->State = HAL_LTDC_STATE_BUSY;
/* 获取图层配置 */
pLayerCfg = &hltdc->LayerCfg[LayerIdx];
/* 更新水平结束位置 */
pLayerCfg->WindowX1 = XSize + pLayerCfg->WindowX0;
/* 更新垂直解锁位置 */
pLayerCfg->WindowY1 = YSize + pLayerCfg->WindowY0;
/* 重新配置颜色帧缓冲的行长 */
pLayerCfg->ImageWidth = XSize;
/* 重新配置颜色帧缓冲的行数 */
pLayerCfg->ImageHeight = YSize;
/* 设置LTDC参数 */
LTDC_SetConfig(hltdc, pLayerCfg, LayerIdx);
/* 此函数没有配置立即更新配置,绘制下一个垂直消隐期更新 */
/* 设置LTDC就绪 */
hltdc->State = HAL_LTDC_STATE_READY;
/* 解锁 */
__HAL_UNLOCK(hltdc);
return HAL_OK;
}
函数描述:
此函数用于设置图层显示区的大小,但不会立即更新,会在下一个垂直消隐期更新。
函数参数:
使用举例:
此函数的使用相对比较简单,直接调用即可,注意设置的显示区最好不要超出图层范围。
本章节涉及到的知识点比较多,而且比较重要,特别是第2小节中的几个知识点尤其重要,望初学者熟练掌握。