13constexpr static const char *
const TAG =
"display.mipi_spi";
16static constexpr size_t MIPI_SPI_MAX_CMD_LOG_BYTES = 64;
19static constexpr uint8_t NORON = 0x13;
22static constexpr uint8_t ALL_ON = 0x23;
23static constexpr uint8_t WRAM = 0x24;
24static constexpr uint8_t MIPI = 0x26;
26static constexpr uint8_t RASET = 0x2B;
27static constexpr uint8_t CASET = 0x2A;
28static constexpr uint8_t WDATA = 0x2C;
29static constexpr uint8_t TEON = 0x35;
31static constexpr uint8_t PIXFMT = 0x3A;
32static constexpr uint8_t BRIGHTNESS = 0x51;
33static constexpr uint8_t SWIRE1 = 0x5A;
34static constexpr uint8_t SWIRE2 = 0x5B;
35static constexpr uint8_t PAGESEL = 0xFE;
40static constexpr uint8_t MADCTL_RGB = 0x00;
47static inline void put16_be(uint8_t *buf, uint16_t value) {
67void internal_dump_config(
const char *model,
int width,
int height,
int offset_width,
int offset_height, uint8_t madctl,
68 bool invert_colors,
int display_bits,
bool is_big_endian,
const optional<uint8_t> &brightness,
86 int WIDTH,
int HEIGHT,
int OFFSET_WIDTH,
int OFFSET_HEIGHT>
88 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
89 spi::DATA_RATE_1MHZ> {
115 if (this->
dc_pin_ !=
nullptr) {
121 pin->digital_write(
true);
133 auto when =
millis() + 120;
137 while (index != vec.size()) {
138 if (vec.size() - index < 2) {
139 esph_log_e(TAG,
"Malformed init sequence");
143 uint8_t cmd = vec[index++];
144 uint8_t
x = vec[index++];
145 if (
x == DELAY_FLAG) {
146 esph_log_d(TAG,
"Delay %dms", cmd);
149 uint8_t num_args =
x & 0x7F;
150 if (vec.size() - index < num_args) {
151 esph_log_e(TAG,
"Malformed init sequence");
155 auto arg_byte = vec[index];
161 esph_log_d(TAG,
"Sleep %dms",
duration);
179 const auto *ptr = vec.data() + index;
180 esph_log_d(TAG,
"Command %02X, length %d, byte %02X", cmd, num_args, arg_byte);
183 if (cmd == SLEEP_OUT)
197 if (w <= 0 ||
h <= 0)
199 if (
get_pixel_mode(bitness) != BUFFERPIXEL || big_endian != IS_BIG_ENDIAN) {
201 esph_log_e(TAG,
"Unsupported color depth or bit order");
204 this->
write_to_display_(x_start, y_start, w,
h,
reinterpret_cast<const BUFFERTYPE *
>(ptr), x_offset, y_offset,
222#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
258 for (
size_t i = 0; i !=
len; i++) {
278 esph_log_v(TAG,
"Set addr %d/%d, %d/%d", x1, y1, x2, y2);
285 put16_be(buf + 2, y2);
288 put16_be(buf + 2, x2);
324 for (
size_t y = 0;
y !=
static_cast<size_t>(
h);
y++) {
343 void write_to_display_(
int x_start,
int y_start,
int w,
int h,
const BUFFERTYPE *ptr,
int x_offset,
int y_offset,
347 ptr += y_offset * (x_offset + w + x_pad) + x_offset;
348 if constexpr (BUFFERPIXEL == DISPLAYPIXEL) {
350 x_pad *
sizeof(BUFFERTYPE));
353 uint8_t dbuffer[DISPLAYPIXEL * 48];
354 uint8_t *dptr = dbuffer;
355 auto stride = x_offset + w + x_pad;
356 for (
size_t y = 0;
y !=
static_cast<size_t>(
h);
y++) {
357 for (
size_t x = 0;
x !=
static_cast<size_t>(w);
x++) {
358 auto color_val = ptr[
y * stride +
x];
361 if constexpr (IS_BIG_ENDIAN) {
362 *dptr++ = color_val & 0xF8;
363 *dptr++ = ((color_val & 0x7) << 5) | (color_val & 0xE000) >> 11;
364 *dptr++ = (color_val >> 5) & 0xF8;
366 *dptr++ = (color_val >> 8) & 0xF8;
367 *dptr++ = (color_val & 0x7E0) >> 3;
368 *dptr++ = color_val << 3;
372 *dptr++ = color_val << 6;
373 *dptr++ = (color_val & 0x1C) << 3;
374 *dptr++ = (color_val & 0xE0);
376 if constexpr (IS_BIG_ENDIAN) {
377 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
378 *dptr++ = (color_val & 3) << 3;
380 *dptr++ = (color_val & 3) << 3;
381 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
385 if (dptr == dbuffer +
sizeof(dbuffer)) {
392 if (dptr != dbuffer) {
431 int FRACTION,
unsigned ROUNDING>
432class MipiSpiBuffer :
public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT,
433 OFFSET_WIDTH, OFFSET_HEIGHT> {
438 static constexpr unsigned BUFFER_WIDTH = (WIDTH + ROUNDING - 1) / ROUNDING * ROUNDING;
439 static constexpr unsigned BUFFER_HEIGHT = (HEIGHT + ROUNDING - 1) / ROUNDING * ROUNDING;
444 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
448 " Buffer pixels: %d bits\n"
449 " Buffer fraction: 1/%d\n"
450 " Buffer bytes: %zu\n"
451 " Draw rounding: %u",
452 this->
rotation_, BUFFERPIXEL * 8, FRACTION,
457 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
461 if (this->
buffer_ ==
nullptr) {
462 this->
mark_failed(LOG_STR(
"Buffer allocation failed"));
467#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
476#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
483 if (this->
page_ !=
nullptr) {
490#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
491 esph_log_v(TAG,
"Drawing from line %d took %dms", this->
start_line_,
millis() - lap);
496 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_,
501 this->
x_high_ = (this->
x_high_ + ROUNDING) / ROUNDING * ROUNDING - 1;
502 this->
y_high_ = (this->
y_high_ + ROUNDING) / ROUNDING * ROUNDING - 1;
506 this->y_low_ - this->
start_line_, BUFFER_WIDTH - w);
508 this->x_low_ = WIDTH;
509 this->y_low_ = HEIGHT;
512#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
513 esph_log_v(TAG,
"Write to display took %dms",
millis() - lap);
517#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
518 esph_log_v(TAG,
"Total update took %dms",
millis() - now);
591 return (color.
red & 0xE0) | (color.
g & 0xE0) >> 3 | color.
b >> 6;
593 if constexpr (IS_BIG_ENDIAN) {
594 return (color.
r & 0xF8) | color.
g >> 5 | (color.
g & 0x1C) << 11 | (color.
b & 0xF8) << 5;
596 return (color.
r & 0xF8) << 8 | (color.
g & 0xFC) << 3 | color.
b >> 3;
599 return static_cast<BUFFERTYPE
>(0);
virtual 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 void clear()
Clear the entire screen by filling it with OFF pixels.
virtual void fill(Color color)
Fill the entire screen with the given color.
Rect get_clipping() const
Get the current the clipping rectangle.
DisplayRotation rotation_
const display_writer_t & get_writer() const
Class for MIPI SPI displays with a buffer.
void draw_pixel_at(int x, int y, Color color) override
int get_height() override
static constexpr unsigned BUFFER_HEIGHT
static BUFFERTYPE convert_color(const Color &color)
static constexpr unsigned BUFFER_WIDTH
void dump_config() override
void fill(Color color) override
static void rotate_coordinates(int &x, int &y)
Base class for MIPI SPI displays.
std::vector< GPIOPin * > enable_pins_
void write_command_(uint8_t cmd, uint8_t data)
int get_height_internal() override
static PixelMode get_pixel_mode(display::ColorBitness bitness)
void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
int get_width_internal() override
void set_init_sequence(const std::vector< uint8_t > &sequence)
void set_reset_pin(GPIOPin *reset_pin)
void write_command_(uint8_t cmd)
void set_dc_pin(GPIOPin *dc_pin)
void set_invert_colors(bool invert_colors)
void set_brightness(uint8_t brightness)
void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len)
void set_model(const char *model)
void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t pad)
Writes a buffer to the display.
void dump_config() override
display::DisplayType get_display_type() override
void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
void draw_pixel_at(int x, int y, Color color) override
void set_enable_pins(std::vector< GPIOPin * > enable_pins)
optional< uint8_t > brightness_
std::vector< uint8_t > init_sequence_
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.
value_type const & value() const
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_270_DEGREES
@ DISPLAY_ROTATION_180_DEGREES
@ DISPLAY_ROTATION_90_DEGREES
const uint8_t MADCTL_YFLIP
const uint8_t MADCTL_XFLIP
const uint8_t SW_RESET_CMD
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)
Providing packet encoding functions for exchanging data with a remote host.
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).
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 IRAM_ATTR HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()