ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
it8951.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4#include <utility>
5#include <vector>
6
11
12#include "it8951_defs.h"
13
14namespace esphome::it8951 {
15
16using namespace display;
17
18// --- Bounded op queue --------------------------------------------------------
19// Fixed-capacity ring buffer used by the loop scheduler. Replaces std::deque
20// to comply with ESPHome's STL container guidelines (std::deque allocates in
21// 512-byte blocks regardless of element size). Size analysis: the deepest
22// observed scenario is UPDATE_REFRESH (10 enqueued ops) + CHECK_LUT_IDLE's
23// 5 push_front rescheduling = 14 simultaneous entries. We use 32 for a
24// comfortable margin while keeping RAM cost low (~192 bytes per instance vs
25// 512+ bytes for std::deque).
26template<typename T, size_t N> class StaticOpQueue {
27 public:
28 bool empty() const { return this->count_ == 0; }
29 size_t size() const { return this->count_; }
30 static constexpr size_t capacity() { return N; }
31
32 bool push_back(const T &value) {
33 if (this->count_ >= N)
34 return false;
35 this->data_[(this->head_ + this->count_) % N] = value;
36 ++this->count_;
37 return true;
38 }
39
40 bool push_front(const T &value) {
41 if (this->count_ >= N)
42 return false;
43 this->head_ = (this->head_ + N - 1) % N;
44 this->data_[this->head_] = value;
45 ++this->count_;
46 return true;
47 }
48
49 void pop_front() {
50 if (this->count_ == 0)
51 return;
52 this->head_ = (this->head_ + 1) % N;
53 --this->count_;
54 }
55
56 const T &front() const { return this->data_[this->head_]; }
57 T &front() { return this->data_[this->head_]; }
58
59 void clear() {
60 this->head_ = 0;
61 this->count_ = 0;
62 }
63
64 private:
65 T data_[N]{};
66 size_t head_{0};
67 size_t count_{0};
68};
69
70// Op queue capacity. See StaticOpQueue comment for sizing analysis.
71static constexpr size_t OP_QUEUE_SIZE = 32;
72
73// --- Op queue ---------------------------------------------------------------
74// Each Op is a single CS-asserted SPI transaction (or a tiny bookkeeping
75// step). The loop processes one Op per iteration after gating on HW_RDY, so
76// the natural ESPHome loop cadence (~8-16 ms) provides inter-op pacing
77// without any blocking waits.
78//
79// Compound Ops (READ_DEV_INFO, XFER_*, DPY_BUF_AREA, ENABLE_1BPP, ...) are
80// short self-contained methods that do all their SPI work inside a single
81// CS cycle (or a small handful of cycles) and complete well under 2ms, so
82// they don't break the no-blocking budget.
83//
84// Each write-type op is a SINGLE CS-asserted transaction. The loop-level
85// HW_RDY gate ensures the controller is ready before dispatching any op, so
86// no blocking waits are needed within write ops.
87//
88// Read ops are decomposed: the command/address that triggers data preparation
89// is sent as write ops (CMD, WRITE_W), then a separate read op runs only
90// after the loop confirms HW_RDY is back HIGH (data ready). No blocking.
91enum class OpType : uint8_t {
92 CMD, // single CS: CMD preamble + command word (a)
93 WRITE_W, // single CS: WRITE preamble + data word (a)
94 WRITE_REG, // single CS: WRITE preamble + addr(a) + value(b)
95 // (caller must enqueue CMD(TCON_REG_WR) before this)
96 READ_DEV_INFO, // single CS: READ preamble + dummy + read DevInfo struct
97 // (caller enqueues CMD(GET_DEV_INFO) first; loop HW_RDY gate
98 // ensures data is ready before this op runs)
99 READ_WORD, // single CS: READ preamble + dummy + read one 16-bit word
100 // into read_result_. Loop HW_RDY gate ensures data ready.
101 CHECK_LUT_IDLE, // checks read_result_; if non-zero, re-enqueues read sequence
102 SET_1BPP, // uses read_result_ to set UP1SR bit 2, enqueues writes
103 XFER_LISAR, // set image-buffer target address (2× reg write: 4 CS transactions)
104 XFER_AREA_CMD, // single CS: CMD preamble + TCON_LD_IMG_AREA
105 XFER_AREA_ARGS, // single CS: WRITE preamble + 5 area-parameter words
106 XFER_ROWS, // single CS: WRITE preamble + row pixel data (time-sliced)
107 XFER_AREA_END, // single CS: CMD preamble + TCON_LD_IMG_END
108 DPY_BUF_CMD, // single CS: CMD preamble + I80_CMD_DPY_BUF_AREA
109 DPY_BUF_ARGS, // single CS: WRITE preamble + 7 display-area words
110 GPIO_RESET_LOW, // drive RESET pin low
111 GPIO_RESET_HIGH, // drive RESET pin high
112 DELAY_MS, // park `delay_until_` for a few ms (no SPI)
113};
114
115struct Op {
117 uint16_t a{0};
118 uint16_t b{0};
119};
120
121// High-level controller phases. Each phase enqueues a sequence of Ops; when
122// the queue drains, advance_phase_() runs the next phase.
123// This separation keeps per-Op work tiny and predictable.
124enum class Phase : uint8_t {
125 IDLE,
126 // Initialisation
127 INIT_RESET, // reset pulse + wake controller + packed-write enable
128 INIT_DEV_INFO, // GET_DEV_INFO and validate
129 INIT_VCOM, // write configured VCOM
130 INIT_TEMP, // force temperature for waveform LUT selection
131 INIT_DONE, // allocate framebuffer; transition to IDLE
132 // Update flow
133 UPDATE_PREPARE, // do_update_, compute dirty region, decide 4bpp/1bpp
134 UPDATE_TRANSFER, // one LD_IMG_AREA, time-sliced row streaming, one LD_IMG_END
135 UPDATE_REFRESH, // wait LUT idle, optionally enable 1bpp, send DPY_BUF_AREA
136 UPDATE_SLEEP, // optional deep sleep
137};
138
139class IT8951Display : public Display,
140 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
141 spi::DATA_RATE_2MHZ> {
142 public:
143 IT8951Display(const char *name, uint16_t width, uint16_t height) : name_(name), width_(width), height_(height) {
144 this->row_width_ = this->compute_row_width_();
145 this->buffer_length_ = static_cast<size_t>(this->row_width_) * static_cast<size_t>(height);
146 }
147
148 // --- Component lifecycle ---
149 void setup() override;
150 void loop() override;
151 void dump_config() override;
152 void on_safe_shutdown() override;
153 void on_powerdown() override;
154 float get_setup_priority() const override { return setup_priority::PROCESSOR; }
155
156 // --- Config setters (called from generated code) ---
157 void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
158 void set_busy_pin(GPIOPin *pin) { this->busy_pin_ = pin; }
159 void set_enable_pins(std::vector<GPIOPin *> pins) { this->enable_pins_ = std::move(pins); }
161 void set_full_update_every(uint8_t n) {
162 this->full_update_every_ = n;
163 // Seed the counter so the very first update trips the full-update branch in
164 // prepare_update_region_, giving a freshly-booted panel a clean GC16 refresh
165 // before any partial (fast-waveform) updates begin.
166 this->partial_update_count_ = n;
167 }
168 void set_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; }
169 void set_sleep_when_done(bool s) { this->sleep_when_done_ = s; }
170 void set_vcom(uint16_t vcom_mv) { this->vcom_ = vcom_mv; }
171 void set_vcom_register(uint16_t selector) { this->vcom_register_ = selector; }
172 void set_force_temperature(int16_t celsius) {
173 this->force_temperature_ = celsius;
174 this->force_temperature_set_ = true;
175 }
176 void set_use_legacy_dpy_area(bool use) { this->use_legacy_dpy_area_ = use; }
177 // Pixel format: true = 4bpp grayscale framebuffer, false = packed 1bpp
178 // monochrome framebuffer. Chosen at config time; the framebuffer is stored
179 // in this native format and every update uses the matching transfer path.
180 void set_grayscale(bool g) { this->grayscale_ = g; }
181 // Monochrome only: ordered-dither pale colours (true) vs a hard 50% threshold.
182 void set_dithering(bool d) { this->dithering_ = d; }
183 void set_update_mode(uint16_t m) { this->default_update_mode_ = static_cast<UpdateMode>(m); }
184 void set_transform(uint8_t t) {
185 this->transform_ = t;
187 }
188 void set_rotation(DisplayRotation rotation) override {
189 Display::set_rotation(rotation);
191 }
192
193 // --- Display API ---
194 void update() override;
197 void fill(Color color) override;
198 void clear() override { this->fill(Color::WHITE); }
199 void draw_pixel_at(int x, int y, Color color) override;
200 int get_width() override { return (this->effective_transform_ & TRANSFORM_SWAP_XY) ? this->height_ : this->width_; }
201 int get_height() override { return (this->effective_transform_ & TRANSFORM_SWAP_XY) ? this->width_ : this->height_; }
202 bool pixel_axes_swapped() const override { return this->effective_transform_ & TRANSFORM_SWAP_XY; }
203
204 protected:
205 // Bulk pixel blit (used by LVGL and image rendering). Overridden to write
206 // straight into the framebuffer, avoiding the base class's per-pixel
207 // draw_pixel_at overhead (watchdog feed, clipping test, dirty-box clamps).
208 void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorBitness bitness,
209 bool big_endian, int x_offset, int y_offset, int x_pad) override;
210
211 int get_height_internal() override { return this->height_; }
212 int get_width_internal() override { return this->width_; }
213
214 // --- Coord transform / dirty region ---
216 // Map display (logical) coordinates to native framebuffer coordinates by
217 // applying effective_transform_ (swap/mirror). Shared by rotate_coordinates_
218 // and the bulk draw_pixels_at path.
219 void apply_transform_(int &x, int &y) const;
220 bool rotate_coordinates_(int &x, int &y);
221 void reset_dirty_region_();
222
223 // --- Framebuffer geometry / monochrome packing ---
224 // Bytes per row for the configured pixel format: 4bpp grayscale packs two
225 // pixels per byte; monochrome packs eight bits per byte, rounded up to a
226 // whole 16-pixel group (matching the controller's 8bpp-load / 1bpp trick).
227 uint16_t compute_row_width_() const {
228 return this->grayscale_ ? static_cast<uint16_t>((static_cast<uint32_t>(this->width_) + 1) / 2)
229 : static_cast<uint16_t>(((static_cast<uint32_t>(this->width_) + 15) / 16) * 2);
230 }
231 void set_mono_pixel_(uint16_t x, uint16_t y, bool value) const;
232 // Write a 4bpp grayscale nibble into the framebuffer (two pixels per byte).
233 void set_gray_pixel_(uint16_t x, uint16_t y, uint8_t nibble) const;
234 // Convert a color and write it at native framebuffer coordinates: a 4bpp
235 // nibble in grayscale mode, or an ordered-dithered bit in monochrome mode.
236 void write_pixel_native_(uint16_t x, uint16_t y, const Color &color) const;
237
238 // --- Op queue / loop machinery ---
239 void enqueue_(OpType type, uint16_t a = 0, uint16_t b = 0);
240 void prepend_(OpType type, uint16_t a = 0, uint16_t b = 0);
241 bool is_busy_() const;
242 void process_op_(const Op &op);
243 void advance_phase_();
244 void set_phase_(Phase next);
246
247 // --- SPI primitives (each is one CS-asserted burst, fully non-blocking) ---
248 void spi_cmd_(uint16_t cmd);
249 void spi_write_word_(uint16_t value);
250 void spi_write_reg_(uint16_t addr, uint16_t value);
251 void spi_write_args_(const uint16_t *args, uint16_t count);
252 uint16_t spi_read_word_(); // non-blocking: HW_RDY confirmed by loop gate
253 void spi_read_dev_info_(); // non-blocking: HW_RDY confirmed by loop gate
254
255 // --- Compound Ops (small bounded helpers) ---
256 void op_xfer_lisar_();
257 void op_xfer_area_args_();
258 void op_xfer_area_end_();
259 bool op_xfer_rows_(); // returns true when current update area fully sent
260 void op_dpy_buf_args_();
261 void op_check_lut_idle_();
262 void op_set_1bpp_();
263
264 // --- Phase enqueuers ---
265 void enqueue_init_reset_();
267 void enqueue_init_vcom_();
268 void enqueue_init_temp_();
272
274
275 // --- Recovery ---
276 void recover_();
277
278 // --- State ---
279 static constexpr uint32_t BUSY_TIMEOUT_MS = 5000;
280
285 // Requests a continuous (non-throttled) main loop while streaming image data
286 // so 20ms transfer slices aren't separated by the ~16ms default loop interval.
288
289 // Pending update bookkeeping
290 bool update_pending_{false};
293 uint16_t area_x_{0}, area_y_{0}, area_w_{0}, area_h_{0};
294 uint16_t transfer_row_{0};
295 bool initialised_{false};
296 // True once TCON_SLEEP has been sent and the controller has not been woken
297 // since. The next update must issue TCON_SYS_RUN before any SPI op.
298 bool asleep_{false};
301
302 // Read result storage for decomposed read-modify-write op sequences
303 uint16_t read_result_{0};
304
305 // Device info
307 uint16_t img_buf_addr_l_{0};
308 uint16_t img_buf_addr_h_{0};
309
310 // Configured properties
311 const char *name_;
312 uint16_t width_;
313 uint16_t height_;
314 uint16_t row_width_;
316 uint8_t *buffer_{};
317 uint8_t transform_{0};
321 uint16_t vcom_{2300};
322 uint16_t vcom_register_{I80_CMD_VCOM_WRITE};
323 int16_t force_temperature_{DEFAULT_FORCE_TEMP_C};
326 bool invert_colors_{false};
327 bool sleep_when_done_{false};
328 // Pixel format selector (see set_grayscale): true = 4bpp grayscale,
329 // false = packed 1bpp monochrome.
330 bool grayscale_{true};
331 // Monochrome dithering (see set_dithering): true = ordered dither.
332 bool dithering_{true};
336 // GPIOs driven high during setup to power on the panel (empty if unused).
337 std::vector<GPIOPin *> enable_pins_;
338
339 // Dirty region (pixel coordinates of bounding box of changes since last update)
340 uint16_t x_low_{0}, y_low_{0}, x_high_{0}, y_high_{0};
341
342 // Saved data rate so we can probe slow then run fast
344
345 // Consecutive recovery attempts; used to give up rather than infinite-loop
346 // when the controller is unresponsive (e.g. wiring issue).
348
349 // DevInfo read retry counter (controller often returns garbage on the first
350 // read after reset; the original driver retried up to 3 times with 100ms
351 // between attempts).
353};
354
355} // namespace esphome::it8951
BedjetMode mode
BedJet operating mode.
uint8_t m
Definition bl0906.h:1
Helper class to request loop() to be called as fast as possible.
Definition helpers.h:2155
int int int const uint8_t ColorOrder ColorBitness bitness
Definition display.h:389
int int int const uint8_t ColorOrder ColorBitness bool int int y_offset
Definition display.h:389
int int int const uint8_t ColorOrder ColorBitness bool int int int x_pad
Definition display.h:389
int int int const uint8_t * ptr
Definition display.h:388
virtual void set_rotation(DisplayRotation rotation)
Internal method to set the display rotation with.
Definition display.cpp:18
int int int const uint8_t ColorOrder ColorBitness bool big_endian
Definition display.h:389
int int int const uint8_t ColorOrder ColorBitness bool int x_offset
Definition display.h:389
int get_height() override
Definition it8951.h:201
void on_powerdown() override
Definition it8951.cpp:354
void update_mode(UpdateMode mode)
Definition it8951.cpp:785
float get_setup_priority() const override
Definition it8951.h:154
void start_update_(UpdateMode mode)
Definition it8951.cpp:760
void spi_write_args_(const uint16_t *args, uint16_t count)
Definition it8951.cpp:518
bool pixel_axes_swapped() const override
Definition it8951.h:202
HighFrequencyLoopRequester high_freq_
Definition it8951.h:287
void set_invert_colors(bool invert_colors)
Definition it8951.h:168
void set_transform(uint8_t t)
Definition it8951.h:184
void set_phase_(Phase next)
Definition it8951.cpp:154
void spi_cmd_(uint16_t cmd)
Definition it8951.cpp:491
bool rotate_coordinates_(int &x, int &y)
Definition it8951.cpp:863
void enqueue_(OpType type, uint16_t a=0, uint16_t b=0)
Definition it8951.cpp:21
void set_reset_pin(GPIOPin *pin)
Definition it8951.h:157
IT8951Display(const char *name, uint16_t width, uint16_t height)
Definition it8951.h:143
uint16_t compute_row_width_() const
Definition it8951.h:227
void spi_write_reg_(uint16_t addr, uint16_t value)
Definition it8951.cpp:507
void set_force_temperature(int16_t celsius)
Definition it8951.h:172
void prepend_(OpType type, uint16_t a=0, uint16_t b=0)
Definition it8951.cpp:28
void set_update_mode(uint16_t m)
Definition it8951.h:183
std::vector< GPIOPin * > enable_pins_
Definition it8951.h:337
int get_height_internal() override
Definition it8951.h:211
StaticOpQueue< Op, OP_QUEUE_SIZE > queue_
Definition it8951.h:281
void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
Definition it8951.cpp:962
void set_mono_pixel_(uint16_t x, uint16_t y, bool value) const
Definition it8951.cpp:1027
void set_busy_pin(GPIOPin *pin)
Definition it8951.h:158
void set_enable_pins(std::vector< GPIOPin * > pins)
Definition it8951.h:159
void set_reset_duration(uint32_t ms)
Definition it8951.h:160
void write_pixel_native_(uint16_t x, uint16_t y, const Color &color) const
Definition it8951.cpp:942
void set_rotation(DisplayRotation rotation) override
Definition it8951.h:188
void apply_transform_(int &x, int &y) const
Definition it8951.cpp:854
void process_op_(const Op &op)
Definition it8951.cpp:81
void fill(Color color) override
Definition it8951.cpp:910
void spi_write_word_(uint16_t value)
Definition it8951.cpp:499
void on_safe_shutdown() override
Definition it8951.cpp:349
static constexpr uint32_t BUSY_TIMEOUT_MS
Definition it8951.h:279
void set_vcom(uint16_t vcom_mv)
Definition it8951.h:170
void set_full_update_every(uint8_t n)
Definition it8951.h:161
void set_sleep_when_done(bool s)
Definition it8951.h:169
void set_use_legacy_dpy_area(bool use)
Definition it8951.h:176
bool prepare_update_region_(UpdateMode &mode)
Definition it8951.cpp:683
DisplayType get_display_type() override
Definition it8951.h:196
void set_gray_pixel_(uint16_t x, uint16_t y, uint8_t nibble) const
Definition it8951.cpp:1046
void set_vcom_register(uint16_t selector)
Definition it8951.h:171
int get_width_internal() override
Definition it8951.h:212
void draw_pixel_at(int x, int y, Color color) override
Definition it8951.cpp:933
const T & front() const
Definition it8951.h:56
bool push_front(const T &value)
Definition it8951.h:40
bool push_back(const T &value)
Definition it8951.h:32
static constexpr size_t capacity()
Definition it8951.h:30
The SPIDevice is what components using the SPI will create.
Definition spi.h:450
uint8_t type
unsigned count
Definition gif.cpp:51
static void int x_start
Definition display.cpp:62
constexpr float PROCESSOR
For components that use data from sensors like displays.
Definition component.h:47
static void uint32_t
static const Color WHITE
Definition color.h:191
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6