From 6c00f6d04639221217fc5d444841ae0ef8a1dd48 Mon Sep 17 00:00:00 2001 From: Liz Date: Wed, 12 Apr 2023 10:11:15 -0400 Subject: [PATCH 1/3] work in progress for acep support --- adafruit_epd/acep_7color.py | 142 +++++++++++++++++++++++++++++ adafruit_epd/epd.py | 39 ++++++-- examples/acep_colorsquares_test.py | 33 +++++++ 3 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 adafruit_epd/acep_7color.py create mode 100644 examples/acep_colorsquares_test.py diff --git a/adafruit_epd/acep_7color.py b/adafruit_epd/acep_7color.py new file mode 100644 index 0000000..c365619 --- /dev/null +++ b/adafruit_epd/acep_7color.py @@ -0,0 +1,142 @@ +# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_epd.ACEP` - Adafruit ACEP - ePaper display driver +==================================================================================== +CircuitPython driver for Adafruit ACEP display breakouts +* Author(s): Dean Miller +""" + +import time +import PIL +from PIL import Image +from micropython import const +import adafruit_framebuf +from adafruit_epd.epd import Adafruit_EPD + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_EPD.git" + +_ACEP_PANEL_SETTING = const(0x00) +_ACEP_POWER_SETTING = const(0x01) +_ACEP_POWER_OFF = const(0x02) +_ACEP_POWER_OFF_SEQUENCE = const(0x03) +_ACEP_POWER_ON = const(0x04) +_ACEP_BOOSTER_SOFT_START = const(0x06) +_ACEP_DEEP_SLEEP = const(0x07) +_ACEP_DTM = const(0x10) +_ACEP_DISPLAY_REFRESH = const(0x12) +_ACEP_PLL = const(0x30) +_ACEP_TSE = const(0x40) +_ACEP_CDI = const(0x50) +_ACEP_TCON = const(0X60) +_ACEP_RESOLUTION = const(0x61) +_ACEP_PWS = const(0xE3) + +class Adafruit_ACEP(Adafruit_EPD): + """driver class for Adafruit ACEP 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().__init__( + width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin + ) + + if ((height % 8) != 0): + height += 8 - (height % 8) + + self._buffer1_size = int(width * height / 2) + self._buffer2_size = 0 + + if sramcs_pin: + self._buffer1 = 0 + self._buffer2 = 0 + else: + self._buffer1 = bytearray(self._buffer1_size) + self._buffer2 = self._buffer1 + + self._framebuf1 = adafruit_framebuf.FrameBuffer( + self._buffer1, width, height, buf_format=adafruit_framebuf.MHMSB + ) + + self.set_black_buffer(0, True) + self.set_color_buffer(0, 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: + time.sleep(0.1) + 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.busy_wait() + + time.sleep(0.1) + self.command(_ACEP_PANEL_SETTING, bytearray([0xEF, 0x08])) + self.command(_ACEP_POWER_SETTING, bytearray([0x37, 0x00, 0x23, 0x23])) + self.command(_ACEP_POWER_OFF_SEQUENCE, bytearray([0x00])) + self.command(_ACEP_BOOSTER_SOFT_START, bytearray([0xC7, 0xC7, 0x1D])) + self.command(_ACEP_PLL, bytearray([0x3C])) + self.command(_ACEP_TSE, bytearray([0x00])) + self.command(_ACEP_CDI, bytearray([0x37])) + self.command(_ACEP_TCON, bytearray([0x22])) + self.command(_ACEP_RESOLUTION, bytearray([0x02, 0x58, 0x01, 0xC0])) + self.command(_ACEP_PWS, bytearray([0xAA])) + time.sleep(0.1) + self.command(_ACEP_CDI, bytearray([0x37])) + + self.command(_ACEP_RESOLUTION, bytearray([0x02, 0x58, 0x01, 0xC0])) + time.sleep(0.1) + + def power_down(self): + """Power down the display - required when not actively displaying!""" + time.sleep(1) + + self.command(_ACEP_DEEP_SLEEP, bytearray([0xA5])) + + time.sleep(0.1) + + def update(self): + """Update the display from internal memory""" + self.command(_ACEP_POWER_ON) + self.busy_wait() + self.command(_ACEP_DISPLAY_REFRESH, bytearray([0x01, 0x00])) + self.busy_wait() + self.command(_ACEP_POWER_OFF) + if not self._busy: + time.sleep(15) # wait 15 seconds + else: + self.busy_wait() + + 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.""" + #self.command(_ACEP_DTM, end=False) + if index == 0: + return self.command(_ACEP_DTM, end=False) + if index == 1: + return self.command(_ACEP_DTM, 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/epd.py b/adafruit_epd/epd.py index fcccc26..1669d40 100644 --- a/adafruit_epd/epd.py +++ b/adafruit_epd/epd.py @@ -27,6 +27,12 @@ class Adafruit_EPD: # pylint: disable=too-many-instance-attributes, too-many-pu RED = const(3) DARK = const(4) LIGHT = const(5) + + acep_GREEN = const(2) + acep_BLUE = const(3) + acep_RED = const(4) + acep_YELLOW = const(5) + acep_ORANGE = const(6) def __init__( self, width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin @@ -248,28 +254,47 @@ def set_color_buffer(self, index, inverted): def _color_dup(self, func, args, color): black = getattr(self._blackframebuf, func) + green = getattr(self._colorframebuf, func) + blue = getattr(self._colorframebuf, func) red = getattr(self._colorframebuf, func) - if self._blackframebuf is self._colorframebuf: # monochrome + yellow = getattr(self._colorframebuf, func) + orange = 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) + else:''' + black(*args, color=(color == Adafruit_EPD.BLACK) != self._black_inverted) + red(*args, color=(color == Adafruit_EPD.acep_RED or color == Adafruit_EPD.RED) != self._color_inverted) + blue(*args, color=(color == Adafruit_EPD.acep_BLUE) != self._color_inverted) + green(*args, color=(color == Adafruit_EPD.acep_GREEN) != self._color_inverted) + yellow(*args, color=(color == Adafruit_EPD.acep_YELLOW) != self._color_inverted) + orange(*args, color=(color == Adafruit_EPD.acep_ORANGE) != self._color_inverted) def pixel(self, x, y, color): """draw a single pixel in the display buffer""" self._color_dup("pixel", (x, y), color) - + def fill(self, color): """fill the screen with the passed color""" - red_fill = ((color == Adafruit_EPD.RED) != self._color_inverted) * 0xFF black_fill = ((color == Adafruit_EPD.BLACK) != self._black_inverted) * 0xFF - + green_fill = ((color == Adafruit_EPD.acep_GREEN) != self._color_inverted) * 0xFF + blue_fill = ((color == Adafruit_EPD.acep_BLUE) != self._color_inverted) * 0xFF + red_fill = ((color == Adafruit_EPD.acep_RED or color == Adafruit_EPD.RED) != self._color_inverted) * 0xFF + yellow_fill = ((color == Adafruit_EPD.acep_YELLOW) != self._color_inverted) * 0xFF + orange_fill = ((color == Adafruit_EPD.acep_ORANGE) != self._color_inverted) * 0xFF if self.sram: self.sram.erase(0x00, self._buffer1_size, black_fill) + self.sram.erase(self._buffer1_size, self._buffer2_size, green_fill) + self.sram.erase(self._buffer1_size, self._buffer2_size, blue_fill) self.sram.erase(self._buffer1_size, self._buffer2_size, red_fill) + self.sram.erase(self._buffer1_size, self._buffer2_size, yellow_fill) + self.sram.erase(self._buffer1_size, self._buffer2_size, orange_fill) else: self._blackframebuf.fill(black_fill) + self._colorframebuf.fill(green_fill) + self._colorframebuf.fill(blue_fill) self._colorframebuf.fill(red_fill) + self._colorframebuf.fill(yellow_fill) + self._colorframebuf.fill(orange_fill) def rect(self, x, y, width, height, color): # pylint: disable=too-many-arguments """draw a rectangle""" diff --git a/examples/acep_colorsquares_test.py b/examples/acep_colorsquares_test.py new file mode 100644 index 0000000..9c8d84f --- /dev/null +++ b/examples/acep_colorsquares_test.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time +import digitalio +import busio +import board +import PIL +from PIL import Image +from adafruit_epd.epd import Adafruit_EPD +from adafruit_epd.acep_7color import Adafruit_ACEP + +#print(dir(board)) +# create the spi device and pins we will need +spi = busio.SPI(board.EPD_SCK, MOSI=board.EPD_MOSI, MISO=None) +epd_cs = digitalio.DigitalInOut(board.EPD_CS) +epd_dc = digitalio.DigitalInOut(board.EPD_DC) +epd_reset = digitalio.DigitalInOut(board.EPD_RESET) +epd_busy = digitalio.DigitalInOut(board.EPD_BUSY) +srcs = None + +display = Adafruit_ACEP(600, 448, spi, cs_pin=epd_cs, dc_pin=epd_dc, sramcs_pin=srcs, rst_pin=epd_reset, busy_pin=epd_busy) + +display.fill(Adafruit_EPD.WHITE) + +display.fill_rect(30, 20, 20, 20, Adafruit_EPD.BLACK) +display.fill_rect(70, 10, 20, 20, Adafruit_EPD.acep_BLUE) +display.fill_rect(110, 10, 20, 20, Adafruit_EPD.acep_RED) +display.fill_rect(150, 10, 20, 20, Adafruit_EPD.acep_GREEN) +display.fill_rect(190, 10, 20, 20, Adafruit_EPD.acep_YELLOW) +display.fill_rect(230, 10, 20, 20, Adafruit_EPD.acep_ORANGE) + +display.display() From 3f5ea2734c7e4749bf51cf3ab26dfb57c24d4770 Mon Sep 17 00:00:00 2001 From: Liz Date: Wed, 12 Apr 2023 10:21:00 -0400 Subject: [PATCH 2/3] pylint and black --- adafruit_epd/acep_7color.py | 17 ++++++++------- adafruit_epd/epd.py | 33 ++++++++++++++++++++---------- examples/acep_colorsquares_test.py | 17 +++++++++------ 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/adafruit_epd/acep_7color.py b/adafruit_epd/acep_7color.py index c365619..c2a804c 100644 --- a/adafruit_epd/acep_7color.py +++ b/adafruit_epd/acep_7color.py @@ -10,8 +10,6 @@ """ import time -import PIL -from PIL import Image from micropython import const import adafruit_framebuf from adafruit_epd.epd import Adafruit_EPD @@ -31,10 +29,11 @@ _ACEP_PLL = const(0x30) _ACEP_TSE = const(0x40) _ACEP_CDI = const(0x50) -_ACEP_TCON = const(0X60) +_ACEP_TCON = const(0x60) _ACEP_RESOLUTION = const(0x61) _ACEP_PWS = const(0xE3) + class Adafruit_ACEP(Adafruit_EPD): """driver class for Adafruit ACEP ePaper display breakouts""" @@ -45,10 +44,10 @@ def __init__( super().__init__( width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin ) - - if ((height % 8) != 0): + + if (height % 8) != 0: height += 8 - (height % 8) - + self._buffer1_size = int(width * height / 2) self._buffer2_size = 0 @@ -101,7 +100,7 @@ def power_up(self): self.command(_ACEP_PWS, bytearray([0xAA])) time.sleep(0.1) self.command(_ACEP_CDI, bytearray([0x37])) - + self.command(_ACEP_RESOLUTION, bytearray([0x02, 0x58, 0x01, 0xC0])) time.sleep(0.1) @@ -110,7 +109,7 @@ def power_down(self): time.sleep(1) self.command(_ACEP_DEEP_SLEEP, bytearray([0xA5])) - + time.sleep(0.1) def update(self): @@ -129,7 +128,7 @@ 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.""" - #self.command(_ACEP_DTM, end=False) + # self.command(_ACEP_DTM, end=False) if index == 0: return self.command(_ACEP_DTM, end=False) if index == 1: diff --git a/adafruit_epd/epd.py b/adafruit_epd/epd.py index 1669d40..15730fb 100644 --- a/adafruit_epd/epd.py +++ b/adafruit_epd/epd.py @@ -27,10 +27,10 @@ class Adafruit_EPD: # pylint: disable=too-many-instance-attributes, too-many-pu RED = const(3) DARK = const(4) LIGHT = const(5) - - acep_GREEN = const(2) - acep_BLUE = const(3) - acep_RED = const(4) + + acep_GREEN = const(2) + acep_BLUE = const(3) + acep_RED = const(4) acep_YELLOW = const(5) acep_ORANGE = const(6) @@ -259,11 +259,16 @@ def _color_dup(self, func, args, color): red = getattr(self._colorframebuf, func) yellow = getattr(self._colorframebuf, func) orange = getattr(self._colorframebuf, func) - '''if self._blackframebuf is self._colorframebuf: # monochrome + # pylint: disable=pointless-string-statement + """if self._blackframebuf is self._colorframebuf: # monochrome black(*args, color=(color != Adafruit_EPD.WHITE) != self._black_inverted) - else:''' + else:""" black(*args, color=(color == Adafruit_EPD.BLACK) != self._black_inverted) - red(*args, color=(color == Adafruit_EPD.acep_RED or color == Adafruit_EPD.RED) != self._color_inverted) + red( + *args, + color=(color in Adafruit_EPD.acep_RED, Adafruit_EPD.RED) + != self._color_inverted + ) blue(*args, color=(color == Adafruit_EPD.acep_BLUE) != self._color_inverted) green(*args, color=(color == Adafruit_EPD.acep_GREEN) != self._color_inverted) yellow(*args, color=(color == Adafruit_EPD.acep_YELLOW) != self._color_inverted) @@ -272,15 +277,21 @@ def _color_dup(self, func, args, color): def pixel(self, x, y, color): """draw a single pixel in the display buffer""" self._color_dup("pixel", (x, y), color) - + def fill(self, color): """fill the screen with the passed color""" black_fill = ((color == Adafruit_EPD.BLACK) != self._black_inverted) * 0xFF green_fill = ((color == Adafruit_EPD.acep_GREEN) != self._color_inverted) * 0xFF blue_fill = ((color == Adafruit_EPD.acep_BLUE) != self._color_inverted) * 0xFF - red_fill = ((color == Adafruit_EPD.acep_RED or color == Adafruit_EPD.RED) != self._color_inverted) * 0xFF - yellow_fill = ((color == Adafruit_EPD.acep_YELLOW) != self._color_inverted) * 0xFF - orange_fill = ((color == Adafruit_EPD.acep_ORANGE) != self._color_inverted) * 0xFF + red_fill = ( + (color in Adafruit_EPD.acep_RED, Adafruit_EPD.RED) != self._color_inverted + ) * 0xFF + yellow_fill = ( + (color == Adafruit_EPD.acep_YELLOW) != self._color_inverted + ) * 0xFF + orange_fill = ( + (color == Adafruit_EPD.acep_ORANGE) != self._color_inverted + ) * 0xFF if self.sram: self.sram.erase(0x00, self._buffer1_size, black_fill) self.sram.erase(self._buffer1_size, self._buffer2_size, green_fill) diff --git a/examples/acep_colorsquares_test.py b/examples/acep_colorsquares_test.py index 9c8d84f..1fc516f 100644 --- a/examples/acep_colorsquares_test.py +++ b/examples/acep_colorsquares_test.py @@ -1,25 +1,30 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT -import time import digitalio import busio import board -import PIL -from PIL import Image from adafruit_epd.epd import Adafruit_EPD from adafruit_epd.acep_7color import Adafruit_ACEP -#print(dir(board)) # create the spi device and pins we will need spi = busio.SPI(board.EPD_SCK, MOSI=board.EPD_MOSI, MISO=None) epd_cs = digitalio.DigitalInOut(board.EPD_CS) epd_dc = digitalio.DigitalInOut(board.EPD_DC) epd_reset = digitalio.DigitalInOut(board.EPD_RESET) -epd_busy = digitalio.DigitalInOut(board.EPD_BUSY) +epd_busy = digitalio.DigitalInOut(board.EPD_BUSY) srcs = None -display = Adafruit_ACEP(600, 448, spi, cs_pin=epd_cs, dc_pin=epd_dc, sramcs_pin=srcs, rst_pin=epd_reset, busy_pin=epd_busy) +display = Adafruit_ACEP( + 600, + 448, + spi, + cs_pin=epd_cs, + dc_pin=epd_dc, + sramcs_pin=srcs, + rst_pin=epd_reset, + busy_pin=epd_busy, +) display.fill(Adafruit_EPD.WHITE) From 58eba4e801f2a4f457083caae507c3ee64cdf48e Mon Sep 17 00:00:00 2001 From: Liz Date: Mon, 17 Apr 2023 16:14:34 -0400 Subject: [PATCH 3/3] adding attempt as subclass this is not working and is probably not the correct approach but wanted to add what i had attempted --- adafruit_epd/acep_7color.py | 59 +++-- adafruit_epd/epd.py | 361 ++++++++++++++++++++++++++--- examples/acep_colorsquares_test.py | 17 +- 3 files changed, 382 insertions(+), 55 deletions(-) diff --git a/adafruit_epd/acep_7color.py b/adafruit_epd/acep_7color.py index c2a804c..0d6a86a 100644 --- a/adafruit_epd/acep_7color.py +++ b/adafruit_epd/acep_7color.py @@ -12,7 +12,7 @@ import time from micropython import const import adafruit_framebuf -from adafruit_epd.epd import Adafruit_EPD +from adafruit_epd.epd import Adafruit_ADV_EPD __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_EPD.git" @@ -34,7 +34,7 @@ _ACEP_PWS = const(0xE3) -class Adafruit_ACEP(Adafruit_EPD): +class Adafruit_ACEP(Adafruit_ADV_EPD): """driver class for Adafruit ACEP ePaper display breakouts""" # pylint: disable=too-many-arguments @@ -48,22 +48,45 @@ def __init__( if (height % 8) != 0: height += 8 - (height % 8) - self._buffer1_size = int(width * height / 2) - self._buffer2_size = 0 - - if sramcs_pin: - self._buffer1 = 0 - self._buffer2 = 0 - else: - self._buffer1 = bytearray(self._buffer1_size) - self._buffer2 = self._buffer1 - - self._framebuf1 = adafruit_framebuf.FrameBuffer( + self._buffer0_size = int(width * height / 2) + self._buffer1_size = self._buffer0_size + self._buffer2_size = self._buffer0_size + self._buffer3_size = self._buffer0_size + self._buffer4_size = self._buffer0_size + self._buffer5_size = self._buffer0_size + + self._buffer0 = bytearray(self._buffer0_size) + self._buffer1 = self._buffer0 + self._buffer2 = self._buffer0 + self._buffer3 = self._buffer0 + self._buffer4 = self._buffer0 + self._buffer5 = self._buffer0 + + self._framebuf_black = adafruit_framebuf.FrameBuffer( + self._buffer0, width, height, buf_format=adafruit_framebuf.MHMSB + ) + self._framebuf_green = adafruit_framebuf.FrameBuffer( self._buffer1, width, height, buf_format=adafruit_framebuf.MHMSB ) + self._framebuf_blue = adafruit_framebuf.FrameBuffer( + self._buffer2, width, height, buf_format=adafruit_framebuf.MHMSB + ) + self._framebuf_red = adafruit_framebuf.FrameBuffer( + self._buffer3, width, height, buf_format=adafruit_framebuf.MHMSB + ) + self._framebuf_yellow = adafruit_framebuf.FrameBuffer( + self._buffer4, width, height, buf_format=adafruit_framebuf.MHMSB + ) + self._framebuf_orange = adafruit_framebuf.FrameBuffer( + self._buffer5, width, height, buf_format=adafruit_framebuf.MHMSB + ) self.set_black_buffer(0, True) - self.set_color_buffer(0, True) + self.set_green_buffer(0, False) + self.set_blue_buffer(0, False) + self.set_red_buffer(0, False) + self.set_yellow_buffer(0, False) + self.set_orange_buffer(0, False) # pylint: enable=too-many-arguments def begin(self, reset=True): @@ -112,17 +135,23 @@ def power_down(self): time.sleep(0.1) + def set_resolution(self): + self.command(_ACEP_RESOLUTION, bytearray([0x02, 0x58, 0x01, 0xC0])) + self.command(_ACEP_DTM, end=False) + def update(self): """Update the display from internal memory""" self.command(_ACEP_POWER_ON) self.busy_wait() - self.command(_ACEP_DISPLAY_REFRESH, bytearray([0x01, 0x00])) + # self.command(_ACEP_DISPLAY_REFRESH, bytearray([0x01, 0x00])) + self.command(_ACEP_DISPLAY_REFRESH) self.busy_wait() self.command(_ACEP_POWER_OFF) if not self._busy: time.sleep(15) # wait 15 seconds else: self.busy_wait() + time.sleep(0.2) def write_ram(self, index): """Send the one byte command for starting the RAM write process. Returns diff --git a/adafruit_epd/epd.py b/adafruit_epd/epd.py index 15730fb..737a7e7 100644 --- a/adafruit_epd/epd.py +++ b/adafruit_epd/epd.py @@ -10,6 +10,7 @@ """ import time +from PIL import Image from micropython import const from digitalio import Direction from adafruit_epd import mcp_sram @@ -254,25 +255,17 @@ def set_color_buffer(self, index, inverted): def _color_dup(self, func, args, color): black = getattr(self._blackframebuf, func) - green = getattr(self._colorframebuf, func) - blue = getattr(self._colorframebuf, func) red = getattr(self._colorframebuf, func) - yellow = getattr(self._colorframebuf, func) - orange = getattr(self._colorframebuf, func) # pylint: disable=pointless-string-statement - """if self._blackframebuf is self._colorframebuf: # monochrome + 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 in Adafruit_EPD.acep_RED, Adafruit_EPD.RED) - != self._color_inverted - ) - blue(*args, color=(color == Adafruit_EPD.acep_BLUE) != self._color_inverted) - green(*args, color=(color == Adafruit_EPD.acep_GREEN) != self._color_inverted) - yellow(*args, color=(color == Adafruit_EPD.acep_YELLOW) != self._color_inverted) - orange(*args, color=(color == Adafruit_EPD.acep_ORANGE) != self._color_inverted) + else: + black(*args, color=(color == Adafruit_EPD.BLACK) != self._black_inverted) + red( + *args, + color=(color in Adafruit_EPD.acep_RED, Adafruit_EPD.RED) + != self._color_inverted + ) def pixel(self, x, y, color): """draw a single pixel in the display buffer""" @@ -281,31 +274,15 @@ def pixel(self, x, y, color): def fill(self, color): """fill the screen with the passed color""" black_fill = ((color == Adafruit_EPD.BLACK) != self._black_inverted) * 0xFF - green_fill = ((color == Adafruit_EPD.acep_GREEN) != self._color_inverted) * 0xFF - blue_fill = ((color == Adafruit_EPD.acep_BLUE) != self._color_inverted) * 0xFF red_fill = ( (color in Adafruit_EPD.acep_RED, Adafruit_EPD.RED) != self._color_inverted ) * 0xFF - yellow_fill = ( - (color == Adafruit_EPD.acep_YELLOW) != self._color_inverted - ) * 0xFF - orange_fill = ( - (color == Adafruit_EPD.acep_ORANGE) != self._color_inverted - ) * 0xFF if self.sram: self.sram.erase(0x00, self._buffer1_size, black_fill) - self.sram.erase(self._buffer1_size, self._buffer2_size, green_fill) - self.sram.erase(self._buffer1_size, self._buffer2_size, blue_fill) self.sram.erase(self._buffer1_size, self._buffer2_size, red_fill) - self.sram.erase(self._buffer1_size, self._buffer2_size, yellow_fill) - self.sram.erase(self._buffer1_size, self._buffer2_size, orange_fill) else: self._blackframebuf.fill(black_fill) - self._colorframebuf.fill(green_fill) - self._colorframebuf.fill(blue_fill) self._colorframebuf.fill(red_fill) - self._colorframebuf.fill(yellow_fill) - self._colorframebuf.fill(orange_fill) def rect(self, x, y, width, height, color): # pylint: disable=too-many-arguments """draw a rectangle""" @@ -419,3 +396,323 @@ def image(self, image): self.pixel(x, y, Adafruit_EPD.BLACK) else: raise ValueError("Image must be in mode RGB or mode L.") + + +class Adafruit_ADV_EPD: # pylint: disable=too-many-instance-attributes, too-many-public-methods + """Base class for 7 Color EPD displays""" + + ACEP_BLACK = const(0) + ACEP_WHITE = const(1) + ACEP_GREEN = const(2) + ACEP_BLUE = const(3) + ACEP_RED = const(4) + ACEP_YELLOW = const(5) + ACEP_ORANGE = const(6) + + 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 + if rst_pin: + self._rst.direction = Direction.OUTPUT + + # Setup busy pin, if we have one + self._busy = busy_pin + if busy_pin: + self._busy.direction = Direction.INPUT + + # Setup dc pin (required) + self._dc = dc_pin + self._dc.direction = Direction.OUTPUT + self._dc.value = False + + # Setup cs pin (required) + self._cs = cs_pin + self._cs.direction = Direction.OUTPUT + self._cs.value = True + + # SPI interface (required) + self.spi_device = spi + while not self.spi_device.try_lock(): + time.sleep(0.01) + self.spi_device.configure(baudrate=1000000) # 1 Mhz + self.spi_device.unlock() + + self._spibuf = bytearray(1) + self._single_byte_tx = False + + self.sram = None + + # pylint: disable=line-too-long + self._buffer0_size = ( + self._buffer1_size + ) = ( + self._buffer2_size + ) = self._buffer3_size = self._buffer4_size = self._buffer5_size = 0 + self._buffer0 = ( + self._buffer1 + ) = self._buffer2 = self._buffer3 = self._buffer4 = self._buffer5 = None + self._framebuf_black = ( + self._framebuf_green + ) = ( + self._framebuf_blue + ) = self._framebuf_red = self._framebuf_yellow = self._framebuf_orange = None + self._blackframebuf = ( + self._greenframebuf + ) = ( + self._blueframebuf + ) = self._redframebuf = self._yellowframebuf = self._orangeframebuf = None + self._black_inverted = ( + self._green_inverted + ) = ( + self._blue_inverted + ) = self._red_inverted = self._yellow_inverted = self._orange_inverted = True + self.hardware_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(0.1) + self._rst.value = True + 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 + + while not self.spi_device.try_lock(): + time.sleep(0.01) + ret = self._spi_transfer(cmd) + + if data is not None: + self._dc.value = True + self._spi_transfer(data) + if end: + self._cs.value = True + self.spi_device.unlock() + + return ret + + def _spi_transfer(self, data): + """Transfer one byte or bytearray, toggling the cs pin if required by the EPD chipset""" + if isinstance(data, int): # single byte! + self._spibuf[0] = data + + # easy & fast case: array and no twiddling + if not self._single_byte_tx and isinstance(data, bytearray): + self.spi_device.write(data) + return None + + # if its a single byte + if isinstance(data, int): # single byte! + if self._single_byte_tx: + self._cs.value = False + try: + self.spi_device.write_readinto(self._spibuf, self._spibuf) + except NotImplementedError: + self.spi_device.write(self._spibuf) + if self._single_byte_tx: + self._cs.value = True + return self._spibuf[0] + + if isinstance(data, bytearray): + for x in data: + self._spi_transfer(x) + return None + + 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 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._framebuf_black + else: + raise RuntimeError("Buffer index must be 0") + self._black_inverted = inverted + + def set_green_buffer(self, index, inverted): + """Set the index for the color buffer data (0 or 1) and whether its inverted""" + if index == 0: + self._greenframebuf = self._framebuf_green + else: + raise RuntimeError("Buffer index must be 0") + self._green_inverted = inverted + + def set_blue_buffer(self, index, inverted): + """Set the index for the color buffer data (0 or 1) and whether its inverted""" + if index == 0: + self._blueframebuf = self._framebuf_blue + else: + raise RuntimeError("Buffer index must be 0") + self._blue_inverted = inverted + + def set_red_buffer(self, index, inverted): + """Set the index for the color buffer data (0 or 1) and whether its inverted""" + if index == 0: + self._redframebuf = self._framebuf_red + else: + raise RuntimeError("Buffer index must be 0") + self._red_inverted = inverted + + def set_yellow_buffer(self, index, inverted): + """Set the index for the color buffer data (0 or 1) and whether its inverted""" + if index == 0: + self._yellowframebuf = self._framebuf_yellow + else: + raise RuntimeError("Buffer index must be 0") + self._yellow_inverted = inverted + + def set_orange_buffer(self, index, inverted): + """Set the index for the color buffer data (0 or 1) and whether its inverted""" + if index == 0: + self._orangeframebuf = self._framebuf_orange + else: + raise RuntimeError("Buffer index must be 0") + self._orange_inverted = inverted + + def color_map(self, image): + # pylint: disable=line-too-long + palette = Image.new("P", (1, 1)) + palette.putpalette((0, 0, 0, 255, 255, 255, 0, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 255, 128, 0, ) + (0, 0, 0) * 249) + + img_7color = image.convert("RGB").quantize(palette=palette) + buf_7color = bytearray(img_7color.tobytes("raw")) + + buf = [0x00] * int(self._width * self._height / 2) + count = 0 + for i in range(0, len(buf_7color), 2): + buf[count] = (buf_7color[i] << 4) + buf_7color[i + 1] + count += 1 + + return buf + + def write_to_buffer(self, buffer): + while not self.spi_device.try_lock(): + time.sleep(0.01) + self._dc.value = True + + self._spi_transfer(buffer) + + self._cs.value = True + self.spi_device.unlock() + time.sleep(0.002) + + 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.set_resolution() + # pylint: disable=pointless-string-statement + """while not self.spi_device.try_lock(): + time.sleep(0.01) + self._dc.value = True + + self._spi_transfer(self._buffer1) + + self._cs.value = True + self.spi_device.unlock() + time.sleep(0.002)""" + self.write_to_buffer(self._buffer0) + self.write_to_buffer(self._buffer1) + self.write_to_buffer(self._buffer2) + self.write_to_buffer(self._buffer3) + self.write_to_buffer(self._buffer4) + self.write_to_buffer(self._buffer5) + + self.update() + + def fill(self, color): + """fill the screen with the passed color""" + + black_fill = color == Adafruit_ADV_EPD.ACEP_BLACK + green_fill = color == Adafruit_ADV_EPD.ACEP_GREEN + blue_fill = color == Adafruit_ADV_EPD.ACEP_BLUE + red_fill = color == Adafruit_ADV_EPD.ACEP_RED + yellow_fill = color == Adafruit_ADV_EPD.ACEP_YELLOW + orange_fill = color == Adafruit_ADV_EPD.ACEP_ORANGE + + self._buffer0 = self._framebuf_black.fill(black_fill) + self._buffer1 = self._framebuf_green.fill(green_fill) + self._buffer2 = self._framebuf_blue.fill(blue_fill) + self._buffer3 = self._framebuf_red.fill(red_fill) + self._buffer4 = self._framebuf_yellow.fill(yellow_fill) + self._buffer5 = self._framebuf_orange.fill(orange_fill) + + def pixel(self, x, y, color): + """draw a single pixel in the display buffer""" + self._framebuf1.set_pixel(x, y, color) + + def rect(self, x, y, width, height, color): # pylint: disable=too-many-arguments + """draw a rectangle""" + self._framebuf1.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""" + self._buffer0 = self._framebuf_black.fill_rect(x, y, width, height, color) + self._buffer1 = self._framebuf_green.fill_rect(x, y, width, height, color) + self._buffer2 = self._framebuf_blue.fill_rect(x, y, width, height, color) + self._buffer3 = self._framebuf_red.fill_rect(x, y, width, height, color) + self._buffer4 = self._framebuf_yellow.fill_rect(x, y, width, height, color) + self._buffer5 = self._framebuf_orange.fill_rect(x, y, width, height, color) + + # pylint: disable=pointless-string-statement + '''@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 + if self._framebuf2: + self._framebuf2.rotation = val''' + + def set_border(self, color): + """Set the border colour.""" + self.command(0x3C, bytearray([color])) diff --git a/examples/acep_colorsquares_test.py b/examples/acep_colorsquares_test.py index 1fc516f..0230d3e 100644 --- a/examples/acep_colorsquares_test.py +++ b/examples/acep_colorsquares_test.py @@ -4,7 +4,7 @@ import digitalio import busio import board -from adafruit_epd.epd import Adafruit_EPD +from adafruit_epd.epd import Adafruit_ADV_EPD from adafruit_epd.acep_7color import Adafruit_ACEP # create the spi device and pins we will need @@ -26,13 +26,14 @@ busy_pin=epd_busy, ) -display.fill(Adafruit_EPD.WHITE) +display.fill(Adafruit_ADV_EPD.ACEP_WHITE) -display.fill_rect(30, 20, 20, 20, Adafruit_EPD.BLACK) -display.fill_rect(70, 10, 20, 20, Adafruit_EPD.acep_BLUE) -display.fill_rect(110, 10, 20, 20, Adafruit_EPD.acep_RED) -display.fill_rect(150, 10, 20, 20, Adafruit_EPD.acep_GREEN) -display.fill_rect(190, 10, 20, 20, Adafruit_EPD.acep_YELLOW) -display.fill_rect(230, 10, 20, 20, Adafruit_EPD.acep_ORANGE) +display.fill_rect(30, 20, 20, 20, Adafruit_ADV_EPD.ACEP_BLACK) +display.fill_rect(70, 10, 20, 20, Adafruit_ADV_EPD.ACEP_BLUE) +display.fill_rect(110, 10, 20, 20, Adafruit_ADV_EPD.ACEP_RED) +display.fill_rect(150, 10, 20, 20, Adafruit_ADV_EPD.ACEP_GREEN) +display.fill_rect(190, 10, 20, 20, Adafruit_ADV_EPD.ACEP_YELLOW) +display.fill_rect(230, 10, 20, 20, Adafruit_ADV_EPD.ACEP_ORANGE) +display.set_border(Adafruit_ADV_EPD.ACEP_BLACK) display.display()