ESPHome 2025.12.0-dev
Loading...
Searching...
No Matches
mipi_spi.h
Go to the documentation of this file.
1#pragma once
2
3#include <utility>
4
8
9namespace esphome {
10namespace mipi_spi {
11
12constexpr static const char *const TAG = "display.mipi_spi";
13static constexpr uint8_t SW_RESET_CMD = 0x01;
14static constexpr uint8_t SLEEP_OUT = 0x11;
15static constexpr uint8_t NORON = 0x13;
16static constexpr uint8_t INVERT_OFF = 0x20;
17static constexpr uint8_t INVERT_ON = 0x21;
18static constexpr uint8_t ALL_ON = 0x23;
19static constexpr uint8_t WRAM = 0x24;
20static constexpr uint8_t MIPI = 0x26;
21static constexpr uint8_t DISPLAY_ON = 0x29;
22static constexpr uint8_t RASET = 0x2B;
23static constexpr uint8_t CASET = 0x2A;
24static constexpr uint8_t WDATA = 0x2C;
25static constexpr uint8_t TEON = 0x35;
26static constexpr uint8_t MADCTL_CMD = 0x36;
27static constexpr uint8_t PIXFMT = 0x3A;
28static constexpr uint8_t BRIGHTNESS = 0x51;
29static constexpr uint8_t SWIRE1 = 0x5A;
30static constexpr uint8_t SWIRE2 = 0x5B;
31static constexpr uint8_t PAGESEL = 0xFE;
32
33static constexpr uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top
34static constexpr uint8_t MADCTL_MX = 0x40; // Bit 6 Right to left
35static constexpr uint8_t MADCTL_MV = 0x20; // Bit 5 Swap axes
36static constexpr uint8_t MADCTL_RGB = 0x00; // Bit 3 Red-Green-Blue pixel order
37static constexpr uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel order
38static constexpr uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally
39static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically
40
41static constexpr uint8_t DELAY_FLAG = 0xFF;
42// store a 16 bit value in a buffer, big endian.
43static inline void put16_be(uint8_t *buf, uint16_t value) {
44 buf[0] = value >> 8;
45 buf[1] = value;
46}
47
48// Buffer mode, conveniently also the number of bytes in a pixel
54
55enum BusType {
59 BUS_TYPE_SINGLE_16 = 16, // Single bit bus, but 16 bits per transfer
60};
61
76template<typename BUFFERTYPE, PixelMode BUFFERPIXEL, bool IS_BIG_ENDIAN, PixelMode DISPLAYPIXEL, BusType BUS_TYPE,
77 int WIDTH, int HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT>
79 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
80 spi::DATA_RATE_1MHZ> {
81 public:
82 MipiSpi() = default;
83 void update() override { this->stop_poller(); }
84 void draw_pixel_at(int x, int y, Color color) override {}
85 void set_model(const char *model) { this->model_ = model; }
86 void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
87 void set_enable_pins(std::vector<GPIOPin *> enable_pins) { this->enable_pins_ = std::move(enable_pins); }
88 void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; }
89 void set_invert_colors(bool invert_colors) {
90 this->invert_colors_ = invert_colors;
91 this->reset_params_();
92 }
93 void set_brightness(uint8_t brightness) {
94 this->brightness_ = brightness;
95 this->reset_params_();
96 }
98
99 int get_width_internal() override { return WIDTH; }
100 int get_height_internal() override { return HEIGHT; }
101 void set_init_sequence(const std::vector<uint8_t> &sequence) { this->init_sequence_ = sequence; }
102
103 // reset the display, and write the init sequence
104 void setup() override {
105 this->spi_setup();
106 if (this->dc_pin_ != nullptr) {
107 this->dc_pin_->setup();
108 this->dc_pin_->digital_write(false);
109 }
110 for (auto *pin : this->enable_pins_) {
111 pin->setup();
112 pin->digital_write(true);
113 }
114 if (this->reset_pin_ != nullptr) {
115 this->reset_pin_->setup();
116 this->reset_pin_->digital_write(true);
117 delay(5);
118 this->reset_pin_->digital_write(false);
119 delay(5);
120 this->reset_pin_->digital_write(true);
121 }
122
123 // need to know when the display is ready for SLPOUT command - will be 120ms after reset
124 auto when = millis() + 120;
125 delay(10);
126 size_t index = 0;
127 auto &vec = this->init_sequence_;
128 while (index != vec.size()) {
129 if (vec.size() - index < 2) {
130 esph_log_e(TAG, "Malformed init sequence");
131 this->mark_failed();
132 return;
133 }
134 uint8_t cmd = vec[index++];
135 uint8_t x = vec[index++];
136 if (x == DELAY_FLAG) {
137 esph_log_d(TAG, "Delay %dms", cmd);
138 delay(cmd);
139 } else {
140 uint8_t num_args = x & 0x7F;
141 if (vec.size() - index < num_args) {
142 esph_log_e(TAG, "Malformed init sequence");
143 this->mark_failed();
144 return;
145 }
146 auto arg_byte = vec[index];
147 switch (cmd) {
148 case SLEEP_OUT: {
149 // are we ready, boots?
150 int duration = when - millis();
151 if (duration > 0) {
152 esph_log_d(TAG, "Sleep %dms", duration);
154 }
155 } break;
156
157 case INVERT_ON:
158 this->invert_colors_ = true;
159 break;
160 case MADCTL_CMD:
161 this->madctl_ = arg_byte;
162 break;
163 case BRIGHTNESS:
164 this->brightness_ = arg_byte;
165 break;
166
167 default:
168 break;
169 }
170 const auto *ptr = vec.data() + index;
171 esph_log_d(TAG, "Command %02X, length %d, byte %02X", cmd, num_args, arg_byte);
172 this->write_command_(cmd, ptr, num_args);
173 index += num_args;
174 if (cmd == SLEEP_OUT)
175 delay(10);
176 }
177 }
178 // init sequence no longer needed
179 this->init_sequence_.clear();
180 }
181
182 // Drawing operations
183
184 void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
185 display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override {
186 if (this->is_failed())
187 return;
188 if (w <= 0 || h <= 0)
189 return;
190 if (get_pixel_mode(bitness) != BUFFERPIXEL || big_endian != IS_BIG_ENDIAN) {
191 // note that the usual logging macros are banned in header files, so use their replacement
192 esph_log_e(TAG, "Unsupported color depth or bit order");
193 return;
194 }
195 this->write_to_display_(x_start, y_start, w, h, reinterpret_cast<const BUFFERTYPE *>(ptr), x_offset, y_offset,
196 x_pad);
197 }
198
199 void dump_config() override {
200 esph_log_config(TAG,
201 "MIPI_SPI Display\n"
202 " Model: %s\n"
203 " Width: %u\n"
204 " Height: %u",
205 this->model_, WIDTH, HEIGHT);
206 if constexpr (OFFSET_WIDTH != 0)
207 esph_log_config(TAG, " Offset width: %u", OFFSET_WIDTH);
208 if constexpr (OFFSET_HEIGHT != 0)
209 esph_log_config(TAG, " Offset height: %u", OFFSET_HEIGHT);
210 esph_log_config(TAG,
211 " Swap X/Y: %s\n"
212 " Mirror X: %s\n"
213 " Mirror Y: %s\n"
214 " Invert colors: %s\n"
215 " Color order: %s\n"
216 " Display pixels: %d bits\n"
217 " Endianness: %s\n",
218 YESNO(this->madctl_ & MADCTL_MV), YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)),
219 YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)), YESNO(this->invert_colors_),
220 this->madctl_ & MADCTL_BGR ? "BGR" : "RGB", DISPLAYPIXEL * 8, IS_BIG_ENDIAN ? "Big" : "Little");
221 if (this->brightness_.has_value())
222 esph_log_config(TAG, " Brightness: %u", this->brightness_.value());
223 if (this->cs_ != nullptr)
224 esph_log_config(TAG, " CS Pin: %s", this->cs_->dump_summary().c_str());
225 if (this->reset_pin_ != nullptr)
226 esph_log_config(TAG, " Reset Pin: %s", this->reset_pin_->dump_summary().c_str());
227 if (this->dc_pin_ != nullptr)
228 esph_log_config(TAG, " DC Pin: %s", this->dc_pin_->dump_summary().c_str());
229 esph_log_config(TAG,
230 " SPI Mode: %d\n"
231 " SPI Data rate: %dMHz\n"
232 " SPI Bus width: %d",
233 this->mode_, static_cast<unsigned>(this->data_rate_ / 1000000), BUS_TYPE);
234 }
235
236 protected:
237 /* METHODS */
238 // convenience functions to write commands with or without data
239 void write_command_(uint8_t cmd, uint8_t data) { this->write_command_(cmd, &data, 1); }
240 void write_command_(uint8_t cmd) { this->write_command_(cmd, &cmd, 0); }
241
242 // Writes a command to the display, with the given bytes.
243 void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) {
244 esph_log_v(TAG, "Command %02X, length %d, bytes %s", cmd, len, format_hex_pretty(bytes, len).c_str());
245 if constexpr (BUS_TYPE == BUS_TYPE_QUAD) {
246 this->enable();
247 this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len);
248 this->disable();
249 } else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) {
250 this->dc_pin_->digital_write(false);
251 this->enable();
252 this->write_cmd_addr_data(0, 0, 0, 0, &cmd, 1, 8);
253 this->disable();
254 this->dc_pin_->digital_write(true);
255 if (len != 0) {
256 this->enable();
257 this->write_cmd_addr_data(0, 0, 0, 0, bytes, len, 8);
258 this->disable();
259 }
260 } else if constexpr (BUS_TYPE == BUS_TYPE_SINGLE) {
261 this->dc_pin_->digital_write(false);
262 this->enable();
263 this->write_byte(cmd);
264 this->disable();
265 this->dc_pin_->digital_write(true);
266 if (len != 0) {
267 this->enable();
268 this->write_array(bytes, len);
269 this->disable();
270 }
271 } else if constexpr (BUS_TYPE == BUS_TYPE_SINGLE_16) {
272 this->dc_pin_->digital_write(false);
273 this->enable();
274 this->write_byte(cmd);
275 this->disable();
276 this->dc_pin_->digital_write(true);
277 for (size_t i = 0; i != len; i++) {
278 this->enable();
279 this->write_byte(0);
280 this->write_byte(bytes[i]);
281 this->disable();
282 }
283 }
284 }
285
286 // write changed parameters to the display
288 if (!this->is_ready())
289 return;
290 this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
291 if (this->brightness_.has_value())
292 this->write_command_(BRIGHTNESS, this->brightness_.value());
293 }
294
295 // set the address window for the next data write
296 void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
297 esph_log_v(TAG, "Set addr %d/%d, %d/%d", x1, y1, x2, y2);
298 uint8_t buf[4];
299 x1 += OFFSET_WIDTH;
300 x2 += OFFSET_WIDTH;
301 y1 += OFFSET_HEIGHT;
302 y2 += OFFSET_HEIGHT;
303 put16_be(buf, y1);
304 put16_be(buf + 2, y2);
305 this->write_command_(RASET, buf, sizeof buf);
306 put16_be(buf, x1);
307 put16_be(buf + 2, x2);
308 this->write_command_(CASET, buf, sizeof buf);
309 if constexpr (BUS_TYPE != BUS_TYPE_QUAD) {
310 this->write_command_(WDATA);
311 }
312 }
313
314 // map the display color bitness to the pixel mode
316 switch (bitness) {
318 return PIXEL_MODE_18; // 18 bits per pixel
320 return PIXEL_MODE_16; // 16 bits per pixel
321 default:
322 return PIXEL_MODE_8; // Default to 8 bits per pixel
323 }
324 }
325
333 void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t pad) {
334 if (pad == 0) {
335 if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) {
336 this->write_array(ptr, w * h);
337 } else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) {
338 this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w * h, 4);
339 } else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) {
340 this->write_cmd_addr_data(0, 0, 0, 0, ptr, w * h, 8);
341 }
342 } else {
343 for (size_t y = 0; y != static_cast<size_t>(h); y++) {
344 if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) {
345 this->write_array(ptr, w);
346 } else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) {
347 this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w, 4);
348 } else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) {
349 this->write_cmd_addr_data(0, 0, 0, 0, ptr, w, 8);
350 }
351 ptr += w + pad;
352 }
353 }
354 }
355
362 void write_to_display_(int x_start, int y_start, int w, int h, const BUFFERTYPE *ptr, int x_offset, int y_offset,
363 int x_pad) {
364 this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1);
365 this->enable();
366 ptr += y_offset * (x_offset + w + x_pad) + x_offset;
367 if constexpr (BUFFERPIXEL == DISPLAYPIXEL) {
368 this->write_display_data_(reinterpret_cast<const uint8_t *>(ptr), w * sizeof(BUFFERTYPE), h,
369 x_pad * sizeof(BUFFERTYPE));
370 } else {
371 // type conversion required, do it in chunks
372 uint8_t dbuffer[DISPLAYPIXEL * 48];
373 uint8_t *dptr = dbuffer;
374 auto stride = x_offset + w + x_pad; // stride in pixels
375 for (size_t y = 0; y != static_cast<size_t>(h); y++) {
376 for (size_t x = 0; x != static_cast<size_t>(w); x++) {
377 auto color_val = ptr[y * stride + x];
378 if constexpr (DISPLAYPIXEL == PIXEL_MODE_18 && BUFFERPIXEL == PIXEL_MODE_16) {
379 // 16 to 18 bit conversion
380 if constexpr (IS_BIG_ENDIAN) {
381 *dptr++ = color_val & 0xF8;
382 *dptr++ = ((color_val & 0x7) << 5) | (color_val & 0xE000) >> 11;
383 *dptr++ = (color_val >> 5) & 0xF8;
384 } else {
385 *dptr++ = (color_val >> 8) & 0xF8; // Blue
386 *dptr++ = (color_val & 0x7E0) >> 3;
387 *dptr++ = color_val << 3;
388 }
389 } else if constexpr (DISPLAYPIXEL == PIXEL_MODE_18 && BUFFERPIXEL == PIXEL_MODE_8) {
390 // 8 bit to 18 bit conversion
391 *dptr++ = color_val << 6; // Blue
392 *dptr++ = (color_val & 0x1C) << 3; // Green
393 *dptr++ = (color_val & 0xE0); // Red
394 } else if constexpr (DISPLAYPIXEL == PIXEL_MODE_16 && BUFFERPIXEL == PIXEL_MODE_8) {
395 if constexpr (IS_BIG_ENDIAN) {
396 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
397 *dptr++ = (color_val & 3) << 3;
398 } else {
399 *dptr++ = (color_val & 3) << 3;
400 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
401 }
402 }
403 // buffer full? Flush.
404 if (dptr == dbuffer + sizeof(dbuffer)) {
405 this->write_display_data_(dbuffer, sizeof(dbuffer), 1, 0);
406 dptr = dbuffer;
407 }
408 }
409 }
410 // flush any remaining data
411 if (dptr != dbuffer) {
412 this->write_display_data_(dbuffer, dptr - dbuffer, 1, 0);
413 }
414 }
415 this->disable();
416 }
417
418 /* PROPERTIES */
419
420 // GPIO pins
422 std::vector<GPIOPin *> enable_pins_{};
423 GPIOPin *dc_pin_{nullptr};
424
425 // other properties set by configuration
428 const char *model_{"Unknown"};
429 std::vector<uint8_t> init_sequence_{};
430 uint8_t madctl_{};
431};
432
448template<typename BUFFERTYPE, PixelMode BUFFERPIXEL, bool IS_BIG_ENDIAN, PixelMode DISPLAYPIXEL, BusType BUS_TYPE,
449 uint16_t WIDTH, uint16_t HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, display::DisplayRotation ROTATION,
450 int FRACTION, unsigned ROUNDING>
451class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT,
452 OFFSET_WIDTH, OFFSET_HEIGHT> {
453 public:
454 // these values define the buffer size needed to write in accordance with the chip pixel alignment
455 // requirements. If the required rounding does not divide the width and height, we round up to the next multiple and
456 // ignore the extra columns and rows when drawing, but use them to write to the display.
457 static constexpr unsigned BUFFER_WIDTH = (WIDTH + ROUNDING - 1) / ROUNDING * ROUNDING;
458 static constexpr unsigned BUFFER_HEIGHT = (HEIGHT + ROUNDING - 1) / ROUNDING * ROUNDING;
459
460 MipiSpiBuffer() { this->rotation_ = ROTATION; }
461
462 void dump_config() override {
463 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
464 OFFSET_HEIGHT>::dump_config();
465 esph_log_config(TAG,
466 " Rotation: %d°\n"
467 " Buffer pixels: %d bits\n"
468 " Buffer fraction: 1/%d\n"
469 " Buffer bytes: %zu\n"
470 " Draw rounding: %u",
471 this->rotation_, BUFFERPIXEL * 8, FRACTION,
472 sizeof(BUFFERTYPE) * BUFFER_WIDTH * BUFFER_HEIGHT / FRACTION, ROUNDING);
473 }
474
475 void setup() override {
476 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
477 OFFSET_HEIGHT>::setup();
478 RAMAllocator<BUFFERTYPE> allocator{};
479 this->buffer_ = allocator.allocate(BUFFER_WIDTH * BUFFER_HEIGHT / FRACTION);
480 if (this->buffer_ == nullptr) {
481 this->mark_failed("Buffer allocation failed");
482 }
483 }
484
485 void update() override {
486#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
487 auto now = millis();
488#endif
489 if (this->is_failed()) {
490 return;
491 }
492 // for updates with a small buffer, we repeatedly call the writer_ function, clipping the height to a fraction of
493 // the display height,
494 for (this->start_line_ = 0; this->start_line_ < HEIGHT; this->start_line_ += HEIGHT / FRACTION) {
495#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
496 auto lap = millis();
497#endif
498 this->end_line_ = this->start_line_ + HEIGHT / FRACTION;
499 if (this->auto_clear_enabled_) {
500 this->clear();
501 }
502 if (this->page_ != nullptr) {
503 this->page_->get_writer()(*this);
504 } else if (this->writer_.has_value()) {
505 (*this->writer_)(*this);
506 } else {
507 this->test_card();
508 }
509#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
510 esph_log_v(TAG, "Drawing from line %d took %dms", this->start_line_, millis() - lap);
511 lap = millis();
512#endif
513 if (this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
514 return;
515 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_,
516 this->y_high_);
517 // Some chips require that the drawing window be aligned on certain boundaries
518 this->x_low_ = this->x_low_ / ROUNDING * ROUNDING;
519 this->y_low_ = this->y_low_ / ROUNDING * ROUNDING;
520 this->x_high_ = (this->x_high_ + ROUNDING) / ROUNDING * ROUNDING - 1;
521 this->y_high_ = (this->y_high_ + ROUNDING) / ROUNDING * ROUNDING - 1;
522 int w = this->x_high_ - this->x_low_ + 1;
523 int h = this->y_high_ - this->y_low_ + 1;
524 this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_,
525 this->y_low_ - this->start_line_, BUFFER_WIDTH - w);
526 // invalidate watermarks
527 this->x_low_ = WIDTH;
528 this->y_low_ = HEIGHT;
529 this->x_high_ = 0;
530 this->y_high_ = 0;
531#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
532 esph_log_v(TAG, "Write to display took %dms", millis() - lap);
533 lap = millis();
534#endif
535 }
536#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
537 esph_log_v(TAG, "Total update took %dms", millis() - now);
538#endif
539 }
540
541 // Draw a pixel at the given coordinates.
542 void draw_pixel_at(int x, int y, Color color) override {
543 if (!this->get_clipping().inside(x, y))
544 return;
546 if (x < 0 || x >= WIDTH || y < this->start_line_ || y >= this->end_line_)
547 return;
548 this->buffer_[(y - this->start_line_) * BUFFER_WIDTH + x] = convert_color(color);
549 if (x < this->x_low_) {
550 this->x_low_ = x;
551 }
552 if (x > this->x_high_) {
553 this->x_high_ = x;
554 }
555 if (y < this->y_low_) {
556 this->y_low_ = y;
557 }
558 if (y > this->y_high_) {
559 this->y_high_ = y;
560 }
561 }
562
563 // Fills the display with a color.
564 void fill(Color color) override {
565 this->x_low_ = 0;
566 this->y_low_ = this->start_line_;
567 this->x_high_ = WIDTH - 1;
568 this->y_high_ = this->end_line_ - 1;
569 std::fill_n(this->buffer_, HEIGHT * BUFFER_WIDTH / FRACTION, convert_color(color));
570 }
571
572 int get_width() override {
574 return HEIGHT;
575 return WIDTH;
576 }
577
578 int get_height() override {
580 return WIDTH;
581 return HEIGHT;
582 }
583
584 protected:
585 // Rotate the coordinates to match the display orientation.
586 static void rotate_coordinates(int &x, int &y) {
587 if constexpr (ROTATION == display::DISPLAY_ROTATION_180_DEGREES) {
588 x = WIDTH - x - 1;
589 y = HEIGHT - y - 1;
590 } else if constexpr (ROTATION == display::DISPLAY_ROTATION_90_DEGREES) {
591 auto tmp = x;
592 x = WIDTH - y - 1;
593 y = tmp;
594 } else if constexpr (ROTATION == display::DISPLAY_ROTATION_270_DEGREES) {
595 auto tmp = y;
596 y = HEIGHT - x - 1;
597 x = tmp;
598 }
599 }
600
601 // Convert a color to the buffer pixel format.
602 static BUFFERTYPE convert_color(const Color &color) {
603 if constexpr (BUFFERPIXEL == PIXEL_MODE_8) {
604 return (color.red & 0xE0) | (color.g & 0xE0) >> 3 | color.b >> 6;
605 } else if constexpr (BUFFERPIXEL == PIXEL_MODE_16) {
606 if constexpr (IS_BIG_ENDIAN) {
607 return (color.r & 0xF8) | color.g >> 5 | (color.g & 0x1C) << 11 | (color.b & 0xF8) << 5;
608 } else {
609 return (color.r & 0xF8) << 8 | (color.g & 0xFC) << 3 | color.b >> 3;
610 }
611 }
612 return static_cast<BUFFERTYPE>(0);
613 }
614
615 BUFFERTYPE *buffer_{};
616 uint16_t x_low_{WIDTH};
617 uint16_t y_low_{HEIGHT};
618 uint16_t x_high_{0};
619 uint16_t y_high_{0};
620 uint16_t start_line_{0};
621 uint16_t end_line_{1};
622};
623
624} // namespace mipi_spi
625} // namespace esphome
uint8_t h
Definition bl0906.h:2
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
bool is_ready() const
virtual void setup()=0
virtual std::string dump_summary() const =0
virtual void digital_write(bool value)=0
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:1084
T * allocate(size_t n)
Definition helpers.h:1104
display_writer_t writer_
Definition display.h:791
virtual void clear()
Clear the entire screen by filling it with OFF pixels.
Definition display.cpp:17
DisplayPage * page_
Definition display.h:792
Rect get_clipping() const
Get the current the clipping rectangle.
Definition display.cpp:715
DisplayRotation rotation_
Definition display.h:790
const display_writer_t & get_writer() const
Definition display.cpp:839
Class for MIPI SPI displays with a buffer.
Definition mipi_spi.h:452
void draw_pixel_at(int x, int y, Color color) override
Definition mipi_spi.h:542
static constexpr unsigned BUFFER_HEIGHT
Definition mipi_spi.h:458
static BUFFERTYPE convert_color(const Color &color)
Definition mipi_spi.h:602
static constexpr unsigned BUFFER_WIDTH
Definition mipi_spi.h:457
void fill(Color color) override
Definition mipi_spi.h:564
static void rotate_coordinates(int &x, int &y)
Definition mipi_spi.h:586
Base class for MIPI SPI displays.
Definition mipi_spi.h:80
std::vector< GPIOPin * > enable_pins_
Definition mipi_spi.h:422
void update() override
Definition mipi_spi.h:83
void write_command_(uint8_t cmd, uint8_t data)
Definition mipi_spi.h:239
int get_height_internal() override
Definition mipi_spi.h:100
static PixelMode get_pixel_mode(display::ColorBitness bitness)
Definition mipi_spi.h:315
void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
Definition mipi_spi.h:296
int get_width_internal() override
Definition mipi_spi.h:99
void set_init_sequence(const std::vector< uint8_t > &sequence)
Definition mipi_spi.h:101
void set_reset_pin(GPIOPin *reset_pin)
Definition mipi_spi.h:86
void write_command_(uint8_t cmd)
Definition mipi_spi.h:240
void set_dc_pin(GPIOPin *dc_pin)
Definition mipi_spi.h:88
void set_invert_colors(bool invert_colors)
Definition mipi_spi.h:89
void setup() override
Definition mipi_spi.h:104
void set_brightness(uint8_t brightness)
Definition mipi_spi.h:93
void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len)
Definition mipi_spi.h:243
void set_model(const char *model)
Definition mipi_spi.h:85
void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t pad)
Writes a buffer to the display.
Definition mipi_spi.h:333
void dump_config() override
Definition mipi_spi.h:199
display::DisplayType get_display_type() override
Definition mipi_spi.h:97
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
Definition mipi_spi.h:184
void draw_pixel_at(int x, int y, Color color) override
Definition mipi_spi.h:84
void set_enable_pins(std::vector< GPIOPin * > enable_pins)
Definition mipi_spi.h:87
optional< uint8_t > brightness_
Definition mipi_spi.h:427
std::vector< uint8_t > init_sequence_
Definition mipi_spi.h:429
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.
Definition mipi_spi.h:362
bool has_value() const
Definition optional.h:92
value_type const & value() const
Definition optional.h:94
uint32_t data_rate_
Definition spi.h:410
The SPIDevice is what components using the SPI will create.
Definition spi.h:427
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)
Definition spi.h:471
uint8_t duration
Definition msa3xx.h:0
@ DISPLAY_ROTATION_270_DEGREES
Definition display.h:138
@ DISPLAY_ROTATION_180_DEGREES
Definition display.h:137
@ DISPLAY_ROTATION_90_DEGREES
Definition display.h:136
const uint8_t MADCTL_CMD
Definition mipi_dsi.h:26
const uint8_t MADCTL_YFLIP
Definition mipi_dsi.h:37
const uint8_t INVERT_ON
Definition mipi_dsi.h:28
const uint8_t DISPLAY_ON
Definition mipi_dsi.h:29
const uint8_t MADCTL_MV
Definition mipi_dsi.h:35
const uint8_t MADCTL_MX
Definition mipi_dsi.h:33
const uint8_t MADCTL_MY
Definition mipi_dsi.h:34
const uint8_t MADCTL_XFLIP
Definition mipi_dsi.h:36
const uint8_t INVERT_OFF
Definition mipi_dsi.h:27
const uint8_t DELAY_FLAG
Definition mipi_dsi.h:31
const uint8_t SLEEP_OUT
Definition mipi_dsi.h:24
const uint8_t SW_RESET_CMD
Definition mipi_dsi.h:23
const uint8_t MADCTL_BGR
Definition mipi_dsi.h:32
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:500
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Definition helpers.cpp:317
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:31
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:30
uint8_t red
Definition color.h:31
uint8_t g
Definition color.h:34
uint8_t b
Definition color.h:38
uint8_t r
Definition color.h:30
uint8_t pad
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6