Add time_keeper.h
+ Refactor most classes to header only.
This commit is contained in:
		
							parent
							
								
									75b51f0c7c
								
							
						
					
					
						commit
						509b57fedb
					
				| @ -1,5 +1 @@ | |||||||
| idf_component_register( | idf_component_register(SRC_DIRS . INCLUDE_DIRS .) | ||||||
|     SRCS main.cpp i2c.h display.cpp panel.cpp panel_device.cpp ssd1306.cpp |  | ||||||
|     INCLUDE_DIRS . |  | ||||||
|     REQUIRES driver |  | ||||||
| ) |  | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ | |||||||
| #include <mutex> | #include <mutex> | ||||||
| 
 | 
 | ||||||
| #include "display.h" | #include "display.h" | ||||||
|  | 
 | ||||||
|  | // 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.
 | // LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
 | ||||||
| @ -13,7 +15,7 @@ | |||||||
| _lock_t Display::ScopedLock::lv_lock_; | _lock_t Display::ScopedLock::lv_lock_; | ||||||
| 
 | 
 | ||||||
| // Static TimeKeeper for managing ESP timers across all displays.
 | // Static TimeKeeper for managing ESP timers across all displays.
 | ||||||
| Display::TimeKeeper Display::timers_; | TimeKeeper Display::timers_; | ||||||
| 
 | 
 | ||||||
| Display::Display(IPanelDevice &device) : | Display::Display(IPanelDevice &device) : | ||||||
|     panel_(device), |     panel_(device), | ||||||
| @ -47,10 +49,14 @@ void Display::set_text(const char *text, | |||||||
| 
 | 
 | ||||||
|   ESP_LOGI(TAG, "Display LVGL Scroll Text"); |   ESP_LOGI(TAG, "Display LVGL Scroll Text"); | ||||||
|   lv_obj_t *scr = lv_display_get_screen_active(lv_display_); |   lv_obj_t *scr = lv_display_get_screen_active(lv_display_); | ||||||
|   lv_objects_[name] = lv_label_create(scr); | 
 | ||||||
|  |   // Create the label if it's `name` doesn't already exist in the map keys.
 | ||||||
|  |   if (!lv_objects_.count(name)) { | ||||||
|  |     lv_objects_[name] = lv_label_create(scr); | ||||||
|  |   } | ||||||
|  |   auto obj = lv_objects_[name]; | ||||||
| 
 | 
 | ||||||
|   // Circular scroll.
 |   // Circular scroll.
 | ||||||
|   auto obj = lv_objects_[name]; |  | ||||||
|   lv_label_set_long_mode(obj, long_mode); |   lv_label_set_long_mode(obj, long_mode); | ||||||
|   lv_label_set_text(obj, text); |   lv_label_set_text(obj, text); | ||||||
| 
 | 
 | ||||||
| @ -60,9 +66,9 @@ void Display::set_text(const char *text, | |||||||
|   lv_obj_align(obj, align, 0, 0); |   lv_obj_align(obj, align, 0, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Display::lvgl_flush_ready(esp_lcd_panel_io_handle_t, | bool Display::lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t, | ||||||
|                                esp_lcd_panel_io_event_data_t *, |                                   esp_lcd_panel_io_event_data_t *, | ||||||
|                                void *user_ctx) |                                   void *user_ctx) | ||||||
| { | { | ||||||
|   auto *disp = (lv_display_t *) user_ctx; |   auto *disp = (lv_display_t *) user_ctx; | ||||||
|   lv_display_flush_ready(disp); |   lv_display_flush_ready(disp); | ||||||
| @ -114,7 +120,7 @@ void Display::lvgl_flush_cb(lv_display_t *display, const lv_area_t *area, | |||||||
|                                 SSD1306::oled_buffer_)); |                                 SSD1306::oled_buffer_)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Display::lvgl_increase_tick(void *) | void Display::lvgl_increase_tick_cb(void *) | ||||||
| { | { | ||||||
|   // Tell LVGL how many milliseconds has elapsed
 |   // Tell LVGL how many milliseconds has elapsed
 | ||||||
|   lv_tick_inc(LVGL_TICK_PERIOD_MS); |   lv_tick_inc(LVGL_TICK_PERIOD_MS); | ||||||
| @ -152,7 +158,7 @@ void Display::register_draw_buffer() | |||||||
| 
 | 
 | ||||||
|   ESP_LOGI(TAG, "Register io panel callback for LVGL flush ready notification"); |   ESP_LOGI(TAG, "Register io panel callback for LVGL flush ready notification"); | ||||||
|   const esp_lcd_panel_io_callbacks_t cbs = { |   const esp_lcd_panel_io_callbacks_t cbs = { | ||||||
|       .on_color_trans_done = Display::lvgl_flush_ready, |       .on_color_trans_done = Display::lvgl_flush_ready_cb, | ||||||
|   }; |   }; | ||||||
|   ESP_ERROR_CHECK( |   ESP_ERROR_CHECK( | ||||||
|       esp_lcd_panel_io_register_event_callbacks(panel_.esp_io_, &cbs, |       esp_lcd_panel_io_register_event_callbacks(panel_.esp_io_, &cbs, | ||||||
| @ -163,7 +169,7 @@ void Display::register_lvgl_tick_timer() | |||||||
| { | { | ||||||
|   ESP_LOGI(TAG, "Use esp_timer to increase LVGL tick"); |   ESP_LOGI(TAG, "Use esp_timer to increase LVGL tick"); | ||||||
|   const esp_timer_create_args_t esp_timer_args = { |   const esp_timer_create_args_t esp_timer_args = { | ||||||
|       .callback = &Display::lvgl_increase_tick, |       .callback = &Display::lvgl_increase_tick_cb, | ||||||
|       .name = "lvgl_tick" |       .name = "lvgl_tick" | ||||||
|   }; |   }; | ||||||
|   timers_.start_new_timer_periodic(esp_timer_args, LVGL_TICK_PERIOD_MS * 1000); |   timers_.start_new_timer_periodic(esp_timer_args, LVGL_TICK_PERIOD_MS * 1000); | ||||||
|  | |||||||
| @ -1,13 +1,12 @@ | |||||||
| #ifndef DISPLAY_H | #ifndef DISPLAY_H | ||||||
| #define DISPLAY_H | #define DISPLAY_H | ||||||
| 
 | 
 | ||||||
| #include <esp_timer.h> |  | ||||||
| 
 | 
 | ||||||
| #include <widgets/label/lv_label.h> | #include <widgets/label/lv_label.h> | ||||||
| 
 | 
 | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <memory> |  | ||||||
| 
 | 
 | ||||||
|  | #include "time_keeper.h" | ||||||
| #include "panel.h" | #include "panel.h" | ||||||
| 
 | 
 | ||||||
| #define LVGL_TICK_PERIOD_MS    5 | #define LVGL_TICK_PERIOD_MS    5 | ||||||
| @ -15,148 +14,125 @@ | |||||||
| #define LVGL_TASK_PRIORITY     2 | #define LVGL_TASK_PRIORITY     2 | ||||||
| 
 | 
 | ||||||
| class Display { | class Display { | ||||||
|   struct ScopedLock { |  | ||||||
|     explicit ScopedLock() { _lock_acquire(&lv_lock_); } |  | ||||||
| 
 |  | ||||||
|     ~ScopedLock() { _lock_release(&lv_lock_); } |  | ||||||
| 
 |  | ||||||
|     // LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
 |  | ||||||
|     // We must use a mutex to protect it.
 |  | ||||||
|     static _lock_t lv_lock_; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   struct Timer { |  | ||||||
|     explicit Timer(esp_timer_create_args_t args) : args_(args) |  | ||||||
|     { |  | ||||||
|       ESP_LOGI(TAG, "Creating esp_timer with name: %s", args.name); |  | ||||||
|       ESP_ERROR_CHECK(esp_timer_create(&args, &esp_timer_)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ~Timer() |  | ||||||
|     { |  | ||||||
|       ESP_ERROR_CHECK(esp_timer_delete(esp_timer_)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     [[maybe_unused]] esp_timer_create_args_t args_{}; |  | ||||||
|     esp_timer_handle_t esp_timer_ = nullptr; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   struct TimeKeeper { |  | ||||||
|     using TimerHandle = Timer *; |  | ||||||
| 
 |  | ||||||
|     // Timers should only be accessed using this method.
 |  | ||||||
|     // For this reason managed_timers_ is private.
 |  | ||||||
|     TimerHandle get_handle(const char *name) |  | ||||||
|     { |  | ||||||
|       return &managed_timers_.at(name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     TimerHandle operator[](const char *name) { return get_handle(name); } |  | ||||||
| 
 |  | ||||||
|     TimerHandle create_timer(esp_timer_create_args_t args) |  | ||||||
|     { |  | ||||||
|       auto rt = managed_timers_.emplace(args.name, args); |  | ||||||
|       if (!rt.second) { |  | ||||||
|         ESP_LOGE(TAG, "Failed to insert timer into Display::managed_timers_"); |  | ||||||
|         return nullptr; |  | ||||||
|       } |  | ||||||
|       return &rt.first->second; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     [[maybe_unused]] void stop_timer(const char *name) |  | ||||||
|     { |  | ||||||
|       ESP_ERROR_CHECK(esp_timer_stop(get_handle(name)->esp_timer_)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     [[maybe_unused]] void delete_timer(const char *name) |  | ||||||
|     { |  | ||||||
|       managed_timers_.erase(name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     [[maybe_unused]] void |  | ||||||
|     start_new_timer_periodic(esp_timer_create_args_t args, |  | ||||||
|                              uint64_t period) |  | ||||||
|     { |  | ||||||
|       start_timer_periodic(create_timer(args)->args_.name, period); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     [[maybe_unused]] void start_timer_periodic(const char *name, |  | ||||||
|                                                uint64_t period) |  | ||||||
|     { |  | ||||||
|       ESP_ERROR_CHECK( |  | ||||||
|           esp_timer_start_periodic(get_handle(name)->esp_timer_, period)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     [[maybe_unused]] void |  | ||||||
|     start_new_timer_once(esp_timer_create_args_t args, |  | ||||||
|                          uint64_t timeout_us) |  | ||||||
|     { |  | ||||||
|       start_timer_once(create_timer(args)->args_.name, timeout_us); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     [[maybe_unused]] void start_timer_once(const char *name, |  | ||||||
|                                            uint64_t timeout_us) |  | ||||||
|     { |  | ||||||
|       ESP_ERROR_CHECK( |  | ||||||
|           esp_timer_start_once(get_handle(name)->esp_timer_, timeout_us)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|   private: |  | ||||||
| 
 |  | ||||||
|     // Timers should only be accessed using the get_handle method.
 |  | ||||||
|     // ~Timer() will delete the timer if called.
 |  | ||||||
|     std::unordered_map<const char *, Timer> managed_timers_; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
|  |   //
 | ||||||
|  |   // CONSTRUCTORS
 | ||||||
|  | 
 | ||||||
|  |   Display(const Display &) = delete; | ||||||
|  | 
 | ||||||
|  |   Display(Display &) = delete; | ||||||
|  | 
 | ||||||
|  |   Display &operator=(Display &) = delete; | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Construct a new Display using an IPanelDevice. | ||||||
|  |    * | ||||||
|  |    * @param device An object that implements the IPanelDevice interface. | ||||||
|  |    */ | ||||||
|   explicit Display(IPanelDevice &device); |   explicit Display(IPanelDevice &device); | ||||||
| 
 | 
 | ||||||
|   ~Display() = default; |   ~Display() = default; | ||||||
| 
 | 
 | ||||||
|  |   //
 | ||||||
|  |   // GETTERS
 | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Getter for accessing LVGL display handle. | ||||||
|  |    * | ||||||
|  |    * @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 const lv_display_t *get() const { return lv_display_; } | ||||||
| 
 | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Getter for accessing LVGL display handle. | ||||||
|  |    * | ||||||
|  |    * @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_t *get() { return lv_display_; } | ||||||
| 
 | 
 | ||||||
|  |   /// Dereference operator for accessing LVGL display handle.
 | ||||||
|   [[nodiscard]] inline const lv_display_t *operator*() const { return get(); } |   [[nodiscard]] inline const lv_display_t *operator*() const { return get(); } | ||||||
| 
 | 
 | ||||||
|  |   /// Dereference operator for accessing LVGL display handle.
 | ||||||
|   [[nodiscard]] inline lv_display_t *operator*() { return get(); } |   [[nodiscard]] inline lv_display_t *operator*() { return get(); } | ||||||
| 
 | 
 | ||||||
|  |   //
 | ||||||
|  |   // LVGL OPERATIONS
 | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Create a LVGL label with some given text on the current display. | ||||||
|  |    * The name of the object can be reused to change text on this label later. | ||||||
|  |    * | ||||||
|  |    * @param text Text to write to the display. | ||||||
|  |    * @param name Name for the LVGL label object associated with this text. | ||||||
|  |    * @param long_mode LVGL long mode for text wider than the current display. | ||||||
|  |    * @param align LVGL alignment to use for placing the label on the display. | ||||||
|  |    */ | ||||||
|   void set_text(const char *text, |   void set_text(const char *text, | ||||||
|                 const char *name, |                 const char *name, | ||||||
|                 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); | ||||||
| 
 | 
 | ||||||
|   static bool lvgl_flush_ready(esp_lcd_panel_io_handle_t panel, |   //
 | ||||||
|                                esp_lcd_panel_io_event_data_t *data, |   // TYPE DEFINITIONS
 | ||||||
|                                void *user_ctx); | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Obtains LVGL API mutex lock for the duration of local scope. | ||||||
|  |    * | ||||||
|  |    * LVGL library is not thread-safe, this example calls LVGL APIs from tasks. | ||||||
|  |    */ | ||||||
|  |   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 TimeKeeper for managing ESP timers across all displays.
 | ||||||
|  |   static TimeKeeper timers_; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |   /// Registers LVGL draw buffers for this display.
 | ||||||
|  |   void register_draw_buffer(); | ||||||
|  | 
 | ||||||
|  |   /// 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, |   static void lvgl_flush_cb(lv_display_t *display, | ||||||
|                             const lv_area_t *area, |                             const lv_area_t *area, | ||||||
|                             uint8_t *px_map); |                             uint8_t *px_map); | ||||||
| 
 | 
 | ||||||
|   static void lvgl_increase_tick(void *arg); |   static void lvgl_increase_tick_cb(void *arg); | ||||||
| 
 | 
 | ||||||
|   [[noreturn]] static void lvgl_port_task(void *arg); |   [[noreturn]] static void lvgl_port_task(void *arg); | ||||||
| 
 | 
 | ||||||
|   // Public static TimeKeeper for managing ESP timers across all displays.
 |   //
 | ||||||
|   static TimeKeeper timers_; |   // PRIVATE MEMBERS
 | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|   void register_draw_buffer(); |  | ||||||
| 
 |  | ||||||
|   static void register_lvgl_tick_timer(); |  | ||||||
| 
 | 
 | ||||||
|  |   /// Panel associated with this Display.
 | ||||||
|   Panel panel_; |   Panel panel_; | ||||||
| 
 | 
 | ||||||
|  |   /// LVGL display handle.
 | ||||||
|   lv_display_t *lv_display_; |   lv_display_t *lv_display_; | ||||||
| 
 | 
 | ||||||
|   // Draw buffer associated with the lv_display_t.
 |   /// LVGL draw buffer associated with this Display's lv_display_t.
 | ||||||
|   void *lv_buf_; |   void *lv_buf_; | ||||||
| 
 | 
 | ||||||
|   // Objects stored in the screen associated with this display.
 |   /**
 | ||||||
|   // @sa Display::set_text
 |    * LVGL object handles stored in the LVGL screen associated with this Display. | ||||||
|   // @sa lv_display_get_screen_active
 |    * | ||||||
|  |    * @sa Display::set_text | ||||||
|  |    * @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_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,6 +18,11 @@ void setup() | |||||||
|              LV_LABEL_LONG_SCROLL, |              LV_LABEL_LONG_SCROLL, | ||||||
|              LV_ALIGN_CENTER); |              LV_ALIGN_CENTER); | ||||||
| 
 | 
 | ||||||
|  |   d.set_text("Test test changing text", | ||||||
|  |              "test-text1", | ||||||
|  |              LV_LABEL_LONG_SCROLL, | ||||||
|  |              LV_ALIGN_CENTER); | ||||||
|  | 
 | ||||||
|   d.set_text("Hello hello hello hello hello hello hello hello!", "test-text2"); |   d.set_text("Hello hello hello hello hello hello hello hello!", "test-text2"); | ||||||
| 
 | 
 | ||||||
|   d.set_text("A random sentence with no meaning at all.", |   d.set_text("A random sentence with no meaning at all.", | ||||||
|  | |||||||
| @ -1,4 +0,0 @@ | |||||||
| #ifndef  MAIN_H |  | ||||||
| #define  MAIN_H |  | ||||||
| 
 |  | ||||||
| #endif // MAIN_H
 |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| 
 |  | ||||||
| #include "panel.h" |  | ||||||
| 
 |  | ||||||
| Panel::Panel(IPanelDevice &device) : |  | ||||||
|     device_(&device), |  | ||||||
|     esp_io_(nullptr), |  | ||||||
|     esp_panel_(nullptr), |  | ||||||
|     esp_panel_config_( |  | ||||||
|         (esp_lcd_panel_dev_config_t) { |  | ||||||
|             .reset_gpio_num = device_->rst_num_, |  | ||||||
|             .bits_per_pixel = 1, |  | ||||||
|             .vendor_config = device_->vendor_config(), |  | ||||||
|         } |  | ||||||
|     ) |  | ||||||
| { |  | ||||||
|   esp_io_ = device_->create_io_handle(); |  | ||||||
| 
 |  | ||||||
|   device_->create_panel(esp_panel_config_, esp_io_, esp_panel_); |  | ||||||
| 
 |  | ||||||
|   ESP_LOGI(TAG, "Resetting panel display"); |  | ||||||
|   ESP_ERROR_CHECK(esp_lcd_panel_reset(esp_panel_)); |  | ||||||
|   ESP_LOGI(TAG, "Initializing panel display"); |  | ||||||
|   ESP_ERROR_CHECK(esp_lcd_panel_init(esp_panel_)); |  | ||||||
|   ESP_LOGI(TAG, "Turning on panel display"); |  | ||||||
|   ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(esp_panel_, true)); |  | ||||||
| } |  | ||||||
| @ -5,7 +5,29 @@ | |||||||
| 
 | 
 | ||||||
| class Panel { | class Panel { | ||||||
| public: | public: | ||||||
|   explicit Panel(IPanelDevice &device); |   explicit Panel(IPanelDevice &device) : | ||||||
|  |       device_(&device), | ||||||
|  |       esp_io_(nullptr), | ||||||
|  |       esp_panel_(nullptr), | ||||||
|  |       esp_panel_config_( | ||||||
|  |           (esp_lcd_panel_dev_config_t) { | ||||||
|  |               .reset_gpio_num = device_->rst_num_, | ||||||
|  |               .bits_per_pixel = 1, | ||||||
|  |               .vendor_config = device_->vendor_config(), | ||||||
|  |           } | ||||||
|  |       ) | ||||||
|  |   { | ||||||
|  |     esp_io_ = device_->create_io_handle(); | ||||||
|  | 
 | ||||||
|  |     device_->create_panel(esp_panel_config_, esp_io_, esp_panel_); | ||||||
|  | 
 | ||||||
|  |     ESP_LOGI(TAG, "Resetting panel display"); | ||||||
|  |     ESP_ERROR_CHECK(esp_lcd_panel_reset(esp_panel_)); | ||||||
|  |     ESP_LOGI(TAG, "Initializing panel display"); | ||||||
|  |     ESP_ERROR_CHECK(esp_lcd_panel_init(esp_panel_)); | ||||||
|  |     ESP_LOGI(TAG, "Turning on panel display"); | ||||||
|  |     ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(esp_panel_, true)); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   ~Panel() = default; |   ~Panel() = default; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,2 +0,0 @@ | |||||||
| 
 |  | ||||||
| #include "panel_device.h" |  | ||||||
| @ -2,22 +2,5 @@ | |||||||
| #include "ssd1306.h" | #include "ssd1306.h" | ||||||
| 
 | 
 | ||||||
| // To use LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted data.
 | // 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]; | uint8_t SSD1306::oled_buffer_[LCD_H_RES * LCD_V_RES / 8]; | ||||||
| 
 |  | ||||||
| SSD1306::SSD1306(I2C &i2c, |  | ||||||
|                  esp_lcd_panel_ssd1306_config_t config, |  | ||||||
|                  int width, |  | ||||||
|                  int height) : |  | ||||||
|     IPanelDevice(i2c, |  | ||||||
|                  (esp_lcd_panel_io_i2c_config_t) { |  | ||||||
|                      .dev_addr = I2C_HW_ADDR, |  | ||||||
|                      .control_phase_bytes = 1, |  | ||||||
|                      .dc_bit_offset = 6, |  | ||||||
|                      .lcd_cmd_bits = LCD_CMD_BITS, |  | ||||||
|                      .lcd_param_bits = LCD_PARAM_BITS, |  | ||||||
|                      .scl_speed_hz = LCD_PIXEL_CLOCK_HZ, |  | ||||||
|                  }, |  | ||||||
|                  width, |  | ||||||
|                  height |  | ||||||
|     ), |  | ||||||
|     ssd1306_config_(config) { } |  | ||||||
|  | |||||||
| @ -18,12 +18,15 @@ | |||||||
| 
 | 
 | ||||||
| #include "panel_device.h" | #include "panel_device.h" | ||||||
| 
 | 
 | ||||||
| // According to SSD1306 datasheet
 | // According to specific display hardware.
 | ||||||
| // https://www.digikey.com/en/products/detail/winstar-display/WEA012864DWPP3N00003/20533255
 | // https://www.digikey.com/en/products/detail/winstar-display/WEA012864DWPP3N00003/20533255
 | ||||||
| #define SCREEN_WIDTH           128  // OLED display width, in pixels.
 | #define SCREEN_WIDTH           128  // OLED display width, in pixels.
 | ||||||
| #define SCREEN_HEIGHT          64  // OLED display height, in pixels.
 | #define SCREEN_HEIGHT          64  // OLED display height, in pixels.
 | ||||||
| #define LCD_H_RES              SCREEN_WIDTH | 
 | ||||||
| #define LCD_V_RES              SCREEN_HEIGHT | // According to SSD1306 datasheet.
 | ||||||
|  | // 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
 | ||||||
| @ -40,7 +43,20 @@ public: | |||||||
|           esp_lcd_panel_ssd1306_config_t config, |           esp_lcd_panel_ssd1306_config_t config, | ||||||
|           int width = SCREEN_WIDTH, |           int width = SCREEN_WIDTH, | ||||||
|           int height = SCREEN_HEIGHT |           int height = SCREEN_HEIGHT | ||||||
|   ); |   ) : | ||||||
|  |       IPanelDevice(i2c, | ||||||
|  |                    (esp_lcd_panel_io_i2c_config_t) { | ||||||
|  |                        .dev_addr = I2C_HW_ADDR, | ||||||
|  |                        .control_phase_bytes = 1, | ||||||
|  |                        .dc_bit_offset = 6, | ||||||
|  |                        .lcd_cmd_bits = LCD_CMD_BITS, | ||||||
|  |                        .lcd_param_bits = LCD_PARAM_BITS, | ||||||
|  |                        .scl_speed_hz = LCD_PIXEL_CLOCK_HZ, | ||||||
|  |                    }, | ||||||
|  |                    width, | ||||||
|  |                    height | ||||||
|  |       ), | ||||||
|  |       ssd1306_config_(config) { } | ||||||
| 
 | 
 | ||||||
|   ~SSD1306() final = default; |   ~SSD1306() final = default; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										131
									
								
								esp/cpp/07_lcd-panel/main/time_keeper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								esp/cpp/07_lcd-panel/main/time_keeper.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | 
 | ||||||
|  | #ifndef TIME_KEEPER_H | ||||||
|  | #define TIME_KEEPER_H | ||||||
|  | 
 | ||||||
|  | #include <esp_log.h> | ||||||
|  | #include <esp_timer.h> | ||||||
|  | 
 | ||||||
|  | #include "i2c.h" | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Stores arguments and ESP timer handle for a Timer. | ||||||
|  |  * In general Timers should be used via the TimeKeeper interface only. | ||||||
|  |  * | ||||||
|  |  * Timers cannot be copied, and are only created by a TimeKeeper instance. | ||||||
|  |  * The public way to access a Timer is by obtaining a TimerHandle (Timer *). | ||||||
|  |  * The TimeKeeper can delete existing Timers, calling it's destructor. | ||||||
|  |  * The ESP timer will be deleted when this class desctructor is called. | ||||||
|  |  */ | ||||||
|  | struct Timer { | ||||||
|  |   Timer(const Timer &) = delete; | ||||||
|  | 
 | ||||||
|  |   Timer(Timer &) = delete; | ||||||
|  | 
 | ||||||
|  |   Timer &operator=(Timer &) = delete; | ||||||
|  | 
 | ||||||
|  |   explicit Timer(esp_timer_create_args_t args) : | ||||||
|  |       args_(args), esp_timer_(nullptr) | ||||||
|  |   { | ||||||
|  |     ESP_LOGI(TAG, "Creating esp_timer with name: %s", args_.name); | ||||||
|  |     ESP_ERROR_CHECK(esp_timer_create(&args, &esp_timer_)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ~Timer() | ||||||
|  |   { | ||||||
|  |     ESP_LOGI(TAG, "Destroying esp_timer with name: %s", args_.name); | ||||||
|  |     ESP_ERROR_CHECK(esp_timer_delete(esp_timer_)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Arguments passed to ESP API during timer creation.
 | ||||||
|  |   esp_timer_create_args_t args_; | ||||||
|  | 
 | ||||||
|  |   /// ESP timer handle.
 | ||||||
|  |   esp_timer_handle_t esp_timer_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * ESP timer mananger class. | ||||||
|  |  * | ||||||
|  |  * Timers should only be accessed using the get_handle method. | ||||||
|  |  * If the Timer destructor is called the underlying ESP timer will be deleted. | ||||||
|  |  */ | ||||||
|  | struct TimeKeeper { | ||||||
|  |   /// Timer handle type used for referring to Timers.
 | ||||||
|  |   using TimerHandle = Timer *; | ||||||
|  | 
 | ||||||
|  |   TimerHandle get_handle(const char *name) | ||||||
|  |   { | ||||||
|  |     return &managed_timers_.at(name); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   TimerHandle operator[](const char *name) { return get_handle(name); } | ||||||
|  | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Create a new managed Timer with the provided ESP arguments. | ||||||
|  |    * The timer can be retrieved later using the args.name field value. | ||||||
|  |    * | ||||||
|  |    * @param args ESP timer creation arguments. | ||||||
|  |    * @return TimerHandle Handle to a Timer managed by this TimeKeeper. | ||||||
|  |    * @sa get_handle | ||||||
|  |    * @sa operator[](const char*) | ||||||
|  |    */ | ||||||
|  |   TimerHandle create_timer(esp_timer_create_args_t args) | ||||||
|  |   { | ||||||
|  |     auto rt = managed_timers_.emplace(args.name, args); | ||||||
|  |     if (!rt.second) { | ||||||
|  |       ESP_LOGE(TAG, "Display::Timer already exists with name %s", args.name); | ||||||
|  |       return nullptr; | ||||||
|  |     } | ||||||
|  |     return &rt.first->second; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Stop a Timer with the given name.
 | ||||||
|  |   [[maybe_unused]] void stop_timer(const char *name) | ||||||
|  |   { | ||||||
|  |     ESP_ERROR_CHECK(esp_timer_stop(get_handle(name)->esp_timer_)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Delete a Timer with the given name.
 | ||||||
|  |   [[maybe_unused]] void delete_timer(const char *name) | ||||||
|  |   { | ||||||
|  |     managed_timers_.erase(name); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Create a Timer with the ESP args and call esp_timer_start_periodic.
 | ||||||
|  |   [[maybe_unused]] void | ||||||
|  |   start_new_timer_periodic(esp_timer_create_args_t args, | ||||||
|  |                            uint64_t period) | ||||||
|  |   { | ||||||
|  |     start_timer_periodic(create_timer(args)->args_.name, period); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Calls esp_timer_start_periodic on the Timer with the given name.
 | ||||||
|  |   [[maybe_unused]] void start_timer_periodic(const char *name, | ||||||
|  |                                              uint64_t period) | ||||||
|  |   { | ||||||
|  |     ESP_ERROR_CHECK( | ||||||
|  |         esp_timer_start_periodic(get_handle(name)->esp_timer_, period)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Create a Timer with the ESP args and call esp_timer_start_once.
 | ||||||
|  |   [[maybe_unused]] void | ||||||
|  |   start_new_timer_once(esp_timer_create_args_t args, | ||||||
|  |                        uint64_t timeout_us) | ||||||
|  |   { | ||||||
|  |     start_timer_once(create_timer(args)->args_.name, timeout_us); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Calls esp_timer_start_once on the Timer with the given name.
 | ||||||
|  |   [[maybe_unused]] void start_timer_once(const char *name, | ||||||
|  |                                          uint64_t timeout_us) | ||||||
|  |   { | ||||||
|  |     ESP_ERROR_CHECK( | ||||||
|  |         esp_timer_start_once(get_handle(name)->esp_timer_, timeout_us)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |   /// Existing ESP timers created for this TimeKeeper instance.
 | ||||||
|  |   std::unordered_map<const char *, Timer> managed_timers_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // TIME_KEEPER_H
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user