mirror of
https://github.com/esphome/esphome.git
synced 2026-10-06 19:06:37 +00:00
[mipi_spi] Implement automatic mapping of offsets (#16722)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI
Jonathan Swoboda
parent
35e5c7c7c3
commit
5b7f8cf90d
@@ -139,6 +139,8 @@ MADCTL_FLIP_FLAG = 0x100 # meta-flag to indicate use of axis flips
|
||||
# Special constant for delays in command sequences
|
||||
DELAY_FLAG = 0xFFF # Special flag to indicate a delay
|
||||
|
||||
CONF_PAD_HEIGHT = "pad_height"
|
||||
CONF_PAD_WIDTH = "pad_width"
|
||||
CONF_PIXEL_MODE = "pixel_mode"
|
||||
CONF_USE_AXIS_FLIPS = "use_axis_flips"
|
||||
|
||||
@@ -202,6 +204,8 @@ def dimension_schema(rounding):
|
||||
rounding
|
||||
),
|
||||
cv.Optional(CONF_OFFSET_WIDTH, default=0): validate_dimension(rounding),
|
||||
cv.Optional(CONF_PAD_WIDTH): validate_dimension(rounding),
|
||||
cv.Optional(CONF_PAD_HEIGHT): validate_dimension(rounding),
|
||||
}
|
||||
),
|
||||
)
|
||||
@@ -311,6 +315,36 @@ class DriverChip:
|
||||
name = name.upper()
|
||||
self.name = name
|
||||
self.initsequence = initsequence
|
||||
if CONF_NATIVE_WIDTH in defaults:
|
||||
if CONF_WIDTH not in defaults:
|
||||
defaults[CONF_WIDTH] = (
|
||||
defaults[CONF_NATIVE_WIDTH]
|
||||
- defaults.get(CONF_OFFSET_WIDTH, 0)
|
||||
- defaults.get(CONF_PAD_WIDTH, 0)
|
||||
)
|
||||
else:
|
||||
native_width = (
|
||||
defaults.get(CONF_WIDTH, 0)
|
||||
+ defaults.get(CONF_OFFSET_WIDTH, 0)
|
||||
+ defaults.get(CONF_PAD_WIDTH, 0)
|
||||
)
|
||||
if native_width != 0:
|
||||
defaults[CONF_NATIVE_WIDTH] = native_width
|
||||
if CONF_NATIVE_HEIGHT in defaults:
|
||||
if CONF_HEIGHT not in defaults:
|
||||
defaults[CONF_HEIGHT] = (
|
||||
defaults[CONF_NATIVE_HEIGHT]
|
||||
- defaults.get(CONF_OFFSET_HEIGHT, 0)
|
||||
- defaults.get(CONF_PAD_HEIGHT, 0)
|
||||
)
|
||||
else:
|
||||
native_height = (
|
||||
defaults.get(CONF_HEIGHT, 0)
|
||||
+ defaults.get(CONF_OFFSET_HEIGHT, 0)
|
||||
+ defaults.get(CONF_PAD_HEIGHT, 0)
|
||||
)
|
||||
if native_height != 0:
|
||||
defaults[CONF_NATIVE_HEIGHT] = native_height
|
||||
self.defaults = defaults
|
||||
DriverChip.models[name] = self
|
||||
|
||||
@@ -336,18 +370,6 @@ class DriverChip:
|
||||
initsequence = list(kwargs.pop("initsequence", self.initsequence))
|
||||
initsequence.extend(kwargs.pop("add_init_sequence", ()))
|
||||
defaults = self.defaults.copy()
|
||||
if (
|
||||
CONF_WIDTH in defaults
|
||||
and CONF_OFFSET_WIDTH in kwargs
|
||||
and CONF_NATIVE_WIDTH not in defaults
|
||||
):
|
||||
defaults[CONF_NATIVE_WIDTH] = defaults[CONF_WIDTH]
|
||||
if (
|
||||
CONF_HEIGHT in defaults
|
||||
and CONF_OFFSET_HEIGHT in kwargs
|
||||
and CONF_NATIVE_HEIGHT not in defaults
|
||||
):
|
||||
defaults[CONF_NATIVE_HEIGHT] = defaults[CONF_HEIGHT]
|
||||
defaults.update(kwargs)
|
||||
return self.__class__(name, initsequence=tuple(initsequence), **defaults)
|
||||
|
||||
@@ -385,13 +407,16 @@ class DriverChip:
|
||||
return CONF_SWAP_XY in transforms and CONF_MIRROR_X in transforms
|
||||
return CONF_SWAP_XY in transforms and CONF_MIRROR_Y in transforms
|
||||
|
||||
def get_dimensions(self, config, swap: bool = True) -> tuple[int, int, int, int]:
|
||||
def get_dimensions(
|
||||
self, config, swap: bool = True
|
||||
) -> tuple[int, int, int, int, int, int]:
|
||||
"""
|
||||
Return the dimensions of the current model.
|
||||
:param config: The current configuration
|
||||
:param swap: If width/height should be swapped when axes are swapped.
|
||||
:return:
|
||||
:return: A tuple (width, height, offset_width, offset_height, pad_width, pad_height).
|
||||
"""
|
||||
|
||||
if CONF_DIMENSIONS in config:
|
||||
# Explicit dimensions, just use as is
|
||||
dimensions = config[CONF_DIMENSIONS]
|
||||
@@ -400,33 +425,71 @@ class DriverChip:
|
||||
height = dimensions[CONF_HEIGHT]
|
||||
offset_width = dimensions[CONF_OFFSET_WIDTH]
|
||||
offset_height = dimensions[CONF_OFFSET_HEIGHT]
|
||||
return width, height, offset_width, offset_height
|
||||
(width, height) = dimensions
|
||||
return width, height, 0, 0
|
||||
if CONF_PAD_WIDTH in dimensions:
|
||||
pad_width = dimensions[CONF_PAD_WIDTH]
|
||||
native_width = width + offset_width + pad_width
|
||||
else:
|
||||
native_width = self.get_default(CONF_NATIVE_WIDTH, 0)
|
||||
if native_width == 0:
|
||||
pad_width = 0
|
||||
native_width = width + offset_width
|
||||
else:
|
||||
pad_width = native_width - width - offset_width
|
||||
if CONF_PAD_HEIGHT in dimensions:
|
||||
pad_height = dimensions[CONF_PAD_HEIGHT]
|
||||
native_height = height + offset_height + pad_height
|
||||
else:
|
||||
native_height = self.get_default(CONF_NATIVE_HEIGHT, 0)
|
||||
if native_height == 0:
|
||||
pad_height = 0
|
||||
native_height = height + offset_height
|
||||
else:
|
||||
pad_height = native_height - height - offset_height
|
||||
if (
|
||||
pad_width + offset_width >= native_width
|
||||
or pad_height + offset_height >= native_height
|
||||
):
|
||||
raise cv.Invalid("Dimensions exceed native size", [CONF_DIMENSIONS])
|
||||
if pad_width < 0 or pad_height < 0:
|
||||
raise cv.Invalid("Invalid offsets", [CONF_DIMENSIONS])
|
||||
|
||||
return width, height, offset_width, offset_height, pad_width, pad_height
|
||||
|
||||
# Must be a tuple
|
||||
width, height = dimensions
|
||||
return width, height, 0, 0, 0, 0
|
||||
|
||||
# Default dimensions, use model defaults
|
||||
transform = self.get_transform(config)
|
||||
|
||||
width = self.get_default(CONF_WIDTH)
|
||||
height = self.get_default(CONF_HEIGHT)
|
||||
native_width = self.get_default(CONF_NATIVE_WIDTH, 0)
|
||||
native_height = self.get_default(CONF_NATIVE_HEIGHT, 0)
|
||||
offset_width = self.get_default(CONF_OFFSET_WIDTH, 0)
|
||||
offset_height = self.get_default(CONF_OFFSET_HEIGHT, 0)
|
||||
pad_width = self.get_default(
|
||||
CONF_PAD_WIDTH, native_width - width - offset_width
|
||||
)
|
||||
pad_height = self.get_default(
|
||||
CONF_PAD_HEIGHT, native_height - height - offset_height
|
||||
)
|
||||
|
||||
if pad_width < 0 or pad_height < 0:
|
||||
raise cv.Invalid("Offsets exceed native size", [CONF_DIMENSIONS])
|
||||
|
||||
# if mirroring axes and there are offsets, also mirror the offsets to cater for situations where
|
||||
# the offset is asymmetric
|
||||
if transform.get(CONF_MIRROR_X):
|
||||
native_width = self.get_default(CONF_NATIVE_WIDTH, width + offset_width * 2)
|
||||
offset_width = native_width - width - offset_width
|
||||
offset_width, pad_width = pad_width, offset_width
|
||||
if transform.get(CONF_MIRROR_Y):
|
||||
native_height = self.get_default(
|
||||
CONF_NATIVE_HEIGHT, height + offset_height * 2
|
||||
)
|
||||
offset_height = native_height - height - offset_height
|
||||
# Swap default dimensions if swap_xy is set, or if rotation is 90/270 and we are not using a buffer
|
||||
offset_height, pad_height = pad_height, offset_height
|
||||
# Swap default dimensions if swap_xy is set, or if rotation is 90/270, and we are not using a buffer
|
||||
if swap and transform.get(CONF_SWAP_XY) is True:
|
||||
width, height = height, width
|
||||
offset_height, offset_width = offset_width, offset_height
|
||||
return width, height, offset_width, offset_height
|
||||
pad_width, pad_height = pad_height, pad_width
|
||||
return width, height, offset_width, offset_height, pad_width, pad_height
|
||||
|
||||
def get_base_transform(self, config):
|
||||
transform = config.get(
|
||||
@@ -450,20 +513,8 @@ class DriverChip:
|
||||
|
||||
def get_transform(self, config) -> dict[str, bool]:
|
||||
transform = self.get_base_transform(config)
|
||||
can_transform = self.rotation_as_transform(config)
|
||||
# Can we use the MADCTL register to set the rotation?
|
||||
if can_transform and CONF_TRANSFORM not in config:
|
||||
rotation = config[CONF_ROTATION]
|
||||
if rotation == 180:
|
||||
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
||||
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
||||
elif rotation == 90:
|
||||
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
||||
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
||||
else:
|
||||
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
||||
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
||||
transform[CONF_TRANSFORM] = True
|
||||
transform[CONF_TRANSFORM] = self.rotation_as_transform(config)
|
||||
return transform
|
||||
|
||||
def swap_xy_schema(self):
|
||||
@@ -498,8 +549,8 @@ class DriverChip:
|
||||
return madctl
|
||||
|
||||
def add_madctl(self, sequence: list, config: dict):
|
||||
# Add the MADCTL command to the sequence based on the configuration.
|
||||
# This takes into account rotation if it can be implemented in the transform
|
||||
# Add the MADCTL command to the sequence based on the base configuration.
|
||||
# Rotation is not applied here, it will be done at runtime.
|
||||
transform = self.get_transform(config)
|
||||
madctl = self.get_madctl(transform, config)
|
||||
sequence.append((MADCTL, madctl & 0xFF))
|
||||
|
||||
@@ -172,7 +172,9 @@ def _config_schema(config):
|
||||
)(config)
|
||||
config = model_schema(config)(config)
|
||||
model = MODELS[config[CONF_MODEL].upper()]
|
||||
width, height, _offset_width, _offset_height = model.get_dimensions(config)
|
||||
width, height, _offset_width, _offset_height, _pad_width, _pad_height = (
|
||||
model.get_dimensions(config)
|
||||
)
|
||||
display.add_metadata(
|
||||
config[CONF_ID],
|
||||
width,
|
||||
@@ -206,7 +208,9 @@ async def to_code(config):
|
||||
model = MODELS[config[CONF_MODEL].upper()]
|
||||
color_depth = COLOR_DEPTHS[get_color_depth(config)]
|
||||
pixel_mode = int(config[CONF_PIXEL_MODE].removesuffix("bit"))
|
||||
width, height, _offset_width, _offset_height = model.get_dimensions(config)
|
||||
width, height, _offset_width, _offset_height, _pad_width, _pad_height = (
|
||||
model.get_dimensions(config)
|
||||
)
|
||||
var = cg.new_Pvariable(config[CONF_ID], width, height, color_depth, pixel_mode)
|
||||
|
||||
sequence = model.get_sequence(config)
|
||||
|
||||
@@ -235,7 +235,9 @@ def _config_schema(config):
|
||||
only_on_variant(supported=[VARIANT_ESP32S3, VARIANT_ESP32P4]),
|
||||
)(config)
|
||||
model = MODELS[config[CONF_MODEL].upper()]
|
||||
width, height, _offset_width, _offset_height = model.get_dimensions(config)
|
||||
width, height, _offset_width, _offset_height, _pad_width, _pad_height = (
|
||||
model.get_dimensions(config)
|
||||
)
|
||||
display.add_metadata(
|
||||
config[CONF_ID],
|
||||
width,
|
||||
@@ -273,7 +275,9 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
async def to_code(config):
|
||||
model = MODELS[config[CONF_MODEL].upper()]
|
||||
width, height, _offset_width, _offset_height = model.get_dimensions(config)
|
||||
width, height, _offset_width, _offset_height, _pad_width, _pad_height = (
|
||||
model.get_dimensions(config)
|
||||
)
|
||||
var = cg.new_Pvariable(config[CONF_ID], width, height)
|
||||
cg.add(var.set_model(model.name))
|
||||
if enable_pin := config.get(CONF_ENABLE_PIN):
|
||||
|
||||
@@ -27,7 +27,7 @@ from esphome.components.mipi import (
|
||||
requires_buffer,
|
||||
)
|
||||
from esphome.components.psram import DOMAIN as PSRAM_DOMAIN
|
||||
from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE
|
||||
from esphome.components.spi import CONF_SPI_MODE, TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.config_validation import ALLOW_EXTRA
|
||||
from esphome.const import (
|
||||
@@ -121,7 +121,9 @@ def denominator(config):
|
||||
"""
|
||||
model = MODELS[config[CONF_MODEL]]
|
||||
frac = config.get(CONF_BUFFER_SIZE)
|
||||
_width, height, _offset_width, _offset_height = model.get_dimensions(config)
|
||||
_width, height, _offset_width, _offset_height, _pad_width, _pad_height = (
|
||||
model.get_dimensions(config)
|
||||
)
|
||||
if frac is None or frac > 0.75 or height < 32:
|
||||
return 1
|
||||
try:
|
||||
@@ -169,11 +171,22 @@ def model_schema(config):
|
||||
]
|
||||
if bus_mode == TYPE_SINGLE:
|
||||
other_options.append(CONF_SPI_16)
|
||||
# Calculate default SPI mode. Mode3 for octal bus or single bus with no cs pin, mode0 otherwise.
|
||||
spi_mode = model.get_default(CONF_SPI_MODE)
|
||||
if not spi_mode:
|
||||
if bus_mode == TYPE_OCTAL or (
|
||||
bus_mode == TYPE_SINGLE
|
||||
and not config.get(CONF_CS_PIN, model.get_default(CONF_CS_PIN))
|
||||
):
|
||||
spi_mode = "MODE3"
|
||||
else:
|
||||
spi_mode = "MODE0"
|
||||
|
||||
schema = (
|
||||
display.FULL_DISPLAY_SCHEMA.extend(
|
||||
spi.spi_device_schema(
|
||||
cs_pin_required=False,
|
||||
default_mode="MODE3" if bus_mode == TYPE_OCTAL else "MODE0",
|
||||
default_mode=spi_mode,
|
||||
default_data_rate=model.get_default(CONF_DATA_RATE, 10_000_000),
|
||||
mode=bus_mode,
|
||||
)
|
||||
@@ -279,8 +292,8 @@ def customise_schema(config):
|
||||
CONF_MIRROR_Y,
|
||||
CONF_SWAP_XY,
|
||||
}
|
||||
width, height, _offset_width, _offset_height = model.get_dimensions(
|
||||
config, not has_hardware_transform
|
||||
width, height, _offset_width, _offset_height, _pad_width, _pad_height = (
|
||||
model.get_dimensions(config, not has_hardware_transform)
|
||||
)
|
||||
display.add_metadata(
|
||||
config[CONF_ID],
|
||||
@@ -313,14 +326,17 @@ def _final_validate(config):
|
||||
# If no drawing methods are configured, and LVGL is not enabled, show a test card
|
||||
config[CONF_SHOW_TEST_CARD] = True
|
||||
|
||||
# Always call this to check dimensions during validation
|
||||
width, height, _offset_width, _offset_height, _pad_width, _pad_height = (
|
||||
model.get_dimensions(config)
|
||||
)
|
||||
|
||||
if PSRAM_DOMAIN not in global_config and CONF_BUFFER_SIZE not in config:
|
||||
# If PSRAM is not enabled, choose a small buffer size by default
|
||||
if not requires_buffer(config):
|
||||
return # No need to pick a size
|
||||
color_depth = get_color_depth(config)
|
||||
frac = denominator(config)
|
||||
width, height, _offset_width, _offset_height = model.get_dimensions(config)
|
||||
|
||||
buffer_size = color_depth // 8 * width * height // frac
|
||||
# Target a buffer size of 20kB, except for large displays, which shouldn't end up here
|
||||
fraction = min(20000.0, buffer_size // 4) / buffer_size
|
||||
@@ -347,8 +363,8 @@ def get_instance(config):
|
||||
CONF_MIRROR_Y,
|
||||
CONF_SWAP_XY,
|
||||
}
|
||||
width, height, offset_width, offset_height = model.get_dimensions(
|
||||
config, not has_hardware_transform
|
||||
width, height, offset_width, offset_height, pad_width, pad_height = (
|
||||
model.get_dimensions(config, not has_hardware_transform)
|
||||
)
|
||||
|
||||
color_depth = int(config[CONF_COLOR_DEPTH].removesuffix("bit"))
|
||||
@@ -374,6 +390,8 @@ def get_instance(config):
|
||||
height,
|
||||
offset_width,
|
||||
offset_height,
|
||||
pad_width,
|
||||
pad_height,
|
||||
madctl,
|
||||
has_hardware_transform,
|
||||
]
|
||||
|
||||
@@ -81,10 +81,15 @@ void internal_dump_config(const char *model, int width, int height, int offset_w
|
||||
* @tparam HEIGHT Height of the display in pixels
|
||||
* @tparam OFFSET_WIDTH The x-offset of the display in pixels
|
||||
* @tparam OFFSET_HEIGHT The y-offset of the display in pixels
|
||||
* @tparam PAD_WIDTH Additional pixels recognised by the controller after the offset and width
|
||||
* @tparam PAD_HEIGHT Additional lines recognised by the controller after the offset and width
|
||||
* @tparam MADCTL The base MADCTL value for the display, with no rotation bits set.
|
||||
* @tparam HAS_HARDWARE_ROTATION Whether the display supports hardware rotation.
|
||||
* buffer
|
||||
*/
|
||||
template<typename BUFFERTYPE, PixelMode BUFFERPIXEL, bool IS_BIG_ENDIAN, PixelMode DISPLAYPIXEL, BusType BUS_TYPE,
|
||||
int WIDTH, int HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, uint16_t MADCTL, bool HAS_HARDWARE_ROTATION>
|
||||
int WIDTH, int HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, int PAD_WIDTH, int PAD_HEIGHT, uint16_t MADCTL,
|
||||
bool HAS_HARDWARE_ROTATION>
|
||||
class MipiSpi : public display::Display,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
||||
spi::DATA_RATE_1MHZ> {
|
||||
@@ -126,17 +131,6 @@ class MipiSpi : public display::Display,
|
||||
return HEIGHT;
|
||||
}
|
||||
|
||||
// If hardware rotation is in use, the actual display width/height changes with rotation
|
||||
int get_width_internal() override {
|
||||
if constexpr (HAS_HARDWARE_ROTATION)
|
||||
return get_width();
|
||||
return WIDTH;
|
||||
}
|
||||
int get_height_internal() override {
|
||||
if constexpr (HAS_HARDWARE_ROTATION)
|
||||
return get_height();
|
||||
return HEIGHT;
|
||||
}
|
||||
void set_init_sequence(const std::vector<uint8_t> &sequence) { this->init_sequence_ = sequence; }
|
||||
|
||||
// reset the display, and write the init sequence
|
||||
@@ -233,14 +227,25 @@ class MipiSpi : public display::Display,
|
||||
}
|
||||
|
||||
void dump_config() override {
|
||||
internal_dump_config(this->model_, this->get_width(), this->get_height(), OFFSET_WIDTH, OFFSET_HEIGHT,
|
||||
(uint8_t) MADCTL, this->invert_colors_, DISPLAYPIXEL * 8, IS_BIG_ENDIAN, this->brightness_,
|
||||
this->cs_, this->reset_pin_, this->dc_pin_, this->mode_, this->data_rate_, BUS_TYPE,
|
||||
HAS_HARDWARE_ROTATION);
|
||||
internal_dump_config(this->model_, this->get_width(), this->get_height(), this->get_offset_width_(),
|
||||
this->get_offset_height_(), (uint8_t) MADCTL, this->invert_colors_, DISPLAYPIXEL * 8,
|
||||
IS_BIG_ENDIAN, this->brightness_, this->cs_, this->reset_pin_, this->dc_pin_, this->mode_,
|
||||
this->data_rate_, BUS_TYPE, HAS_HARDWARE_ROTATION);
|
||||
}
|
||||
|
||||
protected:
|
||||
/* METHODS */
|
||||
// If hardware rotation is in use, the actual display width/height changes with rotation
|
||||
int get_width_internal() override {
|
||||
if constexpr (HAS_HARDWARE_ROTATION)
|
||||
return get_width();
|
||||
return WIDTH;
|
||||
}
|
||||
int get_height_internal() override {
|
||||
if constexpr (HAS_HARDWARE_ROTATION)
|
||||
return get_height();
|
||||
return HEIGHT;
|
||||
}
|
||||
// convenience functions to write commands with or without data
|
||||
void write_command_(uint8_t cmd, uint8_t data) { this->write_command_(cmd, &data, 1); }
|
||||
void write_command_(uint8_t cmd) { this->write_command_(cmd, &cmd, 0); }
|
||||
@@ -330,20 +335,34 @@ class MipiSpi : public display::Display,
|
||||
this->write_command_(MADCTL_CMD, madctl);
|
||||
}
|
||||
|
||||
uint16_t get_offset_width_() {
|
||||
uint16_t get_offset_width_() const {
|
||||
if constexpr (HAS_HARDWARE_ROTATION) {
|
||||
if (this->rotation_ == display::DISPLAY_ROTATION_90_DEGREES ||
|
||||
this->rotation_ == display::DISPLAY_ROTATION_270_DEGREES)
|
||||
return OFFSET_HEIGHT;
|
||||
switch (this->rotation_) {
|
||||
case display::DISPLAY_ROTATION_90_DEGREES:
|
||||
return OFFSET_HEIGHT;
|
||||
case display::DISPLAY_ROTATION_180_DEGREES:
|
||||
return PAD_WIDTH;
|
||||
case display::DISPLAY_ROTATION_270_DEGREES:
|
||||
return PAD_HEIGHT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return OFFSET_WIDTH;
|
||||
}
|
||||
|
||||
uint16_t get_offset_height_() {
|
||||
uint16_t get_offset_height_() const {
|
||||
if constexpr (HAS_HARDWARE_ROTATION) {
|
||||
if (this->rotation_ == display::DISPLAY_ROTATION_90_DEGREES ||
|
||||
this->rotation_ == display::DISPLAY_ROTATION_270_DEGREES)
|
||||
return OFFSET_WIDTH;
|
||||
switch (this->rotation_) {
|
||||
case display::DISPLAY_ROTATION_90_DEGREES:
|
||||
return PAD_WIDTH;
|
||||
case display::DISPLAY_ROTATION_180_DEGREES:
|
||||
return PAD_HEIGHT;
|
||||
case display::DISPLAY_ROTATION_270_DEGREES:
|
||||
return OFFSET_WIDTH;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return OFFSET_HEIGHT;
|
||||
}
|
||||
@@ -396,7 +415,7 @@ class MipiSpi : public display::Display,
|
||||
this->write_cmd_addr_data(0, 0, 0, 0, ptr, w * h, 8);
|
||||
}
|
||||
} else {
|
||||
for (size_t y = 0; y != static_cast<size_t>(h); y++) {
|
||||
for (size_t y = 0; y != h; y++) {
|
||||
if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) {
|
||||
this->write_array(ptr, w);
|
||||
} else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) {
|
||||
@@ -492,19 +511,23 @@ class MipiSpi : public display::Display,
|
||||
* @tparam BUFFERPIXEL Color depth of the buffer
|
||||
* @tparam DISPLAYPIXEL Color depth of the display
|
||||
* @tparam BUS_TYPE The type of the interface bus (single, quad, octal)
|
||||
* @tparam ROTATION The rotation of the display
|
||||
* @tparam WIDTH Width of the display in pixels
|
||||
* @tparam HEIGHT Height of the display in pixels
|
||||
* @tparam OFFSET_WIDTH The x-offset of the display in pixels
|
||||
* @tparam OFFSET_HEIGHT The y-offset of the display in pixels
|
||||
* @tparam PAD_WIDTH Additional pixels recognised by the controller after the offset and width
|
||||
* @tparam PAD_HEIGHT Additional lines recognised by the controller after the offset and width
|
||||
* @tparam MADCTL The base MADCTL value for the display, with no rotation bits set.
|
||||
* @tparam HAS_HARDWARE_ROTATION Whether the display supports hardware rotation.
|
||||
* @tparam FRACTION The fraction of the display size to use for the buffer (e.g. 4 means a 1/4 buffer).
|
||||
* @tparam ROUNDING The alignment requirement for drawing operations (e.g. 2 means that x coordinates must be even)
|
||||
*/
|
||||
template<typename BUFFERTYPE, PixelMode BUFFERPIXEL, bool IS_BIG_ENDIAN, PixelMode DISPLAYPIXEL, BusType BUS_TYPE,
|
||||
uint16_t WIDTH, uint16_t HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, uint16_t MADCTL,
|
||||
bool HAS_HARDWARE_ROTATION, int FRACTION, unsigned ROUNDING>
|
||||
class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT,
|
||||
OFFSET_WIDTH, OFFSET_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION> {
|
||||
uint16_t WIDTH, uint16_t HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, int PAD_WIDTH, int PAD_HEIGHT,
|
||||
uint16_t MADCTL, bool HAS_HARDWARE_ROTATION, int FRACTION, unsigned ROUNDING>
|
||||
class MipiSpiBuffer
|
||||
: public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
|
||||
OFFSET_HEIGHT, PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION> {
|
||||
public:
|
||||
// these values define the buffer size needed to write in accordance with the chip pixel alignment
|
||||
// requirements. If the required rounding does not divide the width and height, we round up to the next multiple and
|
||||
@@ -515,7 +538,7 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
||||
|
||||
void dump_config() override {
|
||||
MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH, OFFSET_HEIGHT,
|
||||
MADCTL, HAS_HARDWARE_ROTATION>::dump_config();
|
||||
PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION>::dump_config();
|
||||
esph_log_config(TAG,
|
||||
" Rotation: %d°\n"
|
||||
" Buffer pixels: %d bits\n"
|
||||
@@ -528,7 +551,7 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
||||
|
||||
void setup() override {
|
||||
MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH, OFFSET_HEIGHT,
|
||||
MADCTL, HAS_HARDWARE_ROTATION>::setup();
|
||||
PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION>::setup();
|
||||
RAMAllocator<BUFFERTYPE> allocator{};
|
||||
this->buffer_ = allocator.allocate(round_buffer(WIDTH) * round_buffer(HEIGHT) / FRACTION);
|
||||
if (this->buffer_ == nullptr) {
|
||||
|
||||
@@ -179,6 +179,9 @@ ILI9342 = DriverChip(
|
||||
# M5Stack Core2 uses ILI9341 chip - mirror_x disabled for correct orientation
|
||||
ILI9341.extend(
|
||||
"M5CORE2",
|
||||
# Reset native dimensions due to axis swap.
|
||||
native_width=320,
|
||||
native_height=240,
|
||||
width=320,
|
||||
height=240,
|
||||
mirror_x=False,
|
||||
@@ -786,3 +789,28 @@ ST7796.extend(
|
||||
dc_pin=0,
|
||||
invert_colors=True,
|
||||
)
|
||||
|
||||
ST7789V.extend(
|
||||
"GEEKMAGIC-SMALLTV",
|
||||
data_rate="40MHz",
|
||||
height=240,
|
||||
width=240,
|
||||
offset_width=0,
|
||||
offset_height=0,
|
||||
invert_colors=True,
|
||||
buffer_size=0.125,
|
||||
reset_pin=2,
|
||||
dc_pin=0,
|
||||
)
|
||||
ST7789V.extend(
|
||||
"GEEKMAGIC-SMALLTV-PRO",
|
||||
data_rate="40MHz",
|
||||
height=240,
|
||||
width=240,
|
||||
offset_width=0,
|
||||
offset_height=0,
|
||||
invert_colors=True,
|
||||
buffer_size=0.125,
|
||||
reset_pin=4,
|
||||
dc_pin=2,
|
||||
)
|
||||
|
||||
@@ -269,3 +269,16 @@ ST7789V.extend(
|
||||
cs_pin=14,
|
||||
dc_pin={"number": 15, "ignore_strapping_warning": True},
|
||||
)
|
||||
|
||||
ST7789V.extend(
|
||||
"WAVESHARE-ESP32-S3-GEEK",
|
||||
cs_pin=10,
|
||||
dc_pin=8,
|
||||
reset_pin=9,
|
||||
width=135,
|
||||
height=240,
|
||||
offset_width=52,
|
||||
offset_height=40,
|
||||
invert_colors=True,
|
||||
data_rate="40MHz",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user