第一部分:文件结构概览
LVGL Display模块由三个核心文件组成:
- lv_display_private.h - 内部数据结构定义
- lv_display.h - 公共接口声明
- lv_display.c - 函数实现
第二部分:lv_display_private.h - 核心数据结构
cpp
struct _lv_display_t {
/*---------------------
* 分辨率相关参数
*--------------------*/
int32_t hor_res; /**< 水平分辨率(像素)*/
int32_t ver_res; /**< 垂直分辨率(像素)*/
int32_t physical_hor_res; /**< 完整/物理显示的水平分辨率,-1表示全屏模式 */
int32_t physical_ver_res; /**< 完整/物理显示的垂直分辨率,-1表示全屏模式 */
int32_t offset_x; /**< 相对于完整/物理显示的水平偏移量,0表示全屏模式 */
int32_t offset_y; /**< 相对于完整/物理显示的垂直偏移量,0表示全屏模式 */
uint32_t dpi; /**< 显示器的DPI(每英寸点数),默认值为LV_DPI_DEF */
/*---------------------
* 缓冲区管理
*--------------------*/
lv_draw_buf_t * buf_1; /**< 缓冲区1 */
lv_draw_buf_t * buf_2; /**< 缓冲区2 */
lv_draw_buf_t * buf_act; /**< 内部使用,当前活动缓冲区 */
lv_display_flush_cb_t flush_cb; /**< 必需:将内部缓冲区写入显示器,完成后必须调用lv_display_flush_ready() */
lv_display_flush_wait_cb_t flush_wait_cb; /**< 用于等待刷新完成,如果不设置则使用flushing标志 */
volatile int flushing; /**< 1:刷新正在进行中(不能是位域,避免IRQ清除时的读写修改问题) */
volatile int flushing_last; /**< 1:这是要刷新的最后一块(不能是位域,避免IRQ清除时的读写修改问题) */
volatile uint32_t last_area : 1; /**< 1:正在渲染最后区域 */
volatile uint32_t last_part : 1; /**< 1:正在渲染当前区域的最后部分 */
lv_display_render_mode_t render_mode; /**< 渲染模式 */
uint32_t antialiasing : 1; /**< 1:在此显示器上启用抗锯齿 */
uint32_t tile_cnt : 8; /**< 将显示缓冲区划分为这些数量的平铺 */
uint32_t rendering_in_progress : 1; /**< 1:当前屏幕渲染正在进行中 */
lv_color_format_t color_format; /**< 颜色格式 */
/*---------------------
* 无效区域管理(核心优化机制)
*--------------------*/
lv_area_t inv_areas[LV_INV_BUF_SIZE]; /**< 无效化(标记为重绘)的区域数组 */
uint8_t inv_area_joined[LV_INV_BUF_SIZE]; /**< 无效区域合并状态数组 */
uint32_t inv_p; /**< 无效区域指针 */
int32_t inv_en_cnt; /**< 无效使能计数器 */
lv_ll_t sync_areas; /**< 双缓冲同步区域(上次刷新期间重绘) */
lv_draw_buf_t _static_buf1; /**< 当用户传入原始缓冲区作为显示绘制缓冲区时使用 */
lv_draw_buf_t _static_buf2; /**< 第二个静态缓冲区 */
/*---------------------
* 层系统管理
*--------------------*/
lv_layer_t * layer_head; /**< 层头指针 */
void (*layer_init)(lv_display_t * disp, lv_layer_t * layer); /**< 层初始化函数指针 */
void (*layer_deinit)(lv_display_t * disp, lv_layer_t * layer); /**< 层反初始化函数指针 */
/*---------------------
* 屏幕管理
*--------------------*/
lv_obj_t ** screens; /**< 屏幕对象数组 */
lv_obj_t * sys_layer; /**< 系统层(@see lv_display_get_layer_sys)*/
lv_obj_t * top_layer; /**< 顶层(@see lv_display_get_layer_top)*/
lv_obj_t * act_scr; /**< 当前在此显示器上活动的屏幕 */
lv_obj_t * bottom_layer; /**< 底层(@see lv_display_get_layer_bottom)*/
lv_obj_t * prev_scr; /**< 前一个屏幕,用于屏幕动画 */
lv_obj_t * scr_to_load; /**< 在lv_screen_load_anim中准备加载的屏幕 */
uint32_t screen_cnt; /**< 屏幕数量 */
uint8_t draw_prev_over_act : 1; /**< 1:在活动屏幕上绘制前一个屏幕 */
uint8_t del_prev : 1; /**< 1:屏幕加载动画完成后自动删除前一个屏幕 */
/*---------------------
* 其他功能
*--------------------*/
void * driver_data; /**< 自定义驱动数据 */
void * user_data; /**< 自定义用户数据 */
lv_event_list_t event_list; /**< 事件列表 */
uint32_t rotation : 3; /**< lv_display_rotation_t的元素(旋转角度)*/
lv_theme_t * theme; /**< 分配给屏幕的主题 */
lv_timer_t * refr_timer; /**< 定期检查脏区域并刷新它们的定时器 */
uint32_t last_activity_time;/**< 此显示器上最后活动的时间 */
lv_area_t refreshed_area; /**< 正在刷新的区域 */
#if LV_USE_PERF_MONITOR
/* 性能监控相关(可选)*/
lv_obj_t * perf_label;
lv_sysmon_backend_data_t perf_sysmon_backend;
lv_sysmon_perf_info_t perf_sysmon_info;
#endif
#if LV_USE_MEM_MONITOR
/* 内存监控相关(可选)*/
lv_obj_t * mem_label;
#endif
};
第三部分:lv_display.h - 公共接口定义
1. 显示旋转枚举:
cpp
typedef enum {
LV_DISPLAY_ROTATION_0 = 0, /**< 不旋转(0度)*/
LV_DISPLAY_ROTATION_90, /**< 顺时针旋转90度 */
LV_DISPLAY_ROTATION_180, /**< 顺时针旋转180度 */
LV_DISPLAY_ROTATION_270 /**< 顺时针旋转270度 */
} lv_display_rotation_t;
2. 渲染模式枚举:
cpp
typedef enum {
/**
* 使用缓冲区渲染屏幕的较小部分。
* 这样缓冲区可以比显示器小以节省RAM。建议至少使用1/10屏幕大小的缓冲区。
*/
LV_DISPLAY_RENDER_MODE_PARTIAL,
/**
* 缓冲区必须是屏幕大小,LVGL将在缓冲区的正确位置渲染。
* 这样缓冲区始终包含整个图像。只有更改的区域会被更新。
* 使用2个缓冲区时,缓冲区内容会自动保持同步,在flush_cb中只需要地址更改。
*/
LV_DISPLAY_RENDER_MODE_DIRECT,
/**
* 即使只改变了一个像素也始终重绘整个屏幕。
* 使用2个缓冲区时,在flush_cb中只需要地址更改。
*/
LV_DISPLAY_RENDER_MODE_FULL,
} lv_display_render_mode_t;
3. 屏幕加载动画枚举:
cpp
typedef enum {
LV_SCR_LOAD_ANIM_NONE, /**< 无动画 */
LV_SCR_LOAD_ANIM_OVER_LEFT, /**< 从左侧滑入 */
LV_SCR_LOAD_ANIM_OVER_RIGHT, /**< 从右侧滑入 */
LV_SCR_LOAD_ANIM_OVER_TOP, /**< 从顶部滑入 */
LV_SCR_LOAD_ANIM_OVER_BOTTOM, /**< 从底部滑入 */
LV_SCR_LOAD_ANIM_MOVE_LEFT, /**< 向左移动 */
LV_SCR_LOAD_ANIM_MOVE_RIGHT, /**< 向右移动 */
LV_SCR_LOAD_ANIM_MOVE_TOP, /**< 向上移动 */
LV_SCR_LOAD_ANIM_MOVE_BOTTOM, /**< 向下移动 */
LV_SCR_LOAD_ANIM_FADE_IN, /**< 淡入 */
LV_SCR_LOAD_ANIM_FADE_OUT, /**< 淡出 */
LV_SCR_LOAD_ANIM_OUT_LEFT, /**< 向左滑出 */
LV_SCR_LOAD_ANIM_OUT_RIGHT, /**< 向右滑出 */
LV_SCR_LOAD_ANIM_OUT_TOP, /**< 向上滑出 */
LV_SCR_LOAD_ANIM_OUT_BOTTOM, /**< 向下滑出 */
} lv_screen_load_anim_t;
核心函数接口分类:
1. 显示设备管理:
cpp
/**
* 创建新的显示设备
* @param hor_res 水平分辨率(像素)
* @param ver_res 垂直分辨率(像素)
* @return 显示对象指针,出错时返回NULL
*/
lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res);
/**
* 删除显示设备
* @param disp 显示指针
*/
void lv_display_delete(lv_display_t * disp);
/**
* 设置默认显示设备,新屏幕将默认创建在此显示设备上
* @param disp 显示指针
*/
void lv_display_set_default(lv_display_t * disp);
/**
* 获取默认显示设备
* @return 默认显示指针
*/
lv_display_t * lv_display_get_default(void);
2. 分辨率管理:
cpp
/**
* 设置显示分辨率,会发送LV_EVENT_RESOLUTION_CHANGED事件
* @param disp 显示指针
* @param hor_res 新的水平分辨率
* @param ver_res 新的垂直分辨率
*/
void lv_display_set_resolution(lv_display_t * disp, int32_t hor_res, int32_t ver_res);
/**
* 设置显示旋转角度,LVGL会自动交换水平和垂直分辨率
* @param disp 显示指针(NULL使用默认显示)
* @param rotation LV_DISPLAY_ROTATION_0/90/180/270
*/
void lv_display_set_rotation(lv_display_t * disp, lv_display_rotation_t rotation);
/**
* 设置DPI(每英寸点数)
* @param disp 显示指针
* @param dpi 新的DPI
*/
void lv_display_set_dpi(lv_display_t * disp, int32_t dpi);
3. 缓冲区管理:
cpp
/**
* 设置显示缓冲区
* @param disp 显示指针
* @param buf1 第一个缓冲区
* @param buf2 第二个缓冲区(NULL表示单缓冲)
* @param buf_size 缓冲区大小(像素数)
* @param color_format 颜色格式
*/
void lv_display_set_buffers(lv_display_t * disp, void * buf1, void * buf2,
uint32_t buf_size, lv_color_format_t color_format);
/**
* 设置刷新回调函数
* @param disp 显示指针
* @param flush_cb 刷新回调函数
*/
void lv_display_set_flush_cb(lv_display_t * disp, lv_display_flush_cb_t flush_cb);
4. 屏幕管理:
cpp
/**
* 在默认显示设备上加载屏幕
* @param scr 屏幕指针
*/
void lv_screen_load(struct _lv_obj_t * scr);
/**
* 带动画切换屏幕
* @param scr 新屏幕指针
* @param anim_type 动画类型(lv_screen_load_anim_t)
* @param time 动画时间
* @param delay 延迟时间
* @param auto_del true:自动删除旧屏幕
*/
void lv_screen_load_anim(lv_obj_t * scr, lv_screen_load_anim_t anim_type,
uint32_t time, uint32_t delay, bool auto_del);
/**
* 获取默认显示设备的活动屏幕
* @return 活动屏幕指针
*/
lv_obj_t * lv_screen_active(void);
第四部分:lv_display.c - 核心函数实现
核心函数实现详解
1. lv_display_create - 显示设备创建函数(最关键)
函数原型:
cpp
lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res)
实现原理详解:
cpp
lv_display_t * lv_display_create(int32_t hor_res, int32_t ver_res)
{
// 1. 分配显示设备内存
lv_display_t * disp = lv_ll_ins_head(disp_ll_p);
LV_ASSERT_MALLOC(disp);
if(!disp) return NULL;
// 2. 清零内存,确保所有成员初始值为0
lv_memzero(disp, sizeof(lv_display_t));
// 3. 设置基本分辨率参数
disp->hor_res = hor_res; // 水平分辨率
disp->ver_res = ver_res; // 垂直分辨率
disp->physical_hor_res = -1; // 物理分辨率设为-1(全屏模式)
disp->physical_ver_res = -1;
disp->offset_x = 0; // 偏移量设为0
disp->offset_y = 0;
disp->antialiasing = LV_COLOR_DEPTH > 8 ? 1 : 0; // 根据颜色深度设置抗锯齿
disp->dpi = LV_DPI_DEF; // 默认DPI
disp->color_format = LV_COLOR_FORMAT_NATIVE; // 原生颜色格式
// 4. 设置渲染分块数量(用于多线程渲染)
#if defined(LV_DRAW_SW_DRAW_UNIT_CNT) && (LV_DRAW_SW_DRAW_UNIT_CNT != 0)
disp->tile_cnt = LV_DRAW_SW_DRAW_UNIT_CNT;
#else
disp->tile_cnt = 1; // 默认单线程
#endif
// 5. 初始化层系统
disp->layer_head = lv_malloc(sizeof(lv_layer_t));
LV_ASSERT_MALLOC(disp->layer_head);
if(disp->layer_head == NULL) return NULL;
lv_layer_init(disp->layer_head);
// 6. 设置层缓冲区参数
if(disp->layer_init) disp->layer_init(disp, disp->layer_head);
disp->layer_head->buf_area.x1 = 0;
disp->layer_head->buf_area.y1 = 0;
disp->layer_head->buf_area.x2 = hor_res - 1;
disp->layer_head->buf_area.y2 = ver_res - 1;
disp->layer_head->color_format = disp->color_format;
// 7. 初始化无效区域管理
disp->inv_en_cnt = 1; // 使能无效区域计数
disp->last_activity_time = lv_tick_get(); // 记录创建时间
// 8. 初始化同步区域链表(双缓冲用)
lv_ll_init(&disp->sync_areas, sizeof(lv_area_t));
// 9. 临时设置默认显示,用于创建默认屏幕
lv_display_t * disp_def_tmp = disp_def;
disp_def = disp;
// 10. 创建刷新定时器(核心机制)
disp->refr_timer = lv_timer_create(lv_display_refr_timer, LV_DEF_REFR_PERIOD, disp);
LV_ASSERT_MALLOC(disp->refr_timer);
if(disp->refr_timer == NULL) {
lv_free(disp);
return NULL;
}
// 11. 初始化主题系统
#if LV_USE_THEME_DEFAULT
if(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
// 12. 创建四层结构(从底到顶)
disp->bottom_layer = lv_obj_create(NULL); // 底层
disp->act_scr = lv_obj_create(NULL); // 默认屏幕
disp->top_layer = lv_obj_create(NULL); // 顶层
disp->sys_layer = lv_obj_create(NULL); // 系统层
// 13. 清除样式,确保纯净状态
lv_obj_remove_style_all(disp->bottom_layer);
lv_obj_remove_style_all(disp->top_layer);
lv_obj_remove_style_all(disp->sys_layer);
// 14. 设置不可点击(顶层和系统层)
lv_obj_remove_flag(disp->top_layer, LV_OBJ_FLAG_CLICKABLE);
lv_obj_remove_flag(disp->sys_layer, LV_OBJ_FLAG_CLICKABLE);
// 15. 禁用滚动条
lv_obj_set_scrollbar_mode(disp->bottom_layer, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_scrollbar_mode(disp->top_layer, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_scrollbar_mode(disp->sys_layer, LV_SCROLLBAR_MODE_OFF);
// 16. 使能默认屏幕无效区域(触发首次刷新)
lv_obj_invalidate(disp->act_scr);
// 17. 恢复默认显示设置
disp_def = disp_def_tmp;
if(disp_def == NULL) disp_def = disp;
// 18. 添加刷新请求事件监听
lv_display_add_event_cb(disp, disp_event_cb, LV_EVENT_REFR_REQUEST, NULL);
// 19. 立即准备刷新定时器(确保启动时立即刷新)
lv_timer_ready(disp->refr_timer);
// 20. 初始化性能监控(如启用)
#if LV_USE_PERF_MONITOR
lv_sysmon_show_performance(disp);
#endif
return disp;
}
2. lv_display_set_buffers - 缓冲区设置函数(核心)
函数原型:
cpp
void lv_display_set_buffers(lv_display_t * disp, void * buf1, void * buf2,
uint32_t buf_size, lv_display_render_mode_t render_mode)
实现原理详解:
cpp
void lv_display_set_buffers(lv_display_t * disp, void * buf1, void * buf2, uint32_t buf_size,
lv_display_render_mode_t render_mode)
{
// 1. 参数验证
LV_ASSERT_MSG(buf1 != NULL, "Null buffer"); // 第一个缓冲区不能为空
// 2. 获取显示参数
lv_color_format_t cf = lv_display_get_color_format(disp);
uint32_t w = lv_display_get_horizontal_resolution(disp);
uint32_t h = lv_display_get_vertical_resolution(disp);
LV_ASSERT_MSG(w != 0 && h != 0, "display resolution is 0");
// 3. 内存对齐检查(硬件要求)
LV_ASSERT_FORMAT_MSG(buf1 == lv_draw_buf_align(buf1, cf),
"buf1 is not aligned: %p", buf1);
LV_ASSERT_FORMAT_MSG(buf2 == NULL || buf2 == lv_draw_buf_align(buf2, cf),
"buf2 is not aligned: %p", buf2);
// 4. 计算行跨度(考虑对齐和颜色格式)
uint32_t stride = lv_draw_buf_width_to_stride(w, cf);
// 5. 根据渲染模式处理缓冲区大小
if(render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
/* 部分模式:根据缓冲区大小计算可渲染的高度 */
h = buf_size / stride;
LV_ASSERT_MSG(h != 0, "the buffer is too small");
}
else {
/* 全屏/直接模式:需要全屏大小的缓冲区 */
LV_ASSERT_FORMAT_MSG(stride * h <= buf_size,
"%s mode requires screen sized buffer(s)",
render_mode == LV_DISPLAY_RENDER_MODE_FULL ? "FULL" : "DIRECT");
}
// 6. 初始化静态缓冲区结构
lv_draw_buf_init(&disp->_static_buf1, w, h, cf, stride, buf1, buf_size);
lv_draw_buf_init(&disp->_static_buf2, w, h, cf, stride, buf2, buf_size);
// 7. 设置显示缓冲区指针
lv_display_set_draw_buffers(disp, &disp->_static_buf1,
buf2 ? &disp->_static_buf2 : NULL);
// 8. 设置渲染模式
lv_display_set_render_mode(disp, render_mode);
}
3. lv_screen_load_anim - 屏幕切换动画函数
函数原型:
cpp
void lv_screen_load_anim(lv_obj_t * scr, lv_screen_load_anim_t anim_type,
uint32_t time, uint32_t delay, bool auto_del)
实现原理详解:
cpp
void lv_screen_load_anim(lv_obj_t * scr, lv_screen_load_anim_t anim_type, uint32_t time, uint32_t delay,
bool auto_del)
{
// 1. 获取显示设备和当前屏幕
lv_display_t * d = lv_obj_get_display(scr);
if(d == NULL) return;
lv_obj_t * prev_scr = d->act_scr;
// 2. 设置动画参数
d->scr_to_load = scr;
d->del_prev = auto_del;
// 3. 根据动画类型设置不同的动画参数
switch(anim_type) {
case LV_SCR_LOAD_ANIM_NONE:
// 无动画,直接加载
scr_load_internal(scr);
return;
case LV_SCR_LOAD_ANIM_OVER_LEFT:
case LV_SCR_LOAD_ANIM_OVER_RIGHT:
case LV_SCR_LOAD_ANIM_OVER_TOP:
case LV_SCR_LOAD_ANIM_OVER_BOTTOM:
// 覆盖动画:新屏幕从边缘滑入
d->draw_prev_over_act = false;
break;
case LV_SCR_LOAD_ANIM_MOVE_LEFT:
case LV_SCR_LOAD_ANIM_MOVE_RIGHT:
case LV_SCR_LOAD_ANIM_MOVE_TOP:
case LV_SCR_LOAD_ANIM_MOVE_BOTTOM:
// 移动动画:两个屏幕同时移动
d->draw_prev_over_act = true;
break;
case LV_SCR_LOAD_ANIM_FADE_IN:
// 淡入动画
lv_obj_set_style_opa(scr, LV_OPA_TRANSP, 0); // 初始透明
break;
}
// 4. 创建动画对象
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, scr);
lv_anim_set_time(&a, time);
lv_anim_set_delay(&a, delay);
lv_anim_set_start_cb(&a, scr_load_anim_start); // 动画开始回调
// 5. 根据动画类型设置动画函数
switch(anim_type) {
case LV_SCR_LOAD_ANIM_OVER_LEFT:
case LV_SCR_LOAD_ANIM_MOVE_LEFT:
lv_anim_set_values(&a, lv_display_get_horizontal_resolution(d), 0);
lv_anim_set_exec_cb(&a, set_x_anim);
break;
case LV_SCR_LOAD_ANIM_OVER_RIGHT:
case LV_SCR_LOAD_ANIM_MOVE_RIGHT:
lv_anim_set_values(&a, -lv_display_get_horizontal_resolution(d), 0);
lv_anim_set_exec_cb(&a, set_x_anim);
break;
case LV_SCR_LOAD_ANIM_FADE_IN:
lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_COVER);
lv_anim_set_exec_cb(&a, opa_scale_anim);
break;
}
// 6. 设置动画完成回调
lv_anim_set_ready_cb(&a, scr_anim_completed);
// 7. 启动动画
lv_anim_start(&a);
}
4. 刷新等待机制(flush_wait_cb)
实现原理:
cpp
// 在刷新过程中,LVGL使用以下机制等待刷新完成:
// 1. 设置刷新状态
disp->flushing = 1; // 开始刷新
disp->flushing_last = 1; // 标记为最后一块
// 2. 调用用户提供的flush_cb
if(disp->flush_cb) {
disp->flush_cb(disp, area, color_p);
}
// 3. 等待刷新完成(两种机制)
if(disp->flush_wait_cb) {
// 方式1:用户自定义等待机制(推荐)
disp->flush_wait_cb(disp);
}
else {
// 方式2:轮询flushing标志
while(disp->flushing) {
// 等待flushing被lv_display_flush_ready()清除
}
}
// 4. 用户在中断中调用lv_display_flush_ready()
void lv_display_flush_ready(lv_display_t * disp)
{
disp->flushing = 0; // 清除刷新标志
}
核心数据结构与交互机制
1. 显示状态管理
- 四层结构:底层 → 屏幕层 → 系统层 → 顶层
- 状态同步:使用volatile确保多线程安全
- 内存管理:静态缓冲区 + 动态分配
2. 无效区域管理(性能优化核心)
cpp
// 无效区域环形缓冲区
lv_area_t inv_areas[LV_INV_BUF_SIZE];
uint8_t inv_area_joined[LV_INV_BUF_SIZE]; // 合并状态
uint32_t inv_p; // 当前位置指针
// 工作流程:
// 1. 对象改变 → 标记区域无效
// 2. 区域合并 → 减少重绘次数
// 3. 刷新时 → 只重绘无效区域
// 4. 双缓冲 → 同步区域管理
3. 渲染流程
cpp
// 1. 定时器触发 lv_display_refr_timer()
// 2. 检查无效区域 inv_areas[]
// 3. 按渲染模式处理:
// - PARTIAL: 部分重绘
// - DIRECT: 直接渲染到缓冲区
// - FULL: 全屏重绘
// 4. 调用flush_cb写入硬件
// 5. 等待flush_ready()完成