Compare commits

..
15 changed files with 139 additions and 172 deletions

No files matched your search

+1 -4
View File
@@ -220,10 +220,7 @@ def get_display_metadata(display_id: ID) -> DisplayMetaData:
height=dimensions[1], height=dimensions[1],
has_hardware_rotation=False, has_hardware_rotation=False,
byte_order=disp_config.get(CONF_BYTE_ORDER, cv.UNDEFINED), byte_order=disp_config.get(CONF_BYTE_ORDER, cv.UNDEFINED),
has_writer=disp_config.get(CONF_AUTO_CLEAR_ENABLED) is True has_writer=config_has_writer(disp_config),
or disp_config.get(CONF_PAGES) is not None
or disp_config.get(CONF_LAMBDA) is not None
or disp_config.get(CONF_SHOW_TEST_CARD) is True,
rotation=disp_config.get(CONF_ROTATION, 0), rotation=disp_config.get(CONF_ROTATION, 0),
draw_rounding=disp_config.get(CONF_DRAW_ROUNDING, 0), draw_rounding=disp_config.get(CONF_DRAW_ROUNDING, 0),
) )
+1 -5
View File
@@ -15,7 +15,6 @@ from esphome.components.mipi import (
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.config_validation import update_interval from esphome.config_validation import update_interval
from esphome.const import ( from esphome.const import (
CONF_AUTO_CLEAR_ENABLED,
CONF_BUSY_PIN, CONF_BUSY_PIN,
CONF_CS_PIN, CONF_CS_PIN,
CONF_DATA_RATE, CONF_DATA_RATE,
@@ -189,10 +188,7 @@ def customise_schema(config):
height, height,
has_hardware_rotation=True, has_hardware_rotation=True,
byte_order=cv.UNDEFINED, byte_order=cv.UNDEFINED,
has_writer=config.get(CONF_AUTO_CLEAR_ENABLED) is True has_writer=display.config_has_writer(config),
or config.get(CONF_PAGES) is not None
or config.get(CONF_LAMBDA) is not None
or config.get(CONF_SHOW_TEST_CARD) is True,
rotation=config.get(CONF_ROTATION, 0), rotation=config.get(CONF_ROTATION, 0),
draw_rounding=0, draw_rounding=0,
) )
+9 -16
View File
@@ -1,4 +1,5 @@
#include "epaper_spi.h" #include "epaper_spi.h"
#include "esphome/components/display/init_sequence.h"
#include <cinttypes> #include <cinttypes>
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
@@ -315,26 +316,18 @@ void EPaperBase::start_data_() {
void EPaperBase::on_safe_shutdown() { this->deep_sleep(); } void EPaperBase::on_safe_shutdown() { this->deep_sleep(); }
void EPaperBase::send_init_sequence_(const uint8_t *sequence, size_t length) { void EPaperBase::send_init_sequence_(const uint8_t *sequence, size_t length) {
size_t index = 0; display::InitSequenceReader reader(sequence, length);
display::InitSequenceEntry entry;
while (index != length) { while (auto step = reader.next(entry)) {
if (length - index < 2) { if (step == display::INIT_SEQUENCE_STEP_MALFORMED) {
this->mark_failed(LOG_STR("Malformed init sequence")); this->mark_failed(LOG_STR("Malformed init sequence"));
return; return;
} }
const uint8_t cmd = sequence[index++]; if (step == display::INIT_SEQUENCE_STEP_DELAY) {
if (const uint8_t x = sequence[index++]; x == DELAY_FLAG) { ESP_LOGV(TAG, "Delay %dms", entry.value);
ESP_LOGV(TAG, "Delay %dms", cmd); delay(entry.value);
delay(cmd);
} else { } else {
const uint8_t num_args = x & 0x7F; this->cmd_data(entry.value, entry.args, entry.count);
if (length - index < num_args) {
ESP_LOGE(TAG, "Malformed init sequence, cmd = %X, num_args = %u", cmd, num_args);
this->mark_failed();
return;
}
this->cmd_data(cmd, sequence + index, num_args);
index += num_args;
} }
} }
} }
@@ -34,7 +34,6 @@ static constexpr uint32_t MAX_TRANSFER_TIME = 10; // Transfer in 10ms blocks to
static constexpr size_t MAX_TRANSFER_SIZE = 128; static constexpr size_t MAX_TRANSFER_SIZE = 128;
// Longer busy waits are the waveform or a stuck pin, and are polled at the normal loop pace // Longer busy waits are the waveform or a stuck pin, and are polled at the normal loop pace
static constexpr uint32_t FAST_BUSY_POLL_MS = 50; static constexpr uint32_t FAST_BUSY_POLL_MS = 50;
static constexpr uint8_t DELAY_FLAG = 0xFF;
class EPaperBase : public Display, class EPaperBase : public Display,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
+1 -3
View File
@@ -38,7 +38,6 @@ from esphome.components.mipi import (
) )
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_AUTO_CLEAR_ENABLED,
CONF_COLOR_ORDER, CONF_COLOR_ORDER,
CONF_DIMENSIONS, CONF_DIMENSIONS,
CONF_ENABLE_PIN, CONF_ENABLE_PIN,
@@ -167,8 +166,7 @@ def _config_schema(config: ConfigType) -> ConfigType:
height, height,
has_hardware_rotation=False, has_hardware_rotation=False,
byte_order=config[CONF_BYTE_ORDER], byte_order=config[CONF_BYTE_ORDER],
has_writer=requires_buffer(config) has_writer=display.config_has_writer(config),
or config.get(CONF_AUTO_CLEAR_ENABLED) is True,
rotation=config.get(CONF_ROTATION, 0), rotation=config.get(CONF_ROTATION, 0),
draw_rounding=config.get(CONF_DRAW_ROUNDING, 0), draw_rounding=config.get(CONF_DRAW_ROUNDING, 0),
) )
+29 -37
View File
@@ -1,6 +1,7 @@
#ifdef USE_ESP32_VARIANT_ESP32P4 #ifdef USE_ESP32_VARIANT_ESP32P4
#include <utility> #include <utility>
#include "mipi_dsi.h" #include "mipi_dsi.h"
#include "esphome/components/display/init_sequence.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include <algorithm> #include <algorithm>
@@ -123,47 +124,38 @@ void MipiDsi::setup() {
this->smark_failed(LOG_STR("esp_lcd_init failed"), err); this->smark_failed(LOG_STR("esp_lcd_init failed"), err);
return; return;
} }
size_t index = 0; display::InitSequenceReader reader(this->init_sequence_, this->init_sequence_len_);
const uint8_t *seq = this->init_sequence_; display::InitSequenceEntry entry;
const size_t len = this->init_sequence_len_; while (auto step = reader.next(entry)) {
while (index != len) { if (step == display::INIT_SEQUENCE_STEP_MALFORMED) {
if (len - index < 2) {
this->mark_failed(LOG_STR("Malformed init sequence")); this->mark_failed(LOG_STR("Malformed init sequence"));
return; return;
} }
uint8_t cmd = seq[index++]; if (step == display::INIT_SEQUENCE_STEP_DELAY) {
uint8_t x = seq[index++]; ESP_LOGD(TAG, "Delay %dms", entry.value);
if (x == DELAY_FLAG) { delay(entry.value);
ESP_LOGD(TAG, "Delay %dms", cmd); continue;
delay(cmd);
} else {
uint8_t num_args = x & 0x7F;
if (len - index < num_args) {
this->mark_failed(LOG_STR("Malformed init sequence"));
return;
}
if (cmd == SLEEP_OUT) {
// are we ready, boots?
int duration = when - millis();
if (duration > 0) {
delay(duration);
}
}
const auto *ptr = seq + index;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
char hex_buf[format_hex_pretty_size(MIPI_DSI_MAX_CMD_LOG_BYTES)];
#endif
ESP_LOGVV(TAG, "Command %02X, length %d, byte(s) %s", cmd, num_args,
format_hex_pretty_to(hex_buf, ptr, num_args, '.'));
err = esp_lcd_panel_io_tx_param(this->io_handle_, cmd, ptr, num_args);
if (err != ESP_OK) {
this->smark_failed(LOG_STR("lcd_panel_io_tx_param failed"), err);
return;
}
index += num_args;
if (cmd == SLEEP_OUT)
delay(10);
} }
const uint8_t cmd = entry.value;
if (cmd == SLEEP_OUT) {
// are we ready, boots?
int duration = when - millis();
if (duration > 0) {
delay(duration);
}
}
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
char hex_buf[format_hex_pretty_size(MIPI_DSI_MAX_CMD_LOG_BYTES)];
#endif
ESP_LOGVV(TAG, "Command %02X, length %d, byte(s) %s", cmd, entry.count,
format_hex_pretty_to(hex_buf, entry.args, entry.count, '.'));
err = esp_lcd_panel_io_tx_param(this->io_handle_, cmd, entry.args, entry.count);
if (err != ESP_OK) {
this->smark_failed(LOG_STR("lcd_panel_io_tx_param failed"), err);
return;
}
if (cmd == SLEEP_OUT)
delay(10);
} }
this->io_lock_ = xSemaphoreCreateBinary(); this->io_lock_ = xSemaphoreCreateBinary();
esp_lcd_dpi_panel_event_callbacks_t cbs = { esp_lcd_dpi_panel_event_callbacks_t cbs = {
-1
View File
@@ -31,7 +31,6 @@ const uint8_t INVERT_OFF = 0x20;
const uint8_t INVERT_ON = 0x21; const uint8_t INVERT_ON = 0x21;
const uint8_t DISPLAY_ON = 0x29; const uint8_t DISPLAY_ON = 0x29;
const uint8_t CMD2_BKSEL = 0xFF; const uint8_t CMD2_BKSEL = 0xFF;
const uint8_t DELAY_FLAG = 0xFF;
const uint8_t MADCTL_BGR = 0x08; const uint8_t MADCTL_BGR = 0x08;
const uint8_t MADCTL_MX = 0x40; const uint8_t MADCTL_MX = 0x40;
const uint8_t MADCTL_MY = 0x80; const uint8_t MADCTL_MY = 0x80;
+1 -3
View File
@@ -50,7 +50,6 @@ from esphome.components.spi import (
) )
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_AUTO_CLEAR_ENABLED,
CONF_BLUE, CONF_BLUE,
CONF_COLOR_ORDER, CONF_COLOR_ORDER,
CONF_CS_PIN, CONF_CS_PIN,
@@ -243,8 +242,7 @@ def _config_schema(config: ConfigType) -> ConfigType:
height, height,
has_hardware_rotation=True, has_hardware_rotation=True,
byte_order=config[CONF_BYTE_ORDER], byte_order=config[CONF_BYTE_ORDER],
has_writer=requires_buffer(config) has_writer=display.config_has_writer(config),
or config.get(CONF_AUTO_CLEAR_ENABLED) is True,
rotation=config.get(CONF_ROTATION, 0), rotation=config.get(CONF_ROTATION, 0),
draw_rounding=config.get(CONF_DRAW_ROUNDING, 0), draw_rounding=config.get(CONF_DRAW_ROUNDING, 0),
) )
+20 -35
View File
@@ -1,5 +1,6 @@
#if defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S31) #if defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S31)
#include "mipi_rgb.h" #include "mipi_rgb.h"
#include "esphome/components/display/init_sequence.h"
#include "esphome/core/gpio.h" #include "esphome/core/gpio.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
@@ -13,8 +14,6 @@
namespace esphome::mipi_rgb { namespace esphome::mipi_rgb {
static const uint8_t DELAY_FLAG = 0xFF;
// Maximum bytes to log for init commands (truncated if larger) // Maximum bytes to log for init commands (truncated if larger)
static constexpr size_t MIPI_RGB_MAX_CMD_LOG_BYTES = 64; static constexpr size_t MIPI_RGB_MAX_CMD_LOG_BYTES = 64;
static constexpr uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top static constexpr uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top
@@ -75,44 +74,30 @@ void MipiRgbSpi::write_data_(uint8_t value) {
this->disable(); this->disable();
} }
/**
* this relies upon the init sequence being well-formed, which is guaranteed by the Python init code.
*/
void MipiRgbSpi::write_init_sequence_() { void MipiRgbSpi::write_init_sequence_() {
size_t index = 0; display::InitSequenceReader reader(this->init_sequence_, this->init_sequence_len_);
const uint8_t *seq = this->init_sequence_; display::InitSequenceEntry entry;
const size_t len = this->init_sequence_len_; while (auto step = reader.next(entry)) {
while (index != len) { if (step == display::INIT_SEQUENCE_STEP_MALFORMED) {
if (len - index < 2) {
this->mark_failed(LOG_STR("Malformed init sequence")); this->mark_failed(LOG_STR("Malformed init sequence"));
return; return;
} }
uint8_t cmd = seq[index++]; if (step == display::INIT_SEQUENCE_STEP_DELAY) {
uint8_t x = seq[index++]; ESP_LOGD(TAG, "Delay %dms", entry.value);
if (x == DELAY_FLAG) { delay(entry.value);
ESP_LOGD(TAG, "Delay %dms", cmd); continue;
delay(cmd);
} else {
uint8_t num_args = x & 0x7F;
if (len - index < num_args) {
this->mark_failed(LOG_STR("Malformed init sequence"));
return;
}
if (cmd == SLEEP_OUT) {
delay(120); // NOLINT
}
const auto *ptr = seq + index;
char hex_buf[format_hex_pretty_size(MIPI_RGB_MAX_CMD_LOG_BYTES)];
ESP_LOGD(TAG, "Write command %02X, length %d, byte(s) %s", cmd, num_args,
format_hex_pretty_to(hex_buf, ptr, num_args, '.'));
index += num_args;
this->write_command_(cmd);
while (num_args-- != 0)
this->write_data_(*ptr++);
if (cmd == SLEEP_OUT)
delay(10);
} }
const uint8_t cmd = entry.value;
if (cmd == SLEEP_OUT)
delay(120); // NOLINT
char hex_buf[format_hex_pretty_size(MIPI_RGB_MAX_CMD_LOG_BYTES)];
ESP_LOGD(TAG, "Write command %02X, length %d, byte(s) %s", cmd, entry.count,
format_hex_pretty_to(hex_buf, entry.args, entry.count, '.'));
this->write_command_(cmd);
for (uint8_t i = 0; i < entry.count; i++)
this->write_data_(entry.args[i]);
if (cmd == SLEEP_OUT)
delay(10);
} }
// this->spi_teardown(); // SPI not needed after this // this->spi_teardown(); // SPI not needed after this
delay(10); delay(10);
+14 -4
View File
@@ -33,7 +33,6 @@ from esphome.components.spi import CONF_SPI_MODE, TYPE_OCTAL, TYPE_QUAD, TYPE_SI
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.config_validation import ALLOW_EXTRA from esphome.config_validation import ALLOW_EXTRA
from esphome.const import ( from esphome.const import (
CONF_AUTO_CLEAR_ENABLED,
CONF_BRIGHTNESS, CONF_BRIGHTNESS,
CONF_BUFFER_SIZE, CONF_BUFFER_SIZE,
CONF_COLOR_ORDER, CONF_COLOR_ORDER,
@@ -73,6 +72,10 @@ Model = mipi_spi_ns.enum("Model")
PixelMode = mipi_spi_ns.enum("PixelMode") PixelMode = mipi_spi_ns.enum("PixelMode")
BusType = mipi_spi_ns.enum("BusType") BusType = mipi_spi_ns.enum("BusType")
# Matches INIT_SEQUENCE_MAX_ARGS, the flash copy buffer in mipi_spi.h
INIT_SEQUENCE_MAX_ARGS = 128
COLOR_ORDERS = { COLOR_ORDERS = {
MODE_RGB: ColorOrder.COLOR_ORDER_RGB, MODE_RGB: ColorOrder.COLOR_ORDER_RGB,
MODE_BGR: ColorOrder.COLOR_ORDER_BGR, MODE_BGR: ColorOrder.COLOR_ORDER_BGR,
@@ -216,7 +219,15 @@ def model_schema(config: ConfigType) -> cv.All | cv.Schema:
bus_mode, lower=True bus_mode, lower=True
), ),
cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True), cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True),
iseqconf: cv.ensure_list(map_sequence), iseqconf: cv.ensure_list(
cv.All(
map_sequence,
cv.Length(
max=INIT_SEQUENCE_MAX_ARGS + 1,
msg=f"An init sequence command can have at most {INIT_SEQUENCE_MAX_ARGS} data bytes",
),
)
),
cv.Optional(CONF_BUFFER_SIZE): cv.All( cv.Optional(CONF_BUFFER_SIZE): cv.All(
cv.percentage, cv.Range(0.12, 1.0) cv.percentage, cv.Range(0.12, 1.0)
), ),
@@ -290,8 +301,7 @@ def customise_schema(config: ConfigType) -> ConfigType:
height, height,
has_hardware_transform, has_hardware_transform,
byte_order=config[CONF_BYTE_ORDER], byte_order=config[CONF_BYTE_ORDER],
has_writer=requires_buffer(config) has_writer=display.config_has_writer(config),
or config.get(CONF_AUTO_CLEAR_ENABLED) is True,
rotation=config.get(CONF_ROTATION, 0), rotation=config.get(CONF_ROTATION, 0),
draw_rounding=config.get(CONF_DRAW_ROUNDING, 0), draw_rounding=config.get(CONF_DRAW_ROUNDING, 0),
) )
+18 -28
View File
@@ -7,12 +7,16 @@
#include "esphome/components/spi/spi.h" #include "esphome/components/spi/spi.h"
#include "esphome/components/display/display.h" #include "esphome/components/display/display.h"
#include "esphome/components/display/display_color_utils.h" #include "esphome/components/display/display_color_utils.h"
#include "esphome/components/display/init_sequence.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome::mipi_spi { namespace esphome::mipi_spi {
/// Largest argument count in one init command; display.py rejects longer ones.
static constexpr size_t INIT_SEQUENCE_MAX_ARGS = 128;
ESPHOME_LOG_TAG(TAG, "display.mipi_spi"); ESPHOME_LOG_TAG(TAG, "display.mipi_spi");
// Maximum bytes to log for commands (truncated if larger) // Maximum bytes to log for commands (truncated if larger)
@@ -48,7 +52,6 @@ static constexpr uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horiz
static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically
static constexpr uint16_t MADCTL_FLIP_FLAG = 0x100; // controller uses axis flip bits static constexpr uint16_t MADCTL_FLIP_FLAG = 0x100; // controller uses axis flip bits
static constexpr uint8_t DELAY_FLAG = 0xFF;
// store a 16 bit value in a buffer, big endian. // store a 16 bit value in a buffer, big endian.
static inline void put16_be(uint8_t *buf, uint16_t value) { static inline void put16_be(uint8_t *buf, uint16_t value) {
buf[0] = value >> 8; buf[0] = value >> 8;
@@ -163,36 +166,23 @@ class MipiSpi : public display::Display,
// need to know when the display is ready for SLPOUT command - will be 120ms after reset // need to know when the display is ready for SLPOUT command - will be 120ms after reset
auto when = millis() + 120; auto when = millis() + 120;
size_t index = 0; display::InitSequenceReader reader(this->init_sequence_, this->init_sequence_len_);
const uint8_t *seq = this->init_sequence_; display::InitSequenceEntry entry;
const size_t len = this->init_sequence_len_; // The sequence is in flash, which SPI DMA (and ESP8266 byte loads) cannot read
while (index != len) { uint8_t args[INIT_SEQUENCE_MAX_ARGS];
if (len - index < 2) { while (auto step = reader.next(entry, sizeof(args))) {
esph_log_e(TAG, "Malformed init sequence"); if (step == display::INIT_SEQUENCE_STEP_MALFORMED) {
this->mark_failed(); this->mark_failed(LOG_STR("Malformed init sequence"));
return; return;
} }
uint8_t cmd = progmem_read_byte(seq + index++); if (step == display::INIT_SEQUENCE_STEP_DELAY) {
uint8_t x = progmem_read_byte(seq + index++); const uint8_t ms = entry.value != 0 ? entry.value : clamp_at_least((int) (when - millis()), 0);
if (x == DELAY_FLAG) { esph_log_d(TAG, "Delay %dms", ms);
if (cmd == 0) { delay(ms);
cmd = clamp_at_least((int) (when - millis()), 0); continue;
}
esph_log_d(TAG, "Delay %dms", cmd);
delay(cmd);
} else {
uint8_t num_args = x & 0x7F;
if (len - index < num_args) {
esph_log_e(TAG, "Malformed init sequence");
this->mark_failed();
return;
}
// The sequence is in flash, which SPI DMA (and ESP8266 byte loads) cannot read
uint8_t args[0x80];
progmem_memcpy(args, seq + index, num_args);
this->write_command_(cmd, args, num_args);
index += num_args;
} }
progmem_memcpy(args, entry.args, entry.count);
this->write_command_(entry.value, args, entry.count);
} }
this->reset_params_(); this->reset_params_();
// Marks init as done, so later commands log at verbose level instead of debug // Marks init as done, so later commands log at verbose level instead of debug
+1 -8
View File
@@ -4,7 +4,6 @@ import pkgutil
from esphome import pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import display, spi from esphome.components import display, spi
from esphome.components.display import CONF_SHOW_TEST_CARD
from esphome.components.mipi import ( from esphome.components.mipi import (
DriverChip, DriverChip,
flatten_sequence, flatten_sequence,
@@ -13,16 +12,13 @@ from esphome.components.mipi import (
) )
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_AUTO_CLEAR_ENABLED,
CONF_CS_PIN, CONF_CS_PIN,
CONF_DATA_RATE, CONF_DATA_RATE,
CONF_DC_PIN, CONF_DC_PIN,
CONF_HEIGHT, CONF_HEIGHT,
CONF_ID, CONF_ID,
CONF_INIT_SEQUENCE, CONF_INIT_SEQUENCE,
CONF_LAMBDA,
CONF_MODEL, CONF_MODEL,
CONF_PAGES,
CONF_RESET_PIN, CONF_RESET_PIN,
CONF_ROTATION, CONF_ROTATION,
CONF_WIDTH, CONF_WIDTH,
@@ -182,10 +178,7 @@ def customise_schema(config: ConfigType) -> ConfigType:
config[CONF_HEIGHT], config[CONF_HEIGHT],
has_hardware_rotation=False, has_hardware_rotation=False,
byte_order=cv.UNDEFINED, byte_order=cv.UNDEFINED,
has_writer=config.get(CONF_AUTO_CLEAR_ENABLED) is True has_writer=display.config_has_writer(config),
or config.get(CONF_PAGES) is not None
or config.get(CONF_LAMBDA) is not None
or config.get(CONF_SHOW_TEST_CARD) is True,
rotation=config.get(CONF_ROTATION, 0), rotation=config.get(CONF_ROTATION, 0),
) )
return config return config
+15 -22
View File
@@ -56,33 +56,26 @@ void ST7305::reset_() {
} }
void ST7305::send_init_sequence_() { void ST7305::send_init_sequence_() {
while (this->init_index_ < this->init_sequence_length_) { display::InitSequenceEntry entry;
if (this->init_sequence_length_ - this->init_index_ < 2) { display::InitSequenceStep step;
this->mark_failed(LOG_STR("Malformed init sequence")); while ((step = this->init_reader_.next(entry)) == display::INIT_SEQUENCE_STEP_COMMAND) {
return;
}
const uint8_t command = progmem_read_byte(&this->init_sequence_[this->init_index_++]);
const uint8_t count = progmem_read_byte(&this->init_sequence_[this->init_index_++]);
if (count == 0xFF) {
// Resume the sequence after the delay instead of blocking the main loop.
this->set_timeout(STARTUP_TIMEOUT_ID, command, [this]() { this->send_init_sequence_(); });
return;
}
if (this->init_sequence_length_ - this->init_index_ < count) {
this->mark_failed(LOG_STR("Malformed init sequence"));
return;
}
this->enable(); this->enable();
this->send_command_(command); this->send_command_(entry.value);
if (count != 0) { if (entry.count != 0) {
this->dc_pin_->digital_write(true); this->dc_pin_->digital_write(true);
for (uint8_t i = 0; i < count; i++) for (uint8_t i = 0; i < entry.count; i++)
this->write_byte(progmem_read_byte(&this->init_sequence_[this->init_index_ + i])); this->write_byte(progmem_read_byte(entry.args + i));
} }
this->disable(); this->disable();
this->init_index_ += count;
} }
this->finish_init_(); if (step == display::INIT_SEQUENCE_STEP_DELAY) {
// Resume the sequence after the delay instead of blocking the main loop.
this->set_timeout(STARTUP_TIMEOUT_ID, entry.value, [this]() { this->send_init_sequence_(); });
} else if (step == display::INIT_SEQUENCE_STEP_MALFORMED) {
this->mark_failed(LOG_STR("Malformed init sequence"));
} else {
this->finish_init_();
}
} }
void ST7305::finish_init_() { void ST7305::finish_init_() {
+3 -5
View File
@@ -4,6 +4,7 @@
#include <cstdint> #include <cstdint>
#include "esphome/components/display/display_buffer.h" #include "esphome/components/display/display_buffer.h"
#include "esphome/components/display/init_sequence.h"
#include "esphome/components/spi/spi.h" #include "esphome/components/spi/spi.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
@@ -35,8 +36,7 @@ class ST7305 : public display::DisplayBuffer,
row_start_(row_start), row_start_(row_start),
row_end_(row_end), row_end_(row_end),
buffer_length_(static_cast<size_t>(width) * static_cast<size_t>(height) / 8), buffer_length_(static_cast<size_t>(width) * static_cast<size_t>(height) / 8),
init_sequence_(init_sequence), init_reader_(init_sequence, init_sequence_length) {}
init_sequence_length_(init_sequence_length) {}
void setup() override; void setup() override;
void update() override; void update() override;
@@ -113,9 +113,7 @@ class ST7305 : public display::DisplayBuffer,
uint8_t row_start_; uint8_t row_start_;
uint8_t row_end_; uint8_t row_end_;
size_t buffer_length_; size_t buffer_length_;
const uint8_t *init_sequence_; display::InitSequenceReader init_reader_;
size_t init_sequence_length_;
size_t init_index_{0};
bool ready_{false}; bool ready_{false};
int x_low_{0}; int x_low_{0};
@@ -93,6 +93,16 @@ def run_schema_validation(config: ConfigType) -> None:
r"value must be a power of two for dictionary value @ data\['draw_rounding'\]", r"value must be a power of two for dictionary value @ data\['draw_rounding'\]",
id="invalid_draw_rounding", id="invalid_draw_rounding",
), ),
pytest.param(
{
"model": "custom",
"dc_pin": 18,
"dimensions": {"width": 320, "height": 240},
"init_sequence": [[0xE0] + [0x00] * 129],
},
r"at most 128 data bytes",
id="init_command_too_long",
),
], ],
) )
def test_basic_configuration_errors( def test_basic_configuration_errors(
@@ -606,3 +616,19 @@ def test_identical_init_sequences_share_one_table(
assert first is not None and second is not None assert first is not None and second is not None
assert first.group(1) == second.group(1) assert first.group(1) == second.group(1)
assert main_cpp.count("mipi_spi_init_sequence[] PROGMEM") == 1 assert main_cpp.count("mipi_spi_init_sequence[] PROGMEM") == 1
def test_init_command_at_buffer_limit(set_core_config: SetCoreConfigCallable) -> None:
"""A command with exactly 128 data bytes fits the C++ copy buffer."""
set_core_config(
PlatformFramework.ESP32_IDF,
platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32},
)
run_schema_validation(
{
"model": "custom",
"dc_pin": 18,
"dimensions": {"width": 320, "height": 240},
"init_sequence": [[0xE0] + [0x00] * 128],
}
)