ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
inkplate.cpp
Go to the documentation of this file.
1#include "inkplate.h"
4#include "esphome/core/log.h"
5
6#include <cinttypes>
7
8#include <hal/gpio_hal.h>
9
10namespace esphome {
11namespace inkplate {
12
13static const char *const TAG = "inkplate";
14
16 for (uint32_t i = 0; i < 256; i++) {
17 this->pin_lut_[i] = ((i & 0b00000011) << 4) | (((i & 0b00001100) >> 2) << 18) | (((i & 0b00010000) >> 4) << 23) |
18 (((i & 0b11100000) >> 5) << 25);
19 }
20
21 this->initialize_();
22
23 this->vcom_pin_->setup();
24 this->powerup_pin_->setup();
25 this->wakeup_pin_->setup();
26 this->gpio0_enable_pin_->setup();
28
29 this->cl_pin_->setup();
30 this->le_pin_->setup();
31 this->ckv_pin_->setup();
32 this->gmod_pin_->setup();
33 this->oe_pin_->setup();
34 this->sph_pin_->setup();
35 this->spv_pin_->setup();
36
45
46 this->wakeup_pin_->digital_write(true);
47 delay(1);
48 this->write_bytes(0x09, {
49 0b00011011, // Power up seq.
50 0b00000000, // Power up delay (3mS per rail)
51 0b00011011, // Power down seq.
52 0b00000000, // Power down delay (6mS per rail)
53 });
54 delay(1);
55 this->wakeup_pin_->digital_write(false);
56}
57
62 RAMAllocator<uint8_t> allocator;
63 RAMAllocator<uint32_t> allocator32;
64 uint32_t buffer_size = this->get_buffer_length_();
65 if (buffer_size == 0)
66 return;
67
68 if (this->partial_buffer_ != nullptr) {
69 allocator.deallocate(this->partial_buffer_, buffer_size);
70 this->partial_buffer_ = nullptr;
71 }
72 if (this->partial_buffer_2_ != nullptr) {
73 allocator.deallocate(this->partial_buffer_2_, buffer_size * 2);
74 this->partial_buffer_2_ = nullptr;
75 }
76 if (this->buffer_ != nullptr) {
77 allocator.deallocate(this->buffer_, buffer_size);
78 this->buffer_ = nullptr;
79 }
80 if (this->glut_ != nullptr) {
81 allocator32.deallocate(this->glut_, 256 * 9);
82 this->glut_ = nullptr;
83 }
84 if (this->glut2_ != nullptr) {
85 allocator32.deallocate(this->glut2_, 256 * 9);
86 this->glut2_ = nullptr;
87 }
88
89 this->buffer_ = allocator.allocate(buffer_size);
90 if (this->buffer_ == nullptr) {
91 ESP_LOGE(TAG, "Could not allocate buffer for display!");
92 this->mark_failed();
93 return;
94 }
95 if (this->greyscale_) {
96 this->glut_ = allocator32.allocate(256 * GLUT_SIZE);
97 if (this->glut_ == nullptr) {
98 ESP_LOGE(TAG, "Could not allocate glut!");
99 this->mark_failed();
100 return;
101 }
102 this->glut2_ = allocator32.allocate(256 * GLUT_SIZE);
103 if (this->glut2_ == nullptr) {
104 ESP_LOGE(TAG, "Could not allocate glut2!");
105 this->mark_failed();
106 return;
107 }
108
109 for (uint8_t i = 0; i < GLUT_SIZE; i++) {
110 for (uint32_t j = 0; j < 256; j++) {
111 uint8_t z = (this->waveform_[j & 0x07][i] << 2) | (this->waveform_[(j >> 4) & 0x07][i]);
112 this->glut_[i * 256 + j] = ((z & 0b00000011) << 4) | (((z & 0b00001100) >> 2) << 18) |
113 (((z & 0b00010000) >> 4) << 23) | (((z & 0b11100000) >> 5) << 25);
114 z = ((this->waveform_[j & 0x07][i] << 2) | (this->waveform_[(j >> 4) & 0x07][i])) << 4;
115 this->glut2_[i * 256 + j] = ((z & 0b00000011) << 4) | (((z & 0b00001100) >> 2) << 18) |
116 (((z & 0b00010000) >> 4) << 23) | (((z & 0b11100000) >> 5) << 25);
117 }
118 }
119
120 } else {
121 this->partial_buffer_ = allocator.allocate(buffer_size);
122 if (this->partial_buffer_ == nullptr) {
123 ESP_LOGE(TAG, "Could not allocate partial buffer for display!");
124 this->mark_failed();
125 return;
126 }
127 this->partial_buffer_2_ = allocator.allocate(buffer_size * 2);
128 if (this->partial_buffer_2_ == nullptr) {
129 ESP_LOGE(TAG, "Could not allocate partial buffer 2 for display!");
130 this->mark_failed();
131 return;
132 }
133
134 memset(this->partial_buffer_, 0, buffer_size);
135 memset(this->partial_buffer_2_, 0, buffer_size * 2);
136 }
137
138 memset(this->buffer_, 0, buffer_size);
139}
140
142
144 if (this->greyscale_) {
145 return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 2u;
146 } else {
147 return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u;
148 }
149}
150
152 this->do_update_();
153
154 if (this->full_update_every_ > 0 && this->partial_updates_ >= this->full_update_every_) {
155 this->block_partial_ = true;
156 }
157
158 this->display();
159}
160
162 if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
163 return;
164
165 if (this->mirror_y_)
166 y = this->get_height_internal() - y - 1;
167
168 if (this->mirror_x_)
169 x = this->get_width_internal() - x - 1;
170
171 if (this->greyscale_) {
172 int x1 = x / 2;
173 int x_sub = x % 2;
174 uint32_t pos = (x1 + y * (this->get_width_internal() / 2));
175 uint8_t current = this->buffer_[pos];
176
177 // float px = (0.2126 * (color.red / 255.0)) + (0.7152 * (color.green / 255.0)) + (0.0722 * (color.blue / 255.0));
178 // px = pow(px, 1.5);
179 // uint8_t gs = (uint8_t)(px*7);
180
181 uint8_t gs = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
182 this->buffer_[pos] = (PIXEL_MASK_GLUT[x_sub] & current) | (x_sub ? gs : gs << 4);
183
184 } else {
185 int x1 = x / 8;
186 int x_sub = x % 8;
187 uint32_t pos = (x1 + y * (this->get_width_internal() / 8));
188 uint8_t current = this->partial_buffer_[pos];
189 this->partial_buffer_[pos] = (~PIXEL_MASK_LUT[x_sub] & current) | (color.is_on() ? 0 : PIXEL_MASK_LUT[x_sub]);
190 }
191}
192
194 LOG_DISPLAY("", "Inkplate", this);
195 ESP_LOGCONFIG(TAG,
196 " Greyscale: %s\n"
197 " Partial Updating: %s\n"
198 " Full Update Every: %" PRIu32,
199 YESNO(this->greyscale_), YESNO(this->partial_updating_), this->full_update_every_);
200 // Log pins
201 LOG_PIN(" CKV Pin: ", this->ckv_pin_);
202 LOG_PIN(" CL Pin: ", this->cl_pin_);
203 LOG_PIN(" GPIO0 Enable Pin: ", this->gpio0_enable_pin_);
204 LOG_PIN(" GMOD Pin: ", this->gmod_pin_);
205 LOG_PIN(" LE Pin: ", this->le_pin_);
206 LOG_PIN(" OE Pin: ", this->oe_pin_);
207 LOG_PIN(" POWERUP Pin: ", this->powerup_pin_);
208 LOG_PIN(" SPH Pin: ", this->sph_pin_);
209 LOG_PIN(" SPV Pin: ", this->spv_pin_);
210 LOG_PIN(" VCOM Pin: ", this->vcom_pin_);
211 LOG_PIN(" WAKEUP Pin: ", this->wakeup_pin_);
212
213 LOG_PIN(" Data 0 Pin: ", this->display_data_0_pin_);
214 LOG_PIN(" Data 1 Pin: ", this->display_data_1_pin_);
215 LOG_PIN(" Data 2 Pin: ", this->display_data_2_pin_);
216 LOG_PIN(" Data 3 Pin: ", this->display_data_3_pin_);
217 LOG_PIN(" Data 4 Pin: ", this->display_data_4_pin_);
218 LOG_PIN(" Data 5 Pin: ", this->display_data_5_pin_);
219 LOG_PIN(" Data 6 Pin: ", this->display_data_6_pin_);
220 LOG_PIN(" Data 7 Pin: ", this->display_data_7_pin_);
221
222 LOG_UPDATE_INTERVAL(this);
223}
224
226 ESP_LOGV(TAG, "Eink off called");
227 if (!panel_on_)
228 return;
229 panel_on_ = false;
230
231 this->oe_pin_->digital_write(false);
232 this->gmod_pin_->digital_write(false);
233
234 GPIO.out_w1tc = this->get_data_pin_mask_() | (1UL << this->cl_pin_->get_pin()) | (1UL << this->le_pin_->get_pin());
235 this->ckv_pin_->digital_write(false);
236 this->sph_pin_->digital_write(false);
237 this->spv_pin_->digital_write(false);
238
239 this->vcom_pin_->digital_write(false);
240
241 this->write_byte(0x01, 0x6F); // Put TPS65186 into standby mode
242
243 delay(100); // NOLINT
244
245 this->write_byte(0x01, 0x4f); // Disable 3V3 to the panel
246
247 if (this->model_ != INKPLATE_6_PLUS)
248 this->wakeup_pin_->digital_write(false);
249
251}
252
254 ESP_LOGV(TAG, "Eink on called");
255 if (panel_on_)
256 return;
257 this->panel_on_ = true;
258
259 this->pins_as_outputs_();
260 this->wakeup_pin_->digital_write(true);
261 this->vcom_pin_->digital_write(true);
262 delay(2);
263
264 this->write_byte(0x01, 0b00101111); // Enable all rails
265
266 delay(1);
267
268 this->write_byte(0x01, 0b10101111); // Switch TPS65186 into active mode
269
270 this->le_pin_->digital_write(false);
271 this->oe_pin_->digital_write(false);
272 this->cl_pin_->digital_write(false);
273 this->sph_pin_->digital_write(true);
274 this->gmod_pin_->digital_write(true);
275 this->spv_pin_->digital_write(true);
276 this->ckv_pin_->digital_write(false);
277 this->oe_pin_->digital_write(false);
278
279 uint32_t timer = millis();
280 do {
281 delay(1);
282 } while (!this->read_power_status_() && ((millis() - timer) < 250));
283 if ((millis() - timer) >= 250) {
284 ESP_LOGW(TAG, "Power supply not detected");
285 this->wakeup_pin_->digital_write(false);
286 this->vcom_pin_->digital_write(false);
287 this->powerup_pin_->digital_write(false);
288 this->panel_on_ = false;
289 return;
290 }
291
292 this->oe_pin_->digital_write(true);
293}
294
296 uint8_t data;
297 auto err = this->read_register(0x0F, &data, 1);
298 if (err == i2c::ERROR_OK) {
299 return data == 0b11111010;
300 }
301 return false;
302}
303
305 ESP_LOGV(TAG, "Fill called");
306 uint32_t start_time = millis();
307
308 // If clipping is active, fall back to base implementation
309 if (this->get_clipping().is_set()) {
310 Display::fill(color);
311 ESP_LOGV(TAG, "Fill finished (%" PRIu32 "ms)", millis() - start_time);
312 return;
313 }
314
315 if (this->greyscale_) {
316 uint8_t fill = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
317 memset(this->buffer_, (fill << 4) | fill, this->get_buffer_length_());
318 } else {
319 uint8_t fill = color.is_on() ? 0x00 : 0xFF;
320 memset(this->partial_buffer_, fill, this->get_buffer_length_());
321 }
322
323 ESP_LOGV(TAG, "Fill finished (%ums)", millis() - start_time);
324}
325
327 ESP_LOGV(TAG, "Display called");
328 uint32_t start_time = millis();
329
330 if (this->greyscale_) {
331 this->display3b_();
332 } else {
333 if (this->partial_updating_ && this->partial_update_()) {
334 ESP_LOGV(TAG, "Display finished (partial) (%" PRIu32 "ms)", millis() - start_time);
335 return;
336 }
337 this->display1b_();
338 }
339 ESP_LOGV(TAG, "Display finished (full) (%" PRIu32 "ms)", millis() - start_time);
340}
341
343 ESP_LOGV(TAG, "Display1b called");
344 uint32_t start_time = millis();
345
346 memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_());
347
348 uint8_t data;
349 uint8_t buffer_value;
350 const uint8_t *buffer_ptr;
351 eink_on_();
352 uint8_t rep = 4;
353 switch (this->model_) {
354 case INKPLATE_10:
355 clean_fast_(0, 1);
356 clean_fast_(1, 10);
357 clean_fast_(2, 1);
358 clean_fast_(0, 10);
359 clean_fast_(2, 1);
360 clean_fast_(1, 10);
361 clean_fast_(2, 1);
362 clean_fast_(0, 10);
363 rep = 5;
364 break;
365 case INKPLATE_6_PLUS:
366 clean_fast_(0, 1);
367 clean_fast_(1, 15);
368 clean_fast_(2, 1);
369 clean_fast_(0, 5);
370 clean_fast_(2, 1);
371 clean_fast_(1, 15);
372 break;
373 case INKPLATE_6:
374 case INKPLATE_6_V2:
375 clean_fast_(0, 1);
376 clean_fast_(1, 18);
377 clean_fast_(2, 1);
378 clean_fast_(0, 18);
379 clean_fast_(2, 1);
380 clean_fast_(1, 18);
381 clean_fast_(2, 1);
382 clean_fast_(0, 18);
383 clean_fast_(2, 1);
384 if (this->model_ == INKPLATE_6_V2)
385 rep = 5;
386 break;
387 case INKPLATE_5:
388 clean_fast_(0, 1);
389 clean_fast_(1, 14);
390 clean_fast_(2, 1);
391 clean_fast_(0, 14);
392 clean_fast_(2, 1);
393 clean_fast_(1, 14);
394 clean_fast_(2, 1);
395 clean_fast_(0, 14);
396 clean_fast_(2, 1);
397 rep = 5;
398 break;
399 case INKPLATE_5_V2:
400 clean_fast_(0, 1);
401 clean_fast_(1, 11);
402 clean_fast_(2, 1);
403 clean_fast_(0, 11);
404 clean_fast_(2, 1);
405 clean_fast_(1, 11);
406 clean_fast_(2, 1);
407 clean_fast_(0, 11);
408 rep = 3;
409 break;
410 }
411
412 uint32_t clock = (1UL << this->cl_pin_->get_pin());
413 uint32_t data_mask = this->get_data_pin_mask_();
414 ESP_LOGV(TAG, "Display1b start loops (%" PRIu32 "ms)", millis() - start_time);
415
416 for (uint8_t k = 0; k < rep; k++) {
417 buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
418 vscan_start_();
419 for (int i = 0, im = this->get_height_internal(); i < im; i++) {
420 buffer_value = *(buffer_ptr--);
421 data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value >> 4) & 0x0F] : LUTB[(buffer_value >> 4) & 0x0F];
422 hscan_start_(this->pin_lut_[data]);
423 data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value) & 0x0F] : LUTB[buffer_value & 0x0F];
424 GPIO.out_w1ts = this->pin_lut_[data] | clock;
425 GPIO.out_w1tc = data_mask | clock;
426
427 for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
428 buffer_value = *(buffer_ptr--);
429 data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value >> 4) & 0x0F] : LUTB[(buffer_value >> 4) & 0x0F];
430 GPIO.out_w1ts = this->pin_lut_[data] | clock;
431 GPIO.out_w1tc = data_mask | clock;
432 data = this->model_ == INKPLATE_6_PLUS ? LUTW[(~buffer_value) & 0x0F] : LUTB[buffer_value & 0x0F];
433 GPIO.out_w1ts = this->pin_lut_[data] | clock;
434 GPIO.out_w1tc = data_mask | clock;
435 }
436 // New Inkplate6 panel doesn't need last clock
437 if (this->model_ != INKPLATE_6_V2) {
438 GPIO.out_w1ts = clock;
439 GPIO.out_w1tc = data_mask | clock;
440 }
441 vscan_end_();
442 }
444 }
445 ESP_LOGV(TAG, "Display1b first loop x %d (%" PRIu32 "ms)", 4, millis() - start_time);
446
447 buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
448 vscan_start_();
449 for (int i = 0, im = this->get_height_internal(); i < im; i++) {
450 buffer_value = *(buffer_ptr--);
451 data = this->model_ == INKPLATE_6_PLUS ? LUTB[(buffer_value >> 4) & 0x0F] : LUT2[(buffer_value >> 4) & 0x0F];
452 hscan_start_(this->pin_lut_[data] | clock);
453 data = this->model_ == INKPLATE_6_PLUS ? LUTB[buffer_value & 0x0F] : LUT2[buffer_value & 0x0F];
454 GPIO.out_w1ts = this->pin_lut_[data] | clock;
455 GPIO.out_w1tc = data_mask | clock;
456
457 for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
458 buffer_value = *(buffer_ptr--);
459 data = this->model_ == INKPLATE_6_PLUS ? LUTB[(buffer_value >> 4) & 0x0F] : LUT2[(buffer_value >> 4) & 0x0F];
460 GPIO.out_w1ts = this->pin_lut_[data] | clock;
461 GPIO.out_w1tc = data_mask | clock;
462 data = this->model_ == INKPLATE_6_PLUS ? LUTB[buffer_value & 0x0F] : LUT2[buffer_value & 0x0F];
463 GPIO.out_w1ts = this->pin_lut_[data] | clock;
464 GPIO.out_w1tc = data_mask | clock;
465 }
466 // New Inkplate6 panel doesn't need last clock
467 if (this->model_ != INKPLATE_6_V2) {
468 GPIO.out_w1ts = clock;
469 GPIO.out_w1tc = data_mask | clock;
470 }
471 vscan_end_();
472 }
474 ESP_LOGV(TAG, "Display1b second loop (%" PRIu32 "ms)", millis() - start_time);
475
476 if (this->model_ == INKPLATE_6_PLUS) {
477 clean_fast_(2, 2);
478 clean_fast_(3, 1);
479 } else {
480 uint32_t send = this->pin_lut_[0];
481 vscan_start_();
482 for (int i = 0, im = this->get_height_internal(); i < im; i++) {
483 hscan_start_(send);
484 GPIO.out_w1ts = send | clock;
485 GPIO.out_w1tc = data_mask | clock;
486 for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
487 GPIO.out_w1ts = send | clock;
488 GPIO.out_w1tc = data_mask | clock;
489 GPIO.out_w1ts = send | clock;
490 GPIO.out_w1tc = data_mask | clock;
491 }
492 // New Inkplate6 panel doesn't need last clock
493 if (this->model_ != INKPLATE_6_V2) {
494 GPIO.out_w1ts = clock;
495 GPIO.out_w1tc = data_mask | clock;
496 }
497 vscan_end_();
498 }
500 ESP_LOGV(TAG, "Display1b third loop (%" PRIu32 "ms)", millis() - start_time);
501 }
502 vscan_start_();
503 eink_off_();
504 this->block_partial_ = false;
505 this->partial_updates_ = 0;
506 ESP_LOGV(TAG, "Display1b finished (%" PRIu32 "ms)", millis() - start_time);
507}
508
510 ESP_LOGV(TAG, "Display3b called");
511 uint32_t start_time = millis();
512
513 eink_on_();
514
515 switch (this->model_) {
516 case INKPLATE_10:
517 if (this->custom_waveform_) {
518 clean_fast_(1, 1);
519 clean_fast_(0, 7);
520 clean_fast_(2, 1);
521 clean_fast_(1, 12);
522 clean_fast_(2, 1);
523 clean_fast_(0, 7);
524 clean_fast_(2, 1);
525 clean_fast_(1, 12);
526 } else {
527 clean_fast_(1, 1);
528 clean_fast_(0, 10);
529 clean_fast_(2, 1);
530 clean_fast_(1, 10);
531 clean_fast_(2, 1);
532 clean_fast_(0, 10);
533 clean_fast_(2, 1);
534 clean_fast_(1, 10);
535 }
536 break;
537 case INKPLATE_6_PLUS:
538 clean_fast_(0, 1);
539 clean_fast_(1, 15);
540 clean_fast_(2, 1);
541 clean_fast_(0, 5);
542 clean_fast_(2, 1);
543 clean_fast_(1, 15);
544 break;
545 case INKPLATE_6:
546 case INKPLATE_6_V2:
547 clean_fast_(0, 1);
548 clean_fast_(1, 18);
549 clean_fast_(2, 1);
550 clean_fast_(0, 18);
551 clean_fast_(2, 1);
552 clean_fast_(1, 18);
553 clean_fast_(2, 1);
554 clean_fast_(0, 18);
555 clean_fast_(2, 1);
556 break;
557 case INKPLATE_5:
558 clean_fast_(0, 1);
559 clean_fast_(1, 14);
560 clean_fast_(2, 1);
561 clean_fast_(0, 14);
562 clean_fast_(2, 1);
563 clean_fast_(1, 14);
564 clean_fast_(2, 1);
565 clean_fast_(0, 14);
566 clean_fast_(2, 1);
567 break;
568 case INKPLATE_5_V2:
569 clean_fast_(0, 1);
570 clean_fast_(1, 11);
571 clean_fast_(2, 1);
572 clean_fast_(0, 11);
573 clean_fast_(2, 1);
574 clean_fast_(1, 11);
575 clean_fast_(2, 1);
576 clean_fast_(0, 11);
577 break;
578 }
579
580 uint32_t clock = (1UL << this->cl_pin_->get_pin());
581 uint32_t data_mask = this->get_data_pin_mask_();
583 uint32_t data;
584 uint8_t glut_size = 9;
585 for (int k = 0; k < glut_size; k++) {
586 pos = this->get_buffer_length_();
587 vscan_start_();
588 for (int i = 0; i < this->get_height_internal(); i++) {
589 data = this->glut2_[k * 256 + this->buffer_[--pos]];
590 data |= this->glut_[k * 256 + this->buffer_[--pos]];
591 hscan_start_(data);
592 data = this->glut2_[k * 256 + this->buffer_[--pos]];
593 data |= this->glut_[k * 256 + this->buffer_[--pos]];
594 GPIO.out_w1ts = data | clock;
595 GPIO.out_w1tc = data_mask | clock;
596
597 for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
598 data = this->glut2_[k * 256 + this->buffer_[--pos]];
599 data |= this->glut_[k * 256 + this->buffer_[--pos]];
600 GPIO.out_w1ts = data | clock;
601 GPIO.out_w1tc = data_mask | clock;
602 data = this->glut2_[k * 256 + this->buffer_[--pos]];
603 data |= this->glut_[k * 256 + this->buffer_[--pos]];
604 GPIO.out_w1ts = data | clock;
605 GPIO.out_w1tc = data_mask | clock;
606 }
607 // New Inkplate6 panel doesn't need last clock
608 if (this->model_ != INKPLATE_6_V2) {
609 GPIO.out_w1ts = clock;
610 GPIO.out_w1tc = data_mask | clock;
611 }
612 vscan_end_();
613 }
615 }
616 clean_fast_(3, 1);
617 vscan_start_();
618 eink_off_();
619 ESP_LOGV(TAG, "Display3b finished (%" PRIu32 "ms)", millis() - start_time);
620}
621
623 ESP_LOGV(TAG, "Partial update called");
624 uint32_t start_time = millis();
625 if (this->greyscale_)
626 return false;
627 if (this->block_partial_)
628 return false;
629
630 this->partial_updates_++;
631
632 uint32_t pos = this->get_buffer_length_() - 1;
633 uint8_t data;
634 uint8_t diffw, diffb;
635 uint32_t n = (this->get_buffer_length_() * 2) - 1;
636
637 for (int i = 0, im = this->get_height_internal(); i < im; i++) {
638 for (int j = 0, jm = (this->get_width_internal() / 8); j < jm; j++) {
639 diffw = this->buffer_[pos] & ~(this->partial_buffer_[pos]);
640 diffb = ~(this->buffer_[pos]) & this->partial_buffer_[pos];
641 pos--;
642 this->partial_buffer_2_[n--] = LUTW[diffw >> 4] & LUTB[diffb >> 4];
643 this->partial_buffer_2_[n--] = LUTW[diffw & 0x0F] & LUTB[diffb & 0x0F];
644 }
645 }
646 ESP_LOGV(TAG, "Partial update buffer built after (%" PRIu32 "ms)", millis() - start_time);
647
648 int rep = (this->model_ == INKPLATE_6_V2) ? 6 : 5;
649
650 eink_on_();
651 uint32_t clock = (1UL << this->cl_pin_->get_pin());
652 uint32_t data_mask = this->get_data_pin_mask_();
653 for (int k = 0; k < rep; k++) {
654 vscan_start_();
655 const uint8_t *data_ptr = &this->partial_buffer_2_[(this->get_buffer_length_() * 2) - 1];
656 for (int i = 0; i < this->get_height_internal(); i++) {
657 data = *(data_ptr--);
658 hscan_start_(this->pin_lut_[data]);
659 for (int j = 0, jm = (this->get_width_internal() / 4) - 1; j < jm; j++) {
660 data = *(data_ptr--);
661 GPIO.out_w1ts = this->pin_lut_[data] | clock;
662 GPIO.out_w1tc = data_mask | clock;
663 }
664 // New Inkplate panel doesn't need last clock
665 if (this->model_ != INKPLATE_6_V2) {
666 GPIO.out_w1ts = clock;
667 GPIO.out_w1tc = data_mask | clock;
668 }
669 vscan_end_();
670 }
672 ESP_LOGV(TAG, "Partial update loop k=%d (%" PRIu32 "ms)", k, millis() - start_time);
673 }
674 clean_fast_(2, 2);
675 clean_fast_(3, 1);
676 vscan_start_();
677 eink_off_();
678
679 memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_());
680 ESP_LOGV(TAG, "Partial update finished (%" PRIu32 "ms)", millis() - start_time);
681 return true;
682}
683
685 this->ckv_pin_->digital_write(true);
687 this->spv_pin_->digital_write(false);
689 this->ckv_pin_->digital_write(false);
691 this->ckv_pin_->digital_write(true);
693 this->spv_pin_->digital_write(true);
695 this->ckv_pin_->digital_write(false);
697 this->ckv_pin_->digital_write(true);
699 this->ckv_pin_->digital_write(false);
701 this->ckv_pin_->digital_write(true);
703 this->ckv_pin_->digital_write(false);
705 this->ckv_pin_->digital_write(true);
706}
707
709 uint32_t clock = (1UL << this->cl_pin_->get_pin());
710 this->sph_pin_->digital_write(false);
711 GPIO.out_w1ts = d | clock;
712 GPIO.out_w1tc = this->get_data_pin_mask_() | clock;
713 this->sph_pin_->digital_write(true);
714 this->ckv_pin_->digital_write(true);
715}
716
718 this->ckv_pin_->digital_write(false);
719 this->le_pin_->digital_write(true);
720 this->le_pin_->digital_write(false);
722}
723
725 ESP_LOGV(TAG, "Clean called");
726 uint32_t start_time = millis();
727
728 eink_on_();
729 clean_fast_(0, 1); // White
730 clean_fast_(0, 8); // White to White
731 clean_fast_(0, 1); // White to Black
732 clean_fast_(0, 8); // Black to Black
733 clean_fast_(2, 1); // Black to White
734 clean_fast_(1, 10); // White to White
735 ESP_LOGV(TAG, "Clean finished (%" PRIu32 "ms)", millis() - start_time);
736}
737
738void Inkplate::clean_fast_(uint8_t c, uint8_t rep) {
739 ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep);
740 uint32_t start_time = millis();
741
742 eink_on_();
743 uint8_t data = 0;
744 if (c == 0) { // White
745 data = 0b10101010;
746 } else if (c == 1) { // Black
747 data = 0b01010101;
748 } else if (c == 2) { // Discharge
749 data = 0b00000000;
750 } else if (c == 3) { // Skip
751 data = 0b11111111;
752 }
753
754 uint32_t send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
755 (((data & 0b11100000) >> 5) << 25);
756 uint32_t clock = (1UL << this->cl_pin_->get_pin());
757
758 for (int k = 0; k < rep; k++) {
759 vscan_start_();
760 for (int i = 0; i < this->get_height_internal(); i++) {
761 hscan_start_(send);
762 GPIO.out_w1ts = send | clock;
763 GPIO.out_w1tc = clock;
764 for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
765 GPIO.out_w1ts = clock;
766 GPIO.out_w1tc = clock;
767 GPIO.out_w1ts = clock;
768 GPIO.out_w1tc = clock;
769 }
770 // New Inkplate panel doesn't need last clock
771 if (this->model_ != INKPLATE_6_V2) {
772 GPIO.out_w1ts = send | clock;
773 GPIO.out_w1tc = clock;
774 }
775 vscan_end_();
776 }
778 ESP_LOGV(TAG, "Clean fast rep loop %d finished (%" PRIu32 "ms)", k, millis() - start_time);
779 }
780 ESP_LOGV(TAG, "Clean fast finished (%" PRIu32 "ms)", millis() - start_time);
781}
782
802
822
823} // namespace inkplate
824} // namespace esphome
void mark_failed()
Mark this component as failed.
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
virtual void digital_write(bool value)=0
virtual uint8_t get_pin() const =0
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2212
void deallocate(T *p, size_t n)
Definition helpers.h:2267
T * allocate(size_t n)
Definition helpers.h:2229
Rect get_clipping() const
Get the current the clipping rectangle.
Definition display.cpp:766
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len)
reads an array of bytes from a specific register in the I²C device
Definition i2c.cpp:25
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const
Definition i2c.h:251
InternalGPIOPin * cl_pin_
Definition inkplate.h:201
void fill(Color color) override
Definition inkplate.cpp:304
InternalGPIOPin * display_data_0_pin_
Definition inkplate.h:191
void clean_fast_(uint8_t c, uint8_t rep)
Definition inkplate.cpp:738
void hscan_start_(uint32_t d)
Definition inkplate.cpp:708
InternalGPIOPin * display_data_7_pin_
Definition inkplate.h:198
InternalGPIOPin * le_pin_
Definition inkplate.h:204
InternalGPIOPin * display_data_4_pin_
Definition inkplate.h:195
InternalGPIOPin * display_data_1_pin_
Definition inkplate.h:192
void draw_absolute_pixel_internal(int x, int y, Color color) override
Definition inkplate.cpp:161
uint8_t waveform_[GLUT_COUNT][GLUT_SIZE]
Definition inkplate.h:187
void dump_config() override
Definition inkplate.cpp:193
int get_width_internal() override
Definition inkplate.h:123
InternalGPIOPin * display_data_6_pin_
Definition inkplate.h:197
InternalGPIOPin * display_data_3_pin_
Definition inkplate.h:194
InternalGPIOPin * display_data_5_pin_
Definition inkplate.h:196
InternalGPIOPin * display_data_2_pin_
Definition inkplate.h:193
int get_height_internal() override
Definition inkplate.h:138
float get_setup_priority() const override
Definition inkplate.cpp:141
void initialize_()
Allocate buffers.
Definition inkplate.cpp:61
bool z
Definition msa3xx.h:1
@ FLAG_OUTPUT
Definition gpio.h:28
@ FLAG_INPUT
Definition gpio.h:27
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
constexpr float PROCESSOR
For components that use data from sensors like displays.
Definition component.h:44
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:30
size_t size_t pos
Definition helpers.h:1082
void HOT delay(uint32_t ms)
Definition core.cpp:28
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:26
static void uint32_t
uint8_t red
Definition color.h:31
uint8_t green
Definition color.h:35
bool is_on() ESPHOME_ALWAYS_INLINE
Definition color.h:70
uint8_t blue
Definition color.h:39
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6