12constexpr static const char *
const TAG =
"display.mipi_spi";
15static constexpr size_t MIPI_SPI_MAX_CMD_LOG_BYTES = 64;
18static constexpr uint8_t NORON = 0x13;
21static constexpr uint8_t ALL_ON = 0x23;
22static constexpr uint8_t WRAM = 0x24;
23static constexpr uint8_t MIPI = 0x26;
25static constexpr uint8_t RASET = 0x2B;
26static constexpr uint8_t CASET = 0x2A;
27static constexpr uint8_t WDATA = 0x2C;
28static constexpr uint8_t TEON = 0x35;
30static constexpr uint8_t PIXFMT = 0x3A;
31static constexpr uint8_t BRIGHTNESS = 0x51;
32static constexpr uint8_t SWIRE1 = 0x5A;
33static constexpr uint8_t SWIRE2 = 0x5B;
34static constexpr uint8_t PAGESEL = 0xFE;
39static constexpr uint8_t MADCTL_RGB = 0x00;
43static constexpr uint16_t MADCTL_FLIP_FLAG = 0x100;
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,
70 bool has_hardware_rotation);
91 int WIDTH,
int HEIGHT,
int OFFSET_WIDTH,
int OFFSET_HEIGHT,
int PAD_WIDTH,
int PAD_HEIGHT, uint16_t MADCTL,
92 bool HAS_HARDWARE_ROTATION>
94 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
95 spi::DATA_RATE_1MHZ> {
114 if constexpr (HAS_HARDWARE_ROTATION) {
139 if (this->
dc_pin_ !=
nullptr) {
145 pin->digital_write(
true);
157 auto when =
millis() + 120;
161 while (index != vec.size()) {
162 if (vec.size() - index < 2) {
163 esph_log_e(TAG,
"Malformed init sequence");
167 uint8_t cmd = vec[index++];
168 uint8_t
x = vec[index++];
169 if (
x == DELAY_FLAG) {
170 esph_log_d(TAG,
"Delay %dms", cmd);
173 uint8_t num_args =
x & 0x7F;
174 if (vec.size() - index < num_args) {
175 esph_log_e(TAG,
"Malformed init sequence");
179 auto arg_byte = vec[index];
185 esph_log_d(TAG,
"Sleep %dms",
duration);
200 const auto *ptr = vec.data() + index;
203 if (cmd == SLEEP_OUT)
218 if (w <= 0 ||
h <= 0)
220 if (
get_pixel_mode(bitness) != BUFFERPIXEL || big_endian != IS_BIG_ENDIAN) {
222 esph_log_e(TAG,
"Unsupported color depth or bit order");
225 this->
write_to_display_(x_start, y_start, w,
h,
reinterpret_cast<const BUFFERTYPE *
>(ptr), x_offset, y_offset,
233 this->
data_rate_, BUS_TYPE, HAS_HARDWARE_ROTATION);
240 if constexpr (HAS_HARDWARE_ROTATION)
245 if constexpr (HAS_HARDWARE_ROTATION)
294 for (
size_t i = 0; i !=
len; i++) {
312 uint8_t madctl = (uint8_t) MADCTL;
313 constexpr bool use_flips = (MADCTL & MADCTL_FLIP_FLAG) != 0;
314 constexpr uint8_t x_mask = use_flips ? MADCTL_XFLIP : MADCTL_MX;
315 constexpr uint8_t y_mask = use_flips ? MADCTL_YFLIP : MADCTL_MY;
316 if constexpr (HAS_HARDWARE_ROTATION) {
334 esph_log_d(TAG,
"Setting MADCTL for rotation %d, value %X", this->
rotation_, madctl);
339 if constexpr (HAS_HARDWARE_ROTATION) {
342 return OFFSET_HEIGHT;
355 if constexpr (HAS_HARDWARE_ROTATION) {
367 return OFFSET_HEIGHT;
372 esph_log_v(TAG,
"Set addr %d/%d, %d/%d", x1, y1, x2, y2);
379 put16_be(buf + 2, y2);
382 put16_be(buf + 2, x2);
418 for (
size_t y = 0;
y !=
h;
y++) {
437 void write_to_display_(
int x_start,
int y_start,
int w,
int h,
const BUFFERTYPE *ptr,
int x_offset,
int y_offset,
441 ptr += y_offset * (x_offset + w + x_pad) + x_offset;
442 if constexpr (BUFFERPIXEL == DISPLAYPIXEL) {
444 x_pad *
sizeof(BUFFERTYPE));
447 uint8_t dbuffer[DISPLAYPIXEL * 48];
448 uint8_t *dptr = dbuffer;
449 auto stride = x_offset + w + x_pad;
450 for (
size_t y = 0;
y !=
static_cast<size_t>(
h);
y++) {
451 for (
size_t x = 0;
x !=
static_cast<size_t>(w);
x++) {
452 auto color_val = ptr[
y * stride +
x];
455 if constexpr (IS_BIG_ENDIAN) {
456 *dptr++ = color_val & 0xF8;
457 *dptr++ = ((color_val & 0x7) << 5) | (color_val & 0xE000) >> 11;
458 *dptr++ = (color_val >> 5) & 0xF8;
460 *dptr++ = (color_val >> 8) & 0xF8;
461 *dptr++ = (color_val & 0x7E0) >> 3;
462 *dptr++ = color_val << 3;
466 *dptr++ = color_val << 6;
467 *dptr++ = (color_val & 0x1C) << 3;
468 *dptr++ = (color_val & 0xE0);
470 if constexpr (IS_BIG_ENDIAN) {
471 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
472 *dptr++ = (color_val & 3) << 3;
474 *dptr++ = (color_val & 3) << 3;
475 *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
479 if (dptr == dbuffer +
sizeof(dbuffer)) {
486 if (dptr != dbuffer) {
526 uint16_t WIDTH, uint16_t HEIGHT,
int OFFSET_WIDTH,
int OFFSET_HEIGHT,
int PAD_WIDTH,
int PAD_HEIGHT,
527 uint16_t MADCTL,
bool HAS_HARDWARE_ROTATION,
int FRACTION,
unsigned ROUNDING>
529 :
public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
530 OFFSET_HEIGHT, PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION> {
540 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH, OFFSET_HEIGHT,
541 PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION>
::dump_config();
544 " Buffer pixels: %d bits\n"
545 " Buffer fraction: 1/%d\n"
546 " Buffer bytes: %zu\n"
547 " Draw rounding: %u",
548 this->
rotation_, BUFFERPIXEL * 8, FRACTION,
553 MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH, OFFSET_HEIGHT,
554 PAD_WIDTH, PAD_HEIGHT, MADCTL, HAS_HARDWARE_ROTATION>
::setup();
557 if (this->
buffer_ ==
nullptr) {
558 this->
mark_failed(LOG_STR(
"Buffer allocation failed"));
563#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
573#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
580 if (this->
page_ !=
nullptr) {
587#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
588 esph_log_v(TAG,
"Drawing from line %d took %dms", this->
start_line_,
millis() - lap);
593 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_,
610#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
611 esph_log_v(TAG,
"Write to display took %dms",
millis() - lap);
615#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
616 esph_log_v(TAG,
"Total update took %dms",
millis() - now);
624 if constexpr (not HAS_HARDWARE_ROTATION) {
677 return (color.
red & 0xE0) | (color.
g & 0xE0) >> 3 | color.
b >> 6;
679 if constexpr (IS_BIG_ENDIAN) {
680 return (color.
r & 0xF8) | color.
g >> 5 | (color.
g & 0x1C) << 11 | (color.
b & 0xF8) << 5;
682 return (color.
r & 0xF8) << 8 | (color.
g & 0xFC) << 3 | color.
b >> 3;
685 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 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
static constexpr size_t round_buffer(size_t size)
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
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)
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
int get_height_internal() override
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
uint16_t get_offset_width_() const
void set_brightness(uint8_t brightness)
int get_height() override
void draw_pixel_at(int x, int y, Color color) override
void write_command_(uint8_t cmd)
display::DisplayType get_display_type() override
std::vector< uint8_t > init_sequence_
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)
void set_init_sequence(const std::vector< uint8_t > &sequence)
void set_model(const char *model)
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_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, bool has_hardware_rotation)
T clamp_at_most(T value, U max)
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 HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()