How to show images from SD without block_size error?

Description

My project is a 800x480 screen in a board that also includes a microSD slot, so, my goal is to load images from the microSD to the screen, my code compiles and works on the ESP32-S3, but I always get white screen and the same error:

[Error] (0.566, +566) block_locate_free: Asserted at expression: block_size(block) >= size lv_tlsf.c:774

What MCU/Processor/Board and compiler are you using?

I am using ESP32-S3 with Espressif IDE 5.1

What LVGL version are you using?

I am using LVGL 9.1.0

What do you want to achieve?

I want lo show on screen an image loaded from the microSD card

What have you tried so far?

  • Increasing LV_MEM_SIZE
  • Changing values on lvconf

Code to reproduce

#define LCD_WIDTH               800
#define LCD_HEIGHT              480
#define LCD_NUM_FB              2
#define LCD_GPIO_BCKL           GPIO_NUM_2

#define EXAMPLE_MAX_CHAR_SIZE    64

#define LV_USE_FS_FATFS 1

size_t imgsize;

static const char *TAG = "ConnectLVGL";

#define MOUNT_POINT "/sdcard"

// Pin assignments can be set in menuconfig, see "SD SPI Example Configuration" menu.
// You can also change the pin assignments here by changing the following 4 lines.
#define PIN_NUM_MISO  13
#define PIN_NUM_MOSI  11
#define PIN_NUM_CLK   12
#define PIN_NUM_CS    10 //10

//----------------//----------------//----------------//CODE LINES FOR THE LVGL PART//----------------//----------------//----------------//----------------//

static SemaphoreHandle_t lvgl_mux = NULL;
static lv_indev_t *indev_touchpad = NULL;

static void disp_flush(lv_display_t *disp, const lv_area_t *area,
		uint8_t *px_map) {
	esp_lcd_panel_handle_t panel_handle =
			(esp_lcd_panel_handle_t) lv_display_get_user_data(disp);
	esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1,
			area->y2 + 1, px_map);
	lv_display_flush_ready(disp);
}
;

void lvgl_lcd_init(lv_display_t *disp) {
	void *buf1 = NULL;
	void *buf2 = NULL;
	int buffer_size;
	esp_lcd_panel_handle_t panel_handle = NULL;

	const esp_lcd_rgb_panel_config_t st7262_panel_config = { .data_width = 16,
			.num_fbs = LCD_NUM_FB, .clk_src = LCD_CLK_SRC_PLL160M, .timings = {
					.pclk_hz = (16 * 1000000), .h_res = LCD_WIDTH, .v_res =
					LCD_HEIGHT, .hsync_pulse_width = 4, .hsync_back_porch = 8,
					.hsync_front_porch = 8, .vsync_pulse_width = 4,
					.vsync_back_porch = 8, .vsync_front_porch = 8, .flags = {
							.hsync_idle_low = true, .vsync_idle_low = true,
							.de_idle_high = false, .pclk_active_neg = true,
							.pclk_idle_high = false, }, },
			.sram_trans_align = 8, .psram_trans_align = 64, .hsync_gpio_num =
					GPIO_NUM_39, .vsync_gpio_num = GPIO_NUM_41, .de_gpio_num =
					GPIO_NUM_40, .pclk_gpio_num = GPIO_NUM_42, .data_gpio_nums =
					{ GPIO_NUM_8, GPIO_NUM_3, GPIO_NUM_4, GPIO_NUM_9,
							GPIO_NUM_1,               // B0 - B4
							GPIO_NUM_5, GPIO_NUM_6, GPIO_NUM_7, GPIO_NUM_15,
							GPIO_NUM_16,
							GPIO_NUM_4, // G0 - G5
							GPIO_NUM_45, GPIO_NUM_48, GPIO_NUM_47, GPIO_NUM_21,
							GPIO_NUM_14,          // R0 - R4
					}, .disp_gpio_num = GPIO_NUM_NC, .flags = {
					.disp_active_low = false, .fb_in_psram = true, }, };

	// create lcd panel
	ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&st7262_panel_config, &panel_handle));
	ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
	ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));

	// assign callback and handle
	lv_display_set_user_data(disp, panel_handle);
	lv_display_set_flush_cb(disp, disp_flush);

#if LCD_NUM_FB == 2
	buffer_size = LCD_WIDTH * LCD_HEIGHT * 2; // 2 = 16bit color data
	esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2);
	lv_display_set_buffers(disp, buf1, buf2, buffer_size,
			LV_DISPLAY_RENDER_MODE_FULL);
#else
    buffer_size = LCD_WIDTH * LCD_HEIGHT / 4;
    buf1 = heap_caps_malloc(sizeof(lv_color_t) * buffer_size, MALLOC_CAP_SPIRAM);
    lv_display_set_buffers(disp, buf1, buf2, buffer_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
#endif
}

uint16_t map(uint16_t n, uint16_t in_min, uint16_t in_max, uint16_t out_min,
		uint16_t out_max) {
	return (n - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void process_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y,
		uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) {
	*x = map(*x, 0, LCD_WIDTH, 0, LCD_WIDTH);
	*y = map(*y, 0, LCD_HEIGHT, 0, LCD_HEIGHT);
}

esp_lcd_touch_handle_t lvgl_touch_init(void) {
	ESP_LOGI(TAG, "Init Touch driver");
	i2c_master_bus_handle_t touch_i2c_bus_handle = NULL;
	const i2c_master_bus_config_t touch_i2c_bus_config = { .clk_source =
			I2C_CLK_SRC_DEFAULT, .i2c_port = -1, .scl_io_num = GPIO_NUM_20,
			.sda_io_num = GPIO_NUM_19, .glitch_ignore_cnt = 7, };
	ESP_ERROR_CHECK(
			i2c_new_master_bus(&touch_i2c_bus_config, &touch_i2c_bus_handle));

	esp_lcd_panel_io_handle_t gt911_touch_io_handle = NULL;
	const esp_lcd_panel_io_i2c_config_t gt911_touch_io_config = { .dev_addr =
	ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, .on_color_trans_done = NULL, .user_ctx =
	NULL, .control_phase_bytes = 1, .dc_bit_offset = 0, .lcd_cmd_bits = 16,
			.lcd_param_bits = 0, .flags = { .dc_low_on_data = 0,
					.disable_control_phase = 1, }, .scl_speed_hz = 400000, };
	ESP_ERROR_CHECK(
			esp_lcd_new_panel_io_i2c(touch_i2c_bus_handle, &gt911_touch_io_config, &gt911_touch_io_handle));

	esp_lcd_touch_handle_t touch_handle = NULL;
	const esp_lcd_touch_config_t gt911_touch_cfg = { .x_max = LCD_WIDTH,
			.y_max = LCD_HEIGHT, .rst_gpio_num = GPIO_NUM_38, .int_gpio_num =
					GPIO_NUM_NC, .levels = { .reset = 0, .interrupt = 0, },
			.flags = { .swap_xy = 0, .mirror_x = 0, .mirror_y = 0, },
			.process_coordinates = process_coordinates, // callback to fix coordinates between gt911 and display
			.interrupt_callback = NULL, };
	ESP_ERROR_CHECK(
			esp_lcd_touch_new_i2c_gt911(gt911_touch_io_handle, &gt911_touch_cfg,
					&touch_handle));

	return touch_handle;
}

static void gt911_touchpad_read(lv_indev_t *indev, lv_indev_data_t *data) {
	esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t) lv_indev_get_user_data(
			indev);

	uint16_t touchpad_x;
	uint16_t touchpad_y;
	uint8_t touchpad_cnt = 0;

	esp_lcd_touch_read_data(tp);

	bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x,
			&touchpad_y, NULL, &touchpad_cnt, 1);
	if (touchpad_pressed && touchpad_cnt > 0) {
		data->point.x = touchpad_x;
		data->point.y = touchpad_y;
		data->state = LV_INDEV_STATE_PRESSED;
	} else {
		data->state = LV_INDEV_STATE_RELEASED;
	}
}

#define LVGL_TICK_PERIOD_MS    2
#define LVGL_TASK_MAX_DELAY_MS 500
#define LVGL_TASK_MIN_DELAY_MS 1
#define LVGL_TASK_STACK_SIZE   (8 * 1024)
#define LVGL_TASK_PRIORITY     2

bool lvgl_lock(int timeout_ms) {
	// Convert timeout in milliseconds to FreeRTOS ticks
	// If `timeout_ms` is set to -1, the program will block until the condition is met
	const TickType_t timeout_ticks =
			(timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
	return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}

void lvgl_unlock(void) {
	xSemaphoreGiveRecursive(lvgl_mux);
}

static void lvgl_port_task(void *arg) {
	uint32_t task_delay_ms = LVGL_TASK_MAX_DELAY_MS;
	while (1) {
		// Lock the mutex due to the LVGL APIs are not thread-safe
		if (lvgl_lock(-1)) {
			task_delay_ms = lv_timer_handler();
			// Release the mutex
			lvgl_unlock();
		}
		if (task_delay_ms > LVGL_TASK_MAX_DELAY_MS) {
			task_delay_ms = LVGL_TASK_MAX_DELAY_MS;
		} else if (task_delay_ms < LVGL_TASK_MIN_DELAY_MS) {
			task_delay_ms = LVGL_TASK_MIN_DELAY_MS;
		}
		vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
	}
}

static void lvgl_tick(void *arg) {
	/* Tell LVGL how many milliseconds has elapsed */
	lv_tick_inc(LVGL_TICK_PERIOD_MS);
}

const esp_task_wdt_config_t wdtConfig = { .timeout_ms = 510000, // timeout: 8.5 min
		.idle_core_mask = 1 << 0 | 1 << 1, // Root PRO & APP
		.trigger_panic = 1,                // Reset on
		};

//----------------//----------------//----------------//CODE LINES FOR THE MICROSD PART//----------------//----------------//----------------//----------------//

void init_sd_card(void) {
	esp_err_t ret;
	sdmmc_card_t *card;
	const char mount_point[] = MOUNT_POINT;
	ESP_LOGI(TAG, "Initializing SD card");

	esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed =
	false, .max_files = 5, .allocation_unit_size = 16 * 1024 };

	sdmmc_host_t host = SDSPI_HOST_DEFAULT();
	spi_bus_config_t bus_cfg = { .mosi_io_num = PIN_NUM_MOSI, .miso_io_num =
	PIN_NUM_MISO, .sclk_io_num = PIN_NUM_CLK, .quadwp_io_num = -1,
			.quadhd_io_num = -1, .max_transfer_sz = 4000, };
	ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CH_AUTO);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "Failed to initialize bus.");
		return;
	}

	sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
	slot_config.gpio_cs = PIN_NUM_CS;
	slot_config.host_id = host.slot;

	ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config,
			&mount_config, &card);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "Failed to mount filesystem.");
		return;
	}

	sdmmc_card_print_info(stdout, card);
}

void display_image_from_sd() {

	lv_obj_t *img;

	img = lv_img_create(lv_scr_act());

	lv_img_set_src(img, "A:/files/ikarus.png");
	// Optional: Error handling
	if (img == NULL) {
		ESP_LOGE(TAG, "Failed to create image object");
	}
	lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);

}
//----------------//----------------//----------------//APP MAIN//----------------//----------------//----------------//----------------//

void app_main(void) {

	//----------------////----------------//LVGL PART//----------------////----------------//

	esp_task_wdt_reconfigure(&wdtConfig);
	ESP_LOGI(TAG, "Turn on LCD backlight");
	ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_LOW_SPEED_MODE,
			.duty_resolution = LEDC_TIMER_8_BIT, .timer_num = LEDC_TIMER_0,
			.freq_hz = 4000,  // Set output frequency at 4 kHz
			.clk_cfg = LEDC_USE_RC_FAST_CLK };
	ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

	// Prepare and then apply the LEDC PWM channel configuration
	ledc_channel_config_t ledc_channel = { .gpio_num = LCD_GPIO_BCKL,
			.speed_mode = LEDC_LOW_SPEED_MODE, .channel = LEDC_CHANNEL_0,
			.intr_type = LEDC_INTR_DISABLE, .timer_sel = LEDC_TIMER_0,
			.duty = 0, // Set duty to 0%
			.hpoint = 0 };
	ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
	ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0,
			(uint32_t) (256 / (100 / 100.00)) - 1);
	ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);

	ESP_LOGI(TAG, "lv_init");
	lv_init();

	ESP_LOGI(TAG, "lv_display_create");
	lv_display_t *disp = lv_display_create(LCD_WIDTH, LCD_HEIGHT);

	ESP_LOGI(TAG, "lvgl_lcd_init");
	lvgl_lcd_init(disp);

	ESP_LOGI(TAG, "Install LVGL tick timer");
	// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
	const esp_timer_create_args_t lvgl_tick_timer_args = { .callback =
			&lvgl_tick, .name = "lvgl_tick" };
	esp_timer_handle_t lvgl_tick_timer = NULL;
	ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
	ESP_ERROR_CHECK(
			esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000));

	ESP_LOGI(TAG, "Create LVGL task");
	lvgl_mux = xSemaphoreCreateRecursiveMutex();
	assert(lvgl_mux);
	xTaskCreate(lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE, NULL,
	LVGL_TASK_PRIORITY, NULL);
	/*
	 // init touch i2c bus
	 esp_lcd_touch_handle_t touch_handle = lvgl_touch_init();
	 indev_touchpad = lv_indev_create();
	 lv_indev_set_type(indev_touchpad, LV_INDEV_TYPE_POINTER);
	 lv_indev_set_user_data(indev_touchpad, touch_handle);
	 lv_indev_set_read_cb(indev_touchpad, gt911_touchpad_read);
	 */
	//lv_demo_widgets();
	//ui_init();
	//----------------////----------------//MICROSD PART//----------------////----------------//
	// Initialize LVGL file system interface
	init_sd_card();
	display_image_from_sd();
	// RUN UI //
	//ui_init();

}

Screenshot and/or video

This error is usually heap corruption. Have you tried removing code, a part at a time, until it stops crashing to try and narrow it down?

I’m not sure how much can I remove if I want the whole project to work, yet being so, I’m gonna try, thank you for your reply Richard

Edit: Removed all the removable things, the error persists

Adding a new image with the LVGL debug, in case that helps