From 5addeea21db64285a19902913dde42a98546625c Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 27 Sep 2023 14:07:29 -0700 Subject: [PATCH] Update begin_transaction --- displayio/_constants.py | 2 + displayio/_display.py | 107 ++++++++++++++++++++------------------ displayio/_displaycore.py | 4 +- displayio/_fourwire.py | 9 ++-- displayio/_i2cdisplay.py | 21 ++++++-- paralleldisplay.py | 24 +++++++-- 6 files changed, 102 insertions(+), 65 deletions(-) diff --git a/displayio/_constants.py b/displayio/_constants.py index d255dbf..19ecd0f 100644 --- a/displayio/_constants.py +++ b/displayio/_constants.py @@ -21,3 +21,5 @@ BACKLIGHT_PWM = 2 NO_COMMAND = 0x100 CIRCUITPY_DISPLAY_LIMIT = 1 + +DELAY = 0x80 diff --git a/displayio/_display.py b/displayio/_display.py index 3f6d7b6..7bfe532 100644 --- a/displayio/_display.py +++ b/displayio/_display.py @@ -36,6 +36,7 @@ from ._constants import ( BACKLIGHT_IN_OUT, BACKLIGHT_PWM, NO_COMMAND, + DELAY, ) __version__ = "0.0.0+auto.0" @@ -43,7 +44,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git" 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. @@ -155,7 +156,42 @@ class Display: 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 @@ -191,38 +227,6 @@ class Display: 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( @@ -455,23 +459,24 @@ class Display: 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, 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() + 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") diff --git a/displayio/_displaycore.py b/displayio/_displaycore.py index 42218ec..57e8dd6 100644 --- a/displayio/_displaycore.py +++ b/displayio/_displaycore.py @@ -397,11 +397,11 @@ class _DisplayCore: """ self._send(data_type, chip_select, data) - def begin_transaction(self) -> None: + def begin_transaction(self) -> bool: """ Begin Bus Transaction """ - self._begin_transaction() + return self._begin_transaction() def end_transaction(self) -> None: """ diff --git a/displayio/_fourwire.py b/displayio/_fourwire.py index 142e308..c343e65 100644 --- a/displayio/_fourwire.py +++ b/displayio/_fourwire.py @@ -132,16 +132,17 @@ class FourWire: else: self._spi.write(data) - def _begin_transaction(self): + def _begin_transaction(self) -> bool: """Begin the SPI transaction by locking, configuring, and setting Chip Select""" - while not self._spi.try_lock(): - pass + if not self._spi.try_lock(): + return False self._spi.configure( baudrate=self._frequency, polarity=self._polarity, phase=self._phase ) self._chip_select.value = False + return True - def _end_transaction(self): + def _end_transaction(self) -> None: """End the SPI transaction by unlocking and setting Chip Select""" self._chip_select.value = True self._spi.unlock() diff --git a/displayio/_i2cdisplay.py b/displayio/_i2cdisplay.py index e37eb8a..7ec9467 100644 --- a/displayio/_i2cdisplay.py +++ b/displayio/_i2cdisplay.py @@ -80,10 +80,9 @@ class I2CDisplay: time.sleep(0.0001) self._reset.value = True - def _begin_transaction(self) -> None: + def _begin_transaction(self) -> bool: """Lock the bus before sending data.""" - while not self._i2c.try_lock(): - pass + return self._i2c.try_lock() def send(self, command: int, data: ReadableBuffer) -> None: """ @@ -109,12 +108,26 @@ class I2CDisplay: command_bytes[2 * i] = 0x80 command_bytes[2 * i + 1] = data[i] + try: self._i2c.writeto(self._dev_addr, buffer=command_bytes) + except OSError as error: + if error.errno == 121: + raise RuntimeError( + f"I2C write error to 0x{self._dev_addr:02x}" + ) from error + raise error else: data_bytes = bytearray(len(data) + 1) data_bytes[0] = 0x40 data_bytes[1:] = data - self._i2c.writeto(self._dev_addr, buffer=data_bytes) + try: + self._i2c.writeto(self._dev_addr, buffer=data_bytes) + except OSError as error: + if error.errno == 121: + raise RuntimeError( + f"I2C write error to 0x{self._dev_addr:02x}" + ) from error + raise error def _end_transaction(self) -> None: """Release the bus after sending data.""" diff --git a/paralleldisplay.py b/paralleldisplay.py index 59fb08c..8cfa87e 100644 --- a/paralleldisplay.py +++ b/paralleldisplay.py @@ -17,8 +17,9 @@ paralleldisplay for Blinka """ +from typing import Optional import microcontroller -import circuitpython_typing +from circuitpython_typing import ReadableBuffer __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git" @@ -37,8 +38,8 @@ class ParallelBus: command: microcontroller.Pin, chip_select: microcontroller.Pin, write: microcontroller.Pin, - read: microcontroller.Pin, - reset: microcontroller.Pin, + read: Optional[microcontroller.Pin], + reset: Optional[microcontroller.Pin] = None, frequency: int = 30000000, ): # pylint: disable=unnecessary-pass @@ -61,9 +62,24 @@ class ParallelBus: """ raise NotImplementedError("ParallelBus reset has not been implemented yet") - def send(self, command: int, data: circuitpython_typing.ReadableBuffer) -> None: + def send(self, command: int, data: ReadableBuffer) -> None: """Sends the given command value followed by the full set of data. Display state, such as vertical scroll, set via ``send`` may or may not be reset once the code is done. """ raise NotImplementedError("ParallelBus send has not been implemented yet") + + def _send( + self, + _data_type: int, + _chip_select: int, + _data: ReadableBuffer, + ) -> None: + pass + + def _begin_transaction(self) -> bool: + # pylint: disable=no-self-use + return True + + def _end_transaction(self) -> None: + pass -- 2.49.0