目录

LVGL 显示缓冲区

LVGL 显示驱动

小结

lv_disp_drv_register 分析

lv_disp_drv_init 分析

lv_disp_drv_register 分析

注意


LVGL 是 GUI 的图形绘制库,既然是图形绘制,那么就需要考虑 2 点:

  1. 开辟绘制的 buffer;
  2. 对接底层实际绘制到屏幕的驱动;

LVGL 显示缓冲区

在 LVGL v8 上,用于绘制的 buffer,使用 lv_disp_draw_buf_t 结构体进行描述:

/*** Structure for holding display buffer information.*/
typedef struct _lv_disp_draw_buf_t {void * buf1; /**< First display buffer.*/void * buf2; /**< Second display buffer.*//*Internal, used by the library*/void * buf_act;uint32_t size; /*In pixel count*//*1: flushing is in progress. (It can't be a bit field because when it's cleared from IRQ Read-Modify-Write issue might occur)*/volatile int flushing;/*1: It was the last chunk to flush. (It can't be a bit field because when it's cleared from IRQ Read-Modify-Write issue might occur)*/volatile int flushing_last;volatile uint32_t last_area         : 1; /*1: the last area is being rendered*/volatile uint32_t last_part         : 1; /*1: the last part of the current area is being rendered*/
} lv_disp_draw_buf_t;

绘制缓冲区是 LVGL 用来渲染屏幕内容的简单数组。 一旦渲染准备就绪,绘制缓冲区的内容将使用显示驱动程序中设置的 flush_cb 函数发送到显示器;

lv_port_disp.c 文件中 (xxx_port_xxx 这种文件,都是需要根据实际的芯片、板卡、和外设来进行移植),有一个函数 lv_port_disp_init;用于初始化显示相关的内容,可以在里面进行绘制缓冲区的配置:

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[MY_DISP_HOR_RES * 10];                          /*A buffer for 10 rows*/lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*///    /* Example for 2) */
//    static lv_disp_draw_buf_t draw_buf_dsc_2;
//    static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10];                        /*A buffer for 10 rows*/
//    static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10];                        /*An other buffer for 10 rows*/
//    lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*///    /* 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[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*A screen sized buffer*/
//    static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*Another screen sized buffer*/
//    lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX);   /*Initialize the display buffer*/............
}

代码中可以看到,有 3 种配置,这里选了第 1 种,配置的绘图缓冲区为:MY_DISP_HOR_RES * 10;也就是 10 行数据;

lv_port_disp_init 函数,是 LVGL 在初始化的时候,需要显式调用的;

注意,这里的 lv_disp_draw_buf_t ,是 static 的,也就是说它是静态的,全局的变量,不能也不要被销毁;

绘制缓冲区可以小于屏幕。在这种情况下,较大的区域将被重新绘制为适合绘制缓冲区的较小部分。 如果只有一个小区域发生变化(例如按下按钮),则只会刷新该区域。

更大的缓冲区会导致更好的性能,但超过 1/10 屏幕大小的缓冲区没有显着的性能改进。 因此,建议选择绘制缓冲区的大小至少为屏幕大小的 1/10。

如果只使用一个缓冲区,LVGL 将屏幕内容绘制到该绘制缓冲区中并将其发送到显示器。 这样 LVGL 需要等到缓冲区的内容发送到显示器,然后再在其中绘制新内容。

如果使用两个缓冲区,LVGL 可以绘制到一个缓冲区中,而另一个缓冲区的内容被发送到后台显示。 应使用 DMA 或其他硬件将数据传输到显示器,让 MCU 同时绘制。 这样,显示的渲染和刷新变得并行。

LVGL 显示驱动

LVGL 使用 lv_disp_drv_t 来表示一个显示驱动,它的定义如下:

/*** Display Driver structure to be registered by HAL.* Only its pointer will be saved in `lv_disp_t` so it should be declared as* `static lv_disp_drv_t my_drv` or allocated dynamically.*/
typedef struct _lv_disp_drv_t {lv_coord_t hor_res;         /**< Horizontal resolution.*/lv_coord_t ver_res;         /**< Vertical resolution.*/lv_coord_tphysical_hor_res;     /**< Horizontal resolution of the full / physical display. Set to -1 for fullscreen mode.*/lv_coord_tphysical_ver_res;     /**< Vertical resolution of the full / physical display. Set to -1 for fullscreen mode.*/lv_coord_toffset_x;             /**< Horizontal offset from the full / physical display. Set to 0 for fullscreen mode.*/lv_coord_t offset_y;             /**< Vertical offset from the full / physical display. Set to 0 for fullscreen mode.*//** Pointer to a buffer initialized with `lv_disp_draw_buf_init()`.* LVGL will use this buffer(s) to draw the screens contents*/lv_disp_draw_buf_t * draw_buf;uint32_t direct_mode : 1;        /**< 1: Use screen-sized buffers and draw to absolute coordinates*/uint32_t full_refresh : 1;       /**< 1: Always make the whole screen redrawn*/uint32_t sw_rotate : 1;          /**< 1: use software rotation (slower)*/uint32_t antialiasing : 1;       /**< 1: anti-aliasing is enabled on this display.*/uint32_t rotated : 2;            /**< 1: turn the display by 90 degree. @warning Does not update coordinates for you!*/uint32_t screen_transp : 1;      /**Handle if the screen doesn't have a solid (opa == LV_OPA_COVER) background.* Use only if required because it's slower.*/uint32_t dpi : 10;              /** DPI (dot per inch) of the display. Default value is `LV_DPI_DEF`.*//** MANDATORY: Write the internal buffer (draw_buf) to the display. 'lv_disp_flush_ready()' has to be* called when finished*/void (*flush_cb)(struct _lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);/** OPTIONAL: Extend the invalidated areas to match with the display drivers requirements* E.g. round `y` to, 8, 16 ..) on a monochrome display*/void (*rounder_cb)(struct _lv_disp_drv_t * disp_drv, lv_area_t * area);/** OPTIONAL: Set a pixel in a buffer according to the special requirements of the display* Can be used for color format not supported in LittelvGL. E.g. 2 bit -> 4 gray scales* @note Much slower then drawing with supported color formats.*/void (*set_px_cb)(struct _lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,lv_color_t color, lv_opa_t opa);void (*clear_cb)(struct _lv_disp_drv_t * disp_drv, uint8_t * buf, uint32_t size);/** OPTIONAL: Called after every refresh cycle to tell the rendering and flushing time + the* number of flushed pixels*/void (*monitor_cb)(struct _lv_disp_drv_t * disp_drv, uint32_t time, uint32_t px);/** OPTIONAL: Called periodically while lvgl waits for operation to be completed.* For example flushing or GPU* User can execute very simple tasks here or yield the task*/void (*wait_cb)(struct _lv_disp_drv_t * disp_drv);/** OPTIONAL: Called when lvgl needs any CPU cache that affects rendering to be cleaned*/void (*clean_dcache_cb)(struct _lv_disp_drv_t * disp_drv);/** OPTIONAL: called when driver parameters are updated */void (*drv_update_cb)(struct _lv_disp_drv_t * disp_drv);/** On CHROMA_KEYED images this color will be transparent.* `LV_COLOR_CHROMA_KEY` by default. (lv_conf.h)*/lv_color_t color_chroma_key;lv_draw_ctx_t * draw_ctx;void (*draw_ctx_init)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);void (*draw_ctx_deinit)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);size_t draw_ctx_size;#if LV_USE_USER_DATAvoid * user_data; /**< Custom display driver user data*/
#endif} lv_disp_drv_t;

lv_port_disp_init 函数中,会调用 lv_disp_drv_init 函数,来进行显示驱动部分的初始化:

void lv_port_disp_init(void)
{
........./*-----------------------------------* 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*//* NOTES : StephenZhou modified, to adapt ST7735S 128x128 Screen */disp_drv.hor_res = 128;disp_drv.ver_res = 128;/*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);
}

这里可以看到,定义了一个 disp_drv 的 driver,而且是静态的;传入到 lv_disp_drv_init 函数进行最基本的结构体初始化:

void lv_disp_drv_init(lv_disp_drv_t * driver)
{lv_memset_00(driver, sizeof(lv_disp_drv_t));driver->hor_res          = 320;driver->ver_res          = 240;driver->physical_hor_res = -1;driver->physical_ver_res = -1;driver->offset_x         = 0;driver->offset_y         = 0;driver->antialiasing     = LV_COLOR_DEPTH > 8 ? 1 : 0;driver->screen_transp    = LV_COLOR_SCREEN_TRANSP;driver->dpi              = LV_DPI_DEF;driver->color_chroma_key = LV_COLOR_CHROMA_KEY;#if LV_USE_GPU_STM32_DMA2Ddriver->draw_ctx_init = lv_draw_stm32_dma2d_ctx_init;driver->draw_ctx_deinit = lv_draw_stm32_dma2d_ctx_init;driver->draw_ctx_size = sizeof(lv_draw_stm32_dma2d_ctx_t);
#elif LV_USE_GPU_NXP_PXPdriver->draw_ctx_init = lv_draw_nxp_pxp_init;driver->draw_ctx_deinit = lv_draw_nxp_pxp_init;driver->draw_ctx_size = sizeof(lv_draw_nxp_pxp_t);
#elif LV_USE_GPU_NXP_VG_LITEdriver->draw_ctx_init = lv_draw_nxp_vglite_init;driver->draw_ctx_deinit = lv_draw_nxp_vglite_init;driver->draw_ctx_size = sizeof(lv_draw_nxp_vglite_t);
#elif LV_USE_GPU_SDLdriver->draw_ctx_init = lv_draw_sdl_init_ctx;driver->draw_ctx_deinit = lv_draw_sdl_deinit_ctx;driver->draw_ctx_size = sizeof(lv_draw_sdl_ctx_t);
#elsedriver->draw_ctx_init = lv_draw_sw_init_ctx;driver->draw_ctx_deinit = lv_draw_sw_init_ctx;driver->draw_ctx_size = sizeof(lv_draw_sw_ctx_t);
#endif}

接着,在外部,显示的配置了 hor_resver_res(这里,我使用的屏是 128x128 的);

接着配置了 disp_drv.flush_cb = disp_flush; 这个是用于对接到 platform 的刷新数据的函数,(这个函数需要自行实现);

然后再将前面初始化完成的 draw_buf 挂接到 display 上:disp_drv.draw_buf = &draw_buf_dsc_1;

最后调用 lv_disp_drv_register,来将初始化完毕的 display driver 注册给 LVGL;

这里需要注意的是,disp_drv.flush_cb = disp_flush; 函数的移植,是需要我们自行根据屏来实现的:

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*/int32_t x;int32_t y;for(y = area->y1; y <= area->y2; y++) {for(x = area->x1; x <= area->x2; x++) {/*Put a pixel to the display. For example:*//*put_px(x, y, *color_p)*/LCD_DrawPoint(x, y, color_p->full);color_p++;}}//    LCD_Fill(area->x1, area->y1, area->x2, area->y2, *((uint16_t *)color_p));/*IMPORTANT!!!*Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv);
}

这里的 LCD_DrawPoint 就是自行实现的,这里使用 STM32 + ST7735S(SPI方式驱屏),所以这里的 LCD_DrawPoint 是对接的这部分驱动实现:

void LCD_DrawPoint(u16 x,u16 y,u16 color)
{LCD_Address_Set(x,y,x,y);//设置光标位置LCD_WriteData_16Bit(color);
}

小结

应用层调用 lv_init,在调用 lv_port_disp_init 函数,来完成 LVGL 的初始化动作:

application

|-------> lv_init()

|-------> lv_port_disp_init()

|------------------> lv_disp_draw_buf_init() 配置绘制 buffer;

|------------------> lv_disp_drv_init() 配置底层驱动,挂接绘制函数 flush_cb;

|-------------------------------> lv_disp_drv_register() 注册驱动;

lv_disp_drv_register 分析

lv_disp_drv_init 分析

要说 lv_disp_drv_register,那么就要先说在他之前调用的 lv_disp_drv_init:

void lv_disp_drv_init(lv_disp_drv_t * driver)
{lv_memset_00(driver, sizeof(lv_disp_drv_t));driver->hor_res          = 320;driver->ver_res          = 240;driver->physical_hor_res = -1;driver->physical_ver_res = -1;driver->offset_x         = 0;driver->offset_y         = 0;driver->antialiasing     = LV_COLOR_DEPTH > 8 ? 1 : 0;driver->screen_transp    = LV_COLOR_SCREEN_TRANSP;driver->dpi              = LV_DPI_DEF;driver->color_chroma_key = LV_COLOR_CHROMA_KEY;#if LV_USE_GPU_STM32_DMA2Ddriver->draw_ctx_init = lv_draw_stm32_dma2d_ctx_init;driver->draw_ctx_deinit = lv_draw_stm32_dma2d_ctx_init;driver->draw_ctx_size = sizeof(lv_draw_stm32_dma2d_ctx_t);
#elif LV_USE_GPU_NXP_PXPdriver->draw_ctx_init = lv_draw_nxp_pxp_init;driver->draw_ctx_deinit = lv_draw_nxp_pxp_init;driver->draw_ctx_size = sizeof(lv_draw_nxp_pxp_t);
#elif LV_USE_GPU_NXP_VG_LITEdriver->draw_ctx_init = lv_draw_nxp_vglite_init;driver->draw_ctx_deinit = lv_draw_nxp_vglite_init;driver->draw_ctx_size = sizeof(lv_draw_nxp_vglite_t);
#elif LV_USE_GPU_SDLdriver->draw_ctx_init = lv_draw_sdl_init_ctx;driver->draw_ctx_deinit = lv_draw_sdl_deinit_ctx;driver->draw_ctx_size = sizeof(lv_draw_sdl_ctx_t);
#elsedriver->draw_ctx_init = lv_draw_sw_init_ctx;driver->draw_ctx_deinit = lv_draw_sw_init_ctx;driver->draw_ctx_size = sizeof(lv_draw_sw_ctx_t);
#endif}

初始化了一些关键的结构,这里,关系最后几个:

  • draw_ctx_init;
  • draw_ctx_deini;
  • draw_ctx_size;

我们没定义 STM32_DMA2D 以及其他的,所以默认就走了最后一个,也就是纯软件的 draw context;

void lv_disp_drv_init(lv_disp_drv_t * driver)
{
......driver->draw_ctx_init = lv_draw_sw_init_ctx;driver->draw_ctx_deinit = lv_draw_sw_init_ctx;driver->draw_ctx_size = sizeof(lv_draw_sw_ctx_t);
......}

OK,看起来只有这个 lv_draw_sw_init_ctx,这个,记住他;

lv_disp_drv_register 分析

lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver)
{// 首先生成一个 _lv_dis_ll 链表的头,displv_disp_t * disp = _lv_ll_ins_head(&LV_GC_ROOT(_lv_disp_ll));if(!disp) {LV_ASSERT_MALLOC(disp);return NULL;}/*Create a draw context if not created yet*/// 这里没有指定 draw_ctxif(driver->draw_ctx == NULL) {// 分配一个 draw_ctxlv_draw_ctx_t * draw_ctx = lv_mem_alloc(driver->draw_ctx_size);LV_ASSERT_MALLOC(draw_ctx);if(draw_ctx == NULL) return NULL;// 调用 draw_ctx_init,也就是 lv_disp_drv_init 函数挂接的 lv_draw_sw_init_ctxdriver->draw_ctx_init(driver, draw_ctx);// 将 draw_ctx 挂接到 drvier->draw_ctxdriver->draw_ctx = draw_ctx;}lv_memset_00(disp, sizeof(lv_disp_t));// 将 driver 挂接到 disp->drvierdisp->driver = driver;// 至此disp 结构,已经包含的几乎所有的 port 相关的操作;// 包含 flush_cb 即,直接绘制到屏的驱动,和平台相关;// 包含绘制相关的接口,在 draw_ctx 中;// 赋值给全局变量 disp_deflv_disp_t * disp_def_tmp = disp_def;disp_def                 = disp; /*Temporarily change the default screen to create the default screens on thenew display*//*Create a refresh timer*/// 创建一个用于 refresh 的 lv_timer,使用默认的 LV_DISP_DEF_REFR_PERIOD=30ms 为周期,lv_timer 的回调函数为 _lv_disp_refr_timerdisp->refr_timer = lv_timer_create(_lv_disp_refr_timer, LV_DISP_DEF_REFR_PERIOD, disp);LV_ASSERT_MALLOC(disp->refr_timer);if(disp->refr_timer == NULL) {lv_mem_free(disp);return NULL;}// 判断分配的 buffer 是否可以支持全刷新模式if(driver->full_refresh && driver->draw_buf->size < (uint32_t)driver->hor_res * driver->ver_res) {driver->full_refresh = 0;LV_LOG_WARN("full_refresh requires at least screen sized draw buffer(s)");}// 设置背景颜色为白色disp->bg_color = lv_color_white();// 设置不透明度
#if LV_COLOR_SCREEN_TRANSPdisp->bg_opa = LV_OPA_TRANSP;
#elsedisp->bg_opa = LV_OPA_COVER;
#endif#if LV_USE_THEME_DEFAULT//设置 themeif(lv_theme_default_is_inited() == false) {disp->theme = lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);}else {disp->theme = lv_theme_default_get();}
#endif//创建系统的 3 个 screendisp->act_scr   = lv_obj_create(NULL); /*Create a default screen on the display*/disp->top_layer = lv_obj_create(NULL); /*Create top layer on the display*/disp->sys_layer = lv_obj_create(NULL); /*Create sys layer on the display*/lv_obj_remove_style_all(disp->top_layer);lv_obj_remove_style_all(disp->sys_layer);lv_obj_clear_flag(disp->top_layer, LV_OBJ_FLAG_CLICKABLE);lv_obj_clear_flag(disp->sys_layer, LV_OBJ_FLAG_CLICKABLE);lv_obj_set_scrollbar_mode(disp->top_layer, LV_SCROLLBAR_MODE_OFF);lv_obj_set_scrollbar_mode(disp->sys_layer, LV_SCROLLBAR_MODE_OFF);lv_obj_invalidate(disp->act_scr);disp_def = disp_def_tmp; /*Revert the default display*/if(disp_def == NULL) disp_def = disp; /*Initialize the default display*/lv_timer_ready(disp->refr_timer); /*Be sure the screen will be refreshed immediately on start up*/return disp;
}

lv_draw_sw_init_ctx 函数实现,其中包含了绘制 rect,line ,background 等等;

void lv_draw_sw_init_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
{LV_UNUSED(drv);lv_draw_sw_ctx_t * draw_sw_ctx = (lv_draw_sw_ctx_t *) draw_ctx;lv_memset_00(draw_sw_ctx, sizeof(lv_draw_sw_ctx_t));draw_sw_ctx->base_draw.draw_arc = lv_draw_sw_arc;draw_sw_ctx->base_draw.draw_rect = lv_draw_sw_rect;draw_sw_ctx->base_draw.draw_bg = lv_draw_sw_bg;draw_sw_ctx->base_draw.draw_letter = lv_draw_sw_letter;draw_sw_ctx->base_draw.draw_img_decoded = lv_draw_sw_img_decoded;draw_sw_ctx->base_draw.draw_line = lv_draw_sw_line;draw_sw_ctx->base_draw.draw_polygon = lv_draw_sw_polygon;draw_sw_ctx->base_draw.wait_for_finish = lv_draw_sw_wait_for_finish;draw_sw_ctx->blend = lv_draw_sw_blend_basic;
}

相关结构体和函数的关系如下所示:

如图所示, lv_disp_t 结构几乎包含了所有和显示相关的内容(直接或者间接的),包括 draw_buffer,flush_cb,绘制接口,refresh timer,和 LVGL 的几层基本 Layer;

注意

适配 lv_disp_drv_t 的时候,有些必须要指定的结构:

  • 指向初始化的 lv_disp_draw_buf_t 变量的 draw_buf 指针;
  • hor_res 显示器的水平分辨率(以像素为单位)
  • ver_res 显示器的垂直分辨率(以像素为单位)
  • flush_cb 一个回调函数,用于将缓冲区的内容复制到显示器的特定区域

lv_disp_flush_ready(&disp_drv) 需要在刷新准备好时调用。 LVGL 可能会以多个块呈现屏幕,因此多次调用 flush_cb。要查看当前是否是渲染的最后一个块,请使用 lv_disp_flush_is_last(&disp_drv)。

一些其他可选的回调,使处理单色、灰度或其他非标准 RGB 显示器更容易、更优化:

  • rounder_cb 四舍五入要重绘的区域的坐标。例如。 2x2 px 可以转换为 2x8。 如果显示控制器只能刷新具有特定高度或宽度的区域(单色显示器通常为 8 像素高度),则可以使用它。
  • set_px_cb 一个自定义函数来写入绘制缓冲区。如果显示器具有特殊的颜色格式,它可用于将像素更紧凑地存储在绘图缓冲区中。 (例如 1 位单色、2 位灰度等) 这样,lv_disp_draw_buf_t 中使用的缓冲区可以更小,以仅容纳给定区域大小所需的位数。请注意,使用set_px_cb 渲染比普通渲染慢。
  • monitor_cb 一个回调函数,告诉我们在多长时间内刷新了多少像素。当最后一个块被渲染并发送到显示器时调用。
  • clean_dcache_cb 用于清理与显示相关的任何缓存的回调。

LVGL (7) 显示对接相关推荐

  1. STM32移植LVGL+旋转编码器接口对接

    写在前面:本菜鸟结合了许多大佬的文章,成功实现了基于LVGL的GUI设计,小开心~浅浅记录一下!~ 本文以单片机STM32F103VET6为核心,利用ST7796芯片驱动分辨率为480*320的LCD ...

  2. LVGL (8) 绘制流程

    目录 Preview LVGL Tick Timer Handler lv_timer_exec 显式刷屏任务 _lv_disp_refr_timer 小结 Preview 整个 LVGL Frame ...

  3. LittleVGL踩坑指南03:Arduino下显示和触摸驱动配置(TFT_eSPI)

    文章版本7.10,LVGL更新极快,不同版本配置方法可能存在差异 前言   触摸搞了好几天,坑太多了,好在总算飞过来了.顺便把显示驱动也写了:提前说一下,我的硬件是2.4寸ILI9341驱动屏幕+XP ...

  4. LittleVGL (LVGL)干货入门教程四之制作和使用中文汉字字库

    LittleVGL (LVGL)干货入门教程四之制作和使用中文汉字字库 前言: 阅读前,请确保你至少拥有以下条件: 已实现显示API(教程一已实现, 链接:LittleVGL (LVGL)入门教程一之 ...

  5. 【LVGL】学习笔记--(1)Keil中嵌入式系统移植LVGL

    一 LVGL简介 最近emwin用的比较烦躁,同时被LVGL酷炫的界面吸引到了,所以准备换用LVGL试试水. LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式GUI所需的一切 ...

  6. 【LVGL应用开发--基于STM32】第1章 初识LVGL

    文章目录 前言 1.1 LVGL简介 1.2 LVGL移植要求 1.3 LVGL源码下载与介绍 前言 在实际应用中,我们时常需要制作 UI 界面来实现人机交互,简单的 UI 可以直接编写代码,但对于那 ...

  7. 分子对接教程 | (6) AutoDock对接操作与对接结果解读

    TCGA | GEO | 文献阅读 | 数据库 | 理论知识 R语言 | Bioconductor | 服务器与Linux 接前文: 分子对接教程 | (1) 软件安装准备 分子对接教程 | (2) ...

  8. 基于嵌入式linux的超声测距信息本地与远端显示的实现

    在学习linux驱动的过程中,正好碰到有个实际的超声模块测试的工作,顺便把学习的知识应用一下,实现了超声测距数据的本地与远端的显示,在此做一下记录. 开发环境:ubuntu20.04 硬件平台:正点原 ...

  9. lvgl8.2 img 图片显示

    1. lvgl 图片显示源 为了提供良好的图片显示灵活性,所以显示图像的来源可以是以下三种 代码中的一个变量(一个带有像素颜色数据的 C 数组). 存储在外部的文件(比如 SD 卡). 带有符号的文本 ...

最新文章

  1. 微信小程序实现画布自适应各种手机尺寸
  2. 教你实现双十一商品标签自动归类(附数据模板)
  3. Windows驱动开发 - 设备对象初步学习
  4. list排序_「肘后备急码」- C#对象List排序
  5. 面向对象之: 反射和双下方法
  6. getContentResolver().query()方法selection参数使用详解(转)
  7. 编写一个函数,计算下式当n=10和n=100的值。
  8. MySQL 事物隔离级别
  9. C++学习——默认构造函数
  10. 最后一届90后毕业了 超485万人在抖音发布毕业视频
  11. pandas 股票分析图
  12. VS2008中 没有QT的代码智能提示
  13. linux系统中find怎么用,Linux系统中查找命令find的使用方法(一)
  14. [转]《帮我买个单》
  15. 超美二次元响应式引导页源码
  16. 互联网从此没有 BAT,该来的还是来了!
  17. 哈·曼丁的故事(二)
  18. 机器学习的最佳入门学习资源
  19. 50岁能自学python吗_35岁了零基础自学Python可行吗?
  20. NLP指南 Your Guide to Natural Language Processing (NLP)

热门文章

  1. 智慧燃气系统基于GIS技术的搭建
  2. linux越狱amd卡代码,为Linux内核贡献27.5万行代码中:AMD意外泄漏下一代APU信息
  3. 1789 员工的直属部门
  4. Android 第三次作业 contentprovider与resolver
  5. 电子工程师入门宝典:最常用十大电子元器件-电子技术方案|电路图讲解
  6. GPON技术学习(一)--------GPON系统整体概况
  7. 排列组合常见解题方法
  8. python入门(四)小康小白
  9. 【数据结构】线性表的应用:稀疏一元多项式运算器
  10. 高德地图js-api简单使用