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( | ||||
|     SRCS main.cpp i2c.h display.cpp panel.cpp panel_device.cpp ssd1306.cpp | ||||
|     INCLUDE_DIRS . | ||||
|     REQUIRES driver | ||||
| ) | ||||
| idf_component_register(SRC_DIRS . INCLUDE_DIRS .) | ||||
|  | ||||
| @ -6,6 +6,8 @@ | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include "display.h" | ||||
| 
 | ||||
| // TODO: Remove this dependency by relocating SSD1306::oledb_buffer_
 | ||||
| #include "ssd1306.h" | ||||
| 
 | ||||
| // LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
 | ||||
| @ -13,7 +15,7 @@ | ||||
| _lock_t Display::ScopedLock::lv_lock_; | ||||
| 
 | ||||
| // Static TimeKeeper for managing ESP timers across all displays.
 | ||||
| Display::TimeKeeper Display::timers_; | ||||
| TimeKeeper Display::timers_; | ||||
| 
 | ||||
| Display::Display(IPanelDevice &device) : | ||||
|     panel_(device), | ||||
| @ -47,10 +49,14 @@ void Display::set_text(const char *text, | ||||
| 
 | ||||
|   ESP_LOGI(TAG, "Display LVGL Scroll Text"); | ||||
|   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.
 | ||||
|   auto obj = lv_objects_[name]; | ||||
|   lv_label_set_long_mode(obj, long_mode); | ||||
|   lv_label_set_text(obj, text); | ||||
| 
 | ||||
| @ -60,9 +66,9 @@ void Display::set_text(const char *text, | ||||
|   lv_obj_align(obj, align, 0, 0); | ||||
| } | ||||
| 
 | ||||
| bool Display::lvgl_flush_ready(esp_lcd_panel_io_handle_t, | ||||
|                                esp_lcd_panel_io_event_data_t *, | ||||
|                                void *user_ctx) | ||||
| 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); | ||||
| @ -114,7 +120,7 @@ void Display::lvgl_flush_cb(lv_display_t *display, const lv_area_t *area, | ||||
|                                 SSD1306::oled_buffer_)); | ||||
| } | ||||
| 
 | ||||
| void Display::lvgl_increase_tick(void *) | ||||
| void Display::lvgl_increase_tick_cb(void *) | ||||
| { | ||||
|   // Tell LVGL how many milliseconds has elapsed
 | ||||
|   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"); | ||||
|   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_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"); | ||||
|   const esp_timer_create_args_t esp_timer_args = { | ||||
|       .callback = &Display::lvgl_increase_tick, | ||||
|       .callback = &Display::lvgl_increase_tick_cb, | ||||
|       .name = "lvgl_tick" | ||||
|   }; | ||||
|   timers_.start_new_timer_periodic(esp_timer_args, LVGL_TICK_PERIOD_MS * 1000); | ||||
|  | ||||
| @ -1,13 +1,12 @@ | ||||
| #ifndef DISPLAY_H | ||||
| #define DISPLAY_H | ||||
| 
 | ||||
| #include <esp_timer.h> | ||||
| 
 | ||||
| #include <widgets/label/lv_label.h> | ||||
| 
 | ||||
| #include <unordered_map> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "time_keeper.h" | ||||
| #include "panel.h" | ||||
| 
 | ||||
| #define LVGL_TICK_PERIOD_MS    5 | ||||
| @ -15,148 +14,125 @@ | ||||
| #define LVGL_TASK_PRIORITY     2 | ||||
| 
 | ||||
| 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: | ||||
|   //
 | ||||
|   // 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); | ||||
| 
 | ||||
|   ~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_; } | ||||
| 
 | ||||
|   /**
 | ||||
|    * 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_; } | ||||
| 
 | ||||
|   /// Dereference operator for accessing LVGL display handle.
 | ||||
|   [[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(); } | ||||
| 
 | ||||
|   //
 | ||||
|   // 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, | ||||
|                 const char *name, | ||||
|                 lv_label_long_mode_t long_mode = LV_LABEL_LONG_SCROLL_CIRCULAR, | ||||
|                 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, | ||||
|                                void *user_ctx); | ||||
|   //
 | ||||
|   // TYPE DEFINITIONS
 | ||||
| 
 | ||||
|   /**
 | ||||
|    * 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, | ||||
|                             const lv_area_t *area, | ||||
|                             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); | ||||
| 
 | ||||
|   // Public static TimeKeeper for managing ESP timers across all displays.
 | ||||
|   static TimeKeeper timers_; | ||||
| 
 | ||||
| private: | ||||
|   void register_draw_buffer(); | ||||
| 
 | ||||
|   static void register_lvgl_tick_timer(); | ||||
|   //
 | ||||
|   // PRIVATE MEMBERS
 | ||||
| 
 | ||||
|   /// Panel associated with this Display.
 | ||||
|   Panel panel_; | ||||
| 
 | ||||
|   /// LVGL display handle.
 | ||||
|   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_; | ||||
| 
 | ||||
|   // Objects stored in the screen associated with this display.
 | ||||
|   // @sa Display::set_text
 | ||||
|   // @sa lv_display_get_screen_active
 | ||||
|   /**
 | ||||
|    * LVGL object handles stored in the LVGL screen associated with this Display. | ||||
|    * | ||||
|    * @sa Display::set_text | ||||
|    * @sa lv_display_get_screen_active | ||||
|    */ | ||||
|   std::unordered_map<const char *, lv_obj_t *> lv_objects_; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -18,6 +18,11 @@ void setup() | ||||
|              LV_LABEL_LONG_SCROLL, | ||||
|              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("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 { | ||||
| 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; | ||||
| 
 | ||||
|  | ||||
| @ -1,2 +0,0 @@ | ||||
| 
 | ||||
| #include "panel_device.h" | ||||
| @ -2,22 +2,5 @@ | ||||
| #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]; | ||||
| 
 | ||||
| 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" | ||||
| 
 | ||||
| // According to SSD1306 datasheet
 | ||||
| // According to specific display hardware.
 | ||||
| // https://www.digikey.com/en/products/detail/winstar-display/WEA012864DWPP3N00003/20533255
 | ||||
| #define SCREEN_WIDTH           128  // OLED display width, 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 LCD_PIXEL_CLOCK_HZ     (400 * 1000) | ||||
| // Bit number used to represent command and parameter
 | ||||
| @ -40,7 +43,20 @@ public: | ||||
|           esp_lcd_panel_ssd1306_config_t config, | ||||
|           int width = SCREEN_WIDTH, | ||||
|           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; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										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