原因说明
LV_USE_PERF_MONITOR会在lv_layer_sys()上放一个 FPS 标签,并周期性lv_label_set_text_fmt,只让一小块区域变脏。
你的disp_flush是这样工作的:
static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { rt_sem_take(_SemaphoreVsync, RT_WAITING_FOREVER); R_GLCDC_BufferChange(&g_display0_ctrl, (uint8_t *) color_p, (display_frame_layer_t) DISPLAY_FRAME_LAYER_1); lv_disp_flush_ready(disp_drv); }R_GLCDC_BufferChange是把整屏显存基址切到color_p,相当于“整帧缓冲交换”,并没有按area做局部拷贝。
在 LVGL 默认的部分刷新 + 双全屏缓冲下,每次只重绘脏区,缓冲区里其它像素可能是旧的或无效的;硬件却整屏显示这块缓冲 → 就容易出现花屏/错位。
性能监视器一开,小区域刷新变多,问题就更容易暴露。
关闭监视器时,刷新节奏、lv_timer_pause行为不同,有时看起来“还能用”,但根因仍是:整指针交换 与 部分刷新 不匹配。
LVGL 官方对“双满屏缓冲 + 只换地址”的说明就是:要设disp_drv.full_refresh = 1(见lv_port_disp_template.c里 “Example 3”)。
修改
void lv_port_disp_init(void) { _SemaphoreVsync = rt_sem_create("lvgl_sem", 1, RT_IPC_FLAG_PRIO); if (RT_NULL == _SemaphoreVsync) { rt_kprintf("lvgl semaphore create failed\r\n"); RT_ASSERT(0); } #if LVGL_VERSION_MAJOR < 9 /*Initialize `disp_buf` with the buffer(s). With only one buffer use NULL instead buf_2 */ lv_disp_draw_buf_init(&disp_buf, &fb_background[0][0], &fb_background[1][0], sizeof(fb_background[0])); /*Basic initialization*/ lv_disp_drv_init(&disp_drv); /*Set the resolution of the display*/ disp_drv.hor_res = LV_HOR_RES_MAX; disp_drv.ver_res = LV_VER_RES_MAX; /*Set a display buffer*/ disp_drv.draw_buf = &disp_buf; /* * GLCDC flush only switches the layer base address to a full framebuffer; it does not * merge partial areas. Partial refresh leaves non-dirty pixels stale in the back buffer, * which looks like corruption (often triggered by LV_USE_PERF_MONITOR updating a small label). * LVGL doc: two full-screen buffers + address swap => set full_refresh (see lv_port_disp_template). */ disp_drv.full_refresh = 1; /*Used to copy the buffer's content to the display*/ disp_drv.flush_cb = disp_flush; /*Finally register the driver*/ lv_disp_drv_register(&disp_drv); #else /*------------------------------------ * Create a display and set a flush_cb * -----------------------------------*/ lv_display_t *disp = lv_display_create(LV_HOR_RES_MAX, LV_VER_RES_MAX); lv_display_set_flush_cb(disp, disp_flush); lv_display_set_flush_wait_cb(disp, vsync_wait_cb); lv_display_set_buffers(disp, &fb_background[0][0], &fb_background[1][0], sizeof(fb_background[0]), LV_DISPLAY_RENDER_MODE_DIRECT); #endif lvgl_init_flag = 1; }在lv_port_disp.c里为 LVGL 8 路径增加了disp_drv.full_refresh = 1,保证每次刷新都会画满一整帧再交给 GLCDC,与当前的 BufferChange 用法一致。
代价:任意脏区都会触发整屏重绘,CPU 会高一些;调试时开LV_USE_PERF_MONITOR一般可以接受,正式发布可关掉监视器。
若仍要部分刷新以省算力,就需要改驱动:在flush_cb里按area把像素写入“当前显示用”的那块显存(或 DMA 局部更新),而不能只靠切换整帧指针。