From 62e86d474570d15a793a0dbd86627fc43d763ce5 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Tue, 19 Mar 2024 15:00:24 -0700 Subject: [PATCH 1/2] Update driver to CircuitPython 9.0.0 --- adafruit_displayio_ssd1306.new.py | 140 ++++++++++++++++++++++++++++++ adafruit_displayio_ssd1306.py | 9 +- 2 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 adafruit_displayio_ssd1306.new.py diff --git a/adafruit_displayio_ssd1306.new.py b/adafruit_displayio_ssd1306.new.py new file mode 100644 index 0000000..a0757d6 --- /dev/null +++ b/adafruit_displayio_ssd1306.new.py @@ -0,0 +1,140 @@ +# SPDX-FileCopyrightText: 2019 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_displayio_ssd1306` +================================================================================ + +DisplayIO driver for SSD1306 monochrome displays + + +* Author(s): Scott Shawcroft + +Implementation Notes +-------------------- + +**Hardware:** + +* `Monochrome 1.3" 128x64 OLED graphic display `_ +* `Monochrome 128x32 I2C OLED graphic display `_ +* `Monochrome 0.96" 128x64 OLED graphic display `_ +* `Monochrome 128x32 SPI OLED graphic display `_ +* `Adafruit FeatherWing OLED - 128x32 OLED `_ +* Monochrome 0.49" 64x32 I2C OLED graphic display +* Monochrome 0.66" 64x48 I2C OLED graphic display (eg https://www.amazon.com/gp/product/B07QF7QK6P) +* Might work on other sub-128 width display: Dots 72x40, 64x48, 96x16 + +**Software and Dependencies:** + +* Adafruit CircuitPython (version 5+) firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +import busdisplay +try: + from typing import Union + import fourwire + import i2cdisplaybus +except ImportError: + pass + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306.git" + +# Sequence from page 19 here: https://cdn-shop.adafruit.com/datasheets/UG-2864HSWEG01+user+guide.pdf +_INIT_SEQUENCE = ( + b"\xAE\x00" # DISPLAY_OFF + b"\x20\x01\x00" # Set memory addressing to horizontal mode. + b"\x81\x01\xcf" # set contrast control + b"\xA1\x00" # Column 127 is segment 0 + b"\xA6\x00" # Normal display + b"\xc8\x00" # Normal display + b"\xA8\x01\x3f" # Mux ratio is 1/64 + b"\xd5\x01\x80" # Set divide ratio + b"\xd9\x01\xf1" # Set pre-charge period + b"\xda\x01\x12" # Set com configuration + b"\xdb\x01\x40" # Set vcom configuration + b"\x8d\x01\x14" # Enable charge pump + b"\xAF\x00" # DISPLAY_ON +) + + +class SSD1306(busdisplay.BusDisplay): + """ + SSD1306 driver + + :param int width: The width of the display + :param int height: The height of the display + :param int rotation: The rotation of the display in degrees. Default is 0. Must be one of + (0, 90, 180, 270) + """ + + def __init__( + self, bus: Union[fourwire.FourWire, i2cdisplaybus.I2CDisplayBus], **kwargs + ) -> None: + # Patch the init sequence for 32 pixel high displays. + init_sequence = bytearray(_INIT_SEQUENCE) + height = kwargs["height"] + width = kwargs["width"] + if "rotation" in kwargs and kwargs["rotation"] % 180 != 0: + height = kwargs["width"] + width = kwargs["height"] + init_sequence[16] = height - 1 # patch mux ratio + if height == 32 and width == 64: # Make sure this only apply to that resolution + init_sequence[16] = 64 - 1 # FORCED for 64x32 because it fail with formula + if height in (32, 16) and width != 64: + init_sequence[25] = 0x02 # patch com configuration + col_offset = ( + 0 if width == 128 else (128 - width) // 2 + ) # https://github.com/micropython/micropython/pull/7411 + row_offset = ( + col_offset if (kwargs["height"] != 48 or kwargs["width"] != 64) else 0 + ) # fix for 0.66" 64x48 OLED + super().__init__( + bus, + init_sequence, + **kwargs, + colstart=col_offset, + rowstart=row_offset, + color_depth=1, + grayscale=True, + pixels_in_byte_share_row=False, + set_column_command=0x21, + set_row_command=0x22, + data_as_commands=True, + brightness_command=0x81, + single_byte_bounds=True, + ) + self._is_awake = True # Display starts in active state (_INIT_SEQUENCE) + + @property + def is_awake(self) -> bool: + """ + The power state of the display. (read-only) + + `True` if the display is active, `False` if in sleep mode. + + :type: bool + """ + return self._is_awake + + def sleep(self) -> None: + """ + Put display into sleep mode. + + Display uses < 10uA in sleep mode. Display remembers display data and operation mode + active prior to sleeping. MP can access (update) the built-in display RAM. + """ + if self._is_awake: + self.bus.send(0xAE, b"") # 0xAE = display off, sleep mode + self._is_awake = False + + def wake(self) -> None: + """ + Wake display from sleep mode + """ + if not self._is_awake: + self.bus.send(0xAF, b"") # 0xAF = display on + self._is_awake = True diff --git a/adafruit_displayio_ssd1306.py b/adafruit_displayio_ssd1306.py index 13edd14..a0757d6 100644 --- a/adafruit_displayio_ssd1306.py +++ b/adafruit_displayio_ssd1306.py @@ -32,10 +32,11 @@ """ -import displayio - +import busdisplay try: from typing import Union + import fourwire + import i2cdisplaybus except ImportError: pass @@ -60,7 +61,7 @@ ) -class SSD1306(displayio.Display): +class SSD1306(busdisplay.BusDisplay): """ SSD1306 driver @@ -71,7 +72,7 @@ class SSD1306(displayio.Display): """ def __init__( - self, bus: Union[displayio.FourWire, displayio.I2CDisplay], **kwargs + self, bus: Union[fourwire.FourWire, i2cdisplaybus.I2CDisplayBus], **kwargs ) -> None: # Patch the init sequence for 32 pixel high displays. init_sequence = bytearray(_INIT_SEQUENCE) From 1c1d0862050907714a6ad3806313478b29c18a4a Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Tue, 19 Mar 2024 15:09:12 -0700 Subject: [PATCH 2/2] Update to still work on older displayio --- adafruit_displayio_ssd1306.new.py | 140 ------------------------------ adafruit_displayio_ssd1306.py | 17 ++-- 2 files changed, 8 insertions(+), 149 deletions(-) delete mode 100644 adafruit_displayio_ssd1306.new.py diff --git a/adafruit_displayio_ssd1306.new.py b/adafruit_displayio_ssd1306.new.py deleted file mode 100644 index a0757d6..0000000 --- a/adafruit_displayio_ssd1306.new.py +++ /dev/null @@ -1,140 +0,0 @@ -# SPDX-FileCopyrightText: 2019 Scott Shawcroft for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -""" -`adafruit_displayio_ssd1306` -================================================================================ - -DisplayIO driver for SSD1306 monochrome displays - - -* Author(s): Scott Shawcroft - -Implementation Notes --------------------- - -**Hardware:** - -* `Monochrome 1.3" 128x64 OLED graphic display `_ -* `Monochrome 128x32 I2C OLED graphic display `_ -* `Monochrome 0.96" 128x64 OLED graphic display `_ -* `Monochrome 128x32 SPI OLED graphic display `_ -* `Adafruit FeatherWing OLED - 128x32 OLED `_ -* Monochrome 0.49" 64x32 I2C OLED graphic display -* Monochrome 0.66" 64x48 I2C OLED graphic display (eg https://www.amazon.com/gp/product/B07QF7QK6P) -* Might work on other sub-128 width display: Dots 72x40, 64x48, 96x16 - -**Software and Dependencies:** - -* Adafruit CircuitPython (version 5+) firmware for the supported boards: - https://github.com/adafruit/circuitpython/releases - -""" - -import busdisplay -try: - from typing import Union - import fourwire - import i2cdisplaybus -except ImportError: - pass - -__version__ = "0.0.0+auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306.git" - -# Sequence from page 19 here: https://cdn-shop.adafruit.com/datasheets/UG-2864HSWEG01+user+guide.pdf -_INIT_SEQUENCE = ( - b"\xAE\x00" # DISPLAY_OFF - b"\x20\x01\x00" # Set memory addressing to horizontal mode. - b"\x81\x01\xcf" # set contrast control - b"\xA1\x00" # Column 127 is segment 0 - b"\xA6\x00" # Normal display - b"\xc8\x00" # Normal display - b"\xA8\x01\x3f" # Mux ratio is 1/64 - b"\xd5\x01\x80" # Set divide ratio - b"\xd9\x01\xf1" # Set pre-charge period - b"\xda\x01\x12" # Set com configuration - b"\xdb\x01\x40" # Set vcom configuration - b"\x8d\x01\x14" # Enable charge pump - b"\xAF\x00" # DISPLAY_ON -) - - -class SSD1306(busdisplay.BusDisplay): - """ - SSD1306 driver - - :param int width: The width of the display - :param int height: The height of the display - :param int rotation: The rotation of the display in degrees. Default is 0. Must be one of - (0, 90, 180, 270) - """ - - def __init__( - self, bus: Union[fourwire.FourWire, i2cdisplaybus.I2CDisplayBus], **kwargs - ) -> None: - # Patch the init sequence for 32 pixel high displays. - init_sequence = bytearray(_INIT_SEQUENCE) - height = kwargs["height"] - width = kwargs["width"] - if "rotation" in kwargs and kwargs["rotation"] % 180 != 0: - height = kwargs["width"] - width = kwargs["height"] - init_sequence[16] = height - 1 # patch mux ratio - if height == 32 and width == 64: # Make sure this only apply to that resolution - init_sequence[16] = 64 - 1 # FORCED for 64x32 because it fail with formula - if height in (32, 16) and width != 64: - init_sequence[25] = 0x02 # patch com configuration - col_offset = ( - 0 if width == 128 else (128 - width) // 2 - ) # https://github.com/micropython/micropython/pull/7411 - row_offset = ( - col_offset if (kwargs["height"] != 48 or kwargs["width"] != 64) else 0 - ) # fix for 0.66" 64x48 OLED - super().__init__( - bus, - init_sequence, - **kwargs, - colstart=col_offset, - rowstart=row_offset, - color_depth=1, - grayscale=True, - pixels_in_byte_share_row=False, - set_column_command=0x21, - set_row_command=0x22, - data_as_commands=True, - brightness_command=0x81, - single_byte_bounds=True, - ) - self._is_awake = True # Display starts in active state (_INIT_SEQUENCE) - - @property - def is_awake(self) -> bool: - """ - The power state of the display. (read-only) - - `True` if the display is active, `False` if in sleep mode. - - :type: bool - """ - return self._is_awake - - def sleep(self) -> None: - """ - Put display into sleep mode. - - Display uses < 10uA in sleep mode. Display remembers display data and operation mode - active prior to sleeping. MP can access (update) the built-in display RAM. - """ - if self._is_awake: - self.bus.send(0xAE, b"") # 0xAE = display off, sleep mode - self._is_awake = False - - def wake(self) -> None: - """ - Wake display from sleep mode - """ - if not self._is_awake: - self.bus.send(0xAF, b"") # 0xAF = display on - self._is_awake = True diff --git a/adafruit_displayio_ssd1306.py b/adafruit_displayio_ssd1306.py index a0757d6..3e67cfe 100644 --- a/adafruit_displayio_ssd1306.py +++ b/adafruit_displayio_ssd1306.py @@ -31,14 +31,15 @@ https://github.com/adafruit/circuitpython/releases """ - -import busdisplay try: from typing import Union - import fourwire - import i2cdisplaybus + from busdisplay import BusDisplay + from fourwire import FourWire + from i2cdisplaybus import I2CDisplayBus except ImportError: - pass + from displayio import FourWire + from displayio import I2CDisplay as I2CDisplayBus + from displayio import Display as BusDisplay __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306.git" @@ -61,7 +62,7 @@ ) -class SSD1306(busdisplay.BusDisplay): +class SSD1306(BusDisplay): """ SSD1306 driver @@ -71,9 +72,7 @@ class SSD1306(busdisplay.BusDisplay): (0, 90, 180, 270) """ - def __init__( - self, bus: Union[fourwire.FourWire, i2cdisplaybus.I2CDisplayBus], **kwargs - ) -> None: + def __init__(self, bus: Union[FourWire, I2CDisplayBus], **kwargs) -> None: # Patch the init sequence for 32 pixel high displays. init_sequence = bytearray(_INIT_SEQUENCE) height = kwargs["height"]