diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index b3fe8b2b41..0bd810ea16 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -66,6 +66,7 @@ MODELS = { "ST7789V": ili9xxx_ns.class_("ILI9XXXST7789V", ILI9XXXDisplay), "S3BOX": ili9xxx_ns.class_("ILI9XXXS3Box", ILI9XXXDisplay), "S3BOX_LITE": ili9xxx_ns.class_("ILI9XXXS3BoxLite", ILI9XXXDisplay), + "WAVESHARE_RES_3_5": ili9xxx_ns.class_("WAVESHARERES35", ILI9XXXDisplay), } COLOR_ORDERS = { diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index ab577b3875..e3f2c94880 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -7,7 +7,6 @@ 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 @@ -17,13 +16,7 @@ static inline void put16_be(uint8_t *buf, uint16_t value) { buf[1] = value; } -void ILI9XXXDisplay::setup() { - ESP_LOGD(TAG, "Setting up ILI9xxx"); - - this->setup_pins_(); - this->init_lcd_(); - - this->command(this->pre_invertcolors_ ? ILI9XXX_INVON : ILI9XXX_INVOFF); +void ILI9XXXDisplay::set_madctl() { // 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_) @@ -32,8 +25,19 @@ void ILI9XXXDisplay::setup() { mad |= MADCTL_MX; if (this->mirror_y_) mad |= MADCTL_MY; - this->send_command(ILI9XXX_MADCTL, &mad, 1); + this->command(ILI9XXX_MADCTL); + this->data(mad); + esph_log_d(TAG, "Wrote MADCTL 0x%02X", mad); +} +void ILI9XXXDisplay::setup() { + ESP_LOGD(TAG, "Setting up ILI9xxx"); + + this->setup_pins_(); + this->init_lcd_(); + + this->set_madctl(); + this->command(this->pre_invertcolors_ ? ILI9XXX_INVON : ILI9XXX_INVOFF); this->x_low_ = this->width_; this->y_low_ = this->height_; this->x_high_ = 0; @@ -89,6 +93,7 @@ void ILI9XXXDisplay::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_); + ESP_LOGCONFIG(TAG, " Color order: %s", this->color_order_ == display::COLOR_ORDER_BGR ? "BGR" : "RGB"); 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_)); @@ -196,7 +201,6 @@ void ILI9XXXDisplay::display_() { 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; } @@ -211,14 +215,13 @@ void ILI9XXXDisplay::display_() { 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, " - "height:%d, mode=%d, 18bit=%d, sw_time=%dus, mw_time=%dus)", + "height:%zu, mode=%d, 18bit=%d, sw_time=%zuus, mw_time=%zuus)", 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); + ESP_LOGV(TAG, "Doing single write of %zu 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 { @@ -267,7 +270,7 @@ void ILI9XXXDisplay::display_() { this->write_array(transfer_buffer, idx); } } - this->disable(); + this->end_data_(); ESP_LOGV(TAG, "Data write took %dms", (unsigned) (millis() - now)); // invalidate watermarks this->x_low_ = this->width_; @@ -290,7 +293,6 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); } - this->enable(); this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); // 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) { @@ -302,7 +304,7 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons this->write_array(ptr + (y + y_offset) * stride + x_offset, w * 2); } } - this->disable(); + this->end_data_(); } // should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color @@ -328,20 +330,6 @@ void ILI9XXXDisplay::send_command(uint8_t command_byte, const uint8_t *data_byte this->end_data_(); } -uint8_t ILI9XXXDisplay::read_command(uint8_t command_byte, uint8_t index) { - uint8_t data = 0x10 + index; - this->send_command(0xD9, &data, 1); // Set Index Register - uint8_t result; - this->start_command_(); - this->write_byte(command_byte); - this->start_data_(); - do { - result = this->read_byte(); - } while (index--); - this->end_data_(); - return result; -} - void ILI9XXXDisplay::start_command_() { this->dc_pin_->digital_write(false); this->enable(); @@ -357,9 +345,9 @@ void ILI9XXXDisplay::end_data_() { this->disable(); } void ILI9XXXDisplay::reset_() { if (this->reset_pin_ != nullptr) { this->reset_pin_->digital_write(false); - delay(10); + delay(20); this->reset_pin_->digital_write(true); - delay(10); + delay(20); } } @@ -369,7 +357,7 @@ void ILI9XXXDisplay::init_lcd_() { while ((cmd = *addr++) > 0) { x = *addr++; num_args = x & 0x7F; - send_command(cmd, addr, num_args); + this->send_command(cmd, addr, num_args); addr += num_args; if (x & 0x80) delay(150); // NOLINT @@ -377,24 +365,19 @@ void ILI9XXXDisplay::init_lcd_() { } // 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); + this->command(ILI9XXX_CASET); + this->data(x1 >> 8); + this->data(x1 & 0xFF); + this->data(x2 >> 8); + this->data(x2 & 0xFF); + this->command(ILI9XXX_PASET); // Page address set + this->data(y1 >> 8); + this->data(y1 & 0xFF); + this->data(y2 >> 8); + this->data(y2 & 0xFF); + this->command(ILI9XXX_RAMWR); // Write to RAM + this->start_data_(); } void ILI9XXXDisplay::invert_colors(bool invert) { diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 590be3e364..7b92bd2336 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -8,6 +8,7 @@ namespace esphome { namespace ili9xxx { +static const char *const TAG = "ili9xxx"; const size_t ILI9XXX_TRANSFER_BUFFER_SIZE = 126; // ensure this is divisible by 6 enum ILI9XXXColorMode { @@ -32,6 +33,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, while ((cmd = *addr++) != 0) { num_args = *addr++ & 0x7F; bits = *addr; + esph_log_d(TAG, "Command %02X, length %d, bits %02X", cmd, num_args, bits); switch (cmd) { case ILI9XXX_MADCTL: { this->swap_xy_ = (bits & MADCTL_MV) != 0; @@ -68,10 +70,9 @@ class ILI9XXXDisplay : public display::DisplayBuffer, this->offset_y_ = offset_y; } void invert_colors(bool invert); - void command(uint8_t value); - void data(uint8_t value); + virtual void command(uint8_t value); + virtual 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; } @@ -92,6 +93,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, void draw_absolute_pixel_internal(int x, int y, Color color) override; void setup_pins_(); + virtual void set_madctl(); void display_(); void init_lcd_(); void set_addr_window_(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2); @@ -127,7 +129,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, bool need_update_ = false; bool is_18bitdisplay_ = false; bool pre_invertcolors_ = false; - display::ColorOrder color_order_{}; + display::ColorOrder color_order_{display::COLOR_ORDER_BGR}; bool swap_xy_{}; bool mirror_x_{}; bool mirror_y_{}; @@ -181,10 +183,48 @@ class ILI9XXXILI9486 : public ILI9XXXDisplay { ILI9XXXILI9486() : ILI9XXXDisplay(INITCMD_ILI9486, 480, 320, false) {} }; -//----------- ILI9XXX_35_TFT rotated display -------------- class ILI9XXXILI9488 : public ILI9XXXDisplay { public: - ILI9XXXILI9488() : ILI9XXXDisplay(INITCMD_ILI9488, 480, 320, true) {} + ILI9XXXILI9488(const uint8_t *seq = INITCMD_ILI9488) : ILI9XXXDisplay(seq, 480, 320, true) {} + + protected: + void set_madctl() override { + uint8_t mad = this->color_order_ == display::COLOR_ORDER_BGR ? MADCTL_BGR : MADCTL_RGB; + uint8_t dfun = 0x22; + this->width_ = 320; + this->height_ = 480; + if (!(this->swap_xy_ || this->mirror_x_ || this->mirror_y_)) { + // no transforms + } else if (this->mirror_y_ && this->mirror_x_) { + // rotate 180 + dfun = 0x42; + } else if (this->swap_xy_) { + this->width_ = 480; + this->height_ = 320; + mad |= 0x20; + if (this->mirror_x_) { + dfun = 0x02; + } else { + dfun = 0x62; + } + } + this->command(ILI9XXX_DFUNCTR); + this->data(0); + this->data(dfun); + this->command(ILI9XXX_MADCTL); + this->data(mad); + } +}; +//----------- Waveshare 3.5 Res Touch - ILI9488 interfaced via 16 bit shift register to parallel */ +class WAVESHARERES35 : public ILI9XXXILI9488 { + public: + WAVESHARERES35() : ILI9XXXILI9488(INITCMD_WAVESHARE_RES_3_5) {} + void data(uint8_t value) override { + this->start_data_(); + this->write_byte(0); + this->write_byte(value); + this->end_data_(); + } }; //----------- ILI9XXX_35_TFT origin colors rotated display -------------- diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index a74824052f..fe3f168c32 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -141,7 +141,8 @@ static const uint8_t PROGMEM INITCMD_ILI9486[] = { 0x00 // End of list }; -static const uint8_t PROGMEM INITCMD_ILI9488[] = { + +static const uint8_t INITCMD_ILI9488[] = { ILI9XXX_GMCTRP1,15, 0x0f, 0x24, 0x1c, 0x0a, 0x0f, 0x08, 0x43, 0x88, 0x32, 0x0f, 0x10, 0x06, 0x0f, 0x07, 0x00, ILI9XXX_GMCTRN1,15, 0x0F, 0x38, 0x30, 0x09, 0x0f, 0x0f, 0x4e, 0x77, 0x3c, 0x07, 0x10, 0x05, 0x23, 0x1b, 0x00, @@ -153,28 +154,27 @@ static const uint8_t PROGMEM INITCMD_ILI9488[] = { ILI9XXX_FRMCTR1, 1, 0xA0, // Frame rate = 60Hz ILI9XXX_INVCTR, 1, 0x02, // Display Inversion Control = 2dot - ILI9XXX_DFUNCTR, 2, 0x02, 0x02, // Nomal scan - 0xE9, 1, 0x00, // Set Image Functio. Disable 24 bit data ILI9XXX_ADJCTL3, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control 3 - - ILI9XXX_MADCTL, 1, 0x28, - //ILI9XXX_PIXFMT, 1, 0x55, // Interface Pixel Format = 16bit ILI9XXX_PIXFMT, 1, 0x66, //ILI9488 only supports 18-bit pixel format in 4/3 wire SPI mode - - - - // 5 frames - //ILI9XXX_ETMOD, 1, 0xC6, // - - ILI9XXX_SLPOUT, 0x80, // Exit sleep mode - //ILI9XXX_INVON , 0, ILI9XXX_DISPON, 0x80, // Set display on 0x00 // end }; +static const uint8_t INITCMD_WAVESHARE_RES_3_5[] = { + ILI9XXX_PWCTR3, 1, 0x33, + ILI9XXX_VMCTR1, 3, 0x00, 0x1e, 0x80, + ILI9XXX_FRMCTR1, 1, 0xA0, + ILI9XXX_GMCTRP1, 15, 0x0, 0x13, 0x18, 0x04, 0x0F, 0x06, 0x3a, 0x56, 0x4d, 0x03, 0x0a, 0x06, 0x30, 0x3e, 0x0f, + ILI9XXX_GMCTRN1, 15, 0x0, 0x13, 0x18, 0x01, 0x11, 0x06, 0x38, 0x34, 0x4d, 0x06, 0x0d, 0x0b, 0x31, 0x37, 0x0f, + ILI9XXX_PIXFMT, 1, 0x55, + ILI9XXX_SLPOUT, 0x80, // slpout, delay + ILI9XXX_DISPON, 0, + 0x00 // End of list +}; + static const uint8_t PROGMEM INITCMD_ILI9488_A[] = { ILI9XXX_GMCTRP1,15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F, ILI9XXX_GMCTRN1,15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F,