154 lines
5.2 KiB
C
154 lines
5.2 KiB
C
/*#############################################################################
|
|
## Author: Shaun Reed ##
|
|
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
|
|
## ##
|
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
|
|
##############################################################################
|
|
*/
|
|
#ifndef DISPLAY_H
|
|
#define DISPLAY_H
|
|
|
|
#include <lv_init.h>
|
|
#include <widgets/label/lv_label.h>
|
|
|
|
#include "i2c.h"
|
|
#include "panel_device.h"
|
|
|
|
#include <esp_lcd_panel_ops.h>
|
|
#include <esp_lcd_types.h>
|
|
|
|
struct LCD
|
|
{
|
|
esp_lcd_panel_handle_t esp_panel_;
|
|
|
|
esp_lcd_panel_io_handle_t esp_io_;
|
|
|
|
/// LVGL display handle.
|
|
lv_display_t* lv_display_;
|
|
|
|
// TODO: Should be a dynamic array.
|
|
lv_obj_t* lv_objects_;
|
|
|
|
struct IPanelDevice* device_;
|
|
};
|
|
|
|
/**
|
|
* Construct a new Display using an object that implements IPanelDevice.
|
|
*
|
|
* @param device An object that implements the IPanelDevice interface.
|
|
*/
|
|
static struct LCD LCD_init(struct IPanelDevice* device)
|
|
{
|
|
struct LCD display;
|
|
ESP_LOGI(LCD_TAG, "Creating panel IO handle");
|
|
display.esp_io_ = NULL;
|
|
esp_lcd_panel_dev_config_t panel_config = {
|
|
.reset_gpio_num = (gpio_num_t)(-1),
|
|
.bits_per_pixel = 1,
|
|
.vendor_config = device->vendor_config_cb(),
|
|
};
|
|
esp_lcd_panel_io_i2c_config_t io_config = {
|
|
.dev_addr = I2C_HW_ADDR,
|
|
// User data to pass to the LVGL flush_ready callback.
|
|
// See IPanelDevice::lvgl_flush_ready_cb
|
|
.user_ctx = NULL,
|
|
.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,
|
|
};
|
|
ESP_ERROR_CHECK(
|
|
esp_lcd_new_panel_io_i2c(I2C_get(), &io_config, &display.esp_io_));
|
|
|
|
// If the passed handle is already allocated, delete it.
|
|
if (display.esp_panel_ != NULL)
|
|
{
|
|
ESP_LOGI(LCD_TAG, "Removing unused panel");
|
|
esp_lcd_panel_del(display.esp_panel_);
|
|
}
|
|
|
|
ESP_LOGI(LCD_TAG, "Installing vendor panel driver");
|
|
device->init_panel_cb(&panel_config, display.esp_io_, &display.esp_panel_);
|
|
|
|
ESP_LOGI(LCD_TAG, "Resetting panel display");
|
|
ESP_ERROR_CHECK(esp_lcd_panel_reset(display.esp_panel_));
|
|
ESP_LOGI(LCD_TAG, "Initializing panel display");
|
|
ESP_ERROR_CHECK(esp_lcd_panel_init(display.esp_panel_));
|
|
ESP_LOGI(LCD_TAG, "Turning on panel display");
|
|
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(display.esp_panel_, true));
|
|
|
|
if (!lv_is_initialized())
|
|
{
|
|
ESP_LOGI(LCD_TAG, "Initialize LVGL");
|
|
lv_init();
|
|
}
|
|
|
|
ESP_LOGI(LCD_TAG, "Creating LVGL display");
|
|
display.lv_display_ = lv_display_create(device->width_, device->height_);
|
|
// assert(display.lv_display_);
|
|
|
|
// associate the i2c panel handle to the display
|
|
lv_display_set_user_data(display.lv_display_, display.esp_panel_);
|
|
|
|
device->register_rendering_data_cb(display.lv_display_, display.esp_io_,
|
|
device->lv_buf_, device->lv_buf_size_);
|
|
device->register_lvgl_tick_timer_cb();
|
|
return display;
|
|
}
|
|
|
|
/**
|
|
* 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 display
|
|
* @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.
|
|
*/
|
|
static void LCD_set_text_with_mode(struct LCD* display, const char* text,
|
|
const char* name,
|
|
lv_label_long_mode_t long_mode,
|
|
lv_align_t align)
|
|
{
|
|
// Lock the mutex due to the LVGL APIs are not thread-safe.
|
|
_lock_acquire(&lv_lock_);
|
|
|
|
ESP_LOGI(LCD_TAG, "Display LVGL Scroll Text");
|
|
lv_obj_t* scr = lv_display_get_screen_active(display->lv_display_);
|
|
|
|
// Create the label if it's `name` doesn't already exist in the map keys.
|
|
display->lv_objects_ = lv_label_create(scr);
|
|
|
|
// Set text and long mode.
|
|
lv_label_set_long_mode(display->lv_objects_, long_mode);
|
|
lv_label_set_text(display->lv_objects_, text);
|
|
|
|
// Set the size of the screen.
|
|
// If you use rotation 90 or 270 use lv_display_get_vertical_resolution.
|
|
lv_obj_set_width(
|
|
display->lv_objects_,
|
|
lv_display_get_horizontal_resolution(display->lv_display_));
|
|
lv_obj_align(display->lv_objects_, align, 0, 0);
|
|
|
|
_lock_release(&lv_lock_);
|
|
}
|
|
|
|
/**
|
|
* 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 display
|
|
* @param text Text to write to the display.
|
|
* @param name Name for the LVGL label object associated with this text.
|
|
*/
|
|
static void LCD_set_text(struct LCD* display, const char* text,
|
|
const char* name)
|
|
{
|
|
LCD_set_text_with_mode(display, text, name, LV_LABEL_LONG_SCROLL_CIRCULAR,
|
|
LV_ALIGN_TOP_MID);
|
|
}
|
|
|
|
#endif // DISPLAY_H
|