1+ // Header guard for the Arduino include
2+ #ifdef ARDUINO_INKPLATE2
3+ #include " Inkplate2Driver.h"
4+ #include " Inkplate.h"
5+
6+ SPIClass epdSPI (VSPI);
7+
8+ SPISettings epdSpiSettings (1000000UL , MSBFIRST, SPI_MODE0);
9+
10+ /* *
11+ *
12+ * @brief writePixelInternal funtion sets pixel data for (x, y) pixel position
13+ *
14+ * @param int16_t x0
15+ * default position for x, will be changed depending on rotation
16+ * @param int16_t y0
17+ * default position for y, will be changed depending on rotation
18+ * @param uint16_t color
19+ * pixel color, in 3bit mode have values in range 0-7
20+ *
21+ * @note If x0 or y0 are out of inkplate screen borders, function will
22+ * exit.
23+ */
24+ void EPDDriver::writePixelInternal (int16_t x0, int16_t y0, uint16_t color)
25+ {
26+ if (x0 > _inkplate->width () - 1 || y0 > _inkplate->height () - 1 || x0 < 0 || y0 < 0 )
27+ return ;
28+ if (color > 2 )
29+ return ;
30+
31+ switch (_inkplate->getRotation ()) // FIXED
32+ {
33+ case 3 :
34+ _swap_int16_t (x0, y0);
35+ x0 = _inkplate->height () - x0 - 1 ;
36+ break ;
37+ case 0 :
38+ x0 = _inkplate->width () - x0 - 1 ;
39+ y0 = _inkplate->height () - y0 - 1 ;
40+ break ;
41+ case 1 :
42+ _swap_int16_t (x0, y0);
43+ y0 = _inkplate->width () - y0 - 1 ;
44+ break ;
45+ }
46+
47+ // Find the specific byte in the frame buffer that needs to be modified.
48+ // Also find the bit in the byte that needs modification.
49+ int _x = x0 / 8 ;
50+ int _xSub = x0 % 8 ;
51+
52+ int _position = E_INK_WIDTH/ 8 * y0 + _x;
53+
54+ // Clear both black and red frame buffer.
55+ *(DMemory4Bit + _position) |= (pixelMaskLUT[7 - _xSub]);
56+ *(DMemory4Bit + (E_INK_WIDTH * E_INK_HEIGHT / 8 ) + _position) |= (pixelMaskLUT[7 - _xSub]);
57+
58+ // To optimize writing pixels into EPD, framebuffer is split in half, where first half is for B&W pixels and other
59+ // half is for red pixels only
60+ if (color < 2 )
61+ {
62+ *(DMemory4Bit + _position) &= ~(color << (7 - _xSub));
63+ }
64+ else
65+ {
66+ *(DMemory4Bit + (E_INK_WIDTH * E_INK_HEIGHT / 8 ) + _position) &= ~(pixelMaskLUT[7 - _xSub]);
67+ }
68+ }
69+
70+
71+ /* *
72+ * @brief begin function initialize Inkplate object with predefined
73+ * settings
74+ *
75+ * @param uint8_t lightWaveform
76+ * if inkplate doesn't work well or if it is fading after turning off
77+ * lightWaveform should be set to 1 in order to fix that, but older boards
78+ * may not support it
79+ *
80+ * @return True if initialization is successful, false if failed or already
81+ * initialized
82+ */
83+ int EPDDriver::initDriver (Inkplate *_inkplatePtr)
84+ {
85+ if (!_beginDone)
86+ {
87+ // Allocate memory for frame buffer
88+ DMemory4Bit = (uint8_t *)ps_malloc (E_INK_WIDTH * E_INK_HEIGHT / 4 );
89+
90+ _inkplate = _inkplatePtr;
91+
92+ image.begin (_inkplatePtr);
93+
94+ if (DMemory4Bit == NULL )
95+ {
96+ return false ;
97+ }
98+
99+ // Clear frame buffer
100+ clearDisplay ();
101+
102+ // Set default rotation
103+ _inkplate->setRotation (1 );
104+
105+ _beginDone = 1 ;
106+ }
107+
108+ // Wake the ePaper and initialize everything
109+ // If it fails, return false
110+ if (!setPanelDeepSleep (false ))
111+ return false ;
112+
113+ // Put the panel to deep sleep
114+ // The panel is always in sleep unless it's being written display data to
115+ setPanelDeepSleep (true );
116+ return true ;
117+ }
118+
119+
120+
121+ /* *
122+ * @brief clearDisplay function clears memory buffer for display
123+ *
124+ * @note This does not clear the actual display, only the memory buffer, you need to call
125+ * display() function after this to clear the display
126+ */
127+ void EPDDriver::clearDisplay ()
128+ {
129+ memset (DMemory4Bit, 0xFF , E_INK_WIDTH * E_INK_HEIGHT / 4 );
130+ }
131+
132+ /* *
133+ * @brief display function update display with new data from buffer
134+ *
135+ * @param bool leaveOn
136+ * if set to 1, it will disable turning supply for eink after
137+ * display update in order to save some time needed for power supply
138+ * to save some time at next display update or increase refreshing speed
139+ */
140+ void EPDDriver::display (bool _leaveOn)
141+ {
142+ // Wake the panel and wait a bit
143+ // The refresh time is long anyway so this delay doesn't make much impact
144+ setPanelDeepSleep (false );
145+ delay (20 );
146+
147+ // First write B&W pixels to epaper
148+ sendCommand (0x10 );
149+ sendData (DMemory4Bit, (E_INK_WIDTH * E_INK_HEIGHT / 8 ));
150+
151+ // Now write red pixels to epaper
152+ sendCommand (0x13 );
153+ sendData (DMemory4Bit + (E_INK_WIDTH * E_INK_HEIGHT / 8 ), (E_INK_WIDTH * E_INK_HEIGHT / 8 ));
154+
155+ // Stop data transfer
156+ sendCommand (0x11 );
157+ sendData (0x00 );
158+
159+ // Send display refresh command
160+ sendCommand (0x12 );
161+ delayMicroseconds (500 ); // Wait at least 200 uS
162+ waitForEpd (60000 );
163+
164+ // Go back to sleep
165+ setPanelDeepSleep (true );
166+ }
167+
168+
169+
170+ uint8_t EPDDriver::getPanelState ()
171+ {
172+ return _panelState;
173+ }
174+ void EPDDriver::setPanelState (uint8_t state)
175+ {
176+ _panelState = state;
177+ }
178+
179+
180+ /* *
181+ * @brief resetPanel resets Inkplate 6COLOR
182+ */
183+ void EPDDriver::resetPanel ()
184+ {
185+ digitalWrite (EPAPER_RST_PIN, LOW);
186+ delay (100 );
187+ digitalWrite (EPAPER_RST_PIN, HIGH);
188+ delay (100 );
189+ }
190+
191+ /* *
192+ * @brief sendCommand sends SPI command to Inkplate 6COLOR
193+ *
194+ * @param uint8_t _command
195+ * predefined command for epaper control
196+ */
197+ void EPDDriver::sendCommand (uint8_t _command)
198+ {
199+ digitalWrite (EPAPER_CS_PIN, LOW);
200+ digitalWrite (EPAPER_DC_PIN, LOW);
201+ delayMicroseconds (10 );
202+ epdSPI.beginTransaction (epdSpiSettings);
203+ epdSPI.transfer (_command);
204+ epdSPI.endTransaction ();
205+ digitalWrite (EPAPER_CS_PIN, HIGH);
206+ delay (1 );
207+ }
208+
209+ /* *
210+ * @brief sendData sends SPI data to Inkplate 6COLOR
211+ *
212+ * @param uint8_t *_data
213+ * pointer to data buffer to be sent to epaper
214+ * @param int _n
215+ * number of data bytes
216+ */
217+ void EPDDriver::sendData (uint8_t *_data, int _n)
218+ {
219+ digitalWrite (EPAPER_CS_PIN, LOW);
220+ digitalWrite (EPAPER_DC_PIN, HIGH);
221+ delayMicroseconds (10 );
222+ epdSPI.beginTransaction (epdSpiSettings);
223+ epdSPI.writeBytes (_data, _n);
224+ epdSPI.endTransaction ();
225+ digitalWrite (EPAPER_CS_PIN, HIGH);
226+ delay (1 );
227+ }
228+
229+ /* *
230+ * @brief sendData sends SPI data to Inkplate 6COLOR
231+ *
232+ * @param uint8_t _data
233+ * data buffer to be sent to epaper
234+ */
235+ void EPDDriver::sendData (uint8_t _data)
236+ {
237+ digitalWrite (EPAPER_CS_PIN, LOW);
238+ digitalWrite (EPAPER_DC_PIN, HIGH);
239+ delayMicroseconds (10 );
240+ epdSPI.beginTransaction (epdSpiSettings);
241+ epdSPI.transfer (_data);
242+ epdSPI.endTransaction ();
243+ digitalWrite (EPAPER_CS_PIN, HIGH);
244+ delay (1 );
245+ }
246+
247+ /* *
248+ * @brief setPanelDeepSleep puts the color ePaper into deep sleep, or wakes it and reinitializes it
249+ *
250+ * @param bool _state
251+ * -'True' sets the panel to sleep
252+ * -'False' wakes the panel
253+ *
254+ * @returns True if successful, False if unsuccessful
255+ *
256+ */
257+ bool EPDDriver::setPanelDeepSleep (bool _state)
258+ {
259+ if (!_state)
260+ {
261+ // _state is false? Wake the panel!
262+
263+ // Set SPI pins
264+ epdSPI.begin (EPAPER_CLK, -1 , EPAPER_DIN, -1 );
265+
266+ // Set up EPD communication pins
267+ pinMode (EPAPER_CS_PIN, OUTPUT);
268+ pinMode (EPAPER_DC_PIN, OUTPUT);
269+ pinMode (EPAPER_RST_PIN, OUTPUT);
270+ pinMode (EPAPER_BUSY_PIN, INPUT_PULLUP);
271+
272+ delay (10 );
273+
274+ // Reinit the panel
275+ // Reset EPD IC
276+ resetPanel ();
277+
278+ sendCommand (0x04 );
279+ if (!waitForEpd (BUSY_TIMEOUT_MS))
280+ return false ; // Waiting for the electronic paper IC to release the idle signal
281+
282+ sendCommand (0x00 ); // Enter panel setting
283+ sendData (0x0f ); // LUT from OTP 128x296
284+ sendData (0x89 ); // Temperature sensor, boost and other related timing settings
285+
286+ sendCommand (0x61 ); // Enter panel resolution setting
287+ sendData (E_INK_WIDTH);
288+ sendData (E_INK_HEIGHT >> 8 );
289+ sendData (E_INK_HEIGHT & 0xff );
290+
291+ sendCommand (0x50 ); // VCOM and data interval setting
292+ sendData (0x77 ); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
293+
294+ return true ;
295+ }
296+ else
297+ {
298+ // _state is true? Put the panel to sleep.
299+
300+ sendCommand (0X50 ); // VCOM and data interval setting
301+ sendData (0xf7 );
302+ sendCommand (0X02 ); // Power EPD off
303+ waitForEpd (BUSY_TIMEOUT_MS);
304+ sendCommand (0X07 ); // Put EPD in deep sleep
305+ sendData (0xA5 );
306+ delay (1 );
307+
308+ // Disable SPI
309+ epdSPI.end ();
310+
311+ // To reduce power consumption, set SPI pins as outputs
312+ pinMode (EPAPER_RST_PIN, INPUT);
313+ pinMode (EPAPER_DC_PIN, INPUT);
314+ pinMode (EPAPER_CS_PIN, INPUT);
315+ pinMode (EPAPER_BUSY_PIN, INPUT);
316+ pinMode (EPAPER_CLK, INPUT);
317+ pinMode (EPAPER_DIN, INPUT);
318+
319+ return true ;
320+ }
321+ }
322+
323+
324+ /* *
325+ * @brief Waits for panel to be ready for data
326+ *
327+ * @param uint8_t _timeout
328+ * Timeout for wait
329+ *
330+ * @return bool is panel ready or timed out
331+ */
332+ bool EPDDriver::waitForEpd (uint16_t _timeout)
333+ {
334+ unsigned long _time = millis ();
335+ while (!digitalRead (EPAPER_BUSY_PIN) && ((millis () - _time) < _timeout))
336+ ;
337+ if (!digitalRead (EPAPER_BUSY_PIN))
338+ return false ;
339+ delay (200 );
340+ return true ;
341+ }
342+
343+
344+ #endif
0 commit comments