예제 #1
0
    def begin(self):
        try:
            self.ctrl = SpiController(cs_count=1)
            s = 'ftdi://0x0403:0x6011/1'
            self.ctrl.configure(s)

            self.slave = self.ctrl.get_port(cs=0, freq=6E6, mode=3)
            self.slave.set_frequency(8000000)

            self.gpio = self.ctrl.get_gpio()
            self.gpio.set_direction(
                0x10, 0x10)  # direction for the fet controller is OUTPUT (1)
            self.gpio.write(0x10)
            time.sleep(0.1)
            # to account for using separate ports for power and comms:
            self.backup_ctrl = SpiController(cs_count=1)
            self.backup_gpio = None
            if not s == 'ftdi://0x0403:0x6011/1':
                backup_s = 'ftdi://0x0403:0x6011/1'
                self.backup_ctrl.configure(backup_s)
                self.backup_gpio = self.backup_ctrl.get_gpio()
                self.backup_gpio.set_direction(0x10, 0x10)
                self.backup_gpio.write(0x10)
                time.sleep(1)
        except Exception as err:
            print('Error in initialising the FTDI driver ...:', err)
예제 #2
0
	def __init__(self, spi_frequency=5e6):
		# Params
		self.spi_frequency = spi_frequency

		# SPI link
		self.spi = SpiController(cs_count=3)
		self.spi.configure('ftdi://ftdi:2232h/1')
		self.slave = self.spi.get_port(cs=2, freq=self.spi_frequency, mode=0)
예제 #3
0
    def __init__(self):
        # Instanciate a SPI controller
        self._spi = SpiController()
        # Configure the first interface (IF/1) of the FTDI device as a SPI master
        self._spi.configure('ftdi://ftdi:2232h/2')

        # Get a SPI port to a SPI slave w/ /CS on A*BUS3 and SPI mode 0 @ 12MHz
        self._slave = self._spi.get_port(cs=1, freq=500e3, mode=0)
예제 #4
0
 def test_usb_device(self):
     """Demo instanciation from an existing UsbDevice.
     """
     candidate = Ftdi.get_identifiers(self.ftdi_url)
     usbdev = UsbTools.get_device(candidate[0])
     spi = SpiController(cs_count=1)
     spi.configure(usbdev, interface=candidate[1])
     flash = SerialFlashManager.get_from_controller(spi, cs=0,
                                                    freq=self.frequency)
예제 #5
0
 def __init__(self, device='ftdi://ftdi:232h/1', debug=False):
     self.device_str = device
     self.debug = debug
     self.ctrl = SpiController(silent_clock=False)
     self.ctrl.configure(device, cs_count=1)
     self.spi = self.ctrl.get_port(cs=0, freq=100000)
     self.gpio = self.ctrl.get_gpio()
     direction = self.gpio.direction
     self.gpio.set_direction(0x30, 0x10) # Add reset as output
     self.gpio.write(0x10)
예제 #6
0
class SPI:
    MSB = 0

    def __init__(self):
        from pyftdi.spi import SpiController
        self._spi = SpiController(cs_count=1)
        self._spi.configure('ftdi://ftdi:ft232h/1')
        self._port = self._spi.get_port(0)
        print(self._port)
        dir(self._port)
        self._port.set_frequency(100000)
        self._port._cpol = 0
        self._port._cpha = 0
        # Change GPIO controller to SPI
        Pin.ft232h_gpio = self._spi.get_gpio()

    def init(self, baudrate=100000, polarity=0, phase=0, bits=8,
                  firstbit=MSB, sck=None, mosi=None, miso=None):
        self._port.set_frequency(baudrate)
        # FTDI device can only support mode 0 and mode 2
        # due to the limitation of MPSSE engine.
        # This means CPHA must = 0
        self._port._cpol = polarity
        if phase != 0:
            raise ValueError("Only SPI phase 0 is supported by FT232H.")
        self._port._cpha = phase

    @property
    def frequency(self):
        return self._port.frequency

    def write(self, buf, start=0, end=None):
        end = end if end else len(buf)
        chunks, rest = divmod(end - start, self._spi.PAYLOAD_MAX_LENGTH)
        for i in range(chunks):
            chunk_start = start + i * self._spi.PAYLOAD_MAX_LENGTH
            chunk_end = chunk_start + self._spi.PAYLOAD_MAX_LENGTH
            self._port.write(buf[chunk_start:chunk_end])
        if rest:
            self._port.write(buf[-1*rest:])

    def readinto(self, buf, start=0, end=None, write_value=0):
        end = end if end else len(buf)
        result = self._port.read(end-start)
        for i, b in enumerate(result):
            buf[start+i] = b

    def write_readinto(self, buffer_out, buffer_in,  out_start=0, out_end=None, in_start=0, in_end=None):
        out_end = out_end if out_end else len(buffer_out)
        in_end = in_end if in_end else len(buffer_in)
        result = self._port.exchange(buffer_out[out_start:out_end],
                                     in_end-in_start, duplex=True)
        for i, b in enumerate(result):
            buffer_in[in_start+i] = b
예제 #7
0
 def get_flash_device(url, cs=0, freq=None):
     """Obtain an instance of the detected flash device"""
     ctrl = SpiController(silent_clock=False)
     ctrl.configure(url)
     spi = ctrl.get_port(cs, freq)
     jedec = SerialFlashManager.read_jedec_id(spi)
     if not jedec:
         # it is likely that the latency setting is too low if this
         # condition is encountered
         raise SerialFlashUnknownJedec("Unable to read JEDEC Id")
     flash = SerialFlashManager._get_flash(spi, jedec)
     flash.set_spi_frequency(freq)
     return flash
예제 #8
0
 def get_flash_device(url, cs=0, freq=None):
     """Obtain an instance of the detected flash device"""
     ctrl = SpiController(silent_clock=False)
     ctrl.configure(url)
     spi = ctrl.get_port(cs, freq)
     jedec = SerialFlashManager.read_jedec_id(spi)
     if not jedec:
         # it is likely that the latency setting is too low if this
         # condition is encountered
         raise SerialFlashUnknownJedec("Unable to read JEDEC Id")
     flash = SerialFlashManager._get_flash(spi, jedec)
     flash.set_spi_frequency(freq)
     return flash
예제 #9
0
def initialize():
    global spi
    global gpio

    # Configure controller with one CS
    ctrl = SpiController(cs_count=1, turbo=True)

    # ctrl.configure('ftdi:///?')  # Use this if you're not sure which device to use
    # Windows users: make sure you've loaded libusb-win32 using Zadig
    try:
        ctrl.configure(config.FTDI_URL)
    except:
        print("Can't configure FTDI. Possible reasons:")
        print(
            "    1. As a current Linux user you don't have an access to the device.\n"
            "        Solution: https://eblot.github.io/pyftdi/installation.html\n"
            "    2. You use the wrong FTDI URL. Replace FTDI_URL in config.py with one of the following:\n"
        )
        ctrl.configure('ftdi:///?')
        sys.exit(1)

    # Get SPI slave
    # CS0, 10MHz, Mode 0 (CLK is low by default, latch on the rising edge)
    spi = ctrl.get_port(cs=0, freq=10E6, mode=0)

    # Get GPIO
    gpio = ctrl.get_gpio()
    gpio.set_direction(0x10, 0x10)
예제 #10
0
def setConfigure(ftdi_interface, ccp_header_file):
    spi = SpiController()
    spi.configure(ftdi_interface)
    global slave
    slave = spi.get_port(cs=0, freq=12E6, mode=0)

    global ccp_header
    ccp_header = cpp_header_parser.read_header(ccp_header_file)

    def makeup_data(keylist):
        return [ReadFromCppHeader(key) for key in keylist]

    blockWrite(ccp_header["gpo_data_15_to_0"], makeup_data(configDesignVals))
    blockWrite(ccp_header["port_cfg_00"], makeup_data(portConfigDesignVals))
    blockWrite(ccp_header["dac_data_port_00"], makeup_data(dacDesignVals))
예제 #11
0
class SPI:
    MSB = 0

    def __init__(self):
        from pyftdi.spi import SpiController
        self._spi = SpiController(cs_count=1)
        self._spi.configure('ftdi:///1')
        self._port = self._spi.get_port(0)
        self._port.set_frequency(100000)
        self._port._cpol = 0
        self._port._cpha = 0
        # Change GPIO controller to SPI
        Pin.ft232h_gpio = self._spi.get_gpio()

    def init(self, baudrate=100000, polarity=0, phase=0, bits=8,
                  firstbit=MSB, sck=None, mosi=None, miso=None):
        self._port.set_frequency(baudrate)
        self._port._cpol = polarity
        self._port._cpha = phase

    @property
    def frequency(self):
        return self._port.frequency

    def write(self, buf, start=0, end=None):
        end = end if end else len(buf)
        chunks, rest = divmod(end - start, self._spi.PAYLOAD_MAX_LENGTH)
        for i in range(chunks):
            chunk_start = start + i * self._spi.PAYLOAD_MAX_LENGTH
            chunk_end = chunk_start + self._spi.PAYLOAD_MAX_LENGTH
            self._port.write(buf[chunk_start:chunk_end])
        if rest:
            self._port.write(buf[-1*rest:])

    def readinto(self, buf, start=0, end=None, write_value=0):
        end = end if end else len(buf)
        result = self._port.read(end-start)
        for i, b in enumerate(result):
            buf[start+i] = b

    def write_readinto(self, buffer_out, buffer_in,  out_start=0, out_end=None, in_start=0, in_end=None):
        out_end = out_end if out_end else len(buffer_out)
        in_end = in_end if in_end else len(buffer_in)
        result = self._port.exchange(buffer_out[out_start:out_end],
                                     in_end-in_start, duplex=True)
        for i, b in enumerate(result):
            buffer_in[in_start+i] = b
예제 #12
0
class PanelControl(object):

	def __init__(self, spi_frequency=5e6):
		# Params
		self.spi_frequency = spi_frequency

		# SPI link
		self.spi = SpiController(cs_count=3)
		self.spi.configure('ftdi://ftdi:2232h/1')
		self.slave = self.spi.get_port(cs=2, freq=self.spi_frequency, mode=0)

	def reg_w16(self, reg, v):
		self.slave.exchange([reg, v >> 8, v & 0xff])

	def reg_w8(self, reg, v):
		self.slave.exchange([reg, v])

	def read_status(self):
		rv = self.slave.exchange([0x00, 0x00], duplex=True)
		return rv[0] | rv[1]

	def send_data(self, data):
		self.slave.exchange([0x80] + data)

	def send_frame(self, frame, width=64, height=64, bits=16):
		# Size of a line
		ll = width * bits // 8

		# Scan all line
		for y in range(height):
			# Send write command to line buffer
			self.send_data(list(frame[y*ll:(y+1)*ll]))

			# Swap line buffer & Write it to line y of back frame buffer
			self.reg_w8(0x03, y)

		# Send frame swap command
		panel.reg_w8(0x04, 0x00)

		# Wait for the frame swap to occur
		while (panel.read_status() & 0x02 == 0):
			pass
예제 #13
0
class SpiAccelTest(object):
    """Basic test for an ADXL345 device selected as CS1,
       SPI mode 3
    """
    def __init__(self):
        self._spi = SpiController()

    def open(self):
        """Open an I2c connection to a slave"""
        self._spi.configure('ftdi://ftdi:2232h/1')

    def read_device_id(self):
        port = self._spi.get_port(1, freq=6E6, mode=3)
        device_id = port.exchange([0x00], 1).tobytes()
        hex_device_id = hexlify(device_id).decode()
        print('DEVICE ID:', hex_device_id)
        return hex_device_id

    def close(self):
        """Close the I2C connection"""
        self._spi.terminate()
예제 #14
0
class SpiDataFlashTest(object):
    """Basic test for a MX25L1606E data flash device selected as CS0,
       SPI mode 0
    """
    def __init__(self):
        self._spi = SpiController()

    def open(self):
        """Open an I2c connection to a slave"""
        self._spi.configure('ftdi://ftdi:2232h/1')

    def read_jedec_id(self):
        port = self._spi.get_port(0, freq=3E6, mode=0)
        jedec_id = port.exchange([0x9f], 3).tobytes()
        hex_jedec_id = hexlify(jedec_id).decode()
        print('JEDEC ID:', hex_jedec_id)
        return hex_jedec_id

    def close(self):
        """Close the I2C connection"""
        self._spi.terminate()
예제 #15
0
class EpdFtdiPort:
    """
    """

    DC_PIN = 1 << 5
    RESET_PIN = 1 << 6
    BUSY_PIN = 1 << 7
    I_PINS = BUSY_PIN
    O_PINS = DC_PIN | RESET_PIN
    IO_PINS = I_PINS | O_PINS

    def __init__(self, debug=False):
        self._debug = debug
        self._spi = SpiController(cs_count=2)
        self._spi_port = None
        self._io_port = None
        self._io = 0

    def open(self, url=None):
        """Open an SPI connection to a slave"""
        url = environ.get('FTDI_DEVICE', url or 'ftdi:///1')
        self._spi.configure(url, debug=self._debug)
        self._spi_port = self._spi.get_port(0, freq=10E6, mode=0)
        self._io_port = self._spi.get_gpio()
        self._io_port.set_direction(self.IO_PINS, self.O_PINS)

    def close(self):
        """Close the SPI connection"""
        self._spi.terminate()

    def reset(self):
        self._io = self.RESET_PIN
        self._io_port.write(self._io)
        sleep(0.2)
        self._io = 0
        self._io_port.write(self._io)
        sleep(0.2)
        self._io = self.RESET_PIN
        self._io_port.write(self._io)
        sleep(0.2)

    def write_command(self, cmd):
        if isinstance(cmd, int):
            data = bytes([cmd])
        self._io &= ~self.DC_PIN
        self._io_port.write(self._io)
        self._spi_port.write(data)

    def write_data(self, data):
        if isinstance(data, int):
            data = bytes([data])
        self._io |= self.DC_PIN
        self._io_port.write(self._io)
        self._spi_port.write(data)

    def wait_ready(self):
        start = now()
        while self._io_port.read() & self.BUSY_PIN:
            sleep(0.05)
        return now() - start
예제 #16
0
    def __init__(self, ftdi_url, spi_freq=1E6):
        """Configure the FTDI interface.

        Keyword arguments:
         ftdi_url -- device url, which can be obtained by Ftdi.show_devices()
         freq -- SPI frequency up to 8E6 (for FPGA running on 64 MHz)
        """
        # Configure SPI master
        self._spi_ctrl = SpiController()
        self._spi_ctrl.configure(ftdi_url + '2') # second port - channel B
        self._spi_port = self._spi_ctrl.get_port(cs=0, freq=spi_freq, mode=0)
        # Configure FPGA logic reset (ICE_RESET_FT)
        self._spi_gpio = self._spi_ctrl.get_gpio()
        self._spi_gpio.set_direction(1 << self.GPIO_RESET_LOGIC_POS, 1 << self.GPIO_RESET_LOGIC_POS)
        self._spi_gpio.write(0)
        # Configure FPGA configuration reset (ICE_RESET)
        self._gpio_ctrl = GpioAsyncController()
        self._gpio_ctrl.configure(ftdi_url + '1', # first port - channel A
                                  direction=(1 << self.GPIO_RESET_CONFIG_POS),
                                  frequency=1e6,
                                  initial=(1 << self.GPIO_RESET_CONFIG_POS))
        self._gpio_ctrl.write(1 << self.GPIO_RESET_CONFIG_POS)
예제 #17
0
class SpiRfda2125Test(object):
    """Basic test for a RFDA2125 Digital Controlled Variable Gain Amplifier
       selected as CS2,
       SPI mode 0
    """
    def __init__(self):
        self._spi = SpiController()

    def open(self):
        """Open an I2c connection to a slave"""
        self._spi.configure('ftdi://ftdi:2232h/1')
        self._port = self._spi.get_port(2, freq=1E6, mode=0)

    def change_attenuation(self, value):
        if not (0.0 <= value <= 31.5):
            print('Out-of-bound attenuation', file=stderr)
        intval = 63 - int(value * 2)
        self._port.write(bytes([intval]), 1)

    def close(self):
        """Close the I2C connection"""
        self._spi.terminate()
예제 #18
0
파일: spi.py 프로젝트: eblot/pyftdi
class SpiAccelTest(object):
    """Basic test for an ADXL345 device selected as CS1,
       SPI mode 3
    """

    def __init__(self):
        self._spi = SpiController(cs_count=3)

    def open(self):
        """Open an SPI connection to a slave"""
        url = environ.get('FTDI_DEVICE', 'ftdi://ftdi:2232h/1')
        self._spi.configure(url)

    def read_device_id(self):
        port = self._spi.get_port(1, freq=6E6, mode=3)
        device_id = port.exchange([0x00], 1).tobytes()
        hex_device_id = hexlify(device_id).decode()
        print('DEVICE ID:', hex_device_id)
        return hex_device_id

    def close(self):
        """Close the SPI connection"""
        self._spi.terminate()
예제 #19
0
파일: spi.py 프로젝트: eblot/pyftdi
class SpiDataFlashTest(object):
    """Basic test for a MX25L1606E data flash device selected as CS0,
       SPI mode 0
    """

    def __init__(self):
        self._spi = SpiController(cs_count=3)

    def open(self):
        """Open an SPI connection to a slave"""
        url = environ.get('FTDI_DEVICE', 'ftdi://ftdi:2232h/1')
        self._spi.configure(url)

    def read_jedec_id(self):
        port = self._spi.get_port(0, freq=3E6, mode=0)
        jedec_id = port.exchange([0x9f], 3).tobytes()
        hex_jedec_id = hexlify(jedec_id).decode()
        print('JEDEC ID:', hex_jedec_id)
        return hex_jedec_id

    def close(self):
        """Close the SPI connection"""
        self._spi.terminate()
예제 #20
0
파일: spi.py 프로젝트: eblot/pyftdi
class SpiRfda2125Test(object):
    """Basic test for a RFDA2125 Digital Controlled Variable Gain Amplifier
       selected as CS2,
       SPI mode 0
    """

    def __init__(self):
        self._spi = SpiController(cs_count=3)

    def open(self):
        """Open an SPI connection to a slave"""
        url = environ.get('FTDI_DEVICE', 'ftdi://ftdi:2232h/1')
        self._spi.configure(url)
        self._port = self._spi.get_port(2, freq=1E6, mode=0)

    def change_attenuation(self, value):
        if not (0.0 <= value <= 31.5):
            print('Out-of-bound attenuation', file=stderr)
        intval = 63-int(value*2)
        self._port.write(bytes([intval]), 1)

    def close(self):
        """Close the SPI connection"""
        self._spi.terminate()
예제 #21
0
파일: spi.py 프로젝트: eblot/pyftdi
class SpiGpioTestCase(unittest.TestCase):
    """Basic test for GPIO access w/ SPI mode

       It expects the following I/O setup:

       AD4 connected t0 AC0
       AD5 connected t0 AC1
       AD6 connected t0 AC2
       AD7 connected t0 AC3
    """

    # AD0: SCLK, AD1: MOSI, AD2: MISO, AD3: /CS
    AD_OFFSET = 4
    AC_OFFSET = 8
    PIN_COUNT = 4

    def setUp(self):
        self._spi = SpiController(cs_count=1)
        url = environ.get('FTDI_DEVICE', 'ftdi://ftdi:2232h/1')
        self._spi.configure(url)
        self._port = self._spi.get_port(0, freq=1E6, mode=0)
        self._io = self._spi.get_gpio()

    def tearDown(self):
        """Close the SPI connection"""
        self._spi.terminate()

    def test_ac_to_ad(self):
        ad_pins = ((1 << self.PIN_COUNT) - 1) << self.AD_OFFSET  # input
        ac_pins = ((1 << self.PIN_COUNT) - 1) << self.AC_OFFSET  # output
        io_pins = ad_pins | ac_pins

        def ac_to_ad(ac_output):
            ac_output &= ac_pins
            ac_output >>= self.AC_OFFSET - self.AD_OFFSET
            return ac_output & ad_pins

        self._io.set_direction(io_pins, ac_pins)
        for ac in range(1 << self.PIN_COUNT):
            ac_out = ac << self.AC_OFFSET
            ad_in = ac_to_ad(ac_out)
            self._io.write(ac_out)
            # random SPI exchange to ensure SPI does not change GPIO
            self._port.exchange([0x00, 0xff], 2)
            rd = self._io.read()
            self.assertEqual(rd, ad_in)
        self.assertRaises(SpiIOError, self._io.write, ad_pins)

    def test_ad_to_ac(self):
        ad_pins = ((1 << self.PIN_COUNT) - 1) << self.AD_OFFSET  # output
        ac_pins = ((1 << self.PIN_COUNT) - 1) << self.AC_OFFSET  # input
        io_pins = ad_pins | ac_pins

        def ad_to_ac(ad_output):
            ad_output &= ad_pins
            ad_output <<= self.AC_OFFSET - self.AD_OFFSET
            return ad_output & ac_pins

        self._io.set_direction(io_pins, ad_pins)
        for ad in range(1 << self.PIN_COUNT):
            ad_out = ad << self.AD_OFFSET
            ac_in = ad_to_ac(ad_out)
            self._io.write(ad_out)
            # random SPI exchange to ensure SPI does not change GPIO
            self._port.exchange([0x00, 0xff], 2)
            rd = self._io.read()
            self.assertEqual(rd, ac_in)
        self.assertRaises(SpiIOError, self._io.write, ac_pins)
예제 #22
0
 def __init__(self, vendor, product, interface=1):
     self._ctrl = SpiController(silent_clock=False)
     self._ctrl.configure(vendor, product, interface)
예제 #23
0
class SerialFlashManager(object):
    """Serial flash manager.

       Automatically detects and instanciate the proper flash device class
       based on the JEDEC identifier which is read out from the device itself.
    """

    CMD_JEDEC_ID = 0x9F

    def __init__(self, vendor, product, interface=1):
        self._ctrl = SpiController(silent_clock=False)
        self._ctrl.configure(vendor, product, interface)

    def get_flash_device(self, cs=0):
        """Obtain an instance of the detected flash device"""
        spi = self._ctrl.get_port(cs)
        jedec = SerialFlashManager.read_jedec_id(spi)
        if not jedec:
            # it is likely that the latency setting is too low if this
            # condition is encountered
            raise SerialFlashUnknownJedec("Unable to read JEDEC Id")
        return SerialFlashManager._get_flash(spi, jedec)

    @staticmethod
    def read_jedec_id(spi):
        """Read flash device JEDEC identifier (3 bytes)"""
        jedec_cmd = Array('B', [SerialFlashManager.CMD_JEDEC_ID])
        return spi.exchange(jedec_cmd, 3).tostring()

    @staticmethod
    def _get_flash(spi, jedec):
		contents = sys.modules[__name__].__dict__
		devices = []
		plugin_dir = os.path.dirname(os.path.realpath(__file__))
		plugin_files = [x[:-3] for x in os.listdir(plugin_dir) if (x.endswith(".py") and not x.startswith("__init__"))]
		sys.path.insert(0, plugin_dir)
		for p in plugin_files:
			mod = __import__(p)
#			print "mod: " + str(mod)

			sf = getattr(mod, "SerialFlash")

#			for sp in sf.__subclasses__():
#				print "subclass: " + str(sp)


#			print "plugin file: " + str(p)
#			print "sf: " + str(sf)

			for name in dir (mod):
				if name.startswith('_'):
					continue
				obj = getattr(mod, name)

				if obj not in sf.__subclasses__():
					continue

#				for p in SerialFlash.__subclasses__():
#					print "subclass: " + p

#				print "name: " + name
#				print "base classes: " + str(inspect.getmro(obj))


#				print "\tclass: %s is class %s" % (str(obj), str(SerialFlash))

#				if not isinstance (obj, SerialFlash):
#					continue


#				if ("SerialFlash" in str(inspect.getmro(obj))):
#					print "found subclass"
#
#				for bn in inspect.getmro(obj):
#					print "base name: " + str(bn)

#
#				print "type: " + str(type(obj))
#				print "sf type: " + str(type(SerialFlash))
#

#				if  isclass(obj) and issubclass(obj, SerialFlash) and obj is not SerialFLash :
#				if  isclass(obj) and "SerialFLash" not in str(obj) :
#				print "\t\tappend name: " + name
				devices.append(obj)


		for device in devices:
			if device.match(jedec):
				return device(spi, jedec)
		raise SerialFlashUnknownJedec(jedec)
예제 #24
0
파일: spi.py 프로젝트: eblot/pyftdi
 def __init__(self):
     self._spi = SpiController(cs_count=3)
예제 #25
0
파일: spi.py 프로젝트: eblot/pyftdi
 def setUp(self):
     self._spi = SpiController(cs_count=1)
     url = environ.get('FTDI_DEVICE', 'ftdi://ftdi:2232h/1')
     self._spi.configure(url)
     self._port = self._spi.get_port(0, freq=1E6, mode=0)
     self._io = self._spi.get_gpio()