|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2006-2021, RT-Thread Development Team
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*
|
|
|
|
|
* Change Logs:
|
|
|
|
|
* Date Author Notes
|
|
|
|
|
* 2025-12-06 Administrator the first version
|
|
|
|
|
* 2025-12-07 Qwen 使用 RT-Thread 内存池替代静态帧缓存,修复 rt_mp_create 参数错误
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* ltdc.c */
|
|
|
|
|
#include "ltdc.h"
|
|
|
|
|
#include "lcd.h"
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <rtthread.h>
|
|
|
|
|
#include <rthw.h>
|
|
|
|
|
|
|
|
|
|
LTDC_HandleTypeDef g_ltdc_handle; /* LTDC句柄 */
|
|
|
|
|
DMA2D_HandleTypeDef g_dma2d_handle; /* DMA2D句柄 */
|
|
|
|
|
|
|
|
|
|
#define MAX_FRAME_WIDTH 1024
|
|
|
|
|
#define MAX_FRAME_HEIGHT 600
|
|
|
|
|
|
|
|
|
|
#if LTDC_PIXFORMAT == LTDC_PIXFORMAT_ARGB8888 || LTDC_PIXFORMAT == LTDC_PIXFORMAT_RGB888
|
|
|
|
|
#define BYTES_PER_PIXEL 4
|
|
|
|
|
#else
|
|
|
|
|
#define BYTES_PER_PIXEL 2
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define FRAME_BUF_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * BYTES_PER_PIXEL)
|
|
|
|
|
#define FRAME_ALIGN 64 /* LTDC 推荐对齐到 64 字节 */
|
|
|
|
|
|
|
|
|
|
static rt_mp_t frame_mp = RT_NULL;
|
|
|
|
|
uint32_t *g_ltdc_framebuf[2] = {RT_NULL, RT_NULL}; /* 初始化为 NULL */
|
|
|
|
|
_ltdc_dev lcdltdc; /* 管理LCD LTDC的重要参数 */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 从内存池分配对齐内存
|
|
|
|
|
*/
|
|
|
|
|
static void* alloc_aligned_frame_buffer(rt_mp_t mp, rt_size_t size, rt_size_t align)
|
|
|
|
|
{
|
|
|
|
|
void *raw, *aligned;
|
|
|
|
|
rt_size_t offset;
|
|
|
|
|
|
|
|
|
|
/* 分配足够空间以保证能对齐 */
|
|
|
|
|
raw = rt_mp_alloc(mp, size + align - 1);
|
|
|
|
|
if (raw == RT_NULL) return RT_NULL;
|
|
|
|
|
|
|
|
|
|
offset = (rt_ubase_t)raw & (align - 1);
|
|
|
|
|
if (offset != 0)
|
|
|
|
|
aligned = (void *)((rt_ubase_t)raw + align - offset);
|
|
|
|
|
else
|
|
|
|
|
aligned = raw;
|
|
|
|
|
|
|
|
|
|
return aligned;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC开关
|
|
|
|
|
* @param sw : 1,打开; 0,关闭;
|
|
|
|
|
* @retval 无
|
|
|
|
|
*/
|
|
|
|
|
void ltdc_switch(uint8_t sw)
|
|
|
|
|
{
|
|
|
|
|
if (sw)
|
|
|
|
|
{
|
|
|
|
|
__HAL_LTDC_ENABLE(&g_ltdc_handle); /* 打开LTDC */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
__HAL_LTDC_DISABLE(&g_ltdc_handle); /* 关闭LTDC */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC开关指定层
|
|
|
|
|
* @param layerx : 0,第一层; 1,第二层;
|
|
|
|
|
* @param sw : 1,打开; 0,关闭;
|
|
|
|
|
* @retval 无
|
|
|
|
|
*/
|
|
|
|
|
void ltdc_layer_switch(uint8_t layerx, uint8_t sw)
|
|
|
|
|
{
|
|
|
|
|
if (sw)
|
|
|
|
|
{
|
|
|
|
|
__HAL_LTDC_LAYER_ENABLE(&g_ltdc_handle, layerx); /* 开启layerx */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
__HAL_LTDC_LAYER_DISABLE(&g_ltdc_handle, layerx); /* 关闭layerx */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__HAL_LTDC_RELOAD_CONFIG(&g_ltdc_handle); /* 立即重新加载配置 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC选择层
|
|
|
|
|
* @param layerx : 层号:0,第一层; 1,第二层;
|
|
|
|
|
* @retval 无
|
|
|
|
|
*/
|
|
|
|
|
void ltdc_select_layer(uint8_t layerx)
|
|
|
|
|
{
|
|
|
|
|
lcdltdc.activelayer = layerx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC显示方向设置
|
|
|
|
|
* @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 LTDC画点函数
|
|
|
|
|
* @param x,y : 写入坐标
|
|
|
|
|
* @param color : 颜色值
|
|
|
|
|
* @retval 无
|
|
|
|
|
*/
|
|
|
|
|
void ltdc_draw_point(uint16_t x, uint16_t y, uint32_t color)
|
|
|
|
|
{
|
|
|
|
|
#if LTDC_PIXFORMAT == LTDC_PIXFORMAT_ARGB8888
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.dir) /* 横屏 */
|
|
|
|
|
{
|
|
|
|
|
*(uint32_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) = color;
|
|
|
|
|
}
|
|
|
|
|
else /* 竖屏 */
|
|
|
|
|
{
|
|
|
|
|
*(uint32_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)) = color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#elif LTDC_PIXFORMAT == LTDC_PIXFORMAT_RGB888
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.dir) /* 横屏 */
|
|
|
|
|
{
|
|
|
|
|
*(uint16_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) = color;
|
|
|
|
|
*(uint8_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x) + 2) = color >> 16;
|
|
|
|
|
}
|
|
|
|
|
else /* 竖屏 */
|
|
|
|
|
{
|
|
|
|
|
*(uint16_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)) = color;
|
|
|
|
|
*(uint8_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y) + 2) = color >> 16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.dir) /* 横屏 */
|
|
|
|
|
{
|
|
|
|
|
*(uint16_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) = color;
|
|
|
|
|
}
|
|
|
|
|
else /* 竖屏 */
|
|
|
|
|
{
|
|
|
|
|
*(uint16_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)) = color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC读点函数
|
|
|
|
|
* @param x,y : 读取点的坐标
|
|
|
|
|
* @retval 颜色值
|
|
|
|
|
*/
|
|
|
|
|
uint32_t ltdc_read_point(uint16_t x, uint16_t y)
|
|
|
|
|
{
|
|
|
|
|
#if LTDC_PIXFORMAT == LTDC_PIXFORMAT_ARGB8888
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.dir) /* 横屏 */
|
|
|
|
|
{
|
|
|
|
|
return *(uint32_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x));
|
|
|
|
|
}
|
|
|
|
|
else /* 竖屏 */
|
|
|
|
|
{
|
|
|
|
|
return *(uint32_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#elif LTDC_PIXFORMAT == LTDC_PIXFORMAT_RGB888
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.dir) /* 横屏 */
|
|
|
|
|
{
|
|
|
|
|
return *(uint32_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x)) & 0XFFFFFF;
|
|
|
|
|
}
|
|
|
|
|
else /* 竖屏 */
|
|
|
|
|
{
|
|
|
|
|
return *(uint32_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y)) & 0XFFFFFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.dir) /* 横屏 */
|
|
|
|
|
{
|
|
|
|
|
return *(uint16_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * y + x));
|
|
|
|
|
}
|
|
|
|
|
else /* 竖屏 */
|
|
|
|
|
{
|
|
|
|
|
return *(uint16_t *)((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * (lcdltdc.pheight - x - 1) + y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC填充矩形, DMA2D填充
|
|
|
|
|
*/
|
|
|
|
|
void ltdc_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
|
|
|
|
|
{
|
|
|
|
|
uint32_t psx, psy, pex, pey;
|
|
|
|
|
uint32_t timeout = 0;
|
|
|
|
|
uint16_t offline;
|
|
|
|
|
uint32_t addr;
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.dir)
|
|
|
|
|
{
|
|
|
|
|
psx = sx;
|
|
|
|
|
psy = sy;
|
|
|
|
|
pex = ex;
|
|
|
|
|
pey = ey;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (ex >= lcdltdc.pheight)
|
|
|
|
|
{
|
|
|
|
|
ex = lcdltdc.pheight - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sx >= lcdltdc.pheight)
|
|
|
|
|
{
|
|
|
|
|
sx = lcdltdc.pheight - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
psx = sy;
|
|
|
|
|
psy = lcdltdc.pheight - ex - 1;
|
|
|
|
|
pex = ey;
|
|
|
|
|
pey = lcdltdc.pheight - sx - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offline = lcdltdc.pwidth - (pex - psx + 1);
|
|
|
|
|
addr = ((uint32_t)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * psy + psx));
|
|
|
|
|
|
|
|
|
|
__HAL_RCC_DMA2D_CLK_ENABLE();
|
|
|
|
|
|
|
|
|
|
DMA2D->CR &= ~(DMA2D_CR_START);
|
|
|
|
|
DMA2D->CR = DMA2D_R2M;
|
|
|
|
|
DMA2D->OPFCCR = LTDC_PIXFORMAT;
|
|
|
|
|
DMA2D->OOR = offline;
|
|
|
|
|
|
|
|
|
|
DMA2D->OMAR = addr;
|
|
|
|
|
DMA2D->NLR = (pey - psy + 1) | ((pex - psx + 1) << 16);
|
|
|
|
|
DMA2D->OCOLR = color;
|
|
|
|
|
DMA2D->CR |= DMA2D_CR_START;
|
|
|
|
|
|
|
|
|
|
while ((DMA2D->ISR & (DMA2D_FLAG_TC)) == 0)
|
|
|
|
|
{
|
|
|
|
|
timeout++;
|
|
|
|
|
if (timeout > 0X1FFFFF) break;
|
|
|
|
|
}
|
|
|
|
|
DMA2D->IFCR |= DMA2D_FLAG_TC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 在指定区域内填充指定颜色块, DMA2D填充
|
|
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
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)g_ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize * (lcdltdc.pwidth * psy + psx));
|
|
|
|
|
|
|
|
|
|
__HAL_RCC_DMA2D_CLK_ENABLE();
|
|
|
|
|
|
|
|
|
|
DMA2D->CR &= ~(DMA2D_CR_START);
|
|
|
|
|
DMA2D->CR = DMA2D_M2M;
|
|
|
|
|
DMA2D->FGPFCCR = LTDC_PIXFORMAT;
|
|
|
|
|
DMA2D->FGOR = 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;
|
|
|
|
|
|
|
|
|
|
while((DMA2D->ISR & (DMA2D_FLAG_TC)) == 0)
|
|
|
|
|
{
|
|
|
|
|
timeout++;
|
|
|
|
|
if (timeout > 0X1FFFFF) break;
|
|
|
|
|
}
|
|
|
|
|
DMA2D->IFCR |= DMA2D_FLAG_TC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC清屏
|
|
|
|
|
*/
|
|
|
|
|
void ltdc_clear(uint32_t color)
|
|
|
|
|
{
|
|
|
|
|
ltdc_fill(0, 0, lcdltdc.width - 1, lcdltdc.height - 1, color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC时钟(Fdclk)设置函数
|
|
|
|
|
*/
|
|
|
|
|
uint8_t ltdc_clk_set(uint32_t pll3n, uint32_t pll3m, uint32_t pll3r)
|
|
|
|
|
{
|
|
|
|
|
RCC_PeriphCLKInitTypeDef periphclk_initure;
|
|
|
|
|
periphclk_initure.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
|
|
|
|
|
periphclk_initure.PLL3.PLL3M = pll3m;
|
|
|
|
|
periphclk_initure.PLL3.PLL3N = pll3n;
|
|
|
|
|
periphclk_initure.PLL3.PLL3P = 2;
|
|
|
|
|
periphclk_initure.PLL3.PLL3Q = 2;
|
|
|
|
|
periphclk_initure.PLL3.PLL3R = pll3r;
|
|
|
|
|
|
|
|
|
|
if (HAL_RCCEx_PeriphCLKConfig(&periphclk_initure) == HAL_OK)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC层窗口设置
|
|
|
|
|
*/
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (lcdltdc.pheight == 1280 && layerx == 0)
|
|
|
|
|
{
|
|
|
|
|
LTDC_Layer1->CFBLR = (width * 4 << 16) | (width * 4 + 7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 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_LayerCfgTypeDef playercfg;
|
|
|
|
|
playercfg.WindowX0 = 0;
|
|
|
|
|
playercfg.WindowY0 = 0;
|
|
|
|
|
playercfg.WindowX1 = lcdltdc.pwidth;
|
|
|
|
|
playercfg.WindowY1 = lcdltdc.pheight;
|
|
|
|
|
playercfg.PixelFormat = pixformat;
|
|
|
|
|
playercfg.Alpha = alpha;
|
|
|
|
|
playercfg.Alpha0 = alpha0;
|
|
|
|
|
playercfg.BlendingFactor1 = (uint32_t)bfac1 << 8;
|
|
|
|
|
playercfg.BlendingFactor2 = (uint32_t)bfac2;
|
|
|
|
|
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 LTDC读取面板ID
|
|
|
|
|
*/
|
|
|
|
|
uint16_t ltdc_panelid_read(void)
|
|
|
|
|
{
|
|
|
|
|
uint8_t idx = 0;
|
|
|
|
|
GPIO_InitTypeDef gpio_init_struct;
|
|
|
|
|
|
|
|
|
|
__HAL_RCC_GPIOG_CLK_ENABLE();
|
|
|
|
|
__HAL_RCC_GPIOI_CLK_ENABLE();
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = GPIO_PIN_6;
|
|
|
|
|
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;
|
|
|
|
|
HAL_GPIO_Init(GPIOI, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
rt_thread_mdelay(1);
|
|
|
|
|
idx = (uint8_t)HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_6);
|
|
|
|
|
idx |= (uint8_t)HAL_GPIO_ReadPin(GPIOI, GPIO_PIN_2) << 1;
|
|
|
|
|
idx |= (uint8_t)HAL_GPIO_ReadPin(GPIOI, GPIO_PIN_7) << 2;
|
|
|
|
|
|
|
|
|
|
switch (idx)
|
|
|
|
|
{
|
|
|
|
|
case 0: return 0X4342;
|
|
|
|
|
case 1: return 0X7084;
|
|
|
|
|
case 2: return 0X7016;
|
|
|
|
|
case 3: return 0X7018;
|
|
|
|
|
case 4: return 0X4384;
|
|
|
|
|
case 5: return 0X1018;
|
|
|
|
|
default: return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC初始化函数
|
|
|
|
|
*/
|
|
|
|
|
int ltdc_init(void)
|
|
|
|
|
{
|
|
|
|
|
uint16_t lcdid = 0;
|
|
|
|
|
|
|
|
|
|
lcdid = ltdc_panelid_read();
|
|
|
|
|
|
|
|
|
|
#if RGB_80_8001280
|
|
|
|
|
lcdid = 0X8081;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (lcdid == 0X4342)
|
|
|
|
|
{
|
|
|
|
|
lcdltdc.pwidth = 480; lcdltdc.pheight = 272;
|
|
|
|
|
lcdltdc.hsw = 1; lcdltdc.hbp = 40; lcdltdc.hfp = 5;
|
|
|
|
|
lcdltdc.vsw = 1; lcdltdc.vbp = 8; lcdltdc.vfp = 8;
|
|
|
|
|
ltdc_clk_set(300, 25, 33);
|
|
|
|
|
}
|
|
|
|
|
else if (lcdid == 0X7084)
|
|
|
|
|
{
|
|
|
|
|
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(300, 25, 9);
|
|
|
|
|
}
|
|
|
|
|
else if (lcdid == 0X7016)
|
|
|
|
|
{
|
|
|
|
|
lcdltdc.pwidth = 1024; lcdltdc.pheight = 600;
|
|
|
|
|
lcdltdc.hsw = 20; lcdltdc.hbp = 160; lcdltdc.hfp = 160;
|
|
|
|
|
lcdltdc.vsw = 3; lcdltdc.vbp = 23; lcdltdc.vfp = 12;
|
|
|
|
|
ltdc_clk_set(128, 32, 8);
|
|
|
|
|
}
|
|
|
|
|
else if (lcdid == 0X7018)
|
|
|
|
|
{
|
|
|
|
|
lcdltdc.pwidth = 1280; lcdltdc.pheight = 800;
|
|
|
|
|
}
|
|
|
|
|
else if (lcdid == 0X4384)
|
|
|
|
|
{
|
|
|
|
|
lcdltdc.pwidth = 800; lcdltdc.pheight = 480;
|
|
|
|
|
lcdltdc.hsw = 48; lcdltdc.hbp = 88; lcdltdc.hfp = 40;
|
|
|
|
|
lcdltdc.vsw = 3; lcdltdc.vbp = 32; lcdltdc.vfp = 13;
|
|
|
|
|
ltdc_clk_set(300, 25, 9);
|
|
|
|
|
}
|
|
|
|
|
else if (lcdid == 0X8081)
|
|
|
|
|
{
|
|
|
|
|
lcdltdc.pwidth = 800; lcdltdc.pheight = 1280;
|
|
|
|
|
lcdltdc.hsw = 5; lcdltdc.hbp = 20; lcdltdc.hfp = 40;
|
|
|
|
|
lcdltdc.vsw = 3; lcdltdc.vbp = 20; lcdltdc.vfp = 30;
|
|
|
|
|
ltdc_clk_set(300, 25, 5);
|
|
|
|
|
}
|
|
|
|
|
else if (lcdid == 0X1018)
|
|
|
|
|
{
|
|
|
|
|
lcdltdc.pwidth = 1280; lcdltdc.pheight = 800;
|
|
|
|
|
lcdltdc.hsw = 10; lcdltdc.hbp = 140; lcdltdc.hfp = 10;
|
|
|
|
|
lcdltdc.vsw = 3; lcdltdc.vbp = 10; lcdltdc.vfp = 10;
|
|
|
|
|
ltdc_clk_set(300, 25, 5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lcddev.width = lcdltdc.pwidth;
|
|
|
|
|
lcddev.height = lcdltdc.pheight;
|
|
|
|
|
lcdltdc.pixformat = LTDC_PIXFORMAT;
|
|
|
|
|
lcdltdc.pixsize = BYTES_PER_PIXEL;
|
|
|
|
|
|
|
|
|
|
/* === 使用 RT-Thread 内存池分配帧缓存(双缓冲)=== */
|
|
|
|
|
if (frame_mp == RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_size_t block_size = FRAME_BUF_SIZE + FRAME_ALIGN; /* 每块大小 */
|
|
|
|
|
rt_size_t block_count = 2; /* 双缓冲 */
|
|
|
|
|
|
|
|
|
|
frame_mp = rt_mp_create("ltdc_mp", block_count, block_size);
|
|
|
|
|
if (frame_mp == RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_kprintf("LTDC: Failed to create memory pool!\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
rt_kprintf("LTDC: Memory pool created (%d blocks, %d bytes each)\n",
|
|
|
|
|
block_count, block_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 分配帧缓冲 0 */
|
|
|
|
|
if (g_ltdc_framebuf[0] == RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
void *aligned_buf = alloc_aligned_frame_buffer(frame_mp, FRAME_BUF_SIZE, FRAME_ALIGN);
|
|
|
|
|
if (aligned_buf == RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_kprintf("LTDC: Failed to allocate frame buffer 0!\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
g_ltdc_framebuf[0] = (uint32_t *)aligned_buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 分配帧缓冲 1(双缓冲) */
|
|
|
|
|
if (g_ltdc_framebuf[1] == RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
void *aligned_buf = alloc_aligned_frame_buffer(frame_mp, FRAME_BUF_SIZE, FRAME_ALIGN);
|
|
|
|
|
if (aligned_buf == RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_kprintf("LTDC: Warning: only single buffer available.\n");
|
|
|
|
|
// 可选:回退到单缓冲
|
|
|
|
|
g_ltdc_framebuf[1] = g_ltdc_framebuf[0];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_ltdc_framebuf[1] = (uint32_t *)aligned_buf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* LTDC配置 */
|
|
|
|
|
g_ltdc_handle.Instance = LTDC;
|
|
|
|
|
|
|
|
|
|
if (lcdid == 0X8081)
|
|
|
|
|
{
|
|
|
|
|
g_ltdc_handle.Init.HSPolarity = LTDC_HSPOLARITY_AH;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
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.State = HAL_LTDC_STATE_RESET;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lcdid == 0X1018 || lcdid == 0X8081)
|
|
|
|
|
{
|
|
|
|
|
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 = 0;
|
|
|
|
|
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)g_ltdc_framebuf[0], LTDC_PIXFORMAT, 255, 0, 6, 7, 0X000000);
|
|
|
|
|
ltdc_layer_window_config(0, 0, 0, lcdltdc.pwidth, lcdltdc.pheight);
|
|
|
|
|
|
|
|
|
|
ltdc_select_layer(0);
|
|
|
|
|
|
|
|
|
|
/* LTDC LCD复位 */
|
|
|
|
|
LTDC_RST(1);
|
|
|
|
|
rt_thread_mdelay(10);
|
|
|
|
|
LTDC_RST(0);
|
|
|
|
|
rt_thread_mdelay(50);
|
|
|
|
|
LTDC_RST(1);
|
|
|
|
|
rt_thread_mdelay(200);
|
|
|
|
|
|
|
|
|
|
LTDC_BL(1);
|
|
|
|
|
ltdc_clear(0XFFFFFFFF);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief LTDC底层IO初始化和时钟使能
|
|
|
|
|
*/
|
|
|
|
|
void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc)
|
|
|
|
|
{
|
|
|
|
|
GPIO_InitTypeDef gpio_init_struct;
|
|
|
|
|
|
|
|
|
|
__HAL_RCC_LTDC_CLK_ENABLE();
|
|
|
|
|
__HAL_RCC_DMA2D_CLK_ENABLE();
|
|
|
|
|
|
|
|
|
|
/* 控制引脚 */
|
|
|
|
|
LTDC_BL_GPIO_CLK_ENABLE();
|
|
|
|
|
LTDC_RST_GPIO_CLK_ENABLE();
|
|
|
|
|
LTDC_DE_GPIO_CLK_ENABLE();
|
|
|
|
|
LTDC_VSYNC_GPIO_CLK_ENABLE();
|
|
|
|
|
LTDC_HSYNC_GPIO_CLK_ENABLE();
|
|
|
|
|
LTDC_CLK_GPIO_CLK_ENABLE();
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = LTDC_BL_GPIO_PIN;
|
|
|
|
|
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
|
|
|
|
|
gpio_init_struct.Pull = GPIO_PULLUP;
|
|
|
|
|
gpio_init_struct.Speed = GPIO_SPEED_HIGH;
|
|
|
|
|
HAL_GPIO_Init(LTDC_BL_GPIO_PORT, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = LTDC_RST_GPIO_PIN;
|
|
|
|
|
HAL_GPIO_Init(LTDC_RST_GPIO_PORT, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = LTDC_DE_GPIO_PIN;
|
|
|
|
|
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;
|
|
|
|
|
HAL_GPIO_Init(LTDC_DE_GPIO_PORT, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = LTDC_VSYNC_GPIO_PIN;
|
|
|
|
|
HAL_GPIO_Init(LTDC_VSYNC_GPIO_PORT, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = LTDC_HSYNC_GPIO_PIN;
|
|
|
|
|
HAL_GPIO_Init(LTDC_HSYNC_GPIO_PORT, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = LTDC_CLK_GPIO_PIN;
|
|
|
|
|
HAL_GPIO_Init(LTDC_CLK_GPIO_PORT, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
/* 数据引脚 */
|
|
|
|
|
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
|
|
|
__HAL_RCC_GPIOD_CLK_ENABLE();
|
|
|
|
|
__HAL_RCC_GPIOE_CLK_ENABLE();
|
|
|
|
|
__HAL_RCC_GPIOG_CLK_ENABLE();
|
|
|
|
|
__HAL_RCC_GPIOH_CLK_ENABLE();
|
|
|
|
|
__HAL_RCC_GPIOI_CLK_ENABLE();
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = GPIO_PIN_8;
|
|
|
|
|
gpio_init_struct.Alternate = GPIO_AF13_LTDC;
|
|
|
|
|
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = GPIO_PIN_2;
|
|
|
|
|
gpio_init_struct.Alternate = GPIO_AF14_LTDC;
|
|
|
|
|
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = GPIO_PIN_6;
|
|
|
|
|
HAL_GPIO_Init(GPIOD, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = GPIO_PIN_5 | GPIO_PIN_6;
|
|
|
|
|
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = GPIO_PIN_6 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14;
|
|
|
|
|
HAL_GPIO_Init(GPIOG, &gpio_init_struct);
|
|
|
|
|
|
|
|
|
|
gpio_init_struct.Pin = 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_struct);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
HAL_GPIO_Init(GPIOI, &gpio_init_struct);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
INIT_DEVICE_EXPORT(ltdc_init);
|