X-Git-Url: https://git.ayoreis.com/hackapet/Adafruit_Blinka_Displayio.git/blobdiff_plain/09889f5e46420240ade4451f7af6f54814f8cfcd..37b04a7ad611fe0578dc3a18248a085df7abe5e1:/fourwire/__init__.py diff --git a/fourwire/__init__.py b/fourwire/__init__.py new file mode 100644 index 0000000..5e136be --- /dev/null +++ b/fourwire/__init__.py @@ -0,0 +1,173 @@ +# SPDX-FileCopyrightText: 2020 Melissa LeBlanc-Williams for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`displayio.fourwire` +================================================================================ + +displayio for Blinka + +**Software and Dependencies:** + +* Adafruit Blinka: + https://github.com/adafruit/Adafruit_Blinka/releases + +* Author(s): Melissa LeBlanc-Williams + +""" + +import time +from typing import Optional +import digitalio +import busio +import microcontroller +from circuitpython_typing import ReadableBuffer +from displayio._constants import ( + CHIP_SELECT_TOGGLE_EVERY_BYTE, + CHIP_SELECT_UNTOUCHED, + DISPLAY_COMMAND, + DISPLAY_DATA, +) + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git" + + +class FourWire: + """Manage updating a display over SPI four wire protocol in the background while + Python code runs. It doesn’t handle display initialization. + """ + + def __init__( + self, + spi_bus: busio.SPI, + *, + command: microcontroller.Pin, + chip_select: Optional[microcontroller.Pin] = None, + reset: Optional[microcontroller.Pin] = None, + baudrate: int = 24000000, + polarity: int = 0, + phase: int = 0, + ): + """Create a FourWire object associated with the given pins. + + The SPI bus and pins are then in use by the display until + displayio.release_displays() is called even after a reload. (It does this so + CircuitPython can use the display after your code is done.) + So, the first time you initialize a display bus in code.py you should call + :py:func`displayio.release_displays` first, otherwise it will error after the + first code.py run. + """ + self._dc = digitalio.DigitalInOut(command) + self._dc.switch_to_output(value=False) + + if chip_select is not None: + self._chip_select = digitalio.DigitalInOut(chip_select) + self._chip_select.switch_to_output(value=True) + else: + self._chip_select = None + + self._frequency = baudrate + self._polarity = polarity + self._phase = phase + + if reset is not None: + self._reset = digitalio.DigitalInOut(reset) + self._reset.switch_to_output(value=True) + self.reset() + else: + self._reset = None + self._spi = spi_bus + + def _release(self): + self.reset() + self._spi.deinit() + self._dc.deinit() + + if self._chip_select is not None: + self._chip_select.deinit() + + if self._reset is not None: + self._reset.deinit() + + def reset(self) -> None: + """Performs a hardware reset via the reset pin. + Raises an exception if called when no reset pin is available. + """ + if self._reset is not None: + self._reset.value = False + time.sleep(0.001) + self._reset.value = True + time.sleep(0.001) + + def send( + self, + command, + data: ReadableBuffer, + *, + toggle_every_byte: bool = False, + ) -> 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. + """ + if not 0 <= command <= 255: + raise ValueError("Command must be an int between 0 and 255") + chip_select = ( + CHIP_SELECT_TOGGLE_EVERY_BYTE + if toggle_every_byte + else CHIP_SELECT_UNTOUCHED + ) + self._begin_transaction() + self._send(DISPLAY_COMMAND, chip_select, bytes([command])) + self._send(DISPLAY_DATA, chip_select, data) + self._end_transaction() + + def _send( + self, + data_type: int, + chip_select: int, + data: ReadableBuffer, + ): + self._dc.value = data_type == DISPLAY_DATA + + if ( + self._chip_select is not None + and chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE + ): + for byte in data: + self._spi.write(bytes([byte])) + self._chip_select.value = True + time.sleep(0.000001) + self._chip_select.value = False + else: + self._spi.write(data) + + def _free(self) -> bool: + """Attempt to free the bus and return False if busy""" + if not self._spi.try_lock(): + return False + self._spi.unlock() + return True + + def _begin_transaction(self) -> bool: + """Begin the SPI transaction by locking, configuring, and setting Chip Select""" + if not self._spi.try_lock(): + return False + self._spi.configure( + baudrate=self._frequency, polarity=self._polarity, phase=self._phase + ) + + if self._chip_select is not None: + self._chip_select.value = False + + return True + + def _end_transaction(self) -> None: + """End the SPI transaction by unlocking and setting Chip Select""" + if self._chip_select is not None: + self._chip_select.value = True + + self._spi.unlock()