【兆易创新GD32H759I-EVAL开发板】图像处理加速器(IPA)的应用

时间:2024-03-16 11:42:05

GD32H7系列的IPA(Image Pixel Accelerator)是一个高效的图像处理硬件加速器,专门设计用于加速图像处理操作,如像素格式转换、图像旋转、缩放等。它的优势在于能够利用硬件加速来实现这些操作,相比于软件实现,可以大大提高处理速度,降低CPU的负担,从而提升整体系统性能。

主要特性

    一个访问存储器的AXI 主设备接口;
    一个支持8 位,16 位,32 位的IPA 配置的AHB 从设备接口;
    3 个4 双字深度的64 位FIFO 独立用于源图像和目标图像;
    支持四种像素格式转换模式:
    制某一源图像到目标图像中;
– 复制某一源图像到目标图像中并同时进行特定的颜色格式转换;
– 将两个不同的源图像进行混合,并将得到的结果进行特定的颜色格式转换;
– 用特定的颜色填充目标图像区域。
    支持两源图像独立配置LUT 大小;
    支持两种LUT 像素格式;
    支持LUT 自动加载;
    支持传输挂起或停止;
    对于源图像和目标图像,支持独立配置行偏移量;
    支持源图像和目标图像独立预定义的像素通道值;
    分别支持两源图像3 种alpha 通道值计算算法;
    对于前景层图像,支持16 种像素格式;
    对于背景层图像,支持11 种像素格式;
    对于目标图像,支持5 种像素格式;
    支持配置图像大小;
    支持AXI 总线带宽自动调节;
    支持一个带有六种事件标志位的中断;
    支持中断使能和清除;
    支持十进制缩放和双线性缩放;
    支持图像旋转(0、90、180、270 度);
    前景层图像支持隔行输入。 

IPA的优势

  1. 硬件加速:直接在硬件级别处理图像数据,加速图像处理任务,提供比软件实现更快的处理速度。
  2. 减轻CPU负担:CPU可以被释放出来处理其他任务,提高系统的多任务处理能力。
  3. 多种图像处理功能:支持像素格式转换、图像旋转、缩放等常见图像处理功能,满足多样化的应用需求。
  4. 支持直接内存访问(DMA):可与DMA配合使用,减少CPU介入,进一步提高数据处理效率。
  5. 灵活配置:提供丰富的配置选项,支持多种图像格式和处理方式,适用于多种不同的应用场景。

GD32H7系列IPA(Image Pixel Accelerator)功能拓展与实际应用

一、转换模式与支持格式

GD32H7的IPA提供灵活、可配置的图像处理功能,通过以下四种主要的转换模式,满足不同的应用需求:

  1. 直接复制:将源图像直接复制到目标图像区域。
  2. 复制并格式转换:在复制过程中,将源图像的格式转换为指定的目标图像格式。
  3. 图像混合并格式转换:将两个不同源图像混合后,再进行特定的颜色格式转换。
  4. 颜色填充:使用特定的颜色填充目标图像区域。

支持的像素格式包括前景层16种、背景层11种以及目标图像5种,覆盖从4位到32位的像素深度,为两个源图像分别提供了256x32的颜色查找表(LUT),增强了图像处理的灵活性和兼容性。

二、基本与高级功能
2.1 基本功能
  • 像素格式转换:转换图像数据的格式,例如从RGB565到RGB888,适用于不同的显示和存储需求。这项功能支撑了从相机的RAW格式到视频监控的YUV格式,乃至于医学影像的DICOM格式的广泛应用场景,确保了不同设备间的图像数据兼容性和显示一致性。

  • 图像旋转:支持90°、180°、270°的旋转操作,适用于摄像头安装方向调整、图像校正等场景,虽然固定角度的旋转在实际应用中使用频率不高,但在特定情况下非常有用。

  • 图像缩放:实现图像的放大或缩小,支持界面适配、图像预览等功能。

2.2 高级功能
  • 图像融合:通过不同的融合模式和透明度设置,实现两幅图像的融合操作,用于图像叠加、特效处理等。

  • 色彩空间转换:支持从RGB到YCbCr等复杂的色彩空间转换,这在多媒体播放器、数字摄影、视频会议和监控系统等多种场景中至关重要,确保了图像色彩的真实性和一致性。

  • 图像滤波:提供简单的图像滤波操作,如平滑和锐化,改善图像质量。

三、实际应用意义与场景
  • 设备与系统兼容性:通过不同格式之间的转换,增强了系统的兼容性,特别是在多种显示设备和图像源格式广泛存在的场景下。

  • 显示适配:将图像源数据转换为与特定显示设备兼容的格式,保证图像的正常显示。

  • 图像处理与分析:支持更多的图像处理算法和应用,通过格式转换实现特定的处理或分析需求,如实现特殊图像效果。

  • 存储空间优化:通过转换为更紧凑的格式,节省存储空间,这对于存储资源有限的应用场景尤为重要。

  • 性能优化:将图像数据转换为更适合当前硬件处理的格式,提高系统性能,尤其在处理器或图形加速器性能受限时显得尤为重要。

        GD32H7的IPA(Image Pixel Accelerator)通过其灵活的图像处理功能和丰富的像素格式支持,为各种应用场景提供了高效、可靠的图像处理解决方案。从基本的像素格式转换到高级的色彩空间转换和图像融合,IPA都能提供硬件级别的加速,显著提高处理效率,同时减轻CPU的负担。这使得IPA成为面向未来的高性能图像处理应用的理想选择。

IPA配置过程中的一些主要参数及其意义和作用:

1. 像素格式转换模式

  • 参数ipa_pixel_format_convert_mode_set()
  • 意义:定义了输入图像数据和输出图像数据的像素格式转换方式。
  • 作用:使得IPA能够将图像数据从一种格式转换为另一种格式,如从RGB转换为YUV,或反之,满足不同显示设备或图像处理要求的格式。

2. 目标像素格式

  • 参数destination_pf (在ipa_destination_parameter_struct结构体中)
  • 意义:指定了输出图像的像素格式。
  • 作用:确保图像数据转换后的格式符合输出设备(如LCD)的要求或其他处理流程的需求。

3. 目标内存基地址

  • 参数destination_memaddr (在ipa_destination_parameter_struct结构体中)
  • 意义:指定了转换后的图像数据存放的内存起始地址。
  • 作用:确保IPA处理后的图像数据被正确地存储在指定的内存位置,供后续显示或进一步处理。

4. 前景预定义的alpha值

  • 参数foreground_prealpha (在ipa_foreground_parameter_struct结构体中)
  • 意义:为整个前景图像设置统一的alpha透明度值。
  • 作用:用于在混合操作中调整前景图像的透明度,实现透明效果或图像覆盖。

5. Alpha计算模式

  • 参数foreground_alpha_algorithm (在ipa_foreground_parameter_struct结构体中)
  • 意义:定义了如何计算最终像素的alpha值。
  • 作用:支持多种alpha混合算法,可实现不同的图像透明度混合效果,如加权混合或透明度覆盖等。

6. 前景/背景像素格式

  • 参数foreground_pfbackground_pf
  • 意义:指定了前景和背景图像的像素格式。
  • 作用:确保图像混合或格式转换过程中,前景和背景数据的格式被正确解析和处理。

7. 图像大小和行偏移量

  • 参数image_height, image_width, foreground_lineoff
  • 意义:定义了待处理图像的尺寸和每行数据的偏移量。
  • 作用:控制图像处理的范围和数据排列,确保图像数据按照预期方式进行处理。

8. 交错模式

  • 参数foreground_interlace_mode
  • 意义:指定是否以交错方式处理图像数据。
  • 作用:适用于处理交错扫描的视频流,保持视频质量和连贯性。

在配置IPA参数时的一些基本概念
 

ipa_foreground_init()

前景预定义的alpha值 foreground_prealpha

  • 概念:Alpha值用于定义像素的透明度。前景预定义的alpha值是在图像融合或者alpha混合操作中,前景图像像素的默认透明度值。这个值会根据设置的alpha计算模式与前景像素的实际alpha值(如果存在)一起决定最终的像素透明度。

Alpha计算模式 foreground_alpha_algorithm

  • 概念:Alpha计算模式定义了如何根据前景图像的alpha值与预定义的alpha值来计算最终的像素透明度。GD32H7的IPA支持多种alpha混合算法,例如完全覆盖(前景完全替代背景)、混合(前景和背景根据各自的alpha值混合)等。
  • 模式示例
    • IPA_FG_ALPHA_MODE_0:可能指一种基本的混合模式,例如使用前景图像的alpha值直接替换。
    • IPA_FG_ALPHA_MODE_1IPA_FG_ALPHA_MODE_2:可能引入更复杂的混合策略,如加权混合,或支持预乘alpha值的混合等。

前景预定义的颜色值

  • 概念:前景预定义的红、绿、蓝色值允许用户为整个前景图像设置一个统一的颜色调整或色彩偏移。这在需要对前景图像进行颜色校正或特殊效果处理时非常有用。

交错模式 foreground_interlace_mode

  • 概念:交错模式通常用于处理交错视频信号,在这种模式下,图像的每一帧由两个字段组成,先传输奇数行(或偶数行),然后是偶数行(或奇数行)。启用交错模式意味着IPA会以特定的方式处理这种类型的图像数据,以保持视频播放的连贯性和质量。

前景偶帧/UV内存基地址 foreground_efuv_memaddr

  • 概念:在处理YUV格式或其他需要UV分量的图像数据时,foreground_efuv_memaddr指定了UV分量数据的内存起始地址。这在进行像素格式转换时尤其重要,确保Y分量和UV分量都能被正确地处理和映射。

背景Alpha计算模式 (ipa_background_init()  )

在图像处理中,alpha值决定了像素的透明度,其中alpha值的范围通常是0到255(0表示完全透明,255表示完全不透明)。背景alpha计算模式用于确定如何结合背景像素和前景像素的alpha值来计算最终像素的alpha值。

模式说明

  • IPA_BG_ALPHA_MODE_0:这种模式可能表示使用固定的背景alpha值,不考虑前景像素的alpha值。这意味着无论前景像素的透明度如何,背景像素的透明度都保持不变。

  • IPA_FG_ALPHA_MODE_1:这种模式可能表示完全使用前景的alpha值,忽略背景的预定义alpha值。这意味着最终的像素透明度将完全由前景像素的alpha值决定。

  • IPA_FG_ALPHA_MODE_2:这种模式可能表示前景和背景的alpha值都被考虑在内,通过某种算法(如加权平均)来计算最终像素的alpha值。这可以用于创建混合效果,其中前景和背景都部分透明,最终像素的透明度是前景和背景透明度的某种组合。

视觉效果

  1. 固定背景透明度(MODE_0):无论前景如何,背景保持其预定义的透明度。这适用于情况,比如在不干扰背景视觉效果的情况下,叠加前景信息。

  2. 完全前景透明度(MODE_1):背景的透明度被前景完全覆盖,只显示前景的透明效果。这适合前景需要完全展示,而背景只是作为装饰的场景。

  3. 混合透明度(MODE_2):前景和背景的透明度被结合,创建一个混合的透明效果。这适合需要将前景和背景融合在一起的复杂视觉效果,如阴影效果或透明图层叠加。

IPA 例子
 


#include "gd32h7xx.h"
#include "systick.h"
#include <stdio.h>
#include "gd32h759i_eval.h"
#include "image1.h"
#include "image2.h"
#include "image3.h"
#include "image4.h"
#include "image5.h"
#include "image6.h"
#include "image7.h"
#include "image8.h"
#include "image9.h"
#include "image10.h"
#include "image11.h"
#include "image12.h"

#if defined (__clang__)
#include "logo.h"
#elif defined ( __ICCARM__ )
#include "logo_iar.h"
#endif
// 定义显示设备的同步脉冲和有效显示区域参数

// 水平方向参数
#define HORIZONTAL_SYNCHRONOUS_PULSE  41 // 水平同步脉冲宽度
#define HORIZONTAL_BACK_PORCH         2 // 水平后 porch 宽度
#define ACTIVE_WIDTH                  480 // 水平有效显示宽度
#define HORIZONTAL_FRONT_PORCH        2 // 水平前 porch 宽度

// 垂直方向参数
#define VERTICAL_SYNCHRONOUS_PULSE    10 // 垂直同步脉冲高度
#define VERTICAL_BACK_PORCH           2 // 垂直后 porch 高度
#define ACTIVE_HEIGHT                 272 // 垂直有效显示高度
#define VERTICAL_FRONT_PORCH          2 // 垂直前 porch 高度

__ALIGNED(16) uint8_t blended_address_buffer[58292];

static void ipa_config(uint32_t baseaddress);
static void tli_config(void);
static void tli_blend_config(void);
static void tli_gpio_config(void);
static void lcd_config(void);
static void cache_enable(void);

/*!
    \brief      main program
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
    /* enable the CPU cache */
    cache_enable();
    /* configure the SysTick, TLI */
    systick_config();
    lcd_config();

    /* enable TLI layers */
    tli_layer_enable(LAYER0);
    tli_layer_enable(LAYER1);
    tli_reload_config(TLI_FRAME_BLANK_RELOAD_EN);
    /* enable TLI */
    tli_enable();

    /* configure TLI and display blend image */

    tli_blend_config();
    tli_reload_config(TLI_REQUEST_RELOAD_EN);

    while(1) {
        /* IPA configuration and display the images one by one */
        ipa_config((uint32_t)&gImage_image1);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image2);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image3);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image4);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image5);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image6);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image7);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image8);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image9);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image10);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image11);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
        ipa_config((uint32_t)&gImage_image12);
        ipa_transfer_enable();
        while(RESET == ipa_interrupt_flag_get(IPA_INT_FLAG_FTF));
        delay_1ms(50);
    }
}

/*!
    \brief      LCD Configuration
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void lcd_config(void)
{
    /* configure the GPIO of TLI */
    tli_gpio_config();
    /* configure TLI peripheral */
    tli_config();
}
/*! 
    \brief      配置TLI外设
    \param[in]  无
    \param[out] 无
    \retval     无
*/
static void tli_config(void)
{
    tli_parameter_struct               tli_init_struct; // TLI配置结构体
    tli_layer_parameter_struct         tli_layer_init_struct; // TLI层配置结构体

    // 启用TLI外设的时钟
    rcu_periph_clock_enable(RCU_TLI);
    tli_gpio_config(); // 配置TLI相关的GPIO

    // 配置PLL2时钟:CK_PLL2P/CK_PLL2Q/CK_PLL2R = HXTAL_VALUE / 25 * 150 / 3
    rcu_pll_input_output_clock_range_config(IDX_PLL2, RCU_PLL2RNG_1M_2M, RCU_PLL2VCO_150M_420M);
    if(ERROR == rcu_pll2_config(25, 150, 3, 3, 3)) {
        while(1) {
        }
    }

    // 启用PLL2R时钟输出并配置TLI时钟分频
    rcu_pll_clock_output_enable(RCU_PLL2R);
    rcu_tli_clock_div_config(RCU_PLL2R_DIV8);

    // 启用PLL2时钟
    rcu_osci_on(RCU_PLL2_CK);

    // 等待PLL2时钟稳定
    if(ERROR == rcu_osci_stab_wait(RCU_PLL2_CK)) {
        while(1) {
        }
    }

    /* 配置TLI参数结构体 */
    // 配置水平同步、垂直同步、DE及像素时钟极性
    tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI;

    // 配置LCD显示时序参数
    tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1;
    tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1;
    tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1;
    tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1;
    tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH
                                    - 1;
    tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 
                                    1;
    tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH +
                                   HORIZONTAL_FRONT_PORCH - 1;
    tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT +
                                   VERTICAL_FRONT_PORCH - 1;
    // 配置LCD背景RGB颜色
    tli_init_struct.backcolor_red = 0xFF;
    tli_init_struct.backcolor_green = 0xFF;
    tli_init_struct.backcolor_blue = 0xFF;
    tli_init(&tli_init_struct); // 初始化TLI

   /*
 * TLI(Transport Layer Interface)层配置
 * 此代码段初始化了一个TLI层的配置结构体,并设置了TLI层0的参数。
 * 配置包括窗口的位置、颜色格式、透明度、帧缓冲区地址等。
 */

    /* 设置TLI层窗口的左边界位置 */
    tli_layer_init_struct.layer_window_leftpos = 80 + HORIZONTAL_SYNCHRONOUS_PULSE +
                                                 HORIZONTAL_BACK_PORCH + 2;
    /* 设置TLI层窗口的右边界位置 */
    tli_layer_init_struct.layer_window_rightpos = (80 + 320 + HORIZONTAL_SYNCHRONOUS_PULSE +
                                                   HORIZONTAL_BACK_PORCH - 1);
    /* 设置TLI层窗口的上边界位置 */
    tli_layer_init_struct.layer_window_toppos = 150 + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH;
    /* 设置TLI层窗口的下边界位置 */
    tli_layer_init_struct.layer_window_bottompos = (150 + 100 + VERTICAL_SYNCHRONOUS_PULSE +
                                                    VERTICAL_BACK_PORCH - 1);
    /* 设置TLI层的颜色格式为RGB565 */
    tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
    /* 设置层的饱和度调整因子为最大值 */
    tli_layer_init_struct.layer_sa = 0xFF;
    /* 设置层的默认蓝色成分值为最大值 */
    tli_layer_init_struct.layer_default_blue = 0xFF;
    /* 设置层的默认绿色成分值为最大值 */
    tli_layer_init_struct.layer_default_green = 0xFF;
    /* 设置层的默认红色成分值为最大值 */
    tli_layer_init_struct.layer_default_red = 0xFF;
    /* 设置层的默认透明度为不透明 */
    tli_layer_init_struct.layer_default_alpha = 0x0;
    /* 设置层的ACF1属性为PASA */
    tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
    /* 设置层的ACF2属性为PASA */
    tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
    /* 设置层的帧缓冲区地址 */
    tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)gImage_logo;
    /* 设置层的帧缓冲区每行字节数 */
    tli_layer_init_struct.layer_frame_line_length = ((320 * 2) + 3);
    /* 设置层的帧缓冲区步长 */
    tli_layer_init_struct.layer_frame_buf_stride_offset = (320 * 2);
    /* 设置层的总行数 */
    tli_layer_init_struct.layer_frame_total_line_number = 100;
    /* 初始化TLI层0并应用配置 */
    tli_layer_init(LAYER0, &tli_layer_init_struct);
}
/*
 * 函数名称:ipa_config
 * 功能描述:初始化并配置IPA(图像处理加速器),包括设置像素格式转换模式、目标内存地址、前景图等。
 * 参数说明:baseaddress - 输入前景图像 的基础地址。
 * 返回值:无。
 */
static void ipa_config(uint32_t baseaddress)
{
    ipa_destination_parameter_struct  ipa_destination_init_struct; // 定义IPA目标配置结构体
    ipa_foreground_parameter_struct   ipa_fg_init_struct; // 定义IPA前景图配置结构体

    // 启用IPA的时钟
    rcu_periph_clock_enable(RCU_IPA);

    // 初始化IPA
    ipa_deinit();
    /* 配置IPA像素格式转换模式 */
    ipa_pixel_format_convert_mode_set(IPA_FGTODE);
    /* 配置目标像素格式 */
    ipa_destination_init_struct.destination_pf = IPA_DPF_RGB565;
    /* 配置目标内存基地址 */
    ipa_destination_init_struct.destination_memaddr = (uint32_t)&blended_address_buffer;
    /* 配置目标预定义的Alpha值(RGB) */
    ipa_destination_init_struct.destination_pregreen = 0;
    ipa_destination_init_struct.destination_preblue = 0;
    ipa_destination_init_struct.destination_prered = 0;
    ipa_destination_init_struct.destination_prealpha = 0;
    /* 配置目标行偏移量 */
    ipa_destination_init_struct.destination_lineoff = 0;
    /* 配置待处理图像的高度 */
    ipa_destination_init_struct.image_height = 118;
    /* 配置待处理图像的宽度 */
    ipa_destination_init_struct.image_width = 247;
    ipa_destination_init_struct.image_rotate = DESTINATION_ROTATE_0;
    ipa_destination_init_struct.image_hor_decimation = DESTINATION_HORDECIMATE_DISABLE;
    ipa_destination_init_struct.image_ver_decimation = DESTINATION_VERDECIMATE_DISABLE;
    /* 初始化IPA目标配置 */
    ipa_destination_init(&ipa_destination_init_struct);

    /* 配置IPA前景图初始化结构体 */
    /* baseaddress: 前景图内存地址的起始位置 */
    ipa_fg_init_struct.foreground_memaddr = baseaddress;
    /* 设置前景图的像素格式为RGB565 */
    ipa_fg_init_struct.foreground_pf = FOREGROUND_PPF_RGB565;
    /* 设置前景图的透明度计算模式为模式0 */
    ipa_fg_init_struct.foreground_alpha_algorithm = IPA_FG_ALPHA_MODE_0;
    /* 设置前景图的预透明值为0 */
    ipa_fg_init_struct.foreground_prealpha = 0x0;
    /* 设置前景图的行偏移为0 */
    ipa_fg_init_struct.foreground_lineoff = 0x0;
    /* 设置前景图的预蓝色值为0 */
    ipa_fg_init_struct.foreground_preblue = 0x0;
    /* 设置前景图的预绿色值为0 */
    ipa_fg_init_struct.foreground_pregreen = 0x0;
    /* 设置前景图的预红色值为0 */
    ipa_fg_init_struct.foreground_prered = 0x0;
    /* 前景图初始化 */
    ipa_foreground_init(&ipa_fg_init_struct);
}

/*!
    \brief      configure TLI peripheral and display blend image
    这个函数负责配置TLI(Thin-Layer Interface)
    以进行图像的混合显示。在此函数中,通常会进行层(Layer)的配置,
    包括层的背景色、透明度、图像数据源等。
    它可能还会设置图像的大小、位置以及如何将多个图像层混合在一起显示的具体参数。
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void tli_blend_config(void)
{
  // 初始化TLI(Terminal Layer Interface)相关的参数和硬件设置
    // 此段代码主要配置TLI的时钟源、PLL2的输入输出时钟范围,并启用PLL2时钟输出。
    tli_parameter_struct               tli_init_struct;
    tli_layer_parameter_struct         tli_layer_init_struct;
    rcu_periph_clock_enable(RCU_TLI);  // 启用TLI的外设时钟
    tli_gpio_config();                 // 配置TLI相关的GPIO
    /* 配置PLL2的输入时钟范围和输出时钟范围 */
    rcu_pll_input_output_clock_range_config(IDX_PLL2, RCU_PLL2RNG_1M_2M, RCU_PLL2VCO_150M_420M);
    /* 配置PLL2时钟:CK_PLL2P/CK_PLL2Q/CK_PLL2R = HXTAL_VALUE / 25 * 150 / 3 */
    if(ERROR == rcu_pll2_config(25, 150, 3, 3, 3)) {
        while(1) {
        }
    }
    /* 启用PLL2R时钟输出 */
    rcu_pll_clock_output_enable(RCU_PLL2R);
    rcu_tli_clock_div_config(RCU_PLL2R_DIV8);  // 配置TLI时钟分频
    rcu_osci_on(RCU_PLL2_CK);  // 开启PLL2时钟
    if(ERROR == rcu_osci_stab_wait(RCU_PLL2_CK)) {  // 等待PLL2时钟稳定
        while(1) {
        }
    }
    /*
    * 配置TLI(Transport Layer Interface)参数结构体
    * 本段代码主要负责初始化TLI参数结构体,并设置LCD显示时序配置和背景颜色。
    */
    /* 配置TLI参数结构体中的信号极性 */
    tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;  /* 水平同步信号极性配置为活动低电平 */
    tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;  /* 垂直同步信号极性配置为活动低电平 */
    tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;    /* 数据使能信号极性配置为活动低电平 */
    tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI; /* 像素时钟极性配置 */

    /* 配置LCD显示时序 */
    tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1;  /* 水平同步脉冲宽度配置 */
    tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1;    /* 垂直同步脉冲宽度配置 */
    tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1;  /* 水平方向后 porch 宽度配置 */
    tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1;      /* 垂直方向后 porch 宽度配置 */
    tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH
                                    - 1; /* 水平方向活动区域宽度配置 */
    tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 
                                    1; /* 垂直方向活动区域高度配置 */
    tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH +
                                   HORIZONTAL_FRONT_PORCH - 1;  /* 水平方向总线周期配置 */
    tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT +
                                   VERTICAL_FRONT_PORCH - 1;     /* 垂直方向总线周期配置 */

    /* 配置LCD背景颜色(红色、绿色、蓝色) */
    tli_init_struct.backcolor_red = 0xFF;   /* 背景红色值配置 */
    tli_init_struct.backcolor_green = 0xFF; /* 背景绿色值配置 */
    tli_init_struct.backcolor_blue = 0xFF;  /* 背景蓝色值配置 */

    /* 应用TLI初始化结构体配置 */
    tli_init(&tli_init_struct);  


    /*
    * TLI(Transfer Layer Interface)层1配置
    * 此代码段初始化TLI层1的设置,包括窗口位置、像素格式、默认颜色及缓冲区配置等。
    */

    /* 设置TLI层1的窗口左边界位置 */
    tli_layer_init_struct.layer_window_leftpos = 80 + HORIZONTAL_SYNCHRONOUS_PULSE +
                                                 HORIZONTAL_BACK_PORCH + 2;
    /* 设置TLI层1的窗口右边界位置 */
    tli_layer_init_struct.layer_window_rightpos = (80 + 247 + HORIZONTAL_SYNCHRONOUS_PULSE +
                                                   HORIZONTAL_BACK_PORCH - 1);
    /* 设置TLI层1的窗口上边界位置 */
    tli_layer_init_struct.layer_window_toppos = 20 + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH;
    /* 设置TLI层1的窗口下边界位置 */
    tli_layer_init_struct.layer_window_bottompos = 20 + 118 + VERTICAL_SYNCHRONOUS_PULSE +
                                                   VERTICAL_BACK_PORCH - 1;
    /* 配置TLI层1的像素格式为RGB565 */
    tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
    /* 设置层的alpha混合因子为全透 */
    tli_layer_init_struct.layer_sa = 0xFF;
    /* 配置层的ACF1参数为PASA */
    tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
    /* 配置层的ACF2参数为PASA */
    tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
    /* 设置层的默认Alpha值为0 */
    tli_layer_init_struct.layer_default_alpha = 0;
    /* 设置层的默认蓝色值为0 */
    tli_layer_init_struct.layer_default_blue = 0;
    /* 设置层的默认绿色值为0 */
    tli_layer_init_struct.layer_default_green = 0;
    /* 设置层的默认红色值为0 */
    tli_layer_init_struct.layer_default_red = 0;
    /* 设置层的帧缓冲区地址 */
    tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)&blended_address_buffer;
    /* 设置层的帧缓冲区每行长度 */
    tli_layer_init_struct.layer_frame_line_length = ((247 * 2) + 3);
    /* 设置层的帧缓冲区步长偏移 */
    tli_layer_init_struct.layer_frame_buf_stride_offset = (247 * 2);
    /* 设置层的帧缓冲区总行数 */
    tli_layer_init_struct.layer_frame_total_line_number = 118;
    /* 初始化TLI层1 */
    tli_layer_init(LAYER1, &tli_layer_init_struct);
}
/*!
    \brief      configure TLI GPIO
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void tli_gpio_config(void)
{
    /* enable GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOD);
    rcu_periph_clock_enable(RCU_GPIOE);
    rcu_periph_clock_enable(RCU_GPIOF);
    rcu_periph_clock_enable(RCU_GPIOH);
    rcu_periph_clock_enable(RCU_GPIOG);

    /* configure HSYNC(PE15), VSYNC(PA7), PCLK(PG7) */
    gpio_af_set(GPIOE, GPIO_AF_14, GPIO_PIN_15);
    gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_7);
    gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_7);
    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_15);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_7);
    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_7);

    /* configure LCD_R7(PG6), LCD_R6(PH12), LCD_R5(PH11), LCD_R4(PA5), LCD_R3(PH9),LCD_R2(PH8),
                 LCD_R1(PH3), LCD_R0(PH2) */
    gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_6);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_12);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_11);
    gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_5);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_9);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_8);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_3);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_2);

    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_6);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_12);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_11);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_5);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_9);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_8);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_3);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_2);

    /* configure  LCD_G7(PD3), LCD_G6(PC7), LCD_G5(PC1), LCD_G4(PH15), LCD_G3(PH14), LCD_G2(PH13),LCD_G1(PB0), LCD_G0(PB1) */
    gpio_af_set(GPIOD, GPIO_AF_14, GPIO_PIN_3);
    gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_7);
    gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_1);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_15);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_14);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_13);
    gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_0);
    gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_1);

    gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_3);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_7);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_1);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_15);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_14);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_13);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_0);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_1);

    /* configure LCD_B7(PB9), LCD_B6(PB8), LCD_B5(PB5), LCD_B4(PC11), LCD_B3(PG11),LCD_B2(PG10), LCD_B1(PG12), LCD_B0(PG14) */
    gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_9);
    gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_8);
    gpio_af_set(GPIOB, GPIO_AF_3, GPIO_PIN_5);
    gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_11);
    gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_11);
    gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_10);
    gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_12);
    gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_14);

    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_9);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_8);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_5);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_11);
    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_11);
    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_12);
    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_14);

    /* configure LCD_DE(PF10) */
    gpio_af_set(GPIOF, GPIO_AF_14, GPIO_PIN_10);
    gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
    /* LCD PWM BackLight(PG13) */
    gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_13);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_13);
    gpio_bit_set(GPIOG, GPIO_PIN_13);
}

/*!
    \brief      enable the CPU cache
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void cache_enable(void)
{
    /* enable i-Cache */
    SCB_EnableICache();

    /* enable d-Cache */
    SCB_EnableDCache();
}