0%

LVGL显示实时图像信息

LVGL介绍

LVGL 是最流行的免费和开源嵌入式图形库,可为任何 MCU 和 MPU 显示类型创建漂亮的 UI。

需求说明

这是去年做的一个小项目,手持热像仪,需要LCD显示摄像头的实时图像。之前做过直接用LCD驱动显示图片效率会高些,这一次需要加一下UI给用户选配置等功能,所以选择使用LVGL。做UI是小问题,主要是如何显示实时图像,本文将介绍使用图像解码器的方法。

Image decoder(图像解码器)

图像解码器原本被用来解码通用图像格式,如 PNG 或 JPG。我把摄像头读来数据通过图像解码器封装了一下,当做自定义格式去解码,解码器接口如下,最后注册到LVGL,主要实现了 get_info 和 read_line 这两个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
uint16_t img_ram[MY_IMG_H][MY_IMG_W];	// 摄像机读来的图像数据

static lv_res_t my_decoder_get_info(lv_img_decoder_t* decoder, const void* src, lv_img_header_t* header)
{
LV_UNUSED(decoder);
if(strcmp("use my_decoder", src))
{// 如果src 不等于 "use my_decoder",不使用此解码器
return LV_RES_INV;
}
header->w = MY_IMG_W;
header->h = MY_IMG_H;
header->always_zero = 0;
header->cf = LV_IMG_CF_TRUE_COLOR; // 使用lv_conf.h 中 LV_COLOR_DEPTH 配置的颜色
return LV_RES_OK;
}

static lv_res_t my_decoder_open(lv_img_decoder_t* decoder, lv_img_decoder_dsc_t* dsc)
{
LV_UNUSED(decoder);
LV_UNUSED(dsc);
return LV_RES_OK;
}

static lv_res_t my_decoder_read_line(lv_img_decoder_t* decoder, lv_img_decoder_dsc_t* dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t* buf)
{
LV_UNUSED(decoder);
LV_UNUSED(dsc);
uint16_t* destBuff = (uint16_t*)buf;
uint16_t* srcBuff = (uint16_t*)img_ram;
uint32_t offset = x + y * MY_IMG_W;
for(int i = 0; i < len; i++)
{
destBuff[i] = srcBuff[offset + i];
}
return LV_RES_OK;
}

static void my_decoder_close(lv_img_decoder_t* decoder, lv_img_decoder_dsc_t* dsc)
{
LV_UNUSED(decoder);
LV_UNUSED(dsc);
return LV_RES_OK;
}

/**
* @brief 创建一个解码器,将自定义解码器的接口注册到此解码器
*/
void my_decoder_init(void)
{
lv_img_decoder_t* dec = lv_img_decoder_create();
lv_img_decoder_set_close_cb(dec, my_decoder_close);
lv_img_decoder_set_info_cb(dec, my_decoder_get_info);
lv_img_decoder_set_open_cb(dec, my_decoder_open);
lv_img_decoder_set_read_line_cb(dec, my_decoder_read_line);
}

定时刷新和 main 函数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void my_timer(lv_timer_t * timer)
{
lv_obj_t * img = timer->user_data; // 取出定时器的userdata
// 这里需实现更新摄像机的图像数据到 img_ram 数组的代码
lv_obj_invalidate(img); // 将img对象标记为无效以重新绘制其区域
}

void my_show_img(void)
{
lv_obj_t * img = lv_img_create(lv_scr_act());
lv_img_set_src(img, "use my_decoder");
lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_size(img, MY_IMG_W, MY_IMG_H);
lv_timer_create(my_timer, 100, (void*)img); // 创建定时器,周期100ms,定时器userdata设为img对象
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int nCmdShow)
{
/*Initialize LittlevGL*/
lv_init();

/*Initialize the HAL for LittlevGL*/
lv_win32_init(hInstance, SW_SHOWNORMAL, 800, 480, NULL);

/*Output prompt information to the console, you can also use printf() to print directly*/
LV_LOG_USER("LVGL initialization completed!");

/*Run the demo*/
my_decoder_init();
my_show_img();

while(!lv_win32_quit_signal) {
/* Periodically call the lv_task handler.
* It could be done in a timer interrupt or an OS task too.*/
lv_task_handler();
usleep(10000); /*Just to let the system breath*/
}
return 0;
}

其他方法

Canvas(画布)(lv_canvas)