mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-29 22:24:26 +00:00 
			
		
		
		
	Drivers for RGB 16 bit parallel displays (#5872)
Co-authored-by: clydebarrow <366188+clydebarrow@users.noreply.github.com>
This commit is contained in:
		| @@ -282,6 +282,7 @@ esphome/components/rgbct/* @jesserockz | ||||
| esphome/components/rp2040/* @jesserockz | ||||
| esphome/components/rp2040_pio_led_strip/* @Papa-DMan | ||||
| esphome/components/rp2040_pwm/* @jesserockz | ||||
| esphome/components/rpi_dpi_rgb/* @clydebarrow | ||||
| esphome/components/rtl87xx/* @kuba2k2 | ||||
| esphome/components/rtttl/* @glmnet | ||||
| esphome/components/safe_mode/* @jsuanet @paulmonigatti | ||||
| @@ -333,6 +334,7 @@ esphome/components/ssd1351_spi/* @kbx81 | ||||
| esphome/components/st7567_base/* @latonita | ||||
| esphome/components/st7567_i2c/* @latonita | ||||
| esphome/components/st7567_spi/* @latonita | ||||
| esphome/components/st7701s/* @clydebarrow | ||||
| esphome/components/st7735/* @SenexCrenshaw | ||||
| esphome/components/st7789v/* @kbx81 | ||||
| esphome/components/st7920/* @marsjan155 | ||||
|   | ||||
							
								
								
									
										1
									
								
								esphome/components/rpi_dpi_rgb/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/rpi_dpi_rgb/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ["@clydebarrow"] | ||||
							
								
								
									
										197
									
								
								esphome/components/rpi_dpi_rgb/display.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								esphome/components/rpi_dpi_rgb/display.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import display | ||||
| from esphome.const import ( | ||||
|     CONF_RESET_PIN, | ||||
|     CONF_DATA_PINS, | ||||
|     CONF_ID, | ||||
|     CONF_IGNORE_STRAPPING_WARNING, | ||||
|     CONF_DIMENSIONS, | ||||
|     CONF_WIDTH, | ||||
|     CONF_HEIGHT, | ||||
|     CONF_LAMBDA, | ||||
|     CONF_COLOR_ORDER, | ||||
|     CONF_RED, | ||||
|     CONF_GREEN, | ||||
|     CONF_BLUE, | ||||
|     CONF_NUMBER, | ||||
|     CONF_OFFSET_HEIGHT, | ||||
|     CONF_OFFSET_WIDTH, | ||||
|     CONF_INVERT_COLORS, | ||||
| ) | ||||
| from esphome.components.esp32 import ( | ||||
|     only_on_variant, | ||||
|     const, | ||||
| ) | ||||
|  | ||||
| DEPENDENCIES = ["esp32"] | ||||
|  | ||||
| CONF_DE_PIN = "de_pin" | ||||
| CONF_PCLK_PIN = "pclk_pin" | ||||
| CONF_HSYNC_PIN = "hsync_pin" | ||||
| CONF_VSYNC_PIN = "vsync_pin" | ||||
|  | ||||
| CONF_HSYNC_FRONT_PORCH = "hsync_front_porch" | ||||
| CONF_HSYNC_PULSE_WIDTH = "hsync_pulse_width" | ||||
| CONF_HSYNC_BACK_PORCH = "hsync_back_porch" | ||||
| CONF_VSYNC_FRONT_PORCH = "vsync_front_porch" | ||||
| CONF_VSYNC_PULSE_WIDTH = "vsync_pulse_width" | ||||
| CONF_VSYNC_BACK_PORCH = "vsync_back_porch" | ||||
| CONF_PCLK_FREQUENCY = "pclk_frequency" | ||||
| CONF_PCLK_INVERTED = "pclk_inverted" | ||||
|  | ||||
| rpi_dpi_rgb_ns = cg.esphome_ns.namespace("rpi_dpi_rgb") | ||||
| RPI_DPI_RGB = rpi_dpi_rgb_ns.class_("RpiDpiRgb", display.Display, cg.Component) | ||||
| ColorOrder = display.display_ns.enum("ColorMode") | ||||
|  | ||||
| COLOR_ORDERS = { | ||||
|     "RGB": ColorOrder.COLOR_ORDER_RGB, | ||||
|     "BGR": ColorOrder.COLOR_ORDER_BGR, | ||||
| } | ||||
| DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema | ||||
|  | ||||
|  | ||||
| def data_pin_validate(value): | ||||
|     """ | ||||
|     It is safe to use strapping pins as RGB output data bits, as they are outputs only, | ||||
|     and not initialised until after boot. | ||||
|     """ | ||||
|     if not isinstance(value, dict): | ||||
|         try: | ||||
|             return DATA_PIN_SCHEMA( | ||||
|                 {CONF_NUMBER: value, CONF_IGNORE_STRAPPING_WARNING: True} | ||||
|             ) | ||||
|         except cv.Invalid: | ||||
|             pass | ||||
|     return DATA_PIN_SCHEMA(value) | ||||
|  | ||||
|  | ||||
| def data_pin_set(length): | ||||
|     return cv.All( | ||||
|         [data_pin_validate], | ||||
|         cv.Length(min=length, max=length, msg=f"Exactly {length} data pins required"), | ||||
|     ) | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     display.FULL_DISPLAY_SCHEMA.extend( | ||||
|         cv.Schema( | ||||
|             { | ||||
|                 cv.GenerateID(): cv.declare_id(RPI_DPI_RGB), | ||||
|                 cv.Required(CONF_DIMENSIONS): cv.Any( | ||||
|                     cv.dimensions, | ||||
|                     cv.Schema( | ||||
|                         { | ||||
|                             cv.Required(CONF_WIDTH): cv.int_, | ||||
|                             cv.Required(CONF_HEIGHT): cv.int_, | ||||
|                             cv.Optional(CONF_OFFSET_HEIGHT, default=0): cv.int_, | ||||
|                             cv.Optional(CONF_OFFSET_WIDTH, default=0): cv.int_, | ||||
|                         } | ||||
|                     ), | ||||
|                 ), | ||||
|                 cv.Optional(CONF_PCLK_FREQUENCY, default="16MHz"): cv.All( | ||||
|                     cv.frequency, cv.Range(min=4e6, max=30e6) | ||||
|                 ), | ||||
|                 cv.Optional(CONF_PCLK_INVERTED, default=True): cv.boolean, | ||||
|                 cv.Required(CONF_DATA_PINS): cv.Any( | ||||
|                     data_pin_set(16), | ||||
|                     cv.Schema( | ||||
|                         { | ||||
|                             cv.Required(CONF_RED): data_pin_set(5), | ||||
|                             cv.Required(CONF_GREEN): data_pin_set(6), | ||||
|                             cv.Required(CONF_BLUE): data_pin_set(5), | ||||
|                         } | ||||
|                     ), | ||||
|                 ), | ||||
|                 cv.Optional(CONF_COLOR_ORDER): cv.one_of( | ||||
|                     *COLOR_ORDERS.keys(), upper=True | ||||
|                 ), | ||||
|                 cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, | ||||
|                 cv.Required(CONF_DE_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Required(CONF_PCLK_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Required(CONF_HSYNC_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, | ||||
|                 cv.Optional(CONF_HSYNC_PULSE_WIDTH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_HSYNC_BACK_PORCH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_HSYNC_FRONT_PORCH, default=20): cv.int_, | ||||
|                 cv.Optional(CONF_VSYNC_PULSE_WIDTH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_VSYNC_BACK_PORCH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_VSYNC_FRONT_PORCH, default=10): cv.int_, | ||||
|             } | ||||
|         ) | ||||
|     ), | ||||
|     only_on_variant(supported=[const.VARIANT_ESP32S3]), | ||||
|     cv.only_with_esp_idf, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await display.register_display(var, config) | ||||
|  | ||||
|     cg.add(var.set_color_mode(COLOR_ORDERS[config[CONF_COLOR_ORDER]])) | ||||
|     cg.add(var.set_invert_colors(config[CONF_INVERT_COLORS])) | ||||
|     cg.add(var.set_hsync_pulse_width(config[CONF_HSYNC_PULSE_WIDTH])) | ||||
|     cg.add(var.set_hsync_back_porch(config[CONF_HSYNC_BACK_PORCH])) | ||||
|     cg.add(var.set_hsync_front_porch(config[CONF_HSYNC_FRONT_PORCH])) | ||||
|     cg.add(var.set_vsync_pulse_width(config[CONF_VSYNC_PULSE_WIDTH])) | ||||
|     cg.add(var.set_vsync_back_porch(config[CONF_VSYNC_BACK_PORCH])) | ||||
|     cg.add(var.set_vsync_front_porch(config[CONF_VSYNC_FRONT_PORCH])) | ||||
|     cg.add(var.set_pclk_inverted(config[CONF_PCLK_INVERTED])) | ||||
|     cg.add(var.set_pclk_frequency(config[CONF_PCLK_FREQUENCY])) | ||||
|     index = 0 | ||||
|     dpins = [] | ||||
|     if CONF_RED in config[CONF_DATA_PINS]: | ||||
|         red_pins = config[CONF_DATA_PINS][CONF_RED] | ||||
|         green_pins = config[CONF_DATA_PINS][CONF_GREEN] | ||||
|         blue_pins = config[CONF_DATA_PINS][CONF_BLUE] | ||||
|         if config[CONF_COLOR_ORDER] == "BGR": | ||||
|             dpins.extend(red_pins) | ||||
|             dpins.extend(green_pins) | ||||
|             dpins.extend(blue_pins) | ||||
|         else: | ||||
|             dpins.extend(blue_pins) | ||||
|             dpins.extend(green_pins) | ||||
|             dpins.extend(red_pins) | ||||
|         # swap bytes to match big-endian format | ||||
|         dpins = dpins[8:16] + dpins[0:8] | ||||
|     else: | ||||
|         dpins = config[CONF_DATA_PINS] | ||||
|     for pin in dpins: | ||||
|         data_pin = await cg.gpio_pin_expression(pin) | ||||
|         cg.add(var.add_data_pin(data_pin, index)) | ||||
|         index += 1 | ||||
|  | ||||
|     if reset_pin := config.get(CONF_RESET_PIN): | ||||
|         reset = await cg.gpio_pin_expression(reset_pin) | ||||
|         cg.add(var.set_reset_pin(reset)) | ||||
|  | ||||
|     if CONF_DIMENSIONS in config: | ||||
|         dimensions = config[CONF_DIMENSIONS] | ||||
|         if isinstance(dimensions, dict): | ||||
|             cg.add(var.set_dimensions(dimensions[CONF_WIDTH], dimensions[CONF_HEIGHT])) | ||||
|             cg.add( | ||||
|                 var.set_offsets( | ||||
|                     dimensions[CONF_OFFSET_WIDTH], dimensions[CONF_OFFSET_HEIGHT] | ||||
|                 ) | ||||
|             ) | ||||
|         else: | ||||
|             (width, height) = dimensions | ||||
|             cg.add(var.set_dimensions(width, height)) | ||||
|  | ||||
|     if lamb := config.get(CONF_LAMBDA): | ||||
|         lambda_ = await cg.process_lambda( | ||||
|             lamb, [(display.DisplayRef, "it")], return_type=cg.void | ||||
|         ) | ||||
|         cg.add(var.set_writer(lambda_)) | ||||
|  | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_DE_PIN]) | ||||
|     cg.add(var.set_de_pin(pin)) | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_PCLK_PIN]) | ||||
|     cg.add(var.set_pclk_pin(pin)) | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_HSYNC_PIN]) | ||||
|     cg.add(var.set_hsync_pin(pin)) | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_VSYNC_PIN]) | ||||
|     cg.add(var.set_vsync_pin(pin)) | ||||
							
								
								
									
										116
									
								
								esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| #ifdef USE_ESP32_VARIANT_ESP32S3 | ||||
| #include "rpi_dpi_rgb.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace rpi_dpi_rgb { | ||||
|  | ||||
| void RpiDpiRgb::setup() { | ||||
|   esph_log_config(TAG, "Setting up RPI_DPI_RGB"); | ||||
|   esp_lcd_rgb_panel_config_t config{}; | ||||
|   config.flags.fb_in_psram = 1; | ||||
|   config.timings.h_res = this->width_; | ||||
|   config.timings.v_res = this->height_; | ||||
|   config.timings.hsync_pulse_width = this->hsync_pulse_width_; | ||||
|   config.timings.hsync_back_porch = this->hsync_back_porch_; | ||||
|   config.timings.hsync_front_porch = this->hsync_front_porch_; | ||||
|   config.timings.vsync_pulse_width = this->vsync_pulse_width_; | ||||
|   config.timings.vsync_back_porch = this->vsync_back_porch_; | ||||
|   config.timings.vsync_front_porch = this->vsync_front_porch_; | ||||
|   config.timings.flags.pclk_active_neg = this->pclk_inverted_; | ||||
|   config.timings.pclk_hz = this->pclk_frequency_; | ||||
|   config.clk_src = LCD_CLK_SRC_PLL160M; | ||||
|   config.sram_trans_align = 64; | ||||
|   config.psram_trans_align = 64; | ||||
|   size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); | ||||
|   for (size_t i = 0; i != data_pin_count; i++) { | ||||
|     config.data_gpio_nums[i] = this->data_pins_[i]->get_pin(); | ||||
|   } | ||||
|   config.data_width = data_pin_count; | ||||
|   config.disp_gpio_num = -1; | ||||
|   config.hsync_gpio_num = this->hsync_pin_->get_pin(); | ||||
|   config.vsync_gpio_num = this->vsync_pin_->get_pin(); | ||||
|   config.de_gpio_num = this->de_pin_->get_pin(); | ||||
|   config.pclk_gpio_num = this->pclk_pin_->get_pin(); | ||||
|   esp_err_t err = esp_lcd_new_rgb_panel(&config, &this->handle_); | ||||
|   if (err != ESP_OK) { | ||||
|     esph_log_e(TAG, "lcd_new_rgb_panel failed: %s", esp_err_to_name(err)); | ||||
|   } | ||||
|   ESP_ERROR_CHECK(esp_lcd_panel_reset(this->handle_)); | ||||
|   ESP_ERROR_CHECK(esp_lcd_panel_init(this->handle_)); | ||||
|   esph_log_config(TAG, "RPI_DPI_RGB setup complete"); | ||||
| } | ||||
|  | ||||
| void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, | ||||
|                                display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { | ||||
|   if (w <= 0 || h <= 0) | ||||
|     return; | ||||
|   // if color mapping is required, pass the buck. | ||||
|   // note that endianness is not considered here - it is assumed to match! | ||||
|   if (bitness != display::COLOR_BITNESS_565) { | ||||
|     return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, | ||||
|                                             x_pad); | ||||
|   } | ||||
|   x_start += this->offset_x_; | ||||
|   y_start += this->offset_y_; | ||||
|   esp_err_t err; | ||||
|   // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. | ||||
|   if (x_offset == 0 && x_pad == 0 && y_offset == 0) { | ||||
|     // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother | ||||
|     err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y_start, x_start + w, y_start + h, ptr); | ||||
|   } else { | ||||
|     // draw line by line | ||||
|     auto stride = x_offset + w + x_pad; | ||||
|     for (int y = 0; y != h; y++) { | ||||
|       err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y + y_start, x_start + w, y + y_start + 1, | ||||
|                                       ptr + ((y + y_offset) * stride + x_offset) * 2); | ||||
|       if (err != ESP_OK) | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   if (err != ESP_OK) | ||||
|     esph_log_e(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err)); | ||||
| } | ||||
|  | ||||
| void RpiDpiRgb::draw_pixel_at(int x, int y, Color color) { | ||||
|   if (!this->get_clipping().inside(x, y)) | ||||
|     return;  // NOLINT | ||||
|  | ||||
|   switch (this->rotation_) { | ||||
|     case display::DISPLAY_ROTATION_0_DEGREES: | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_90_DEGREES: | ||||
|       std::swap(x, y); | ||||
|       x = this->width_ - x - 1; | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_180_DEGREES: | ||||
|       x = this->width_ - x - 1; | ||||
|       y = this->height_ - y - 1; | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_270_DEGREES: | ||||
|       std::swap(x, y); | ||||
|       y = this->height_ - y - 1; | ||||
|       break; | ||||
|   } | ||||
|   auto pixel = convert_big_endian(display::ColorUtil::color_to_565(color)); | ||||
|  | ||||
|   this->draw_pixels_at(x, y, 1, 1, (const uint8_t *) &pixel, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565, true, | ||||
|                        0, 0, 0); | ||||
|   App.feed_wdt(); | ||||
| } | ||||
|  | ||||
| void RpiDpiRgb::dump_config() { | ||||
|   ESP_LOGCONFIG("", "RPI_DPI_RGB LCD"); | ||||
|   ESP_LOGCONFIG(TAG, "  Height: %u", this->height_); | ||||
|   ESP_LOGCONFIG(TAG, "  Width: %u", this->width_); | ||||
|   LOG_PIN("  DE Pin: ", this->de_pin_); | ||||
|   LOG_PIN("  Reset Pin: ", this->reset_pin_); | ||||
|   size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); | ||||
|   for (size_t i = 0; i != data_pin_count; i++) | ||||
|     ESP_LOGCONFIG(TAG, "  Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str()); | ||||
| } | ||||
|  | ||||
| }  // namespace rpi_dpi_rgb | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif  // USE_ESP32_VARIANT_ESP32S3 | ||||
							
								
								
									
										92
									
								
								esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| // | ||||
| // Created by Clyde Stubbs on 29/10/2023. | ||||
| // | ||||
| #pragma once | ||||
|  | ||||
| // only applicable on ESP32-S3 | ||||
| #ifdef USE_ESP32_VARIANT_ESP32S3 | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/gpio.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/components/display/display.h" | ||||
| #include "esp_lcd_panel_ops.h" | ||||
|  | ||||
| #include "esp_lcd_panel_rgb.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace rpi_dpi_rgb { | ||||
|  | ||||
| constexpr static const char *const TAG = "rpi_dpi_rgb"; | ||||
|  | ||||
| class RpiDpiRgb : public display::Display { | ||||
|  public: | ||||
|   void update() override { this->do_update_(); } | ||||
|   void setup() override; | ||||
|   void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, | ||||
|                       display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; | ||||
|   void draw_pixel_at(int x, int y, Color color) override; | ||||
|  | ||||
|   display::ColorOrder get_color_mode() { return this->color_mode_; } | ||||
|   void set_color_mode(display::ColorOrder color_mode) { this->color_mode_ = color_mode; } | ||||
|   void set_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; } | ||||
|  | ||||
|   void add_data_pin(InternalGPIOPin *data_pin, size_t index) { this->data_pins_[index] = data_pin; }; | ||||
|   void set_de_pin(InternalGPIOPin *de_pin) { this->de_pin_ = de_pin; } | ||||
|   void set_pclk_pin(InternalGPIOPin *pclk_pin) { this->pclk_pin_ = pclk_pin; } | ||||
|   void set_vsync_pin(InternalGPIOPin *vsync_pin) { this->vsync_pin_ = vsync_pin; } | ||||
|   void set_hsync_pin(InternalGPIOPin *hsync_pin) { this->hsync_pin_ = hsync_pin; } | ||||
|   void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } | ||||
|   void set_width(uint16_t width) { this->width_ = width; } | ||||
|   void set_dimensions(uint16_t width, uint16_t height) { | ||||
|     this->width_ = width; | ||||
|     this->height_ = height; | ||||
|   } | ||||
|   int get_width() override { return this->width_; } | ||||
|   int get_height() override { return this->height_; } | ||||
|   void set_hsync_back_porch(uint16_t hsync_back_porch) { this->hsync_back_porch_ = hsync_back_porch; } | ||||
|   void set_hsync_front_porch(uint16_t hsync_front_porch) { this->hsync_front_porch_ = hsync_front_porch; } | ||||
|   void set_hsync_pulse_width(uint16_t hsync_pulse_width) { this->hsync_pulse_width_ = hsync_pulse_width; } | ||||
|   void set_vsync_pulse_width(uint16_t vsync_pulse_width) { this->vsync_pulse_width_ = vsync_pulse_width; } | ||||
|   void set_vsync_back_porch(uint16_t vsync_back_porch) { this->vsync_back_porch_ = vsync_back_porch; } | ||||
|   void set_vsync_front_porch(uint16_t vsync_front_porch) { this->vsync_front_porch_ = vsync_front_porch; } | ||||
|   void set_pclk_frequency(uint32_t pclk_frequency) { this->pclk_frequency_ = pclk_frequency; } | ||||
|   void set_pclk_inverted(bool inverted) { this->pclk_inverted_ = inverted; } | ||||
|   void set_offsets(int16_t offset_x, int16_t offset_y) { | ||||
|     this->offset_x_ = offset_x; | ||||
|     this->offset_y_ = offset_y; | ||||
|   } | ||||
|   display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   int get_width_internal() override { return this->width_; } | ||||
|   int get_height_internal() override { return this->height_; } | ||||
|   InternalGPIOPin *de_pin_{nullptr}; | ||||
|   InternalGPIOPin *pclk_pin_{nullptr}; | ||||
|   InternalGPIOPin *hsync_pin_{nullptr}; | ||||
|   InternalGPIOPin *vsync_pin_{nullptr}; | ||||
|   GPIOPin *reset_pin_{nullptr}; | ||||
|   InternalGPIOPin *data_pins_[16] = {}; | ||||
|   uint16_t hsync_front_porch_ = 8; | ||||
|   uint16_t hsync_pulse_width_ = 4; | ||||
|   uint16_t hsync_back_porch_ = 8; | ||||
|   uint16_t vsync_front_porch_ = 8; | ||||
|   uint16_t vsync_pulse_width_ = 4; | ||||
|   uint16_t vsync_back_porch_ = 8; | ||||
|   uint32_t pclk_frequency_ = 16 * 1000 * 1000; | ||||
|   bool pclk_inverted_{true}; | ||||
|  | ||||
|   bool invert_colors_{}; | ||||
|   display::ColorOrder color_mode_{display::COLOR_ORDER_BGR}; | ||||
|   size_t width_{}; | ||||
|   size_t height_{}; | ||||
|   int16_t offset_x_{0}; | ||||
|   int16_t offset_y_{0}; | ||||
|  | ||||
|   esp_lcd_panel_handle_t handle_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace rpi_dpi_rgb | ||||
| }  // namespace esphome | ||||
| #endif  // USE_ESP32_VARIANT_ESP32S3 | ||||
							
								
								
									
										1
									
								
								esphome/components/st7701s/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/st7701s/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ["@clydebarrow"] | ||||
							
								
								
									
										253
									
								
								esphome/components/st7701s/display.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								esphome/components/st7701s/display.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,253 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import ( | ||||
|     spi, | ||||
|     display, | ||||
| ) | ||||
| from esphome.const import ( | ||||
|     CONF_DC_PIN, | ||||
|     CONF_RESET_PIN, | ||||
|     CONF_DATA_PINS, | ||||
|     CONF_ID, | ||||
|     CONF_DIMENSIONS, | ||||
|     CONF_WIDTH, | ||||
|     CONF_HEIGHT, | ||||
|     CONF_LAMBDA, | ||||
|     CONF_MIRROR_X, | ||||
|     CONF_MIRROR_Y, | ||||
|     CONF_COLOR_ORDER, | ||||
|     CONF_TRANSFORM, | ||||
|     CONF_OFFSET_HEIGHT, | ||||
|     CONF_OFFSET_WIDTH, | ||||
|     CONF_INVERT_COLORS, | ||||
|     CONF_RED, | ||||
|     CONF_GREEN, | ||||
|     CONF_BLUE, | ||||
|     CONF_NUMBER, | ||||
|     CONF_IGNORE_STRAPPING_WARNING, | ||||
| ) | ||||
|  | ||||
| from esphome.components.esp32 import ( | ||||
|     only_on_variant, | ||||
|     const, | ||||
| ) | ||||
| from esphome.components.rpi_dpi_rgb.display import ( | ||||
|     CONF_PCLK_FREQUENCY, | ||||
|     CONF_PCLK_INVERTED, | ||||
| ) | ||||
| from .init_sequences import ( | ||||
|     ST7701S_INITS, | ||||
|     cmd, | ||||
| ) | ||||
|  | ||||
| CONF_INIT_SEQUENCE = "init_sequence" | ||||
| CONF_DE_PIN = "de_pin" | ||||
| CONF_PCLK_PIN = "pclk_pin" | ||||
| CONF_HSYNC_PIN = "hsync_pin" | ||||
| CONF_VSYNC_PIN = "vsync_pin" | ||||
|  | ||||
| CONF_HSYNC_PULSE_WIDTH = "hsync_pulse_width" | ||||
| CONF_HSYNC_BACK_PORCH = "hsync_back_porch" | ||||
| CONF_HSYNC_FRONT_PORCH = "hsync_front_porch" | ||||
| CONF_VSYNC_PULSE_WIDTH = "vsync_pulse_width" | ||||
| CONF_VSYNC_BACK_PORCH = "vsync_back_porch" | ||||
| CONF_VSYNC_FRONT_PORCH = "vsync_front_porch" | ||||
|  | ||||
| DEPENDENCIES = ["spi", "esp32"] | ||||
|  | ||||
| st7701s_ns = cg.esphome_ns.namespace("st7701s") | ||||
| ST7701S = st7701s_ns.class_("ST7701S", display.Display, cg.Component, spi.SPIDevice) | ||||
| ColorOrder = display.display_ns.enum("ColorMode") | ||||
|  | ||||
| COLOR_ORDERS = { | ||||
|     "RGB": ColorOrder.COLOR_ORDER_RGB, | ||||
|     "BGR": ColorOrder.COLOR_ORDER_BGR, | ||||
| } | ||||
| DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema | ||||
|  | ||||
|  | ||||
| def data_pin_validate(value): | ||||
|     """ | ||||
|     It is safe to use strapping pins as RGB output data bits, as they are outputs only, | ||||
|     and not initialised until after boot. | ||||
|     """ | ||||
|     if not isinstance(value, dict): | ||||
|         try: | ||||
|             return DATA_PIN_SCHEMA( | ||||
|                 {CONF_NUMBER: value, CONF_IGNORE_STRAPPING_WARNING: True} | ||||
|             ) | ||||
|         except cv.Invalid: | ||||
|             pass | ||||
|     return DATA_PIN_SCHEMA(value) | ||||
|  | ||||
|  | ||||
| def data_pin_set(length): | ||||
|     return cv.All( | ||||
|         [data_pin_validate], | ||||
|         cv.Length(min=length, max=length, msg=f"Exactly {length} data pins required"), | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def map_sequence(value): | ||||
|     """ | ||||
|     An initialisation sequence can be selected from one of the pre-defined sequences in init_sequences.py, | ||||
|     or can be a literal array of data bytes. | ||||
|     The format is a repeated sequence of [CMD, LEN, <data>] where <data> is LEN bytes. | ||||
|     """ | ||||
|     if not isinstance(value, list): | ||||
|         value = cv.int_(value) | ||||
|         value = cv.one_of(*ST7701S_INITS)(value) | ||||
|         return ST7701S_INITS[value] | ||||
|     # value = cv.ensure_list(cv.uint8_t)(value) | ||||
|     data_length = len(value) | ||||
|     if data_length == 0: | ||||
|         raise cv.Invalid("Empty sequence") | ||||
|     value = cmd(*value) | ||||
|     return value | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     display.FULL_DISPLAY_SCHEMA.extend( | ||||
|         cv.Schema( | ||||
|             { | ||||
|                 cv.GenerateID(): cv.declare_id(ST7701S), | ||||
|                 cv.Required(CONF_DIMENSIONS): cv.Any( | ||||
|                     cv.dimensions, | ||||
|                     cv.Schema( | ||||
|                         { | ||||
|                             cv.Required(CONF_WIDTH): cv.int_, | ||||
|                             cv.Required(CONF_HEIGHT): cv.int_, | ||||
|                             cv.Optional(CONF_OFFSET_HEIGHT, default=0): cv.int_, | ||||
|                             cv.Optional(CONF_OFFSET_WIDTH, default=0): cv.int_, | ||||
|                         } | ||||
|                     ), | ||||
|                 ), | ||||
|                 cv.Optional(CONF_TRANSFORM): cv.Schema( | ||||
|                     { | ||||
|                         cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, | ||||
|                         cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, | ||||
|                     } | ||||
|                 ), | ||||
|                 cv.Required(CONF_DATA_PINS): cv.Any( | ||||
|                     data_pin_set(16), | ||||
|                     cv.Schema( | ||||
|                         { | ||||
|                             cv.Required(CONF_RED): data_pin_set(5), | ||||
|                             cv.Required(CONF_GREEN): data_pin_set(6), | ||||
|                             cv.Required(CONF_BLUE): data_pin_set(5), | ||||
|                         } | ||||
|                     ), | ||||
|                 ), | ||||
|                 cv.Optional(CONF_INIT_SEQUENCE, default=1): cv.ensure_list( | ||||
|                     map_sequence | ||||
|                 ), | ||||
|                 cv.Optional(CONF_COLOR_ORDER): cv.one_of( | ||||
|                     *COLOR_ORDERS.keys(), upper=True | ||||
|                 ), | ||||
|                 cv.Optional(CONF_PCLK_FREQUENCY, default="16MHz"): cv.All( | ||||
|                     cv.frequency, cv.Range(min=4e6, max=30e6) | ||||
|                 ), | ||||
|                 cv.Optional(CONF_PCLK_INVERTED, default=True): cv.boolean, | ||||
|                 cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, | ||||
|                 cv.Required(CONF_DE_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Required(CONF_PCLK_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Required(CONF_HSYNC_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_output_pin_schema, | ||||
|                 cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, | ||||
|                 cv.Optional(CONF_DC_PIN): pins.gpio_output_pin_schema, | ||||
|                 cv.Optional(CONF_HSYNC_PULSE_WIDTH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_HSYNC_BACK_PORCH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_HSYNC_FRONT_PORCH, default=20): cv.int_, | ||||
|                 cv.Optional(CONF_VSYNC_PULSE_WIDTH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_VSYNC_BACK_PORCH, default=10): cv.int_, | ||||
|                 cv.Optional(CONF_VSYNC_FRONT_PORCH, default=10): cv.int_, | ||||
|             } | ||||
|         ).extend(spi.spi_device_schema(cs_pin_required=False, default_data_rate=1e6)) | ||||
|     ), | ||||
|     only_on_variant(supported=[const.VARIANT_ESP32S3]), | ||||
|     cv.only_with_esp_idf, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await display.register_display(var, config) | ||||
|     await spi.register_spi_device(var, config) | ||||
|  | ||||
|     sequence = [] | ||||
|     for seq in config[CONF_INIT_SEQUENCE]: | ||||
|         sequence.extend(seq) | ||||
|     cg.add(var.set_init_sequence(sequence)) | ||||
|     cg.add(var.set_color_mode(COLOR_ORDERS[config[CONF_COLOR_ORDER]])) | ||||
|     cg.add(var.set_invert_colors(config[CONF_INVERT_COLORS])) | ||||
|     cg.add(var.set_hsync_pulse_width(config[CONF_HSYNC_PULSE_WIDTH])) | ||||
|     cg.add(var.set_hsync_back_porch(config[CONF_HSYNC_BACK_PORCH])) | ||||
|     cg.add(var.set_hsync_front_porch(config[CONF_HSYNC_FRONT_PORCH])) | ||||
|     cg.add(var.set_vsync_pulse_width(config[CONF_VSYNC_PULSE_WIDTH])) | ||||
|     cg.add(var.set_vsync_back_porch(config[CONF_VSYNC_BACK_PORCH])) | ||||
|     cg.add(var.set_vsync_front_porch(config[CONF_VSYNC_FRONT_PORCH])) | ||||
|     cg.add(var.set_pclk_inverted(config[CONF_PCLK_INVERTED])) | ||||
|     cg.add(var.set_pclk_frequency(config[CONF_PCLK_FREQUENCY])) | ||||
|     index = 0 | ||||
|     dpins = [] | ||||
|     if CONF_RED in config[CONF_DATA_PINS]: | ||||
|         red_pins = config[CONF_DATA_PINS][CONF_RED] | ||||
|         green_pins = config[CONF_DATA_PINS][CONF_GREEN] | ||||
|         blue_pins = config[CONF_DATA_PINS][CONF_BLUE] | ||||
|         if config[CONF_COLOR_ORDER] == "BGR": | ||||
|             dpins.extend(red_pins) | ||||
|             dpins.extend(green_pins) | ||||
|             dpins.extend(blue_pins) | ||||
|         else: | ||||
|             dpins.extend(blue_pins) | ||||
|             dpins.extend(green_pins) | ||||
|             dpins.extend(red_pins) | ||||
|         # swap bytes to match big-endian format | ||||
|         dpins = dpins[8:16] + dpins[0:8] | ||||
|     else: | ||||
|         dpins = config[CONF_DATA_PINS] | ||||
|     for pin in dpins: | ||||
|         data_pin = await cg.gpio_pin_expression(pin) | ||||
|         cg.add(var.add_data_pin(data_pin, index)) | ||||
|         index += 1 | ||||
|  | ||||
|     if dc_pin := config.get(CONF_DC_PIN): | ||||
|         dc = await cg.gpio_pin_expression(dc_pin) | ||||
|         cg.add(var.set_dc_pin(dc)) | ||||
|  | ||||
|     if reset_pin := config.get(CONF_RESET_PIN): | ||||
|         reset = await cg.gpio_pin_expression(reset_pin) | ||||
|         cg.add(var.set_reset_pin(reset)) | ||||
|  | ||||
|     if transform := config.get(CONF_TRANSFORM): | ||||
|         cg.add(var.set_mirror_x(transform[CONF_MIRROR_X])) | ||||
|         cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y])) | ||||
|  | ||||
|     if CONF_DIMENSIONS in config: | ||||
|         dimensions = config[CONF_DIMENSIONS] | ||||
|         if isinstance(dimensions, dict): | ||||
|             cg.add(var.set_dimensions(dimensions[CONF_WIDTH], dimensions[CONF_HEIGHT])) | ||||
|             cg.add( | ||||
|                 var.set_offsets( | ||||
|                     dimensions[CONF_OFFSET_WIDTH], dimensions[CONF_OFFSET_HEIGHT] | ||||
|                 ) | ||||
|             ) | ||||
|         else: | ||||
|             (width, height) = dimensions | ||||
|             cg.add(var.set_dimensions(width, height)) | ||||
|  | ||||
|     if lamb := config.get(CONF_LAMBDA): | ||||
|         lambda_ = await cg.process_lambda( | ||||
|             lamb, [(display.DisplayRef, "it")], return_type=cg.void | ||||
|         ) | ||||
|         cg.add(var.set_writer(lambda_)) | ||||
|  | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_DE_PIN]) | ||||
|     cg.add(var.set_de_pin(pin)) | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_PCLK_PIN]) | ||||
|     cg.add(var.set_pclk_pin(pin)) | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_HSYNC_PIN]) | ||||
|     cg.add(var.set_hsync_pin(pin)) | ||||
|     pin = await cg.gpio_pin_expression(config[CONF_VSYNC_PIN]) | ||||
|     cg.add(var.set_vsync_pin(pin)) | ||||
							
								
								
									
										363
									
								
								esphome/components/st7701s/init_sequences.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								esphome/components/st7701s/init_sequences.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,363 @@ | ||||
| # These are initialisation sequences for ST7701S displays. The contents are somewhat arcane. | ||||
|  | ||||
|  | ||||
| def cmd(c, *args): | ||||
|     """ | ||||
|     Create a command sequence | ||||
|     :param c: The command (8 bit) | ||||
|     :param args: zero or more arguments (8 bit values) | ||||
|     :return: a list with the command, the argument count and the arguments | ||||
|     """ | ||||
|     return [c, len(args)] + list(args) | ||||
|  | ||||
|  | ||||
| ST7701S_1_INIT = ( | ||||
|     cmd(0x01) | ||||
|     + cmd(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10) | ||||
|     + cmd(0xC0, 0x3B, 0x00) | ||||
|     + cmd(0xC1, 0x0D, 0x02) | ||||
|     + cmd(0xC2, 0x31, 0x05) | ||||
|     + cmd(0xCD, 0x08) | ||||
|     + cmd( | ||||
|         0xB0, | ||||
|         0x00, | ||||
|         0x11, | ||||
|         0x18, | ||||
|         0x0E, | ||||
|         0x11, | ||||
|         0x06, | ||||
|         0x07, | ||||
|         0x08, | ||||
|         0x07, | ||||
|         0x22, | ||||
|         0x04, | ||||
|         0x12, | ||||
|         0x0F, | ||||
|         0xAA, | ||||
|         0x31, | ||||
|         0x18, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xB1, | ||||
|         0x00, | ||||
|         0x11, | ||||
|         0x19, | ||||
|         0x0E, | ||||
|         0x12, | ||||
|         0x07, | ||||
|         0x08, | ||||
|         0x08, | ||||
|         0x08, | ||||
|         0x22, | ||||
|         0x04, | ||||
|         0x11, | ||||
|         0x11, | ||||
|         0xA9, | ||||
|         0x32, | ||||
|         0x18, | ||||
|     ) | ||||
|     + cmd(0xFF, 0x77, 0x01, 0x00, 0x00, 0x11) | ||||
|     + cmd(0xB0, 0x60) | ||||
|     + cmd(0xB1, 0x32) | ||||
|     + cmd(0xB2, 0x07) | ||||
|     + cmd(0xB3, 0x80) | ||||
|     + cmd(0xB5, 0x49) | ||||
|     + cmd(0xB7, 0x85) | ||||
|     + cmd(0xB8, 0x21) | ||||
|     + cmd(0xC1, 0x78) | ||||
|     + cmd(0xC2, 0x78) | ||||
|     + cmd(0xE0, 0x00, 0x1B, 0x02) | ||||
|     + cmd(0xE1, 0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x44, 0x44) | ||||
|     + cmd(0xE2, 0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, 0x00, 0xEC, 0xA0, 0x00, 0x00) | ||||
|     + cmd(0xE3, 0x00, 0x00, 0x11, 0x11) | ||||
|     + cmd(0xE4, 0x44, 0x44) | ||||
|     + cmd( | ||||
|         0xE5, | ||||
|         0x0A, | ||||
|         0xE9, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|         0x0C, | ||||
|         0xEB, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|         0x0E, | ||||
|         0xED, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|         0x10, | ||||
|         0xEF, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|     ) | ||||
|     + cmd(0xE6, 0x00, 0x00, 0x11, 0x11) | ||||
|     + cmd(0xE7, 0x44, 0x44) | ||||
|     + cmd( | ||||
|         0xE8, | ||||
|         0x09, | ||||
|         0xE8, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|         0x0B, | ||||
|         0xEA, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|         0x0D, | ||||
|         0xEC, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|         0x0F, | ||||
|         0xEE, | ||||
|         0xD8, | ||||
|         0xA0, | ||||
|     ) | ||||
|     + cmd(0xEB, 0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40) | ||||
|     + cmd(0xEC, 0x3C, 0x00) | ||||
|     + cmd( | ||||
|         0xED, | ||||
|         0xAB, | ||||
|         0x89, | ||||
|         0x76, | ||||
|         0x54, | ||||
|         0x02, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0x20, | ||||
|         0x45, | ||||
|         0x67, | ||||
|         0x98, | ||||
|         0xBA, | ||||
|     ) | ||||
|     + cmd(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13) | ||||
|     + cmd(0xE5, 0xE4) | ||||
|     + cmd(0x3A, 0x60) | ||||
| ) | ||||
|  | ||||
| # This is untested | ||||
| ST7701S_7_INIT = ( | ||||
|     cmd( | ||||
|         0xFF, | ||||
|         0x77, | ||||
|         0x01, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x10, | ||||
|     ) | ||||
|     + cmd(0xC0, 0x3B, 0x00) | ||||
|     + cmd(0xC1, 0x0B, 0x02) | ||||
|     + cmd(0xC2, 0x07, 0x02) | ||||
|     + cmd(0xCC, 0x10) | ||||
|     + cmd(0xCD, 0x08) | ||||
|     + cmd( | ||||
|         0xB0, | ||||
|         0x00, | ||||
|         0x11, | ||||
|         0x16, | ||||
|         0x0E, | ||||
|         0x11, | ||||
|         0x06, | ||||
|         0x05, | ||||
|         0x09, | ||||
|         0x08, | ||||
|         0x21, | ||||
|         0x06, | ||||
|         0x13, | ||||
|         0x10, | ||||
|         0x29, | ||||
|         0x31, | ||||
|         0x18, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xB1, | ||||
|         0x00, | ||||
|         0x11, | ||||
|         0x16, | ||||
|         0x0E, | ||||
|         0x11, | ||||
|         0x07, | ||||
|         0x05, | ||||
|         0x09, | ||||
|         0x09, | ||||
|         0x21, | ||||
|         0x05, | ||||
|         0x13, | ||||
|         0x11, | ||||
|         0x2A, | ||||
|         0x31, | ||||
|         0x18, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xFF, | ||||
|         0x77, | ||||
|         0x01, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x11, | ||||
|     ) | ||||
|     + cmd(0xB0, 0x6D) | ||||
|     + cmd(0xB1, 0x37) | ||||
|     + cmd(0xB2, 0x81) | ||||
|     + cmd(0xB3, 0x80) | ||||
|     + cmd(0xB5, 0x43) | ||||
|     + cmd(0xB7, 0x85) | ||||
|     + cmd(0xB8, 0x20) | ||||
|     + cmd(0xC1, 0x78) | ||||
|     + cmd(0xC2, 0x78) | ||||
|     + cmd(0xD0, 0x88) | ||||
|     + cmd( | ||||
|         0xE0, | ||||
|         3, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x02, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xE1, | ||||
|         0x03, | ||||
|         0xA0, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x04, | ||||
|         0xA0, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x20, | ||||
|         0x20, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xE2, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xE3, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x11, | ||||
|         0x00, | ||||
|     ) | ||||
|     + cmd(0xE4, 0x22, 0x00) | ||||
|     + cmd( | ||||
|         0xE5, | ||||
|         0x05, | ||||
|         0xEC, | ||||
|         0xA0, | ||||
|         0xA0, | ||||
|         0x07, | ||||
|         0xEE, | ||||
|         0xA0, | ||||
|         0xA0, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xE6, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x11, | ||||
|         0x00, | ||||
|     ) | ||||
|     + cmd(0xE7, 0x22, 0x00) | ||||
|     + cmd( | ||||
|         0xE8, | ||||
|         0x06, | ||||
|         0xED, | ||||
|         0xA0, | ||||
|         0xA0, | ||||
|         0x08, | ||||
|         0xEF, | ||||
|         0xA0, | ||||
|         0xA0, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xEB, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x40, | ||||
|         0x40, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xED, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0xBA, | ||||
|         0x0A, | ||||
|         0xBF, | ||||
|         0x45, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0x54, | ||||
|         0xFB, | ||||
|         0xA0, | ||||
|         0xAB, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|         0xFF, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xEF, | ||||
|         0x10, | ||||
|         0x0D, | ||||
|         0x04, | ||||
|         0x08, | ||||
|         0x3F, | ||||
|         0x1F, | ||||
|     ) | ||||
|     + cmd( | ||||
|         0xFF, | ||||
|         0x77, | ||||
|         0x01, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x13, | ||||
|     ) | ||||
|     + cmd(0xEF, 0x08) | ||||
|     + cmd( | ||||
|         0xFF, | ||||
|         0x77, | ||||
|         0x01, | ||||
|         0x00, | ||||
|         0x00, | ||||
|         0x00, | ||||
|     ) | ||||
|     + cmd(0x3A, 0x66) | ||||
| ) | ||||
|  | ||||
| ST7701S_INITS = { | ||||
|     1: ST7701S_1_INIT, | ||||
|     # 7: ST7701S_7_INIT, | ||||
| } | ||||
							
								
								
									
										180
									
								
								esphome/components/st7701s/st7701s.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								esphome/components/st7701s/st7701s.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| #ifdef USE_ESP32_VARIANT_ESP32S3 | ||||
| #include "st7701s.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace st7701s { | ||||
|  | ||||
| void ST7701S::setup() { | ||||
|   esph_log_config(TAG, "Setting up ST7701S"); | ||||
|   this->spi_setup(); | ||||
|   esp_lcd_rgb_panel_config_t config{}; | ||||
|   config.flags.fb_in_psram = 1; | ||||
|   config.timings.h_res = this->width_; | ||||
|   config.timings.v_res = this->height_; | ||||
|   config.timings.hsync_pulse_width = this->hsync_pulse_width_; | ||||
|   config.timings.hsync_back_porch = this->hsync_back_porch_; | ||||
|   config.timings.hsync_front_porch = this->hsync_front_porch_; | ||||
|   config.timings.vsync_pulse_width = this->vsync_pulse_width_; | ||||
|   config.timings.vsync_back_porch = this->vsync_back_porch_; | ||||
|   config.timings.vsync_front_porch = this->vsync_front_porch_; | ||||
|   config.timings.flags.pclk_active_neg = this->pclk_inverted_; | ||||
|   config.timings.pclk_hz = this->pclk_frequency_; | ||||
|   config.clk_src = LCD_CLK_SRC_PLL160M; | ||||
|   config.sram_trans_align = 64; | ||||
|   config.psram_trans_align = 64; | ||||
|   size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); | ||||
|   for (size_t i = 0; i != data_pin_count; i++) { | ||||
|     config.data_gpio_nums[i] = this->data_pins_[i]->get_pin(); | ||||
|   } | ||||
|   config.data_width = data_pin_count; | ||||
|   config.disp_gpio_num = -1; | ||||
|   config.hsync_gpio_num = this->hsync_pin_->get_pin(); | ||||
|   config.vsync_gpio_num = this->vsync_pin_->get_pin(); | ||||
|   config.de_gpio_num = this->de_pin_->get_pin(); | ||||
|   config.pclk_gpio_num = this->pclk_pin_->get_pin(); | ||||
|   esp_err_t err = esp_lcd_new_rgb_panel(&config, &this->handle_); | ||||
|   if (err != ESP_OK) { | ||||
|     esph_log_e(TAG, "lcd_new_rgb_panel failed: %s", esp_err_to_name(err)); | ||||
|   } | ||||
|   ESP_ERROR_CHECK(esp_lcd_panel_reset(this->handle_)); | ||||
|   ESP_ERROR_CHECK(esp_lcd_panel_init(this->handle_)); | ||||
|   this->write_init_sequence_(); | ||||
|   esph_log_config(TAG, "ST7701S setup complete"); | ||||
| } | ||||
|  | ||||
| void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, | ||||
|                              display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { | ||||
|   if (w <= 0 || h <= 0) | ||||
|     return; | ||||
|   // if color mapping is required, pass the buck. | ||||
|   // note that endianness is not considered here - it is assumed to match! | ||||
|   if (bitness != display::COLOR_BITNESS_565) { | ||||
|     return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, | ||||
|                                             x_pad); | ||||
|   } | ||||
|   x_start += this->offset_x_; | ||||
|   y_start += this->offset_y_; | ||||
|   esp_err_t err; | ||||
|   // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. | ||||
|   if (x_offset == 0 && x_pad == 0 && y_offset == 0) { | ||||
|     // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother | ||||
|     err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y_start, x_start + w, y_start + h, ptr); | ||||
|   } else { | ||||
|     // draw line by line | ||||
|     auto stride = x_offset + w + x_pad; | ||||
|     for (int y = 0; y != h; y++) { | ||||
|       err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y + y_start, x_start + w, y + y_start + 1, | ||||
|                                       ptr + ((y + y_offset) * stride + x_offset) * 2); | ||||
|       if (err != ESP_OK) | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   if (err != ESP_OK) | ||||
|     esph_log_e(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err)); | ||||
| } | ||||
|  | ||||
| void ST7701S::draw_pixel_at(int x, int y, Color color) { | ||||
|   if (!this->get_clipping().inside(x, y)) | ||||
|     return;  // NOLINT | ||||
|  | ||||
|   switch (this->rotation_) { | ||||
|     case display::DISPLAY_ROTATION_0_DEGREES: | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_90_DEGREES: | ||||
|       std::swap(x, y); | ||||
|       x = this->width_ - x - 1; | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_180_DEGREES: | ||||
|       x = this->width_ - x - 1; | ||||
|       y = this->height_ - y - 1; | ||||
|       break; | ||||
|     case display::DISPLAY_ROTATION_270_DEGREES: | ||||
|       std::swap(x, y); | ||||
|       y = this->height_ - y - 1; | ||||
|       break; | ||||
|   } | ||||
|   auto pixel = convert_big_endian(display::ColorUtil::color_to_565(color)); | ||||
|  | ||||
|   this->draw_pixels_at(x, y, 1, 1, (const uint8_t *) &pixel, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565, true, | ||||
|                        0, 0, 0); | ||||
|   App.feed_wdt(); | ||||
| } | ||||
|  | ||||
| void ST7701S::write_command_(uint8_t value) { | ||||
|   this->enable(); | ||||
|   if (this->dc_pin_ == nullptr) { | ||||
|     this->write(value, 9); | ||||
|   } else { | ||||
|     this->dc_pin_->digital_write(false); | ||||
|     this->write_byte(value); | ||||
|     this->dc_pin_->digital_write(true); | ||||
|   } | ||||
|   this->disable(); | ||||
| } | ||||
|  | ||||
| void ST7701S::write_data_(uint8_t value) { | ||||
|   this->enable(); | ||||
|   if (this->dc_pin_ == nullptr) { | ||||
|     this->write(value | 0x100, 9); | ||||
|   } else { | ||||
|     this->dc_pin_->digital_write(true); | ||||
|     this->write_byte(value); | ||||
|   } | ||||
|   this->disable(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * this relies upon the init sequence being well-formed, which is guaranteed by the Python init code. | ||||
|  */ | ||||
|  | ||||
| void ST7701S::write_sequence_(uint8_t cmd, size_t len, const uint8_t *bytes) { | ||||
|   this->write_command_(cmd); | ||||
|   while (len-- != 0) | ||||
|     this->write_data_(*bytes++); | ||||
| } | ||||
|  | ||||
| void ST7701S::write_init_sequence_() { | ||||
|   for (size_t i = 0; i != this->init_sequence_.size();) { | ||||
|     uint8_t cmd = this->init_sequence_[i++]; | ||||
|     size_t len = this->init_sequence_[i++]; | ||||
|     this->write_sequence_(cmd, len, &this->init_sequence_[i]); | ||||
|     i += len; | ||||
|     esph_log_v(TAG, "Command %X, %d bytes", cmd, len); | ||||
|     if (cmd == SW_RESET_CMD) | ||||
|       delay(6); | ||||
|   } | ||||
|   // st7701 does not appear to support axis swapping | ||||
|   this->write_sequence_(CMD2_BKSEL, sizeof(CMD2_BK0), CMD2_BK0); | ||||
|   this->write_command_(SDIR_CMD);  // this is in the BK0 command set | ||||
|   this->write_data_(this->mirror_x_ ? 0x04 : 0x00); | ||||
|   uint8_t val = this->color_mode_ == display::COLOR_ORDER_BGR ? 0x08 : 0x00; | ||||
|   if (this->mirror_y_) | ||||
|     val |= 0x10; | ||||
|   this->write_command_(MADCTL_CMD); | ||||
|   this->write_data_(val); | ||||
|   esph_log_d(TAG, "write MADCTL %X", val); | ||||
|   this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF); | ||||
|   this->set_timeout(120, [this] { | ||||
|     this->write_command_(SLEEP_OUT); | ||||
|     this->write_command_(DISPLAY_ON); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| void ST7701S::dump_config() { | ||||
|   ESP_LOGCONFIG("", "ST7701S RGB LCD"); | ||||
|   ESP_LOGCONFIG(TAG, "  Height: %u", this->height_); | ||||
|   ESP_LOGCONFIG(TAG, "  Width: %u", this->width_); | ||||
|   LOG_PIN("  CS Pin: ", this->cs_); | ||||
|   LOG_PIN("  DC Pin: ", this->dc_pin_); | ||||
|   LOG_PIN("  DE Pin: ", this->de_pin_); | ||||
|   LOG_PIN("  Reset Pin: ", this->reset_pin_); | ||||
|   size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); | ||||
|   for (size_t i = 0; i != data_pin_count; i++) | ||||
|     ESP_LOGCONFIG(TAG, "  Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str()); | ||||
|   ESP_LOGCONFIG(TAG, "  SPI Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000)); | ||||
| } | ||||
|  | ||||
| }  // namespace st7701s | ||||
| }  // namespace esphome | ||||
| #endif  // USE_ESP32_VARIANT_ESP32S3 | ||||
							
								
								
									
										115
									
								
								esphome/components/st7701s/st7701s.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								esphome/components/st7701s/st7701s.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| // | ||||
| // Created by Clyde Stubbs on 29/10/2023. | ||||
| // | ||||
| #pragma once | ||||
|  | ||||
| // only applicable on ESP32-S3 | ||||
| #ifdef USE_ESP32_VARIANT_ESP32S3 | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/spi/spi.h" | ||||
| #include "esphome/components/display/display.h" | ||||
| #include "esp_lcd_panel_ops.h" | ||||
|  | ||||
| #include "esp_lcd_panel_rgb.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace st7701s { | ||||
|  | ||||
| constexpr static const char *const TAG = "display.st7701s"; | ||||
| const uint8_t SW_RESET_CMD = 0x01; | ||||
| const uint8_t SLEEP_OUT = 0x11; | ||||
| const uint8_t SDIR_CMD = 0xC7; | ||||
| const uint8_t MADCTL_CMD = 0x36; | ||||
| const uint8_t INVERT_OFF = 0x20; | ||||
| const uint8_t INVERT_ON = 0x21; | ||||
| const uint8_t DISPLAY_ON = 0x29; | ||||
| const uint8_t CMD2_BKSEL = 0xFF; | ||||
| const uint8_t CMD2_BK0[5] = {0x77, 0x01, 0x00, 0x00, 0x10}; | ||||
|  | ||||
| class ST7701S : public display::Display, | ||||
|                 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, | ||||
|                                       spi::DATA_RATE_1MHZ> { | ||||
|  public: | ||||
|   void update() override { this->do_update_(); } | ||||
|   void setup() override; | ||||
|   void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, | ||||
|                       display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; | ||||
|  | ||||
|   display::ColorOrder get_color_mode() { return this->color_mode_; } | ||||
|   void set_color_mode(display::ColorOrder color_mode) { this->color_mode_ = color_mode; } | ||||
|   void set_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; } | ||||
|  | ||||
|   void add_data_pin(InternalGPIOPin *data_pin, size_t index) { this->data_pins_[index] = data_pin; }; | ||||
|   void set_de_pin(InternalGPIOPin *de_pin) { this->de_pin_ = de_pin; } | ||||
|   void set_pclk_pin(InternalGPIOPin *pclk_pin) { this->pclk_pin_ = pclk_pin; } | ||||
|   void set_vsync_pin(InternalGPIOPin *vsync_pin) { this->vsync_pin_ = vsync_pin; } | ||||
|   void set_hsync_pin(InternalGPIOPin *hsync_pin) { this->hsync_pin_ = hsync_pin; } | ||||
|   void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; } | ||||
|   void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } | ||||
|   void set_width(uint16_t width) { this->width_ = width; } | ||||
|   void set_pclk_frequency(uint32_t pclk_frequency) { this->pclk_frequency_ = pclk_frequency; } | ||||
|   void set_pclk_inverted(bool inverted) { this->pclk_inverted_ = inverted; } | ||||
|   void set_dimensions(uint16_t width, uint16_t height) { | ||||
|     this->width_ = width; | ||||
|     this->height_ = height; | ||||
|   } | ||||
|   int get_width() override { return this->width_; } | ||||
|   int get_height() override { return this->height_; } | ||||
|   void set_hsync_back_porch(uint16_t hsync_back_porch) { this->hsync_back_porch_ = hsync_back_porch; } | ||||
|   void set_hsync_front_porch(uint16_t hsync_front_porch) { this->hsync_front_porch_ = hsync_front_porch; } | ||||
|   void set_hsync_pulse_width(uint16_t hsync_pulse_width) { this->hsync_pulse_width_ = hsync_pulse_width; } | ||||
|   void set_vsync_pulse_width(uint16_t vsync_pulse_width) { this->vsync_pulse_width_ = vsync_pulse_width; } | ||||
|   void set_vsync_back_porch(uint16_t vsync_back_porch) { this->vsync_back_porch_ = vsync_back_porch; } | ||||
|   void set_vsync_front_porch(uint16_t vsync_front_porch) { this->vsync_front_porch_ = vsync_front_porch; } | ||||
|   void set_init_sequence(const std::vector<uint8_t> &init_sequence) { this->init_sequence_ = init_sequence; } | ||||
|   void set_mirror_x(bool mirror_x) { this->mirror_x_ = mirror_x; } | ||||
|   void set_mirror_y(bool mirror_y) { this->mirror_y_ = mirror_y; } | ||||
|   void set_offsets(int16_t offset_x, int16_t offset_y) { | ||||
|     this->offset_x_ = offset_x; | ||||
|     this->offset_y_ = offset_y; | ||||
|   } | ||||
|   display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } | ||||
|   int get_width_internal() override { return this->width_; } | ||||
|   int get_height_internal() override { return this->height_; } | ||||
|   void dump_config() override; | ||||
|   void draw_pixel_at(int x, int y, Color color) override; | ||||
|  | ||||
|   // this will be horribly slow. | ||||
|  protected: | ||||
|   void write_command_(uint8_t value); | ||||
|   void write_data_(uint8_t value); | ||||
|   void write_sequence_(uint8_t cmd, size_t len, const uint8_t *bytes); | ||||
|   void write_init_sequence_(); | ||||
|  | ||||
|   InternalGPIOPin *de_pin_{nullptr}; | ||||
|   InternalGPIOPin *pclk_pin_{nullptr}; | ||||
|   InternalGPIOPin *hsync_pin_{nullptr}; | ||||
|   InternalGPIOPin *vsync_pin_{nullptr}; | ||||
|   GPIOPin *reset_pin_{nullptr}; | ||||
|   GPIOPin *dc_pin_{nullptr}; | ||||
|   InternalGPIOPin *data_pins_[16] = {}; | ||||
|   uint16_t hsync_pulse_width_ = 10; | ||||
|   uint16_t hsync_back_porch_ = 10; | ||||
|   uint16_t hsync_front_porch_ = 20; | ||||
|   uint16_t vsync_pulse_width_ = 10; | ||||
|   uint16_t vsync_back_porch_ = 10; | ||||
|   uint16_t vsync_front_porch_ = 10; | ||||
|   std::vector<uint8_t> init_sequence_; | ||||
|   uint32_t pclk_frequency_ = 16 * 1000 * 1000; | ||||
|   bool pclk_inverted_{true}; | ||||
|  | ||||
|   bool invert_colors_{}; | ||||
|   display::ColorOrder color_mode_{display::COLOR_ORDER_BGR}; | ||||
|   size_t width_{}; | ||||
|   size_t height_{}; | ||||
|   int16_t offset_x_{0}; | ||||
|   int16_t offset_y_{0}; | ||||
|   bool mirror_x_{}; | ||||
|   bool mirror_y_{}; | ||||
|  | ||||
|   esp_lcd_panel_handle_t handle_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace st7701s | ||||
| }  // namespace esphome | ||||
| #endif | ||||
							
								
								
									
										40
									
								
								tests/components/rpi_dpi_rgb/test.esp32-s3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								tests/components/rpi_dpi_rgb/test.esp32-s3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| psram: | ||||
|   mode: octal | ||||
|   speed: 80MHz | ||||
| display: | ||||
|   - platform: rpi_dpi_rgb | ||||
|     update_interval: never | ||||
|     auto_clear_enabled: false | ||||
|     id: rpi_display | ||||
|     color_order: RGB | ||||
|     rotation: 90 | ||||
|     dimensions: | ||||
|       width: 800 | ||||
|       height: 480 | ||||
|     de_pin: | ||||
|       number: 40 | ||||
|     hsync_pin: 39 | ||||
|     vsync_pin: 41 | ||||
|     pclk_pin: 42 | ||||
|     data_pins: | ||||
|       red: | ||||
|         - number: 45  # r1 | ||||
|           ignore_strapping_warning: true | ||||
|         - 48        # r2 | ||||
|         - 47        # r3 | ||||
|         - 21        # r4 | ||||
|         - number: 14  # r5 | ||||
|           ignore_strapping_warning: false | ||||
|       green: | ||||
|         - 5         # g0 | ||||
|         - 6         # g1 | ||||
|         - 7         # g2 | ||||
|         - 15        # g3 | ||||
|         - 16        # g4 | ||||
|         - 4         # g5 | ||||
|       blue: | ||||
|         - 8         # b1 | ||||
|         - 3         # b2 | ||||
|         - 46        # b3 | ||||
|         - 9         # b4 | ||||
|         - 1         # b5 | ||||
							
								
								
									
										60
									
								
								tests/components/st7701s/test.esp32-s3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								tests/components/st7701s/test.esp32-s3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| psram: | ||||
|   mode: octal | ||||
|   speed: 80MHz | ||||
| spi: | ||||
|   - id: lcd_spi | ||||
|     clk_pin: 41 | ||||
|     mosi_pin: 48 | ||||
|  | ||||
| i2c: | ||||
|   sda: 39 | ||||
|   scl: 40 | ||||
|   scan: false | ||||
|   id: bus_a | ||||
|  | ||||
| pca9554: | ||||
|   - id: p_c_a | ||||
|     pin_count: 16 | ||||
|     address: 0x20 | ||||
|  | ||||
| display: | ||||
|   - platform: st7701s | ||||
|     spi_mode: MODE3 | ||||
|     color_order: RGB | ||||
|     dimensions: | ||||
|       width: 480 | ||||
|       height: 480 | ||||
|     invert_colors: true | ||||
|     transform: | ||||
|       mirror_x: true | ||||
|       mirror_y: true | ||||
|     cs_pin: | ||||
|       pca9554: p_c_a | ||||
|       number: 4 | ||||
|     reset_pin: | ||||
|       pca9554: p_c_a | ||||
|       number: 5 | ||||
|     de_pin: 18 | ||||
|     hsync_pin: 16 | ||||
|     vsync_pin: 17 | ||||
|     pclk_pin: 21 | ||||
|     init_sequence: 1 | ||||
|     data_pins: | ||||
|       - number: 0 | ||||
|         ignore_strapping_warning: true | ||||
|       - 1 | ||||
|       - 2 | ||||
|       - 3 | ||||
|       - number: 4 | ||||
|         ignore_strapping_warning: false | ||||
|       - 5 | ||||
|       - 6 | ||||
|       - 7 | ||||
|       - 8 | ||||
|       - 9 | ||||
|       - 10 | ||||
|       - 11 | ||||
|       - 12 | ||||
|       - 13 | ||||
|       - 14 | ||||
|       - 15 | ||||
		Reference in New Issue
	
	Block a user