Load many images from SPI FLASH,But it's not filesystem mode

I developed using STM32,it is F103 series。so,I want to load the image from external storage(SPI FLASH、Not SDcard)。STM32F103 donot have some RAM。so,I use W25Q128(spi flash) to save Many pictures.bin。but,I am having trouble displaying the button image。Can you provide relevant processing routines,and No file system mode

As far as I know, only the data of the whole picture can be read into the RAM of the chip and then displayed,so, this requires the MCU’s internal RAM to be at least large enough to hold the entire image.

I used Emwin for the UI before。I can use pic.bin from spi flash。I want to change the Emwin project to LVGL,But I don’t seem to find a similar mechanism。but,I saw someone using SPI FLASH to do what I wanted。But I don’t know how to do that。Someone has done it

If you convert the images using the image converter to a normal C file, the solution I described here of adding an __attribute__((section(".qspi"))) to the image data array should work.

You would need to look up what the appropriate section to use for your platform is; that does not depend on LVGL. Typically it’s named something along the lines of .ExtFlash but your experience may vary, as this type of thing is essentially up to developer preference.

You will also need to set up your build system so that a separate file to flash to QSPI is produced, and flash that whenever you update the number or content of your images.

If you can’t map the SPI flash as system flash you can also use a custom image decoder that ready the lines of the images on demand.

That is cool,I will try

At first, I could successfully display a single picture (BMP.bin pictures are stored in SPI FLASH).

But since my STM32 doesn’t have much RAM, as well as external SRAM. So what I did was to create a public cache (buff[151024]) and then read BMP. Bin from SPI Flash in one go to buffbuff[151024]。

If an imgbtn is displayed, it is normal. If multiple imgbtn are displayed on one interface, multiple imgbtn will become the same imgbtn. It seems that the buff[15*1024] is used by multiple imgbtn. But what I want to achieve is that an imgbtn should display its own picture. But lvgl seems to refresh the interface, causing the button that has displayed its own picture in front to become the last button picture created.

As I said above. I think that because multiple imgbtn share a global buff[15*1024], if lvgl refreshes the entire screen, it will cause multiple buttons to display the same image. In fact, when I create the next button, I don’t want the previous button image to be affected and refreshed. I want it to be independent. In this way, multiple buttons can use the same global buff. When reading their respective pictures through SPI FLASH, they can only change when they are touched, otherwise they will not be affected by other factors.

Here is how I use malloc, which is similar to using global buff:

/*Request a memory space
@param size: Is the size of bmp
pbuf = lv_mem_alloc(size);
/*Read pictures from SPI FLASH at one time*/
LoadBmpDataFromFlash(pbuf, (uint8_t *)pName);	
/*Given artificially known parameters, imitate the generated .c file*/
	image->header.cf = LV_IMG_CF_TRUE_COLOR;
	image->header.always_zero = 0;
	image->header.w = 80;
	image->header.h = 80;
	image->data_size = 6400 * LV_COLOR_SIZE / 8;		
	image->data = pbuf;
    /*Display pictures and release memory*/  
  notes:The reason why I release the memory is because my stm32 RAM is not enough. If I keep occupying it, the RAM will overflow
	lv_img_set_src(img_wifi, &image);
	lv_imgbtn_set_src(imgbtn1, LV_BTN_STATE_CHECKED_RELEASED, &image);

This is how I read bmp.bin from SPI FLASH and display it

But it can only display one picture. If multiple pictures are displayed, they will all become the last read bmp.bin. I don’t know how to deal with it. I want them to remain interlocked and not affect each other. Like the principle of TFT/RGB LCD. I display an image in an area on the screen. Only if I actively refresh the area through a control command, the image in the area can change, otherwise it will remain the same. This is not related to using the same buff or using lv_mem_alloc。

LVGL needs the image data to be addressible at all times; it’s not possible to reliably free the memory and keep the image on the screen.

If the images are in SPI flash, is there a reason why you can’t just read them straight from there without copying them to RAM first?

This is my first contact with LVGL. I used emwin before. Read the picture into the allocated memory at one time, and then release it after successful display. This method is to use the following

/*EMWIN set button skin*/

Because I don’t understand lvgl, I think it can be achieved in this way, but the effect I achieved is not ideal.

If it is displayed while reading, it seems to use the file system just like using SD Fatfs. However, many application scenarios will not use the file system. So currently I have not thought about how to proceed

Actually, it’s NOT the case. LVGL will call the image decoder if needs some data from the image. E.g. we can use images from SD card too when the data is really available on demand.

Yes, if you use the same buffer LVGL won’t know if the buffer has changed.

Note that you can’t lv_mem_free(pbuf); because it’s still used by LVGL.

You need a custom image decoder. Here are the steps:

  1. Set the image source like this: lv_imgbtn_set_src(imgbtn1, <state>, "A/1"). 1st image from the “A” driver. For an other image use “A/2”.
  2. Create an image decoder like described here.
  3. In decoder_info() check if src[0]==‘A’. If so, set its width/height in the header. Use header->cf = LV_IMG_CF_RAW.
  4. decoder_open() if dsc->src[0] == 'A' set dsc->img_data = NULL and header->cf = LV_IMG_CF_RAW;
  5. In decoder_built_in_read_line() just read the pixels told by the parameters. See dsc->src to decide which image to read.
  6. In decoder_close you have nothing to do if you haven’t allocated any temporal memory.

It should work this way, but it’d be really great to have an example for this.

1 Like

Oh… I didn’t think of doing it this way. That’s a good idea.