firmware: Actually working LCD with LVGL
This commit is contained in:
		@@ -84,6 +84,8 @@ void example_lvgl_demo_ui(lv_display_t *disp)
 | 
				
			|||||||
    // init default theme
 | 
					    // init default theme
 | 
				
			||||||
    lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK,
 | 
					    lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK,
 | 
				
			||||||
                          font_normal);
 | 
					                          font_normal);
 | 
				
			||||||
 | 
					    // lv_sysmon_hide_performance(disp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // bullet style
 | 
					    // bullet style
 | 
				
			||||||
    lv_style_init(&style_bullet);
 | 
					    lv_style_init(&style_bullet);
 | 
				
			||||||
    lv_style_set_border_width(&style_bullet, 0);
 | 
					    lv_style_set_border_width(&style_bullet, 0);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,5 @@
 | 
				
			|||||||
#include <inttypes.h>
 | 
					#include <inttypes.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
#include <sys/lock.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "sdkconfig.h"
 | 
					#include "sdkconfig.h"
 | 
				
			||||||
#include "freertos/FreeRTOS.h"
 | 
					#include "freertos/FreeRTOS.h"
 | 
				
			||||||
@@ -22,52 +20,70 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// in ms
 | 
					// in ms
 | 
				
			||||||
#define LVGL_TICK_PERIOD     2
 | 
					#define LVGL_TICK_PERIOD     2
 | 
				
			||||||
 | 
					#define LVGL_TASK_MAX_DELAY  500
 | 
				
			||||||
 | 
					#define LVGL_TASK_MIN_DELAY  1
 | 
				
			||||||
#define LVGL_TASK_PRIORITY   2
 | 
					#define LVGL_TASK_PRIORITY   2
 | 
				
			||||||
#define LVGL_TASK_STACK_SIZE (4 * 1024)
 | 
					#define LVGL_TASK_STACK_SIZE (8 * 1024)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char *TAG = "valconomy";
 | 
					static const char *TAG = "valconomy";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
i2c_master_bus_handle_t i2c_bus_handle;
 | 
					static i2c_master_bus_handle_t i2c_bus_handle;
 | 
				
			||||||
 | 
					static i2c_master_dev_handle_t exio_cfg_handle, exio_o_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LVGL library is not thread-safe, this example will call LVGL APIs from different tasks, so use a mutex to protect it
 | 
					// LVGL library is not thread-safe, we will call LVGL APIs from different tasks, so use a mutex to protect it
 | 
				
			||||||
static _lock_t lvgl_api_lock;
 | 
					static SemaphoreHandle_t lvgl_mtx = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern void example_lvgl_demo_ui(lv_display_t *disp);
 | 
					extern void example_lvgl_demo_ui(lv_display_t *disp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool example_on_vsync(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_ctx) {
 | 
					bool val_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_mtx, timeout_ticks) == pdTRUE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void val_lvgl_unlock(void) {
 | 
				
			||||||
 | 
					  xSemaphoreGiveRecursive(lvgl_mtx);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool val_on_vsync(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_ctx) {
 | 
				
			||||||
  return false;
 | 
					  return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
 | 
					static void val_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
 | 
				
			||||||
  esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp);
 | 
					  esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp);
 | 
				
			||||||
  // pass the draw buffer to the driver
 | 
					  // pass the draw buffer to the driver
 | 
				
			||||||
  esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, px_map);
 | 
					  esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, px_map);
 | 
				
			||||||
  lv_disp_flush_ready(disp);
 | 
					  lv_disp_flush_ready(disp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void example_increase_lvgl_tick(void *arg) {
 | 
					static void val_increase_lvgl_tick(void *arg) {
 | 
				
			||||||
  /* Tell LVGL how many milliseconds has elapsed */
 | 
					  /* Tell LVGL how many milliseconds has elapsed */
 | 
				
			||||||
  lv_tick_inc(LVGL_TICK_PERIOD);
 | 
					  lv_tick_inc(LVGL_TICK_PERIOD);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void example_lvgl_port_task(void *arg) {
 | 
					static void val_lvgl_port_task(void *arg) {
 | 
				
			||||||
  ESP_LOGI(TAG, "Starting LVGL task");
 | 
					  ESP_LOGI(TAG, "Starting LVGL task");
 | 
				
			||||||
  uint32_t time_till_next_ms = 0;
 | 
					  uint32_t task_delay_ms = LVGL_TASK_MAX_DELAY;
 | 
				
			||||||
  while (1) {
 | 
					  while (1) {
 | 
				
			||||||
    _lock_acquire(&lvgl_api_lock);
 | 
					    // Lock the mutex due to the LVGL APIs are not thread-safe
 | 
				
			||||||
    time_till_next_ms = lv_timer_handler();
 | 
					    if (val_lvgl_lock(-1)) {
 | 
				
			||||||
    _lock_release(&lvgl_api_lock);
 | 
					      task_delay_ms = lv_timer_handler();
 | 
				
			||||||
 | 
					      // Release the mutex
 | 
				
			||||||
    // in case of task watch dog timeout, set the minimal delay to 10ms
 | 
					      val_lvgl_unlock();
 | 
				
			||||||
    if (time_till_next_ms < 10) {
 | 
					 | 
				
			||||||
       time_till_next_ms = 10;
 | 
					 | 
				
			||||||
     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     usleep(1000 * time_till_next_ms);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (task_delay_ms > LVGL_TASK_MAX_DELAY) {
 | 
				
			||||||
 | 
					      task_delay_ms = LVGL_TASK_MAX_DELAY;
 | 
				
			||||||
 | 
					    } else if (task_delay_ms < LVGL_TASK_MIN_DELAY) {
 | 
				
			||||||
 | 
					      task_delay_ms = LVGL_TASK_MIN_DELAY;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static esp_err_t i2c_master_init(void) {
 | 
					static void val_i2c_master_init(void) {
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Init I2C");
 | 
				
			||||||
  i2c_master_bus_config_t i2c_mst_config = {
 | 
					  i2c_master_bus_config_t i2c_mst_config = {
 | 
				
			||||||
    .clk_source = I2C_CLK_SRC_DEFAULT,
 | 
					    .clk_source = I2C_CLK_SRC_DEFAULT,
 | 
				
			||||||
    .i2c_port = I2C_NUM_0,
 | 
					    .i2c_port = I2C_NUM_0,
 | 
				
			||||||
@@ -77,12 +93,7 @@ static esp_err_t i2c_master_init(void) {
 | 
				
			|||||||
    .flags.enable_internal_pullup = true,
 | 
					    .flags.enable_internal_pullup = true,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return i2c_new_master_bus(&i2c_mst_config, &i2c_bus_handle);
 | 
					  ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &i2c_bus_handle));
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void setup_lcd() {
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Init I2C");
 | 
					 | 
				
			||||||
  ESP_ERROR_CHECK(i2c_master_init());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  i2c_device_config_t exio_dev_cfg = {
 | 
					  i2c_device_config_t exio_dev_cfg = {
 | 
				
			||||||
    .dev_addr_length = I2C_ADDR_BIT_LEN_7,
 | 
					    .dev_addr_length = I2C_ADDR_BIT_LEN_7,
 | 
				
			||||||
@@ -90,21 +101,22 @@ static void setup_lcd() {
 | 
				
			|||||||
    .scl_speed_hz = 100000,
 | 
					    .scl_speed_hz = 100000,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  i2c_master_dev_handle_t exio_cfg_handle, exio_o_handle;
 | 
					 | 
				
			||||||
  ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &exio_dev_cfg, &exio_cfg_handle));
 | 
					  ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &exio_dev_cfg, &exio_cfg_handle));
 | 
				
			||||||
  exio_dev_cfg.device_address = 0x38;
 | 
					  exio_dev_cfg.device_address = 0x38;
 | 
				
			||||||
  ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &exio_dev_cfg, &exio_o_handle));
 | 
					  ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, &exio_dev_cfg, &exio_o_handle));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_lcd_panel_handle_t val_setup_lcd(void) {
 | 
				
			||||||
  uint8_t i2c_b;
 | 
					  uint8_t i2c_b;
 | 
				
			||||||
  i2c_b = 0x01; // Output enable
 | 
					  i2c_b = 0x01; // Output enable
 | 
				
			||||||
  ESP_ERROR_CHECK(i2c_master_transmit(exio_cfg_handle, &i2c_b, 1, -1));
 | 
					  ESP_ERROR_CHECK(i2c_master_transmit(exio_cfg_handle, &i2c_b, 1, -1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  i2c_b = 0x00; // Reset everything
 | 
					  i2c_b = 0x00; // Reset everything
 | 
				
			||||||
  ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
 | 
					  ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
 | 
				
			||||||
  usleep(10 * 1000);
 | 
					  vTaskDelay(pdMS_TO_TICKS(10));
 | 
				
			||||||
  i2c_b = 0x08; // Pull LCD out of reset
 | 
					  i2c_b = 0x08; // Pull LCD out of reset
 | 
				
			||||||
  ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
 | 
					  ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
 | 
				
			||||||
  usleep(100 * 1000);
 | 
					  vTaskDelay(pdMS_TO_TICKS(100));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ESP_LOGI(TAG, "Install RGB LCD panel driver");
 | 
					  ESP_LOGI(TAG, "Install RGB LCD panel driver");
 | 
				
			||||||
  esp_lcd_panel_handle_t panel_handle = NULL;
 | 
					  esp_lcd_panel_handle_t panel_handle = NULL;
 | 
				
			||||||
@@ -163,12 +175,16 @@ static void setup_lcd() {
 | 
				
			|||||||
  i2c_b = 0x0e; // LCD out of reset, backlight on
 | 
					  i2c_b = 0x0e; // LCD out of reset, backlight on
 | 
				
			||||||
  ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
 | 
					  ESP_ERROR_CHECK(i2c_master_transmit(exio_o_handle, &i2c_b, 1, -1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return panel_handle;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void val_setup_lvgl(esp_lcd_panel_handle_t lcd_panel) {
 | 
				
			||||||
  ESP_LOGI(TAG, "Initialize LVGL library");
 | 
					  ESP_LOGI(TAG, "Initialize LVGL library");
 | 
				
			||||||
  lv_init();
 | 
					  lv_init();
 | 
				
			||||||
  // create a lvgl display
 | 
					  // create a lvgl display
 | 
				
			||||||
  lv_display_t *display = lv_display_create(LCD_HRES, LCD_VRES);
 | 
					  lv_display_t *display = lv_display_create(LCD_HRES, LCD_VRES);
 | 
				
			||||||
  // associate the rgb panel handle to the display
 | 
					  // associate the rgb panel handle to the display
 | 
				
			||||||
  lv_display_set_user_data(display, panel_handle);
 | 
					  lv_display_set_user_data(display, lcd_panel);
 | 
				
			||||||
  // set color depth
 | 
					  // set color depth
 | 
				
			||||||
  lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
 | 
					  lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -176,39 +192,46 @@ static void setup_lcd() {
 | 
				
			|||||||
  void *buf1 = NULL;
 | 
					  void *buf1 = NULL;
 | 
				
			||||||
  void *buf2 = NULL;
 | 
					  void *buf2 = NULL;
 | 
				
			||||||
  ESP_LOGI(TAG, "Use frame buffers as LVGL draw buffers");
 | 
					  ESP_LOGI(TAG, "Use frame buffers as LVGL draw buffers");
 | 
				
			||||||
  ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2));
 | 
					  ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(lcd_panel, 2, &buf1, &buf2));
 | 
				
			||||||
  // set LVGL draw buffers and direct mode
 | 
					  // set LVGL draw buffers and direct mode
 | 
				
			||||||
  lv_display_set_buffers(display, buf1, buf2, LCD_HRES * LCD_VRES * LCD_PIXEL_SIZE, LV_DISPLAY_RENDER_MODE_DIRECT);
 | 
					  lv_display_set_buffers(display, buf1, buf2, LCD_HRES * LCD_VRES * LCD_PIXEL_SIZE, LV_DISPLAY_RENDER_MODE_DIRECT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // set the callback which can copy the rendered image to an area of the display
 | 
					  // set the callback which can copy the rendered image to an area of the display
 | 
				
			||||||
  lv_display_set_flush_cb(display, example_lvgl_flush_cb);
 | 
					  lv_display_set_flush_cb(display, val_lvgl_flush_cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ESP_LOGI(TAG, "Register event callbacks");
 | 
					  ESP_LOGI(TAG, "Register event callbacks");
 | 
				
			||||||
  esp_lcd_rgb_panel_event_callbacks_t cbs = {
 | 
					  esp_lcd_rgb_panel_event_callbacks_t cbs = {
 | 
				
			||||||
    .on_vsync = example_on_vsync,
 | 
					    .on_vsync = val_on_vsync,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, display));
 | 
					  ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(lcd_panel, &cbs, display));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ESP_LOGI(TAG, "Install LVGL tick timer");
 | 
					  ESP_LOGI(TAG, "Install LVGL tick timer");
 | 
				
			||||||
  // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
 | 
					  // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
 | 
				
			||||||
  const esp_timer_create_args_t lvgl_tick_timer_args = {
 | 
					  const esp_timer_create_args_t lvgl_tick_timer_args = {
 | 
				
			||||||
    .callback = &example_increase_lvgl_tick,
 | 
					    .callback = &val_increase_lvgl_tick,
 | 
				
			||||||
    .name = "lvgl_tick"
 | 
					    .name = "lvgl_tick"
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  esp_timer_handle_t lvgl_tick_timer = NULL;
 | 
					  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_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
 | 
				
			||||||
  ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD * 1000));
 | 
					  ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD * 1000));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lvgl_mtx = xSemaphoreCreateRecursiveMutex();
 | 
				
			||||||
 | 
					  assert(lvgl_mtx);
 | 
				
			||||||
  ESP_LOGI(TAG, "Create LVGL task");
 | 
					  ESP_LOGI(TAG, "Create LVGL task");
 | 
				
			||||||
  xTaskCreate(example_lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE, NULL, LVGL_TASK_PRIORITY, NULL);
 | 
					  xTaskCreate(val_lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE, NULL, LVGL_TASK_PRIORITY, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ESP_LOGI(TAG, "Display LVGL UI");
 | 
					  ESP_LOGI(TAG, "Display LVGL UI");
 | 
				
			||||||
  // Lock the mutex due to the LVGL APIs are not thread-safe
 | 
					  // Lock the mutex due to the LVGL APIs are not thread-safe
 | 
				
			||||||
  _lock_acquire(&lvgl_api_lock);
 | 
					  assert(val_lvgl_lock(-1));
 | 
				
			||||||
  example_lvgl_demo_ui(display);
 | 
					  example_lvgl_demo_ui(display);
 | 
				
			||||||
  _lock_release(&lvgl_api_lock);
 | 
					  val_lvgl_unlock();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void app_main(void) {
 | 
					void app_main(void) {
 | 
				
			||||||
  setup_lcd();
 | 
					  val_i2c_master_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  esp_lcd_panel_handle_t lcd_panel = val_setup_lcd();
 | 
				
			||||||
 | 
					  assert(lcd_panel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  val_setup_lvgl(lcd_panel);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,4 +15,7 @@ CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
 | 
				
			|||||||
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
 | 
					CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
 | 
				
			||||||
CONFIG_ESP_WIFI_DPP_SUPPORT=y
 | 
					CONFIG_ESP_WIFI_DPP_SUPPORT=y
 | 
				
			||||||
CONFIG_FREERTOS_HZ=1000
 | 
					CONFIG_FREERTOS_HZ=1000
 | 
				
			||||||
 | 
					CONFIG_LV_DEF_REFR_PERIOD=24
 | 
				
			||||||
 | 
					CONFIG_LV_USE_SYSMON=y
 | 
				
			||||||
 | 
					CONFIG_LV_USE_PERF_MONITOR=y
 | 
				
			||||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
 | 
					CONFIG_IDF_EXPERIMENTAL_FEATURES=y
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user