-
Notifications
You must be signed in to change notification settings - Fork 56
/
Adafruit_EPD.cpp
713 lines (638 loc) · 18.7 KB
/
Adafruit_EPD.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
/*!
* @file Adafruit_EPD.cpp
*
* @mainpage Adafruit EPD driver
*
* @section intro_sec Introduction
*
* This is the documentation for Adafruit's EPD driver for the
* Arduino platform. It is designed specifically to work with the
* Adafruit EPD breakouts.
*
* These displays use SPI to communicate, 6 pins are required to
* interface
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* @section dependencies Dependencies
*
* This library depends on <a
* href="https://github.com/adafruit/Adafruit-GFX-Library"> Adafruit_GFX</a>
* being present on your system. Please make sure you have installed the latest
* version before using this library.
*
* @section author Author
*
* Written by Dean Miller for Adafruit Industries.
*
* @section license License
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Adafruit_EPD.h"
#include <stdlib.h>
bool Adafruit_EPD::_isInTransaction = false;
/**************************************************************************/
/*!
@brief constructor if using external SRAM chip and software SPI
@param width the width of the display in pixels
@param height the height of the display in pixels
@param spi_mosi the SID pin to use
@param spi_clock the SCLK pin to use
@param DC the data/command pin to use
@param RST the reset pin to use
@param CS the chip select pin to use
@param SRCS the SRAM chip select pin to use
@param spi_miso the MISO pin to use
@param BUSY the busy pin to use
*/
/**************************************************************************/
Adafruit_EPD::Adafruit_EPD(int width, int height, int16_t spi_mosi,
int16_t spi_clock, int16_t DC, int16_t RST,
int16_t CS, int16_t SRCS, int16_t spi_miso,
int16_t BUSY)
: Adafruit_GFX(width, height), sram(spi_mosi, spi_miso, spi_clock, SRCS) {
_cs_pin = CS;
_reset_pin = RST;
_dc_pin = DC;
_busy_pin = BUSY;
if (SRCS >= 0) {
use_sram = true;
} else {
use_sram = false;
}
spi_dev = new Adafruit_SPIDevice(CS, spi_clock, spi_miso, spi_mosi,
4000000, // frequency
SPI_BITORDER_MSBFIRST, // bit order
SPI_MODE0 // data mode
);
singleByteTxns = false;
buffer1_size = buffer2_size = 0;
buffer1_addr = buffer2_addr = 0;
colorbuffer_addr = blackbuffer_addr = 0;
buffer1 = buffer2 = color_buffer = black_buffer = NULL;
}
// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset
/**************************************************************************/
/*!
@brief constructor if using on-chip RAM and hardware SPI
@param width the width of the display in pixels
@param height the height of the display in pixels
@param DC the data/command pin to use
@param RST the reset pin to use
@param CS the chip select pin to use
@param SRCS the SRAM chip select pin to use
@param BUSY the busy pin to use
@param spi the SPI bus to use
*/
/**************************************************************************/
Adafruit_EPD::Adafruit_EPD(int width, int height, int16_t DC, int16_t RST,
int16_t CS, int16_t SRCS, int16_t BUSY,
SPIClass *spi)
: Adafruit_GFX(width, height), sram(SRCS) {
_cs_pin = CS;
_reset_pin = RST;
_dc_pin = DC;
_busy_pin = BUSY;
if (SRCS >= 0) {
use_sram = true;
} else {
use_sram = false;
}
spi_dev = new Adafruit_SPIDevice(CS,
4000000, // frequency
SPI_BITORDER_MSBFIRST, // bit order
SPI_MODE0, // data mode
spi);
singleByteTxns = false;
buffer1_size = buffer2_size = 0;
buffer1_addr = buffer2_addr = 0;
colorbuffer_addr = blackbuffer_addr = 0;
buffer1 = buffer2 = color_buffer = black_buffer = NULL;
}
/**************************************************************************/
/*!
@brief default destructor
*/
/**************************************************************************/
Adafruit_EPD::~Adafruit_EPD() {
if (buffer1 != NULL) {
free(buffer1);
buffer1 = NULL;
}
if (buffer2 != NULL) {
free(buffer2);
buffer2 = NULL;
}
}
/**************************************************************************/
/*!
@brief begin communication with and set up the display.
@param reset if true the reset pin will be toggled.
*/
/**************************************************************************/
void Adafruit_EPD::begin(bool reset) {
setBlackBuffer(0, true); // black defaults to inverted
setColorBuffer(1, false); // red defaults to not inverted
layer_colors[EPD_WHITE] = 0b00;
layer_colors[EPD_BLACK] = 0b01;
layer_colors[EPD_RED] = 0b10;
layer_colors[EPD_GRAY] = 0b10;
layer_colors[EPD_DARK] = 0b01;
layer_colors[EPD_LIGHT] = 0b10;
if (use_sram) {
sram.begin();
sram.write8(0, K640_SEQUENTIAL_MODE, MCPSRAM_WRSR);
}
// Serial.println("set pins");
// set pin directions
pinMode(_dc_pin, OUTPUT);
pinMode(_cs_pin, OUTPUT);
#if defined(BUSIO_USE_FAST_PINIO)
csPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(_cs_pin));
csPinMask = digitalPinToBitMask(_cs_pin);
dcPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(_dc_pin));
dcPinMask = digitalPinToBitMask(_dc_pin);
#endif
csHigh();
if (!spi_dev->begin()) {
return;
}
// Serial.println("hard reset");
if (reset) {
hardwareReset();
}
// Serial.println("busy");
if (_busy_pin >= 0) {
pinMode(_busy_pin, INPUT);
}
// Serial.println("done!");
}
/**************************************************************************/
/*!
@brief reset Perform a hardware reset
*/
/**************************************************************************/
void Adafruit_EPD::hardwareReset(void) {
if (_reset_pin >= 0) {
// Setup reset pin direction
pinMode(_reset_pin, OUTPUT);
// VDD (3.3V) goes high at start, lets just chill for a ms
digitalWrite(_reset_pin, HIGH);
delay(10);
// bring reset low
digitalWrite(_reset_pin, LOW);
// wait 10ms
delay(10);
// bring out of reset
digitalWrite(_reset_pin, HIGH);
delay(10);
}
}
/**************************************************************************/
/*!
@brief draw a single pixel on the screen
@param x the x axis position
@param y the y axis position
@param color the color of the pixel
*/
/**************************************************************************/
void Adafruit_EPD::drawPixel(int16_t x, int16_t y, uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
return;
uint8_t *black_pBuf, *color_pBuf;
// check rotation, move pixel around if necessary
switch (getRotation()) {
case 1:
EPD_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
EPD_swap(x, y);
y = HEIGHT - y - 1;
break;
}
// deal with non-8-bit heights
uint16_t _HEIGHT = HEIGHT;
if (_HEIGHT % 8 != 0) {
_HEIGHT += 8 - (_HEIGHT % 8);
}
uint16_t addr = ((uint32_t)(WIDTH - 1 - x) * (uint32_t)_HEIGHT + y) / 8;
uint8_t black_c, color_c;
if (use_sram) {
black_c = sram.read8(blackbuffer_addr + addr);
black_pBuf = &black_c;
color_c = sram.read8(colorbuffer_addr + addr);
color_pBuf = &color_c;
} else {
color_pBuf = color_buffer + addr;
black_pBuf = black_buffer + addr;
}
bool color_bit, black_bit;
black_bit = layer_colors[color] & 0x1;
color_bit = layer_colors[color] & 0x2;
if ((color_bit && colorInverted) || (!color_bit && !colorInverted)) {
*color_pBuf &= ~(1 << (7 - y % 8));
} else {
*color_pBuf |= (1 << (7 - y % 8));
}
if ((black_bit && blackInverted) || (!black_bit && !blackInverted)) {
*black_pBuf &= ~(1 << (7 - y % 8));
} else {
*black_pBuf |= (1 << (7 - y % 8));
}
if (use_sram) {
sram.write8(colorbuffer_addr + addr, *color_pBuf);
sram.write8(blackbuffer_addr + addr, *black_pBuf);
}
}
void Adafruit_EPD::writeRAMFramebufferToEPD(uint8_t *framebuffer,
uint32_t framebuffer_size,
uint8_t EPDlocation,
bool invertdata) {
// write image
writeRAMCommand(EPDlocation);
dcHigh();
// Serial.printf("Writing from RAM location %04x: \n", &framebuffer);
for (uint32_t i = 0; i < framebuffer_size; i++) {
uint8_t d = framebuffer[i];
if (invertdata)
d = ~d;
/*
Serial.printf("%02x", d);
if ((i+1) % (WIDTH/8) == 0)
Serial.println();
*/
SPItransfer(d);
}
// Serial.println();
csHigh();
return;
}
void Adafruit_EPD::writeSRAMFramebufferToEPD(uint16_t SRAM_buffer_addr,
uint32_t buffer_size,
uint8_t EPDlocation,
bool invertdata) {
(void)invertdata;
uint8_t c;
// use SRAM
sram.csLow();
_isInTransaction = true;
// send read command
SPItransfer(MCPSRAM_READ);
// send address
SPItransfer(SRAM_buffer_addr >> 8);
SPItransfer(SRAM_buffer_addr & 0xFF);
// first data byte from SRAM will be transfered in at the same time
// as the EPD command is transferred out
c = writeRAMCommand(EPDlocation);
dcHigh();
for (uint32_t i = 0; i < buffer_size; i++) {
c = SPItransfer(c);
/*
Serial.print("0x"); Serial.print((byte)c, HEX); Serial.print(", ");
if (i % 32 == 31) {
Serial.println();
Serial.print("$");
Serial.print(i, HEX);
Serial.print(": ");
}
*/
}
csHigh();
sram.csHigh();
_isInTransaction = false;
}
/**************************************************************************/
/*!
@brief Transfer the data stored in the buffer(s) to the display
*/
/**************************************************************************/
void Adafruit_EPD::display(bool sleep) {
#ifdef EPD_DEBUG
Serial.println(" Powering Up");
#endif
powerUp();
#ifdef EPD_DEBUG
Serial.println(" Set RAM address");
#endif
// Set X & Y ram counters
setRAMAddress(0, 0);
if (use_sram) {
#ifdef EPD_DEBUG
Serial.println(" Write SRAM buff to EPD");
#endif
writeSRAMFramebufferToEPD(buffer1_addr, buffer1_size, 0);
} else {
#ifdef EPD_DEBUG
Serial.println(" Write RAM buff to EPD");
#endif
writeRAMFramebufferToEPD(buffer1, buffer1_size, 0);
}
if (buffer2_size != 0) {
// oh there's another buffer eh?
delay(2);
// Set X & Y ram counters
setRAMAddress(0, 0);
if (use_sram) {
writeSRAMFramebufferToEPD(buffer2_addr, buffer2_size, 1);
} else {
writeRAMFramebufferToEPD(buffer2, buffer2_size, 1);
}
}
#ifdef EPD_DEBUG
Serial.println(" Update");
#endif
update();
partialsSinceLastFullUpdate = 0;
if (sleep) {
#ifdef EPD_DEBUG
Serial.println(" Powering Down");
#endif
powerDown();
}
}
/**************************************************************************/
/*!
@brief Determine whether the black pixel data is the first or second buffer
@param index 0 or 1, for primary or secondary value
@param inverted Whether to invert the logical value
*/
/**************************************************************************/
void Adafruit_EPD::setBlackBuffer(int8_t index, bool inverted) {
if (index == 0) {
if (use_sram) {
blackbuffer_addr = buffer1_addr;
} else {
black_buffer = buffer1;
}
}
if (index == 1) {
if (use_sram) {
blackbuffer_addr = buffer2_addr;
} else {
black_buffer = buffer2;
}
}
blackInverted = inverted;
}
/**************************************************************************/
/*!
@brief Determine whether the color pixel data is the first or second buffer
@param index 0 or 1, for primary or secondary value
@param inverted Whether to invert the logical value
*/
/**************************************************************************/
void Adafruit_EPD::setColorBuffer(int8_t index, bool inverted) {
if (index == 0) {
if (use_sram) {
colorbuffer_addr = buffer1_addr;
} else {
color_buffer = buffer1;
}
}
if (index == 1) {
if (use_sram) {
colorbuffer_addr = buffer2_addr;
} else {
color_buffer = buffer2;
}
}
colorInverted = inverted;
}
/**************************************************************************/
/*!
@brief clear all data buffers
*/
/**************************************************************************/
void Adafruit_EPD::clearBuffer() {
if (use_sram) {
if (blackInverted) {
sram.erase(blackbuffer_addr, buffer1_size, 0xFF);
} else {
sram.erase(blackbuffer_addr, buffer1_size, 0x00);
}
if (colorInverted) {
sram.erase(colorbuffer_addr, buffer2_size, 0xFF);
} else {
sram.erase(colorbuffer_addr, buffer2_size, 0x00);
}
} else {
if (black_buffer) {
if (blackInverted) {
memset(black_buffer, 0xFF, buffer1_size);
} else {
memset(black_buffer, 0x00, buffer1_size);
}
}
if (color_buffer) {
if (colorInverted) {
memset(color_buffer, 0xFF, buffer2_size);
} else {
memset(color_buffer, 0x00, buffer2_size);
}
}
}
}
/**************************************************************************/
/*!
@brief clear the display twice to remove any spooky ghost images
*/
/**************************************************************************/
void Adafruit_EPD::clearDisplay() {
clearBuffer();
display();
delay(100);
display();
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void Adafruit_EPD::EPD_commandList(const uint8_t *init_code) {
uint8_t buf[64];
while (init_code[0] != 0xFE) {
uint8_t cmd = init_code[0];
init_code++;
uint8_t num_args = init_code[0];
init_code++;
if (cmd == 0xFF) {
busy_wait();
delay(num_args);
continue;
}
if (num_args > sizeof(buf)) {
Serial.println("ERROR - buf not large enough!");
while (1)
delay(10);
}
for (int i = 0; i < num_args; i++) {
buf[i] = init_code[0];
init_code++;
}
EPD_command(cmd, buf, num_args);
}
}
/**************************************************************************/
/*!
@brief send an EPD command followed by data
@param c the command to send
@param buf the buffer of data to send
@param len the length of the data buffer
*/
/**************************************************************************/
void Adafruit_EPD::EPD_command(uint8_t c, const uint8_t *buf, uint16_t len) {
EPD_command(c, false);
EPD_data(buf, len);
}
/**************************************************************************/
/*!
@brief send an EPD command with no data
@param c the command to send
@param end if true the cs pin will be pulled high following the transaction.
If false the cs pin will remain low.
@returns the data byte read over the SPI bus
*/
/**************************************************************************/
uint8_t Adafruit_EPD::EPD_command(uint8_t c, bool end) {
// SPI
csHigh();
dcLow();
csLow();
uint8_t data = SPItransfer(c);
#ifdef EPD_DEBUG
Serial.print("\tCommand: 0x");
Serial.println(c, HEX);
#endif
if (end) {
csHigh();
}
return data;
}
/**************************************************************************/
/*!
@brief send data to the display
@param buf the data buffer to send
@param len the length of the data buffer
*/
/**************************************************************************/
void Adafruit_EPD::EPD_data(const uint8_t *buf, uint16_t len) {
// SPI
dcHigh();
#ifdef EPD_DEBUG
Serial.print("\tData: ");
#endif
for (uint16_t i = 0; i < len; i++) {
SPItransfer(buf[i]);
#ifdef EPD_DEBUG
Serial.print("0x");
Serial.print(buf[i], HEX);
Serial.print(", ");
#endif
}
#ifdef EPD_DEBUG
Serial.println();
#endif
csHigh();
}
/**************************************************************************/
/*!
@brief send data to the display
@param data the data byte to send
*/
/**************************************************************************/
void Adafruit_EPD::EPD_data(uint8_t data) {
// SPI
csHigh();
dcHigh();
csLow();
#ifdef DEBUG
Serial.print("Data: ");
Serial.print("0x");
Serial.println(data, HEX);
#endif
SPItransfer(data);
csHigh();
}
/**************************************************************************/
/*!
@brief transfer a single byte over SPI.
@param d the data to send
@returns the data byte read
*/
/**************************************************************************/
uint8_t Adafruit_EPD::SPItransfer(uint8_t d) {
// Serial.print("-> 0x"); Serial.println((byte)d, HEX);
if (singleByteTxns) {
uint8_t b;
csLow();
b = spi_dev->transfer(d);
csHigh();
return b;
} else {
return spi_dev->transfer(d);
}
}
/**************************************************************************/
/*!
@brief set chip select pin high
*/
/**************************************************************************/
void Adafruit_EPD::csHigh() {
#ifdef BUSIO_USE_FAST_PINIO
*csPort = *csPort | csPinMask;
#else
digitalWrite(_cs_pin, HIGH);
#endif
if (_isInTransaction) {
spi_dev->endTransaction();
_isInTransaction = false;
}
}
/**************************************************************************/
/*!
@brief set chip select pin low
*/
/**************************************************************************/
void Adafruit_EPD::csLow() {
if (!_isInTransaction) {
spi_dev->beginTransaction();
_isInTransaction = true;
}
#ifdef BUSIO_USE_FAST_PINIO
*csPort = *csPort & ~csPinMask;
#else
digitalWrite(_cs_pin, LOW);
#endif
}
/**************************************************************************/
/*!
@brief set data/command pin high
*/
/**************************************************************************/
void Adafruit_EPD::dcHigh() {
#ifdef BUSIO_USE_FAST_PINIO
*dcPort = *dcPort | dcPinMask;
#else
digitalWrite(_dc_pin, HIGH);
#endif
}
/**************************************************************************/
/*!
@brief set data/command pin low
*/
/**************************************************************************/
void Adafruit_EPD::dcLow() {
#ifdef BUSIO_USE_FAST_PINIO
*dcPort = *dcPort & ~dcPinMask;
#else
digitalWrite(_dc_pin, LOW);
#endif
}