]> Repositories - Adafruit_Blinka-hackapet.git/commitdiff
Create separate BCM2711 structure.
authoradam_cummick:g9T51EHpC9gPQqG6sb9Q@gitlab.com <adamc@facts-eng.com>
Wed, 20 Oct 2021 18:18:08 +0000 (14:18 -0400)
committeradam_cummick:g9T51EHpC9gPQqG6sb9Q@gitlab.com <adamc@facts-eng.com>
Wed, 20 Oct 2021 18:18:08 +0000 (14:18 -0400)
14 files changed:
src/adafruit_blinka/board/raspberrypi/raspi_4b.py [new file with mode: 0644]
src/adafruit_blinka/board/raspberrypi/raspi_cm4.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/__init__.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/neopixel.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/pin.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/pulseio/PWMOut.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/pulseio/PulseIn.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/pulseio/__init__.py [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein64 [new file with mode: 0644]
src/adafruit_blinka/microcontroller/bcm283x/pin.py
src/board.py
src/microcontroller/__init__.py
src/microcontroller/pin.py

diff --git a/src/adafruit_blinka/board/raspberrypi/raspi_4b.py b/src/adafruit_blinka/board/raspberrypi/raspi_4b.py
new file mode 100644 (file)
index 0000000..0b6187c
--- /dev/null
@@ -0,0 +1,55 @@
+"""Pin definitions for 40-pin Raspberry Pi models."""
+
+from adafruit_blinka.microcontroller.bcm2711 import pin
+
+D0 = pin.D0
+D1 = pin.D1
+
+D2 = pin.D2
+SDA = pin.SDA
+D3 = pin.D3
+SCL = pin.SCL
+
+D4 = pin.D4
+D5 = pin.D5
+D6 = pin.D6
+
+D7 = pin.D7
+CE1 = pin.D7
+D8 = pin.D8
+CE0 = pin.D8
+D9 = pin.D9
+MISO = pin.D9
+D10 = pin.D10
+MOSI = pin.D10
+D11 = pin.D11
+SCLK = pin.D11
+SCK = pin.D11
+
+D12 = pin.D12
+D13 = pin.D13
+
+D14 = pin.D14
+TXD = pin.D14
+D15 = pin.D15
+RXD = pin.D15
+# create alias for most of the examples
+TX = pin.D14
+RX = pin.D15
+
+D16 = pin.D16
+D17 = pin.D17
+D18 = pin.D18
+D19 = pin.D19
+MISO_1 = pin.D19
+D20 = pin.D20
+MOSI_1 = pin.D20
+D21 = pin.D21
+SCLK_1 = pin.D21
+SCK_1 = pin.D21
+D22 = pin.D22
+D23 = pin.D23
+D24 = pin.D24
+D25 = pin.D25
+D26 = pin.D26
+D27 = pin.D27
diff --git a/src/adafruit_blinka/board/raspberrypi/raspi_cm4.py b/src/adafruit_blinka/board/raspberrypi/raspi_cm4.py
new file mode 100644 (file)
index 0000000..bd0d4c7
--- /dev/null
@@ -0,0 +1,74 @@
+"""Pin definitions for Raspberry Pi Compute Modules."""
+
+from adafruit_blinka.microcontroller.bcm2711 import pin
+
+D2 = pin.D2
+SDA = pin.SDA
+D3 = pin.D3
+SCL = pin.SCL
+
+D4 = pin.D4
+D5 = pin.D5
+D6 = pin.D6
+
+D7 = pin.D7
+CE1 = pin.D7
+D8 = pin.D8
+CE0 = pin.D8
+D9 = pin.D9
+MISO = pin.D9
+D10 = pin.D10
+MOSI = pin.D10
+D11 = pin.D11
+SCLK = pin.D11
+SCK = pin.D11
+
+D12 = pin.D12
+D13 = pin.D13
+
+D14 = pin.D14
+TXD = pin.D14
+D15 = pin.D15
+RXD = pin.D15
+# create alias for most of the examples
+TX = pin.D14
+RX = pin.D15
+
+D16 = pin.D16
+D17 = pin.D17
+D18 = pin.D18
+D19 = pin.D19
+MISO_1 = pin.D19
+D20 = pin.D20
+MOSI_1 = pin.D20
+D21 = pin.D21
+SCLK_1 = pin.D21
+SCK_1 = pin.D21
+D22 = pin.D22
+D23 = pin.D23
+D24 = pin.D24
+D25 = pin.D25
+D26 = pin.D26
+D27 = pin.D27
+D28 = pin.D28
+D29 = pin.D29
+D30 = pin.D30
+D31 = pin.D31
+D32 = pin.D32
+D33 = pin.D33
+D34 = pin.D34
+D35 = pin.D35
+D36 = pin.D36
+D37 = pin.D37
+D38 = pin.D38
+D39 = pin.D39
+D40 = pin.D40
+MISO_2 = pin.D40
+D41 = pin.D41
+MOSI_2 = pin.D41
+D42 = pin.D42
+SCLK_2 = pin.D42
+SCK_2 = pin.D43
+D43 = pin.D43
+D44 = pin.D44
+D45 = pin.D45
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/__init__.py b/src/adafruit_blinka/microcontroller/bcm2711/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/neopixel.py b/src/adafruit_blinka/microcontroller/bcm2711/neopixel.py
new file mode 100644 (file)
index 0000000..6235f3c
--- /dev/null
@@ -0,0 +1,123 @@
+"""BCM2711 NeoPixel Driver Class"""
+import time
+import atexit
+import _rpi_ws281x as ws
+
+# LED configuration.
+# pylint: disable=redefined-outer-name,too-many-branches,too-many-statements
+# pylint: disable=global-statement,protected-access
+LED_CHANNEL = 0
+LED_FREQ_HZ = 800000  # Frequency of the LED signal.  We only support 800KHz
+LED_DMA_NUM = 10  # DMA channel to use, can be 0-14.
+LED_BRIGHTNESS = 255  # We manage the brightness in the neopixel library
+LED_INVERT = 0  # We don't support inverted logic
+LED_STRIP = None  # We manage the color order within the neopixel library
+
+# a 'static' object that we will use to manage our PWM DMA channel
+# we only support one LED strip per raspi
+_led_strip = None
+_buf = None
+
+
+def neopixel_write(gpio, buf):
+    """NeoPixel Writing Function"""
+    global _led_strip  # we'll have one strip we init if its not at first
+    global _buf  # we save a reference to the buf, and if it changes we will cleanup and re-init.
+
+    if _led_strip is None or buf is not _buf:
+        # This is safe to call since it doesn't do anything if _led_strip is None
+        neopixel_cleanup()
+
+        # Create a ws2811_t structure from the LED configuration.
+        # Note that this structure will be created on the heap so you
+        # need to be careful that you delete its memory by calling
+        # delete_ws2811_t when it's not needed.
+        _led_strip = ws.new_ws2811_t()
+        _buf = buf
+
+        # Initialize all channels to off
+        for channum in range(2):
+            channel = ws.ws2811_channel_get(_led_strip, channum)
+            ws.ws2811_channel_t_count_set(channel, 0)
+            ws.ws2811_channel_t_gpionum_set(channel, 0)
+            ws.ws2811_channel_t_invert_set(channel, 0)
+            ws.ws2811_channel_t_brightness_set(channel, 0)
+
+        channel = ws.ws2811_channel_get(_led_strip, LED_CHANNEL)
+
+        # Initialize the channel in use
+        count = 0
+        if len(buf) % 3 == 0:
+            # most common, divisible by 3 is likely RGB
+            LED_STRIP = ws.WS2811_STRIP_RGB
+            count = len(buf) // 3
+        elif len(buf) % 4 == 0:
+            LED_STRIP = ws.SK6812_STRIP_RGBW
+            count = len(buf) // 4
+        else:
+            raise RuntimeError("We only support 3 or 4 bytes-per-pixel")
+
+        ws.ws2811_channel_t_count_set(
+            channel, count
+        )  # we manage 4 vs 3 bytes in the library
+        ws.ws2811_channel_t_gpionum_set(channel, gpio._pin.id)
+        ws.ws2811_channel_t_invert_set(channel, LED_INVERT)
+        ws.ws2811_channel_t_brightness_set(channel, LED_BRIGHTNESS)
+        ws.ws2811_channel_t_strip_type_set(channel, LED_STRIP)
+
+        # Initialize the controller
+        ws.ws2811_t_freq_set(_led_strip, LED_FREQ_HZ)
+        ws.ws2811_t_dmanum_set(_led_strip, LED_DMA_NUM)
+
+        resp = ws.ws2811_init(_led_strip)
+        if resp != ws.WS2811_SUCCESS:
+            if resp == -5:
+                raise RuntimeError(
+                    "NeoPixel support requires running with sudo, please try again!"
+                )
+            message = ws.ws2811_get_return_t_str(resp)
+            raise RuntimeError(
+                "ws2811_init failed with code {0} ({1})".format(resp, message)
+            )
+        atexit.register(neopixel_cleanup)
+
+    channel = ws.ws2811_channel_get(_led_strip, LED_CHANNEL)
+    if gpio._pin.id != ws.ws2811_channel_t_gpionum_get(channel):
+        raise RuntimeError("Raspberry Pi neopixel support is for one strip only!")
+
+    if ws.ws2811_channel_t_strip_type_get(channel) == ws.WS2811_STRIP_RGB:
+        bpp = 3
+    else:
+        bpp = 4
+    # assign all colors!
+    for i in range(len(buf) // bpp):
+        r = buf[bpp * i]
+        g = buf[bpp * i + 1]
+        b = buf[bpp * i + 2]
+        if bpp == 3:
+            pixel = (r << 16) | (g << 8) | b
+        else:
+            w = buf[bpp * i + 3]
+            pixel = (w << 24) | (r << 16) | (g << 8) | b
+        ws.ws2811_led_set(channel, i, pixel)
+
+    resp = ws.ws2811_render(_led_strip)
+    if resp != ws.WS2811_SUCCESS:
+        message = ws.ws2811_get_return_t_str(resp)
+        raise RuntimeError(
+            "ws2811_render failed with code {0} ({1})".format(resp, message)
+        )
+    time.sleep(0.001 * ((len(buf) // 100) + 1))  # about 1ms per 100 bytes
+
+
+def neopixel_cleanup():
+    """Cleanup when we're done"""
+    global _led_strip
+
+    if _led_strip is not None:
+        # Ensure ws2811_fini is called before the program quits.
+        ws.ws2811_fini(_led_strip)
+        # Example of calling delete function to clean up structure memory.  Isn't
+        # strictly necessary at the end of the program execution here, but is good practice.
+        ws.delete_ws2811_t(_led_strip)
+        _led_strip = None
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/pin.py b/src/adafruit_blinka/microcontroller/bcm2711/pin.py
new file mode 100644 (file)
index 0000000..7fe5996
--- /dev/null
@@ -0,0 +1,161 @@
+"""Broadcom BCM283x pin names"""
+import RPi.GPIO as GPIO
+from adafruit_blinka.agnostic import detector
+
+GPIO.setmode(GPIO.BCM)  # Use BCM pins D4 = GPIO #4
+GPIO.setwarnings(False)  # shh!
+
+
+class Pin:
+    """Pins dont exist in CPython so...lets make our own!"""
+
+    IN = 0
+    OUT = 1
+    LOW = 0
+    HIGH = 1
+    PULL_NONE = 0
+    PULL_UP = 1
+    PULL_DOWN = 2
+
+    id = None
+    _value = LOW
+    _mode = IN
+
+    def __init__(self, bcm_number):
+        self.id = bcm_number
+
+    def __repr__(self):
+        return str(self.id)
+
+    def __eq__(self, other):
+        return self.id == other
+
+    def init(self, mode=IN, pull=None):
+        """Initialize the Pin"""
+        if mode is not None:
+            if mode == self.IN:
+                self._mode = self.IN
+                GPIO.setup(self.id, GPIO.IN)
+            elif mode == self.OUT:
+                self._mode = self.OUT
+                GPIO.setup(self.id, GPIO.OUT)
+            else:
+                raise RuntimeError("Invalid mode for pin: %s" % self.id)
+        if pull is not None:
+            if self._mode != self.IN:
+                raise RuntimeError("Cannot set pull resistor on output")
+            if pull == self.PULL_UP:
+                GPIO.setup(self.id, GPIO.IN, pull_up_down=GPIO.PUD_UP)
+            elif pull == self.PULL_DOWN:
+                GPIO.setup(self.id, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
+            else:
+                raise RuntimeError("Invalid pull for pin: %s" % self.id)
+
+    def value(self, val=None):
+        """Set or return the Pin Value"""
+        if val is not None:
+            if val == self.LOW:
+                self._value = val
+                GPIO.output(self.id, val)
+            elif val == self.HIGH:
+                self._value = val
+                GPIO.output(self.id, val)
+            else:
+                raise RuntimeError("Invalid value for pin")
+            return None
+        return GPIO.input(self.id)
+
+
+# Pi 1B rev1 only?
+D0 = Pin(0)
+D1 = Pin(1)
+
+D2 = Pin(2)
+SDA = Pin(2)
+D3 = Pin(3)
+SCL = Pin(3)
+
+D4 = Pin(4)
+D5 = Pin(5)
+D6 = Pin(6)
+
+D7 = Pin(7)
+CE1 = Pin(7)
+D8 = Pin(8)
+CE0 = Pin(8)
+D9 = Pin(9)
+MISO = Pin(9)
+D10 = Pin(10)
+MOSI = Pin(10)
+D11 = Pin(11)
+SCLK = Pin(11)  # Raspberry Pi naming
+SCK = Pin(11)  # CircuitPython naming
+
+D12 = Pin(12)
+D13 = Pin(13)
+
+D14 = Pin(14)
+TXD = Pin(14)
+D15 = Pin(15)
+RXD = Pin(15)
+
+D16 = Pin(16)
+D17 = Pin(17)
+D18 = Pin(18)
+D19 = Pin(19)
+MISO_1 = Pin(19)
+D20 = Pin(20)
+MOSI_1 = Pin(20)
+D21 = Pin(21)
+SCLK_1 = Pin(21)
+SCK_1 = Pin(21)
+D22 = Pin(22)
+D23 = Pin(23)
+D24 = Pin(24)
+D25 = Pin(25)
+D26 = Pin(26)
+D27 = Pin(27)
+D28 = Pin(28)
+D29 = Pin(29)
+D30 = Pin(30)
+D31 = Pin(31)
+D32 = Pin(32)
+D33 = Pin(33)
+D34 = Pin(34)
+D35 = Pin(35)
+D36 = Pin(36)
+D37 = Pin(37)
+D38 = Pin(38)
+D39 = Pin(39)
+D40 = Pin(40)
+MISO_2 = Pin(40)
+D41 = Pin(41)
+MOSI_2 = Pin(41)
+D42 = Pin(42)
+SCLK_2 = Pin(42)
+SCK_2 = Pin(43)
+D43 = Pin(43)
+D44 = Pin(44)
+D45 = Pin(45)
+
+# ordered as spiId, sckId, mosiId, misoId
+spiPorts = (
+    (0, SCLK, MOSI, MISO),
+    (6, SCLK_1, MOSI_1, MISO_1),
+    (2, SCLK_2, MOSI_2, MISO_2),
+    (3, D3, D2, D1),
+    (4, D7, D6, D5),
+    (5, D15, D14, D13),
+)
+
+# ordered as uartId, txId, rxId
+uartPorts = ((1, TXD, RXD),)
+
+# These are the known hardware I2C ports / pins.
+# For software I2C ports created with the i2c-gpio overlay, see:
+#     https://github.com/adafruit/Adafruit_Python_Extended_Bus
+i2cPorts = (
+    (1, SCL, SDA),
+    (0, D1, D0),  # both pi 1 and pi 2 i2c ports!
+    (10, D45, D44),  # internal i2c bus for the CM4
+)
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/pulseio/PWMOut.py b/src/adafruit_blinka/microcontroller/bcm2711/pulseio/PWMOut.py
new file mode 100644 (file)
index 0000000..65f269f
--- /dev/null
@@ -0,0 +1,162 @@
+"""Custom PWMOut Wrapper for Rpi.GPIO PWM Class"""
+import RPi.GPIO as GPIO
+
+GPIO.setmode(GPIO.BCM)  # Use BCM pins D4 = GPIO #4
+GPIO.setwarnings(False)  # shh!
+
+
+# pylint: disable=unnecessary-pass
+class PWMError(IOError):
+    """Base class for PWM errors."""
+
+    pass
+
+
+# pylint: enable=unnecessary-pass
+
+
+class PWMOut:
+    """Pulse Width Modulation Output Class"""
+
+    def __init__(self, pin, *, frequency=500, duty_cycle=0, variable_frequency=False):
+        self._pwmpin = None
+        self._period = 0
+        self._open(pin, duty_cycle, frequency, variable_frequency)
+
+    def __del__(self):
+        self.deinit()
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, t, value, traceback):
+        self.deinit()
+
+    def _open(self, pin, duty=0, freq=500, variable_frequency=False):
+        self._pin = pin
+        GPIO.setup(pin.id, GPIO.OUT)
+        self._pwmpin = GPIO.PWM(pin.id, freq)
+
+        if variable_frequency:
+            print("Variable Frequency is not supported, continuing without it...")
+
+        # set frequency
+        self.frequency = freq
+        # set duty
+        self.duty_cycle = duty
+
+        self.enabled = True
+
+    def deinit(self):
+        """Deinit the PWM."""
+        if self._pwmpin is not None:
+            self._pwmpin.stop()
+            GPIO.cleanup(self._pin.id)
+            self._pwmpin = None
+
+    def _is_deinited(self):
+        if self._pwmpin is None:
+            raise ValueError(
+                "Object has been deinitialize and can no longer "
+                "be used. Create a new object."
+            )
+
+    @property
+    def period(self):
+        """Get or set the PWM's output period in seconds.
+
+        Raises:
+            PWMError: if an I/O or OS error occurs.
+            TypeError: if value type is not int or float.
+
+        :type: int, float
+        """
+        return 1.0 / self.frequency
+
+    @period.setter
+    def period(self, period):
+        if not isinstance(period, (int, float)):
+            raise TypeError("Invalid period type, should be int or float.")
+
+        self.frequency = 1.0 / period
+
+    @property
+    def duty_cycle(self):
+        """Get or set the PWM's output duty cycle which is the fraction of
+        each pulse which is high. 16-bit
+
+        Raises:
+            PWMError: if an I/O or OS error occurs.
+            TypeError: if value type is not int or float.
+            ValueError: if value is out of bounds of 0.0 to 1.0.
+
+        :type: int, float
+        """
+        return int(self._duty_cycle * 65535)
+
+    @duty_cycle.setter
+    def duty_cycle(self, duty_cycle):
+        if not isinstance(duty_cycle, (int, float)):
+            raise TypeError("Invalid duty cycle type, should be int or float.")
+
+        if not 0 <= duty_cycle <= 65535:
+            raise ValueError("Invalid duty cycle value, should be between 0 and 65535")
+
+        # convert from 16-bit
+        duty_cycle /= 65535.0
+
+        self._duty_cycle = duty_cycle
+        self._pwmpin.ChangeDutyCycle(round(self._duty_cycle * 100))
+
+    @property
+    def frequency(self):
+        """Get or set the PWM's output frequency in Hertz.
+
+        Raises:
+            PWMError: if an I/O or OS error occurs.
+            TypeError: if value type is not int or float.
+
+        :type: int, float
+        """
+
+        return self._frequency
+
+    @frequency.setter
+    def frequency(self, frequency):
+        if not isinstance(frequency, (int, float)):
+            raise TypeError("Invalid frequency type, should be int or float.")
+
+        self._pwmpin.ChangeFrequency(round(frequency))
+        self._frequency = frequency
+
+    @property
+    def enabled(self):
+        """Get or set the PWM's output enabled state.
+
+        Raises:
+            PWMError: if an I/O or OS error occurs.
+            TypeError: if value type is not bool.
+
+        :type: bool
+        """
+        return self._enabled
+
+    @enabled.setter
+    def enabled(self, value):
+        if not isinstance(value, bool):
+            raise TypeError("Invalid enabled type, should be string.")
+
+        if value:
+            self._pwmpin.start(round(self._duty_cycle * 100))
+        else:
+            self._pwmpin.stop()
+
+        self._enabled = value
+
+    # String representation
+    def __str__(self):
+        return "pin %s (freq=%f Hz, duty_cycle=%f%%)" % (
+            self._pin,
+            self.frequency,
+            self.duty_cycle,
+        )
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/pulseio/PulseIn.py b/src/adafruit_blinka/microcontroller/bcm2711/pulseio/PulseIn.py
new file mode 100644 (file)
index 0000000..700d8de
--- /dev/null
@@ -0,0 +1,178 @@
+"""Custom PulseIn Class to read PWM signals"""
+import time
+import subprocess
+import os
+import atexit
+import random
+import struct
+import sysv_ipc
+
+DEBUG = False
+queues = []
+procs = []
+
+# The message queues live outside of python space, and must be formally cleaned!
+def final():
+    """In case the program is cancelled or quit, we need to clean up the PulseIn
+    helper process and also the message queue, this is called at exit to do so"""
+    if DEBUG:
+        print("Cleaning up message queues", queues)
+        print("Cleaning up processes", procs)
+    for q in queues:
+        q.remove()
+    for proc in procs:
+        proc.terminate()
+
+
+atexit.register(final)
+
+# pylint: disable=c-extension-no-member
+class PulseIn:
+    """PulseIn Class to read PWM signals"""
+
+    def __init__(self, pin, maxlen=2, idle_state=False):
+        """Create a PulseIn object associated with the given pin.
+        The object acts as a read-only sequence of pulse lengths with
+        a given max length. When it is active, new pulse lengths are
+        added to the end of the list. When there is no more room
+        (len() == maxlen) the oldest pulse length is removed to make room."""
+        self._pin = pin
+        self._maxlen = maxlen
+        self._idle_state = idle_state
+        self._queue_key = random.randint(1, 9999)
+        try:
+            self._mq = sysv_ipc.MessageQueue(None, flags=sysv_ipc.IPC_CREX)
+            if DEBUG:
+                print("Message Queue Key: ", self._mq.key)
+            queues.append(self._mq)
+        except sysv_ipc.ExistentialError:
+            raise RuntimeError(
+                "Message queue creation failed"
+            ) from sysv_ipc.ExistentialError
+
+        # Check if OS is 64-bit
+        if struct.calcsize("P") * 8 == 64:
+            libgpiod_filename = "libgpiod_pulsein64"
+        else:
+            libgpiod_filename = "libgpiod_pulsein"
+
+        dir_path = os.path.dirname(os.path.realpath(__file__))
+        cmd = [
+            dir_path + "/" + libgpiod_filename,
+            "--pulses",
+            str(maxlen),
+            "--queue",
+            str(self._mq.key),
+        ]
+        if idle_state:
+            cmd.append("-i")
+        cmd.append("gpiochip0")
+        cmd.append(str(pin))
+        if DEBUG:
+            print(cmd)
+
+        self._process = subprocess.Popen(cmd)
+        procs.append(self._process)
+
+        # wait for it to start up
+        if DEBUG:
+            print("Waiting for startup success message from subprocess")
+        message = self._wait_receive_msg(timeout=0.25)
+        if message[0] != b"!":
+            raise RuntimeError("Could not establish message queue with subprocess")
+        self._paused = False
+
+    # pylint: disable=redefined-builtin
+    def _wait_receive_msg(self, timeout=0, type=2):
+        """Internal helper that will wait for new messages of a given type,
+        and throw an exception on timeout"""
+        if timeout > 0:
+            stamp = time.monotonic()
+            while (time.monotonic() - stamp) < timeout:
+                try:
+                    message = self._mq.receive(block=False, type=type)
+                    return message
+                except sysv_ipc.BusyError:
+                    time.sleep(0.001)  # wait a bit then retry!
+            # uh-oh timed out
+            raise RuntimeError(
+                "Timed out waiting for PulseIn message. Make sure libgpiod is installed."
+            )
+        message = self._mq.receive(block=True, type=type)
+        return message
+
+    # pylint: enable=redefined-builtin
+
+    def deinit(self):
+        """Deinitialises the PulseIn and releases any hardware and software
+        resources for reuse."""
+        # Clean up after ourselves
+        self._process.terminate()
+        procs.remove(self._process)
+        self._mq.remove()
+        queues.remove(self._mq)
+
+    def __enter__(self):
+        """No-op used by Context Managers."""
+        return self
+
+    def __exit__(self, exc_type, exc_value, tb):
+        """Automatically deinitializes the hardware when exiting a context."""
+        self.deinit()
+
+    def resume(self, trigger_duration=0):
+        """Resumes pulse capture after an optional trigger pulse."""
+        if trigger_duration != 0:
+            self._mq.send("t%d" % trigger_duration, True, type=1)
+        else:
+            self._mq.send("r", True, type=1)
+        self._paused = False
+
+    def pause(self):
+        """Pause pulse capture"""
+        self._mq.send("p", True, type=1)
+        self._paused = True
+
+    @property
+    def paused(self):
+        """True when pulse capture is paused as a result of pause() or
+        an error during capture such as a signal that is too fast."""
+        return self._paused
+
+    @property
+    def maxlen(self):
+        """The maximum length of the PulseIn. When len() is equal to maxlen,
+        it is unclear which pulses are active and which are idle."""
+        return self._maxlen
+
+    def clear(self):
+        """Clears all captured pulses"""
+        self._mq.send("c", True, type=1)
+
+    def popleft(self):
+        """Removes and returns the oldest read pulse."""
+        self._mq.send("^", True, type=1)
+        message = self._wait_receive_msg()
+        reply = int(message[0].decode("utf-8"))
+        # print(reply)
+        if reply == -1:
+            raise IndexError("pop from empty list")
+        return reply
+
+    def __len__(self):
+        """Returns the current pulse length"""
+        self._mq.send("l", True, type=1)
+        message = self._wait_receive_msg()
+        return int(message[0].decode("utf-8"))
+
+    # pylint: disable=redefined-builtin
+    def __getitem__(self, index, type=None):
+        """Returns the value at the given index or values in slice."""
+        self._mq.send("i%d" % index, True, type=1)
+        message = self._wait_receive_msg()
+        ret = int(message[0].decode("utf-8"))
+        if ret == -1:
+            raise IndexError("list index out of range")
+        return ret
+
+    # pylint: enable=redefined-builtin
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/pulseio/__init__.py b/src/adafruit_blinka/microcontroller/bcm2711/pulseio/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein b/src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein
new file mode 100644 (file)
index 0000000..0291c1b
Binary files /dev/null and b/src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein differ
diff --git a/src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein64 b/src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein64
new file mode 100644 (file)
index 0000000..0143060
Binary files /dev/null and b/src/adafruit_blinka/microcontroller/bcm2711/pulseio/libgpiod_pulsein64 differ
index dd8abe60e943cf0f3f8dac1ef61f1f1303f81e69..423435e00b6af82a9753f0245bdb4748b51abecf 100644 (file)
@@ -139,21 +139,11 @@ D44 = Pin(44)
 D45 = Pin(45)
 
 # ordered as spiId, sckId, mosiId, misoId
-if detector.board.id in ["RASPBERRY_PI_4B", "RASPBERRY_PI_CM4"]:
-    spiPorts = (
-        (0, SCLK, MOSI, MISO),
-        (6, SCLK_1, MOSI_1, MISO_1),
-        (2, SCLK_2, MOSI_2, MISO_2),
-        (3, D3, D2, D1),
-        (4, D7, D6, D5),
-        (5, D15, D14, D13),
-    )
-else:
-    spiPorts = (
-        (0, SCLK, MOSI, MISO),
-        (1, SCLK_1, MOSI_1, MISO_1),
-        (2, SCLK_2, MOSI_2, MISO_2),
-    )
+spiPorts = (
+    (0, SCLK, MOSI, MISO),
+    (1, SCLK_1, MOSI_1, MISO_1),
+    (2, SCLK_2, MOSI_2, MISO_2),
+)
 
 # ordered as uartId, txId, rxId
 uartPorts = ((1, TXD, RXD),)
@@ -164,5 +154,4 @@ uartPorts = ((1, TXD, RXD),)
 i2cPorts = (
     (1, SCL, SDA),
     (0, D1, D0),  # both pi 1 and pi 2 i2c ports!
-    (10, D45, D44),  # internal i2c bus for the CM4
 )
index 304daf5561f6075c4e98deb12ce025fd3f370ea8..36a248d8dbc80efc340ba520f007ec187fd3b8d9 100755 (executable)
@@ -54,6 +54,12 @@ elif board_id == ap_board.PYBOARD:
 elif board_id == ap_board.RASPBERRY_PI_PICO:
     from adafruit_blinka.board.raspberrypi.pico import *
 
+elif detector.board.RASPBERRY_PI_4B or detector.board.RASPBERRY_PI_400:
+    from adafruit_blinka.board.raspberrypi.raspi_4b import *
+
+elif detector.board.RASPBERRY_PI_CM4:
+    from adafruit_blinka.board.raspberrypi.raspi_cm4 import *
+
 elif detector.board.any_raspberry_pi_40_pin:
     from adafruit_blinka.board.raspberrypi.raspi_40pin import *
 
index 82bc1a95a2dd91b74cd9b3ceedae9c751a5bc6ed..d9ca071a7b43ab38da57b157f59fc2cf03368d9f 100755 (executable)
@@ -57,7 +57,10 @@ elif chip_id == ap_chip.STM32F405:
 elif chip_id == ap_chip.RP2040:
     from adafruit_blinka.microcontroller.rp2040 import *
 elif chip_id == ap_chip.BCM2XXX:
-    from adafruit_blinka.microcontroller.bcm283x import *
+    if board_id in ["RASPBERRY_PI_4B", "RASPBERRY_PI_400", "RASPBERRY_PI_CM4",]:
+        from adafruit_blinka.microcontroller.bcm2711.pin import *
+    else:
+        from adafruit_blinka.microcontroller.bcm283x.pin import *
 elif chip_id == ap_chip.DRA74X:
     from adafruit_blinka.microcontroller.dra74x.pin import *
 elif chip_id == ap_chip.AM33XX:
index c46a335b31762fbc4f5b0237ebae984f11344e4d..15e015227bfab40a985cd68ab959fcb9c27ddac3 100755 (executable)
@@ -1,7 +1,7 @@
 """Pins named after their chip name."""
 
 from adafruit_platformdetect.constants import chips as ap_chip
-from adafruit_blinka.agnostic import chip_id
+from adafruit_blinka.agnostic import board_id, chip_id
 
 # We intentionally are patching into this namespace so skip the wildcard check.
 # pylint: disable=unused-wildcard-import,wildcard-import,ungrouped-imports
@@ -13,7 +13,10 @@ elif chip_id == ap_chip.STM32F405:
 elif chip_id == ap_chip.RP2040:
     from adafruit_blinka.microcontroller.rp2040.pin import *
 elif chip_id == ap_chip.BCM2XXX:
-    from adafruit_blinka.microcontroller.bcm283x.pin import *
+    if board_id in ["RASPBERRY_PI_4B", "RASPBERRY_PI_400", "RASPBERRY_PI_CM4",]:
+        from adafruit_blinka.microcontroller.bcm2711.pin import *
+    else:
+        from adafruit_blinka.microcontroller.bcm283x.pin import *
 elif chip_id == ap_chip.DRA74X:
     from adafruit_blinka.microcontroller.dra74x.pin import *
 elif chip_id == ap_chip.AM33XX: