Struggling to properly setup LVGL(ESP-IDF, LVGL9.1.0, RGB565 display, esp32s3)

Description

I have this mostly working but I am struggling with setting up buffers and a canvas i draw on. The colors are not correct. Can someone verify I am creating the buffers and canvas correctly, Im not sure psram_draw_buf is setup correctly.

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

adafruit qualia esp32s3, 16mb flash, 8mb psram, RGB565 display(480.480), ESP-IDF 5.2.1

What LVGL version are you using?

9.1.0

What do you want to achieve?

I need to be able to use the line, circle and set pixel functions.

What have you tried so far?

esp_lcd_panel_dev_config_t rgb_endian to RGB and BGR, bits_per_pixel to 16, 18 and 24

lv_canvas_fill_bg(canvas, lv_palette_main(LV_PALETTE_RED), LV_OPA_COVER); displays a dark blue background

label_dsc.color = lv_palette_main(LV_PALETTE_ORANGE); displays green / black text, kind of a speckled effect

Code to reproduce

#include "freertos/FreeRTOS.h"
#include <stdio.h>
#include <esp_lcd_panel_rgb.h>
#include <esp_lcd_panel_ops.h>
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include <esp_log.h>
#include <freertos/task.h>
#include "esp_lcd_panel_io_additions.h"
#include "esp_lcd_st7701.h"
#include "esp_io_expander.h"
#include "esp_io_expander_tca9554.h"
#include "lvgl.h"
#include <esp_timer.h>
#include <driver/ledc.h>
#include <freertos/semphr.h>
#include "driver/i2c.h"

#include "init_codes.h"

const char *TAG = "Test";

#define IO_EXP_ADDR 0x3F
#define I2C_BUS 0

#define I2C_MASTER_SCL_IO GPIO_NUM_18 /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO GPIO_NUM_8  /*!< GPIO number used for I2C master data  */
#define I2C_BUS 0                     /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ 400000     /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0   /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0   /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000

#define LCD_BRIGHTNESS 255
#define SCREEN_WIDTH 480
#define SCREEN_HEIGHT 480

static lv_disp_t *disp;
static TaskHandle_t lvgl_task;
static esp_timer_handle_t lvgl_tick_timer;
static uint8_t *buf1;
static uint8_t *buf2;
static esp_lcd_panel_handle_t panel_handle;
static SemaphoreHandle_t lvgl_mux = NULL;

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()
{
        xSemaphoreGiveRecursive(lvgl_mux);
}

// static void flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map){
static void flush_cb(lv_disp_t *drv, const lv_area_t *area, uint8_t *color_map)
{
        ESP_LOGV(TAG, "flush_cb");
        esp_lcd_panel_draw_bitmap(panel_handle,
                                  area->x1,
                                  area->y1,
                                  area->x2 + 1,
                                  area->y2 + 1,
                                  color_map);
        lv_display_flush_ready(drv);
}

static void tick(void *arg)
{
    lv_tick_inc(2);
}

#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 50
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
void task(void *arg)
{
    uint32_t delay = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
    while (true)
    {
            lvgl_lock(1);
            delay = lv_timer_handler();
            lvgl_unlock();
            if (delay > EXAMPLE_LVGL_TASK_MAX_DELAY_MS)
            {
                    delay = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
            }
            else if (delay < EXAMPLE_LVGL_TASK_MIN_DELAY_MS)
            {
                    delay = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
            }
            vTaskDelay(delay / portTICK_PERIOD_MS);
    }
}

void init_rgb() {
esp_io_expander_handle_t io_expander = NULL;
    esp_io_expander_new_i2c_tca9554(I2C_BUS, IO_EXP_ADDR, &io_expander);

    spi_line_config_t spi_line = {
        .cs_io_type = IO_TYPE_EXPANDER,
        .cs_gpio_num = IO_EXPANDER_PIN_NUM_1,
        .scl_io_type = IO_TYPE_EXPANDER,
        .scl_gpio_num = IO_EXPANDER_PIN_NUM_0,
        .sda_io_type = IO_TYPE_EXPANDER,
        .sda_gpio_num = IO_EXPANDER_PIN_NUM_7,
        .io_expander = io_expander
    };

    esp_lcd_panel_io_3wire_spi_config_t io_config = {
        .line_config = spi_line,
        .expect_clk_speed = PANEL_IO_3WIRE_SPI_CLK_MAX,
        .spi_mode = 0,
        .lcd_cmd_bytes = 1,
        .lcd_param_bytes = 1,
        .flags = {
            .use_dc_bit = 1,
            .dc_zero_on_data = 0,
            .lsb_first = 0,
            .cs_high_active = 0,
            .del_keep_cs_inactive = 0,
        },
    };

    esp_lcd_panel_io_handle_t panel_io = NULL;
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&io_config, &panel_io));

    ESP_LOGI(TAG, "Install ST7701S panel driver");
    esp_lcd_rgb_panel_config_t rgb_config = {
        //            .clk_src = LCD_CLK_SRC_XTAL,
        .clk_src = LCD_CLK_SRC_DEFAULT,
        .timings = {
            .pclk_hz = 14 * 1000 * 1000,
            .h_res = 480,
            .v_res = 480,
            .hsync_pulse_width = 2,
            .hsync_back_porch = 44,
            .hsync_front_porch = 50,
            .vsync_pulse_width = 2,
            .vsync_back_porch = 18,
            .vsync_front_porch = 16,
            .flags = {
                .hsync_idle_low = 0,
                .vsync_idle_low = 0,
                .de_idle_high = 0,
                .pclk_active_neg = false,
                .pclk_idle_high = false,
            }},

        .data_width = 16,
        .bits_per_pixel = 16,
        .num_fbs = 2,
        .bounce_buffer_size_px = 0, // 10*H_RES,
        .psram_trans_align = 64,
        .hsync_gpio_num = 41,
        .vsync_gpio_num = 42,
        .de_gpio_num = 2,
        .pclk_gpio_num = 1,
        .disp_gpio_num = -1,
        .data_gpio_nums = {40, 39, 38, 0, 45, 48, 47, 21, 14, 13, 12, 11, 10, 9, 46, 3},

        .flags = {
            .fb_in_psram = 1,
        }};
    st7701_vendor_config_t vendor_config = {
        .rgb_config = &rgb_config,
        .init_cmds = TL021WVC,
        .init_cmds_size = sizeof(TL021WVC) / sizeof(st7701_lcd_init_cmd_t),
        .flags = {
            .auto_del_panel_io = 1,
        },
    };
    const esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = -1,
        .rgb_endian = LCD_RGB_ENDIAN_RGB,
        .bits_per_pixel = 16,
        .vendor_config = &vendor_config,
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(panel_io, &panel_config, &panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
    esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, (void **) &buf1, (void **) &buf2);

    lvgl_mux = xSemaphoreCreateRecursiveMutex();

    lv_init();
    disp = lv_display_create(480, 480);
    lv_display_set_flush_cb(disp, flush_cb);
    lv_display_set_buffers(disp, buf1, buf2, 480 * 480 * 2, LV_DISPLAY_RENDER_MODE_DIRECT);

    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &tick,
        .arg = NULL,
        .dispatch_method = ESP_TIMER_TASK,
        .name = "lvgl_tick",
        .skip_unhandled_events = true};
    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, 2 * 1000));

    xTaskCreate(task, "LVGL", 20000, NULL, 2, &lvgl_task);
}

void app_main(void)
{
    ESP_LOGI(TAG, "Turn on LCD backlight");

    // Prepare and then apply the LEDC PWM timer configuration
    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 = 6000, // Set output frequency at 6 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 = GPIO_NUM_16,
        .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, LCD_BRIGHTNESS);
    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);

    ESP_LOGI(TAG, "Install 3-wire SPI panel IO");

    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    i2c_param_config(I2C_BUS, &conf);

    i2c_driver_install(I2C_BUS, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);

    init_rgb();

    vTaskDelay(1000 / portTICK_PERIOD_MS);

    size_t buffer_size = SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(lv_color_t);
    
    lv_color_t *psram_draw_buf = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM);
    if (psram_draw_buf == NULL) {
        ESP_LOGE("Canvas", "Failed to allocate memory in PSRAM");
        return;
    }

    lv_obj_t *canvas = lv_canvas_create(lv_scr_act());
    lv_canvas_set_buffer(canvas, psram_draw_buf, SCREEN_WIDTH, SCREEN_HEIGHT, LV_COLOR_FORMAT_RGB565);


    lv_obj_center(canvas);
    lv_canvas_fill_bg(canvas, lv_palette_main(LV_PALETTE_RED), LV_OPA_COVER);


    lv_draw_rect_dsc_t rect_dsc;
    lv_draw_rect_dsc_init(&rect_dsc);
    rect_dsc.radius = 10;
    rect_dsc.bg_opa = LV_OPA_COVER;
    rect_dsc.bg_color = lv_palette_main(LV_PALETTE_BLUE);

    lv_draw_label_dsc_t label_dsc;
    lv_draw_label_dsc_init(&label_dsc);
    label_dsc.color = lv_palette_main(LV_PALETTE_ORANGE);
    label_dsc.text = "Some text on text canvas";


    lv_layer_t layer;
    lv_canvas_init_layer(canvas, &layer);

    lv_canvas_set_px(canvas, 240, 240, lv_palette_main(LV_PALETTE_GREY), LV_OPA_COVER);


    lv_area_t coords_rect = {100, 100, 150, 150};
    lv_draw_rect(&layer, &rect_dsc, &coords_rect);

    lv_area_t coords_text = {240, 280, 300, 320};
    lv_draw_label(&layer, &label_dsc, &coords_text);

    lv_canvas_finish_layer(canvas, &layer);

    while (true)
    {
        //ESP_LOGI(TAG, "LOOP");
        vTaskDelay(10 / portTICK_PERIOD_MS);

        lvgl_lock(-1);


        lvgl_unlock();
    }
}