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
							 |