From 49945f728cba2d57b878054f711fb4f904b70a2d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 6 Feb 2024 16:07:37 +0000 Subject: [PATCH] All: Always use software CS. --- inky/inky.py | 54 ++++++++++++++++++++++++++---------------- inky/inky_ac073tc1a.py | 12 +++++++--- inky/inky_ssd1608.py | 12 +++++++--- inky/inky_ssd1683.py | 48 ++++++++++++++++++++++++++----------- inky/inky_uc8159.py | 31 ++++++++++++++---------- 5 files changed, 104 insertions(+), 53 deletions(-) diff --git a/inky/inky.py b/inky/inky.py index 24b01ba..dbf5f65 100644 --- a/inky/inky.py +++ b/inky/inky.py @@ -6,7 +6,8 @@ import gpiod import gpiodevice import numpy -from gpiod.line import Direction, Edge, Value +from gpiod.line import Bias, Direction, Edge, Value +from gpiodevice import platform from . import eeprom @@ -17,19 +18,19 @@ BLACK = 1 RED = YELLOW = 2 -# GPIO pins required by BCM number -RESET_PIN = "PIN13" # GPIO 27 -BUSY_PIN = "PIN11" # GPIO 17 -DC_PIN = "PIN15" # GPIO 22 +if platform.get_name().startswith("Raspberry Pi 5"): + RESET_PIN = "PIN13" # GPIO 27 + BUSY_PIN = "PIN11" # GPIO 17 + DC_PIN = "PIN15" # GPIO 22 +else: + RESET_PIN = "GPIO27" + BUSY_PIN = "GPIO17" + DC_PIN = "GPIO22" # In addition the following pins are used for SPI -# CS_PIN = 8 -# MOSI_PIN = 10 -# SCLK_PIN = 11 -# SCLK_PIN = 11 - -# SPI channel for device 0 -CS0 = 0 +MOSI_PIN = 10 +SCLK_PIN = 11 +CS0_PIN = 8 _SPI_CHUNK_SIZE = 4096 _SPI_COMMAND = 0 @@ -55,7 +56,7 @@ class Inky: RED = 2 YELLOW = 2 - def __init__(self, resolution=(400, 300), colour="black", cs_channel=CS0, dc_pin=DC_PIN, reset_pin=RESET_PIN, busy_pin=BUSY_PIN, h_flip=False, v_flip=False, + def __init__(self, resolution=(400, 300), colour="black", cs_pin=CS0_PIN, dc_pin=DC_PIN, reset_pin=RESET_PIN, busy_pin=BUSY_PIN, h_flip=False, v_flip=False, spi_bus=None, i2c_bus=None, gpio=None): """Initialise an Inky Display. @@ -103,7 +104,11 @@ def __init__(self, resolution=(400, 300), colour="black", cs_channel=CS0, dc_pin self.dc_pin = dc_pin self.reset_pin = reset_pin self.busy_pin = busy_pin - self.cs_channel = cs_channel + self.cs_pin = cs_pin + try: + self.cs_channel = [8, 7].index(cs_pin) + except ValueError: + self.cs_channel = 0 self.h_flip = h_flip self.v_flip = v_flip @@ -225,18 +230,21 @@ def setup(self): gpiochip = gpiodevice.find_chip_by_platform() if gpiodevice.check_pins_available(gpiochip, { + "Chip Select": self.cs_pin, "Data/Command": self.dc_pin, "Reset": self.reset_pin, "Busy": self.busy_pin }): + self.cs_pin = gpiochip.line_offset_from_id(self.cs_pin) self.dc_pin = gpiochip.line_offset_from_id(self.dc_pin) self.reset_pin = gpiochip.line_offset_from_id(self.reset_pin) self.busy_pin = gpiochip.line_offset_from_id(self.busy_pin) self._gpio = gpiochip.request_lines(consumer="inky", config={ - self.dc_pin: gpiod.LineSettings(direction=Direction.OUTPUT), - self.reset_pin: gpiod.LineSettings(direction=Direction.OUTPUT), - self.busy_pin: gpiod.LineSettings(direction=Direction.INPUT, edge_detection=Edge.FALLING) + self.cs_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE, bias=Bias.DISABLED), + self.dc_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE, bias=Bias.DISABLED), + self.reset_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE, bias=Bias.DISABLED), + self.busy_pin: gpiod.LineSettings(direction=Direction.INPUT, edge_detection=Edge.FALLING, bias=Bias.DISABLED) }) if self._spi_bus is None: @@ -258,9 +266,12 @@ def setup(self): def _busy_wait(self, timeout=30.0): """Wait for busy/wait pin.""" - event = self._gpio.wait_edge_events(timedelta(seconds=timeout)) - if not event: - raise RuntimeError("Timeout waiting for busy signal to clear.") + if self._gpio.get_value(self.busy_pin) == Value.ACTIVE: + event = self._gpio.wait_edge_events(timedelta(seconds=timeout)) + if not event: + raise RuntimeError("Timeout waiting for busy signal to clear.") + for event in self._gpio.read_edge_events(): + pass def _update(self, buf_a, buf_b, busy_wait=True): """Update display. @@ -383,6 +394,7 @@ def _spi_write(self, dc, values): :param dc: whether to write as data or command :param values: list of values to write """ + self._gpio.set_value(self.cs_pin, Value.INACTIVE) self._gpio.set_value(self.dc_pin, Value.ACTIVE if dc else Value.INACTIVE) try: self._spi_bus.xfer3(values) @@ -391,6 +403,8 @@ def _spi_write(self, dc, values): offset = x * _SPI_CHUNK_SIZE self._spi_bus.xfer(values[offset : offset + _SPI_CHUNK_SIZE]) + self._gpio.set_value(self.cs_pin, Value.ACTIVE) + def _send_command(self, command, data=None): """Send command over SPI. diff --git a/inky/inky_ac073tc1a.py b/inky/inky_ac073tc1a.py index 88a16f3..c29054f 100644 --- a/inky/inky_ac073tc1a.py +++ b/inky/inky_ac073tc1a.py @@ -6,6 +6,7 @@ import gpiod import gpiodevice from gpiod.line import Direction, Edge, Value +from gpiodevice import platform from PIL import Image from . import eeprom @@ -24,9 +25,14 @@ ORANGE = 6 CLEAN = 7 -RESET_PIN = "PIN13" # GPIO 27 -BUSY_PIN = "PIN11" # GPIO 17 -DC_PIN = "PIN15" # GPIO 22 +if platform.get_name().startswith("Raspberry Pi 5"): + RESET_PIN = "PIN13" # GPIO 27 + BUSY_PIN = "PIN11" # GPIO 17 + DC_PIN = "PIN15" # GPIO 22 +else: + RESET_PIN = "GPIO27" + BUSY_PIN = "GPIO17" + DC_PIN = "GPIO22" MOSI_PIN = 10 SCLK_PIN = 11 diff --git a/inky/inky_ssd1608.py b/inky/inky_ssd1608.py index 6abae4e..0e565ba 100644 --- a/inky/inky_ssd1608.py +++ b/inky/inky_ssd1608.py @@ -6,6 +6,7 @@ import gpiodevice import numpy from gpiod.line import Direction, Edge, Value +from gpiodevice import platform from PIL import Image from . import eeprom, ssd1608 @@ -14,9 +15,14 @@ BLACK = 1 RED = YELLOW = 2 -RESET_PIN = "PIN13" # GPIO 27 -BUSY_PIN = "PIN11" # GPIO 17 -DC_PIN = "PIN15" # GPIO 22 +if platform.get_name().startswith("Raspberry Pi 5"): + RESET_PIN = "PIN13" # GPIO 27 + BUSY_PIN = "PIN11" # GPIO 17 + DC_PIN = "PIN15" # GPIO 22 +else: + RESET_PIN = "GPIO27" + BUSY_PIN = "GPIO17" + DC_PIN = "GPIO22" MOSI_PIN = 10 SCLK_PIN = 11 diff --git a/inky/inky_ssd1683.py b/inky/inky_ssd1683.py index 0727143..2da3f71 100644 --- a/inky/inky_ssd1683.py +++ b/inky/inky_ssd1683.py @@ -5,7 +5,8 @@ import gpiod import gpiodevice import numpy -from gpiod.line import Direction, Edge, Value +from gpiod.line import Bias, Direction, Edge, Value +from gpiodevice import platform from PIL import Image from . import eeprom, ssd1683 @@ -14,13 +15,18 @@ BLACK = 1 RED = YELLOW = 2 -RESET_PIN = 27 -BUSY_PIN = 17 -DC_PIN = 22 +if platform.get_name().startswith("Raspberry Pi 5"): + RESET_PIN = "PIN13" # GPIO 27 + BUSY_PIN = "PIN11" # GPIO 17 + DC_PIN = "PIN15" # GPIO 22 +else: + RESET_PIN = "GPIO27" + BUSY_PIN = "GPIO17" + DC_PIN = "GPIO22" MOSI_PIN = 10 SCLK_PIN = 11 -CS0_PIN = 0 +CS0_PIN = 8 SUPPORTED_DISPLAYS = 17, 18, 19 @@ -86,6 +92,10 @@ def __init__(self, resolution=(400, 300), colour="black", cs_pin=CS0_PIN, dc_pin self.reset_pin = reset_pin self.busy_pin = busy_pin self.cs_pin = cs_pin + try: + self.cs_channel = [8, 7].index(cs_pin) + except ValueError: + self.cs_channel = 0 self.h_flip = h_flip self.v_flip = v_flip @@ -117,18 +127,21 @@ def setup(self): gpiochip = gpiodevice.find_chip_by_platform() gpiodevice.friendly_errors = True if gpiodevice.check_pins_available(gpiochip, { + "Chip Select": self.cs_pin, "Data/Command": self.dc_pin, "Reset": self.reset_pin, "Busy": self.busy_pin }): + self.cs_pin = gpiochip.line_offset_from_id(self.cs_pin) self.dc_pin = gpiochip.line_offset_from_id(self.dc_pin) self.reset_pin = gpiochip.line_offset_from_id(self.reset_pin) self.busy_pin = gpiochip.line_offset_from_id(self.busy_pin) self._gpio = gpiochip.request_lines(consumer="inky", config={ - self.dc_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE), - self.reset_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE), - self.busy_pin: gpiod.LineSettings(direction=Direction.INPUT, edge_detection=Edge.FALLING, debounce_period=timedelta(milliseconds=10)) + self.cs_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE, bias=Bias.DISABLED), + self.dc_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE, bias=Bias.DISABLED), + self.reset_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE, bias=Bias.DISABLED), + self.busy_pin: gpiod.LineSettings(direction=Direction.INPUT, edge_detection=Edge.FALLING, bias=Bias.DISABLED) }) if self._spi_bus is None: @@ -136,7 +149,7 @@ def setup(self): self._spi_bus = spidev.SpiDev() - self._spi_bus.open(0, self.cs_pin) + self._spi_bus.open(0, self.cs_channel) self._spi_bus.max_speed_hz = 10000000 # Should be good for 20MHz according to datasheet self._gpio_setup = True @@ -147,14 +160,17 @@ def setup(self): time.sleep(0.5) self._send_command(0x12) # Soft Reset - time.sleep(1.0) + time.sleep(1.0) # Required, or we'll miss buf_a (black) self._busy_wait() - def _busy_wait(self, timeout=5.0): + def _busy_wait(self, timeout=30.0): """Wait for busy/wait pin.""" - event = self._gpio.wait_edge_events(timedelta(seconds=timeout)) - if not event: - raise RuntimeError("Timeout waiting for busy signal to clear.") + if self._gpio.get_value(self.busy_pin) == Value.ACTIVE: + event = self._gpio.wait_edge_events(timedelta(seconds=timeout)) + if not event: + raise RuntimeError("Timeout waiting for busy signal to clear.") + for event in self._gpio.read_edge_events(): + pass def _update(self, buf_a, buf_b, busy_wait=True): """Update display. @@ -272,7 +288,9 @@ def _spi_write(self, dc, values): :param values: list of values to write """ + self._gpio.set_value(self.cs_pin, Value.INACTIVE) self._gpio.set_value(self.dc_pin, Value.ACTIVE if dc else Value.INACTIVE) + try: self._spi_bus.xfer3(values) except AttributeError: @@ -280,6 +298,8 @@ def _spi_write(self, dc, values): offset = x * _SPI_CHUNK_SIZE self._spi_bus.xfer(values[offset:offset + _SPI_CHUNK_SIZE]) + self._gpio.set_value(self.cs_pin, Value.ACTIVE) + def _send_command(self, command, data=None): """Send command over SPI. diff --git a/inky/inky_uc8159.py b/inky/inky_uc8159.py index 004b702..aac6008 100644 --- a/inky/inky_uc8159.py +++ b/inky/inky_uc8159.py @@ -7,7 +7,8 @@ import gpiod import gpiodevice import numpy -from gpiod.line import Direction, Edge, Value +from gpiod.line import Bias, Direction, Edge, Value +from gpiodevice import platform from PIL import Image from . import eeprom @@ -43,9 +44,14 @@ [255, 255, 255] ] -RESET_PIN = "PIN13" # GPIO 27 -BUSY_PIN = "PIN11" # GPIO 17 -DC_PIN = "PIN15" # GPIO 22 +if platform.get_name().startswith("Raspberry Pi 5"): + RESET_PIN = "PIN13" # GPIO 27 + BUSY_PIN = "PIN11" # GPIO 17 + DC_PIN = "PIN15" # GPIO 22 +else: + RESET_PIN = "GPIO27" + BUSY_PIN = "GPIO17" + DC_PIN = "GPIO22" MOSI_PIN = 10 SCLK_PIN = 11 @@ -218,12 +224,11 @@ def setup(self): self.dc_pin = gpiochip.line_offset_from_id(self.dc_pin) self.reset_pin = gpiochip.line_offset_from_id(self.reset_pin) self.busy_pin = gpiochip.line_offset_from_id(self.busy_pin) - self._gpio = gpiochip.request_lines(consumer="inky", config={ - self.cs_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE), - self.dc_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE), - self.reset_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE), - self.busy_pin: gpiod.LineSettings(direction=Direction.INPUT, edge_detection=Edge.RISING, debounce_period=timedelta(milliseconds=10)) + self.cs_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE, bias=Bias.DISABLED), + self.dc_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE, bias=Bias.DISABLED), + self.reset_pin: gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.ACTIVE, bias=Bias.DISABLED), + self.busy_pin: gpiod.LineSettings(direction=Direction.INPUT, edge_detection=Edge.RISING, debounce_period=timedelta(milliseconds=10), bias=Bias.DISABLED) }) if self._spi_bus is None: @@ -242,6 +247,7 @@ def setup(self): self._gpio.set_value(self.reset_pin, Value.INACTIVE) time.sleep(0.1) self._gpio.set_value(self.reset_pin, Value.ACTIVE) + time.sleep(0.1) self._busy_wait(1.0) @@ -338,16 +344,14 @@ def _busy_wait(self, timeout=40.0): return for event in self._gpio.read_edge_events(): - print(timeout, event) + if event.Type == Edge.RISING: + return def _update(self, buf): """Update display. Dispatches display update to correct driver. - :param buf_a: Black/White pixels - :param buf_b: Yellow/Red pixels - """ self.setup() self._send_command(UC8159_DTM1, buf) @@ -438,6 +442,7 @@ def _spi_write(self, dc, values): for x in range(((len(values) - 1) // _SPI_CHUNK_SIZE) + 1): offset = x * _SPI_CHUNK_SIZE self._spi_bus.xfer(values[offset : offset + _SPI_CHUNK_SIZE]) + self._gpio.set_value(self.cs_pin, Value.ACTIVE) def _send_command(self, command, data=None):