Files
FireAlarmCtrlCn/FW/Core/my_src/ltdc_drv.c
2026-04-06 19:02:09 +08:00

374 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "lcd_base_display.h"
#include "ltdc_drv.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK 阿波罗STM32H7开发板
//LTDC驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2017/8/12
//版本V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////
LTDC_HandleTypeDef LTDC_Handler; //LTDC句柄
//根据不同的颜色格式,定义帧缓存数组
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
//unsigned int ltdc_lcd_framebuf[800][480] __attribute__((at(LCD_FRAME_BUF_ADDR))); //定义最大屏分辨率时,LCD所需的帧缓存数组大小
static unsigned int * const my_address = (unsigned int *) (LCD_FRAME_BUF_ADDR);
#define ltdc_lcd_framebuf (*my_address)
static unsigned int * const my_address_2 = (unsigned int *) (LCD_FRAME_BUF_2_ADDR);
#define ltdc_lcd_framebuf_2 (*my_address_2)
#else
unsigned short ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR))); //定义最大屏分辨率时,LCD所需的帧缓存数组大小
#endif
unsigned int *ltdc_framebuf[2]; //LTDC LCD帧缓存数组指针,必须指向对应大小的内存区域
_ltdc_dev lcdltdc; //管理LCD LTDC的重要参数
//打开LCD开关
//lcd_switch:1 打开,0关闭
void LTDC_Switch(unsigned char sw)
{
if(sw==1) __HAL_LTDC_ENABLE(&LTDC_Handler);
else if(sw==0)__HAL_LTDC_DISABLE(&LTDC_Handler);
}
//开关指定层
//layerx:层号,0,第一层; 1,第二层
//sw:1 打开;0关闭
void LTDC_Layer_Switch(unsigned char layerx,unsigned char sw)
{
if(sw==1) __HAL_LTDC_LAYER_ENABLE(&LTDC_Handler,layerx);
else if(sw==0) __HAL_LTDC_LAYER_DISABLE(&LTDC_Handler,layerx);
__HAL_LTDC_RELOAD_CONFIG(&LTDC_Handler);
}
//选择层
//layerx:层号;0,第一层;1,第二层;
void LTDC_Select_Layer(unsigned char layerx)
{
lcdltdc.activelayer=layerx;
}
//设置LCD显示方向
//dir:0,竖屏1,横屏
void LTDC_Display_Dir(unsigned char 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;
}
}
//画点函数
//x,y:坐标
//color:颜色值
void LTDC_Draw_Point(unsigned short x,unsigned short y,unsigned int color)
{
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
if(lcdltdc.dir) //横屏
{
*(unsigned int*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x))=color;
}else //竖屏
{
*(unsigned int*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y))=color;
}
#else
if(lcdltdc.dir) //横屏
{
*(unsigned short*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x))=color;
}else //竖屏
{
*(unsigned short*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y))=color;
}
#endif
}
//读点函数
//返回值:颜色值
unsigned int LTDC_Read_Point(unsigned short x,unsigned short y)
{
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
if(lcdltdc.dir) //横屏
{
return *(unsigned int*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x));
}else //竖屏
{
return *(unsigned int*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y));
}
#else
if(lcdltdc.dir) //横屏
{
return *(unsigned short*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*y+x));
}else //竖屏
{
return *(unsigned short*)((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y));
}
#endif
}
//LTDC填充矩形,DMA2D填充
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!
//color:要填充的颜色
void LTDC_Fill(unsigned short sx,unsigned short sy,unsigned short ex,unsigned short ey,unsigned int color)
{
unsigned int psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化
unsigned int timeout=0;
unsigned short offline;
unsigned int 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=((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx));
RCC->AHB1ENR|=1<<23; //使能DM2D时钟
DMA2D->CR=3<<16; //寄存器到存储器模式
DMA2D->OPFCCR=LCD_PIXFORMAT; //设置颜色格式
DMA2D->OOR=offline; //设置行偏移
DMA2D->CR&=~(1<<0); //先停止DMA2D
DMA2D->OMAR=addr; //输出存储器地址
DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16); //设定行数寄存器
DMA2D->OCOLR=color; //设定输出颜色寄存器
DMA2D->CR|=1<<0; //启动DMA2D
while((DMA2D->ISR&(1<<1))==0) //等待传输完成
{
timeout++;
if(timeout>0X1FFFFF)break; //超时退出
}
DMA2D->IFCR|=1<<1; //清除传输完成标志
}
//在指定区域内填充单个颜色
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
//void LTDC_Fill(unsigned short sx,unsigned short sy,unsigned short ex,unsigned short ey,unsigned int color)
//{
// unsigned int psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化
// unsigned int timeout=0;
// unsigned short offline;
// unsigned int 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=((unsigned int)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的模式
// DMA2D_Handler.Instance=DMA2D;
// DMA2D_Handler.Init.Mode=DMA2D_R2M; //内存到存储器模式
// DMA2D_Handler.Init.ColorMode=LCD_PIXFORMAT; //设置颜色格式
// DMA2D_Handler.Init.OutputOffset=offline; //输出偏移
// HAL_DMA2D_Init(&DMA2D_Handler); //初始化DMA2D
// HAL_DMA2D_ConfigLayer(&DMA2D_Handler,lcdltdc.activelayer); //层配置
// HAL_DMA2D_Start(&DMA2D_Handler,color,(unsigned int)addr,pex-psx+1,pey-psy+1);//开启传输
// HAL_DMA2D_PollForTransfer(&DMA2D_Handler,1000);//传输数据
// while((__HAL_DMA2D_GET_FLAG(&DMA2D_Handler,DMA2D_FLAG_TC)==0)&&(timeout<0X5000))//等待DMA2D完成
// {
// timeout++;
// }
// __HAL_DMA2D_CLEAR_FLAG(&DMA2D_Handler,DMA2D_FLAG_TC); //清除传输完成标志
//}
//在指定区域内填充指定颜色块,DMA2D填充
//此函数仅支持unsigned short,RGB565格式的颜色数组填充.
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!
//color:要填充的颜色数组首地址
void LTDC_Color_Fill(unsigned short sx,unsigned short sy,unsigned short ex,unsigned short ey,unsigned short *color)
{
unsigned int psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化
unsigned int timeout=0;
unsigned short offline;
unsigned int 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=((unsigned int)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx));
RCC->AHB1ENR|=1<<23; //使能DM2D时钟
DMA2D->CR=0<<16; //存储器到存储器模式
DMA2D->FGPFCCR=LCD_PIXFORMAT; //设置颜色格式
DMA2D->FGOR=0; //前景层行偏移为0
DMA2D->OOR=offline; //设置行偏移
DMA2D->CR&=~(1<<0); //先停止DMA2D
DMA2D->FGMAR=(unsigned int)color; //源地址
DMA2D->OMAR=addr; //输出存储器地址
DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16); //设定行数寄存器
DMA2D->CR|=1<<0; //启动DMA2D
while((DMA2D->ISR&(1<<1))==0) //等待传输完成
{
timeout++;
if(timeout>0X1FFFFF)break; //超时退出
}
DMA2D->IFCR|=1<<1; //清除传输完成标志
}
//LCD清屏
//color:颜色值
void LTDC_Clear(unsigned int color)
{
LTDC_Fill(0,0,lcdltdc.width-1,lcdltdc.height-1,color);
}
//LTDC时钟(Fdclk)设置函数
//PLL3_VCO Input=HSE_VALUE/PLL3M
//PLL3_VCO Output=PLL3_VCO Input * PLL3N
//PLLLCDCLK = PLL3_VCO Output/PLL3R
//假如HSE_VALUE=25MHzPLL3M=5PLL3N=160,PLL3R=88
//LTDCLK=PLLLCDCLK=25/5*160/88=9MHz
//返回值:0,成功;1,失败。
unsigned char LTDC_Clk_Set(unsigned int pll3m,unsigned int pll3n,unsigned int pll3r)
{
RCC_PeriphCLKInitTypeDef PeriphClkIniture;
PeriphClkIniture.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
PeriphClkIniture.PLL3.PLL3M = pll3m;
PeriphClkIniture.PLL3.PLL3N = pll3n;
PeriphClkIniture.PLL3.PLL3P = 2;
PeriphClkIniture.PLL3.PLL3Q = 2;
PeriphClkIniture.PLL3.PLL3R = pll3r;
if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkIniture)==HAL_OK) //配置像素时钟这里配置为时钟为18.75MHZ
{
return 0; //成功
}
else return 1; //失败
}
//LTDC,层颜窗口设置,窗口以LCD面板坐标系为基准
//注意:此函数必须在LTDC_Layer_Parameter_Config之后再设置.
//layerx:层值,0/1.
//sx,sy:起始坐标
//width,height:宽度和高度
void LTDC_Layer_Window_Config(unsigned char layerx,unsigned short sx,unsigned short sy,unsigned short width,unsigned short height)
{
HAL_LTDC_SetWindowPosition(&LTDC_Handler,sx,sy,layerx); //设置窗口的位置
HAL_LTDC_SetWindowSize(&LTDC_Handler,width,height,layerx);//设置窗口大小
}
//LTDC,基本参数设置.
//注意:此函数,必须在LTDC_Layer_Window_Config之前设置.
//layerx:层值,0/1.
//bufaddr:层颜色帧缓存起始地址
//pixformat:颜色格式.0,ARGB8888;1,RGB888;2,RGB565;3,ARGB1555;4,ARGB4444;5,L8;6;AL44;7;AL88
//alpha:层颜色Alpha值,0,全透明;255,不透明
//alpha0:默认颜色Alpha值,0,全透明;255,不透明
//bfac1:混合系数1,4(100),恒定的Alpha;6(101),像素Alpha*恒定Alpha
//bfac2:混合系数2,5(101),恒定的Alpha;7(111),像素Alpha*恒定Alpha
//bkcolor:层默认颜色,32位,低24位有效,RGB888格式
//返回值:无
void LTDC_Layer_Parameter_Config(unsigned char layerx,unsigned int bufaddr,unsigned char pixformat,unsigned char alpha,unsigned char alpha0,unsigned char bfac1,unsigned char bfac2,unsigned int 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=(unsigned int)bfac1<<8; //设置层混合系数
pLayerCfg.BlendingFactor2=(unsigned int)bfac2<<8; //设置层混合系数
pLayerCfg.FBStartAdress=bufaddr; //设置层颜色帧缓存起始地址
pLayerCfg.ImageWidth=lcdltdc.pwidth; //设置颜色帧缓冲区的宽度
pLayerCfg.ImageHeight=lcdltdc.pheight; //设置颜色帧缓冲区的高度
pLayerCfg.Backcolor.Red=(unsigned char)(bkcolor&0X00FF0000)>>16; //背景颜色红色部分
pLayerCfg.Backcolor.Green=(unsigned char)(bkcolor&0X0000FF00)>>8; //背景颜色绿色部分
pLayerCfg.Backcolor.Blue=(unsigned char)bkcolor&0X000000FF; //背景颜色蓝色部分
HAL_LTDC_ConfigLayer(&LTDC_Handler,&pLayerCfg,layerx); //设置所选中的层
}
//LCD初始化函数
void LTDC_Init(void)
{
unsigned short lcdid=0;
lcdltdc.pwidth=800; //面板宽度,单位:像素
lcdltdc.pheight=480; //面板高度,单位:像素
lcdltdc.hsw=1; //水平同步宽度
lcdltdc.hbp=46; //水平后廊
lcdltdc.hfp=210; //水平前廊
lcdltdc.vsw=1; //垂直同步宽度
lcdltdc.vbp=23; //垂直后廊
lcdltdc.vfp=22; //垂直前廊
//LTDC_Clk_Set(5,160,47);//设置像素时钟 17Mhz
lcddev.width=lcdltdc.pwidth;
lcddev.height=lcdltdc.pheight;
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
ltdc_framebuf[0]=(unsigned int*)&ltdc_lcd_framebuf;
ltdc_framebuf[1]=(unsigned int*)&ltdc_lcd_framebuf_2;
lcdltdc.pixsize=4; //每个像素占4个字节
#else
lcdltdc.pixsize=2; //每个像素占2个字节
ltdc_framebuf[0]=(unsigned int*)&ltdc_lcd_framebuf;
#endif
/*
//LTDC配置
LTDC_Handler.Instance=LTDC;
LTDC_Handler.Init.HSPolarity=LTDC_HSPOLARITY_AL; //水平同步极性
LTDC_Handler.Init.VSPolarity=LTDC_VSPOLARITY_AL; //垂直同步极性
LTDC_Handler.Init.DEPolarity=LTDC_DEPOLARITY_AL; //数据使能极性
LTDC_Handler.Init.PCPolarity=LTDC_PCPOLARITY_IPC; //像素时钟极性
//if(lcdid==0X1018)LTDC_Handler.Init.PCPolarity=LTDC_PCPOLARITY_IIPC; //像素时钟极性相反
LTDC_Handler.Init.HorizontalSync=lcdltdc.hsw-1; //水平同步宽度
LTDC_Handler.Init.VerticalSync=lcdltdc.vsw-1; //垂直同步宽度
LTDC_Handler.Init.AccumulatedHBP=lcdltdc.hsw+lcdltdc.hbp-1; //水平同步后沿宽度
LTDC_Handler.Init.AccumulatedVBP=lcdltdc.vsw+lcdltdc.vbp-1; //垂直同步后沿高度
LTDC_Handler.Init.AccumulatedActiveW=lcdltdc.hsw+lcdltdc.hbp+lcdltdc.pwidth-1;//有效宽度
LTDC_Handler.Init.AccumulatedActiveH=lcdltdc.vsw+lcdltdc.vbp+lcdltdc.pheight-1;//有效高度
LTDC_Handler.Init.TotalWidth=lcdltdc.hsw+lcdltdc.hbp+lcdltdc.pwidth+lcdltdc.hfp-1; //总宽度
LTDC_Handler.Init.TotalHeigh=lcdltdc.vsw+lcdltdc.vbp+lcdltdc.pheight+lcdltdc.vfp-1; //总高度
LTDC_Handler.Init.Backcolor.Red=0; //屏幕背景层红色部分
LTDC_Handler.Init.Backcolor.Green=0; //屏幕背景层绿色部分
LTDC_Handler.Init.Backcolor.Blue=0; //屏幕背景色蓝色部分
HAL_LTDC_Init(&LTDC_Handler);
//层配置
//LTDC_Layer_Parameter_Config(0,(unsigned int)ltdc_framebuf[0],LCD_PIXFORMAT,255,0,6,7,0X000000);//层参数配置
//LTDC_Layer_Window_Config(0,0,0,lcdltdc.pwidth,lcdltdc.pheight); //层窗口配置,以LCD面板坐标系为基准,不要随便修改!
*/
LTDC_Display_Dir(1); //默认横屏
LTDC_Select_Layer(1); //选择第2层
LTDC_Clear(0); //清屏
LTDC_Select_Layer(0); //选择第1层
LTDC_Clear(0XFF000000); //清屏
}