|
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