ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
epaper_spi_ssd1677_gray4.cpp
Go to the documentation of this file.
2
3#include "esphome/core/log.h"
4
5namespace esphome::epaper_spi {
6
7ESPHOME_LOG_TAG(TAG, "epaper_spi.ssd1677_gray4");
8
9// Combine two source bytes (4 pixels each, 2 bits per pixel, most significant pixel first) into
10// one plane byte covering the same 8 pixels (1 bit per pixel), choosing high or low bit
11static uint8_t plane_byte(uint8_t first, uint8_t second, bool high_bit) {
12 uint8_t out = 0;
13 for (const uint8_t src : {first, second}) {
14 for (uint8_t shift = 6;; shift -= 2) {
15 const uint8_t level = (src >> shift) & 0x03;
16 out = (uint8_t) ((out << 1) | (high_bit ? (level >> 1) : (level & 1)));
17 if (shift == 0)
18 break;
19 }
20 }
21 return out;
22}
23
25 if (this->get_clipping().is_set()) {
26 // Falls back to the generic per-pixel implementation for the clipped rectangle.
27 EPaperBase::fill(color);
28 return;
29 }
30 const uint8_t level = color_to_gray4(color);
31 this->buffer_.fill((uint8_t) (level | (level << 2) | (level << 4) | (level << 6)));
32 this->x_low_ = 0;
33 this->y_low_ = 0;
34 this->x_high_ = this->width_;
35 this->y_high_ = this->height_;
36}
37
38void HOT EPaperSSD1677Gray4::draw_pixel_at(int x, int y, Color color) {
39 if (!this->rotate_coordinates_(x, y))
40 return;
41 const uint8_t level = color_to_gray4(color);
42 const size_t byte_position = (size_t) y * this->row_width_ + x / 4;
43 const uint8_t shift = (uint8_t) (6 - 2 * (x % 4)); // most significant pixel first
44 const uint8_t original = this->buffer_[byte_position];
45 this->buffer_[byte_position] = (uint8_t) ((original & ~(0x03 << shift)) | (level << shift));
46}
47
48// A partial update reduces each pixel to its high bit: levels 2 and 3 are light, 0 and 1 dark.
49void EPaperSSD1677Gray4::plane_row(size_t y, uint8_t *out) {
50 const size_t src_row = y * this->row_width_;
51 for (size_t i = 0; i != this->plane_row_length_(); i++)
52 out[i] = plane_byte(this->buffer_[src_row + 2 * i], this->buffer_[src_row + 2 * i + 1], true);
53}
54
55// the high bit of every pixel's level goes to the new (bw) plane
56// (0x24), the low bit to the old (red) plane (0x26)
58 if (this->is_partial_push_())
60
61 auto start_time = millis();
62 const bool first_pass = this->send_red_;
63 if (this->current_data_index_ == 0) {
64 if (first_pass) {
65 // With partial updates enabled the window follows the changed area, but the four-level
66 // refresh drives every pixel from both planes, and the reset before it does not keep RAM.
67 this->x_low_ = 0;
68 this->x_high_ = this->width_;
69 this->y_low_ = 0;
70 this->y_high_ = this->height_;
71 this->set_window();
72 }
73 this->command(first_pass ? 0x24 : 0x26);
74 this->current_data_index_ = this->y_low_;
75 }
76 const size_t plane_row_length = (this->x_high_ - this->x_low_) / 8;
77 // Stack-backed for every panel width in practice; only a custom `dimensions:` far wider than any
78 // supported panel would fall back to the heap.
79 SmallBufferWithHeapFallback<128> bytes_to_send_alloc(plane_row_length);
80 uint8_t *bytes_to_send = bytes_to_send_alloc.get();
81 ESP_LOGV(TAG, "Writing %u bytes at line %zu at %ums", plane_row_length, this->current_data_index_,
82 (unsigned) millis());
83 this->start_data_();
84 while (this->current_data_index_ != this->y_high_) {
85 const size_t src_row = this->current_data_index_ * this->row_width_ + this->x_low_ / 4;
86 for (size_t i = 0; i != plane_row_length; i++) {
87 const uint8_t plane = plane_byte(this->buffer_[src_row + 2 * i], this->buffer_[src_row + 2 * i + 1], first_pass);
88 // The OTP grayscale waveform treats data as inverted relative to monochrome
89 bytes_to_send[i] = (uint8_t) ~plane;
90 // What the next partial update compares against: the high bits, as a black-and-white frame.
91 if (first_pass && this->sent_.is_valid())
92 this->sent_[this->current_data_index_ * plane_row_length + i] = plane;
93 }
94 ++this->current_data_index_;
95 this->write_array(bytes_to_send, plane_row_length);
96 if (millis() - start_time > MAX_TRANSFER_TIME) {
97 // Let the main loop run and come back next loop
98 this->disable();
99 return false;
100 }
101 }
102
103 this->disable();
104 this->current_data_index_ = 0;
105 if (first_pass) {
106 this->send_red_ = false;
107 return false;
108 }
109 this->send_red_ = true;
110 return true;
111}
112
114 if (this->is_partial_push_()) {
115 ESP_LOGV(TAG, "Black-and-white partial refresh");
116 // The border follows the LUT selected in 0x3C. The model's setting (sent with the init
117 // sequence) picks the LUT that is white under the four-level waveform's inverted data; under
118 // the black-and-white waveform that LUT drives black and the border darkens, so use LUT1.
119 this->cmd_data(0x3C, {0x01});
121 return;
122 }
123 ESP_LOGV(TAG, "Four-level refresh");
124 this->cmd_data(0x1A, {0x67, 0x00}); // force temperature by OTP
125 this->cmd_data(0x22, {0xD7}); // four-level update sequence, panel's OTP waveform
126 this->command(0x20); // master activation
127}
128
129} // namespace esphome::epaper_spi
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:819
Rect get_clipping() const
Get the current the clipping rectangle.
Definition display.cpp:826
void command(uint8_t value)
void fill(Color color) override
Definition epaper_spi.h:88
bool rotate_coordinates_(int &x, int &y)
Check and rotate coordinates based on the transform flags.
virtual void refresh_screen(bool partial)=0
Refresh the screen after data transfer.
split_buffer::SplitBuffer buffer_
Definition epaper_spi.h:190
void cmd_data(uint8_t command, const uint8_t *ptr, size_t length)
void plane_row(size_t y, uint8_t *out) override
void draw_pixel_at(int x, int y, Color color) override
void fill(uint8_t value) const
Fill the entire buffer with a single byte value.
uint8_t second
std::vector< uint8_t > & out
Definition gif.cpp:49
ESPHOME_LOG_TAG(TAG, "display")
constexpr uint8_t color_to_gray4(Color color)
Map RGB color to one of 4 discrete gray levels (2 bits per pixel)
Definition colorconv.h:31
const void * src
Definition hal.h:68
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6