Description
What MCU/Processor/Board and compiler are you using?
STM32L432KB
What LVGL version are you using?
v8.3.11
What do you want to achieve?
load image from external spi flash without filesystem with limited Flash and RAM.since I can’t store whole picture in RAM, I think I should use read_line_cb instead of open_cb.
What have you tried so far?
1.read and display image without LVGL(I just need read buff from flash and send it to LCD), it is OK
2.create custom image decoder with LVGL
3.set dsc->img_data = NULL; in decoder open_cb
3.read image data from spi flash in read_line_cb, it doesn’t work
Code to reproduce
Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.
The code block(s) should be formatted like:
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_res_t res = LV_RES_OK;
LOG_N("decoder info");
header->w = ((lv_img_dsc_t *)src)->header.w;
header->h = ((lv_img_dsc_t *)src)->header.h;
header->cf = ((lv_img_dsc_t *)src)->header.cf;
return res;
}
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
LOG_N("decoder open");
dsc->img_data = NULL;
return LV_RES_OK;
}
lv_res_t 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); /*Unused*/
lv_res_t res = LV_RES_OK;
LOG_N("here:x:%d,y:%d,len:%d,addr:%x", x, y, len, buf);
//read buff from spi flash, when I don't use LVGL, i just need send the buf to LCD, it will work
read_spinorflash_buff(buf, y * len * LV_COLOR_DEPTH / 8, MY_DISP_HOR_RES * LV_COLOR_DEPTH / 8);
return res;
}
void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
LOG_N("decoder close");
}
void lv_custom_pic_decoder_init(void)
{
lv_img_decoder_t * dec = lv_img_decoder_create();
lv_img_decoder_set_info_cb(dec, decoder_info);
lv_img_decoder_set_open_cb(dec, decoder_open);
lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
lv_img_decoder_set_close_cb(dec, decoder_close);
}
void lv_custom_pic_display(void)
{
//buff to store pic
uint16_t size = 4800;
uint8_t* pic_buff = (uint8_t*)lv_mem_alloc(size);
//create image
lv_img_dsc_t *img_dsc = malloc(sizeof(lv_img_dsc_t));
img_dsc->header.always_zero = 0;
img_dsc->header.w = 240; //width of picture store in flash
img_dsc->header.h = 240; //height of picture store in flash
img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR;
img_dsc->data = (uint8_t*) pic_buff;
img_dsc->data_size = 240 * 240 * LV_COLOR_DEPTH / 8;
//display image
lv_obj_t * img = lv_img_create(lv_scr_act());
lv_img_set_src(img, img_dsc);
lv_mem_free(pic_buff);
}
Screenshot and/or video
No thing display in lcd
You can try the following code. Replace IMAGE_OFFSET
in the code with the offset of the image resource relative to the first address of FLASH. Be careful not to place the image at the first address of FLASH
, because at this time the offset is 0, and lvgl will think it is empty data.
static lv_res_t decoder_info(lv_img_decoder_t* decoder, const void* src, lv_img_header_t* header)
{
LV_UNUSED(decoder); /*Unused*/
lv_res_t res = LV_RES_OK;
header->w = ((lv_img_dsc_t*)src)->header.w;
header->h = ((lv_img_dsc_t*)src)->header.h;
header->cf = ((lv_img_dsc_t*)src)->header.cf;
return res;
}
static lv_res_t decoder_open(lv_img_decoder_t* decoder, lv_img_decoder_dsc_t* dsc)
{
LV_UNUSED(decoder); /*Unused*/
return LV_RES_OK;
}
lv_res_t 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); /*Unused*/
const lv_img_dsc_t* img_dsc = dsc->src;
uint32_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
px_size = ((px_size + 7) >> 3);
uint32_t stride = img_dsc->header.w * px_size;
uint32_t offset = (lv_uintptr_t)img_dsc->data + stride * y + px_size * x;
read_spinorflash_buff(buf, len * px_size, offset);
return LV_RES_OK;
}
void decoder_close(lv_img_decoder_t* decoder, lv_img_decoder_dsc_t* dsc)
{
LV_UNUSED(decoder); /*Unused*/
}
void lv_custom_pic_decoder_init(void)
{
lv_img_decoder_t* dec = lv_img_decoder_create();
lv_img_decoder_set_info_cb(dec, decoder_info);
lv_img_decoder_set_open_cb(dec, decoder_open);
lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
lv_img_decoder_set_close_cb(dec, decoder_close);
}
void flash_image_test(void)
{
lv_custom_pic_decoder_init();
// create image
lv_img_dsc_t* img_dsc = malloc(sizeof(lv_img_dsc_t));
lv_memset_00(img_dsc, sizeof(lv_img_dsc_t));
img_dsc->header.w = 240;
img_dsc->header.h = 240;
img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR;
img_dsc->data = (const uint8_t*)(lv_uintptr_t)IMAGE_OFFSET;
// display image
lv_obj_t* img = lv_img_create(lv_scr_act());
lv_img_set_src(img, img_dsc);
}
Hi @FASTSHIFT :
Thanks for your suggestion, I test these code but failed.
here is my code.
I stored four picture in my SPI flash.
first one start from 0x00
second one start from 0x1c200(115200 = 240 * 240 * 2)
third one start from 0x54600…
I don’t use memory map with SPI flash, so I don’t understand how IMAGE_OFFSET works?
in my understanding, img_dsc->data should point the buff store image.
and the key should be how to update the buff and let LVGL use.
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_res_t res = LV_RES_OK;
LOG_N("decoder info");
header->w = ((lv_img_dsc_t *)src)->header.w;
header->h = ((lv_img_dsc_t *)src)->header.h;
header->cf = ((lv_img_dsc_t *)src)->header.cf;
return res;
}
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
LOG_N("decoder open");
//dsc->img_data = NULL;
return LV_RES_OK;
}
lv_res_t 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); /*Unused*/
lv_res_t res = LV_RES_OK;
LOG_N("here:x:%d,y:%d,len:%d,addr:%x", x, y, len, buf);
const lv_img_dsc_t *img_dsc = dsc->src;
uint32_t px_size = lv_img_cf_get_px_size(dsc->header.cf);
px_size = ((px_size + 7) >> 3);
uint32_t stride = img_dsc->header.w * px_size;
uint32_t offset = (lv_uintptr_t) img_dsc->data + stride * y + px_size * x;
read_spinorflash_buff(buf, len * px_size, offset);
return res;
}
void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
LOG_N("decoder close");
}
void lv_custom_pic_decoder_init(void)
{
lv_img_decoder_t * dec = lv_img_decoder_create();
lv_img_decoder_set_info_cb(dec, decoder_info);
lv_img_decoder_set_open_cb(dec, decoder_open);
lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
lv_img_decoder_set_close_cb(dec, decoder_close);
}
#define IMAGE_OFFSET 115200
void flash_image_test(void)
{
lv_custom_pic_decoder_init();
// create image
lv_img_dsc_t* img_dsc = malloc(sizeof(lv_img_dsc_t));
lv_memset_00(img_dsc, sizeof(lv_img_dsc_t));
img_dsc->header.w = 240;
img_dsc->header.h = 240;
img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR;
img_dsc->data = (const uint8_t*)(lv_uintptr_t)IMAGE_OFFSET;
// display image
lv_obj_t* img = lv_img_create(lv_scr_act());
lv_img_set_src(img, img_dsc);
}