一、函数介绍:
首先我们要知道lv_label_set_text_fmt函数的作用,他是给LVGL中的标签设置文本用的:
/**
* Set a new formatted text for a label. Memory will be allocated to store the text by the label.
* @param obj pointer to a label object
* @param fmt `printf`-like format
* @example lv_label_set_text_fmt(label1, "%d user", user_num);
*/
void lv_label_set_text_fmt(lv_obj_t * obj, const char * fmt, ...) LV_FORMAT_ATTRIBUTE(2, 3);
//函数实现
void lv_label_set_text_fmt(lv_obj_t * obj, const char * fmt, ...)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(fmt);
lv_obj_invalidate(obj);
lv_label_t * label = (lv_label_t *)obj;
/*If text is NULL then refresh*/
if(fmt == NULL) {
lv_label_refr_text(obj);
return;
}
if(label->text != NULL && label->static_txt == 0) {
lv_mem_free(label->text);
label->text = NULL;
}
va_list args;
va_start(args, fmt);
label->text = _lv_txt_set_text_vfmt(fmt, args);
va_end(args);
label->static_txt = 0; /*Now the text is dynamically allocated*/
lv_label_refr_text(obj);
}
由函数实现可知,在使用该函数时,会分配一个空间,所以要确保设备有足够的内存以供使用。
二、问题描述:
在我最近的一个项目中,使用到了这个函数,用来更新标签上的文本,大概流程就是首先接收信号,接收到了数据更新的信号后,会调用这个函数,进行数据的更新:
// 1.发送信号:
lv_event_send(obj, LV_EVENT_USER_REFRESH_RESULT, dataTypedef); // 自定义信号,用于更新数据,参数为一个结构体数组
// 2.在控件的回调函数中接收该信号,并进行处理
static void RefreDataCbk(lv_event_t *event)
{
lv_event_code_t code = lv_event_get_code(event);
lv_obj_t *obj = event->current_target;
if(code == LV_EVENT_USER_REFRESH_RESULT) // 接收到信号
{
DataTypedef *data = (DataTypedef *)event->param
for(u8 i = 0; i < 5; i++)
{
if(data[i]->param1[0] == '\0')
break;
lv_label_set_text_fmt(Label1[i], "%s", data[i]->param1);
lv_obj_clear_flag(Label1[i], LV_OBJ_FLAG_HIDDEN);
lv_label_set_text_fmt(Label2[i], "%d", data[i]->param2);
lv_obj_clear_flag(Label2[i], LV_OBJ_FLAG_HIDDEN);
lv_label_set_text_fmt(Label3[i], "%llu", data[i]->param3);
lv_obj_clear_flag(Label3[i], LV_OBJ_FLAG_HIDDEN);
}
}
}
基本流程就是这样,但在实际使用过程中会出现卡死的情况,而且是有时会卡死,有时候又不会,让人摸不着头脑。尝试多次后无果,感觉我的流程没有任何问题,于是上官网的论坛去找,还真有:
看了一下,情况跟我的有点区别,发问人是因为分别在两个线程中调用这个函数,导致卡死了,官方解释到是因为没有上锁,导致的资源竞态问题,而我是在同一个线程中使用的,应该不存在这个问题。但是也给我了启发,会不会也是这种问题?就是我在处理这个当前信号的同时,又接收到一个信号,导致同时有两个对标签进行设置文本,导致卡死了?
三、问题解决:
既然找到了大概的方向,那么开始解决吧:
有两个方法:
其一:取消使用信号接收刷新标志的方式,改为用一个定时器定时去刷新:
lv_timer_create(DataRefreshTimer, 100, NULL); // 开启定时器,更新数据
// 定时器函数,注意:此处的变量升级为全局变量
void DataRefreshTimer(lv_timer_t *tm)
{
for(u8 i = 0; i < 5; i++)
{
if(data[i]->param1[0] == '\0')
break;
lv_label_set_text_fmt(Label1[i], "%s", data[i]->param1);
lv_obj_clear_flag(Label1[i], LV_OBJ_FLAG_HIDDEN);
lv_label_set_text_fmt(Label2[i], "%d", data[i]->param2);
lv_obj_clear_flag(Label2[i], LV_OBJ_FLAG_HIDDEN);
lv_label_set_text_fmt(Label3[i], "%llu", data[i]->param3);
lv_obj_clear_flag(Label3[i], LV_OBJ_FLAG_HIDDEN);
}
}
其二:人工上锁,用一个全局变量,进入前赋值,处理完赋值,当接收到信号后,但是还在处理当中时,就不处理/不发送这个信号:
// 1.发送信号:
if(FlagDone)
lv_event_send(obj, LV_EVENT_USER_REFRESH_RESULT, dataTypedef); // 为1时才可以进来
// 2.在控件的回调函数中接收该信号,并进行处理
static void RefreDataCbk(lv_event_t *event)
{
lv_event_code_t code = lv_event_get_code(event);
lv_obj_t *obj = event->current_target;
if(code == LV_EVENT_USER_REFRESH_RESULT) // 接收到信号
{
FlagDone = 0; // 进来,置0
DataTypedef *data = (DataTypedef *)event->param
for(u8 i = 0; i < 5; i++)
{
if(data[i]->param1[0] == '\0')
break;
lv_label_set_text_fmt(Label1[i], "%s", data[i]->param1);
lv_obj_clear_flag(Label1[i], LV_OBJ_FLAG_HIDDEN);
lv_label_set_text_fmt(Label2[i], "%d", data[i]->param2);
lv_obj_clear_flag(Label2[i], LV_OBJ_FLAG_HIDDEN);
lv_label_set_text_fmt(Label3[i], "%llu", data[i]->param3);
lv_obj_clear_flag(Label3[i], LV_OBJ_FLAG_HIDDEN);
}
FlagDone = 1; // 出去,置1
}
}
这个方法我还没试过,但应该有用吧。。。
需要注意的是,可能存在数据更新不完全的情况,就是在最后一次接收数据时,前面没有处理完,导致忽略了最后一个信号,数据更新不完整,可以考虑再发送一次来解决(没试过,我猜的)。