在多线程并发使用的时候,总是在运行过程中莫名其妙的 crash,后面才意识到 LVGL 本身不支持并发,加了互斥锁解决了。

在引入矢量字库时(freetype),又有启动时会概率性 crash 的问题,每次 crash 的地方还不一样,这个坑爬了好久,甚至都怀疑是不是 freetype 有问题,还好总算找到方法解决了,不过还不清楚具体原因,先记录。

以下以 linux 平台下 C++ 语言的方式举例说明,理解方法即可。

一、多线程并发使用

多线程并发使用时,需要在以下两种情况加上互斥锁:

1. 移植时在调用 lv_tick_inc(1) 和 lv_task_handler() 时上锁

// 线程函数
void LvglDrive::prvLvTickTask(LvglDrive *context)
{
	while(context->th_tasktick_runing)
	{
		context->m_lvmutex.lock();
		lv_tick_inc(1);
		context->m_lvmutex.unlock();
		usleep(1 * 1000);
	}
	return ;	
}

// 线程函数
void LvglDrive::prvLvHandlerTask(LvglDrive *context)
{
	while(context->th_tasktick_runing)
	{	
		context->m_lvmutex.lock();
		lv_task_handler();
		context->m_lvmutex.unlock();
		usleep(5 * 1000);
	}
	return ;
}

2. 操作控件时上锁

void GuiManager::toast_show(const char *str, unsigned int timeout)
{
	LvglDrive::getInstance()->lock();
	lv_timer_reset(lv_timer_toast);
	lv_timer_pause(lv_timer_toast);
	if(timeout){
		lv_timer_set_period(lv_timer_toast, timeout);
		lv_timer_resume(lv_timer_toast);
	}
	lv_label_set_text(lv_obj_toast, str);
	lv_obj_set_style_pad_top(lv_obj_toast, 5, 0);
	lv_obj_set_style_pad_left(lv_obj_toast, 15, 0);
	lv_obj_set_style_pad_right(lv_obj_toast, 15, 0);
	lv_obj_set_style_pad_bottom(lv_obj_toast, 8, 0);
	LvglDrive::getInstance()->unlock();
	return ;
}

void GuiManager::toast_hide()
{
	LvglDrive::getInstance()->lock();
	lv_timer_reset(lv_timer_toast);
	lv_timer_pause(lv_timer_toast);
	lv_label_set_text(lv_obj_toast, "");
	lv_obj_set_style_pad_top(lv_obj_toast, 0, 0);
	lv_obj_set_style_pad_left(lv_obj_toast, 0, 0);
	lv_obj_set_style_pad_right(lv_obj_toast, 0, 0);
	lv_obj_set_style_pad_bottom(lv_obj_toast, 0, 0);
	LvglDrive::getInstance()->unlock();
	return ;
}

二、引入矢量字库(freetype)

需要先把所有需要用到的 UI 控件都创建和初始化完成之后,再去创建两个线程去调用 lv_tick_inc(1) 和 lv_task_handler(),顺序如下:

1、初始化 framebuffer、lv_init()、lv_freetype_init()、lv_port_disp_init()

2、初始化所需要的 UI 控件

3、创建线程调用  lv_task_handler() 、lv_tick_inc(1) 

4、按需要设置 UI 控件

bool GuiManager::running()
{
	// 初始化 framebuffer、lv_init()、lv_freetype_init()、lv_port_disp_init()
	if(LvglDrive::getInstance()->init() == false){
		eprintf("lvgl driver running failed!!!\n");
		return false;
	}
	
	...
	
	static lv_ft_info_t ft_info_18;
    ft_info_18.name = RES_FONT_PATH;
    ft_info_18.weight = 18;
    ft_info_18.style = FT_FONT_STYLE_NORMAL | FT_FONT_STYLE_BOLD;
    lv_ft_font_init(&ft_info_18);
	
	static lv_style_t style_18;
    lv_style_init(&style_18);
    lv_style_set_text_font(&style_18, ft_info_18.font);
	lv_style_set_radius(&style_18, LV_RADIUS_CIRCLE);
	
	lv_obj_toast = lv_label_create(lv_layer_sys());
	lv_obj_add_style(lv_obj_toast, &style_18, 0);
	lv_obj_set_style_bg_opa(lv_obj_toast, LV_OPA_30, 0);
	lv_obj_set_style_bg_color(lv_obj_toast, lv_color_black(), 0);
	lv_obj_set_style_text_color(lv_obj_toast, lv_color_white(), 0);
	lv_label_set_text(lv_obj_toast, "");
	lv_obj_set_style_pad_top(lv_obj_toast, 0, 0);
	lv_obj_set_style_pad_left(lv_obj_toast, 0, 0);
	lv_obj_set_style_pad_right(lv_obj_toast, 0, 0);
	lv_obj_set_style_pad_bottom(lv_obj_toast, 0, 0);
	lv_obj_align(lv_obj_toast, LV_ALIGN_CENTER, 0, 50);
	
	lv_timer_toast = lv_timer_create(on_timer_lv_toast_refresh, 500, this);
	lv_timer_pause(lv_timer_toast);
	
	...

	// 创建线程:prvLvTickTask()、prvLvHandlerTask()
	LvglDrive::getInstance()->run();
	return true;
}