diff --git a/adafruit_epd/epd.py b/adafruit_epd/epd.py index b315f51..a768f6b 100644 --- a/adafruit_epd/epd.py +++ b/adafruit_epd/epd.py @@ -27,120 +27,285 @@ """ import time -import digitalio +from micropython import const +from digitalio import Direction from adafruit_epd import mcp_sram -class Adafruit_EPD: +class Adafruit_EPD: # pylint: disable=too-many-instance-attributes, too-many-public-methods """Base class for EPD displays """ - BLACK = 0 - WHITE = 1 - INVERSE = 2 - RED = 3 - DARK = 4 - LIGHT = 5 - - # pylint: disable=too-many-arguments - def __init__(self, width, height, rst_pin, dc_pin, busy_pin, srcs_pin, cs_pin, spi): - self.width = width - self.height = height - - # Setup reset pin. + BLACK = const(0) + WHITE = const(1) + INVERSE = const(2) + RED = const(3) + DARK = const(4) + LIGHT = const(5) + + + def __init__(self, width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin): # pylint: disable=too-many-arguments + self._width = width + self._height = height + + # Setup reset pin, if we have one self._rst = rst_pin - self._rst.direction = digitalio.Direction.OUTPUT + if rst_pin: + self._rst.direction = Direction.OUTPUT - # Setup busy pin. + # Setup busy pin, if we have one self._busy = busy_pin - self._busy.direction = digitalio.Direction.INPUT + if busy_pin: + self._busy.direction = Direction.INPUT - # Setup dc pin. + # Setup dc pin (required) self._dc = dc_pin - self._dc.direction = digitalio.Direction.OUTPUT + self._dc.direction = Direction.OUTPUT + self._dc.value = False - # Setup cs pin. + # Setup cs pin (required) self._cs = cs_pin - self._cs.direction = digitalio.Direction.OUTPUT + self._cs.direction = Direction.OUTPUT + self._cs.value = True + # SPI interface (required) self.spi_device = spi + self._spibuf = bytearray(1) + self._single_byte_tx = False + + self.sram = None + if sramcs_pin: + self.sram = mcp_sram.Adafruit_MCP_SRAM(sramcs_pin, spi) + + self._buf = bytearray(3) + self._buffer1_size = self._buffer2_size = 0 + self._buffer1 = self._buffer2 = None + self._framebuf1 = self._framebuf2 = None + self._colorframebuf = self._blackframebuf = None + self._black_inverted = self._color_inverted = True + self.hardware_reset() + + def display(self): # pylint: disable=too-many-branches + """show the contents of the display buffer""" + self.power_up() + + self.set_ram_address(0, 0) - self.sram = mcp_sram.Adafruit_MCP_SRAM(srcs_pin, spi) - # pylint: enable=too-many-arguments + if self.sram: + while not self.spi_device.try_lock(): + pass + self.sram.cs_pin.value = False + #send read command + self._buf[0] = mcp_sram.Adafruit_MCP_SRAM.SRAM_READ + #send start address + self._buf[1] = 0 + self._buf[2] = 0 + self.spi_device.write(self._buf, end=3) + self.spi_device.unlock() + + #first data byte from SRAM will be transfered in at the + #same time as the EPD command is transferred out + databyte = self.write_ram(0) + + while not self.spi_device.try_lock(): + pass + self._dc.value = True + + if self.sram: + for _ in range(self._buffer1_size): + databyte = self._spi_transfer(databyte) + self.sram.cs_pin.value = True + else: + for databyte in self._buffer1: + self._spi_transfer(databyte) - def begin(self, reset=True): - """Begin display and reset if desired.""" self._cs.value = True - self._dc.value = False + self.spi_device.unlock() + time.sleep(.002) + + if self.sram: + while not self.spi_device.try_lock(): + pass + self.sram.cs_pin.value = False + #send read command + self._buf[0] = mcp_sram.Adafruit_MCP_SRAM.SRAM_READ + #send start address + self._buf[1] = self._buffer1_size >> 8 + self._buf[2] = self._buffer1_size + self.spi_device.write(self._buf, end=3) + self.spi_device.unlock() + + #first data byte from SRAM will be transfered in at the + #same time as the EPD command is transferred out + databyte = self.write_ram(1) + + while not self.spi_device.try_lock(): + pass + self._dc.value = True + + if self.sram: + for _ in range(self._buffer2_size): + databyte = self._spi_transfer(databyte) + self.sram.cs_pin.value = True + else: + for databyte in self._buffer2: + self._spi_transfer(databyte) + + self._cs.value = True + self.spi_device.unlock() + self.update() + - if reset: + def hardware_reset(self): + """If we have a reset pin, do a hardware reset by toggling it""" + if self._rst: self._rst.value = False - time.sleep(.1) + time.sleep(0.1) self._rst.value = True - time.sleep(.1) + time.sleep(0.1) def command(self, cmd, data=None, end=True): """Send command byte to display.""" self._cs.value = True self._dc.value = False self._cs.value = False - outbuf = bytearray(1) while not self.spi_device.try_lock(): pass - self.spi_device.write_readinto(bytearray([cmd]), outbuf) + ret = self._spi_transfer(cmd) if data is not None: - self.data(data) - else: - self.spi_device.unlock() - + self._dc.value = True + for b in data: + self._spi_transfer(b) if end: self._cs.value = True + self.spi_device.unlock() - return outbuf[0] + return ret - def data(self, dat): - """Send data to display.""" - self._dc.value = True - self.spi_device.write(dat) - self._cs.value = True - self.spi_device.unlock() + def _spi_transfer(self, databyte): + """Transfer one byte, toggling the cs pin if required by the EPD chipset""" + self._spibuf[0] = databyte + if self._single_byte_tx: + self._cs.value = False + self.spi_device.write_readinto(self._spibuf, self._spibuf) + if self._single_byte_tx: + self._cs.value = True + return self._spibuf[0] + + def power_up(self): + """Power up the display in preparation for writing RAM and updating. + must be implemented in subclass""" + raise NotImplementedError() + + def power_down(self): + """Power down the display, must be implemented in subclass""" + raise NotImplementedError() + + def update(self): + """Update the display from internal memory, must be implemented in subclass""" + raise NotImplementedError() + + def write_ram(self, index): + """Send the one byte command for starting the RAM write process. Returns + the byte read at the same time over SPI. index is the RAM buffer, can be + 0 or 1 for tri-color displays. must be implemented in subclass""" + raise NotImplementedError() + + def set_ram_address(self, x, y): + """Set the RAM address location, must be implemented in subclass""" + raise NotImplementedError() - def draw_pixel(self, x, y, color): - """This should be overridden in the subclass""" - pass + def set_black_buffer(self, index, inverted): + """Set the index for the black buffer data (0 or 1) and whether its inverted""" + if index == 0: + self._blackframebuf = self._framebuf1 + elif index == 1: + self._blackframebuf = self._framebuf2 + else: + raise RuntimeError("Buffer index must be 0 or 1") + self._black_inverted = inverted + + def set_color_buffer(self, index, inverted): + """Set the index for the color buffer data (0 or 1) and whether its inverted""" + if index == 0: + self._colorframebuf = self._framebuf1 + elif index == 1: + self._colorframebuf = self._framebuf2 + else: + raise RuntimeError("Buffer index must be 0 or 1") + self._color_inverted = inverted + + def _color_dup(self, func, args, color): + black = getattr(self._blackframebuf, func) + red = getattr(self._colorframebuf, func) + if self._blackframebuf is self._colorframebuf: # monochrome + black(*args, color=(color != Adafruit_EPD.WHITE) != self._black_inverted) + else: + black(*args, color=(color == Adafruit_EPD.BLACK) != self._black_inverted) + red(*args, color=(color == Adafruit_EPD.RED) != self._color_inverted) + + def pixel(self, x, y, color): + """draw a single pixel in the display buffer""" + self._color_dup('pixel', (x, y), color) - #framebuf methods def fill(self, color): """fill the screen with the passed color""" - self.fill_rect(0, 0, self.width, self.height, color) + red_fill = ((color == Adafruit_EPD.RED) != self._color_inverted) * 0xFF + black_fill = ((color == Adafruit_EPD.BLACK) != self._black_inverted) * 0xFF + + if self.sram: + self.sram.erase(0x00, self._buffer1_size, black_fill) + self.sram.erase(self._buffer1_size, self._buffer2_size, red_fill) + else: + self._blackframebuf.fill(black_fill) + self._colorframebuf.fill(red_fill) - # pylint: disable=too-many-arguments - def fill_rect(self, x, y, width, height, color): + def rect(self, x, y, width, height, color): # pylint: disable=too-many-arguments + """draw a rectangle""" + self._color_dup('rect', (x, y, width, height), color) + + def fill_rect(self, x, y, width, height, color): # pylint: disable=too-many-arguments """fill a rectangle with the passed color""" - if width < 1 or height < 1 or (x+width) <= 0: - return - if (y+height) <= 0 or y >= self.height or x >= self.width: - return - xend = min(self.width, x+width) - yend = min(self.height, y+height) - x = max(x, 0) - y = max(y, 0) - for _x in range(xend - x): - for _y in range(yend - y): - self.draw_pixel(x + _x, y + _y, color) - return - - def pixel(self, x, y, color=None): - """draw a pixel""" - if x < 0 or x >= self.width or y < 0 or y >= self.height: - return None - #TODO: figure this out when we know what framebuffer we - # will actually use - #if color is None: - # return self.get_pixel(self, x, y) - - self.draw_pixel(x, y, color) - return None + self._color_dup('fill_rect', (x, y, width, height), color) + + def line(self, x_0, y_0, x_1, y_1, color): # pylint: disable=too-many-arguments + """Draw a line from (x_0, y_0) to (x_1, y_1) in passed color""" + self._color_dup('line', (x_0, y_0, x_1, y_1), color) + + def text(self, string, x, y, color, *, font_name="font5x8.bin"): + """Write text string at location (x, y) in given color, using font file""" + if self._blackframebuf is self._colorframebuf: # monochrome + self._blackframebuf.text(string, x, y, font_name=font_name, + color=(color != Adafruit_EPD.WHITE) != self._black_inverted) + else: + self._blackframebuf.text(string, x, y, font_name=font_name, + color=(color == Adafruit_EPD.BLACK) != self._black_inverted) + self._colorframebuf.text(string, x, y, font_name=font_name, + color=(color == Adafruit_EPD.RED) != self._color_inverted) + + @property + def width(self): + """The width of the display, accounting for rotation""" + if self.rotation in (0, 2): + return self._width + return self._height + + @property + def height(self): + """The height of the display, accounting for rotation""" + if self.rotation in (0, 2): + return self._height + return self._width + + @property + def rotation(self): + """The rotation of the display, can be one of (0, 1, 2, 3)""" + return self._framebuf1.rotation + + @rotation.setter + def rotation(self, val): + self._framebuf1.rotation = val + self._framebuf2.rotation = val def hline(self, x, y, width, color): """draw a horizontal line""" @@ -150,9 +315,35 @@ def vline(self, x, y, height, color): """draw a vertical line""" self.fill_rect(x, y, 1, height, color) - def rect(self, x, y, width, height, color): - """draw a rectangle""" - self.fill_rect(x, y, width, 1, color) - self.fill_rect(x, y+height, width, 1, color) - self.fill_rect(x, y, 1, height, color) - self.fill_rect(x+width, y, 1, height, color) + + def image(self, image): + """Set buffer to value of Python Imaging Library image. The image should + be in RGB mode and a size equal to the display size. + """ + if image.mode != 'RGB': + raise ValueError('Image must be in mode RGB.') + imwidth, imheight = image.size + if imwidth != self.width or imheight != self.height: + raise ValueError('Image must be same dimensions as display ({0}x{1}).' \ + .format(self.width, self.height)) + # Grab all the pixels from the image, faster than getpixel. + pix = image.load() + + for y in iter(range(image.size[1])): + for x in iter(range(image.size[0])): + if x == 0: + x = 1 + pixel = pix[x, y] + + addr = int(((self._width - x) * self._height + y)/8) + + if pixel == (0xFF, 0, 0): + addr = addr + self._buffer1_size + current = self.sram.read8(addr) + + if pixel in ((0xFF, 0, 0), (0, 0, 0)): + current = current & ~(1 << (7 - y%8)) + else: + current = current | (1 << (7 - y%8)) + + self.sram.write8(addr, current) diff --git a/adafruit_epd/il0373.py b/adafruit_epd/il0373.py index 5215826..bc10951 100644 --- a/adafruit_epd/il0373.py +++ b/adafruit_epd/il0373.py @@ -28,205 +28,122 @@ import time from micropython import const +import adafruit_framebuf from adafruit_epd.epd import Adafruit_EPD -from adafruit_epd.mcp_sram import Adafruit_MCP_SRAM -IL0373_PANEL_SETTING = const(0x00) -IL0373_POWER_SETTING = const(0x01) -IL0373_POWER_OFF = const(0x02) -IL0373_POWER_OFF_SEQUENCE = const(0x03) -IL0373_POWER_ON = const(0x04) -IL0373_POWER_ON_MEASURE = const(0x05) -IL0373_BOOSTER_SOFT_START = const(0x06) -IL0373_DEEP_SLEEP = const(0x07) -IL0373_DTM1 = const(0x10) -IL0373_DATA_STOP = const(0x11) -IL0373_DISPLAY_REFRESH = const(0x12) -IL0373_DTM2 = const(0x13) -IL0373_PDTM1 = const(0x14) -IL0373_PDTM2 = const(0x15) -IL0373_PDRF = const(0x16) -IL0373_LUT1 = const(0x20) -IL0373_LUTWW = const(0x21) -IL0373_LUTBW = const(0x22) -IL0373_LUTWB = const(0x23) -IL0373_LUTBB = const(0x24) -IL0373_PLL = const(0x30) -IL0373_CDI = const(0x50) -IL0373_RESOLUTION = const(0x61) -IL0373_VCM_DC_SETTING = const(0x82) +_IL0373_PANEL_SETTING = const(0x00) +_IL0373_POWER_SETTING = const(0x01) +_IL0373_POWER_OFF = const(0x02) +_IL0373_POWER_OFF_SEQUENCE = const(0x03) +_IL0373_POWER_ON = const(0x04) +_IL0373_POWER_ON_MEASURE = const(0x05) +_IL0373_BOOSTER_SOFT_START = const(0x06) +_IL0373_DEEP_SLEEP = const(0x07) +_IL0373_DTM1 = const(0x10) +_IL0373_DATA_STOP = const(0x11) +_IL0373_DISPLAY_REFRESH = const(0x12) +_IL0373_DTM2 = const(0x13) +_IL0373_PDTM1 = const(0x14) +_IL0373_PDTM2 = const(0x15) +_IL0373_PDRF = const(0x16) +_IL0373_LUT1 = const(0x20) +_IL0373_LUTWW = const(0x21) +_IL0373_LUTBW = const(0x22) +_IL0373_LUTWB = const(0x23) +_IL0373_LUTBB = const(0x24) +_IL0373_PLL = const(0x30) +_IL0373_CDI = const(0x50) +_IL0373_RESOLUTION = const(0x61) +_IL0373_VCM_DC_SETTING = const(0x82) class Adafruit_IL0373(Adafruit_EPD): """driver class for Adafruit IL0373 ePaper display breakouts""" # pylint: disable=too-many-arguments - def __init__(self, width, height, rst_pin, dc_pin, busy_pin, srcs_pin, cs_pin, spi): - super(Adafruit_IL0373, self).__init__(width, height, rst_pin, dc_pin, busy_pin, - srcs_pin, cs_pin, spi) - - self.bw_bufsize = int(width * height / 8) - self.red_bufsize = int(width * height / 8) - - self.begin() + def __init__(self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin): + super(Adafruit_IL0373, self).__init__(width, height, spi, cs_pin, dc_pin, + sramcs_pin, rst_pin, busy_pin) + + self._buffer1_size = int(width * height / 8) + self._buffer2_size = int(width * height / 8) + + if sramcs_pin: + self._buffer1 = self.sram.get_view(0) + self._buffer2 = self.sram.get_view(self._buffer1_size) + else: + self._buffer1 = bytearray((width * height) // 8) + self._buffer2 = bytearray((width * height) // 8) + # since we have *two* framebuffers - one for red and one for black + # we dont subclass but manage manually + self._framebuf1 = adafruit_framebuf.FrameBuffer(self._buffer1, width, height, + buf_format=adafruit_framebuf.MHMSB) + self._framebuf2 = adafruit_framebuf.FrameBuffer(self._buffer2, width, height, + buf_format=adafruit_framebuf.MHMSB) + self.set_black_buffer(0, True) + self.set_color_buffer(1, True) # pylint: enable=too-many-arguments def begin(self, reset=True): """Begin communication with the display and set basic settings""" - super(Adafruit_IL0373, self).begin(reset) - - while self._busy.value is False: - pass - - self.command(IL0373_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09])) - self.command(IL0373_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17])) - - def update(self): - """update the display""" - self.command(IL0373_DISPLAY_REFRESH) - - while self._busy.value is False: - pass - - self.command(IL0373_CDI, bytearray([0x17])) - self.command(IL0373_VCM_DC_SETTING, bytearray([0x00])) - self.command(IL0373_POWER_OFF) - time.sleep(2) + if reset: + self.hardware_reset() + self.power_down() + + def busy_wait(self): + """Wait for display to be done with current task, either by polling the + busy pin, or pausing""" + if self._busy: + while not self._busy.value: + pass + else: + time.sleep(0.5) def power_up(self): - """power up the display""" - self.command(IL0373_POWER_ON) - - while self._busy.value is False: - pass - - time.sleep(.2) - - self.command(IL0373_PANEL_SETTING, bytearray([0xCF])) - self.command(IL0373_CDI, bytearray([0x37])) - self.command(IL0373_PLL, bytearray([0x29])) - _b1 = self.height & 0xFF - _b2 = (self.height >> 8) & 0xFF - _b3 = self.width & 0xFF - _b4 = (self.width >> 8) & 0xFF - self.command(IL0373_RESOLUTION, bytearray([_b1, _b2, _b3, _b4])) - self.command(IL0373_VCM_DC_SETTING, bytearray([0x0A])) - - - def display(self): - """show the contents of the display buffer""" - self.power_up() - - while not self.spi_device.try_lock(): - pass - self.sram.cs_pin.value = False - #send read command - self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_READ])) - #send start address - self.spi_device.write(bytearray([0x00, 0x00])) - self.spi_device.unlock() - - #first data byte from SRAM will be transfered in at the - #same time as the EPD command is transferred out - cmd = self.command(IL0373_DTM1, end=False) - - while not self.spi_device.try_lock(): - pass - self._dc.value = True - xfer = bytearray([cmd]) - outbuf = bytearray(1) - for _ in range(self.bw_bufsize): - outbuf[0] = xfer[0] - self.spi_device.write_readinto(outbuf, xfer) - self._cs.value = True - self.sram.cs_pin.value = True - - time.sleep(.002) + """Power up the display in preparation for writing RAM and updating""" + self.hardware_reset() + self.busy_wait() + + self.command(_IL0373_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09])) + self.command(_IL0373_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17])) + self.command(_IL0373_POWER_ON) + + self.busy_wait() + time.sleep(0.2) + + self.command(_IL0373_PANEL_SETTING, bytearray([0xCF])) + self.command(_IL0373_CDI, bytearray([0x37])) + self.command(_IL0373_PLL, bytearray([0x29])) + _b1 = self._width & 0xFF + _b2 = (self._height >> 8) & 0xFF + _b3 = self._height & 0xFF + self.command(_IL0373_RESOLUTION, bytearray([_b1, _b2, _b3])) + self.command(_IL0373_VCM_DC_SETTING, bytearray([0x0A])) + time.sleep(0.05) + + def power_down(self): + """Power down the display - required when not actively displaying!""" + self.command(_IL0373_CDI, bytearray([0x17])) + self.command(_IL0373_VCM_DC_SETTING, bytearray([0x00])) + self.command(_IL0373_POWER_OFF) - self.sram.cs_pin.value = False - #send read command - self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_READ])) - #send start address - self.spi_device.write(bytearray([(self.bw_bufsize >> 8), (self.bw_bufsize & 0xFF)])) - self.spi_device.unlock() - - #first data byte from SRAM will be transfered in at the - #same time as the EPD command is transferred out - cmd = self.command(IL0373_DTM2, end=False) - - while not self.spi_device.try_lock(): - pass - self._dc.value = True - xfer = bytearray([cmd]) - outbuf = bytearray(1) - for _ in range(self.bw_bufsize): - outbuf[0] = xfer[0] - self.spi_device.write_readinto(outbuf, xfer) - self._cs.value = True - self.sram.cs_pin.value = True - self.spi_device.unlock() - - self.update() - - def image(self, image): - """Set buffer to value of Python Imaging Library image. The image should - be in RGB mode and a size equal to the display size. - """ - if image.mode != 'RGB': - raise ValueError('Image must be in mode RGB.') - imwidth, imheight = image.size - if imwidth != self.width or imheight != self.height: - raise ValueError('Image must be same dimensions as display ({0}x{1}).' \ - .format(self.width, self.height)) - # Grab all the pixels from the image, faster than getpixel. - pix = image.load() - - for y in iter(range(image.size[1])): - for x in iter(range(image.size[0])): - if x == 0: - x = 1 - pixel = pix[x, y] - - addr = int(((self.width - x) * self.height + y)/8) - - if pixel == (0xFF, 0, 0): - addr = addr + self.bw_bufsize - current = self.sram.read8(addr) - - if pixel in ((0xFF, 0, 0), (0, 0, 0)): - current = current & ~(1 << (7 - y%8)) - else: - current = current | (1 << (7 - y%8)) - - self.sram.write8(addr, current) - - def draw_pixel(self, x, y, color): - """draw a single pixel in the display buffer""" - if (x < 0) or (x >= self.width) or (y < 0) or (y >= self.height): - return - - if x == 0: - x = 1 - - addr = ((self.width - x) * self.height + y) // 8 - if color == Adafruit_EPD.RED: - addr = addr + self.bw_bufsize - current = self.sram.read8(addr) - - if color == Adafruit_EPD.WHITE: - current = current | (1 << (7 - y%8)) - elif color in (Adafruit_EPD.RED, Adafruit_EPD.BLACK): - current = current & ~(1 << (7 - y%8)) - elif color == Adafruit_EPD.INVERSE: - current = current ^ (1 << (7 - y%8)) - - self.sram.write8(addr, current) - return - - def clear_buffer(self): - """clear the display buffer""" - self.sram.erase(0x00, self.bw_bufsize, 0xFF) - self.sram.erase(self.bw_bufsize, self.red_bufsize, 0xFF) - - def clear_display(self): - """clear the entire display""" - self.clear_buffer() - self.display() + def update(self): + """Update the display from internal memory""" + self.command(_IL0373_DISPLAY_REFRESH) + time.sleep(0.1) + self.busy_wait() + if not self._busy: + time.sleep(15) # wait 15 seconds + + def write_ram(self, index): + """Send the one byte command for starting the RAM write process. Returns + the byte read at the same time over SPI. index is the RAM buffer, can be + 0 or 1 for tri-color displays.""" + if index == 0: + return self.command(_IL0373_DTM1, end=False) + if index == 1: + return self.command(_IL0373_DTM2, end=False) + raise RuntimeError("RAM index must be 0 or 1") + + def set_ram_address(self, x, y): # pylint: disable=unused-argument, no-self-use + """Set the RAM address location, not used on this chipset but required by + the superclass""" + return # on this chip it does nothing diff --git a/adafruit_epd/il0398.py b/adafruit_epd/il0398.py new file mode 100644 index 0000000..3d110a6 --- /dev/null +++ b/adafruit_epd/il0398.py @@ -0,0 +1,149 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Dean Miller for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_epd.il0398` - Adafruit IL0398 - ePaper display driver +==================================================================================== +CircuitPython driver for Adafruit IL0398 display breakouts +* Author(s): Dean Miller, ladyada +""" + +import time +from micropython import const +import adafruit_framebuf +from adafruit_epd.epd import Adafruit_EPD + +_IL0398_PANEL_SETTING = const(0x00) +_IL0398_POWER_SETTING = const(0x01) +_IL0398_POWER_OFF = const(0x02) +_IL0398_POWER_OFF_SEQUENCE = const(0x03) +_IL0398_POWER_ON = const(0x04) +_IL0398_POWER_ON_MEASURE = const(0x05) +_IL0398_BOOSTER_SOFT_START = const(0x06) +_IL0398_DEEP_SLEEP = const(0x07) +_IL0398_DTM1 = const(0x10) +_IL0398_DATA_STOP = const(0x11) +_IL0398_DISPLAY_REFRESH = const(0x12) +_IL0398_DTM2 = const(0x13) +_IL0398_PDTM1 = const(0x14) +_IL0398_PDTM2 = const(0x15) +_IL0398_PDRF = const(0x16) +_IL0398_LUT1 = const(0x20) +_IL0398_LUTWW = const(0x21) +_IL0398_LUTBW = const(0x22) +_IL0398_LUTWB = const(0x23) +_IL0398_LUTBB = const(0x24) +_IL0398_PLL = const(0x30) +_IL0398_CDI = const(0x50) +_IL0398_RESOLUTION = const(0x61) +_IL0398_GETSTATUS = const(0x71) +_IL0398_VCM_DC_SETTING = const(0x82) + +class Adafruit_IL0398(Adafruit_EPD): + """driver class for Adafruit IL0373 ePaper display breakouts""" + # pylint: disable=too-many-arguments + def __init__(self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin): + super(Adafruit_IL0398, self).__init__(width, height, spi, cs_pin, dc_pin, + sramcs_pin, rst_pin, busy_pin) + + self._buffer1_size = int(width * height / 8) + self._buffer2_size = int(width * height / 8) + + if sramcs_pin: + self._buffer1 = self.sram.get_view(0) + self._buffer2 = self.sram.get_view(self._buffer1_size) + else: + self._buffer1 = bytearray((width * height) // 8) + self._buffer2 = bytearray((width * height) // 8) + # since we have *two* framebuffers - one for red and one for black + # we dont subclass but manage manually + self._framebuf1 = adafruit_framebuf.FrameBuffer(self._buffer1, width, height, + buf_format=adafruit_framebuf.MHMSB) + self._framebuf2 = adafruit_framebuf.FrameBuffer(self._buffer2, width, height, + buf_format=adafruit_framebuf.MHMSB) + self.set_black_buffer(0, True) + self.set_color_buffer(1, True) + # pylint: enable=too-many-arguments + + def begin(self, reset=True): + """Begin communication with the display and set basic settings""" + if reset: + self.hardware_reset() + self.power_down() + + def busy_wait(self): + """Wait for display to be done with current task, either by polling the + busy pin, or pausing""" + if self._busy: + while not self._busy.value: + #self.command(_IL0398_GETSTATUS) + time.sleep(0.01) + else: + time.sleep(0.5) + + def power_up(self): + """Power up the display in preparation for writing RAM and updating""" + self.hardware_reset() + self.busy_wait() + + self.command(_IL0398_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17])) + self.command(_IL0398_POWER_ON) + + self.busy_wait() + time.sleep(0.2) + + self.command(_IL0398_PANEL_SETTING, bytearray([0x0F])) + _b0 = (self._width >> 8) & 0xFF + _b1 = self._width & 0xFF + _b2 = (self._height >> 8) & 0xFF + _b3 = self._height & 0xFF + self.command(_IL0398_RESOLUTION, bytearray([_b0, _b1, _b2, _b3])) + time.sleep(0.05) + + def power_down(self): + """Power down the display - required when not actively displaying!""" + self.command(_IL0398_CDI, bytearray([0xF7])) + self.command(_IL0398_POWER_OFF) + self.busy_wait() + self.command(_IL0398_DEEP_SLEEP, bytearray([0xA5])) + + def update(self): + """Update the display from internal memory""" + self.command(_IL0398_DISPLAY_REFRESH) + time.sleep(0.1) + self.busy_wait() + if not self._busy: + time.sleep(15) # wait 15 seconds + + def write_ram(self, index): + """Send the one byte command for starting the RAM write process. Returns + the byte read at the same time over SPI. index is the RAM buffer, can be + 0 or 1 for tri-color displays.""" + if index == 0: + return self.command(_IL0398_DTM1, end=False) + if index == 1: + return self.command(_IL0398_DTM2, end=False) + raise RuntimeError("RAM index must be 0 or 1") + + def set_ram_address(self, x, y): # pylint: disable=unused-argument, no-self-use + """Set the RAM address location, not used on this chipset but required by + the superclass""" + return # on this chip it does nothing diff --git a/adafruit_epd/il91874.py b/adafruit_epd/il91874.py new file mode 100644 index 0000000..5efe627 --- /dev/null +++ b/adafruit_epd/il91874.py @@ -0,0 +1,172 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Dean Miller for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_epd.il91874` - Adafruit IL91874 - ePaper display driver +==================================================================================== +CircuitPython driver for Adafruit IL91874 display breakouts +* Author(s): Dean Miller, Ladyada +""" + +import time +from micropython import const +import adafruit_framebuf +from adafruit_epd.epd import Adafruit_EPD + +_IL91874_PANEL_SETTING = const(0x00) +_IL91874_POWER_SETTING = const(0x01) +_IL91874_POWER_OFF = const(0x02) +_IL91874_POWER_OFF_SEQUENCE = const(0x03) +_IL91874_POWER_ON = const(0x04) +_IL91874_POWER_ON_MEASURE = const(0x05) +_IL91874_BOOSTER_SOFT_START = const(0x06) +_IL91874_DEEP_SLEEP = const(0x07) +_IL91874_DTM1 = const(0x10) +_IL91874_DATA_STOP = const(0x11) +_IL91874_DISPLAY_REFRESH = const(0x12) +_IL91874_DTM2 = const(0x13) +_IL91874_PDTM1 = const(0x14) +_IL91874_PDTM2 = const(0x15) +_IL91874_PDRF = const(0x16) +_IL91874_LUT1 = const(0x20) +_IL91874_LUTWW = const(0x21) +_IL91874_LUTBW = const(0x22) +_IL91874_LUTWB = const(0x23) +_IL91874_LUTBB = const(0x24) +_IL91874_PLL = const(0x30) +_IL91874_CDI = const(0x50) +_IL91874_RESOLUTION = const(0x61) +_IL91874_VCM_DC_SETTING = const(0x82) + +# pylint: disable=line-too-long +_LUT_VCOMDC = b'\x00\x00\x00\x1a\x1a\x00\x00\x01\x00\n\n\x00\x00\x08\x00\x0e\x01\x0e\x01\x10\x00\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01' +_LUT_WW = b'\x90\x1a\x1a\x00\x00\x01@\n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x80\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01' +_LUT_BW = b'\xa0\x1a\x1a\x00\x00\x01\x00\n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x90\n\n\x00\x00\x08\xb0\x04\x10\x00\x00\x05\xb0\x03\x0e\x00\x00\n\xc0#\x00\x00\x00\x01' +_LUT_BB = b'\x90\x1a\x1a\x00\x00\x01@\n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x80\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01' +_LUT_WB = b'\x90\x1a\x1a\x00\x00\x01 \n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x10\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01' +# pylint: enable=line-too-long + +class Adafruit_IL91874(Adafruit_EPD): + """driver class for Adafruit IL91874 ePaper display breakouts""" + # pylint: disable=too-many-arguments + def __init__(self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin): + super(Adafruit_IL91874, self).__init__(width, height, spi, cs_pin, dc_pin, + sramcs_pin, rst_pin, busy_pin) + + self._buffer1_size = int(width * height / 8) + self._buffer2_size = int(width * height / 8) + + if sramcs_pin: + self._buffer1 = self.sram.get_view(0) + self._buffer2 = self.sram.get_view(self._buffer1_size) + else: + self._buffer1 = bytearray((width * height) // 8) + self._buffer2 = bytearray((width * height) // 8) + # since we have *two* framebuffers - one for red and one for black + # we dont subclass but manage manually + self._framebuf1 = adafruit_framebuf.FrameBuffer(self._buffer1, width, height, + buf_format=adafruit_framebuf.MHMSB) + self._framebuf2 = adafruit_framebuf.FrameBuffer(self._buffer2, width, height, + buf_format=adafruit_framebuf.MHMSB) + self.set_black_buffer(0, True) + self.set_color_buffer(1, False) + self._single_byte_tx = True + + def begin(self, reset=True): + """Begin communication with the display and set basic settings""" + if reset: + self.hardware_reset() + + self.power_down() + + def busy_wait(self): + """Wait for display to be done with current task, either by polling the + busy pin, or pausing""" + if self._busy: + while not self._busy.value: + pass + else: + time.sleep(0.5) + + def power_up(self): + """Power up the display in preparation for writing RAM and updating""" + self.hardware_reset() + time.sleep(0.2) + self.command(_IL91874_POWER_ON) + self.busy_wait() + + self.command(_IL91874_PANEL_SETTING, bytearray([0xAF])) + self.command(_IL91874_PLL, bytearray([0x3A])) + self.command(_IL91874_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09])) + self.command(_IL91874_BOOSTER_SOFT_START, bytearray([0x07, 0x07, 0x17])) + + self.command(0xF8, bytearray([0x60, 0xA5])) # mystery command in example code + self.command(0xF8, bytearray([0x89, 0xA5])) # mystery command in example code + self.command(0xF8, bytearray([0x90, 0x00])) # mystery command in example code + self.command(0xF8, bytearray([0x93, 0xA2])) # mystery command in example code + self.command(0xF8, bytearray([0x73, 0x41])) # mystery command in example code + + self.command(_IL91874_VCM_DC_SETTING, bytearray([0x12])) + self.command(_IL91874_CDI, bytearray([0x87])) + + # Look Up Tables + self.command(_IL91874_LUT1, _LUT_VCOMDC) + self.command(_IL91874_LUTWW, _LUT_WW) + self.command(_IL91874_LUTBW, _LUT_BW) + self.command(_IL91874_LUTWB, _LUT_WB) + self.command(_IL91874_LUTBB, _LUT_BB) + + _b0 = (self._width >> 8) & 0xFF + _b1 = self._width & 0xFF + _b2 = (self._height >> 8) & 0xFF + _b3 = self._height & 0xFF + self.command(_IL91874_RESOLUTION, bytearray([_b0, _b1, _b2, _b3])) + self.command(_IL91874_PDRF, bytearray([0x00])) + + def power_down(self): + """Power down the display - required when not actively displaying!""" + self.command(_IL91874_POWER_OFF, bytearray([0x17])) + self.busy_wait() + + if self._rst: # Only deep sleep if we can get out of it + self.command(_IL91874_DEEP_SLEEP, bytearray([0xA5])) + + def update(self): + """Update the display from internal memory""" + self.command(_IL91874_DISPLAY_REFRESH) + self.busy_wait() + if not self._busy: + time.sleep(16) # wait 16 seconds + + def write_ram(self, index): + """Send the one byte command for starting the RAM write process. Returns + the byte read at the same time over SPI. index is the RAM buffer, can be + 0 or 1 for tri-color displays.""" + if index == 0: + return self.command(_IL91874_DTM1, end=False) + if index == 1: + return self.command(_IL91874_DTM2, end=False) + raise RuntimeError("RAM index must be 0 or 1") + + def set_ram_address(self, x, y): # pylint: disable=unused-argument, no-self-use + """Set the RAM address location, not used on this chipset but required by + the superclass""" + return # on this chip it does nothing diff --git a/adafruit_epd/mcp_sram.py b/adafruit_epd/mcp_sram.py index fd3d1da..bccb733 100644 --- a/adafruit_epd/mcp_sram.py +++ b/adafruit_epd/mcp_sram.py @@ -27,10 +27,24 @@ """ from micropython import const -import digitalio +from adafruit_bus_device import spi_device SRAM_SEQUENTIAL_MODE = const(1 << 6) +class Adafruit_MCP_SRAM_View: + """A interface class that turns an SRAM chip into something like a memoryview""" + def __init__(self, sram, offset): + self._sram = sram + self._offset = offset + self._buf = [0] + + def __getitem__(self, i): + return self._sram.read(self._offset+i, 1)[0] + + def __setitem__(self, i, val): + self._buf[0] = val + self._sram.write(self._offset+i, self._buf) + class Adafruit_MCP_SRAM: """supporting class for communicating with Microchip SRAM chips""" @@ -41,40 +55,39 @@ class Adafruit_MCP_SRAM: def __init__(self, cs_pin, spi): # Handle hardware SPI + self._spi = spi_device.SPIDevice(spi, cs_pin, baudrate=8000000) self.spi_device = spi self.cs_pin = cs_pin + self._buf = bytearray(3) + self._buf[0] = Adafruit_MCP_SRAM.SRAM_WRSR + self._buf[1] = 0x43 + with self._spi as spidev: + spidev.write(self._buf, end=2) # pylint: disable=no-member - self.cs_pin.direction = digitalio.Direction.OUTPUT - while not self.spi_device.try_lock(): - pass - self.cs_pin.value = False - self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_WRSR, 0x43])) - self.cs_pin.value = True - self.spi_device.unlock() + def get_view(self, offset): + """Create an object that can be used as a memoryview, with a given offset""" + return Adafruit_MCP_SRAM_View(self, offset) def write(self, addr, buf, reg=SRAM_WRITE): """write the passed buffer to the passed address""" - cmd = bytearray([reg, (addr >> 8) & 0xFF, addr & 0xFF] + buf) + self._buf[0] = reg + self._buf[1] = addr >> 8 + self._buf[2] = addr - while not self.spi_device.try_lock(): - pass - self.cs_pin.value = False - self.spi_device.write(cmd) - self.cs_pin.value = True - self.spi_device.unlock() + with self._spi as spi: + spi.write(self._buf, end=3) # pylint: disable=no-member + spi.write(bytearray(buf)) # pylint: disable=no-member def read(self, addr, length, reg=SRAM_READ): """read passed number of bytes at the passed address""" - cmd = bytearray([reg, (addr >> 8) & 0xFF, addr & 0xFF]) + self._buf[0] = reg + self._buf[1] = addr >> 8 + self._buf[2] = addr buf = bytearray(length) - while not self.spi_device.try_lock(): - pass - self.cs_pin.value = False - self.spi_device.write(cmd) - self.spi_device.readinto(buf) - self.cs_pin.value = True - self.spi_device.unlock() + with self._spi as spi: + spi.write(self._buf, end=3) # pylint: disable=no-member + spi.readinto(buf) # pylint: disable=no-member return buf def read8(self, addr, reg=SRAM_READ): @@ -96,13 +109,11 @@ def write16(self, addr, value, reg=SRAM_WRITE): def erase(self, addr, length, value): """erase the passed number of bytes starting at the passed address""" - cmd = bytearray([Adafruit_MCP_SRAM.SRAM_WRITE, (addr >> 8) & 0xFF, addr & 0xFF]) - - while not self.spi_device.try_lock(): - pass - self.cs_pin.value = False - self.spi_device.write(cmd) - for _ in range(length): - self.spi_device.write(bytearray([value])) - self.cs_pin.value = True - self.spi_device.unlock() + self._buf[0] = Adafruit_MCP_SRAM.SRAM_WRITE + self._buf[1] = addr >> 8 + self._buf[2] = addr + fill = bytearray([value]) + with self._spi as spi: + spi.write(self._buf, end=3) # pylint: disable=no-member + for _ in range(length): + spi.write(fill) # pylint: disable=no-member diff --git a/examples/epd_bitmap.py b/examples/epd_bitmap.py index 1b5a344..d9986be 100644 --- a/examples/epd_bitmap.py +++ b/examples/epd_bitmap.py @@ -13,12 +13,14 @@ busy = digitalio.DigitalInOut(board.D6) # give them all to our driver -display = Adafruit_IL0373(152, 152, rst, dc, busy, srcs, ecs, spi) +display = Adafruit_IL0373(152, 152, spi, + cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs, + rst_pin=rst, busy_pin=busy) FILENAME = "blinka.bmp" # clear the buffer -display.clear_buffer() +display.fill(Adafruit_EPD.WHITE) def read_le(s): # as of this writting, int.from_bytes does not have LE support, DIY! @@ -76,11 +78,11 @@ class BMPError(Exception): for col in range(bmpWidth): b, g, r = bytearray(f.read(3)) # BMP files store RGB in BGR if r < 0x80 and g < 0x80 and b < 0x80: - display.draw_pixel(row, col, Adafruit_EPD.BLACK) + display.pixel(row, col, Adafruit_EPD.BLACK) elif r >= 0x80 and g >= 0x80 and b >= 0x80: - display.draw_pixel(row, col, Adafruit_EPD.WHITE) + display.pixel(row, col, Adafruit_EPD.WHITE) elif r >= 0x80: - display.draw_pixel(row, col, Adafruit_EPD.RED) + display.pixel(row, col, Adafruit_EPD.RED) except OSError as e: if e.args[0] == 28: diff --git a/examples/epd_blinka.py b/examples/epd_blinka.py index bbc20b6..8b9b57a 100644 --- a/examples/epd_blinka.py +++ b/examples/epd_blinka.py @@ -5,14 +5,11 @@ from PIL import Image from PIL import ImageDraw from PIL import ImageFont +from adafruit_epd.epd import Adafruit_EPD from adafruit_epd.il0373 import Adafruit_IL0373 # create the spi device and pins we will need spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) -while not spi.try_lock(): - pass -spi.configure(baudrate=16000000) -spi.unlock() ecs = digitalio.DigitalInOut(board.D22) dc = digitalio.DigitalInOut(board.D13) @@ -20,8 +17,14 @@ rst = digitalio.DigitalInOut(board.D19) busy = digitalio.DigitalInOut(board.D26) + # give them all to our driver -display = Adafruit_IL0373(152, 152, rst, dc, busy, srcs, ecs, spi) +print("Creating display") +display = Adafruit_IL0373(104, 212, spi, + cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs, + rst_pin=rst, busy_pin=busy) + + # Create blank image for drawing. # Make sure to create image with mode '1' for 1-bit color. width = display.width @@ -33,7 +36,7 @@ BLACK = (0x00, 0x00, 0x00) # clear the buffer -display.clear_buffer() +display.fill(Adafruit_EPD.WHITE) # Get drawing object to draw on image. draw = ImageDraw.Draw(image) diff --git a/examples/epd_shieldtest.py b/examples/epd_shieldtest.py new file mode 100644 index 0000000..7d61998 --- /dev/null +++ b/examples/epd_shieldtest.py @@ -0,0 +1,60 @@ +# EInk Shield test +import time +import digitalio +import busio +import board +from analogio import AnalogIn +from adafruit_epd.epd import Adafruit_EPD +from adafruit_epd.il91874 import Adafruit_IL91874 + +# create the spi device and pins we will need +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) +ecs = digitalio.DigitalInOut(board.D10) +dc = digitalio.DigitalInOut(board.D9) +srcs = digitalio.DigitalInOut(board.D8) # can be None to use internal memory + +# give them all to our driver +print("Creating display") +display = Adafruit_IL91874(176, 264, spi, # 2.7" Tri-color display + cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs, + rst_pin=None, busy_pin=None) + +display.rotation = 1 + +def read_buttons(): + with AnalogIn(board.A3) as ain: + reading = ain.value / 65535 + if reading > 0.75: + return None + if reading > 0.4: + return 4 + if reading > 0.25: + return 3 + if reading > 0.13: + return 2 + return 1 + +while True: + button = read_buttons() + if not button: + continue + print("Button #%d pressed" % button) + if button == 1: + print("Clear buffer") + display.fill(Adafruit_EPD.WHITE) + display.display() + if button == 2: + print("Draw Rectangles") + display.fill_rect(5, 5, 10, 10, Adafruit_EPD.RED) + display.rect(0, 0, 20, 30, Adafruit_EPD.BLACK) + display.display() + if button == 3: + print("Draw lines") + display.line(0, 0, display.width-1, display.height-1, Adafruit_EPD.BLACK) + display.line(0, display.height-1, display.width-1, 0, Adafruit_EPD.RED) + display.display() + if button == 4: + print("Draw text") + display.text('hello world', 25, 10, Adafruit_EPD.BLACK) + display.display() + time.sleep(0.01) diff --git a/examples/epd_simpletest.py b/examples/epd_simpletest.py index 3e99257..6498a27 100644 --- a/examples/epd_simpletest.py +++ b/examples/epd_simpletest.py @@ -3,31 +3,46 @@ import board from adafruit_epd.epd import Adafruit_EPD from adafruit_epd.il0373 import Adafruit_IL0373 +from adafruit_epd.il91874 import Adafruit_IL91874 # pylint: disable=unused-import +from adafruit_epd.il0398 import Adafruit_IL0398 # pylint: disable=unused-import # create the spi device and pins we will need spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) ecs = digitalio.DigitalInOut(board.D10) dc = digitalio.DigitalInOut(board.D9) -srcs = digitalio.DigitalInOut(board.D8) -rst = digitalio.DigitalInOut(board.D7) -busy = digitalio.DigitalInOut(board.D6) +srcs = digitalio.DigitalInOut(board.D7) # can be None to use internal memory +rst = digitalio.DigitalInOut(board.D11) # can be None to not use this pin +busy = digitalio.DigitalInOut(board.D12) # can be None to not use this pin # give them all to our driver -display = Adafruit_IL0373(152, 152, rst, dc, busy, srcs, ecs, spi) +print("Creating display") +#display = Adafruit_IL91874(176, 264, spi, # 2.7" Tri-color display +#display = Adafruit_IL0373(152, 152, spi, # 1.54" Tri-color display +#display = Adafruit_IL0373(128, 296, spi, # 2.9" Tri-color display +#display = Adafruit_IL0398(400, 300, spi, # 4.2" Tri-color display +display = Adafruit_IL0373(104, 212, spi, # 2.13" Tri-color display + cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs, + rst_pin=rst, busy_pin=busy) + +# IF YOU HAVE A FLEXIBLE DISPLAY (2.13" or 2.9") uncomment these lines! +#display.set_black_buffer(1, False) +#display.set_color_buffer(1, False) + +display.rotation = 1 # clear the buffer -display.clear_buffer() +print("Clear buffer") +display.fill(Adafruit_EPD.WHITE) +display.pixel(10, 100, Adafruit_EPD.BLACK) -r_width = 5 -r_pos = display.height +print("Draw Rectangles") +display.fill_rect(5, 5, 10, 10, Adafruit_EPD.RED) +display.rect(0, 0, 20, 30, Adafruit_EPD.BLACK) -color = Adafruit_EPD.BLACK -while r_pos > display.height/2: - if r_pos < display.height - 50: - color = Adafruit_EPD.RED - display.rect(display.width - r_pos, display.height - r_pos, - display.width - 2*(display.width - r_pos), - display.height - 2*(display.height - r_pos), color) - r_pos = r_pos - r_width +print("Draw lines") +display.line(0, 0, display.width-1, display.height-1, Adafruit_EPD.BLACK) +display.line(0, display.height-1, display.width-1, 0, Adafruit_EPD.RED) +print("Draw text") +display.text('hello world', 25, 10, Adafruit_EPD.BLACK) display.display()