基于littleVGL的双缓存机制,改善刷新率问题

本文介绍了如何通过littleVGL的双缓存机制,在不依赖GPU的情况下,改善STM32F429嵌入式系统的屏幕刷新率问题。详细阐述了配置显示缓冲区、ltdc设置、littleVGL初始化及刷新函数的修改步骤,最终实现了30ms一帧的显示效果。

基于littleVGL的双缓存机制,不使用GPU,改善刷新率问题

前言

在使用littleVGL作为图形库的时候,单缓冲的显示有类似拉窗帘的效果,不能用于实际产品中,littleVGL支持双缓存显示可以满足我们的显示要求

一、运行环境

当前项目使用的是MCU是STM32F429的片子,本身的资源非常丰富,使用的LTDC的外设驱动屏幕刷新,具体ltdc的外设等后面文章在记录;使用了外扩的SDRAM的前80048022字节作为显示缓冲区;屏幕800480,RGB565;

二、实现步骤

1.指定显示缓冲区

代码如下(示例):

/**
  * @brief LCD显存和LVGL缓存定义
  */
uint16_t lcd_buffer_1[LCD_WIDTH * LCD_HIGH] __attribute__((at(SDRAM_LCD_ADDR_1)));
uint16_t lcd_buffer_2[LCD_WIDTH * LCD_HIGH] __attribute__((at(SDRAM_LCD_ADDR_2)));

2.指定ltdc显示buffer

代码如下(示例):pLayerCfg.FBStartAdress = (uint32_t)lcd_buffer_1;

/* LTDC init function */
void MX_LTDC_Init(void)
{

  /* USER CODE BEGIN LTDC_Init 0 */

  /* USER CODE END LTDC_Init 0 */

  LTDC_LayerCfgTypeDef pLayerCfg = {0};

  /* USER CODE BEGIN LTDC_Init 1 */

  /* USER CODE END LTDC_Init 1 */
  hltdc.Instance = LTDC;
  hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
  hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
  hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
  hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
  hltdc.Init.HorizontalSync = 1;
  hltdc.Init.VerticalSync = 1;
  hltdc.Init.AccumulatedHBP = 47;
  hltdc.Init.AccumulatedVBP = 24;
  hltdc.Init.AccumulatedActiveW = 847;
  hltdc.Init.AccumulatedActiveH = 504;
  hltdc.Init.TotalWidth = 1057;
  hltdc.Init.TotalHeigh = 526;
  hltdc.Init.Backcolor.Blue = 0;
  hltdc.Init.Backcolor.Green = 0;
  hltdc.Init.Backcolor.Red = 0;
  if (HAL_LTDC_Init(&hltdc) != HAL_OK)
  {
    Error_Handler();
  }
  pLayerCfg.WindowX0 = 0;
  pLayerCfg.WindowX1 = 800;
  pLayerCfg.WindowY0 = 0;
  pLayerCfg.WindowY1 = 480;
  pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
  pLayerCfg.Alpha = 255;
  pLayerCfg.Alpha0 = 0;
  pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
  pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
  pLayerCfg.FBStartAdress = (uint32_t)lcd_buffer_1;
  pLayerCfg.ImageWidth = 800;
  pLayerCfg.ImageHeight = 480;
  pLayerCfg.Backcolor.Blue = 0;
  pLayerCfg.Backcolor.Green = 0;
  pLayerCfg.Backcolor.Red = 0;
  if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN LTDC_Init 2 */

  /* USER CODE END LTDC_Init 2 */

}

3.配置littleVGL的初始化

void lv_port_disp_init(void)
{
    /*-------------------------
     * Initialize your display
     * -----------------------*/
    disp_init();

    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/

    /* LVGL requires a buffer where it internally draws the widgets.
     * Later this buffer will passed your display drivers `flush_cb` to copy its content to your display.
     * The buffer has to be greater than 1 display row
     *
     * There are three buffering configurations:
     * 1. Create ONE buffer with some rows:
     *      LVGL will draw the display's content here and writes it to your display
     *
     * 2. Create TWO buffer with some rows:
     *      LVGL will draw the display's content to a buffer and writes it your display.
     *      You should use DMA to write the buffer's content to the display.
     *      It will enable LVGL to draw the next part of the screen to the other buffer while
     *      the data is being sent form the first buffer. It makes rendering and flushing parallel.
     *
     * 3. Create TWO screen-sized buffer:
     *      Similar to 2) but the buffer have to be screen sized. When LVGL is ready it will give the
     *      whole frame to display. This way you only need to change the frame buffer's address instead of
     *      copying the pixels.
     * */

    static lv_disp_buf_t draw_buf_dsc_3;
	lv_disp_buf_init(&draw_buf_dsc_3, lcd_buffer_2, lcd_buffer_1, LV_HOR_RES_MAX * LV_VER_RES_MAX);
		
    /*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/

    lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/

    /*Set up the functions to access to your display*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = LV_HOR_RES_MAX;
    disp_drv.ver_res = LV_VER_RES_MAX;

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = disp_flush;

    /*Set a display buffer*/
	disp_drv.buffer = &draw_buf_dsc_3;
#if LV_USE_GPU
    /*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/

    /*Blend two color array using opacity*/
    disp_drv.gpu_blend_cb = gpu_blend;

    /*Fill a memory array with a color*/
    disp_drv.gpu_fill_cb = gpu_fill;
#endif

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

根据我们的需要创建2个显示缓冲区

3.更改lvgl刷新函数

/* Flush the content of the internal buffer the specific area on the display
 * You can use DMA or any hardware acceleration to do this operation in the background but
 * 'lv_disp_flush_ready()' has to be called when finished. */
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
	HAL_LTDC_SetAddress_NoReload(&hltdc,(uint32_t)color_p,0);
	HAL_LTDC_Reload(&hltdc,LTDC_RELOAD_VERTICAL_BLANKING);
    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
	lv_disp_flush_ready(disp_drv);	
}

这里需要注意的是当lvgl需要刷新时,重新配置帧缓冲区地址而不重新加载,然后重新加载LTDC层配置, 有二中方式,一个是是立刻加载,一个是一帧结束加载,
LTDC_RELOAD_IMMEDIATE : Immediate Reload
LTDC_RELOAD_VERTICAL_BLANKING : Reload in the next Vertical Blanking
至此lvgl刷新的问题就可以解决了,测试拉帘效果消失,可以达到解决30ms一帧的显示效果,这是在没有使用DMA2D的情况下的效果,因为芯片限制,如果后面无法使用ST的片子,替换的片子不可能保证都有GPU的功能


总结

lvgl显示刷新问题基本上使用双缓冲区可以得到解决

评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值