mirror of
https://github.com/esphome/esphome.git
synced 2024-11-28 09:44:12 +01:00
Speed up (and fix) ili9xxx display component. (#5406)
This commit is contained in:
parent
ab1cc0ed6e
commit
993cd55b1d
5 changed files with 317 additions and 280 deletions
|
@ -2,6 +2,7 @@ import esphome.codegen as cg
|
|||
import esphome.config_validation as cv
|
||||
from esphome import core, pins
|
||||
from esphome.components import display, spi, font
|
||||
from esphome.components.display import validate_rotation
|
||||
from esphome.core import CORE, HexInt
|
||||
from esphome.const import (
|
||||
CONF_COLOR_PALETTE,
|
||||
|
@ -13,6 +14,9 @@ from esphome.const import (
|
|||
CONF_PAGES,
|
||||
CONF_RESET_PIN,
|
||||
CONF_DIMENSIONS,
|
||||
CONF_WIDTH,
|
||||
CONF_HEIGHT,
|
||||
CONF_ROTATION,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
@ -26,28 +30,35 @@ def AUTO_LOAD():
|
|||
|
||||
CODEOWNERS = ["@nielsnl68", "@clydebarrow"]
|
||||
|
||||
ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx")
|
||||
ili9XXXSPI = ili9XXX_ns.class_(
|
||||
ili9xxx_ns = cg.esphome_ns.namespace("ili9xxx")
|
||||
ILI9XXXDisplay = ili9xxx_ns.class_(
|
||||
"ILI9XXXDisplay", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer
|
||||
)
|
||||
|
||||
ILI9XXXColorMode = ili9XXX_ns.enum("ILI9XXXColorMode")
|
||||
ILI9XXXColorMode = ili9xxx_ns.enum("ILI9XXXColorMode")
|
||||
ColorOrder = display.display_ns.enum("ColorMode")
|
||||
|
||||
MODELS = {
|
||||
"M5STACK": ili9XXX_ns.class_("ILI9XXXM5Stack", ili9XXXSPI),
|
||||
"M5CORE": ili9XXX_ns.class_("ILI9XXXM5CORE", ili9XXXSPI),
|
||||
"TFT_2.4": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI),
|
||||
"TFT_2.4R": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI),
|
||||
"ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI),
|
||||
"ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI),
|
||||
"ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI),
|
||||
"ILI9481-18": ili9XXX_ns.class_("ILI9XXXILI948118", ili9XXXSPI),
|
||||
"ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
|
||||
"ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
|
||||
"ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI),
|
||||
"ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI),
|
||||
"S3BOX": ili9XXX_ns.class_("ILI9XXXS3Box", ili9XXXSPI),
|
||||
"S3BOX_LITE": ili9XXX_ns.class_("ILI9XXXS3BoxLite", ili9XXXSPI),
|
||||
"M5STACK": ili9xxx_ns.class_("ILI9XXXM5Stack", ILI9XXXDisplay),
|
||||
"M5CORE": ili9xxx_ns.class_("ILI9XXXM5CORE", ILI9XXXDisplay),
|
||||
"TFT_2.4": ili9xxx_ns.class_("ILI9XXXILI9341", ILI9XXXDisplay),
|
||||
"TFT_2.4R": ili9xxx_ns.class_("ILI9XXXILI9342", ILI9XXXDisplay),
|
||||
"ILI9341": ili9xxx_ns.class_("ILI9XXXILI9341", ILI9XXXDisplay),
|
||||
"ILI9342": ili9xxx_ns.class_("ILI9XXXILI9342", ILI9XXXDisplay),
|
||||
"ILI9481": ili9xxx_ns.class_("ILI9XXXILI9481", ILI9XXXDisplay),
|
||||
"ILI9481-18": ili9xxx_ns.class_("ILI9XXXILI948118", ILI9XXXDisplay),
|
||||
"ILI9486": ili9xxx_ns.class_("ILI9XXXILI9486", ILI9XXXDisplay),
|
||||
"ILI9488": ili9xxx_ns.class_("ILI9XXXILI9488", ILI9XXXDisplay),
|
||||
"ILI9488_A": ili9xxx_ns.class_("ILI9XXXILI9488A", ILI9XXXDisplay),
|
||||
"ST7796": ili9xxx_ns.class_("ILI9XXXST7796", ILI9XXXDisplay),
|
||||
"ST7789V": ili9xxx_ns.class_("ILI9XXXST7789V", ILI9XXXDisplay),
|
||||
"S3BOX": ili9xxx_ns.class_("ILI9XXXS3Box", ILI9XXXDisplay),
|
||||
"S3BOX_LITE": ili9xxx_ns.class_("ILI9XXXS3BoxLite", ILI9XXXDisplay),
|
||||
}
|
||||
|
||||
COLOR_ORDERS = {
|
||||
"RGB": ColorOrder.COLOR_ORDER_RGB,
|
||||
"BGR": ColorOrder.COLOR_ORDER_BGR,
|
||||
}
|
||||
|
||||
COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
|
||||
|
@ -55,6 +66,14 @@ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
|
|||
CONF_LED_PIN = "led_pin"
|
||||
CONF_COLOR_PALETTE_IMAGES = "color_palette_images"
|
||||
CONF_INVERT_DISPLAY = "invert_display"
|
||||
CONF_INVERT_COLORS = "invert_colors"
|
||||
CONF_MIRROR_X = "mirror_x"
|
||||
CONF_MIRROR_Y = "mirror_y"
|
||||
CONF_SWAP_XY = "swap_xy"
|
||||
CONF_COLOR_ORDER = "color_order"
|
||||
CONF_OFFSET_HEIGHT = "offset_height"
|
||||
CONF_OFFSET_WIDTH = "offset_width"
|
||||
CONF_TRANSFORM = "transform"
|
||||
|
||||
|
||||
def _validate(config):
|
||||
|
@ -77,6 +96,7 @@ def _validate(config):
|
|||
"TFT_2.4R",
|
||||
"ILI9341",
|
||||
"ILI9342",
|
||||
"ST7789V",
|
||||
]:
|
||||
raise cv.Invalid(
|
||||
"Provided model can't run on ESP8266. Use an ESP32 with PSRAM onboard"
|
||||
|
@ -88,9 +108,19 @@ CONFIG_SCHEMA = cv.All(
|
|||
font.validate_pillow_installed,
|
||||
display.FULL_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ili9XXXSPI),
|
||||
cv.GenerateID(): cv.declare_id(ILI9XXXDisplay),
|
||||
cv.Required(CONF_MODEL): cv.enum(MODELS, upper=True, space="_"),
|
||||
cv.Optional(CONF_DIMENSIONS): cv.dimensions,
|
||||
cv.Optional(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.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_LED_PIN): cv.invalid(
|
||||
|
@ -101,7 +131,19 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list(
|
||||
cv.file_
|
||||
),
|
||||
cv.Optional(CONF_INVERT_DISPLAY): cv.boolean,
|
||||
cv.Optional(CONF_INVERT_DISPLAY): cv.invalid(
|
||||
"'invert_display' has been replaced by 'invert_colors'"
|
||||
),
|
||||
cv.Optional(CONF_INVERT_COLORS): cv.boolean,
|
||||
cv.Optional(CONF_COLOR_ORDER): cv.one_of(*COLOR_ORDERS.keys(), upper=True),
|
||||
cv.Exclusive(CONF_ROTATION, CONF_ROTATION): validate_rotation,
|
||||
cv.Exclusive(CONF_TRANSFORM, CONF_ROTATION): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_SWAP_XY, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MIRROR_X, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("1s"))
|
||||
|
@ -119,6 +161,13 @@ async def to_code(config):
|
|||
await spi.register_spi_device(var, config)
|
||||
dc = await cg.gpio_pin_expression(config[CONF_DC_PIN])
|
||||
cg.add(var.set_dc_pin(dc))
|
||||
if CONF_COLOR_ORDER in config:
|
||||
cg.add(var.set_color_order(COLOR_ORDERS[config[CONF_COLOR_ORDER]]))
|
||||
if CONF_TRANSFORM in config:
|
||||
transform = config[CONF_TRANSFORM]
|
||||
cg.add(var.set_swap_xy(transform[CONF_SWAP_XY]))
|
||||
cg.add(var.set_mirror_x(transform[CONF_MIRROR_X]))
|
||||
cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y]))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
|
@ -131,9 +180,17 @@ async def to_code(config):
|
|||
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_dimentions(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])
|
||||
var.set_offsets(
|
||||
dimensions[CONF_OFFSET_WIDTH], dimensions[CONF_OFFSET_HEIGHT]
|
||||
)
|
||||
)
|
||||
else:
|
||||
(width, height) = dimensions
|
||||
cg.add(var.set_dimensions(width, height))
|
||||
|
||||
rhs = None
|
||||
if config[CONF_COLOR_PALETTE] == "GRAYSCALE":
|
||||
|
@ -178,5 +235,5 @@ async def to_code(config):
|
|||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||
cg.add(var.set_palette(prog_arr))
|
||||
|
||||
if CONF_INVERT_DISPLAY in config:
|
||||
cg.add(var.invert_display(config[CONF_INVERT_DISPLAY]))
|
||||
if CONF_INVERT_COLORS in config:
|
||||
cg.add(var.invert_colors(config[CONF_INVERT_COLORS]))
|
||||
|
|
|
@ -8,11 +8,31 @@ namespace esphome {
|
|||
namespace ili9xxx {
|
||||
|
||||
static const char *const TAG = "ili9xxx";
|
||||
static const uint16_t SPI_SETUP_US = 100; // estimated fixed overhead in microseconds for an SPI write
|
||||
static const uint16_t SPI_MAX_BLOCK_SIZE = 4092; // Max size of continuous SPI transfer
|
||||
|
||||
// store a 16 bit value in a buffer, big endian.
|
||||
static inline void put16_be(uint8_t *buf, uint16_t value) {
|
||||
buf[0] = value >> 8;
|
||||
buf[1] = value;
|
||||
}
|
||||
|
||||
void ILI9XXXDisplay::setup() {
|
||||
ESP_LOGD(TAG, "Setting up ILI9xxx");
|
||||
|
||||
this->setup_pins_();
|
||||
this->initialize();
|
||||
this->command(this->pre_invertdisplay_ ? ILI9XXX_INVON : ILI9XXX_INVOFF);
|
||||
this->init_lcd_();
|
||||
|
||||
this->command(this->pre_invertcolors_ ? ILI9XXX_INVON : ILI9XXX_INVOFF);
|
||||
// custom x/y transform and color order
|
||||
uint8_t mad = this->color_order_ == display::COLOR_ORDER_BGR ? MADCTL_BGR : MADCTL_RGB;
|
||||
if (this->swap_xy_)
|
||||
mad |= MADCTL_MV;
|
||||
if (this->mirror_x_)
|
||||
mad |= MADCTL_MX;
|
||||
if (this->mirror_y_)
|
||||
mad |= MADCTL_MY;
|
||||
this->send_command(ILI9XXX_MADCTL, &mad, 1);
|
||||
|
||||
this->x_low_ = this->width_;
|
||||
this->y_low_ = this->height_;
|
||||
|
@ -47,6 +67,8 @@ void ILI9XXXDisplay::setup_pins_() {
|
|||
|
||||
void ILI9XXXDisplay::dump_config() {
|
||||
LOG_DISPLAY("", "ili9xxx", this);
|
||||
ESP_LOGCONFIG(TAG, " Width Offset: %u", this->offset_x_);
|
||||
ESP_LOGCONFIG(TAG, " Height Offset: %u", this->offset_y_);
|
||||
switch (this->buffer_color_mode_) {
|
||||
case BITS_8_INDEXED:
|
||||
ESP_LOGCONFIG(TAG, " Color mode: 8bit Indexed");
|
||||
|
@ -64,8 +86,12 @@ void ILI9XXXDisplay::dump_config() {
|
|||
ESP_LOGCONFIG(TAG, " Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000));
|
||||
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Swap_xy: %s", YESNO(this->swap_xy_));
|
||||
ESP_LOGCONFIG(TAG, " Mirror_x: %s", YESNO(this->mirror_x_));
|
||||
ESP_LOGCONFIG(TAG, " Mirror_y: %s", YESNO(this->mirror_y_));
|
||||
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGCONFIG(TAG, " => Failed to init Memory: YES!");
|
||||
|
@ -141,12 +167,14 @@ void HOT ILI9XXXDisplay::draw_absolute_pixel_internal(int x, int y, Color color)
|
|||
}
|
||||
if (updated) {
|
||||
// low and high watermark may speed up drawing from buffer
|
||||
this->x_low_ = (x < this->x_low_) ? x : this->x_low_;
|
||||
this->y_low_ = (y < this->y_low_) ? y : this->y_low_;
|
||||
this->x_high_ = (x > this->x_high_) ? x : this->x_high_;
|
||||
this->y_high_ = (y > this->y_high_) ? y : this->y_high_;
|
||||
// ESP_LOGVV(TAG, "=>>> pixel (x:%d, y:%d) (xl:%d, xh:%d, yl:%d, yh:%d", x, y, this->x_low_, this->x_high_,
|
||||
// this->y_low_, this->y_high_);
|
||||
if (x < this->x_low_)
|
||||
this->x_low_ = x;
|
||||
if (y < this->y_low_)
|
||||
this->y_low_ = y;
|
||||
if (x > this->x_high_)
|
||||
this->x_high_ = x;
|
||||
if (y > this->y_high_)
|
||||
this->y_high_ = y;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,59 +193,82 @@ void ILI9XXXDisplay::update() {
|
|||
}
|
||||
|
||||
void ILI9XXXDisplay::display_() {
|
||||
// we will only update the changed window to the display
|
||||
uint16_t w = this->x_high_ - this->x_low_ + 1; // NOLINT
|
||||
uint16_t h = this->y_high_ - this->y_low_ + 1; // NOLINT
|
||||
uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_);
|
||||
|
||||
uint8_t transfer_buffer[ILI9XXX_TRANSFER_BUFFER_SIZE];
|
||||
// check if something was displayed
|
||||
if ((this->x_high_ < this->x_low_) || (this->y_high_ < this->y_low_)) {
|
||||
ESP_LOGV(TAG, "Nothing to display");
|
||||
return;
|
||||
}
|
||||
|
||||
set_addr_window_(this->x_low_, this->y_low_, w, h);
|
||||
// we will only update the changed rows to the display
|
||||
size_t const w = this->x_high_ - this->x_low_ + 1;
|
||||
size_t const h = this->y_high_ - this->y_low_ + 1;
|
||||
|
||||
size_t mhz = this->data_rate_ / 1000000;
|
||||
// estimate time for a single write
|
||||
size_t sw_time = this->width_ * h * 16 / mhz + this->width_ * h * 2 / SPI_MAX_BLOCK_SIZE * SPI_SETUP_US * 2;
|
||||
// estimate time for multiple writes
|
||||
size_t mw_time = (w * h * 16) / mhz + w * h * 2 / ILI9XXX_TRANSFER_BUFFER_SIZE * SPI_SETUP_US;
|
||||
ESP_LOGV(TAG,
|
||||
"Start display(xlow:%d, ylow:%d, xhigh:%d, yhigh:%d, width:%d, "
|
||||
"heigth:%d, start_pos:%" PRId32 ")",
|
||||
this->x_low_, this->y_low_, this->x_high_, this->y_high_, w, h, start_pos);
|
||||
|
||||
this->start_data_();
|
||||
for (uint16_t row = 0; row < h; row++) {
|
||||
uint32_t pos = start_pos + (row * width_);
|
||||
uint32_t rem = w;
|
||||
|
||||
while (rem > 0) {
|
||||
uint32_t sz = std::min(rem, ILI9XXX_TRANSFER_BUFFER_SIZE);
|
||||
// ESP_LOGVV(TAG, "Send to display(pos:%d, rem:%d, zs:%d)", pos, rem, sz);
|
||||
buffer_to_transfer_(pos, sz);
|
||||
if (this->is_18bitdisplay_) {
|
||||
for (uint32_t i = 0; i < sz; ++i) {
|
||||
uint16_t color_val = transfer_buffer_[i];
|
||||
|
||||
uint8_t red = color_val & 0x1F;
|
||||
uint8_t green = (color_val & 0x7E0) >> 5;
|
||||
uint8_t blue = (color_val & 0xF800) >> 11;
|
||||
|
||||
uint8_t pass_buff[3];
|
||||
|
||||
pass_buff[2] = (uint8_t) ((red / 32.0) * 64) << 2;
|
||||
pass_buff[1] = (uint8_t) green << 2;
|
||||
pass_buff[0] = (uint8_t) ((blue / 32.0) * 64) << 2;
|
||||
|
||||
this->write_array(pass_buff, sizeof(pass_buff));
|
||||
}
|
||||
"height:%d, mode=%d, 18bit=%d, sw_time=%dus, mw_time=%dus)",
|
||||
this->x_low_, this->y_low_, this->x_high_, this->y_high_, w, h, this->buffer_color_mode_,
|
||||
this->is_18bitdisplay_, sw_time, mw_time);
|
||||
auto now = millis();
|
||||
this->enable();
|
||||
if (this->buffer_color_mode_ == BITS_16 && !this->is_18bitdisplay_ && sw_time < mw_time) {
|
||||
// 16 bit mode maps directly to display format
|
||||
ESP_LOGV(TAG, "Doing single write of %d bytes", this->width_ * h * 2);
|
||||
set_addr_window_(0, this->y_low_, this->width_ - 1, this->y_high_);
|
||||
this->write_array(this->buffer_ + this->y_low_ * this->width_ * 2, h * this->width_ * 2);
|
||||
} else {
|
||||
this->write_array16(transfer_buffer_, sz);
|
||||
ESP_LOGV(TAG, "Doing multiple write");
|
||||
size_t rem = h * w; // remaining number of pixels to write
|
||||
set_addr_window_(this->x_low_, this->y_low_, this->x_high_, this->y_high_);
|
||||
size_t idx = 0; // index into transfer_buffer
|
||||
size_t pixel = 0; // pixel number offset
|
||||
size_t pos = this->y_low_ * this->width_ + this->x_low_;
|
||||
while (rem-- != 0) {
|
||||
uint16_t color_val;
|
||||
switch (this->buffer_color_mode_) {
|
||||
case BITS_8:
|
||||
color_val = display::ColorUtil::color_to_565(display::ColorUtil::rgb332_to_color(this->buffer_[pos++]));
|
||||
break;
|
||||
case BITS_8_INDEXED:
|
||||
color_val = display::ColorUtil::color_to_565(
|
||||
display::ColorUtil::index8_to_color_palette888(this->buffer_[pos++], this->palette_));
|
||||
break;
|
||||
default: // case BITS_16:
|
||||
color_val = (buffer_[pos * 2] << 8) + buffer_[pos * 2 + 1];
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
pos += sz;
|
||||
rem -= sz;
|
||||
if (this->is_18bitdisplay_) {
|
||||
transfer_buffer[idx++] = (uint8_t) ((color_val & 0xF800) >> 8); // Blue
|
||||
transfer_buffer[idx++] = (uint8_t) ((color_val & 0x7E0) >> 3); // Green
|
||||
transfer_buffer[idx++] = (uint8_t) (color_val << 3); // Red
|
||||
} else {
|
||||
put16_be(transfer_buffer + idx, color_val);
|
||||
idx += 2;
|
||||
}
|
||||
if (idx == ILI9XXX_TRANSFER_BUFFER_SIZE) {
|
||||
this->write_array(transfer_buffer, idx);
|
||||
idx = 0;
|
||||
App.feed_wdt();
|
||||
}
|
||||
this->end_data_();
|
||||
|
||||
// end of line? Skip to the next.
|
||||
if (++pixel == w) {
|
||||
pixel = 0;
|
||||
pos += this->width_ - w;
|
||||
}
|
||||
}
|
||||
// flush any balance.
|
||||
if (idx != 0) {
|
||||
this->write_array(transfer_buffer, idx);
|
||||
}
|
||||
}
|
||||
this->disable();
|
||||
ESP_LOGV(TAG, "Data write took %dms", (unsigned) (millis() - now));
|
||||
// invalidate watermarks
|
||||
this->x_low_ = this->width_;
|
||||
this->y_low_ = this->height_;
|
||||
|
@ -225,26 +276,6 @@ void ILI9XXXDisplay::display_() {
|
|||
this->y_high_ = 0;
|
||||
}
|
||||
|
||||
uint32_t ILI9XXXDisplay::buffer_to_transfer_(uint32_t pos, uint32_t sz) {
|
||||
for (uint32_t i = 0; i < sz; ++i) {
|
||||
switch (this->buffer_color_mode_) {
|
||||
case BITS_8_INDEXED:
|
||||
transfer_buffer_[i] = display::ColorUtil::color_to_565(
|
||||
display::ColorUtil::index8_to_color_palette888(this->buffer_[pos + i], this->palette_));
|
||||
break;
|
||||
case BITS_16:
|
||||
transfer_buffer_[i] = ((uint16_t) this->buffer_[(pos + i) * 2] << 8) | this->buffer_[((pos + i) * 2) + 1];
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
transfer_buffer_[i] =
|
||||
display::ColorUtil::color_to_565(display::ColorUtil::rgb332_to_color(this->buffer_[pos + i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
// should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color
|
||||
// values per bit is huge
|
||||
uint32_t ILI9XXXDisplay::get_buffer_length_() { return this->get_width_internal() * this->get_height_internal(); }
|
||||
|
@ -303,11 +334,11 @@ void ILI9XXXDisplay::reset_() {
|
|||
}
|
||||
}
|
||||
|
||||
void ILI9XXXDisplay::init_lcd_(const uint8_t *init_cmd) {
|
||||
void ILI9XXXDisplay::init_lcd_() {
|
||||
uint8_t cmd, x, num_args;
|
||||
const uint8_t *addr = init_cmd;
|
||||
while ((cmd = progmem_read_byte(addr++)) > 0) {
|
||||
x = progmem_read_byte(addr++);
|
||||
const uint8_t *addr = this->init_sequence_;
|
||||
while ((cmd = *addr++) > 0) {
|
||||
x = *addr++;
|
||||
num_args = x & 0x7F;
|
||||
send_command(cmd, addr, num_args);
|
||||
addr += num_args;
|
||||
|
@ -316,27 +347,29 @@ void ILI9XXXDisplay::init_lcd_(const uint8_t *init_cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t w, uint16_t h) {
|
||||
uint16_t x2 = (x1 + w - 1), y2 = (y1 + h - 1);
|
||||
this->command(ILI9XXX_CASET); // Column address set
|
||||
this->start_data_();
|
||||
this->write_byte(x1 >> 8);
|
||||
this->write_byte(x1);
|
||||
this->write_byte(x2 >> 8);
|
||||
this->write_byte(x2);
|
||||
this->end_data_();
|
||||
this->command(ILI9XXX_PASET); // Row address set
|
||||
this->start_data_();
|
||||
this->write_byte(y1 >> 8);
|
||||
this->write_byte(y1);
|
||||
this->write_byte(y2 >> 8);
|
||||
this->write_byte(y2);
|
||||
this->end_data_();
|
||||
this->command(ILI9XXX_RAMWR); // Write to RAM
|
||||
// Tell the display controller where we want to draw pixels.
|
||||
// when called, the SPI should have already been enabled, only the D/C pin will be toggled here.
|
||||
void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
|
||||
uint8_t buf[4];
|
||||
this->dc_pin_->digital_write(false);
|
||||
this->write_byte(ILI9XXX_CASET); // Column address set
|
||||
put16_be(buf, x1 + this->offset_x_);
|
||||
put16_be(buf + 2, x2 + this->offset_x_);
|
||||
this->dc_pin_->digital_write(true);
|
||||
this->write_array(buf, sizeof buf);
|
||||
this->dc_pin_->digital_write(false);
|
||||
this->write_byte(ILI9XXX_PASET); // Row address set
|
||||
put16_be(buf, y1 + this->offset_y_);
|
||||
put16_be(buf + 2, y2 + this->offset_y_);
|
||||
this->dc_pin_->digital_write(true);
|
||||
this->write_array(buf, sizeof buf);
|
||||
this->dc_pin_->digital_write(false);
|
||||
this->write_byte(ILI9XXX_RAMWR); // Write to RAM
|
||||
this->dc_pin_->digital_write(true);
|
||||
}
|
||||
|
||||
void ILI9XXXDisplay::invert_display(bool invert) {
|
||||
this->pre_invertdisplay_ = invert;
|
||||
void ILI9XXXDisplay::invert_colors(bool invert) {
|
||||
this->pre_invertcolors_ = invert;
|
||||
if (is_ready()) {
|
||||
this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF);
|
||||
}
|
||||
|
@ -345,132 +378,5 @@ void ILI9XXXDisplay::invert_display(bool invert) {
|
|||
int ILI9XXXDisplay::get_width_internal() { return this->width_; }
|
||||
int ILI9XXXDisplay::get_height_internal() { return this->height_; }
|
||||
|
||||
// M5Stack display
|
||||
void ILI9XXXM5Stack::initialize() {
|
||||
this->init_lcd_(INITCMD_M5STACK);
|
||||
if (this->width_ == 0)
|
||||
this->width_ = 320;
|
||||
if (this->height_ == 0)
|
||||
this->height_ = 240;
|
||||
this->pre_invertdisplay_ = true;
|
||||
}
|
||||
|
||||
// M5CORE display // Based on the configuration settings of M5stact's M5GFX code.
|
||||
void ILI9XXXM5CORE::initialize() {
|
||||
this->init_lcd_(INITCMD_M5CORE);
|
||||
if (this->width_ == 0)
|
||||
this->width_ = 320;
|
||||
if (this->height_ == 0)
|
||||
this->height_ = 240;
|
||||
this->pre_invertdisplay_ = true;
|
||||
}
|
||||
|
||||
// 24_TFT display
|
||||
void ILI9XXXILI9341::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9341);
|
||||
if (this->width_ == 0)
|
||||
this->width_ = 240;
|
||||
if (this->height_ == 0)
|
||||
this->height_ = 320;
|
||||
}
|
||||
// 24_TFT rotated display
|
||||
void ILI9XXXILI9342::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9341);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 320;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 240;
|
||||
}
|
||||
}
|
||||
|
||||
// 35_TFT display
|
||||
void ILI9XXXILI9481::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9481);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 480;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 320;
|
||||
}
|
||||
}
|
||||
|
||||
void ILI9XXXILI948118::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9481_18);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 320;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 480;
|
||||
}
|
||||
this->is_18bitdisplay_ = true;
|
||||
}
|
||||
|
||||
// 35_TFT display
|
||||
void ILI9XXXILI9486::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9486);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 480;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 320;
|
||||
}
|
||||
}
|
||||
// 40_TFT display
|
||||
void ILI9XXXILI9488::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9488);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 480;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 320;
|
||||
}
|
||||
this->is_18bitdisplay_ = true;
|
||||
}
|
||||
// 40_TFT display
|
||||
void ILI9XXXILI9488A::initialize() {
|
||||
this->init_lcd_(INITCMD_ILI9488_A);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 480;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 320;
|
||||
}
|
||||
this->is_18bitdisplay_ = true;
|
||||
}
|
||||
// 40_TFT display
|
||||
void ILI9XXXST7796::initialize() {
|
||||
this->init_lcd_(INITCMD_ST7796);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 320;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 480;
|
||||
}
|
||||
}
|
||||
|
||||
// 24_TFT rotated display
|
||||
void ILI9XXXS3Box::initialize() {
|
||||
this->init_lcd_(INITCMD_S3BOX);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 320;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 240;
|
||||
}
|
||||
}
|
||||
|
||||
// 24_TFT rotated display
|
||||
void ILI9XXXS3BoxLite::initialize() {
|
||||
this->init_lcd_(INITCMD_S3BOXLITE);
|
||||
if (this->width_ == 0) {
|
||||
this->width_ = 320;
|
||||
}
|
||||
if (this->height_ == 0) {
|
||||
this->height_ = 240;
|
||||
}
|
||||
this->pre_invertdisplay_ = true;
|
||||
}
|
||||
|
||||
} // namespace ili9xxx
|
||||
} // namespace esphome
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
namespace esphome {
|
||||
namespace ili9xxx {
|
||||
|
||||
const uint32_t ILI9XXX_TRANSFER_BUFFER_SIZE = 64;
|
||||
const size_t ILI9XXX_TRANSFER_BUFFER_SIZE = 126; // ensure this is divisible by 6
|
||||
|
||||
enum ILI9XXXColorMode {
|
||||
BITS_8 = 0x08,
|
||||
|
@ -23,20 +23,47 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
|||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, ILI9XXXDisplay_DATA_RATE> {
|
||||
public:
|
||||
ILI9XXXDisplay() = default;
|
||||
ILI9XXXDisplay(uint8_t const *init_sequence, int16_t width, int16_t height, bool invert_colors)
|
||||
: init_sequence_{init_sequence}, width_{width}, height_{height}, pre_invertcolors_{invert_colors} {
|
||||
uint8_t cmd, num_args, bits;
|
||||
const uint8_t *addr = init_sequence;
|
||||
while ((cmd = *addr++) != 0) {
|
||||
num_args = *addr++ & 0x7F;
|
||||
if (cmd == ILI9XXX_MADCTL) {
|
||||
bits = *addr;
|
||||
this->swap_xy_ = (bits & MADCTL_MV) != 0;
|
||||
this->mirror_x_ = (bits & MADCTL_MX) != 0;
|
||||
this->mirror_y_ = (bits & MADCTL_MY) != 0;
|
||||
this->color_order_ = (bits & MADCTL_BGR) ? display::COLOR_ORDER_BGR : display::COLOR_ORDER_RGB;
|
||||
break;
|
||||
}
|
||||
addr += num_args;
|
||||
}
|
||||
}
|
||||
|
||||
void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; }
|
||||
float get_setup_priority() const override;
|
||||
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
|
||||
void set_palette(const uint8_t *palette) { this->palette_ = palette; }
|
||||
void set_buffer_color_mode(ILI9XXXColorMode color_mode) { this->buffer_color_mode_ = color_mode; }
|
||||
void set_dimentions(int16_t width, int16_t height) {
|
||||
void set_dimensions(int16_t width, int16_t height) {
|
||||
this->height_ = height;
|
||||
this->width_ = width;
|
||||
}
|
||||
void invert_display(bool invert);
|
||||
void set_offsets(int16_t offset_x, int16_t offset_y) {
|
||||
this->offset_x_ = offset_x;
|
||||
this->offset_y_ = offset_y;
|
||||
}
|
||||
void invert_colors(bool invert);
|
||||
void command(uint8_t value);
|
||||
void data(uint8_t value);
|
||||
void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes);
|
||||
uint8_t read_command(uint8_t command_byte, uint8_t index);
|
||||
void set_color_order(display::ColorOrder color_order) { this->color_order_ = color_order; }
|
||||
void set_swap_xy(bool swap_xy) { this->swap_xy_ = swap_xy; }
|
||||
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 update() override;
|
||||
|
||||
|
@ -50,16 +77,17 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
|||
protected:
|
||||
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||
void setup_pins_();
|
||||
virtual void initialize() = 0;
|
||||
|
||||
void display_();
|
||||
void init_lcd_(const uint8_t *init_cmd);
|
||||
void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
||||
|
||||
void init_lcd_();
|
||||
void set_addr_window_(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2);
|
||||
void reset_();
|
||||
|
||||
uint8_t const *init_sequence_{};
|
||||
int16_t width_{0}; ///< Display width as modified by current rotation
|
||||
int16_t height_{0}; ///< Display height as modified by current rotation
|
||||
int16_t offset_x_{0};
|
||||
int16_t offset_y_{0};
|
||||
uint16_t x_low_{0};
|
||||
uint16_t y_low_{0};
|
||||
uint16_t x_high_{0};
|
||||
|
@ -77,10 +105,6 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
|||
void start_data_();
|
||||
void end_data_();
|
||||
|
||||
uint16_t transfer_buffer_[ILI9XXX_TRANSFER_BUFFER_SIZE];
|
||||
|
||||
uint32_t buffer_to_transfer_(uint32_t pos, uint32_t sz);
|
||||
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
GPIOPin *dc_pin_{nullptr};
|
||||
GPIOPin *busy_pin_{nullptr};
|
||||
|
@ -88,77 +112,87 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
|||
bool prossing_update_ = false;
|
||||
bool need_update_ = false;
|
||||
bool is_18bitdisplay_ = false;
|
||||
bool pre_invertdisplay_ = false;
|
||||
bool pre_invertcolors_ = false;
|
||||
display::ColorOrder color_order_{};
|
||||
bool swap_xy_{};
|
||||
bool mirror_x_{};
|
||||
bool mirror_y_{};
|
||||
};
|
||||
|
||||
//----------- M5Stack display --------------
|
||||
class ILI9XXXM5Stack : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXM5Stack() : ILI9XXXDisplay(INITCMD_M5STACK, 320, 240, true) {}
|
||||
};
|
||||
|
||||
//----------- M5Stack display --------------
|
||||
class ILI9XXXM5CORE : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXM5CORE() : ILI9XXXDisplay(INITCMD_M5CORE, 320, 240, true) {}
|
||||
};
|
||||
|
||||
//----------- ST7789V display --------------
|
||||
class ILI9XXXST7789V : public ILI9XXXDisplay {
|
||||
public:
|
||||
ILI9XXXST7789V() : ILI9XXXDisplay(INITCMD_ST7789V, 240, 320, false) {}
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_24_TFT display --------------
|
||||
class ILI9XXXILI9341 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXILI9341() : ILI9XXXDisplay(INITCMD_ILI9341, 240, 320, false) {}
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_24_TFT rotated display --------------
|
||||
class ILI9XXXILI9342 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXILI9342() : ILI9XXXDisplay(INITCMD_ILI9341, 320, 240, false) {}
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_??_TFT rotated display --------------
|
||||
class ILI9XXXILI9481 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXILI9481() : ILI9XXXDisplay(INITCMD_ILI9481, 480, 320, false) {}
|
||||
};
|
||||
|
||||
//----------- ILI9481 in 18 bit mode --------------
|
||||
class ILI9XXXILI948118 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXILI948118() : ILI9XXXDisplay(INITCMD_ILI9481_18, 320, 480, true) {}
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_35_TFT rotated display --------------
|
||||
class ILI9XXXILI9486 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXILI9486() : ILI9XXXDisplay(INITCMD_ILI9486, 480, 320, false) {}
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_35_TFT rotated display --------------
|
||||
class ILI9XXXILI9488 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXILI9488() : ILI9XXXDisplay(INITCMD_ILI9488, 480, 320, true) {}
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_35_TFT origin colors rotated display --------------
|
||||
class ILI9XXXILI9488A : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXILI9488A() : ILI9XXXDisplay(INITCMD_ILI9488_A, 480, 320, true) {}
|
||||
};
|
||||
|
||||
//----------- ILI9XXX_35_TFT rotated display --------------
|
||||
class ILI9XXXST7796 : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXST7796() : ILI9XXXDisplay(INITCMD_ST7796, 320, 480, false) {}
|
||||
};
|
||||
|
||||
class ILI9XXXS3Box : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXS3Box() : ILI9XXXDisplay(INITCMD_S3BOX, 320, 240, false) {}
|
||||
};
|
||||
|
||||
class ILI9XXXS3BoxLite : public ILI9XXXDisplay {
|
||||
protected:
|
||||
void initialize() override;
|
||||
public:
|
||||
ILI9XXXS3BoxLite() : ILI9XXXDisplay(INITCMD_S3BOXLITE, 320, 240, true) {}
|
||||
};
|
||||
|
||||
} // namespace ili9xxx
|
||||
|
|
|
@ -289,6 +289,33 @@ static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = {
|
|||
0x00 // End of list
|
||||
};
|
||||
|
||||
static const uint8_t PROGMEM INITCMD_ST7789V[] = {
|
||||
ILI9XXX_SLPOUT , 0x80, // Exit Sleep
|
||||
ILI9XXX_DISPON , 0x80, // Display on
|
||||
ILI9XXX_MADCTL , 1, 0x08, // Memory Access Control, BGR
|
||||
ILI9XXX_DFUNCTR, 2, 0x0A, 0x82,
|
||||
ILI9XXX_PIXFMT , 1, 0x55,
|
||||
ILI9XXX_FRMCTR2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33,
|
||||
ILI9XXX_ETMOD, 1, 0x35, 0xBB, 1, 0x28,
|
||||
ILI9XXX_PWCTR1 , 1, 0x0C, // Power control VRH[5:0]
|
||||
ILI9XXX_PWCTR3 , 2, 0x01, 0xFF,
|
||||
ILI9XXX_PWCTR4 , 1, 0x10,
|
||||
ILI9XXX_PWCTR5 , 1, 0x20,
|
||||
ILI9XXX_IFCTR , 1, 0x0F,
|
||||
ILI9XXX_PWSET, 2, 0xA4, 0xA1,
|
||||
ILI9XXX_GMCTRP1 , 14,
|
||||
0xd0, 0x00, 0x02, 0x07, 0x0a,
|
||||
0x28, 0x32, 0x44, 0x42, 0x06, 0x0e,
|
||||
0x12, 0x14, 0x17,
|
||||
ILI9XXX_GMCTRN1 , 14,
|
||||
0xd0, 0x00, 0x02, 0x07, 0x0a,
|
||||
0x28, 0x31, 0x54, 0x47,
|
||||
0x0e, 0x1c, 0x17, 0x1b,
|
||||
0x1e,
|
||||
ILI9XXX_DISPON , 0x80, // Display on
|
||||
0x00 // End of list
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace ili9xxx
|
||||
} // namespace esphome
|
||||
|
|
|
@ -1554,6 +1554,8 @@ sensor:
|
|||
memory_address: 0x7d
|
||||
name: Adres sensor
|
||||
|
||||
psram:
|
||||
|
||||
esp32_touch:
|
||||
setup_mode: false
|
||||
iir_filter: 10ms
|
||||
|
@ -2992,6 +2994,12 @@ display:
|
|||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: ili9xxx
|
||||
invert_colors: true
|
||||
dimensions: 320x240
|
||||
transform:
|
||||
swap_xy: true
|
||||
mirror_x: true
|
||||
mirror_y: false
|
||||
model: TFT 2.4
|
||||
cs_pin: GPIO5
|
||||
dc_pin: GPIO4
|
||||
|
@ -3000,6 +3008,11 @@ display:
|
|||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: ili9xxx
|
||||
dimensions:
|
||||
width: 320
|
||||
height: 240
|
||||
offset_width: 20
|
||||
offset_height: 10
|
||||
model: TFT 2.4
|
||||
cs_pin: GPIO5
|
||||
dc_pin: GPIO4
|
||||
|
|
Loading…
Reference in a new issue