Skip to content

Commit

Permalink
Add support for Pico-ResTouch-LCD-3.5 to ili9xxx driver (esphome#6129)
Browse files Browse the repository at this point in the history
* Working version of Waveshare 3.5 Res Touch driver.

* Default color order BGR
  • Loading branch information
clydebarrow authored Jan 23, 2024
1 parent 4812997 commit 23071e9
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 70 deletions.
1 change: 1 addition & 0 deletions esphome/components/ili9xxx/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def AUTO_LOAD():
"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 = {
Expand Down
83 changes: 33 additions & 50 deletions esphome/components/ili9xxx/ili9xxx_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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_)
Expand All @@ -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;
Expand Down Expand Up @@ -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_));
Expand Down Expand Up @@ -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;
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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_;
Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -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();
Expand All @@ -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);
}
}

Expand All @@ -369,32 +357,27 @@ 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
}
}

// 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) {
Expand Down
52 changes: 46 additions & 6 deletions esphome/components/ili9xxx/ili9xxx_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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; }
Expand All @@ -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);
Expand Down Expand Up @@ -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_{};
Expand Down Expand Up @@ -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 --------------
Expand Down
28 changes: 14 additions & 14 deletions esphome/components/ili9xxx/ili9xxx_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand All @@ -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,
Expand Down

0 comments on commit 23071e9

Please sign in to comment.