]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blobdiff - displayio/_display.py
bug fixes
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / _display.py
index d7d04cf2389dcf5ba1c3badbb42cd754c6851e1b..9572a8132462e0c10f8987f11a43f31618dbcf6f 100644 (file)
@@ -36,6 +36,7 @@ from ._constants import (
     BACKLIGHT_IN_OUT,
     BACKLIGHT_PWM,
     NO_COMMAND,
     BACKLIGHT_IN_OUT,
     BACKLIGHT_PWM,
     NO_COMMAND,
+    DELAY,
 )
 
 __version__ = "0.0.0+auto.0"
 )
 
 __version__ = "0.0.0+auto.0"
@@ -43,7 +44,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
 
 class Display:
 
 
 class Display:
-    # pylint: disable=too-many-instance-attributes
+    # pylint: disable=too-many-instance-attributes, too-many-statements
     """This initializes a display and connects it into CircuitPython. Unlike other objects
     in CircuitPython, Display objects live until ``displayio.release_displays()`` is called.
     This is done so that CircuitPython can use the display itself.
     """This initializes a display and connects it into CircuitPython. Unlike other objects
     in CircuitPython, Display objects live until ``displayio.release_displays()`` is called.
     This is done so that CircuitPython can use the display itself.
@@ -81,7 +82,7 @@ class Display:
         backlight_on_high: bool = True,
         SH1107_addressing: bool = False,
     ):
         backlight_on_high: bool = True,
         SH1107_addressing: bool = False,
     ):
-        # pylint: disable=too-many-locals,invalid-name
+        # pylint: disable=too-many-locals,invalid-name, too-many-branches
         """Create a Display object on the given display bus (`displayio.FourWire` or
         `paralleldisplay.ParallelBus`).
 
         """Create a Display object on the given display bus (`displayio.FourWire` or
         `paralleldisplay.ParallelBus`).
 
@@ -111,6 +112,13 @@ class Display:
         The initialization sequence should always leave the display memory access inline with
         the scan of the display to minimize tearing artifacts.
         """
         The initialization sequence should always leave the display memory access inline with
         the scan of the display to minimize tearing artifacts.
         """
+
+        if rotation % 90 != 0:
+            raise ValueError("Display rotation must be in 90 degree increments")
+
+        if SH1107_addressing and color_depth != 1:
+            raise ValueError("color_depth must be 1 when SH1107_addressing is True")
+
         # Turn off auto-refresh as we init
         self._auto_refresh = False
         ram_width = 0x100
         # Turn off auto-refresh as we init
         self._auto_refresh = False
         ram_width = 0x100
@@ -155,7 +163,42 @@ class Display:
         self._brightness = brightness
         self._auto_refresh = auto_refresh
 
         self._brightness = brightness
         self._auto_refresh = auto_refresh
 
-        self._initialize(init_sequence)
+        i = 0
+        while i < len(init_sequence):
+            command = init_sequence[i]
+            data_size = init_sequence[i + 1]
+            delay = (data_size & DELAY) != 0
+            data_size &= ~DELAY
+            while self._core.begin_transaction():
+                pass
+
+            if self._core.data_as_commands:
+                full_command = bytearray(data_size + 1)
+                full_command[0] = command
+                full_command[1:] = init_sequence[i + 2 : i + 2 + data_size]
+                self._core.send(
+                    DISPLAY_COMMAND,
+                    CHIP_SELECT_TOGGLE_EVERY_BYTE,
+                    full_command,
+                )
+            else:
+                self._core.send(
+                    DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, bytes([command])
+                )
+                self._core.send(
+                    DISPLAY_DATA,
+                    CHIP_SELECT_UNTOUCHED,
+                    init_sequence[i + 2 : i + 2 + data_size],
+                )
+            self._core.end_transaction()
+            delay_time_ms = 10
+            if delay:
+                data_size += 1
+                delay_time_ms = init_sequence[i + 1 + data_size]
+                if delay_time_ms == 255:
+                    delay_time_ms = 500
+            time.sleep(delay_time_ms / 1000)
+            i += 2 + data_size
 
         self._current_group = None
         self._last_refresh_call = 0
 
         self._current_group = None
         self._last_refresh_call = 0
@@ -191,38 +234,6 @@ class Display:
         allocate_display(display_instance)
         return display_instance
 
         allocate_display(display_instance)
         return display_instance
 
-    def _initialize(self, init_sequence):
-        i = 0
-        while i < len(init_sequence):
-            command = init_sequence[i]
-            data_size = init_sequence[i + 1]
-            delay = (data_size & 0x80) > 0
-            data_size &= ~0x80
-
-            if self._core.data_as_commands:
-                self._core.send(
-                    DISPLAY_COMMAND,
-                    CHIP_SELECT_TOGGLE_EVERY_BYTE,
-                    bytes([command]) + init_sequence[i + 2 : i + 2 + data_size],
-                )
-            else:
-                self._core.send(
-                    DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, bytes([command])
-                )
-                self._core.send(
-                    DISPLAY_DATA,
-                    CHIP_SELECT_UNTOUCHED,
-                    init_sequence[i + 2 : i + 2 + data_size],
-                )
-            delay_time_ms = 10
-            if delay:
-                data_size += 1
-                delay_time_ms = init_sequence[i + 1 + data_size]
-                if delay_time_ms == 255:
-                    delay_time_ms = 500
-            time.sleep(delay_time_ms / 1000)
-            i += 2 + data_size
-
     def _send_pixels(self, pixels):
         if not self._core.data_as_commands:
             self._core.send(
     def _send_pixels(self, pixels):
         if not self._core.data_as_commands:
             self._core.send(
@@ -316,7 +327,7 @@ class Display:
             )
         return areas
 
             )
         return areas
 
-    def background(self):
+    def _background(self):
         """Run background refresh tasks. Do not call directly"""
         if (
             self._auto_refresh
         """Run background refresh tasks. Do not call directly"""
         if (
             self._auto_refresh
@@ -327,8 +338,7 @@ class Display:
 
     def _refresh_area(self, area) -> bool:
         """Loop through dirty areas and redraw that area."""
 
     def _refresh_area(self, area) -> bool:
         """Loop through dirty areas and redraw that area."""
-        # pylint: disable=too-many-locals
-        buffer_size = 128
+        # pylint: disable=too-many-locals, too-many-branches
 
         clipped = Area()
         # Clip the area to the display by overlapping the areas.
 
         clipped = Area()
         # Clip the area to the display by overlapping the areas.
@@ -340,6 +350,9 @@ class Display:
         pixels_per_word = 32 // self._core.colorspace.depth
         pixels_per_buffer = clipped.size()
 
         pixels_per_word = 32 // self._core.colorspace.depth
         pixels_per_buffer = clipped.size()
 
+        # We should have lots of memory
+        buffer_size = clipped.size() // pixels_per_word
+
         subrectangles = 1
         # for SH1107 and other boundary constrained controllers
         #      write one single row at a time
         subrectangles = 1
         # for SH1107 and other boundary constrained controllers
         #      write one single row at a time
@@ -388,9 +401,14 @@ class Display:
                     8 // self._core.colorspace.depth
                 )
 
                     8 // self._core.colorspace.depth
                 )
 
-            buffer = memoryview(bytearray([0] * (buffer_size * 4)))
-            mask = memoryview(bytearray([0] * mask_length))
+            buffer = memoryview(bytearray([0] * (buffer_size * 4))).cast("I")
+            mask = memoryview(bytearray([0] * (mask_length * 4))).cast("I")
             self._core.fill_area(subrectangle, mask, buffer)
             self._core.fill_area(subrectangle, mask, buffer)
+
+            # Can't acquire display bus; skip the rest of the data.
+            if not self._core.bus_free():
+                return False
+
             self._core.begin_transaction()
             self._send_pixels(buffer[:subrectangle_size_bytes])
             self._core.end_transaction()
             self._core.begin_transaction()
             self._send_pixels(buffer[:subrectangle_size_bytes])
             self._core.end_transaction()
@@ -414,12 +432,12 @@ class Display:
         self._core.fill_area(area, mask, buffer)
         return buffer
 
         self._core.fill_area(area, mask, buffer)
         return buffer
 
-    def release(self) -> None:
+    def _release(self) -> None:
         """Release the display and free its resources"""
         self.auto_refresh = False
         self._core.release_display_core()
 
         """Release the display and free its resources"""
         self.auto_refresh = False
         self._core.release_display_core()
 
-    def reset(self) -> None:
+    def _reset(self) -> None:
         """Reset the display"""
         self.auto_refresh = True
         circuitpython_splash.x = 0
         """Reset the display"""
         self.auto_refresh = True
         circuitpython_splash.x = 0
@@ -453,23 +471,24 @@ class Display:
             elif self._backlight_type == BACKLIGHT_IN_OUT:
                 self._backlight.value = value > 0.99
             elif self._brightness_command is not None:
             elif self._backlight_type == BACKLIGHT_IN_OUT:
                 self._backlight.value = value > 0.99
             elif self._brightness_command is not None:
-                self._core.begin_transaction()
-                if self._core.data_as_commands:
-                    self._core.send(
-                        DISPLAY_COMMAND,
-                        CHIP_SELECT_TOGGLE_EVERY_BYTE,
-                        bytes([self._brightness_command, 0xFF * value]),
-                    )
-                else:
-                    self._core.send(
-                        DISPLAY_COMMAND,
-                        CHIP_SELECT_TOGGLE_EVERY_BYTE,
-                        bytes([self._brightness_command]),
-                    )
-                    self._core.send(
-                        DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, round(value * 255)
-                    )
-                self._core.end_transaction()
+                okay = self._core.begin_transaction()
+                if okay:
+                    if self._core.data_as_commands:
+                        self._core.send(
+                            DISPLAY_COMMAND,
+                            CHIP_SELECT_TOGGLE_EVERY_BYTE,
+                            bytes([self._brightness_command, round(0xFF * value)]),
+                        )
+                    else:
+                        self._core.send(
+                            DISPLAY_COMMAND,
+                            CHIP_SELECT_TOGGLE_EVERY_BYTE,
+                            bytes([self._brightness_command]),
+                        )
+                        self._core.send(
+                            DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, round(value * 255)
+                        )
+                    self._core.end_transaction()
             self._brightness = value
         else:
             raise ValueError("Brightness must be between 0.0 and 1.0")
             self._brightness = value
         else:
             raise ValueError("Brightness must be between 0.0 and 1.0")
@@ -491,6 +510,8 @@ class Display:
 
     @rotation.setter
     def rotation(self, value: int):
 
     @rotation.setter
     def rotation(self, value: int):
+        if value % 90 != 0:
+            raise ValueError("Display rotation must be in 90 degree increments")
         self._core.set_rotation(value)
 
     @property
         self._core.set_rotation(value)
 
     @property