Refactor ownership of draw buffer data.
This commit is contained in:
		
							parent
							
								
									63899a606a
								
							
						
					
					
						commit
						00ea5dae20
					
				@ -2,7 +2,7 @@
 | 
				
			|||||||
dependencies:
 | 
					dependencies:
 | 
				
			||||||
  ## Required IDF version
 | 
					  ## Required IDF version
 | 
				
			||||||
  idf:
 | 
					  idf:
 | 
				
			||||||
    version: '>=4.1.0'
 | 
					    version: '>=5.3.0'
 | 
				
			||||||
  # # Put list of dependencies here
 | 
					  # # Put list of dependencies here
 | 
				
			||||||
  # # For components maintained by Espressif:
 | 
					  # # For components maintained by Espressif:
 | 
				
			||||||
  # component: "~1.0.0"
 | 
					  # component: "~1.0.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -2091,6 +2091,15 @@ CONFIG_MDNS_TASK_STACK_SIZE=4096
 | 
				
			|||||||
CONFIG_MDNS_TASK_AFFINITY_CPU0=y
 | 
					CONFIG_MDNS_TASK_AFFINITY_CPU0=y
 | 
				
			||||||
# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set
 | 
					# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set
 | 
				
			||||||
CONFIG_MDNS_TASK_AFFINITY=0x0
 | 
					CONFIG_MDNS_TASK_AFFINITY=0x0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# MDNS Memory Configuration
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL=y
 | 
				
			||||||
 | 
					CONFIG_MDNS_MEMORY_ALLOC_INTERNAL=y
 | 
				
			||||||
 | 
					# CONFIG_MDNS_MEMORY_CUSTOM_IMPL is not set
 | 
				
			||||||
 | 
					# end of MDNS Memory Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000
 | 
					CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000
 | 
				
			||||||
CONFIG_MDNS_TIMER_PERIOD_MS=100
 | 
					CONFIG_MDNS_TIMER_PERIOD_MS=100
 | 
				
			||||||
# CONFIG_MDNS_NETWORKING_SOCKET is not set
 | 
					# CONFIG_MDNS_NETWORKING_SOCKET is not set
 | 
				
			||||||
 | 
				
			|||||||
@ -12,8 +12,6 @@ For instructions on setting up the ESP-IDF see [04_-esp-idf-arduino](./../04_esp
 | 
				
			|||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Temperature and humidity sensor served on a web page within the local network.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To build this example run the following commands.
 | 
					To build this example run the following commands.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
idf_component_register(
 | 
					idf_component_register(
 | 
				
			||||||
    SRCS main.cpp display.cpp ssd1306.cpp i2c.h time_keeper.h panel.h panel_device.h
 | 
					    SRCS
 | 
				
			||||||
 | 
					    main.cpp display.cpp panel_device.cpp scoped_lock.cpp
 | 
				
			||||||
 | 
					    i2c.h time_keeper.h panel.h ssd1306.h
 | 
				
			||||||
    INCLUDE_DIRS .
 | 
					    INCLUDE_DIRS .
 | 
				
			||||||
    REQUIRES driver
 | 
					    REQUIRES driver
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
@ -10,19 +10,11 @@
 | 
				
			|||||||
// TODO: Remove this dependency by relocating SSD1306::oledb_buffer_
 | 
					// TODO: Remove this dependency by relocating SSD1306::oledb_buffer_
 | 
				
			||||||
#include "ssd1306.h"
 | 
					#include "ssd1306.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
 | 
					 | 
				
			||||||
// We must use a mutex to protect it.
 | 
					 | 
				
			||||||
_lock_t Display::ScopedLock::lv_lock_;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Static TimeKeeper for managing ESP timers across all displays.
 | 
					// Static TimeKeeper for managing ESP timers across all displays.
 | 
				
			||||||
TimeKeeper Display::timers_;
 | 
					TimeKeeper Display::timers_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Tag used for ESP logging.
 | 
					 | 
				
			||||||
const char * TAG = "Display";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Display::Display(IPanelDevice &device) :
 | 
					Display::Display(IPanelDevice &device) :
 | 
				
			||||||
    panel_(device),
 | 
					    panel_(device)
 | 
				
			||||||
    lv_buf_(nullptr)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  if (!lv_is_initialized()) {
 | 
					  if (!lv_is_initialized()) {
 | 
				
			||||||
    ESP_LOGI(TAG, "Initialize LVGL");
 | 
					    ESP_LOGI(TAG, "Initialize LVGL");
 | 
				
			||||||
@ -34,12 +26,7 @@ Display::Display(IPanelDevice &device) :
 | 
				
			|||||||
  // associate the i2c panel handle to the display
 | 
					  // associate the i2c panel handle to the display
 | 
				
			||||||
  lv_display_set_user_data(lv_display_, panel_.esp_panel_);
 | 
					  lv_display_set_user_data(lv_display_, panel_.esp_panel_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  register_draw_buffer();
 | 
					  panel_.register_display_callbacks(lv_display_);
 | 
				
			||||||
  register_lvgl_tick_timer();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Create LVGL FreeRTOS task");
 | 
					 | 
				
			||||||
  xTaskCreate(Display::lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE,
 | 
					 | 
				
			||||||
              nullptr, LVGL_TASK_PRIORITY, nullptr);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Display::set_text(const char *text,
 | 
					void Display::set_text(const char *text,
 | 
				
			||||||
@ -68,110 +55,3 @@ void Display::set_text(const char *text,
 | 
				
			|||||||
  lv_obj_set_width(obj, lv_display_get_horizontal_resolution(lv_display_));
 | 
					  lv_obj_set_width(obj, lv_display_get_horizontal_resolution(lv_display_));
 | 
				
			||||||
  lv_obj_align(obj, align, 0, 0);
 | 
					  lv_obj_align(obj, align, 0, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
bool Display::lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t,
 | 
					 | 
				
			||||||
                                  esp_lcd_panel_io_event_data_t *,
 | 
					 | 
				
			||||||
                                  void *user_ctx)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  auto *disp = (lv_display_t *) user_ctx;
 | 
					 | 
				
			||||||
  lv_display_flush_ready(disp);
 | 
					 | 
				
			||||||
  return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Display::lvgl_flush_cb(lv_display_t *display, const lv_area_t *area,
 | 
					 | 
				
			||||||
                            uint8_t *px_map) // NOLINT(*-non-const-parameter)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  auto panel_handle =
 | 
					 | 
				
			||||||
      (esp_lcd_panel_handle_t) lv_display_get_user_data(display);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Necessary because LVGL reserves 2x4 bytes in the buffer for a palette.
 | 
					 | 
				
			||||||
  // For more information about the monochrome, please refer to:
 | 
					 | 
				
			||||||
  // https://docs.lvgl.io/9.2/porting/display.html#monochrome-displays
 | 
					 | 
				
			||||||
  // Skip the palette here.
 | 
					 | 
				
			||||||
  px_map += LVGL_PALETTE_SIZE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  uint16_t hor_res = lv_display_get_physical_horizontal_resolution(display);
 | 
					 | 
				
			||||||
  int32_t x1 = area->x1;
 | 
					 | 
				
			||||||
  int32_t x2 = area->x2;
 | 
					 | 
				
			||||||
  int32_t y1 = area->y1;
 | 
					 | 
				
			||||||
  int32_t y2 = area->y2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (int32_t y = y1; y <= y2; y++) {
 | 
					 | 
				
			||||||
    for (int32_t x = x1; x <= x2; x++) {
 | 
					 | 
				
			||||||
      /* The order of bits is MSB first.
 | 
					 | 
				
			||||||
                  MSB           LSB
 | 
					 | 
				
			||||||
         bits      7 6 5 4 3 2 1 0
 | 
					 | 
				
			||||||
         pixels    0 1 2 3 4 5 6 7
 | 
					 | 
				
			||||||
                  Left         Right
 | 
					 | 
				
			||||||
      */
 | 
					 | 
				
			||||||
      bool chroma_color = (px_map[(hor_res >> 3) * y + (x >> 3)] &
 | 
					 | 
				
			||||||
                           1 << (7 - x % 8));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // Write to the buffer as required for the display.
 | 
					 | 
				
			||||||
      // It writes only 1-bit for monochrome displays mapped vertically.
 | 
					 | 
				
			||||||
      uint8_t *buf = SSD1306::oled_buffer_ + hor_res * (y >> 3) + (x);
 | 
					 | 
				
			||||||
      if (chroma_color) {
 | 
					 | 
				
			||||||
        (*buf) &= ~(1 << (y % 8));
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        (*buf) |= (1 << (y % 8));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  // Pass the draw buffer to the driver.
 | 
					 | 
				
			||||||
  ESP_ERROR_CHECK(
 | 
					 | 
				
			||||||
      esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1,
 | 
					 | 
				
			||||||
                                SSD1306::oled_buffer_));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Display::lvgl_increase_tick_cb(void *)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  // Tell LVGL how many milliseconds has elapsed
 | 
					 | 
				
			||||||
  lv_tick_inc(LVGL_TICK_PERIOD_MS);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[noreturn]] void Display::lvgl_port_task(void *)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Starting LVGL task");
 | 
					 | 
				
			||||||
  for (uint32_t time_to_next_ms = 0; true; usleep(1000 * time_to_next_ms)) {
 | 
					 | 
				
			||||||
    ScopedLock lock;
 | 
					 | 
				
			||||||
    time_to_next_ms = lv_timer_handler();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Display::register_draw_buffer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  // Create draw buffer.
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Allocate separate LVGL draw buffers");
 | 
					 | 
				
			||||||
  lv_buf_ = heap_caps_calloc(1, panel_.device_->lv_buf_size_,
 | 
					 | 
				
			||||||
                             MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
 | 
					 | 
				
			||||||
  assert(lv_buf_);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Set LVGL draw buffers");
 | 
					 | 
				
			||||||
  // Color format must be set first, LVGL9 suooprt new monochromatic format.
 | 
					 | 
				
			||||||
  lv_display_set_color_format(lv_display_, LV_COLOR_FORMAT_I1);
 | 
					 | 
				
			||||||
  lv_display_set_buffers(lv_display_, lv_buf_, nullptr,
 | 
					 | 
				
			||||||
                         panel_.device_->lv_buf_size_,
 | 
					 | 
				
			||||||
                         LV_DISPLAY_RENDER_MODE_FULL);
 | 
					 | 
				
			||||||
  lv_display_set_rotation(lv_display_, LV_DISPLAY_ROTATION_0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Set LVGL callback for flushing to the display");
 | 
					 | 
				
			||||||
  lv_display_set_flush_cb(lv_display_, Display::lvgl_flush_cb);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Register io panel callback for LVGL flush ready notification");
 | 
					 | 
				
			||||||
  const esp_lcd_panel_io_callbacks_t cbs = {
 | 
					 | 
				
			||||||
      .on_color_trans_done = Display::lvgl_flush_ready_cb,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  ESP_ERROR_CHECK(
 | 
					 | 
				
			||||||
      esp_lcd_panel_io_register_event_callbacks(panel_.esp_io_, &cbs,
 | 
					 | 
				
			||||||
                                                lv_display_));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Display::register_lvgl_tick_timer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  ESP_LOGI(TAG, "Use esp_timer to increase LVGL tick");
 | 
					 | 
				
			||||||
  const esp_timer_create_args_t esp_timer_args = {
 | 
					 | 
				
			||||||
      .callback = &Display::lvgl_increase_tick_cb,
 | 
					 | 
				
			||||||
      .name = "lvgl_tick"
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  timers_.start_new_timer_periodic(esp_timer_args, LVGL_TICK_PERIOD_MS * 1000);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,17 +1,13 @@
 | 
				
			|||||||
#ifndef DISPLAY_H
 | 
					#ifndef DISPLAY_H
 | 
				
			||||||
#define DISPLAY_H
 | 
					#define DISPLAY_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <widgets/label/lv_label.h>
 | 
					#include <widgets/label/lv_label.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <unordered_map>
 | 
					#include <unordered_map>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "time_keeper.h"
 | 
					#include "time_keeper.h"
 | 
				
			||||||
#include "panel.h"
 | 
					#include "panel.h"
 | 
				
			||||||
 | 
					#include "scoped_lock.h"
 | 
				
			||||||
#define LVGL_TICK_PERIOD_MS    5
 | 
					 | 
				
			||||||
#define LVGL_TASK_STACK_SIZE   (4 * 1024)
 | 
					 | 
				
			||||||
#define LVGL_TASK_PRIORITY     2
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Encapsulates lv_display handle and related LVGL operations.
 | 
					 * Encapsulates lv_display handle and related LVGL operations.
 | 
				
			||||||
@ -37,6 +33,8 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Display &operator=(Display &) = delete;
 | 
					  Display &operator=(Display &) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  using lv_display_handle_t = lv_display_t *;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  // GETTERS
 | 
					  // GETTERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,20 +43,20 @@ public:
 | 
				
			|||||||
   *
 | 
					   *
 | 
				
			||||||
   * @sa ScopedLock for calling custom LVGL API's not implemented by Display.
 | 
					   * @sa ScopedLock for calling custom LVGL API's not implemented by Display.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  [[nodiscard]] inline const lv_display_t *get() const { return lv_display_; }
 | 
					  [[nodiscard]] inline lv_display_handle_t get() const { return lv_display_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Getter for accessing LVGL display handle.
 | 
					   * Getter for accessing LVGL display handle.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @sa ScopedLock for calling custom LVGL API's not implemented by Display.
 | 
					   * @sa ScopedLock for calling custom LVGL API's not implemented by Display.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  [[nodiscard]] inline lv_display_t *get() { return lv_display_; }
 | 
					  [[nodiscard]] inline lv_display_handle_t get() { return lv_display_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Dereference operator for accessing LVGL display handle.
 | 
					  /// Dereference operator for accessing LVGL display handle.
 | 
				
			||||||
  [[nodiscard]] inline const lv_display_t *operator*() const { return get(); }
 | 
					  [[nodiscard]] inline lv_display_handle_t operator*() const { return get(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Dereference operator for accessing LVGL display handle.
 | 
					  /// Dereference operator for accessing LVGL display handle.
 | 
				
			||||||
  [[nodiscard]] inline lv_display_t *operator*() { return get(); }
 | 
					  [[nodiscard]] inline lv_display_handle_t operator*() { return get(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  // LVGL OPERATIONS
 | 
					  // LVGL OPERATIONS
 | 
				
			||||||
@ -77,24 +75,6 @@ public:
 | 
				
			|||||||
                lv_label_long_mode_t long_mode = LV_LABEL_LONG_SCROLL_CIRCULAR,
 | 
					                lv_label_long_mode_t long_mode = LV_LABEL_LONG_SCROLL_CIRCULAR,
 | 
				
			||||||
                lv_align_t align = LV_ALIGN_TOP_MID);
 | 
					                lv_align_t align = LV_ALIGN_TOP_MID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // TYPE DEFINITIONS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Obtains LVGL API mutex lock for the duration of local scope.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * LVGL library is not thread-safe, this lock should be held when making calls
 | 
					 | 
				
			||||||
   * to the LVGL API, and released as soon as possible when finished.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  struct ScopedLock {
 | 
					 | 
				
			||||||
    explicit ScopedLock() { _lock_acquire(&lv_lock_); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ~ScopedLock() { _lock_release(&lv_lock_); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Mutex used to protect LVGL API calls.
 | 
					 | 
				
			||||||
    static _lock_t lv_lock_;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  // PUBLIC STATIC MEMBERS
 | 
					  // PUBLIC STATIC MEMBERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -103,30 +83,6 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // PRIVATE METHODS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Registers LVGL draw buffers for this display.
 | 
					 | 
				
			||||||
  void register_draw_buffer();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // PRIVATE STATIC METHODS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Registers LVGL ticker timer callback for rendering this display.
 | 
					 | 
				
			||||||
  static void register_lvgl_tick_timer();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static bool lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t panel,
 | 
					 | 
				
			||||||
                                  esp_lcd_panel_io_event_data_t *data,
 | 
					 | 
				
			||||||
                                  void *user_ctx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static void lvgl_flush_cb(lv_display_t *display,
 | 
					 | 
				
			||||||
                            const lv_area_t *area,
 | 
					 | 
				
			||||||
                            uint8_t *px_map);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static void lvgl_increase_tick_cb(void *arg);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  [[noreturn]] static void lvgl_port_task(void *arg);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  // PRIVATE MEMBERS
 | 
					  // PRIVATE MEMBERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -134,10 +90,7 @@ private:
 | 
				
			|||||||
  Panel panel_;
 | 
					  Panel panel_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// LVGL display handle.
 | 
					  /// LVGL display handle.
 | 
				
			||||||
  lv_display_t *lv_display_;
 | 
					  lv_display_handle_t lv_display_;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// LVGL draw buffer associated with this Display's lv_display_t.
 | 
					 | 
				
			||||||
  void *lv_buf_;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * LVGL object handles stored in the LVGL screen associated with this Display.
 | 
					   * LVGL object handles stored in the LVGL screen associated with this Display.
 | 
				
			||||||
@ -146,6 +99,9 @@ private:
 | 
				
			|||||||
   * @sa lv_display_get_screen_active
 | 
					   * @sa lv_display_get_screen_active
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  std::unordered_map<const char *, lv_obj_t *> lv_objects_;
 | 
					  std::unordered_map<const char *, lv_obj_t *> lv_objects_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Tag used for ESP logging.
 | 
				
			||||||
 | 
					  constexpr static const char *TAG = "Display";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // DISPLAY_H
 | 
					#endif // DISPLAY_H
 | 
				
			||||||
 | 
				
			|||||||
@ -78,7 +78,7 @@ private:
 | 
				
			|||||||
  // PRIVATE MEMBERS
 | 
					  // PRIVATE MEMBERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Tag used for ESP logging.
 | 
					  /// Tag used for ESP logging.
 | 
				
			||||||
  const char * TAG = "I2C";
 | 
					  constexpr static const char *TAG = "I2C";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif //I2C_H
 | 
					#endif //I2C_H
 | 
				
			||||||
 | 
				
			|||||||
@ -57,13 +57,24 @@ struct Panel {
 | 
				
			|||||||
  /// ESP LCD panel configuration structure.
 | 
					  /// ESP LCD panel configuration structure.
 | 
				
			||||||
  esp_lcd_panel_dev_config_t esp_panel_config_;
 | 
					  esp_lcd_panel_dev_config_t esp_panel_config_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Registers LVGL draw buffers and callbacks for rendering the display.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param display_handle Pointer to the LVGL display to use for rendering.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  inline void register_display_callbacks(lv_display_t *display_handle) const
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    device_->register_draw_buffer(display_handle, esp_io_);
 | 
				
			||||||
 | 
					    device_->register_lvgl_tick_timer();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  // PRIVATE MEMBERS
 | 
					  // PRIVATE MEMBERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Tag used for ESP logging.
 | 
					  /// Tag used for ESP logging.
 | 
				
			||||||
  const char * TAG = "Panel";
 | 
					  constexpr static const char *TAG = "Panel";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif //PANEL_H
 | 
					#endif //PANEL_H
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										135
									
								
								esp/cpp/07_lcd-panel-i2c/main/panel_device.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								esp/cpp/07_lcd-panel-i2c/main/panel_device.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					#include "panel_device.h"
 | 
				
			||||||
 | 
					#include "display.h"
 | 
				
			||||||
 | 
					#include "scoped_lock.h"
 | 
				
			||||||
 | 
					#include "time_keeper.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// To use LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted data.
 | 
				
			||||||
 | 
					uint8_t IPanelDevice::oled_buffer_[LCD_H_RES * LCD_V_RES / 8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool IPanelDevice::lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t,
 | 
				
			||||||
 | 
					                                       esp_lcd_panel_io_event_data_t *,
 | 
				
			||||||
 | 
					                                       void *user_ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto *disp = (lv_display_t *) user_ctx;
 | 
				
			||||||
 | 
					  lv_display_flush_ready(disp);
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void IPanelDevice::lvgl_flush_cb(lv_display_t *display, const lv_area_t *area,
 | 
				
			||||||
 | 
					                                 uint8_t *px_map) // NOLINT(*-non-const-parameter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  auto panel_handle =
 | 
				
			||||||
 | 
					      (esp_lcd_panel_handle_t) lv_display_get_user_data(display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Necessary because LVGL reserves 2x4 bytes in the buffer for a palette.
 | 
				
			||||||
 | 
					  // For more information about the monochrome, please refer to:
 | 
				
			||||||
 | 
					  // https://docs.lvgl.io/9.2/porting/display.html#monochrome-displays
 | 
				
			||||||
 | 
					  // Skip the palette here.
 | 
				
			||||||
 | 
					  px_map += LVGL_PALETTE_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint16_t hor_res = lv_display_get_physical_horizontal_resolution(display);
 | 
				
			||||||
 | 
					  int32_t x1 = area->x1;
 | 
				
			||||||
 | 
					  int32_t x2 = area->x2;
 | 
				
			||||||
 | 
					  int32_t y1 = area->y1;
 | 
				
			||||||
 | 
					  int32_t y2 = area->y2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (int32_t y = y1; y <= y2; y++) {
 | 
				
			||||||
 | 
					    for (int32_t x = x1; x <= x2; x++) {
 | 
				
			||||||
 | 
					      /* The order of bits is MSB first.
 | 
				
			||||||
 | 
					                  MSB           LSB
 | 
				
			||||||
 | 
					         bits      7 6 5 4 3 2 1 0
 | 
				
			||||||
 | 
					         pixels    0 1 2 3 4 5 6 7
 | 
				
			||||||
 | 
					                  Left         Right
 | 
				
			||||||
 | 
					      */
 | 
				
			||||||
 | 
					      bool chroma_color = (px_map[(hor_res >> 3) * y + (x >> 3)] &
 | 
				
			||||||
 | 
					                           1 << (7 - x % 8));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Write to the buffer as required for the display.
 | 
				
			||||||
 | 
					      // It writes only 1-bit for monochrome displays mapped vertically.
 | 
				
			||||||
 | 
					      uint8_t *buf = IPanelDevice::oled_buffer_ + hor_res * (y >> 3) + (x);
 | 
				
			||||||
 | 
					      if (chroma_color) {
 | 
				
			||||||
 | 
					        (*buf) &= ~(1 << (y % 8));
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        (*buf) |= (1 << (y % 8));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // Pass the draw buffer to the driver.
 | 
				
			||||||
 | 
					  ESP_ERROR_CHECK(
 | 
				
			||||||
 | 
					      esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1,
 | 
				
			||||||
 | 
					                                IPanelDevice::oled_buffer_));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void IPanelDevice::lvgl_increase_tick_cb(void *)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Tell LVGL how many milliseconds has elapsed
 | 
				
			||||||
 | 
					  lv_tick_inc(LVGL_TICK_PERIOD_MS);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[noreturn]] void IPanelDevice::lvgl_port_task(void *)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Optionally initialize some LVGL objects here before entering loop below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Starting LVGL task");
 | 
				
			||||||
 | 
					  for (uint32_t time_to_next_ms = 0; true; usleep(1000 * time_to_next_ms)) {
 | 
				
			||||||
 | 
					    // Obtain LVGL API lock before calling any LVGL methods.
 | 
				
			||||||
 | 
					    ScopedLock lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Optionally handle LVGL input or event logic here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update LVGL periodic timers.
 | 
				
			||||||
 | 
					    time_to_next_ms = lv_timer_handler();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void IPanelDevice::register_draw_buffer(lv_display_t *display_handle,
 | 
				
			||||||
 | 
					                                        esp_lcd_panel_io_handle_t io_handle)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Create draw buffer.
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Allocate separate LVGL draw buffers");
 | 
				
			||||||
 | 
					  lv_buf_ = heap_caps_calloc(1, lv_buf_size_,
 | 
				
			||||||
 | 
					                             MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
 | 
				
			||||||
 | 
					  assert(lv_buf_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Set LVGL draw buffers");
 | 
				
			||||||
 | 
					  // Color format must be set first, LVGL9 suooprt new monochromatic format.
 | 
				
			||||||
 | 
					  lv_display_set_color_format(display_handle, LV_COLOR_FORMAT_I1);
 | 
				
			||||||
 | 
					  lv_display_set_buffers(display_handle, lv_buf_, nullptr,
 | 
				
			||||||
 | 
					                         lv_buf_size_,
 | 
				
			||||||
 | 
					                         LV_DISPLAY_RENDER_MODE_FULL);
 | 
				
			||||||
 | 
					  lv_display_set_rotation(display_handle, LV_DISPLAY_ROTATION_0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Set LVGL callback for flushing to the display");
 | 
				
			||||||
 | 
					  lv_display_set_flush_cb(display_handle, IPanelDevice::lvgl_flush_cb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Register io panel callback for LVGL flush ready notification");
 | 
				
			||||||
 | 
					  const esp_lcd_panel_io_callbacks_t cbs = {
 | 
				
			||||||
 | 
					      .on_color_trans_done = IPanelDevice::lvgl_flush_ready_cb,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  ESP_ERROR_CHECK(
 | 
				
			||||||
 | 
					      esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs,
 | 
				
			||||||
 | 
					                                                display_handle));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void IPanelDevice::register_lvgl_tick_timer()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Use esp_timer to increase LVGL tick");
 | 
				
			||||||
 | 
					  const esp_timer_create_args_t esp_timer_args = {
 | 
				
			||||||
 | 
					      .callback = &IPanelDevice::lvgl_increase_tick_cb,
 | 
				
			||||||
 | 
					      // Data to pass to the IPanelDevice::lvgl_port_task callback.
 | 
				
			||||||
 | 
					      .arg = nullptr,
 | 
				
			||||||
 | 
					      .name = "lvgl_tick",
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  Display::timers_.start_new_timer_periodic(esp_timer_args,
 | 
				
			||||||
 | 
					                                            LVGL_TICK_PERIOD_MS * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // LVGL requires a FreeRTOS task for running it's event loop.
 | 
				
			||||||
 | 
					  // The lvgl_port_task callback can update the UI or handle input logic.
 | 
				
			||||||
 | 
					  // For this basic example we don't do either of these things.
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Create LVGL FreeRTOS task");
 | 
				
			||||||
 | 
					  // Optionally set user data to pass to LVGL's FreeRTOS task callback here.
 | 
				
			||||||
 | 
					  void *user_data = nullptr;
 | 
				
			||||||
 | 
					  xTaskCreate(lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE,
 | 
				
			||||||
 | 
					              user_data, LVGL_TASK_PRIORITY, nullptr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -13,6 +13,12 @@
 | 
				
			|||||||
// LVGL reserves 2x4 bytes in the buffer to be used as a palette.
 | 
					// LVGL reserves 2x4 bytes in the buffer to be used as a palette.
 | 
				
			||||||
// This additional space must be added to the IPanelDevice::buf_size_.
 | 
					// This additional space must be added to the IPanelDevice::buf_size_.
 | 
				
			||||||
#define LVGL_PALETTE_SIZE      8
 | 
					#define LVGL_PALETTE_SIZE      8
 | 
				
			||||||
 | 
					#define LVGL_TICK_PERIOD_MS    5
 | 
				
			||||||
 | 
					#define LVGL_TASK_STACK_SIZE   (4 * 1024)
 | 
				
			||||||
 | 
					#define LVGL_TASK_PRIORITY     2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LCD_H_RES              128
 | 
				
			||||||
 | 
					#define LCD_V_RES              64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Encapsulates vendor specific ESP LCD panel initialization logic.
 | 
					 * Encapsulates vendor specific ESP LCD panel initialization logic.
 | 
				
			||||||
@ -139,28 +145,116 @@ public:
 | 
				
			|||||||
  /// ESP LCD panel IO configuration.
 | 
					  /// ESP LCD panel IO configuration.
 | 
				
			||||||
  esp_lcd_panel_io_i2c_config_t esp_io_config_;
 | 
					  esp_lcd_panel_io_i2c_config_t esp_io_config_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Registers LVGL draw buffers for this display.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * An implementation of the interface can optionally override this method to
 | 
				
			||||||
 | 
					   * provide custom LVGL callbacks and display configurations.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param display_handle LVGL display handle to use for rendering.
 | 
				
			||||||
 | 
					   * @param io_handle IO handle for the ESP LCD panel.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  virtual void register_draw_buffer(lv_display_t *display_handle,
 | 
				
			||||||
 | 
					                                    esp_lcd_panel_io_handle_t io_handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Registers LVGL ticker timer callback for rendering this display.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * An implementation of the interface can optionally override this method to
 | 
				
			||||||
 | 
					   * provide custom LVGL callbacks and tick configurations.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  virtual void register_lvgl_tick_timer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  // PRIVATE METHODS
 | 
					  // PRIVATE METHODS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Initializes the ESP panel using vendor specific APIs and configurations.
 | 
					    * Initializes the ESP panel using vendor specific APIs and configurations.
 | 
				
			||||||
   * This method should implement any setup logic specific to the device.
 | 
					    * This method should implement any setup logic specific to the device.
 | 
				
			||||||
   *
 | 
					    *
 | 
				
			||||||
   * @param config ESP LCD panel configuration.
 | 
					    * @param config ESP LCD panel configuration.
 | 
				
			||||||
   * @param io ESP LCD panel IO handle.
 | 
					    * @param io ESP LCD panel IO handle.
 | 
				
			||||||
   * @param [out] panel ESP LCD panel handle output pointer location.
 | 
					    * @param [out] panel ESP LCD panel handle output pointer location.
 | 
				
			||||||
   */
 | 
					    */
 | 
				
			||||||
  virtual void init_panel(esp_lcd_panel_dev_config_t &config,
 | 
					  virtual void init_panel(esp_lcd_panel_dev_config_t &config,
 | 
				
			||||||
                          esp_lcd_panel_io_handle_t io,
 | 
					                          esp_lcd_panel_io_handle_t io,
 | 
				
			||||||
                          esp_lcd_panel_handle_t &panel) = 0;
 | 
					                          esp_lcd_panel_handle_t &panel) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // PRIVATE STATIC METHODS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The callback invoked when panel IO finishes transferring color data.
 | 
				
			||||||
 | 
					 * This signals that the panel is ready to flush image data to the display.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param panel LCD panel IO handles.
 | 
				
			||||||
 | 
					 * @param data Panel IO event data, fed by driver.
 | 
				
			||||||
 | 
					 * @param user_ctx User data, passed from `esp_lcd_panel_io_xxx_config_t`.
 | 
				
			||||||
 | 
					 * @return Whether a high priority task has been waken up by this function.
 | 
				
			||||||
 | 
					 * @sa SSD1306::SSD1306 for setting user_ctx data passed to the callback.
 | 
				
			||||||
 | 
					 * @sa register_rendering_data for overriding this callback.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					  static bool lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t panel,
 | 
				
			||||||
 | 
					                                  esp_lcd_panel_io_event_data_t *data,
 | 
				
			||||||
 | 
					                                  void *user_ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * The callback invoked for flushing the rendered image to the display.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * `px_map` contains the rendered image as raw pixel map and it should be
 | 
				
			||||||
 | 
					   *  copied to `area` on the display.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param display LVGL display handle to use for rendering.
 | 
				
			||||||
 | 
					   * @param area Area of the display being flushed.
 | 
				
			||||||
 | 
					   * @param px_map Rendered image data for writing to the display area.
 | 
				
			||||||
 | 
					   * @sa register_rendering_data for overriding this callback.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static void lvgl_flush_cb(lv_display_t *display,
 | 
				
			||||||
 | 
					                            const lv_area_t *area,
 | 
				
			||||||
 | 
					                            uint8_t *px_map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Callback invoked for every period of the timer.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * This callback _must_ call lv_tick_inc to inform LVGL how much time has
 | 
				
			||||||
 | 
					   * elapsed since the last period of the timer.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param data User data passed to the callback.
 | 
				
			||||||
 | 
					   * @sa register_lvgl_tick_timer for setting user data and the tick period of
 | 
				
			||||||
 | 
					   *        the timer, or overriding this callback entirely.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static void lvgl_increase_tick_cb(void *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * FreeRTOS task callback invoked for handling LVGL events or updating the UI.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * This function is intentionally an endless loop and should never return.
 | 
				
			||||||
 | 
					   * LVGL initialization logic can optionally be added before entering the loop.
 | 
				
			||||||
 | 
					   * Input logic can optionally be handled within the loop.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * This callback _must_ call lv_timer_handler to handle LVGL periodic timers.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param data User data passed to the callback.
 | 
				
			||||||
 | 
					   * @sa register_lvgl_tick_timer for overriding this callback.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  [[noreturn]] static void lvgl_port_task(void *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Draw buffer for this panel device.
 | 
				
			||||||
 | 
					   * For LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted data.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static uint8_t oled_buffer_[LCD_H_RES * LCD_V_RES / 8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  // PRIVATE MEMBERS
 | 
					  // PRIVATE MEMBERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// LVGL draw buffer associated with this Display's lv_display_t.
 | 
				
			||||||
 | 
					  void *lv_buf_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Tag used for ESP logging.
 | 
					  /// Tag used for ESP logging.
 | 
				
			||||||
  const char * TAG = "IPanelDevice";
 | 
					  constexpr static const char *TAG = "IPanelDevice";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // PANEL_DEVICE_H
 | 
					#endif // PANEL_DEVICE_H
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										5
									
								
								esp/cpp/07_lcd-panel-i2c/main/scoped_lock.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								esp/cpp/07_lcd-panel-i2c/main/scoped_lock.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					#include "scoped_lock.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
 | 
				
			||||||
 | 
					// We must use a mutex to protect it.
 | 
				
			||||||
 | 
					_lock_t ScopedLock::lv_lock_;
 | 
				
			||||||
							
								
								
									
										21
									
								
								esp/cpp/07_lcd-panel-i2c/main/scoped_lock.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								esp/cpp/07_lcd-panel-i2c/main/scoped_lock.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					#ifndef SCOPED_LOCK_H
 | 
				
			||||||
 | 
					#define SCOPED_LOCK_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Obtains LVGL API mutex lock for the duration of local scope.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * LVGL library is not thread-safe, this lock should be held when making calls
 | 
				
			||||||
 | 
					 * to the LVGL API, and released as soon as possible when finished.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct ScopedLock {
 | 
				
			||||||
 | 
					  explicit ScopedLock() { _lock_acquire(&lv_lock_); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ~ScopedLock() { _lock_release(&lv_lock_); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Mutex used to protect LVGL API calls.
 | 
				
			||||||
 | 
					  static _lock_t lv_lock_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // SCOPED_LOCK_H
 | 
				
			||||||
@ -1,6 +0,0 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
#include "ssd1306.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// To use LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted data.
 | 
					 | 
				
			||||||
// TODO: Remove this and SSD1306 can be header only.
 | 
					 | 
				
			||||||
uint8_t SSD1306::oled_buffer_[LCD_H_RES * LCD_V_RES / 8];
 | 
					 | 
				
			||||||
@ -25,8 +25,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// According to SSD1306 datasheet.
 | 
					// According to SSD1306 datasheet.
 | 
				
			||||||
// https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
 | 
					// https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
 | 
				
			||||||
#define LCD_H_RES              128
 | 
					 | 
				
			||||||
#define LCD_V_RES              64
 | 
					 | 
				
			||||||
#define I2C_HW_ADDR            0x3C
 | 
					#define I2C_HW_ADDR            0x3C
 | 
				
			||||||
#define LCD_PIXEL_CLOCK_HZ     (400 * 1000)
 | 
					#define LCD_PIXEL_CLOCK_HZ     (400 * 1000)
 | 
				
			||||||
// Bit number used to represent command and parameter
 | 
					// Bit number used to represent command and parameter
 | 
				
			||||||
@ -62,6 +60,9 @@ public:
 | 
				
			|||||||
      IPanelDevice(i2c,
 | 
					      IPanelDevice(i2c,
 | 
				
			||||||
                   (esp_lcd_panel_io_i2c_config_t) {
 | 
					                   (esp_lcd_panel_io_i2c_config_t) {
 | 
				
			||||||
                       .dev_addr = I2C_HW_ADDR,
 | 
					                       .dev_addr = I2C_HW_ADDR,
 | 
				
			||||||
 | 
					                       // User data to pass to the LVGL flush_ready callback.
 | 
				
			||||||
 | 
					                       // See IPanelDevice::lvgl_flush_ready_cb
 | 
				
			||||||
 | 
					                       .user_ctx = nullptr,
 | 
				
			||||||
                       .control_phase_bytes = 1,
 | 
					                       .control_phase_bytes = 1,
 | 
				
			||||||
                       .dc_bit_offset = 6,
 | 
					                       .dc_bit_offset = 6,
 | 
				
			||||||
                       .lcd_cmd_bits = LCD_CMD_BITS,
 | 
					                       .lcd_cmd_bits = LCD_CMD_BITS,
 | 
				
			||||||
@ -95,12 +96,6 @@ public:
 | 
				
			|||||||
  /// SSD1306 configuration structure.
 | 
					  /// SSD1306 configuration structure.
 | 
				
			||||||
  esp_lcd_panel_ssd1306_config_t ssd1306_config_;
 | 
					  esp_lcd_panel_ssd1306_config_t ssd1306_config_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Draw buffer for this panel device.
 | 
					 | 
				
			||||||
   * For LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted data.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  static uint8_t oled_buffer_[LCD_H_RES * LCD_V_RES / 8];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
@ -113,12 +108,6 @@ private:
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io, &config, &panel));
 | 
					    ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io, &config, &panel));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // PRIVATE MEMBERS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Tag used for ESP logging.
 | 
					 | 
				
			||||||
  const char * TAG = "SSD1306";
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // SSD1306_H
 | 
					#endif // SSD1306_H
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
#ifndef TIME_KEEPER_H
 | 
					#ifndef TIME_KEEPER_H
 | 
				
			||||||
#define TIME_KEEPER_H
 | 
					#define TIME_KEEPER_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,13 +19,13 @@ struct Timer {
 | 
				
			|||||||
  explicit Timer(esp_timer_create_args_t args) :
 | 
					  explicit Timer(esp_timer_create_args_t args) :
 | 
				
			||||||
      args_(args), esp_timer_(nullptr)
 | 
					      args_(args), esp_timer_(nullptr)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    ESP_LOGI(TAG, "Creating esp_timer with name: %s", args_.name);
 | 
					    ESP_LOGI(TAG, "Creating esp_timer with name: '%s'", args_.name);
 | 
				
			||||||
    ESP_ERROR_CHECK(esp_timer_create(&args, &esp_timer_));
 | 
					    ESP_ERROR_CHECK(esp_timer_create(&args, &esp_timer_));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ~Timer()
 | 
					  ~Timer()
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    ESP_LOGI(TAG, "Destroying esp_timer with name: %s", args_.name);
 | 
					    ESP_LOGI(TAG, "Destroying esp_timer with name: '%s'", args_.name);
 | 
				
			||||||
    ESP_ERROR_CHECK(esp_timer_delete(esp_timer_));
 | 
					    ESP_ERROR_CHECK(esp_timer_delete(esp_timer_));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -51,7 +50,7 @@ private:
 | 
				
			|||||||
  // PRIVATE MEMBERS
 | 
					  // PRIVATE MEMBERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Tag used for ESP logging.
 | 
					  /// Tag used for ESP logging.
 | 
				
			||||||
  const char * TAG = "Timer";
 | 
					  constexpr static const char *TAG = "Timer";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -86,11 +85,11 @@ struct TimeKeeper {
 | 
				
			|||||||
   * @sa get_handle
 | 
					   * @sa get_handle
 | 
				
			||||||
   * @sa operator[](const char*)
 | 
					   * @sa operator[](const char*)
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  TimerHandle create_timer(esp_timer_create_args_t args)
 | 
					  [[maybe_unused]] TimerHandle create_timer(esp_timer_create_args_t args)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    auto rt = managed_timers_.emplace(args.name, args);
 | 
					    auto rt = managed_timers_.emplace(args.name, args);
 | 
				
			||||||
    if (!rt.second) {
 | 
					    if (!rt.second) {
 | 
				
			||||||
      ESP_LOGE(TAG, "Display::Timer already exists with name %s", args.name);
 | 
					      ESP_LOGE(TAG, "Timer already exists with name '%s'", args.name);
 | 
				
			||||||
      return nullptr;
 | 
					      return nullptr;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return &rt.first->second;
 | 
					    return &rt.first->second;
 | 
				
			||||||
@ -105,7 +104,9 @@ struct TimeKeeper {
 | 
				
			|||||||
  /// Delete a Timer with the given name.
 | 
					  /// Delete a Timer with the given name.
 | 
				
			||||||
  [[maybe_unused]] void delete_timer(const char *name)
 | 
					  [[maybe_unused]] void delete_timer(const char *name)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    managed_timers_.erase(name);
 | 
					    if (managed_timers_.erase(name) == 0) {
 | 
				
			||||||
 | 
					      ESP_LOGE(TAG, "Attempt to delete timer that does not exist: '%s'", name);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Create a Timer with the ESP args and call esp_timer_start_periodic.
 | 
					  /// Create a Timer with the ESP args and call esp_timer_start_periodic.
 | 
				
			||||||
@ -149,7 +150,7 @@ private:
 | 
				
			|||||||
  std::unordered_map<const char *, Timer> managed_timers_;
 | 
					  std::unordered_map<const char *, Timer> managed_timers_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Tag used for ESP logging.
 | 
					  /// Tag used for ESP logging.
 | 
				
			||||||
  const char * TAG = "TimeKeeper";
 | 
					  constexpr static const char *TAG = "TimeKeeper";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // TIME_KEEPER_H
 | 
					#endif // TIME_KEEPER_H
 | 
				
			||||||
 | 
				
			|||||||
@ -2091,7 +2091,15 @@ CONFIG_MDNS_TASK_STACK_SIZE=4096
 | 
				
			|||||||
CONFIG_MDNS_TASK_AFFINITY_CPU0=y
 | 
					CONFIG_MDNS_TASK_AFFINITY_CPU0=y
 | 
				
			||||||
# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set
 | 
					# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set
 | 
				
			||||||
CONFIG_MDNS_TASK_AFFINITY=0x0
 | 
					CONFIG_MDNS_TASK_AFFINITY=0x0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# MDNS Memory Configuration
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL=y
 | 
					CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL=y
 | 
				
			||||||
 | 
					CONFIG_MDNS_MEMORY_ALLOC_INTERNAL=y
 | 
				
			||||||
 | 
					# CONFIG_MDNS_MEMORY_CUSTOM_IMPL is not set
 | 
				
			||||||
 | 
					# end of MDNS Memory Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000
 | 
					CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000
 | 
				
			||||||
CONFIG_MDNS_TIMER_PERIOD_MS=100
 | 
					CONFIG_MDNS_TIMER_PERIOD_MS=100
 | 
				
			||||||
# CONFIG_MDNS_NETWORKING_SOCKET is not set
 | 
					# CONFIG_MDNS_NETWORKING_SOCKET is not set
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user