Finish cleanup.

This commit is contained in:
Shaun Reed 2025-02-16 14:31:50 -05:00
parent 96b6a8bec9
commit e2bb406139
9 changed files with 183 additions and 45 deletions

View File

@ -1 +1,5 @@
idf_component_register(SRC_DIRS . INCLUDE_DIRS .)
idf_component_register(
SRCS main.cpp display.cpp ssd1306.cpp i2c.h time_keeper.h panel.h panel_device.h
INCLUDE_DIRS .
REQUIRES driver
)

View File

@ -129,11 +129,9 @@ void Display::lvgl_increase_tick_cb(void *)
[[noreturn]] void Display::lvgl_port_task(void *)
{
ESP_LOGI(TAG, "Starting LVGL task");
for (uint32_t time_till_next_ms = 0; true;) {
_lock_acquire(&ScopedLock::lv_lock_);
time_till_next_ms = lv_timer_handler();
_lock_release(&ScopedLock::lv_lock_);
usleep(1000 * time_till_next_ms);
for (uint32_t time_to_next_ms = 0; true; usleep(1000 * time_to_next_ms)) {
ScopedLock lock;
time_to_next_ms = lv_timer_handler();
}
}

View File

@ -13,19 +13,17 @@
#define LVGL_TASK_STACK_SIZE (4 * 1024)
#define LVGL_TASK_PRIORITY 2
/**
* Encapsulates lv_display handle and related LVGL operations.
* Contains helper methods that wrap basic LVGL operations such as drawing text.
* The underlying lv_display can be obtained for manual LVGL operations.
* @sa ScopedLock
* @sa Display::get()
*/
class Display {
public:
//
// CONSTRUCTORS
Display(const Display &) = delete;
Display(Display &) = delete;
Display &operator=(Display &) = delete;
/**
* Construct a new Display using an IPanelDevice.
* Construct a new Display using an object that implements IPanelDevice.
*
* @param device An object that implements the IPanelDevice interface.
*/
@ -33,6 +31,12 @@ public:
~Display() = default;
Display(const Display &) = delete;
Display(Display &) = delete;
Display &operator=(Display &) = delete;
//
// GETTERS

View File

@ -5,14 +5,23 @@
#include <driver/i2c_master.h>
// TODO: Refactor tags for-each class.
static const char *TAG = "lcd-panel";
/**
* Encapsulates ESP I2C creation and usage.
*/
struct I2C {
/**
* Construct and initialize an ESP I2C master bus.
* An I2C constructor may only be called one time in any application.
*
* @param sda GPIO pin number for SDA
* @param scl GPIO pin number for SCL
* @param rst GPIO pin number for RST
*/
I2C(gpio_num_t sda, gpio_num_t scl, int rst = -1) :
esp_i2c_bus_(nullptr),
rst_num_(rst),
esp_bus_config_(
(i2c_master_bus_config_t) {
I2C((i2c_master_bus_config_t) {
.i2c_port = I2C_BUS_PORT,
.sda_io_num = sda,
.scl_io_num = scl,
@ -21,22 +30,44 @@ struct I2C {
.flags {
.enable_internal_pullup = true,
},
}
)
},
rst
) { }
/**
* Construct an ESP I2C master bus given a specific ESP I2C configuration.
* An I2C constructor may only be called one time in any application.
*
* @param config ESP I2C master bus configuration.
* @param rst GPIO pin number for RST
*/
explicit I2C(i2c_master_bus_config_t config, int rst = -1) :
esp_bus_config_(config),
rst_num_(rst)
{
i2c_master_bus_handle_t i2c;
ESP_LOGI(TAG, "Initializing new master I2C bus");
ESP_ERROR_CHECK(i2c_new_master_bus(&esp_bus_config_, &esp_i2c_bus_));
ESP_ERROR_CHECK(i2c_new_master_bus(&esp_bus_config_, &i2c));
}
~I2C() = default;
// TODO: Can you use the I2C get_master_bus API in a static method?
i2c_master_bus_handle_t esp_i2c_bus_;
/**
* ESP I2C master bus handle getter.
* This will fail if an I2C instance was never constructed.
*/
static i2c_master_bus_handle_t get()
{
i2c_master_bus_handle_t i2c = nullptr;
ESP_ERROR_CHECK(i2c_master_get_bus_handle(0, &i2c));
return i2c;
}
int rst_num_;
private:
/// ESP I2C master bus configuration used during initialization.
i2c_master_bus_config_t esp_bus_config_;
/// RST GPIO pin number.
int rst_num_;
};
#endif //I2C_H

View File

@ -3,8 +3,19 @@
#include "panel_device.h"
class Panel {
public:
/**
* Encapsulates esp_lcd_panel handles and operations.
* The only exception is esp_lcd_panel_io_i2c_config_t owned by IPanelDevice.
* This structure requires details specific to the implementing device.
*
* Panel is an implementation detail of Display, not meant to be used directly.
*/
struct Panel {
/**
* Construct a new Panel using an object that implements IPanelDevice.
*
* @param device An object that implements the IPanelDevice interface.
*/
explicit Panel(IPanelDevice &device) :
device_(&device),
esp_io_(nullptr),
@ -31,13 +42,16 @@ public:
~Panel() = default;
/// Pointer to object using known interface for IPanelDevice.
IPanelDevice *device_;
/// ESP LCD panel IO handle.
esp_lcd_panel_io_handle_t esp_io_;
/// ESP LCD panel handle.
esp_lcd_panel_handle_t esp_panel_;
private:
/// ESP LCD panel configuration structure.
esp_lcd_panel_dev_config_t esp_panel_config_;
};

View File

@ -10,10 +10,28 @@
#include "i2c.h"
// LVGL reserves 2x4 bytes in the buffer to be used as a palette.
// This additional space must be added to the IPanelDevice::buf_size_.
#define LVGL_PALETTE_SIZE 8
/**
* Encapsulates vendor specific ESP LCD pabel initialization logic.
* This pure virtual interface can be inherited from for using new LCD devices.
* See the SSD1306 example for implementing a PanelDevice for NT35510 or ST7789.
*
* At this time only I2C is supported.
* Classes that inherit from this interface should likely be marked final.
*/
class IPanelDevice {
public:
/**
* Construct an IPanelDevice.
*
* @param i2c I2C object. Eventually this will mature to IProtocol or similar.
* @param config I2C configuration for this device.
* @param height Height of the device screen in pixels.
* @param width Width of the device screen in pixels.
*/
explicit IPanelDevice(I2C &i2c,
esp_lcd_panel_io_i2c_config_t config,
int width,
@ -21,6 +39,15 @@ public:
IPanelDevice(i2c, config, width, height,
width * height / 8 + LVGL_PALETTE_SIZE) { }
/**
* Construct an IPanelDevice.
*
* @param i2c I2C object. Eventually this will mature to IProtocol or similar.
* @param config I2C configuration for this device.
* @param height Height of the device screen in pixels.
* @param width Width of the device screen in pixels.
* @param draw_buf_size Size of the draw buffer for this device.
*/
explicit IPanelDevice(I2C &i2c,
esp_lcd_panel_io_i2c_config_t io_config,
int width,
@ -30,11 +57,15 @@ public:
height_(height),
rst_num_(i2c.rst_num_),
lv_buf_size_(draw_buf_size),
esp_i2c_bus_(i2c.esp_i2c_bus_),
esp_io_config_(io_config) { }
virtual ~IPanelDevice() = default;
/**
* Create an LVGL display using the width and height of this device.
*
* @return Handle to the created LVGL display.
*/
[[nodiscard]] lv_display_t *create_display() const
{
auto display = lv_display_create(width_, height_);
@ -42,15 +73,28 @@ public:
return display;
}
/**
* Create an ESP LCD panel IO handle.
*
* @return The created ESP LCD panel IO handle.
*/
[[nodiscard]] esp_lcd_panel_io_handle_t create_io_handle()
{
ESP_LOGI(TAG, "Creating panel IO handle");
esp_lcd_panel_io_handle_t handle = nullptr;
ESP_ERROR_CHECK(
esp_lcd_new_panel_io_i2c(esp_i2c_bus_, &esp_io_config_, &handle));
esp_lcd_new_panel_io_i2c(I2C::get(), &esp_io_config_, &handle));
return handle;
}
/**
* Create and initialize an ESP panel handle.
* IPanelDevice implementors must initialize the panel within init_panel.
*
* @param config ESP LCD panel configuration.
* @param io ESP LCD panel IO handle.
* @param [out] panel ESP LCD panel handle output pointer location.
*/
void create_panel(esp_lcd_panel_dev_config_t &config,
esp_lcd_panel_io_handle_t io,
esp_lcd_panel_handle_t &panel)
@ -62,25 +106,43 @@ public:
}
ESP_LOGI(TAG, "Install SSD1306 panel driver");
// Call pure virtual method responsible for initializing the panel handle.
init_panel(config, io, panel);
}
/**
* Retrieve the device specific vendor configuration structure.
*
* @return Address of vendor configuration structure.
* @sa SSD1306::vendor_config
*/
virtual void *vendor_config() = 0;
/// Width of the device screen in pixels.
int32_t width_;
/// Height of the device screen in pixels.
int32_t height_;
/// RST GPIO pin number.
int rst_num_;
// LVGL reserves 2x4 bytes in the buffer to be used as a palette.
/// LVGL draw buffer size for the device.
size_t lv_buf_size_;
i2c_master_bus_handle_t esp_i2c_bus_;
/// ESP LCD panel IO configuration.
esp_lcd_panel_io_i2c_config_t esp_io_config_;
private:
/**
* Initializes the ESP panel using vendor specific APIs and configurations.
* This method should implement any setup logic specific to the device.
*
* @param config ESP LCD panel configuration.
* @param io ESP LCD panel IO handle.
* @param [out] panel ESP LCD panel handle output pointer location.
*/
virtual void init_panel(esp_lcd_panel_dev_config_t &config,
esp_lcd_panel_io_handle_t io,
esp_lcd_panel_handle_t &panel) = 0;

View File

@ -33,12 +33,27 @@
#define LCD_CMD_BITS 8
#define LCD_PARAM_BITS 8
/**
* Example of implementing the IPanelDevice interface for SSD1306 LCD device.
*/
class SSD1306 final : public IPanelDevice {
public:
// Constructors allow overriding ssd1306 config.
/**
* Construct a new SSD1306 device.
*
* @param i2c I2C master bus to manage this device.
*/
explicit SSD1306(I2C &i2c) :
SSD1306(i2c, {.height = SCREEN_HEIGHT}) { }
/**
* Construct a new SSD1306 device given a specific SSD1306 configuration.
*
* @param i2c I2C master bus to manage this device.
* @param config SSD1306 vendor configuration.
* @param width Width of the device screen in pixels.
* @param height Height of the device screen in pixels.
*/
SSD1306(I2C &i2c,
esp_lcd_panel_ssd1306_config_t config,
int width = SCREEN_WIDTH,
@ -60,18 +75,27 @@ public:
~SSD1306() final = default;
/**
* Provides the SSD1306 vendor configuration to IPanelDevice consumers.
*
* @return Address of the SSD1306 vendor configuration structure.
*/
void *vendor_config() override
{
return &ssd1306_config_;
}
// The configuration structure specific to the SSD1306.
/// SSD1306 configuration structure.
esp_lcd_panel_ssd1306_config_t ssd1306_config_;
// For LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted 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:
/// Initializes the ESP LCD panel handle for the SSD1306 device.
void init_panel(esp_lcd_panel_dev_config_t &config,
esp_lcd_panel_io_handle_t io,
esp_lcd_panel_handle_t &panel) override

View File

@ -17,12 +17,6 @@
* 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)
{
@ -36,6 +30,12 @@ struct Timer {
ESP_ERROR_CHECK(esp_timer_delete(esp_timer_));
}
Timer(const Timer &) = delete;
Timer(Timer &) = delete;
Timer &operator=(Timer &) = delete;
/// Arguments passed to ESP API during timer creation.
esp_timer_create_args_t args_;

View File

@ -8,6 +8,7 @@ shaunrd0/klips/esp/
├── 04_esp-idf-arduino # CMake example instead of Arduino IDE for ESP development.
├── 05_temp-humidity-web # Temperature and humidity sensor within a web browser.
├── 06_i2c-scanner # Simple I2C device scanner.
├── 07_lcd-panel-i2c # Drawing to a LCD display with LVGL over I2C.
├── ESP32-basic-starter-kit.pdf # PDF for tutorials in ESP32 starter kit.
├── ESP32-dev-module.png
└── README.md