[ili9xxx] Add custom init sequence and selectable 16/18 bit mode

This commit is contained in:
clydebarrow 2024-05-15 06:18:19 +10:00
parent 6fd8c77f4b
commit fe5004de7c
5 changed files with 94 additions and 32 deletions

View File

@ -47,6 +47,12 @@ ILI9XXXDisplay = ili9xxx_ns.class_(
display.DisplayBuffer,
)
PixelMode = ili9xxx_ns.enum("PixelMode")
PIXEL_MODES = {
"16bit": PixelMode.PIXEL_MODE_16,
"18bit": PixelMode.PIXEL_MODE_18,
}
ILI9XXXColorMode = ili9xxx_ns.enum("ILI9XXXColorMode")
ColorOrder = display.display_ns.enum("ColorMode")
@ -68,6 +74,7 @@ MODELS = {
"S3BOX": ili9xxx_ns.class_("ILI9XXXS3Box", ILI9XXXDisplay),
"S3BOX_LITE": ili9xxx_ns.class_("ILI9XXXS3BoxLite", ILI9XXXDisplay),
"WAVESHARE_RES_3_5": ili9xxx_ns.class_("WAVESHARERES35", ILI9XXXDisplay),
"CUSTOM": ILI9XXXDisplay,
}
COLOR_ORDERS = {
@ -80,14 +87,37 @@ 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_PIXEL_MODE = "pixel_mode"
CONF_INIT_SEQUENCE = "init_sequence"
def cmd(c, *args):
"""
Create a command sequence
:param c: The command (8 bit)
:param args: zero or more arguments (8 bit values)
:return: a list with the command, the argument count and the arguments
"""
return [c, len(args)] + list(args)
def map_sequence(value):
"""
An initialisation sequence is a literal array of data bytes.
The format is a repeated sequence of [CMD, <data>]
"""
if len(value) == 0:
raise cv.Invalid("Empty sequence")
return cmd(*value)
def _validate(config):
if config.get(CONF_COLOR_PALETTE) == "IMAGE_ADAPTIVE" and not config.get(
CONF_COLOR_PALETTE_IMAGES
if (
config.get(CONF_COLOR_PALETTE) == "IMAGE_ADAPTIVE"
and CONF_COLOR_PALETTE_IMAGES not in config
):
raise cv.Invalid(
"Color palette in IMAGE_ADAPTIVE mode requires at least one 'color_palette_images' entry to generate palette"
"IMAGE_ADAPTIVE palette requires at least one 'color_palette_images' entry"
)
if (
config.get(CONF_COLOR_PALETTE_IMAGES)
@ -96,7 +126,8 @@ def _validate(config):
raise cv.Invalid(
"Providing color palette images requires palette mode to be 'IMAGE_ADAPTIVE'"
)
if CORE.is_esp8266 and config.get(CONF_MODEL) not in [
model = config[CONF_MODEL]
if CORE.is_esp8266 and model not in [
"M5STACK",
"TFT_2.4",
"TFT_2.4R",
@ -104,9 +135,12 @@ def _validate(config):
"ILI9342",
"ST7789V",
]:
raise cv.Invalid(
"Provided model can't run on ESP8266. Use an ESP32 with PSRAM onboard"
)
raise cv.Invalid("Selected model can't run on ESP8266.")
if model == "CUSTOM":
if CONF_INIT_SEQUENCE not in config or CONF_DIMENSIONS not in config:
raise cv.Invalid("CUSTOM model requires init_sequence and dimensions")
return config
@ -116,6 +150,7 @@ CONFIG_SCHEMA = cv.All(
{
cv.GenerateID(): cv.declare_id(ILI9XXXDisplay),
cv.Required(CONF_MODEL): cv.enum(MODELS, upper=True, space="_"),
cv.Optional(CONF_PIXEL_MODE): cv.enum(PIXEL_MODES),
cv.Optional(CONF_DIMENSIONS): cv.Any(
cv.dimensions,
cv.Schema(
@ -150,6 +185,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean,
}
),
cv.Optional(CONF_INIT_SEQUENCE): cv.ensure_list(map_sequence),
}
)
.extend(cv.polling_component_schema("1s"))
@ -167,6 +203,14 @@ 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 init_sequences := config.get(CONF_INIT_SEQUENCE):
sequence = []
for seq in init_sequences:
sequence.extend(seq)
cg.add(var.add_init_sequence(sequence))
if pixel_mode := config.get(CONF_PIXEL_MODE):
cg.add(var.set_pixel_mode(pixel_mode))
if CONF_COLOR_ORDER in config:
cg.add(var.set_color_order(COLOR_ORDERS[config[CONF_COLOR_ORDER]]))
if CONF_TRANSFORM in config:

View File

@ -34,7 +34,26 @@ void ILI9XXXDisplay::setup() {
ESP_LOGD(TAG, "Setting up ILI9xxx");
this->setup_pins_();
this->init_lcd_();
this->init_lcd_(this->init_sequence_);
this->init_lcd_(this->extra_init_sequence_.data());
switch (this->pixel_mode_) {
case PIXEL_MODE_16:
if (this->is_18bitdisplay_) {
this->command(ILI9XXX_PIXFMT);
this->data(0x55);
this->is_18bitdisplay_ = false;
}
break;
case PIXEL_MODE_18:
if (!this->is_18bitdisplay_) {
this->command(ILI9XXX_PIXFMT);
this->data(0x66);
this->is_18bitdisplay_ = true;
}
break;
default:
break;
}
this->set_madctl();
this->command(this->pre_invertcolors_ ? ILI9XXX_INVON : ILI9XXX_INVOFF);
@ -356,10 +375,11 @@ void ILI9XXXDisplay::reset_() {
}
}
void ILI9XXXDisplay::init_lcd_() {
void ILI9XXXDisplay::init_lcd_(const uint8_t *addr) {
if (addr == nullptr)
return;
uint8_t cmd, x, num_args;
const uint8_t *addr = this->init_sequence_;
while ((cmd = *addr++) > 0) {
while ((cmd = *addr++) != 0) {
x = *addr++;
num_args = x & 0x7F;
this->send_command(cmd, addr, num_args);

View File

@ -17,6 +17,12 @@ enum ILI9XXXColorMode {
BITS_16 = 0x10,
};
enum PixelMode {
PIXEL_MODE_UNSPECIFIED,
PIXEL_MODE_16,
PIXEL_MODE_18,
};
class ILI9XXXDisplay : public display::DisplayBuffer,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_40MHZ> {
@ -52,6 +58,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
}
}
void add_init_sequence(const std::vector<uint8_t> &sequence) { this->extra_init_sequence_ = sequence; }
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; }
@ -73,6 +80,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
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 set_pixel_mode(PixelMode mode) { this->pixel_mode_ = mode; }
void update() override;
@ -99,11 +107,12 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
virtual void set_madctl();
void display_();
void init_lcd_();
void init_lcd_(const uint8_t *);
void set_addr_window_(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2);
void reset_();
uint8_t const *init_sequence_{};
std::vector<uint8_t> extra_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};
@ -112,7 +121,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
uint16_t y_low_{0};
uint16_t x_high_{0};
uint16_t y_high_{0};
const uint8_t *palette_;
const uint8_t *palette_{};
ILI9XXXColorMode buffer_color_mode_{BITS_16};
@ -133,6 +142,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
bool prossing_update_ = false;
bool need_update_ = false;
bool is_18bitdisplay_ = false;
PixelMode pixel_mode_{};
bool pre_invertcolors_ = false;
display::ColorOrder color_order_{display::COLOR_ORDER_BGR};
bool swap_xy_{};

View File

@ -157,7 +157,7 @@ static const uint8_t INITCMD_ILI9488[] = {
0xE9, 1, 0x00, // Set Image Functio. Disable 24 bit data
ILI9XXX_ADJCTL3, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control 3
ILI9XXX_PIXFMT, 1, 0x55, //ILI9488 only supports 18-bit pixel format in 4/3 wire SPI mode
ILI9XXX_PIXFMT, 1, 0x66, //ILI9488 only supports 18-bit pixel format in 4/3 wire SPI mode
ILI9XXX_SLPOUT, 0x80, // Exit sleep mode
ILI9XXX_DISPON, 0x80, // Set display on
0x00 // end
@ -194,8 +194,8 @@ static const uint8_t PROGMEM INITCMD_ILI9488_A[] = {
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
//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

View File

@ -12,24 +12,12 @@ display:
swap_xy: true
mirror_x: true
mirror_y: false
model: TFT 2.4
color_palette: GRAYSCALE
model: custom
cs_pin: 12
dc_pin: 13
reset_pin: 14
init_sequence:
- [0xFF, 0x77, 0x01, 0x00, 0x00, 0x10]
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: 25
dc_pin: 26
reset_pin: 27
auto_clear_enabled: false
rotation: 90
lambda: |-
it.fill(Color::WHITE);