目前在做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更新数字时钟时界面出现轻微撕裂感。 |