Skip to content

Commit 2f2ea1c

Browse files
committed
Add Inkplate 2 support
1 parent a9ea348 commit 2f2ea1c

File tree

4 files changed

+476
-0
lines changed

4 files changed

+476
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
**************************************************
3+
*
4+
* @file Inkplate2BoardFile.h
5+
* @brief Wrrapper for the different Inkplate boards and
6+
* it's classes.
7+
*
8+
*
9+
* @copyright GNU General Public License v3.0
10+
* @authors Borna Biro for soldered.com
11+
***************************************************/
12+
13+
// Header guard.
14+
#ifndef __INKPLATE2_BOARD_SELECT_H__
15+
#define __INKPLATE2_BOARD_SELECT_H__
16+
17+
// Board select check.
18+
#ifdef ARDUINO_INKPLATE2
19+
20+
// Include Inkplate10 board header file.
21+
#include "Inkplate2Driver.h"
22+
23+
// Wrapper for different Inkplate boards.
24+
class InkplateBoardClass : public EPDDriver
25+
{
26+
public:
27+
InkplateBoardClass(){};
28+
};
29+
30+
#endif
31+
#endif
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
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

Comments
 (0)