LVGL源码研究 - 初探

背景

在上一篇中成功运行了LVGL自带的Demo,这次从Demo的程序入口进行初步分析。

入口

主函数

int main(int argc, char **argv)
	// lvgl初始化
	lv_init();
	
	// 硬件初始化:包括显示设备、输入设备
	hal_init();

	// 选择一个Demo运行
	lv_demo_widgets();
	
	// 周期性运行lv_timer_handler接口,该接口内部主要处理:绘制、获取输入信息等
	while (1)
        lv_timer_handler();
        usleep(5 * 1000);

lvgl具体初始化

lv_init
    if(lv_initialized)
        return;

	// 初始化一个内存池,通过tlsf方式
    lv_mem_init();

	// 定时器模块初始化,_lv_timer_ll
	_lv_timer_core_init();
	
	// 文件系统初始化,_lv_fsdrv_ll
	_lv_fs_init();

	// 动画模块初始化,_lv_anim_ll
	_lv_anim_core_init();

	// 组初始化,_lv_group_ll
	_lv_group_init();

	// 样式初始化,_lv_obj_style_trans_ll
	_lv_obj_style_init();

	// 显示和输入设备初始化
	_lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
    _lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));

	// 图片解码初始化,_lv_img_decoder_ll
	_lv_img_decoder_init();

	// utf-8编码和大小端测试
	
	// 其他模块初始化
	lv_extra_init();

	lv_initialized = true;

hal_init初始化

static void hal_init(void)
    // 创建窗口,模拟显示设备,底层调用的SDL
	monitor_init();
		monitor_sdl_init();
		lv_timer_create(sdl_event_handler, 10, NULL);
	
	// 创建tick线程
	SDL_CreateThread(tick_thread, "tick", NULL);

	// 创建显示buffer,该大小可以不必是整个屏幕大小,如该例子就是设置的100行大小
	lv_disp_draw_buf_init(&disp_buf1, buf1_1, buf1_2, MONITOR_HOR_RES * 100);
	
	// 创建显示驱动并注册刷新回调函数
	disp_drv.flush_cb = monitor_flush;
	lv_disp_drv_register(&disp_drv);

	// 创建鼠标设备驱动并注册鼠标读取驱动函数
	indev_drv_1.read_cb = mouse_read;
	lv_indev_drv_register(&indev_drv_1);

// 该线程为lvgl提供时间基准,如果在Linux这种比较完善的OS上,可以不用专门开个线程调用lv_tick_inc,可以设置LV_TICK_CUSTOM
static int tick_thread(void *data)
    while (1)
        SDL_Delay(5);
		lv_tick_inc(5);

数据结构

链表是LVGL中最重要的数据结构,简单说一下。具体接口在lv_ll.c中

// 一个链表元素类型
typedef uint8_t lv_ll_node_t;
typedef struct {
    
    
    uint32_t n_size;
    lv_ll_node_t * head;
    lv_ll_node_t * tail;
} lv_ll_t;

// 链表初始化
void _lv_ll_init(lv_ll_t * ll_p, uint32_t node_size);
// 增加元素
void * _lv_ll_ins_head(lv_ll_t * ll_p);
void * _lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act);
void * _lv_ll_ins_tail(lv_ll_t * ll_p);
// 修改列表
void _lv_ll_remove(lv_ll_t * ll_p, void * node_p);
void _lv_ll_clear(lv_ll_t * ll_p);
void _lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node, bool head);
void _lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after);
// 获取元素
void * _lv_ll_get_head(const lv_ll_t * ll_p);
void * _lv_ll_get_tail(const lv_ll_t * ll_p);
void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act);
void * _lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act);
uint32_t _lv_ll_get_len(const lv_ll_t * ll_p);
bool _lv_ll_is_empty(lv_ll_t * ll_p);

链表元素及操作本身没什么好说的,具体说一下,定义为链表类型的全局变量的定义方式。具体在lv_gc.h,以_lv_timer_ll为例

#define LV_DISPATCH(f, t, n)            f(t, n)

#define LV_ITERATE_ROOTS(f) \
    LV_DISPATCH(f, lv_ll_t, _lv_timer_ll) /*Linked list to store the lv_timers*/

#define LV_EXTERN_ROOT(root_type, root_name) extern root_type root_name;
LV_ITERATE_ROOTS(LV_EXTERN_ROOT)

首先,需要明确:编译时会执行LV_ITERATE_ROOTS(LV_EXTERN_ROOT)这行代码,不需要手动调用。展开步骤如下

LV_DISPATCH(f, lv_ll_t, _lv_timer_ll) -->  
	LV_DISPATCH(LV_EXTERN_ROOT, lv_ll_t, _lv_timer_ll)  -->  
		LV_EXTERN_ROOT(lv_ll_t, _lv_timer_ll)  -->  
    		extern lv_ll_t _lv_timer_ll;

最终的效果是,定义了一个类型为lv_ll_t的全局变量_lv_timer_ll,其他变量的定义类似。

使用

  • 从GitHub下载最新lvgl源码
git clone https://github.com/lvgl/lvgl.git
  • 拷贝下载好的lvgl目录到自己的工程中,如test_prj
  • lvgl/lv_conf_template.h拷贝为lv_conf.h,并打开#if 0,使能配置内容。
  • 在需要使用lvgl接口函数的地方,包含lvgl/lvgl.h头文件
  • 在定时器或者线程中周期调用lv_tick_inc(x),x应该为1-10ms
  • 调用lv_init()
  • 创建绘制缓冲区
static lv_disp_darw_buf_t draw_buf;
static lv_color_t buf1[DISP_HOR_RES * DISP_VER_RES / 10];                        /*Declare a buffer for 1/10 screen size*/
lv_disp_draw_buf_init(&draw_buf, buf1, NULL, MY_DISP_HOR_RES * MY_DISP_VER_SER / 10);  /*Initialize the display buffer.*/
  • 注册显示驱动,用于拷贝渲染信息到显示区域
lv_disp_drv_t disp_drv;               /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv);          /*Basic initialization*/
disp_drv.flush_cb = my_disp_flush;    /*Set your driver function*/
disp_drv.buffer = &draw_buf;          /*Assign the buffer to the display*/
disp_drv.hor_res = MY_DISP_HOR_RES;   /*Set the horizontal resolution of the display*/
disp_drv.hor_res = MY_DISP_VER_RES;   /*Set the verizontal resolution of the display*/
lv_disp_drv_register(&disp_drv);      /*Finally register the driver*/

void my_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
    
    
    int32_t x, y;
    /*It's a very slow but simple implementation.
     *`set_pixel` needs to be written by you to a set pixel on the screen*/
    for(y = area->y1; y <= area->y2; y++) {
    
    
        for(x = area->x1; x <= area->x2; x++) {
    
    
            set_pixel(x, y, *color_p);
            color_p++;
        }
    }

    lv_disp_flush_ready(disp);         /* Indicate you are ready with the flushing*/
}

  • 注册输入驱动,用于读取输入设备
lv_indev_drv_t indev_drv;                  /*Descriptor of a input device driver*/
lv_indev_drv_init(&indev_drv);             /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER;    /*Touch pad is a pointer-like device*/
indev_drv.read_cb = my_touchpad_read;      /*Set your driver function*/
lv_indev_drv_register(&indev_drv);         /*Finally register the driver*/

bool my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data)
{
    
    
    /*`touchpad_is_pressed` and `touchpad_get_xy` needs to be implemented by you*/
    if(touchpad_is_pressed()) {
    
    
      data->state = LV_INDEV_STATE_PRESSED;
      touchpad_get_xy(&data->point.x, &data->point.y);
    } else {
    
    
      data->state = LV_INDEV_STATE_RELEASED;
    }
 
}
  • 在主循环或者线程中每隔几毫秒,周期性调用lv_timer_handler(),该函数内将会做:重绘显示区域;读取输入设备信息;展示动画等。

总结:

  • 下载lvgl源码并根据需要修改lv_conf.h
  • 调用lv_init()初始化
  • 调用设备自身的驱动,如LCD显示设备,Touch输入设备
  • 注册显示和输入设备的驱动到lvgl,用于嫁接lvgl和不同硬件设备之间的桥梁
  • 在中断函数或者线程中周期性调用lv_tick_inc(x),来告诉消逝时间,类似于系统时间。主要通过调用lv_tick_get()来获取当前tick数量,即认为当前系统时间,用其相对值。
  • 在线程中周期性调用lv_timer_handler(),来处理lvgl相关任务,如需要重绘界面等。这是lvgl核心,真正动起来的地方。绘制动作,读取用户操作等。GUI的本质也就是根据用户操作,绘制相应内容。

注意事项

LVGL是非线程安全的,具体可以参考LVGL文档。我是基于V8研究的。
在这里插入图片描述
在这里插入图片描述

おすすめ

転載: blog.csdn.net/donglicaiju76152/article/details/118520906