ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
st7305.cpp
Go to the documentation of this file.
1#include "st7305.h"
2
3#include <algorithm>
4
6#include "esphome/core/log.h"
7
8namespace esphome::st7305 {
9
10ESPHOME_LOG_TAG(TAG, "st7305");
11
13 this->spi_setup();
14 this->dc_pin_->setup();
15 if (this->reset_pin_ != nullptr)
16 this->reset_pin_->setup();
17
18 if (!this->init_buffer_())
19 return;
20
21 this->reset_();
22}
23
25 this->init_internal_(this->buffer_length_);
26 if (this->buffer_ == nullptr) {
27 this->mark_failed(LOG_STR("Buffer allocation failed"));
28 return false;
29 }
30 this->clear_dirty_();
31 return true;
32}
33
35 static constexpr uint32_t RESET_HIGH_MS = 50;
36 static constexpr uint32_t RESET_LOW_MS = 20;
37 static constexpr uint32_t RESET_SETTLE_MS = 50;
38 static constexpr uint32_t SOFT_RESET_MS = 5;
39
40 if (this->reset_pin_ == nullptr) {
41 this->enable();
42 this->send_command_(0x01); // software reset
43 this->disable();
44 this->set_timeout(STARTUP_TIMEOUT_ID, SOFT_RESET_MS, [this]() { this->send_init_sequence_(); });
45 return;
46 }
47 this->reset_pin_->digital_write(true);
48 this->set_timeout(STARTUP_TIMEOUT_ID, RESET_HIGH_MS, [this]() {
49 this->reset_pin_->digital_write(false);
50 this->set_timeout(STARTUP_TIMEOUT_ID, RESET_LOW_MS, [this]() {
51 this->reset_pin_->digital_write(true);
52 this->set_timeout(STARTUP_TIMEOUT_ID, RESET_SETTLE_MS, [this]() { this->send_init_sequence_(); });
53 });
54 });
55}
56
59 if (this->init_sequence_length_ - this->init_index_ < 2) {
60 this->mark_failed(LOG_STR("Malformed init sequence"));
61 return;
62 }
63 const uint8_t command = progmem_read_byte(&this->init_sequence_[this->init_index_++]);
64 const uint8_t count = progmem_read_byte(&this->init_sequence_[this->init_index_++]);
65 if (count == 0xFF) {
66 // Resume the sequence after the delay instead of blocking the main loop.
67 this->set_timeout(STARTUP_TIMEOUT_ID, command, [this]() { this->send_init_sequence_(); });
68 return;
69 }
70 if (this->init_sequence_length_ - this->init_index_ < count) {
71 this->mark_failed(LOG_STR("Malformed init sequence"));
72 return;
73 }
74 this->enable();
75 this->send_command_(command);
76 if (count != 0) {
77 this->dc_pin_->digital_write(true);
78 for (uint8_t i = 0; i < count; i++)
80 }
81 this->disable();
82 this->init_index_ += count;
83 }
84 this->finish_init_();
85}
86
88 this->write_full_frame_();
89 this->clear_dirty_();
90 this->ready_ = true;
91}
92
94 const uint8_t caset[] = {this->column_start_, this->column_end_};
95 const uint8_t raset[] = {this->row_start_, this->row_end_};
96 this->enable();
97 this->send_command_(0x2A);
98 this->send_data_(caset, sizeof(caset));
99 this->send_command_(0x2B);
100 this->send_data_(raset, sizeof(raset));
101 this->send_command_(0x2C);
102 this->send_data_(this->buffer_, this->buffer_length_);
103 this->disable();
104}
105
107 Window window{};
109 // RASET rows are x pairs; CASET columns are 12-pixel bands of y blocks.
110 window.stride = static_cast<uint16_t>(this->height_ / 4);
111 window.outer_start = static_cast<uint16_t>(this->x_low_ >> 1);
112 window.outer_end = static_cast<uint16_t>(this->x_high_ >> 1);
113 const int inv_min = this->height_ - 1 - this->y_high_;
114 const int inv_max = this->height_ - 1 - this->y_low_;
115 window.block_start = static_cast<uint16_t>((inv_min >> 2) / 3 * 3);
116 window.block_end =
117 static_cast<uint16_t>(std::min(((inv_max >> 2) / 3) * 3 + 2, static_cast<int>(window.stride) - 1));
118 // The panel mirrors the column address window (MADCTL MX) but still fills it in ascending
119 // order, so the bands are addressed from the far end of the column range.
120 const int last_band = window.stride / 3 - 1;
121 window.caset_start = static_cast<uint8_t>(this->column_start_ + last_band - window.block_end / 3);
122 window.caset_end = static_cast<uint8_t>(this->column_start_ + last_band - window.block_start / 3);
123 } else {
124 // RASET rows are y pairs; CASET columns are 12-pixel bands of x blocks.
125 window.stride = static_cast<uint16_t>(this->width_ / 4);
126 window.outer_start = static_cast<uint16_t>(this->y_low_ >> 1);
127 window.outer_end = static_cast<uint16_t>(this->y_high_ >> 1);
128 const int block_min = this->x_low_ >> 2;
129 const int block_max = this->x_high_ >> 2;
130 window.block_start = static_cast<uint16_t>(block_min / 3 * 3);
131 window.block_end = static_cast<uint16_t>(std::min((block_max / 3) * 3 + 2, static_cast<int>(window.stride) - 1));
132 window.caset_start = static_cast<uint8_t>(this->column_start_ + window.block_start / 3);
133 window.caset_end = static_cast<uint8_t>(this->column_start_ + window.block_end / 3);
134 }
135 return window;
136}
137
138void ST7305::write_window_(const Window &window) {
139 const uint8_t caset[] = {window.caset_start, window.caset_end};
140 const uint8_t raset[] = {static_cast<uint8_t>(this->row_start_ + window.outer_start),
141 static_cast<uint8_t>(this->row_start_ + window.outer_end)};
142 this->enable();
143 this->send_command_(0x2A);
144 this->send_data_(caset, sizeof(caset));
145 this->send_command_(0x2B);
146 this->send_data_(raset, sizeof(raset));
147 this->send_command_(0x2C);
148 if (window.block_start == 0 && window.block_end == window.stride - 1) {
149 // Full-height window: every outer unit's data is contiguous with the next.
150 this->send_data_(this->buffer_ + window.outer_start * window.stride,
151 static_cast<size_t>(window.outer_end - window.outer_start + 1) * window.stride);
152 } else {
153 const size_t length = window.block_end - window.block_start + 1;
154 for (uint16_t outer = window.outer_start; outer <= window.outer_end; outer++)
155 this->send_data_(this->buffer_ + outer * window.stride + window.block_start, length);
156 }
157 this->disable();
158}
159
161 if (!this->ready_)
162 return;
163
164 this->do_update_();
165 if (!this->is_dirty_())
166 return;
167
168 const Window window = this->compute_window_();
169 if (window.data_length() > this->buffer_length_ / 2) {
170 this->write_full_frame_();
171 } else {
172 this->write_window_(window);
173 }
174 this->clear_dirty_();
175}
176
178 if (x < 0 || y < 0 || x >= this->width_ || y >= this->height_)
179 return;
180 size_t index;
181 uint8_t mask;
183 const int inv_y = this->height_ - 1 - y;
184 index = static_cast<size_t>(x >> 1) * (this->height_ / 4) + (inv_y >> 2);
185 mask = static_cast<uint8_t>(1 << (7 - (((inv_y & 3) << 1) | (x & 1))));
186 } else {
187 index = static_cast<size_t>(y >> 1) * (this->width_ / 4) + (x >> 2);
188 mask = static_cast<uint8_t>(1 << (7 - (((x & 3) << 1) | (y & 1))));
189 }
190 // The init sequence enables INVON, so a set bit is white and a clear bit is black.
191 uint8_t &value = this->buffer_[index];
192 const uint8_t previous = value;
193 assign_bits(value, mask, color.is_on());
194 if (value == previous)
195 return;
196 this->extend_dirty_(x, y, x, y);
197}
198
199void ST7305::fill(Color color) {
200 if (this->get_clipping().is_set()) {
202 return;
203 }
204 if (this->buffer_ == nullptr)
205 return;
206
207 const uint8_t value = color.is_on() ? 0xFF : 0x00;
208 size_t first = this->buffer_length_;
209 size_t last = 0;
210 for (size_t index = 0; index < this->buffer_length_; index++) {
211 if (this->buffer_[index] == value)
212 continue;
213 this->buffer_[index] = value;
214 if (first == this->buffer_length_)
215 first = index;
216 last = index;
217 }
218 if (first == this->buffer_length_)
219 return;
220
221 // Widen the changed index range to whole outer units, and the other axis to the full
222 // display. The extra pixels are current in the buffer, so sending them is harmless.
224 const int stride = this->height_ / 4;
225 this->extend_dirty_(static_cast<int>(first / stride) * 2, 0, static_cast<int>(last / stride) * 2 + 1,
226 this->height_ - 1);
227 } else {
228 const int stride = this->width_ / 4;
229 this->extend_dirty_(0, static_cast<int>(first / stride) * 2, this->width_ - 1,
230 static_cast<int>(last / stride) * 2 + 1);
231 }
232}
233
234void ST7305::extend_dirty_(int x0, int y0, int x1, int y1) {
235 if (x0 < this->x_low_)
236 this->x_low_ = x0;
237 if (x1 > this->x_high_)
238 this->x_high_ = x1;
239 if (y0 < this->y_low_)
240 this->y_low_ = y0;
241 if (y1 > this->y_high_)
242 this->y_high_ = y1;
243}
244
246 LOG_DISPLAY("", "ST7305", this);
247 ESP_LOGCONFIG(TAG, " Model: %s", this->model_);
248 ESP_LOGCONFIG(TAG, " Orientation: %s",
249 this->orientation_ == Orientation::ORIENTATION_LANDSCAPE ? LOG_STR_LITERAL("landscape")
250 : LOG_STR_LITERAL("portrait"));
251 ESP_LOGCONFIG(TAG, " Window: columns %u..%u, rows %u..%u", this->column_start_, this->column_end_, this->row_start_,
252 this->row_end_);
253 LOG_PIN(" DC Pin: ", this->dc_pin_);
254 LOG_PIN(" Reset Pin: ", this->reset_pin_);
255 LOG_UPDATE_INTERVAL(this);
256}
257
258} // namespace esphome::st7305
void mark_failed()
Mark this component as failed.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
virtual void setup()=0
virtual void digital_write(bool value)=0
void init_internal_(uint32_t buffer_length)
virtual void fill(Color color)
Fill the entire screen with the given color.
Definition display.cpp:16
Rect get_clipping() const
Get the current the clipping rectangle.
Definition display.cpp:826
void draw_absolute_pixel_internal(int x, int y, Color color) override
Definition st7305.cpp:177
void write_window_(const Window &window)
Definition st7305.cpp:138
static constexpr uint32_t STARTUP_TIMEOUT_ID
Scheduler id for the non-blocking reset and init sequence.
Definition st7305.h:84
void setup() override
Definition st7305.cpp:12
void update() override
Definition st7305.cpp:160
void extend_dirty_(int x0, int y0, int x1, int y1)
Definition st7305.cpp:234
void send_data_(const uint8_t *data, size_t length)
Definition st7305.h:90
Orientation orientation_
Definition st7305.h:110
bool is_dirty_() const
Definition st7305.h:95
const uint8_t * init_sequence_
Definition st7305.h:116
void send_command_(uint8_t command)
Definition st7305.h:86
void dump_config() override
Definition st7305.cpp:245
const char * model_
Definition st7305.h:107
Window compute_window_() const
Definition st7305.cpp:106
size_t init_sequence_length_
Definition st7305.h:117
void fill(Color color) override
Definition st7305.cpp:199
unsigned count
Definition gif.cpp:51
ESPHOME_LOG_TAG(TAG, "st7305")
@ ORIENTATION_LANDSCAPE
Definition st7305.h:14
ESPHOME_ALWAYS_INLINE constexpr void assign_bits(T &value, std::type_identity_t< T > mask, bool on)
Set the bits of mask in value when on is true, clear them otherwise.
Definition helpers.h:1072
uint8_t progmem_read_byte(const uint8_t *addr)
Definition hal.h:51
static void uint32_t
bool is_on() ESPHOME_ALWAYS_INLINE
Definition color.h:70
An addressable write window.
Definition st7305.h:55
size_t data_length() const
Definition st7305.h:64
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6