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