From a04a6a8d48eb91e041d036dbb70506dfd9e774fb Mon Sep 17 00:00:00 2001 From: BlitzCityDIY Date: Thu, 14 Aug 2025 11:04:47 -0400 Subject: [PATCH 1/7] in progress, need to test with pi --- adafruit_epd/uc8179.py | 252 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 adafruit_epd/uc8179.py diff --git a/adafruit_epd/uc8179.py b/adafruit_epd/uc8179.py new file mode 100644 index 0000000..d3a606e --- /dev/null +++ b/adafruit_epd/uc8179.py @@ -0,0 +1,252 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_epd.uc8179` - Adafruit UC8179 - ePaper display driver +==================================================================================== +CircuitPython driver for Adafruit UC8179 display breakouts +* Author(s): Liz Clark +""" + +import time + +import adafruit_framebuf +from micropython import const + +from adafruit_epd.epd import Adafruit_EPD + +try: + """Needed for type annotations""" + import typing + + from busio import SPI + from digitalio import DigitalInOut + from typing_extensions import Literal + +except ImportError: + pass + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_EPD.git" + +# UC8179 commands +_UC8179_PANELSETTING = const(0x00) +_UC8179_POWERSETTING = const(0x01) +_UC8179_POWEROFF = const(0x02) +_UC8179_POWERON = const(0x04) +_UC8179_DEEPSLEEP = const(0x07) +_UC8179_WRITE_RAM1 = const(0x10) +_UC8179_DATASTOP = const(0x11) +_UC8179_DISPLAYREFRESH = const(0x12) +_UC8179_WRITE_RAM2 = const(0x13) +_UC8179_DUALSPI = const(0x15) +_UC8179_WRITE_VCOM = const(0x50) +_UC8179_TCON = const(0x60) +_UC8179_TRES = const(0x61) +_UC8179_GET_STATUS = const(0x71) + +BUSY_WAIT = const(500) # milliseconds + + +class Adafruit_UC8179(Adafruit_EPD): + """driver class for Adafruit UC8179 ePaper display breakouts""" + + # Default initialization sequence + _default_init_code = bytes([ + _UC8179_POWERSETTING, 4, + 0x07, # VGH=20V + 0x07, # VGL=-20V + 0x3F, # VDH=15V + 0x3F, # VDL=-15V + + _UC8179_POWERON, 0, + 0xFF, 100, # busy wait + + _UC8179_PANELSETTING, 1, + 0b010111, # BW OTP LUT + + _UC8179_TRES, 4, + 0x02, 0x88, 0x01, 0xE0, + + _UC8179_DUALSPI, 1, 0x00, + + _UC8179_WRITE_VCOM, 2, 0x10, 0x07, + _UC8179_TCON, 1, 0x22, + + 0xFE # End marker + ]) + + def __init__( + self, + width: int, + height: int, + spi: SPI, + *, + cs_pin: DigitalInOut, + dc_pin: DigitalInOut, + sramcs_pin: DigitalInOut, + rst_pin: DigitalInOut, + busy_pin: DigitalInOut, + ) -> None: + # Adjust height to be divisible by 8 (direct from Arduino) + if (height % 8) != 0: + height += 8 - (height % 8) + + super().__init__(width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin) + + # Calculate buffer sizes exactly as Arduino does: width * height / 8 + self._buffer1_size = width * height // 8 + self._buffer2_size = self._buffer1_size + + if sramcs_pin: + # Using external SRAM + self._buffer1 = self.sram.get_view(0) + self._buffer2 = self.sram.get_view(self._buffer1_size) + else: + # Using internal RAM + self._buffer1 = bytearray(self._buffer1_size) + self._buffer2 = bytearray(self._buffer2_size) + + # Create frame buffers + 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, + ) + + # Set up which frame buffer is which color + self.set_black_buffer(0, True) + self.set_color_buffer(1, False) + + # UC8179 uses single byte transactions + self._single_byte_tx = True + + # Custom init code if needed + self._epd_init_code = None + + # Default refresh delay (from Adafruit_EPD base class in Arduino) + self.default_refresh_delay = 15 # seconds + # pylint: enable=too-many-arguments + + def begin(self, reset: bool = True) -> None: + """Begin communication with the display and set basic settings""" + # Direct port of Arduino begin() method + + # Note: Arduino sets _data_entry_mode = THINKINK_UC8179 + # This appears to be specific to SRAM organization for this chip + + if reset: + self.hardware_reset() + + # Black buffer defaults to inverted (0 means black) + self.set_black_buffer(0, True) + # Red/color buffer defaults to not inverted (1 means red) + self.set_color_buffer(1, False) + + self.power_down() + + def busy_wait(self) -> None: + """Wait for display to be done with current task, either by polling the + busy pin, or pausing""" + if self._busy: + # Wait for busy pin to go HIGH + while not self._busy.value: + self.command(_UC8179_GET_STATUS) + time.sleep(0.1) + else: + # No busy pin, just wait + time.sleep(BUSY_WAIT / 1000.0) + # Additional delay after busy signal + time.sleep(0.2) + + def power_up(self) -> None: + """Power up the display in preparation for writing RAM and updating""" + self.hardware_reset() + + # Use custom init code if provided, otherwise use default + init_code = self._epd_init_code if self._epd_init_code else self._default_init_code + + # Process initialization sequence + self._command_list(init_code) + + # Set display resolution (using WIDTH and HEIGHT macros in Arduino) + self.command( + _UC8179_TRES, + bytearray([self._width >> 8, self._width & 0xFF, self._height >> 8, self._height & 0xFF]), + ) + + def power_down(self) -> None: + """Power down the display - required when not actively displaying!""" + self.command(_UC8179_POWEROFF) + self.busy_wait() + + # Only deep sleep if we have a reset pin to wake it up + if self._rst: + self.command(_UC8179_DEEPSLEEP, bytearray([0x05])) + time.sleep(0.1) + + def update(self) -> None: + """Update the display from internal memory""" + self.command(_UC8179_DISPLAYREFRESH) + time.sleep(0.1) + self.busy_wait() + + if not self._busy: + # If no busy pin, use default refresh delay + time.sleep(self.default_refresh_delay / 1000.0) + + def write_ram(self, index: Literal[0, 1]) -> int: + """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(_UC8179_WRITE_RAM1, end=False) + if index == 1: + return self.command(_UC8179_WRITE_RAM2, end=False) + raise RuntimeError("RAM index must be 0 or 1") + + def set_ram_address(self, x: int, y: int) -> None: # noqa: PLR6301, F841 + """Set the RAM address location, not used on this chipset but required by + the superclass""" + # Not used in UC8179 chip + pass + + def set_ram_window(self, x1: int, y1: int, x2: int, y2: int) -> None: # noqa: PLR6301, F841 + """Set the RAM window, not used on this chipset but required by + the superclass""" + # Not used in UC8179 chip + pass + + def _command_list(self, init_sequence: bytes) -> None: + """Process a command list for initialization""" + i = 0 + while i < len(init_sequence): + cmd = init_sequence[i] + i += 1 + + # Check for end marker + if cmd == 0xFE: + break + + # Get number of data bytes + num_data = init_sequence[i] + i += 1 + + # Check for delay command (0xFF) + if cmd == 0xFF: + time.sleep(num_data / 1000.0) + else: + # Send command with data if any + if num_data > 0: + self.command(cmd, bytearray(init_sequence[i:i + num_data])) + i += num_data + else: + self.command(cmd) \ No newline at end of file From 5ea27cf74b9e55d2bf7b9af98e47d7f1b849dd15 Mon Sep 17 00:00:00 2001 From: BlitzCityDIY Date: Thu, 14 Aug 2025 11:38:24 -0400 Subject: [PATCH 2/7] update power_up --- adafruit_epd/uc8179.py | 123 ++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 81 deletions(-) diff --git a/adafruit_epd/uc8179.py b/adafruit_epd/uc8179.py index d3a606e..8c7a79e 100644 --- a/adafruit_epd/uc8179.py +++ b/adafruit_epd/uc8179.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# SPDX-FileCopyrightText: 2024 # # SPDX-License-Identifier: MIT @@ -6,7 +6,7 @@ `adafruit_epd.uc8179` - Adafruit UC8179 - ePaper display driver ==================================================================================== CircuitPython driver for Adafruit UC8179 display breakouts -* Author(s): Liz Clark +* Author(s): Your Name Here """ import time @@ -52,31 +52,6 @@ class Adafruit_UC8179(Adafruit_EPD): """driver class for Adafruit UC8179 ePaper display breakouts""" - # Default initialization sequence - _default_init_code = bytes([ - _UC8179_POWERSETTING, 4, - 0x07, # VGH=20V - 0x07, # VGL=-20V - 0x3F, # VDH=15V - 0x3F, # VDL=-15V - - _UC8179_POWERON, 0, - 0xFF, 100, # busy wait - - _UC8179_PANELSETTING, 1, - 0b010111, # BW OTP LUT - - _UC8179_TRES, 4, - 0x02, 0x88, 0x01, 0xE0, - - _UC8179_DUALSPI, 1, 0x00, - - _UC8179_WRITE_VCOM, 2, 0x10, 0x07, - _UC8179_TCON, 1, 0x22, - - 0xFE # End marker - ]) - def __init__( self, width: int, @@ -125,32 +100,18 @@ def __init__( # Set up which frame buffer is which color self.set_black_buffer(0, True) self.set_color_buffer(1, False) - + # UC8179 uses single byte transactions self._single_byte_tx = True - # Custom init code if needed - self._epd_init_code = None - # Default refresh delay (from Adafruit_EPD base class in Arduino) self.default_refresh_delay = 15 # seconds # pylint: enable=too-many-arguments def begin(self, reset: bool = True) -> None: """Begin communication with the display and set basic settings""" - # Direct port of Arduino begin() method - - # Note: Arduino sets _data_entry_mode = THINKINK_UC8179 - # This appears to be specific to SRAM organization for this chip - if reset: self.hardware_reset() - - # Black buffer defaults to inverted (0 means black) - self.set_black_buffer(0, True) - # Red/color buffer defaults to not inverted (1 means red) - self.set_color_buffer(1, False) - self.power_down() def busy_wait(self) -> None: @@ -170,24 +131,50 @@ def busy_wait(self) -> None: def power_up(self) -> None: """Power up the display in preparation for writing RAM and updating""" self.hardware_reset() - - # Use custom init code if provided, otherwise use default - init_code = self._epd_init_code if self._epd_init_code else self._default_init_code - - # Process initialization sequence - self._command_list(init_code) - - # Set display resolution (using WIDTH and HEIGHT macros in Arduino) + + # Power setting + self.command( + _UC8179_POWERSETTING, + bytearray( + [ + 0x07, # VGH=20V + 0x07, # VGL=-20V + 0x3F, # VDH=15V + 0x3F, # VDL=-15V + ] + ), + ) + + # Power on + self.command(_UC8179_POWERON) + time.sleep(0.1) # 100ms delay + self.busy_wait() + + # Panel setting + self.command(_UC8179_PANELSETTING, bytearray([0b010111])) # BW OTP LUT + + # Resolution setting self.command( _UC8179_TRES, - bytearray([self._width >> 8, self._width & 0xFF, self._height >> 8, self._height & 0xFF]), + bytearray( + [self._width >> 8, self._width & 0xFF, self._height >> 8, self._height & 0xFF] + ), ) + # Dual SPI setting + self.command(_UC8179_DUALSPI, bytearray([0x00])) + + # VCOM setting + self.command(_UC8179_WRITE_VCOM, bytearray([0x10, 0x07])) + + # TCON setting + self.command(_UC8179_TCON, bytearray([0x22])) + def power_down(self) -> None: """Power down the display - required when not actively displaying!""" self.command(_UC8179_POWEROFF) self.busy_wait() - + # Only deep sleep if we have a reset pin to wake it up if self._rst: self.command(_UC8179_DEEPSLEEP, bytearray([0x05])) @@ -196,12 +183,12 @@ def power_down(self) -> None: def update(self) -> None: """Update the display from internal memory""" self.command(_UC8179_DISPLAYREFRESH) - time.sleep(0.1) + time.sleep(0.1) # 100ms delay self.busy_wait() - + if not self._busy: # If no busy pin, use default refresh delay - time.sleep(self.default_refresh_delay / 1000.0) + time.sleep(self.default_refresh_delay) def write_ram(self, index: Literal[0, 1]) -> int: """Send the one byte command for starting the RAM write process. Returns @@ -224,29 +211,3 @@ def set_ram_window(self, x1: int, y1: int, x2: int, y2: int) -> None: # noqa: P the superclass""" # Not used in UC8179 chip pass - - def _command_list(self, init_sequence: bytes) -> None: - """Process a command list for initialization""" - i = 0 - while i < len(init_sequence): - cmd = init_sequence[i] - i += 1 - - # Check for end marker - if cmd == 0xFE: - break - - # Get number of data bytes - num_data = init_sequence[i] - i += 1 - - # Check for delay command (0xFF) - if cmd == 0xFF: - time.sleep(num_data / 1000.0) - else: - # Send command with data if any - if num_data > 0: - self.command(cmd, bytearray(init_sequence[i:i + num_data])) - i += num_data - else: - self.command(cmd) \ No newline at end of file From 6044f1110f8557dbc5f20c905e1b42acf90fd907 Mon Sep 17 00:00:00 2001 From: BlitzCityDIY Date: Thu, 14 Aug 2025 11:42:05 -0400 Subject: [PATCH 3/7] Update uc8179.py --- adafruit_epd/uc8179.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_epd/uc8179.py b/adafruit_epd/uc8179.py index 8c7a79e..b62f1c9 100644 --- a/adafruit_epd/uc8179.py +++ b/adafruit_epd/uc8179.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries # # SPDX-License-Identifier: MIT @@ -6,7 +6,7 @@ `adafruit_epd.uc8179` - Adafruit UC8179 - ePaper display driver ==================================================================================== CircuitPython driver for Adafruit UC8179 display breakouts -* Author(s): Your Name Here +* Author(s): Liz Clark """ import time From 6b19e3e906816ea1d8e8b8e85cf962b7a78b845d Mon Sep 17 00:00:00 2001 From: Liz Date: Thu, 14 Aug 2025 20:31:55 -0400 Subject: [PATCH 4/7] fix text, turn off single byte --- adafruit_epd/uc8179.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_epd/uc8179.py b/adafruit_epd/uc8179.py index b62f1c9..9bf5968 100644 --- a/adafruit_epd/uc8179.py +++ b/adafruit_epd/uc8179.py @@ -102,7 +102,7 @@ def __init__( self.set_color_buffer(1, False) # UC8179 uses single byte transactions - self._single_byte_tx = True + self._single_byte_tx = False # Default refresh delay (from Adafruit_EPD base class in Arduino) self.default_refresh_delay = 15 # seconds @@ -151,7 +151,7 @@ def power_up(self) -> None: self.busy_wait() # Panel setting - self.command(_UC8179_PANELSETTING, bytearray([0b010111])) # BW OTP LUT + self.command(_UC8179_PANELSETTING, bytearray([0b011111])) # BW OTP LUT # Resolution setting self.command( From f0648713059c99f43199333dfbfc0af70147a9dd Mon Sep 17 00:00:00 2001 From: Liz Date: Fri, 15 Aug 2025 13:14:09 -0400 Subject: [PATCH 5/7] Revert "Merge branch 'ssd1883' into UC8197" This reverts commit c69536e083fa67b7a3820b44ce33246842a9bc82, reversing changes made to 6b19e3e906816ea1d8e8b8e85cf962b7a78b845d. --- adafruit_epd/ssd1680.py | 10 +- adafruit_epd/ssd1683.py | 262 ----------------------------------- examples/epd_bitmap.py | 2 - examples/epd_blinka.py | 2 - examples/epd_pillow_demo.py | 2 - examples/epd_pillow_image.py | 2 - examples/epd_simpletest.py | 2 - 7 files changed, 5 insertions(+), 277 deletions(-) delete mode 100644 adafruit_epd/ssd1683.py diff --git a/adafruit_epd/ssd1680.py b/adafruit_epd/ssd1680.py index 9b98446..4c0bd7c 100644 --- a/adafruit_epd/ssd1680.py +++ b/adafruit_epd/ssd1680.py @@ -242,7 +242,7 @@ def power_up(self): self.command( _SSD1680_DRIVER_CONTROL, - bytearray([self._height & 0xFF, (self._height) >> 8, 0x00]), + bytearray([self._height, (self._height) >> 8, 0x00]), ) self.command(_SSD1680_DATA_MODE, bytearray([0x03])) @@ -251,19 +251,19 @@ def power_up(self): self.command(_SSD1680_GATE_VOLTAGE, bytearray([0x17])) self.command(_SSD1680_SOURCE_VOLTAGE, bytearray([0x41, 0x00, 0x32])) - self.command(_SSD1680_SET_RAMXPOS, bytearray([0x00, (self._width // 8) - 1])) + self.command(_SSD1680_SET_RAMXPOS, bytearray([0x00, (self._width // 8)])) self.command( _SSD1680_SET_RAMYPOS, - bytearray([0x00, 0x00, (self._height - 1) & 0xFF, (self._height - 1) >> 8]), + bytearray([0x00, 0x00, self._height, (self._height) >> 8]), ) # Set border waveform self.command(_SSD1680_WRITE_BORDER, bytearray([0x05])) # Set ram X count - self.command(_SSD1680_SET_RAMXCOUNT, bytearray([0x00])) + self.command(_SSD1680_SET_RAMXCOUNT, bytearray([0x01])) # Set ram Y count - self.command(_SSD1680_SET_RAMYCOUNT, bytearray([0x00, 0x00])) + self.command(_SSD1680_SET_RAMYCOUNT, bytearray([self._height, 0])) self.busy_wait() def update(self): diff --git a/adafruit_epd/ssd1683.py b/adafruit_epd/ssd1683.py deleted file mode 100644 index 757b4b8..0000000 --- a/adafruit_epd/ssd1683.py +++ /dev/null @@ -1,262 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -""" -`adafruit_epd.ssd1683` - Adafruit SSD1683 - ePaper display driver -==================================================================================== -CircuitPython driver for Adafruit SSD1683 display breakouts -* Author(s): Liz Clark -""" - -import time - -import adafruit_framebuf -from micropython import const - -from adafruit_epd.epd import Adafruit_EPD - -try: - """Needed for type annotations""" - import typing - - from busio import SPI - from digitalio import DigitalInOut - from typing_extensions import Literal - -except ImportError: - pass - -__version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_EPD.git" - -# Command constants -_SSD1683_DRIVER_CONTROL = const(0x01) -_SSD1683_GATE_VOLTAGE = const(0x03) -_SSD1683_SOURCE_VOLTAGE = const(0x04) -_SSD1683_PROGOTP_INITIAL = const(0x08) -_SSD1683_PROGREG_INITIAL = const(0x09) -_SSD1683_READREG_INITIAL = const(0x0A) -_SSD1683_BOOST_SOFTSTART = const(0x0C) -_SSD1683_DEEP_SLEEP = const(0x10) -_SSD1683_DATA_MODE = const(0x11) -_SSD1683_SW_RESET = const(0x12) -_SSD1683_HV_READY = const(0x14) -_SSD1683_VCI_DETECT = const(0x15) -_SSD1683_PROGRAM_WSOTP = const(0x16) -_SSD1683_PROGRAM_AUTO = const(0x17) -_SSD1683_TEMP_CONTROL = const(0x18) -_SSD1683_TEMP_WRITE = const(0x1A) -_SSD1683_TEMP_READ = const(0x1B) -_SSD1683_TEMP_CONTROLEXT = const(0x1C) -_SSD1683_MASTER_ACTIVATE = const(0x20) -_SSD1683_DISP_CTRL1 = const(0x21) -_SSD1683_DISP_CTRL2 = const(0x22) -_SSD1683_WRITE_RAM1 = const(0x24) -_SSD1683_WRITE_RAM2 = const(0x26) -_SSD1683_READ_RAM1 = const(0x27) -_SSD1683_SENSE_VCOM = const(0x28) -_SSD1683_SENSEDUR_VCOM = const(0x29) -_SSD1683_PROGOTP_VCOM = const(0x2A) -_SSD1683_WRITE_VCOM = const(0x2C) -_SSD1683_READ_OTP = const(0x2D) -_SSD1683_READ_USERID = const(0x2E) -_SSD1683_READ_STATUS = const(0x2F) -_SSD1683_WRITE_LUT = const(0x32) -_SSD1683_WRITE_BORDER = const(0x3C) -_SSD1683_END_OPTION = const(0x3F) -_SSD1683_SET_RAMXPOS = const(0x44) -_SSD1683_SET_RAMYPOS = const(0x45) -_SSD1683_SET_RAMXCOUNT = const(0x4E) -_SSD1683_SET_RAMYCOUNT = const(0x4F) - -# Other constants -_EPD_RAM_BW = const(0x10) -_EPD_RAM_RED = const(0x13) -_BUSY_WAIT = const(500) - - -class Adafruit_SSD1683(Adafruit_EPD): - """driver class for Adafruit SSD1683 ePaper display breakouts""" - - def __init__( - self, - width: int, - height: int, - spi: SPI, - *, - cs_pin: DigitalInOut, - dc_pin: DigitalInOut, - sramcs_pin: DigitalInOut, - rst_pin: DigitalInOut, - busy_pin: DigitalInOut, - ) -> None: - super().__init__(width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin) - - stride = width - if stride % 8 != 0: - stride += 8 - stride % 8 - - self._buffer1_size = int(stride * height / 8) - self._buffer2_size = self._buffer1_size - - if sramcs_pin: - self._buffer1 = self.sram.get_view(0) - self._buffer2 = self.sram.get_view(self._buffer1_size) - else: - self._buffer1 = bytearray(self._buffer1_size) - self._buffer2 = bytearray(self._buffer2_size) - - 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) - - # Set single byte transactions flag - self._single_byte_tx = True - - # Set the display update value - self._display_update_val = 0xF7 - - # Default initialization sequence (tri-color mode) - self._default_init_code = bytes( - [ - _SSD1683_SW_RESET, - 0, # Software reset - 0xFF, - 50, # Wait for busy (50ms delay) - _SSD1683_WRITE_BORDER, - 1, # Border waveform control - 0x05, # Border color/waveform - _SSD1683_TEMP_CONTROL, - 1, # Temperature control - 0x80, # Read temp - _SSD1683_DATA_MODE, - 1, # Data entry mode - 0x03, # Y decrement, X increment - 0xFE, # End of initialization - ] - ) - - def begin(self, reset: bool = True) -> None: - """Begin communication with the display and set basic settings""" - if reset: - self.hardware_reset() - self.power_down() - - def busy_wait(self) -> None: - """Wait for display to be done with current task, either by polling the - busy pin, or pausing""" - if self._busy: - while self._busy.value: # wait for busy low - time.sleep(0.01) - else: - time.sleep(_BUSY_WAIT / 1000.0) # Convert ms to seconds - - def power_up(self) -> None: - """Power up the display in preparation for writing RAM and updating""" - self.hardware_reset() - time.sleep(0.1) - self.busy_wait() - - # Use custom init code if provided, otherwise use default - init_code = self._default_init_code - if hasattr(self, "_epd_init_code") and self._epd_init_code is not None: - init_code = self._epd_init_code - - # Send initialization sequence - self._send_command_list(init_code) - - # Set RAM window - self.set_ram_window(0, 0, (self._width // 8) - 1, self._height - 1) - - # Set RAM address to start position - self.set_ram_address(0, 0) - - # Set LUT if we have one - if hasattr(self, "_epd_lut_code") and self._epd_lut_code: - self._send_command_list(self._epd_lut_code) - - # Set display size and driver output control - _b0 = (self._height - 1) & 0xFF - _b1 = ((self._height - 1) >> 8) & 0xFF - _b2 = 0x00 - self.command(_SSD1683_DRIVER_CONTROL, bytearray([_b0, _b1, _b2])) - - def power_down(self) -> None: - """Power down the display - required when not actively displaying!""" - # Only deep sleep if we can get out of it - if self._rst: - # deep sleep - self.command(_SSD1683_DEEP_SLEEP, bytearray([0x01])) - time.sleep(0.1) - else: - self.command(_SSD1683_SW_RESET) - self.busy_wait() - - def update(self) -> None: - """Update the display from internal memory""" - # display update sequence - self.command(_SSD1683_DISP_CTRL2, bytearray([self._display_update_val])) - self.command(_SSD1683_MASTER_ACTIVATE) - self.busy_wait() - - if not self._busy: - time.sleep(1) # wait 1 second - - def write_ram(self, index: Literal[0, 1]) -> int: - """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(_SSD1683_WRITE_RAM1, end=False) - if index == 1: - return self.command(_SSD1683_WRITE_RAM2, end=False) - raise RuntimeError("RAM index must be 0 or 1") - - def set_ram_address(self, x: int, y: int) -> None: - """Set the RAM address location""" - # set RAM x address count - self.command(_SSD1683_SET_RAMXCOUNT, bytearray([x & 0xFF])) - - # set RAM y address count - self.command(_SSD1683_SET_RAMYCOUNT, bytearray([y & 0xFF, (y >> 8) & 0xFF])) - - def set_ram_window(self, x1: int, y1: int, x2: int, y2: int) -> None: - """Set the RAM window for partial updates""" - # Set ram X start/end position - self.command(_SSD1683_SET_RAMXPOS, bytearray([x1 & 0xFF, x2 & 0xFF])) - - # Set ram Y start/end position - self.command( - _SSD1683_SET_RAMYPOS, - bytearray([y1 & 0xFF, (y1 >> 8) & 0xFF, y2 & 0xFF, (y2 >> 8) & 0xFF]), - ) - - def _send_command_list(self, init_sequence: bytes) -> None: - """Send a sequence of commands from an initialization list""" - i = 0 - while i < len(init_sequence): - cmd = init_sequence[i] - i += 1 - - if cmd == 0xFE: # End marker - break - elif cmd == 0xFF: # Delay marker - if i < len(init_sequence): - delay_ms = init_sequence[i] - i += 1 - time.sleep(delay_ms / 1000.0) - elif i < len(init_sequence): - num_args = init_sequence[i] - i += 1 - if num_args > 0 and (i + num_args) <= len(init_sequence): - args = init_sequence[i : i + num_args] - self.command(cmd, bytearray(args)) - i += num_args - else: - self.command(cmd) diff --git a/examples/epd_bitmap.py b/examples/epd_bitmap.py index ac91a71..87a4607 100644 --- a/examples/epd_bitmap.py +++ b/examples/epd_bitmap.py @@ -14,7 +14,6 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680 from adafruit_epd.ssd1681 import Adafruit_SSD1681 -from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D # create the spi device and pins we will need @@ -37,7 +36,6 @@ # display = Adafruit_UC8151D(128, 296, # 2.9" mono flexible display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 -# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display display = Adafruit_IL0373( 104, diff --git a/examples/epd_blinka.py b/examples/epd_blinka.py index 69a472e..c950e4d 100644 --- a/examples/epd_blinka.py +++ b/examples/epd_blinka.py @@ -17,7 +17,6 @@ from adafruit_epd.ssd1675b import Adafruit_SSD1675B from adafruit_epd.ssd1680 import Adafruit_SSD1680 from adafruit_epd.ssd1681 import Adafruit_SSD1681 -from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D # create the spi device and pins we will need @@ -36,7 +35,6 @@ # display = Adafruit_SSD1680(122, 250, # 2.13" HD Tri-color display # display = Adafruit_SSD1681(200, 200, # 1.54" HD Tri-color display # display = Adafruit_SSD1675(122, 250, # 2.13" HD mono display -# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL91874(176, 264, # 2.7" Tri-color display # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display diff --git a/examples/epd_pillow_demo.py b/examples/epd_pillow_demo.py index 88a07db..11dfb3a 100644 --- a/examples/epd_pillow_demo.py +++ b/examples/epd_pillow_demo.py @@ -20,7 +20,6 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 -from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D # First define some color constants @@ -50,7 +49,6 @@ # display = Adafruit_SSD1680(122, 250, # 2.13" HD Tri-color or mono display # display = Adafruit_SSD1680Z(122, 250, # Newer 2.13" mono display # display = Adafruit_SSD1681(200, 200, # 1.54" HD Tri-color display -# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL91874(176, 264, # 2.7" Tri-color display # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display diff --git a/examples/epd_pillow_image.py b/examples/epd_pillow_image.py index 6b16497..cf96d27 100644 --- a/examples/epd_pillow_image.py +++ b/examples/epd_pillow_image.py @@ -22,7 +22,6 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 -from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D # create the spi device and pins we will need @@ -40,7 +39,6 @@ # display = Adafruit_SSD1680(122, 250, # 2.13" HD Tri-color or mono display # display = Adafruit_SSD1680Z(122, 250, # Newer 2.13" mono display # display = Adafruit_SSD1681(200, 200, # 1.54" HD Tri-color display -# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL91874(176, 264, # 2.7" Tri-color display # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display diff --git a/examples/epd_simpletest.py b/examples/epd_simpletest.py index 51012b0..b8d311e 100644 --- a/examples/epd_simpletest.py +++ b/examples/epd_simpletest.py @@ -15,7 +15,6 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 -from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D # create the spi device and pins we will need @@ -34,7 +33,6 @@ # display = Adafruit_SSD1680(122, 250, # 2.13" HD Tri-color display # display = Adafruit_SSD1681(200, 200, # 1.54" HD Tri-color display # display = Adafruit_SSD1681(200, 200, # 1.54" HD Tri-color display -# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL91874(176, 264, # 2.7" Tri-color display # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display From 18be7bba5409baf955ff2f9d833366dd17ef731b Mon Sep 17 00:00:00 2001 From: Liz Date: Fri, 15 Aug 2025 13:19:36 -0400 Subject: [PATCH 6/7] tested 5.83", update inits in examples --- examples/epd_bitmap.py | 7 ++++++- examples/epd_blinka.py | 7 ++++++- examples/epd_pillow_demo.py | 7 ++++++- examples/epd_pillow_image.py | 7 ++++++- examples/epd_simpletest.py | 7 ++++++- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/examples/epd_bitmap.py b/examples/epd_bitmap.py index 87a4607..7f2d4aa 100644 --- a/examples/epd_bitmap.py +++ b/examples/epd_bitmap.py @@ -15,6 +15,7 @@ from adafruit_epd.ssd1680 import Adafruit_SSD1680 from adafruit_epd.ssd1681 import Adafruit_SSD1681 from adafruit_epd.uc8151d import Adafruit_UC8151D +from adafruit_epd.uc8179 import Adafruit_UC8179 # create the spi device and pins we will need spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -34,6 +35,8 @@ # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display # display = Adafruit_UC8151D(128, 296, # 2.9" mono flexible display +# display = Adafruit_UC8179(648, 480, # 5.83" mono 648x480 display +# display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display @@ -48,7 +51,9 @@ busy_pin=busy, ) -# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY uncomment these lines! +# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY OR! +# UC8179 5.83" or 7.5" displays +# uncomment these lines! # display.set_black_buffer(1, False) # display.set_color_buffer(1, False) diff --git a/examples/epd_blinka.py b/examples/epd_blinka.py index c950e4d..01b1fa0 100644 --- a/examples/epd_blinka.py +++ b/examples/epd_blinka.py @@ -18,6 +18,7 @@ from adafruit_epd.ssd1680 import Adafruit_SSD1680 from adafruit_epd.ssd1681 import Adafruit_SSD1681 from adafruit_epd.uc8151d import Adafruit_UC8151D +from adafruit_epd.uc8179 import Adafruit_UC8179 # create the spi device and pins we will need spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -39,6 +40,8 @@ # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display # display = Adafruit_UC8151D(128, 296, # 2.9" mono flexible display +# display = Adafruit_UC8179(648, 480, # 5.83" mono 648x480 display +# display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display # display = Adafruit_IL0373(104, 212, # 2.13" Tri-color display @@ -53,7 +56,9 @@ busy_pin=busy, ) -# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY uncomment these lines! +# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY OR! +# UC8179 5.83" or 7.5" displays +# uncomment these lines! # display.set_black_buffer(1, False) # display.set_color_buffer(1, False) diff --git a/examples/epd_pillow_demo.py b/examples/epd_pillow_demo.py index 11dfb3a..0749844 100644 --- a/examples/epd_pillow_demo.py +++ b/examples/epd_pillow_demo.py @@ -21,6 +21,7 @@ from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 from adafruit_epd.uc8151d import Adafruit_UC8151D +from adafruit_epd.uc8179 import Adafruit_UC8179 # First define some color constants WHITE = (0xFF, 0xFF, 0xFF) @@ -53,6 +54,8 @@ # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display # display = Adafruit_UC8151D(128, 296, # 2.9" mono flexible display +# display = Adafruit_UC8179(648, 480, # 5.83" mono 648x480 display +# display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display @@ -67,7 +70,9 @@ busy_pin=busy, ) -# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY uncomment these lines! +# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY OR! +# UC8179 5.83" or 7.5" displays +# uncomment these lines! # display.set_black_buffer(1, False) # display.set_color_buffer(1, False) diff --git a/examples/epd_pillow_image.py b/examples/epd_pillow_image.py index cf96d27..a24ce81 100644 --- a/examples/epd_pillow_image.py +++ b/examples/epd_pillow_image.py @@ -23,6 +23,7 @@ from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 from adafruit_epd.uc8151d import Adafruit_UC8151D +from adafruit_epd.uc8179 import Adafruit_UC8179 # create the spi device and pins we will need spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -43,6 +44,8 @@ # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display # display = Adafruit_UC8151D(128, 296, # 2.9" mono flexible display +# display = Adafruit_UC8179(648, 480, # 5.83" mono 648x480 display +# display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display @@ -57,7 +60,9 @@ busy_pin=busy, ) -# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY uncomment these lines! +# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY OR! +# UC8179 5.83" or 7.5" displays +# uncomment these lines! # display.set_black_buffer(1, False) # display.set_color_buffer(1, False) diff --git a/examples/epd_simpletest.py b/examples/epd_simpletest.py index b8d311e..587b4dd 100644 --- a/examples/epd_simpletest.py +++ b/examples/epd_simpletest.py @@ -16,6 +16,7 @@ from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 from adafruit_epd.uc8151d import Adafruit_UC8151D +from adafruit_epd.uc8179 import Adafruit_UC8179 # create the spi device and pins we will need spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -37,6 +38,8 @@ # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display # display = Adafruit_UC8151D(128, 296, # 2.9" mono flexible display +# display = Adafruit_UC8179(648, 480, # 5.83" mono 648x480 display +# display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display @@ -51,7 +54,9 @@ busy_pin=busy, ) -# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY uncomment these lines! +# IF YOU HAVE A 2.13" FLEXIBLE DISPLAY OR! +# UC8179 5.83" or 7.5" displays +# uncomment these lines! # display.set_black_buffer(1, False) # display.set_color_buffer(1, False) From cd138e9d5dd672efd19e0feddf4d70ea259e3948 Mon Sep 17 00:00:00 2001 From: Liz Date: Fri, 15 Aug 2025 17:10:34 -0400 Subject: [PATCH 7/7] parity with main --- adafruit_epd/ssd1680.py | 10 +- adafruit_epd/ssd1683.py | 262 +++++++++++++++++++++++++++++++++++ examples/epd_bitmap.py | 2 + examples/epd_blinka.py | 2 + examples/epd_pillow_demo.py | 2 + examples/epd_pillow_image.py | 2 + examples/epd_simpletest.py | 2 + 7 files changed, 277 insertions(+), 5 deletions(-) create mode 100644 adafruit_epd/ssd1683.py diff --git a/adafruit_epd/ssd1680.py b/adafruit_epd/ssd1680.py index 4c0bd7c..9b98446 100644 --- a/adafruit_epd/ssd1680.py +++ b/adafruit_epd/ssd1680.py @@ -242,7 +242,7 @@ def power_up(self): self.command( _SSD1680_DRIVER_CONTROL, - bytearray([self._height, (self._height) >> 8, 0x00]), + bytearray([self._height & 0xFF, (self._height) >> 8, 0x00]), ) self.command(_SSD1680_DATA_MODE, bytearray([0x03])) @@ -251,19 +251,19 @@ def power_up(self): self.command(_SSD1680_GATE_VOLTAGE, bytearray([0x17])) self.command(_SSD1680_SOURCE_VOLTAGE, bytearray([0x41, 0x00, 0x32])) - self.command(_SSD1680_SET_RAMXPOS, bytearray([0x00, (self._width // 8)])) + self.command(_SSD1680_SET_RAMXPOS, bytearray([0x00, (self._width // 8) - 1])) self.command( _SSD1680_SET_RAMYPOS, - bytearray([0x00, 0x00, self._height, (self._height) >> 8]), + bytearray([0x00, 0x00, (self._height - 1) & 0xFF, (self._height - 1) >> 8]), ) # Set border waveform self.command(_SSD1680_WRITE_BORDER, bytearray([0x05])) # Set ram X count - self.command(_SSD1680_SET_RAMXCOUNT, bytearray([0x01])) + self.command(_SSD1680_SET_RAMXCOUNT, bytearray([0x00])) # Set ram Y count - self.command(_SSD1680_SET_RAMYCOUNT, bytearray([self._height, 0])) + self.command(_SSD1680_SET_RAMYCOUNT, bytearray([0x00, 0x00])) self.busy_wait() def update(self): diff --git a/adafruit_epd/ssd1683.py b/adafruit_epd/ssd1683.py new file mode 100644 index 0000000..757b4b8 --- /dev/null +++ b/adafruit_epd/ssd1683.py @@ -0,0 +1,262 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_epd.ssd1683` - Adafruit SSD1683 - ePaper display driver +==================================================================================== +CircuitPython driver for Adafruit SSD1683 display breakouts +* Author(s): Liz Clark +""" + +import time + +import adafruit_framebuf +from micropython import const + +from adafruit_epd.epd import Adafruit_EPD + +try: + """Needed for type annotations""" + import typing + + from busio import SPI + from digitalio import DigitalInOut + from typing_extensions import Literal + +except ImportError: + pass + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_EPD.git" + +# Command constants +_SSD1683_DRIVER_CONTROL = const(0x01) +_SSD1683_GATE_VOLTAGE = const(0x03) +_SSD1683_SOURCE_VOLTAGE = const(0x04) +_SSD1683_PROGOTP_INITIAL = const(0x08) +_SSD1683_PROGREG_INITIAL = const(0x09) +_SSD1683_READREG_INITIAL = const(0x0A) +_SSD1683_BOOST_SOFTSTART = const(0x0C) +_SSD1683_DEEP_SLEEP = const(0x10) +_SSD1683_DATA_MODE = const(0x11) +_SSD1683_SW_RESET = const(0x12) +_SSD1683_HV_READY = const(0x14) +_SSD1683_VCI_DETECT = const(0x15) +_SSD1683_PROGRAM_WSOTP = const(0x16) +_SSD1683_PROGRAM_AUTO = const(0x17) +_SSD1683_TEMP_CONTROL = const(0x18) +_SSD1683_TEMP_WRITE = const(0x1A) +_SSD1683_TEMP_READ = const(0x1B) +_SSD1683_TEMP_CONTROLEXT = const(0x1C) +_SSD1683_MASTER_ACTIVATE = const(0x20) +_SSD1683_DISP_CTRL1 = const(0x21) +_SSD1683_DISP_CTRL2 = const(0x22) +_SSD1683_WRITE_RAM1 = const(0x24) +_SSD1683_WRITE_RAM2 = const(0x26) +_SSD1683_READ_RAM1 = const(0x27) +_SSD1683_SENSE_VCOM = const(0x28) +_SSD1683_SENSEDUR_VCOM = const(0x29) +_SSD1683_PROGOTP_VCOM = const(0x2A) +_SSD1683_WRITE_VCOM = const(0x2C) +_SSD1683_READ_OTP = const(0x2D) +_SSD1683_READ_USERID = const(0x2E) +_SSD1683_READ_STATUS = const(0x2F) +_SSD1683_WRITE_LUT = const(0x32) +_SSD1683_WRITE_BORDER = const(0x3C) +_SSD1683_END_OPTION = const(0x3F) +_SSD1683_SET_RAMXPOS = const(0x44) +_SSD1683_SET_RAMYPOS = const(0x45) +_SSD1683_SET_RAMXCOUNT = const(0x4E) +_SSD1683_SET_RAMYCOUNT = const(0x4F) + +# Other constants +_EPD_RAM_BW = const(0x10) +_EPD_RAM_RED = const(0x13) +_BUSY_WAIT = const(500) + + +class Adafruit_SSD1683(Adafruit_EPD): + """driver class for Adafruit SSD1683 ePaper display breakouts""" + + def __init__( + self, + width: int, + height: int, + spi: SPI, + *, + cs_pin: DigitalInOut, + dc_pin: DigitalInOut, + sramcs_pin: DigitalInOut, + rst_pin: DigitalInOut, + busy_pin: DigitalInOut, + ) -> None: + super().__init__(width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin) + + stride = width + if stride % 8 != 0: + stride += 8 - stride % 8 + + self._buffer1_size = int(stride * height / 8) + self._buffer2_size = self._buffer1_size + + if sramcs_pin: + self._buffer1 = self.sram.get_view(0) + self._buffer2 = self.sram.get_view(self._buffer1_size) + else: + self._buffer1 = bytearray(self._buffer1_size) + self._buffer2 = bytearray(self._buffer2_size) + + 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) + + # Set single byte transactions flag + self._single_byte_tx = True + + # Set the display update value + self._display_update_val = 0xF7 + + # Default initialization sequence (tri-color mode) + self._default_init_code = bytes( + [ + _SSD1683_SW_RESET, + 0, # Software reset + 0xFF, + 50, # Wait for busy (50ms delay) + _SSD1683_WRITE_BORDER, + 1, # Border waveform control + 0x05, # Border color/waveform + _SSD1683_TEMP_CONTROL, + 1, # Temperature control + 0x80, # Read temp + _SSD1683_DATA_MODE, + 1, # Data entry mode + 0x03, # Y decrement, X increment + 0xFE, # End of initialization + ] + ) + + def begin(self, reset: bool = True) -> None: + """Begin communication with the display and set basic settings""" + if reset: + self.hardware_reset() + self.power_down() + + def busy_wait(self) -> None: + """Wait for display to be done with current task, either by polling the + busy pin, or pausing""" + if self._busy: + while self._busy.value: # wait for busy low + time.sleep(0.01) + else: + time.sleep(_BUSY_WAIT / 1000.0) # Convert ms to seconds + + def power_up(self) -> None: + """Power up the display in preparation for writing RAM and updating""" + self.hardware_reset() + time.sleep(0.1) + self.busy_wait() + + # Use custom init code if provided, otherwise use default + init_code = self._default_init_code + if hasattr(self, "_epd_init_code") and self._epd_init_code is not None: + init_code = self._epd_init_code + + # Send initialization sequence + self._send_command_list(init_code) + + # Set RAM window + self.set_ram_window(0, 0, (self._width // 8) - 1, self._height - 1) + + # Set RAM address to start position + self.set_ram_address(0, 0) + + # Set LUT if we have one + if hasattr(self, "_epd_lut_code") and self._epd_lut_code: + self._send_command_list(self._epd_lut_code) + + # Set display size and driver output control + _b0 = (self._height - 1) & 0xFF + _b1 = ((self._height - 1) >> 8) & 0xFF + _b2 = 0x00 + self.command(_SSD1683_DRIVER_CONTROL, bytearray([_b0, _b1, _b2])) + + def power_down(self) -> None: + """Power down the display - required when not actively displaying!""" + # Only deep sleep if we can get out of it + if self._rst: + # deep sleep + self.command(_SSD1683_DEEP_SLEEP, bytearray([0x01])) + time.sleep(0.1) + else: + self.command(_SSD1683_SW_RESET) + self.busy_wait() + + def update(self) -> None: + """Update the display from internal memory""" + # display update sequence + self.command(_SSD1683_DISP_CTRL2, bytearray([self._display_update_val])) + self.command(_SSD1683_MASTER_ACTIVATE) + self.busy_wait() + + if not self._busy: + time.sleep(1) # wait 1 second + + def write_ram(self, index: Literal[0, 1]) -> int: + """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(_SSD1683_WRITE_RAM1, end=False) + if index == 1: + return self.command(_SSD1683_WRITE_RAM2, end=False) + raise RuntimeError("RAM index must be 0 or 1") + + def set_ram_address(self, x: int, y: int) -> None: + """Set the RAM address location""" + # set RAM x address count + self.command(_SSD1683_SET_RAMXCOUNT, bytearray([x & 0xFF])) + + # set RAM y address count + self.command(_SSD1683_SET_RAMYCOUNT, bytearray([y & 0xFF, (y >> 8) & 0xFF])) + + def set_ram_window(self, x1: int, y1: int, x2: int, y2: int) -> None: + """Set the RAM window for partial updates""" + # Set ram X start/end position + self.command(_SSD1683_SET_RAMXPOS, bytearray([x1 & 0xFF, x2 & 0xFF])) + + # Set ram Y start/end position + self.command( + _SSD1683_SET_RAMYPOS, + bytearray([y1 & 0xFF, (y1 >> 8) & 0xFF, y2 & 0xFF, (y2 >> 8) & 0xFF]), + ) + + def _send_command_list(self, init_sequence: bytes) -> None: + """Send a sequence of commands from an initialization list""" + i = 0 + while i < len(init_sequence): + cmd = init_sequence[i] + i += 1 + + if cmd == 0xFE: # End marker + break + elif cmd == 0xFF: # Delay marker + if i < len(init_sequence): + delay_ms = init_sequence[i] + i += 1 + time.sleep(delay_ms / 1000.0) + elif i < len(init_sequence): + num_args = init_sequence[i] + i += 1 + if num_args > 0 and (i + num_args) <= len(init_sequence): + args = init_sequence[i : i + num_args] + self.command(cmd, bytearray(args)) + i += num_args + else: + self.command(cmd) diff --git a/examples/epd_bitmap.py b/examples/epd_bitmap.py index 7f2d4aa..3e18441 100644 --- a/examples/epd_bitmap.py +++ b/examples/epd_bitmap.py @@ -14,6 +14,7 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680 from adafruit_epd.ssd1681 import Adafruit_SSD1681 +from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D from adafruit_epd.uc8179 import Adafruit_UC8179 @@ -39,6 +40,7 @@ # display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 +# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display display = Adafruit_IL0373( 104, diff --git a/examples/epd_blinka.py b/examples/epd_blinka.py index 01b1fa0..1dd366c 100644 --- a/examples/epd_blinka.py +++ b/examples/epd_blinka.py @@ -17,6 +17,7 @@ from adafruit_epd.ssd1675b import Adafruit_SSD1675B from adafruit_epd.ssd1680 import Adafruit_SSD1680 from adafruit_epd.ssd1681 import Adafruit_SSD1681 +from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D from adafruit_epd.uc8179 import Adafruit_UC8179 @@ -36,6 +37,7 @@ # display = Adafruit_SSD1680(122, 250, # 2.13" HD Tri-color display # display = Adafruit_SSD1681(200, 200, # 1.54" HD Tri-color display # display = Adafruit_SSD1675(122, 250, # 2.13" HD mono display +# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL91874(176, 264, # 2.7" Tri-color display # display = Adafruit_EK79686(176, 264, # 2.7" Tri-color display # display = Adafruit_IL0373(152, 152, # 1.54" Tri-color display diff --git a/examples/epd_pillow_demo.py b/examples/epd_pillow_demo.py index 0749844..2579322 100644 --- a/examples/epd_pillow_demo.py +++ b/examples/epd_pillow_demo.py @@ -20,6 +20,7 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 +from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D from adafruit_epd.uc8179 import Adafruit_UC8179 @@ -58,6 +59,7 @@ # display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 +# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display display = Adafruit_IL0373( 104, diff --git a/examples/epd_pillow_image.py b/examples/epd_pillow_image.py index a24ce81..e7c75dd 100644 --- a/examples/epd_pillow_image.py +++ b/examples/epd_pillow_image.py @@ -22,6 +22,7 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 +from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D from adafruit_epd.uc8179 import Adafruit_UC8179 @@ -48,6 +49,7 @@ # display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 +# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display display = Adafruit_IL0373( 104, diff --git a/examples/epd_simpletest.py b/examples/epd_simpletest.py index 587b4dd..bef35dd 100644 --- a/examples/epd_simpletest.py +++ b/examples/epd_simpletest.py @@ -15,6 +15,7 @@ from adafruit_epd.ssd1675 import Adafruit_SSD1675 from adafruit_epd.ssd1680 import Adafruit_SSD1680, Adafruit_SSD1680Z from adafruit_epd.ssd1681 import Adafruit_SSD1681 +from adafruit_epd.ssd1683 import Adafruit_SSD1683 from adafruit_epd.uc8151d import Adafruit_UC8151D from adafruit_epd.uc8179 import Adafruit_UC8179 @@ -42,6 +43,7 @@ # display = Adafruit_UC8179(800, 480, # 7.5" mono 800x480 display # display = Adafruit_IL0373(128, 296, # 2.9" Tri-color display IL0373 # display = Adafruit_SSD1680(128, 296, # 2.9" Tri-color display SSD1680 +# display = Adafruit_SSD1683(400, 300, # 4.2" 300x400 Tri-Color display # display = Adafruit_IL0398(400, 300, # 4.2" Tri-color display display = Adafruit_IL0373( 104,