打印

GD32H759加载lvgl数字时钟动态更新时,界面有撕裂感

[复制链接]
248|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xie375966220|  楼主 | 2025-3-28 18:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
目前在做GD32H759+GUI Guider上生成的加载数字时钟界面,按照12小时制显示。目前界面更新时会有轻微抖动,仔细观察存在轻微撕裂感,不知道各位大神有没有好办法支援一下
关于SDRAM以及LVGL部分重要代码如下:
SDRAM部分参考官网例程得来:
exmc_sdram_parameter_struct sdram_init_struct;
  exmc_sdram_timing_parameter_struct sdram_timing_init_struct;
  exmc_sdram_command_parameter_struct sdram_command_init_struct;
  exmc_sdram_struct_para_init(&sdram_init_struct);

  uint32_t command_content = 0, bank_select;
  uint32_t timeout = SDRAM_TIMEOUT;
        rcu_periph_clock_enable(RCU_EXMC);   //时钟使能
        //容量计算从SDRAM芯片手册入手:MT48LC32M16A2P,行地址13根,列地址10根,bank数为4:8K*1K*4=32MB
        FLX_XS_DRV_IO_EXMC();
       
        /* specify which SDRAM to read and write */
    if(EXMC_SDRAM_DEVICE0 == sdramDevice) {
        bank_select = EXMC_SDRAM_DEVICE0_SELECT;
    } else {
        bank_select = EXMC_SDRAM_DEVICE1_SELECT;
    }

    /* EXMC SDRAM device initialization sequence --------------------------------*/
    /* step 1 : configure SDRAM timing registers --------------------------------*/
    /* LMRD: 2 clock cycles */
#ifdef SDRAM_TEST_CONFIG_VALUE
    sdram_timing_init_struct.load_mode_register_delay = 4;
    /* XSRD: min = 67ns */
    sdram_timing_init_struct.exit_selfrefresh_delay = 24;
    /* RASD: min=42ns , max=120k (ns) */
    sdram_timing_init_struct.row_address_select_delay = 16;
    /* ARFD: min=60ns */
    sdram_timing_init_struct.auto_refresh_delay = 11;
    /* WRD:  min=1 Clock cycles +6ns */
    sdram_timing_init_struct.write_recovery_delay = 4;
    /* RPD:  min=18ns */
    sdram_timing_init_struct.row_precharge_delay = 8;
    /* RCD:  min=18ns */
    sdram_timing_init_struct.row_to_column_delay = 6;
#else
    sdram_timing_init_struct.load_mode_register_delay = 2;
    /* XSRD: min = 67ns */
    sdram_timing_init_struct.exit_selfrefresh_delay = 12;
    /* RASD: min=42ns , max=120k (ns) */
    sdram_timing_init_struct.row_address_select_delay = 8;
    /* ARFD: min=60ns */
    sdram_timing_init_struct.auto_refresh_delay = 11;
    /* WRD:  min=1 Clock cycles +6ns */
    sdram_timing_init_struct.write_recovery_delay = 2;
    /* RPD:  min=18ns */
    sdram_timing_init_struct.row_precharge_delay = 4;
    /* RCD:  min=18ns */
    sdram_timing_init_struct.row_to_column_delay = 4;
#endif
    /* step 2 : configure SDRAM control registers ---------------------------------*/
    sdram_init_struct.sdram_device = sdramDevice;
    sdram_init_struct.column_address_width = EXMC_SDRAM_COW_ADDRESS_9;
    sdram_init_struct.row_address_width = EXMC_SDRAM_ROW_ADDRESS_13;
    sdram_init_struct.data_width = EXMC_SDRAM_DATABUS_WIDTH_16B;
    sdram_init_struct.internal_bank_number = EXMC_SDRAM_4_INTER_BANK;
    sdram_init_struct.cas_latency = EXMC_CAS_LATENCY_3_SDCLK;
    sdram_init_struct.write_protection = DISABLE;
    sdram_init_struct.sdclock_config = EXMC_SDCLK_PERIODS_3_CK_EXMC;
//    sdram_init_struct.sdclock_config = EXMC_SDCLK_PERIODS_4_CK_EXMC;
    sdram_init_struct.burst_read_switch = ENABLE;
    sdram_init_struct.pipeline_read_delay = EXMC_PIPELINE_DELAY_1_CK_EXMC;
    sdram_init_struct.timing = &sdram_timing_init_struct;
    /* EXMC SDRAM bank initialization */
    exmc_sdram_init(&sdram_init_struct);

    /* step 3 : configure CKE high command---------------------------------------*/
    sdram_command_init_struct.command = EXMC_SDRAM_CLOCK_ENABLE;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK;
    sdram_command_init_struct.mode_register_content = 0;
    /* wait until the SDRAM controller is ready */
    while((exmc_flag_get(sdramDevice, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) {
        timeout--;
    }
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 4 : insert 10ms delay----------------------------------------------*/
    delay_ms(100);

    /* step 5 : configure precharge all command----------------------------------*/
    sdram_command_init_struct.command = EXMC_SDRAM_PRECHARGE_ALL;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK;
    sdram_command_init_struct.mode_register_content = 0;
    /* wait until the SDRAM controller is ready */
    timeout = SDRAM_TIMEOUT;
    while((exmc_flag_get(sdramDevice, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) {
        timeout--;
    }
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 6 : configure Auto-Refresh command-----------------------------------*/
    sdram_command_init_struct.command = EXMC_SDRAM_AUTO_REFRESH;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_8_SDCLK;
    sdram_command_init_struct.mode_register_content = 0;
    /* wait until the SDRAM controller is ready */
    timeout = SDRAM_TIMEOUT;
    while((exmc_flag_get(sdramDevice, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) {
        timeout--;
    }
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 7 : configure load mode register command-----------------------------*/
    /* program mode register */
    command_content = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1        |
                      SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
                      SDRAM_MODEREG_CAS_LATENCY_3           |
                      SDRAM_MODEREG_OPERATING_MODE_STANDARD |
                      SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

    sdram_command_init_struct.command = EXMC_SDRAM_LOAD_MODE_REGISTER;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK;
    sdram_command_init_struct.mode_register_content = command_content;

    /* wait until the SDRAM controller is ready */
    timeout = SDRAM_TIMEOUT;
    while((exmc_flag_get(sdramDevice, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) {
        timeout--;
    }
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 8 : set the auto-refresh rate counter--------------------------------*/
    /* 64ms, 8192-cycle refresh, 64ms/8192=7.81us */
    /* SDCLK_Freq = SYS_Freq/2 */
    /* (7.81 us * SDCLK_Freq) - 20 */
    exmc_sdram_refresh_count_set(1542);
//    exmc_sdram_refresh_count_set(1151);

    /* wait until the SDRAM controller is ready */
    timeout = SDRAM_TIMEOUT;
    while((exmc_flag_get(sdramDevice, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)) {
        timeout--;
    }
MPU相关配置如下:
static void mpu_config(void)
{
    mpu_region_init_struct mpu_init_struct;
    mpu_region_struct_para_init(&mpu_init_struct);

    /* disable the MPU */
    ARM_MPU_Disable();
    ARM_MPU_SetRegion(0, 0);
   
    /* configure the MPU attributes for Reserved, no access */
    mpu_init_struct.region_base_address  = 0x0;
    mpu_init_struct.region_size          = MPU_REGION_SIZE_4GB;
    mpu_init_struct.access_permission    = MPU_AP_NO_ACCESS;
    mpu_init_struct.access_bufferable    = MPU_ACCESS_NON_BUFFERABLE;
    mpu_init_struct.access_cacheable     = MPU_ACCESS_NON_CACHEABLE;
    mpu_init_struct.access_shareable     = MPU_ACCESS_SHAREABLE;
    mpu_init_struct.region_number        = MPU_REGION_NUMBER0;
    mpu_init_struct.subregion_disable    = 0x87;
    mpu_init_struct.instruction_exec     = MPU_INSTRUCTION_EXEC_NOT_PERMIT;
    mpu_init_struct.tex_type             = MPU_TEX_TYPE0;
    mpu_region_config(&mpu_init_struct);
    mpu_region_enable();
   
    /* configure the MPU attributes for SDRAM */
    mpu_init_struct.region_base_address  = 0xC0000000;
    mpu_init_struct.region_size          = MPU_REGION_SIZE_32MB;
    mpu_init_struct.access_permission    = MPU_AP_FULL_ACCESS;
    mpu_init_struct.access_bufferable    = MPU_ACCESS_BUFFERABLE;
    mpu_init_struct.access_cacheable     = MPU_ACCESS_CACHEABLE;
    mpu_init_struct.access_shareable     = MPU_ACCESS_NON_SHAREABLE;
    mpu_init_struct.region_number        = MPU_REGION_NUMBER1;
    mpu_init_struct.subregion_disable    = MPU_SUBREGION_ENABLE;
    mpu_init_struct.instruction_exec     = MPU_INSTRUCTION_EXEC_PERMIT;
    mpu_init_struct.tex_type             = MPU_TEX_TYPE0;
    mpu_region_config(&mpu_init_struct);
    mpu_region_enable();
   
    /* enable the MPU */
    ARM_MPU_Enable(MPU_MODE_PRIV_DEFAULT);
}
LVGL内存管理和缓冲区采用内部缓存:lv_conf.h文件中相关宏如下:
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0
    /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
//#define   LV_MEM_SDRAM         (1)
#ifdef  LV_MEM_SDRAM
    #define LV_MEM_SIZE (2048U * 1024U)          /*[bytes]*/
#else
                #define LV_MEM_SIZE (300U * 1024U)          /*[bytes]*/
#endif
    /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
#ifdef  LV_MEM_SDRAM   
//                #define LV_MEM_ADR (LCD_FRAME_BUF_ADDR+ACTIVE_WIDTH * ACTIVE_HEIGHT*6)     /*0: unused*/
                #define LV_MEM_ADR (LCD_FRAME_BUF_ADDR+0x800000)     /*0: unused*/
#else
                #define LV_MEM_ADR (0)     /*0: unused*/
#endif
                /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
    #if LV_MEM_ADR == 0
        //#define LV_MEM_POOL_INCLUDE your_alloc_library  /* Uncomment if using an external allocator*/
        //#define LV_MEM_POOL_ALLOC   your_alloc          /* Uncomment if using an external allocator*/
    #endif
lv_port_disp_template.c
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 to your display driver's `flush_cb` to copy its content to your display.
     * The buffer has to be greater than 1 display row
     *
     * There are 3 buffering configurations:
     * 1. Create ONE buffer:
     *      LVGL will draw the display's content here and writes it to your display
     *
     * 2. Create TWO buffer:
     *      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. Double buffering
     *      Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
     *      This way LVGL will always provide the whole rendered screen in `flush_cb`
     *      and you only need to change the frame buffer's address.
     */

    /* Example for 1) */
    static lv_disp_draw_buf_t draw_buf_dsc_1;
    static lv_color_t buf_1[ACTIVE_WIDTH * 120];                          /*A buffer for 10 rows*/
    lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, ACTIVE_WIDTH * 120);   /*Initialize the display buffer*/
//    static lv_color_t buf_1[ACTIVE_WIDTH * ACTIVE_HEIGHT/4] __attribute__((at(LCD_FRAME_BUF_ADDR+ACTIVE_WIDTH * ACTIVE_HEIGHT*4)));                          /*A buffer for 10 rows*/
//    lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, ACTIVE_WIDTH * ACTIVE_HEIGHT/4);   /*Initialize the display buffer*/
//                /* 清理缓存确保数据一致性 */
//    SCB_CleanDCache_by_Addr((uint32_t*)buf_1, ACTIVE_WIDTH * ACTIVE_HEIGHT/4 * sizeof(lv_color_t));
    /* Example for 2) */
//    static lv_disp_draw_buf_t draw_buf_dsc_2;
//    static lv_color_t buf_2_1[ACTIVE_WIDTH * ACTIVE_HEIGHT/4] __attribute__((at(LCD_FRAME_BUF_ADDR+ACTIVE_WIDTH * ACTIVE_HEIGHT*4)));                          /*A buffer for 10 rows*/
//    static lv_color_t buf_2_2[ACTIVE_WIDTH * ACTIVE_HEIGHT/4] __attribute__((at(LCD_FRAME_BUF_ADDR+ACTIVE_WIDTH * ACTIVE_HEIGHT*6)));                          /*A buffer for 10 rows*/
               
//    static lv_color_t buf_2_1[ACTIVE_WIDTH * 120];                        /*A buffer for 10 rows*/
//    static lv_color_t buf_2_2[ACTIVE_WIDTH * 120];                        /*An other buffer for 10 rows*/
//   lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, ACTIVE_WIDTH * ACTIVE_HEIGHT/4);   /*Initialize the display buffer*/
//                /* 清理缓存确保数据一致性 */
//    SCB_CleanDCache_by_Addr((uint32_t*)buf_2_1, ACTIVE_WIDTH * ACTIVE_HEIGHT/4 * sizeof(lv_color_t));
                /* 清理缓存确保数据一致性 */
//    SCB_CleanDCache_by_Addr((uint32_t*)buf_2_2, ACTIVE_WIDTH * ACTIVE_HEIGHT/4 * sizeof(lv_color_t));

//    /* Example for 3) also set disp_drv.full_refresh = 1 below*/
//////    static lv_disp_draw_buf_t draw_buf_dsc_3;
//////    static lv_color_t buf_3_1[ACTIVE_WIDTH * ACTIVE_HEIGHT] __attribute__((at(LCD_FRAME_BUF_ADDR+ACTIVE_WIDTH * ACTIVE_HEIGHT*4)));            /*A screen sized buffer*/
//////    static lv_color_t buf_3_2[ACTIVE_WIDTH * ACTIVE_HEIGHT] __attribute__((at(LCD_FRAME_BUF_ADDR+ACTIVE_WIDTH * ACTIVE_HEIGHT*6)));            /*Another screen sized buffer*/
//////      lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1,buf_3_2,
//////                          ACTIVE_WIDTH * ACTIVE_HEIGHT);   /*Initialize the display buffer*/
///////* 清理缓存确保数据一致性 */
//////    SCB_CleanDCache_by_Addr((uint32_t*)buf_3_1, ACTIVE_WIDTH * ACTIVE_HEIGHT * sizeof(lv_color_t));
///////* 清理缓存确保数据一致性 */
//////    SCB_CleanDCache_by_Addr((uint32_t*)buf_3_2, ACTIVE_WIDTH * ACTIVE_HEIGHT * sizeof(lv_color_t));   
/*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/

//    static 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 = ACTIVE_WIDTH;
    disp_drv.ver_res = ACTIVE_HEIGHT;
       


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

    /*Set a display buffer*/
    disp_drv.draw_buf = &draw_buf_dsc_1;

    /*Required for Example 3)*/
//    disp_drv.full_refresh = 1;

    /* Fill a memory array with a color if you have GPU.
     * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
     * But if you have a different GPU you can use with this callback.*/
    //disp_drv.gpu_fill_cb = gpu_fill;

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
                if(disp_flush_enabled) {
    uint16_t offline;
    uint32_t addr;   
//                uint32_t size = lv_area_get_width(area) * lv_area_get_height(area) * sizeof(lv_color_t);
//    SCB_CleanDCache_by_Addr((uint32_t*)color_p, size);
                        SCB_CleanInvalidateDCache();
//                        SCB_CleanDCache();
    addr = (uint32_t)ltdc_framebuf[1] + 2 * (disp_drv->hor_res * area->y1 + area->x1); /* 输出存储器地址 */   

   
    //  偏移量
    offline = disp_drv->hor_res - (area->x2 - area->x1 + 1);
    IPA_CTL = 0x00000000UL | IPA_CTL_FTFIE;
    // 存储器到存储器模式
    /* Set up pixel format */
    IPA_FPCTL = IPA_DPF_RGB565; // 颜色格式为RGB565
   
    /* Set up pointers */
    IPA_FMADDR = (uint32_t)color_p;  /* 源地址 */
    IPA_DMADDR =  addr;
    IPA_FLOFF = 0;                   // 前景层行偏移为0
    IPA_DLOFF = offline;             /* 设置行偏移 */
    /*  Set up size */
    IPA_IMS = (uint32_t)((area->x2 - area->x1 + 1) << 16) | (uint16_t)(area->y2 - area->y1 + 1);
    IPA_CTL |= (1 << 0); // 使能IPA
    /* transfer enable */
    ipa_transfer_enable();
    g_gpu_state = 1;
//                SCB_CleanInvalidateDCache_by_Addr((uint32_t*)disp_drv->draw_buf->buf_act, disp_drv->draw_buf->size * sizeof(lv_color_t));
        }   
}

界面加载800*480的图片正常,数字时钟仅显示时分,按s更新数字时钟时界面出现轻微撕裂感。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

4

帖子

0

粉丝