19static constexpr size_t MIPI_SPI_MAX_CMD_LOG_BYTES = 64;
22static constexpr uint8_t SW_RESET_CMD = 0x01;
23static constexpr uint8_t SLEEP_OUT = 0x11;
24static constexpr uint8_t NORON = 0x13;
25static constexpr uint8_t INVERT_OFF = 0x20;
26static constexpr uint8_t INVERT_ON = 0x21;
27static constexpr uint8_t ALL_ON = 0x23;
28static constexpr uint8_t WRAM = 0x24;
29static constexpr uint8_t MIPI = 0x26;
30static constexpr uint8_t DISPLAY_ON = 0x29;
31static constexpr uint8_t RASET = 0x2B;
32static constexpr uint8_t CASET = 0x2A;
33static constexpr uint8_t WDATA = 0x2C;
34static constexpr uint8_t TEON = 0x35;
35static constexpr uint8_t MADCTL_CMD = 0x36;
36static constexpr uint8_t PIXFMT = 0x3A;
37static constexpr uint8_t BRIGHTNESS = 0x51;
38static constexpr uint8_t SWIRE1 = 0x5A;
39static constexpr uint8_t SWIRE2 = 0x5B;
40static constexpr uint8_t PAGESEL = 0xFE;
42static constexpr uint8_t MADCTL_MY = 0x80;
43static constexpr uint8_t MADCTL_MX = 0x40;
44static constexpr uint8_t MADCTL_MV = 0x20;
45static constexpr uint8_t MADCTL_RGB = 0x00;
46static constexpr uint8_t MADCTL_BGR = 0x08;
47static constexpr uint8_t MADCTL_XFLIP = 0x02;
48static constexpr uint8_t MADCTL_YFLIP = 0x01;
49static constexpr uint16_t MADCTL_FLIP_FLAG = 0x100;
51static constexpr uint8_t DELAY_FLAG = 0xFF;
53static inline void put16_be(uint8_t *buf, uint16_t value) {
73void internal_dump_config(
const char *model,
int width,
int height,
int offset_width,
int offset_height, uint8_t madctl,
74 bool invert_colors,
int display_bits,
bool is_big_endian,
const optional<uint8_t> &brightness,
76 bool has_hardware_rotation);
103 int WIDTH,
int HEIGHT,
int OFFSET_WIDTH,
int OFFSET_HEIGHT,
int PAD_WIDTH,
int PAD_HEIGHT, uint16_t MADCTL,
104 bool HAS_HARDWARE_ROTATION>
107 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
108 spi::DATA_RATE_1MHZ> {
129 if constexpr (HAS_HARDWARE_ROTATION) {
146 if (this->
dc_pin_ !=
nullptr) {
152 pin->digital_write(
true);
165 auto when =
millis() + 120;
169 while (index !=
len) {
170 if (
len - index < 2) {
171 esph_log_e(TAG,
"Malformed init sequence");
177 if (
x == DELAY_FLAG) {
181 esph_log_d(TAG,
"Delay %dms", cmd);
184 uint8_t num_args =
x & 0x7F;
185 if (
len - index < num_args) {
186 esph_log_e(TAG,
"Malformed init sequence");
206 this->
data_rate_, BUS_TYPE, HAS_HARDWARE_ROTATION);
216 if (w <= 0 ||
h <= 0)
229 if constexpr (HAS_HARDWARE_ROTATION)
234 if constexpr (HAS_HARDWARE_ROTATION)
285 for (
size_t i = 0; i !=
len; i++) {
304 uint8_t madctl = (uint8_t) MADCTL;
305 constexpr bool use_flips = (MADCTL & MADCTL_FLIP_FLAG) != 0;
306 constexpr uint8_t x_mask = use_flips ? MADCTL_XFLIP : MADCTL_MX;
307 constexpr uint8_t y_mask = use_flips ? MADCTL_YFLIP : MADCTL_MY;
308 if constexpr (HAS_HARDWARE_ROTATION) {
326 esph_log_d(TAG,
"Setting MADCTL for rotation %d, value %X", this->
rotation_, madctl);
331 if constexpr (HAS_HARDWARE_ROTATION) {
334 return OFFSET_HEIGHT;
347 if constexpr (HAS_HARDWARE_ROTATION) {
359 return OFFSET_HEIGHT;
364 esph_log_v(TAG,
"Set addr %d/%d, %d/%d", x1, y1, x2, y2);
371 put16_be(buf + 2, y2);
374 put16_be(buf + 2, x2);
410 for (
size_t y = 0;
y !=
h;
y++) {
432 esph_log_e(TAG,
"Unsupported color depth, bit order or rotation");
446 if constexpr (BUFFERPIXEL == DISPLAYPIXEL) {
451 uint8_t dbuffer[DISPLAYPIXEL * 48];
452 uint8_t *dptr = dbuffer;
454 for (
size_t y = 0;
y !=
static_cast<size_t>(
h);
y++) {
455 for (
size_t x = 0;
x !=
static_cast<size_t>(
w);
x++) {
456 auto color_val =
ptr[
y * stride +
x];
459 if constexpr (IS_BIG_ENDIAN) {
460 *dptr++ = color_val & 0xF8;
461 *dptr++ = ((color_val & 0x7) << 5) | (color_val & 0xE000) >> 11;
462 *dptr++ = (color_val >> 5) & 0xF8;
464 *dptr++ = (color_val >> 8) & 0xF8;
465 *dptr++ = (color_val & 0x7E0) >> 3;
466 *dptr++ = color_val << 3;
470 *dptr++ = color_val << 6;
471 *dptr++ = (color_val & 0x1C) << 3;
472 *dptr++ = (color_val & 0xE0);
474 if constexpr (IS_BIG_ENDIAN) {
475 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
476 *dptr++ = (color_val & 3) << 3;
478 *dptr++ = (color_val & 3) << 3;
479 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
483 if (dptr == dbuffer +
sizeof(dbuffer)) {
490 if (dptr != dbuffer) {
532 uint16_t WIDTH, uint16_t HEIGHT,
int OFFSET_WIDTH,
int OFFSET_HEIGHT,
int PAD_WIDTH,
int PAD_HEIGHT,
533 uint16_t MADCTL,
bool HAS_HARDWARE_ROTATION,
int FRACTION,
unsigned ROUNDING>
535 :
public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
536 OFFSET_HEIGHT, PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION> {
546 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH, OFFSET_HEIGHT,
547 PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION>
::dump_config();
550 " Buffer pixels: %d bits\n"
551 " Buffer fraction: 1/%d\n"
552 " Buffer bytes: %zu\n"
553 " Draw rounding: %u",
554 this->
rotation_, BUFFERPIXEL * 8, FRACTION,
559 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH, OFFSET_HEIGHT,
560 PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION>
::setup();
563 if (this->
buffer_ ==
nullptr) {
564 this->
mark_failed(LOG_STR(
"Buffer allocation failed"));
569#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
579#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
589 }
else if (this->
page_ !=
nullptr) {
595#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
596 esph_log_v(TAG,
"Drawing from line %d took %dms", this->
start_line_,
millis() - lap);
601 esph_log_v(TAG,
"x_low %d, y_low %d, x_high %d, y_high %d", this->
x_low_, this->
y_low_, this->
x_high_,
618#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
619 esph_log_v(TAG,
"Write to display took %dms",
millis() - lap);
623#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
624 esph_log_v(TAG,
"Total update took %dms",
millis() - now);
635 if constexpr (not HAS_HARDWARE_ROTATION) {
700 const int first_col = std::max(0, -x_start);
702 const int first_row = std::max<int>(0, this->
start_line_ - y_start);
703 const int end_row = std::min<int>(
h, this->
end_line_ - y_start);
704 if (first_col >= end_col || first_row >= end_row)
708 for (
int y = first_row;
y != end_row;
y++) {
709 memcpy(this->
buffer_ + (y_start +
y - this->
start_line_) * buffer_stride + x_start + first_col,
711 (end_col - first_col) *
sizeof(BUFFERTYPE));
713 this->
x_low_ = std::min<int>(this->
x_low_, x_start + first_col);
714 this->
y_low_ = std::min<int>(this->
y_low_, y_start + first_row);
715 this->
x_high_ = std::max<int>(this->
x_high_, x_start + end_col - 1);
716 this->
y_high_ = std::max<int>(this->
y_high_, y_start + end_row - 1);
726 return (color.
red & 0xE0) | (color.
g & 0xE0) >> 3 | color.
b >> 6;
728 if constexpr (IS_BIG_ENDIAN) {
729 return (color.
r & 0xF8) | color.
g >> 5 | (color.
g & 0x1C) << 11 | (color.
b & 0xF8) << 5;
731 return (color.
r & 0xF8) << 8 | (color.
g & 0xFC) << 3 | color.
b >> 3;
734 return static_cast<BUFFERTYPE
>(0);
void mark_failed()
Mark this component as failed.
virtual void digital_write(bool value)=0
An STL allocator that uses SPI or internal RAM.
virtual bool pixel_axes_swapped() const
True when draw_pixel_at() swaps x and y before writing, so pixels that are neighbours along x land a ...
int int int const uint8_t ColorOrder ColorBitness bitness
virtual void clear()
Clear the entire screen by filling it with OFF pixels.
int int int const uint8_t ColorOrder ColorBitness bool int int y_offset
virtual void fill(Color color)
Fill the entire screen with the given color.
int int int const uint8_t ColorOrder ColorBitness bool int int int x_pad
int int int const uint8_t * ptr
bool ESPHOME_ALWAYS_INLINE is_point_clipped(int x, int y) const
Whether (x, y) falls outside the active clipping rectangle.
int int int const uint8_t ColorOrder ColorBitness bool big_endian
Rect get_clipping() const
Get the current the clipping rectangle.
int int int const uint8_t ColorOrder ColorBitness bool int x_offset
DisplayRotation rotation_
virtual void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad)
Draws a block for draw_pixels_at(); the parameters are the same.
const display_writer_t & get_writer() const
virtual void set_brightness(uint8_t brightness)=0
Class for MIPI SPI displays with a buffer.
void draw_pixel_at(int x, int y, Color color) override
static constexpr size_t round_buffer(size_t size)
void draw_pixels_slow(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
bool pixel_axes_swapped() const override
void fill(Color color) override
static BUFFERTYPE convert_color(const Color &color)
void dump_config() override
Base class for MIPI SPI displays.
uint16_t get_offset_height_() const
void write_to_display_(int x_start, int y_start, int w, int h, const BUFFERTYPE *ptr, int x_offset, int y_offset, int x_pad)
Writes a buffer to the display.
int get_width_internal() override
void set_init_sequence(const uint8_t *sequence, size_t len)
const uint8_t * init_sequence_
void set_brightness(uint8_t brightness) override
static PixelMode get_pixel_mode(display::ColorBitness bitness)
void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len)
void set_reset_pin(GPIOPin *reset_pin)
optional< uint8_t > brightness_
void set_invert_colors(bool invert_colors)
int get_height_internal() override
void dump_config() override
uint16_t get_offset_width_() const
int get_height() override
void draw_pixel_at(int x, int y, Color color) override
void write_command_(uint8_t cmd)
void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t stride)
Writes a buffer to the display.
display::DisplayType get_display_type() override
void set_dc_pin(GPIOPin *dc_pin)
void set_rotation(display::DisplayRotation rotation) override
void write_command_(uint8_t cmd, uint8_t data)
void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
bool is_native_block_(display::ColorBitness bitness, bool big_endian) const
size_t init_sequence_len_
void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
void set_model(const char *model)
virtual void draw_pixels_slow(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad)
std::vector< GPIOPin * > enable_pins_
void set_enable_pins(std::vector< GPIOPin * > enable_pins)
The SPIDevice is what components using the SPI will create.
void spi_setup() override
void write_byte(uint8_t data)
void write_array(const uint8_t *data, size_t length)
void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, const uint8_t *data, size_t length, uint8_t bus_width=1)
@ DISPLAY_ROTATION_0_DEGREES
@ DISPLAY_ROTATION_270_DEGREES
@ DISPLAY_ROTATION_180_DEGREES
@ DISPLAY_ROTATION_90_DEGREES
constexpr bool rotation_swaps_axes(DisplayRotation rotation)
Whether a rotation swaps the x and y axes.
void internal_dump_config(const char *model, int width, int height, int offset_width, int offset_height, uint8_t madctl, bool invert_colors, int display_bits, bool is_big_endian, const optional< uint8_t > &brightness, GPIOPin *cs, GPIOPin *reset, GPIOPin *dc, int spi_mode, uint32_t data_rate, int bus_width, bool has_hardware_rotation)
ESPHOME_LOG_TAG(TAG, "display.mipi_spi")
T clamp_at_most(T value, U max)
void progmem_memcpy(void *dst, const void *src, size_t len)
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
T clamp_at_least(T value, U min)
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
uint8_t progmem_read_byte(const uint8_t *addr)