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