|
28 | 28 |
|
29 | 29 | import time |
30 | 30 | from micropython import const |
| 31 | +import adafruit_framebuf |
31 | 32 | from adafruit_epd.epd import Adafruit_EPD |
32 | | -from adafruit_epd.mcp_sram import Adafruit_MCP_SRAM |
33 | 33 |
|
34 | | -IL0373_PANEL_SETTING = const(0x00) |
35 | | -IL0373_POWER_SETTING = const(0x01) |
36 | | -IL0373_POWER_OFF = const(0x02) |
37 | | -IL0373_POWER_OFF_SEQUENCE = const(0x03) |
38 | | -IL0373_POWER_ON = const(0x04) |
39 | | -IL0373_POWER_ON_MEASURE = const(0x05) |
40 | | -IL0373_BOOSTER_SOFT_START = const(0x06) |
41 | | -IL0373_DEEP_SLEEP = const(0x07) |
42 | | -IL0373_DTM1 = const(0x10) |
43 | | -IL0373_DATA_STOP = const(0x11) |
44 | | -IL0373_DISPLAY_REFRESH = const(0x12) |
45 | | -IL0373_DTM2 = const(0x13) |
46 | | -IL0373_PDTM1 = const(0x14) |
47 | | -IL0373_PDTM2 = const(0x15) |
48 | | -IL0373_PDRF = const(0x16) |
49 | | -IL0373_LUT1 = const(0x20) |
50 | | -IL0373_LUTWW = const(0x21) |
51 | | -IL0373_LUTBW = const(0x22) |
52 | | -IL0373_LUTWB = const(0x23) |
53 | | -IL0373_LUTBB = const(0x24) |
54 | | -IL0373_PLL = const(0x30) |
55 | | -IL0373_CDI = const(0x50) |
56 | | -IL0373_RESOLUTION = const(0x61) |
57 | | -IL0373_VCM_DC_SETTING = const(0x82) |
| 34 | +_IL0373_PANEL_SETTING = const(0x00) |
| 35 | +_IL0373_POWER_SETTING = const(0x01) |
| 36 | +_IL0373_POWER_OFF = const(0x02) |
| 37 | +_IL0373_POWER_OFF_SEQUENCE = const(0x03) |
| 38 | +_IL0373_POWER_ON = const(0x04) |
| 39 | +_IL0373_POWER_ON_MEASURE = const(0x05) |
| 40 | +_IL0373_BOOSTER_SOFT_START = const(0x06) |
| 41 | +_IL0373_DEEP_SLEEP = const(0x07) |
| 42 | +_IL0373_DTM1 = const(0x10) |
| 43 | +_IL0373_DATA_STOP = const(0x11) |
| 44 | +_IL0373_DISPLAY_REFRESH = const(0x12) |
| 45 | +_IL0373_DTM2 = const(0x13) |
| 46 | +_IL0373_PDTM1 = const(0x14) |
| 47 | +_IL0373_PDTM2 = const(0x15) |
| 48 | +_IL0373_PDRF = const(0x16) |
| 49 | +_IL0373_LUT1 = const(0x20) |
| 50 | +_IL0373_LUTWW = const(0x21) |
| 51 | +_IL0373_LUTBW = const(0x22) |
| 52 | +_IL0373_LUTWB = const(0x23) |
| 53 | +_IL0373_LUTBB = const(0x24) |
| 54 | +_IL0373_PLL = const(0x30) |
| 55 | +_IL0373_CDI = const(0x50) |
| 56 | +_IL0373_RESOLUTION = const(0x61) |
| 57 | +_IL0373_VCM_DC_SETTING = const(0x82) |
58 | 58 |
|
59 | 59 | class Adafruit_IL0373(Adafruit_EPD): |
60 | 60 | """driver class for Adafruit IL0373 ePaper display breakouts""" |
61 | 61 | # pylint: disable=too-many-arguments |
62 | | - def __init__(self, width, height, rst_pin, dc_pin, busy_pin, srcs_pin, cs_pin, spi): |
63 | | - super(Adafruit_IL0373, self).__init__(width, height, rst_pin, dc_pin, busy_pin, |
64 | | - srcs_pin, cs_pin, spi) |
65 | | - |
66 | | - self.bw_bufsize = int(width * height / 8) |
67 | | - self.red_bufsize = int(width * height / 8) |
68 | | - |
69 | | - self.begin() |
| 62 | + def __init__(self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin): |
| 63 | + super(Adafruit_IL0373, self).__init__(width, height, spi, cs_pin, dc_pin, |
| 64 | + sramcs_pin, rst_pin, busy_pin) |
| 65 | + |
| 66 | + self._buffer1_size = int(width * height / 8) |
| 67 | + self._buffer2_size = int(width * height / 8) |
| 68 | + |
| 69 | + if sramcs_pin: |
| 70 | + self._buffer1 = self.sram.get_view(0) |
| 71 | + self._buffer2 = self.sram.get_view(self._buffer1_size) |
| 72 | + else: |
| 73 | + self._buffer1 = bytearray((width * height) // 8) |
| 74 | + self._buffer2 = bytearray((width * height) // 8) |
| 75 | + # since we have *two* framebuffers - one for red and one for black |
| 76 | + # we dont subclass but manage manually |
| 77 | + self._framebuf1 = adafruit_framebuf.FrameBuffer(self._buffer1, width, height, |
| 78 | + buf_format=adafruit_framebuf.MHMSB) |
| 79 | + self._framebuf2 = adafruit_framebuf.FrameBuffer(self._buffer2, width, height, |
| 80 | + buf_format=adafruit_framebuf.MHMSB) |
| 81 | + self.set_black_buffer(0, True) |
| 82 | + self.set_color_buffer(1, True) |
70 | 83 | # pylint: enable=too-many-arguments |
71 | 84 |
|
72 | 85 | def begin(self, reset=True): |
73 | 86 | """Begin communication with the display and set basic settings""" |
74 | | - super(Adafruit_IL0373, self).begin(reset) |
75 | | - |
76 | | - while self._busy.value is False: |
77 | | - pass |
78 | | - |
79 | | - self.command(IL0373_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09])) |
80 | | - self.command(IL0373_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17])) |
81 | | - |
82 | | - def update(self): |
83 | | - """update the display""" |
84 | | - self.command(IL0373_DISPLAY_REFRESH) |
85 | | - |
86 | | - while self._busy.value is False: |
87 | | - pass |
88 | | - |
89 | | - self.command(IL0373_CDI, bytearray([0x17])) |
90 | | - self.command(IL0373_VCM_DC_SETTING, bytearray([0x00])) |
91 | | - self.command(IL0373_POWER_OFF) |
92 | | - time.sleep(2) |
| 87 | + if reset: |
| 88 | + self.hardware_reset() |
| 89 | + self.power_down() |
| 90 | + |
| 91 | + def busy_wait(self): |
| 92 | + """Wait for display to be done with current task, either by polling the |
| 93 | + busy pin, or pausing""" |
| 94 | + if self._busy: |
| 95 | + while not self._busy.value: |
| 96 | + pass |
| 97 | + else: |
| 98 | + time.sleep(0.5) |
93 | 99 |
|
94 | 100 | def power_up(self): |
95 | | - """power up the display""" |
96 | | - self.command(IL0373_POWER_ON) |
97 | | - |
98 | | - while self._busy.value is False: |
99 | | - pass |
100 | | - |
101 | | - time.sleep(.2) |
102 | | - |
103 | | - self.command(IL0373_PANEL_SETTING, bytearray([0xCF])) |
104 | | - self.command(IL0373_CDI, bytearray([0x37])) |
105 | | - self.command(IL0373_PLL, bytearray([0x29])) |
106 | | - _b1 = self.height & 0xFF |
107 | | - _b2 = (self.height >> 8) & 0xFF |
108 | | - _b3 = self.width & 0xFF |
109 | | - _b4 = (self.width >> 8) & 0xFF |
110 | | - self.command(IL0373_RESOLUTION, bytearray([_b1, _b2, _b3, _b4])) |
111 | | - self.command(IL0373_VCM_DC_SETTING, bytearray([0x0A])) |
112 | | - |
113 | | - |
114 | | - def display(self): |
115 | | - """show the contents of the display buffer""" |
116 | | - self.power_up() |
117 | | - |
118 | | - while not self.spi_device.try_lock(): |
119 | | - pass |
120 | | - self.sram.cs_pin.value = False |
121 | | - #send read command |
122 | | - self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_READ])) |
123 | | - #send start address |
124 | | - self.spi_device.write(bytearray([0x00, 0x00])) |
125 | | - self.spi_device.unlock() |
126 | | - |
127 | | - #first data byte from SRAM will be transfered in at the |
128 | | - #same time as the EPD command is transferred out |
129 | | - cmd = self.command(IL0373_DTM1, end=False) |
130 | | - |
131 | | - while not self.spi_device.try_lock(): |
132 | | - pass |
133 | | - self._dc.value = True |
134 | | - xfer = bytearray([cmd]) |
135 | | - outbuf = bytearray(1) |
136 | | - for _ in range(self.bw_bufsize): |
137 | | - outbuf[0] = xfer[0] |
138 | | - self.spi_device.write_readinto(outbuf, xfer) |
139 | | - self._cs.value = True |
140 | | - self.sram.cs_pin.value = True |
141 | | - |
142 | | - time.sleep(.002) |
| 101 | + """Power up the display in preparation for writing RAM and updating""" |
| 102 | + self.hardware_reset() |
| 103 | + self.busy_wait() |
| 104 | + |
| 105 | + self.command(_IL0373_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09])) |
| 106 | + self.command(_IL0373_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17])) |
| 107 | + self.command(_IL0373_POWER_ON) |
| 108 | + |
| 109 | + self.busy_wait() |
| 110 | + time.sleep(0.2) |
| 111 | + |
| 112 | + self.command(_IL0373_PANEL_SETTING, bytearray([0xCF])) |
| 113 | + self.command(_IL0373_CDI, bytearray([0x37])) |
| 114 | + self.command(_IL0373_PLL, bytearray([0x29])) |
| 115 | + _b1 = self._width & 0xFF |
| 116 | + _b2 = (self._height >> 8) & 0xFF |
| 117 | + _b3 = self._height & 0xFF |
| 118 | + self.command(_IL0373_RESOLUTION, bytearray([_b1, _b2, _b3])) |
| 119 | + self.command(_IL0373_VCM_DC_SETTING, bytearray([0x0A])) |
| 120 | + time.sleep(0.05) |
| 121 | + |
| 122 | + def power_down(self): |
| 123 | + """Power down the display - required when not actively displaying!""" |
| 124 | + self.command(_IL0373_CDI, bytearray([0x17])) |
| 125 | + self.command(_IL0373_VCM_DC_SETTING, bytearray([0x00])) |
| 126 | + self.command(_IL0373_POWER_OFF) |
143 | 127 |
|
144 | | - self.sram.cs_pin.value = False |
145 | | - #send read command |
146 | | - self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_READ])) |
147 | | - #send start address |
148 | | - self.spi_device.write(bytearray([(self.bw_bufsize >> 8), (self.bw_bufsize & 0xFF)])) |
149 | | - self.spi_device.unlock() |
150 | | - |
151 | | - #first data byte from SRAM will be transfered in at the |
152 | | - #same time as the EPD command is transferred out |
153 | | - cmd = self.command(IL0373_DTM2, end=False) |
154 | | - |
155 | | - while not self.spi_device.try_lock(): |
156 | | - pass |
157 | | - self._dc.value = True |
158 | | - xfer = bytearray([cmd]) |
159 | | - outbuf = bytearray(1) |
160 | | - for _ in range(self.bw_bufsize): |
161 | | - outbuf[0] = xfer[0] |
162 | | - self.spi_device.write_readinto(outbuf, xfer) |
163 | | - self._cs.value = True |
164 | | - self.sram.cs_pin.value = True |
165 | | - self.spi_device.unlock() |
166 | | - |
167 | | - self.update() |
168 | | - |
169 | | - def image(self, image): |
170 | | - """Set buffer to value of Python Imaging Library image. The image should |
171 | | - be in RGB mode and a size equal to the display size. |
172 | | - """ |
173 | | - if image.mode != 'RGB': |
174 | | - raise ValueError('Image must be in mode RGB.') |
175 | | - imwidth, imheight = image.size |
176 | | - if imwidth != self.width or imheight != self.height: |
177 | | - raise ValueError('Image must be same dimensions as display ({0}x{1}).' \ |
178 | | - .format(self.width, self.height)) |
179 | | - # Grab all the pixels from the image, faster than getpixel. |
180 | | - pix = image.load() |
181 | | - |
182 | | - for y in iter(range(image.size[1])): |
183 | | - for x in iter(range(image.size[0])): |
184 | | - if x == 0: |
185 | | - x = 1 |
186 | | - pixel = pix[x, y] |
187 | | - |
188 | | - addr = int(((self.width - x) * self.height + y)/8) |
189 | | - |
190 | | - if pixel == (0xFF, 0, 0): |
191 | | - addr = addr + self.bw_bufsize |
192 | | - current = self.sram.read8(addr) |
193 | | - |
194 | | - if pixel in ((0xFF, 0, 0), (0, 0, 0)): |
195 | | - current = current & ~(1 << (7 - y%8)) |
196 | | - else: |
197 | | - current = current | (1 << (7 - y%8)) |
198 | | - |
199 | | - self.sram.write8(addr, current) |
200 | | - |
201 | | - def draw_pixel(self, x, y, color): |
202 | | - """draw a single pixel in the display buffer""" |
203 | | - if (x < 0) or (x >= self.width) or (y < 0) or (y >= self.height): |
204 | | - return |
205 | | - |
206 | | - if x == 0: |
207 | | - x = 1 |
208 | | - |
209 | | - addr = ((self.width - x) * self.height + y) // 8 |
210 | | - if color == Adafruit_EPD.RED: |
211 | | - addr = addr + self.bw_bufsize |
212 | | - current = self.sram.read8(addr) |
213 | | - |
214 | | - if color == Adafruit_EPD.WHITE: |
215 | | - current = current | (1 << (7 - y%8)) |
216 | | - elif color in (Adafruit_EPD.RED, Adafruit_EPD.BLACK): |
217 | | - current = current & ~(1 << (7 - y%8)) |
218 | | - elif color == Adafruit_EPD.INVERSE: |
219 | | - current = current ^ (1 << (7 - y%8)) |
220 | | - |
221 | | - self.sram.write8(addr, current) |
222 | | - return |
223 | | - |
224 | | - def clear_buffer(self): |
225 | | - """clear the display buffer""" |
226 | | - self.sram.erase(0x00, self.bw_bufsize, 0xFF) |
227 | | - self.sram.erase(self.bw_bufsize, self.red_bufsize, 0xFF) |
228 | | - |
229 | | - def clear_display(self): |
230 | | - """clear the entire display""" |
231 | | - self.clear_buffer() |
232 | | - self.display() |
| 128 | + def update(self): |
| 129 | + """Update the display from internal memory""" |
| 130 | + self.command(_IL0373_DISPLAY_REFRESH) |
| 131 | + time.sleep(0.1) |
| 132 | + self.busy_wait() |
| 133 | + if not self._busy: |
| 134 | + time.sleep(15) # wait 15 seconds |
| 135 | + |
| 136 | + def write_ram(self, index): |
| 137 | + """Send the one byte command for starting the RAM write process. Returns |
| 138 | + the byte read at the same time over SPI. index is the RAM buffer, can be |
| 139 | + 0 or 1 for tri-color displays.""" |
| 140 | + if index == 0: |
| 141 | + return self.command(_IL0373_DTM1, end=False) |
| 142 | + if index == 1: |
| 143 | + return self.command(_IL0373_DTM2, end=False) |
| 144 | + raise RuntimeError("RAM index must be 0 or 1") |
| 145 | + |
| 146 | + def set_ram_address(self, x, y): # pylint: disable=unused-argument, no-self-use |
| 147 | + """Set the RAM address location, not used on this chipset but required by |
| 148 | + the superclass""" |
| 149 | + return # on this chip it does nothing |
0 commit comments