forked from esphome/esphome
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* setup ili9341 framework used epaper-waveshare as start * first version working * added models for now only M5Stack * get_buffer_length is huge * fill, low/high watermark, buffer tests failed. RAM is to small for ili9341 16 bit color mode * removed high/low watermark debug log * added standard 2.4" TFT model * code cleanup * make ledpin optional busy pin is not needed * make bufer 1 byte to avoid the buffer allocation error * gitignore * added backlight pin to dump_config * huge speed increase 8bit color framebuffer (256 colors) lo and high watermark for drawing to screen * fix for images * higher spi data rates * Set spi data rate to 40Mhz Experimental * fixed: formatting fixed: the last row and column being trimmed fixed: namings * Update the code to use Color class * fixed minor color things * fixed linting * #patch minor fixes * fix gitignore too * Update esphome/components/ili9341/ili9341_display.cpp Co-authored-by: Oleg <[email protected]> * reverting the changes as it's being fixed in PR-1241 Co-authored-by: Michiel van Turnhout <[email protected]> Co-authored-by: Michiel van Turnhout <[email protected]> Co-authored-by: Oleg <[email protected]>
- Loading branch information
1 parent
fc01a70
commit 9816e67
Showing
8 changed files
with
549 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,3 +119,4 @@ config/ | |
tests/build/ | ||
tests/.esphome/ | ||
/.temp-clang-tidy.cpp | ||
.pio/ |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import esphome.codegen as cg | ||
import esphome.config_validation as cv | ||
from esphome import pins | ||
from esphome.components import display, spi | ||
from esphome.const import CONF_DC_PIN, \ | ||
CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, CONF_RESET_PIN | ||
|
||
DEPENDENCIES = ['spi'] | ||
|
||
CONF_LED_PIN = 'led_pin' | ||
|
||
ili9341_ns = cg.esphome_ns.namespace('ili9341') | ||
ili9341 = ili9341_ns.class_('ILI9341Display', cg.PollingComponent, spi.SPIDevice, | ||
display.DisplayBuffer) | ||
ILI9341M5Stack = ili9341_ns.class_('ILI9341M5Stack', ili9341) | ||
ILI9341TFT24 = ili9341_ns.class_('ILI9341TFT24', ili9341) | ||
|
||
ILI9341Model = ili9341_ns.enum('ILI9341Model') | ||
|
||
MODELS = { | ||
'M5STACK': ILI9341Model.M5STACK, | ||
'TFT_2.4': ILI9341Model.TFT_24, | ||
} | ||
|
||
ILI9341_MODEL = cv.enum(MODELS, upper=True, space="_") | ||
|
||
CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({ | ||
cv.GenerateID(): cv.declare_id(ili9341), | ||
cv.Required(CONF_MODEL): ILI9341_MODEL, | ||
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): pins.gpio_output_pin_schema, | ||
}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()), | ||
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) | ||
|
||
|
||
def to_code(config): | ||
if config[CONF_MODEL] == 'M5STACK': | ||
lcd_type = ILI9341M5Stack | ||
if config[CONF_MODEL] == 'TFT_2.4': | ||
lcd_type = ILI9341TFT24 | ||
rhs = lcd_type.new() | ||
var = cg.Pvariable(config[CONF_ID], rhs) | ||
|
||
yield cg.register_component(var, config) | ||
yield display.register_display(var, config) | ||
yield spi.register_spi_device(var, config) | ||
cg.add(var.set_model(config[CONF_MODEL])) | ||
dc = yield cg.gpio_pin_expression(config[CONF_DC_PIN]) | ||
cg.add(var.set_dc_pin(dc)) | ||
|
||
if CONF_LAMBDA in config: | ||
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], | ||
return_type=cg.void) | ||
cg.add(var.set_writer(lambda_)) | ||
if CONF_RESET_PIN in config: | ||
reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) | ||
cg.add(var.set_reset_pin(reset)) | ||
if CONF_LED_PIN in config: | ||
led_pin = yield cg.gpio_pin_expression(config[CONF_LED_PIN]) | ||
cg.add(var.set_led_pin(led_pin)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#pragma once | ||
|
||
namespace esphome { | ||
namespace ili9341 { | ||
|
||
// Color definitions | ||
// clang-format off | ||
static const uint8_t MADCTL_MY = 0x80; ///< Bit 7 Bottom to top | ||
static const uint8_t MADCTL_MX = 0x40; ///< Bit 6 Right to left | ||
static const uint8_t MADCTL_MV = 0x20; ///< Bit 5 Reverse Mode | ||
static const uint8_t MADCTL_ML = 0x10; ///< Bit 4 LCD refresh Bottom to top | ||
static const uint8_t MADCTL_RGB = 0x00; ///< Bit 3 Red-Green-Blue pixel order | ||
static const uint8_t MADCTL_BGR = 0x08; ///< Bit 3 Blue-Green-Red pixel order | ||
static const uint8_t MADCTL_MH = 0x04; ///< Bit 2 LCD refresh right to left | ||
// clang-format on | ||
|
||
static const uint16_t ILI9341_TFTWIDTH = 320; ///< ILI9341 max TFT width | ||
static const uint16_t ILI9341_TFTHEIGHT = 240; ///< ILI9341 max TFT height | ||
|
||
// All ILI9341 specific commands some are used by init() | ||
static const uint8_t ILI9341_NOP = 0x00; | ||
static const uint8_t ILI9341_SWRESET = 0x01; | ||
static const uint8_t ILI9341_RDDID = 0x04; | ||
static const uint8_t ILI9341_RDDST = 0x09; | ||
|
||
static const uint8_t ILI9341_SLPIN = 0x10; | ||
static const uint8_t ILI9341_SLPOUT = 0x11; | ||
static const uint8_t ILI9341_PTLON = 0x12; | ||
static const uint8_t ILI9341_NORON = 0x13; | ||
|
||
static const uint8_t ILI9341_RDMODE = 0x0A; | ||
static const uint8_t ILI9341_RDMADCTL = 0x0B; | ||
static const uint8_t ILI9341_RDPIXFMT = 0x0C; | ||
static const uint8_t ILI9341_RDIMGFMT = 0x0A; | ||
static const uint8_t ILI9341_RDSELFDIAG = 0x0F; | ||
|
||
static const uint8_t ILI9341_INVOFF = 0x20; | ||
static const uint8_t ILI9341_INVON = 0x21; | ||
static const uint8_t ILI9341_GAMMASET = 0x26; | ||
static const uint8_t ILI9341_DISPOFF = 0x28; | ||
static const uint8_t ILI9341_DISPON = 0x29; | ||
|
||
static const uint8_t ILI9341_CASET = 0x2A; | ||
static const uint8_t ILI9341_PASET = 0x2B; | ||
static const uint8_t ILI9341_RAMWR = 0x2C; | ||
static const uint8_t ILI9341_RAMRD = 0x2E; | ||
|
||
static const uint8_t ILI9341_PTLAR = 0x30; | ||
static const uint8_t ILI9341_VSCRDEF = 0x33; | ||
static const uint8_t ILI9341_MADCTL = 0x36; | ||
static const uint8_t ILI9341_VSCRSADD = 0x37; | ||
static const uint8_t ILI9341_PIXFMT = 0x3A; | ||
|
||
static const uint8_t ILI9341_WRDISBV = 0x51; | ||
static const uint8_t ILI9341_RDDISBV = 0x52; | ||
static const uint8_t ILI9341_WRCTRLD = 0x53; | ||
|
||
static const uint8_t ILI9341_FRMCTR1 = 0xB1; | ||
static const uint8_t ILI9341_FRMCTR2 = 0xB2; | ||
static const uint8_t ILI9341_FRMCTR3 = 0xB3; | ||
static const uint8_t ILI9341_INVCTR = 0xB4; | ||
static const uint8_t ILI9341_DFUNCTR = 0xB6; | ||
|
||
static const uint8_t ILI9341_PWCTR1 = 0xC0; | ||
static const uint8_t ILI9341_PWCTR2 = 0xC1; | ||
static const uint8_t ILI9341_PWCTR3 = 0xC2; | ||
static const uint8_t ILI9341_PWCTR4 = 0xC3; | ||
static const uint8_t ILI9341_PWCTR5 = 0xC4; | ||
static const uint8_t ILI9341_VMCTR1 = 0xC5; | ||
static const uint8_t ILI9341_VMCTR2 = 0xC7; | ||
|
||
static const uint8_t ILI9341_RDID4 = 0xD3; | ||
static const uint8_t ILI9341_RDINDEX = 0xD9; | ||
static const uint8_t ILI9341_RDID1 = 0xDA; | ||
static const uint8_t ILI9341_RDID2 = 0xDB; | ||
static const uint8_t ILI9341_RDID3 = 0xDC; | ||
static const uint8_t ILI9341_RDIDX = 0xDD; // TBC | ||
|
||
static const uint8_t ILI9341_GMCTRP1 = 0xE0; | ||
static const uint8_t ILI9341_GMCTRN1 = 0xE1; | ||
|
||
} // namespace ili9341 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
#include "ili9341_display.h" | ||
#include "esphome/core/log.h" | ||
#include "esphome/core/application.h" | ||
#include "esphome/core/helpers.h" | ||
|
||
namespace esphome { | ||
namespace ili9341 { | ||
|
||
static const char *TAG = "ili9341"; | ||
|
||
void ILI9341Display::setup_pins_() { | ||
this->init_internal_(this->get_buffer_length_()); | ||
this->dc_pin_->setup(); // OUTPUT | ||
this->dc_pin_->digital_write(false); | ||
if (this->reset_pin_ != nullptr) { | ||
this->reset_pin_->setup(); // OUTPUT | ||
this->reset_pin_->digital_write(true); | ||
} | ||
if (this->led_pin_ != nullptr) { | ||
this->led_pin_->setup(); | ||
this->led_pin_->digital_write(true); | ||
} | ||
this->spi_setup(); | ||
|
||
this->reset_(); | ||
} | ||
|
||
void ILI9341Display::dump_config() { | ||
LOG_DISPLAY("", "ili9341", this); | ||
ESP_LOGCONFIG(TAG, " Width: %d, Height: %d, Rotation: %d", this->width_, this->height_, this->rotation_); | ||
LOG_PIN(" Reset Pin: ", this->reset_pin_); | ||
LOG_PIN(" DC Pin: ", this->dc_pin_); | ||
LOG_PIN(" Busy Pin: ", this->busy_pin_); | ||
LOG_PIN(" Backlight Pin: ", this->led_pin_); | ||
LOG_UPDATE_INTERVAL(this); | ||
} | ||
|
||
float ILI9341Display::get_setup_priority() const { return setup_priority::PROCESSOR; } | ||
void ILI9341Display::command(uint8_t value) { | ||
this->start_command_(); | ||
this->write_byte(value); | ||
this->end_command_(); | ||
} | ||
|
||
void ILI9341Display::reset_() { | ||
if (this->reset_pin_ != nullptr) { | ||
this->reset_pin_->digital_write(false); | ||
delay(10); | ||
this->reset_pin_->digital_write(true); | ||
delay(10); | ||
} | ||
} | ||
|
||
void ILI9341Display::data(uint8_t value) { | ||
this->start_data_(); | ||
this->write_byte(value); | ||
this->end_data_(); | ||
} | ||
|
||
void ILI9341Display::send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes) { | ||
this->command(command_byte); // Send the command byte | ||
this->start_data_(); | ||
this->write_array(data_bytes, num_data_bytes); | ||
this->end_data_(); | ||
} | ||
|
||
uint8_t ILI9341Display::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 ILI9341Display::update() { | ||
this->do_update_(); | ||
this->display_(); | ||
} | ||
|
||
void ILI9341Display::display_() { | ||
// we will only update the changed window to the display | ||
int w = this->x_high_ - this->x_low_ + 1; | ||
int h = this->y_high_ - this->y_low_ + 1; | ||
|
||
set_addr_window_(this->x_low_, this->y_low_, w, h); | ||
this->start_data_(); | ||
uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_); | ||
for (uint16_t row = 0; row < h; row++) { | ||
for (uint16_t col = 0; col < w; col++) { | ||
uint32_t pos = start_pos + (row * width_) + col; | ||
|
||
uint16_t color = convert_to_16bit_color_(buffer_[pos]); | ||
this->write_byte(color >> 8); | ||
this->write_byte(color); | ||
} | ||
} | ||
this->end_data_(); | ||
|
||
// invalidate watermarks | ||
this->x_low_ = this->width_; | ||
this->y_low_ = this->height_; | ||
this->x_high_ = 0; | ||
this->y_high_ = 0; | ||
} | ||
|
||
uint16_t ILI9341Display::convert_to_16bit_color_(uint8_t color_8bit) { | ||
int r = color_8bit >> 5; | ||
int g = (color_8bit >> 2) & 0x07; | ||
int b = color_8bit & 0x03; | ||
uint16_t color = (r * 0x04) << 11; | ||
color |= (g * 0x09) << 5; | ||
color |= (b * 0x0A); | ||
|
||
return color; | ||
} | ||
|
||
uint8_t ILI9341Display::convert_to_8bit_color_(uint16_t color_16bit) { | ||
// convert 16bit color to 8 bit buffer | ||
uint8_t r = color_16bit >> 11; | ||
uint8_t g = (color_16bit >> 5) & 0x3F; | ||
uint8_t b = color_16bit & 0x1F; | ||
|
||
return ((b / 0x0A) | ((g / 0x09) << 2) | ((r / 0x04) << 5)); | ||
} | ||
|
||
void ILI9341Display::fill(Color color) { | ||
auto color565 = color.to_rgb_565(); | ||
memset(this->buffer_, convert_to_8bit_color_(color565), this->get_buffer_length_()); | ||
this->x_low_ = 0; | ||
this->y_low_ = 0; | ||
this->x_high_ = this->get_width_internal() - 1; | ||
this->y_high_ = this->get_height_internal() - 1; | ||
} | ||
|
||
void ILI9341Display::fill_internal_(Color color) { | ||
this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal()); | ||
this->start_data_(); | ||
|
||
auto color565 = color.to_rgb_565(); | ||
for (uint32_t i = 0; i < (this->get_width_internal()) * (this->get_height_internal()); i++) { | ||
this->write_byte(color565 >> 8); | ||
this->write_byte(color565); | ||
buffer_[i] = 0; | ||
} | ||
this->end_data_(); | ||
} | ||
|
||
void HOT ILI9341Display::draw_absolute_pixel_internal(int x, int y, Color color) { | ||
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) | ||
return; | ||
|
||
// 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_; | ||
|
||
uint32_t pos = (y * width_) + x; | ||
auto color565 = color.to_rgb_565(); | ||
buffer_[pos] = convert_to_8bit_color_(color565); | ||
} | ||
|
||
// should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color | ||
// values per bit is huge | ||
uint32_t ILI9341Display::get_buffer_length_() { return this->get_width_internal() * this->get_height_internal(); } | ||
|
||
void ILI9341Display::start_command_() { | ||
this->dc_pin_->digital_write(false); | ||
this->enable(); | ||
} | ||
|
||
void ILI9341Display::end_command_() { this->disable(); } | ||
void ILI9341Display::start_data_() { | ||
this->dc_pin_->digital_write(true); | ||
this->enable(); | ||
} | ||
void ILI9341Display::end_data_() { this->disable(); } | ||
|
||
void ILI9341Display::init_lcd_(const uint8_t *init_cmd) { | ||
uint8_t cmd, x, num_args; | ||
const uint8_t *addr = init_cmd; | ||
while ((cmd = pgm_read_byte(addr++)) > 0) { | ||
x = pgm_read_byte(addr++); | ||
num_args = x & 0x7F; | ||
send_command(cmd, addr, num_args); | ||
addr += num_args; | ||
if (x & 0x80) | ||
delay(150); // NOLINT | ||
} | ||
} | ||
|
||
void ILI9341Display::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(ILI9341_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(ILI9341_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(ILI9341_RAMWR); // Write to RAM | ||
} | ||
|
||
void ILI9341Display::invert_display_(bool invert) { this->command(invert ? ILI9341_INVON : ILI9341_INVOFF); } | ||
|
||
int ILI9341Display::get_width_internal() { return this->width_; } | ||
int ILI9341Display::get_height_internal() { return this->height_; } | ||
|
||
// M5Stack display | ||
void ILI9341M5Stack::initialize() { | ||
this->init_lcd_(INITCMD_M5STACK); | ||
this->width_ = 320; | ||
this->height_ = 240; | ||
this->invert_display_(true); | ||
this->fill_internal_(COLOR_BLACK); | ||
} | ||
|
||
// 24_TFT display | ||
void ILI9341TFT24::initialize() { | ||
this->init_lcd_(INITCMD_TFT); | ||
this->width_ = 240; | ||
this->height_ = 320; | ||
this->fill_internal_(COLOR_BLACK); | ||
} | ||
|
||
} // namespace ili9341 | ||
} // namespace esphome |
Oops, something went wrong.