Exemple #1
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
Exemple #2
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)
Exemple #3
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
Exemple #4
0
def ftdi_spi(device='ftdi://::/1', bus_speed_hz=12000000, gpio_CS=3, gpio_DC=5, gpio_RST=6,
        reset_hold_time=0, reset_release_time=0):
    """
    Bridges an `SPI <https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus>`_
    (Serial Peripheral Interface) bus over an FTDI USB device to provide :py:func:`data` and
    :py:func:`command` methods.

    :param device: A URI describing the location of the FTDI device. If ``None`` is
        supplied (default), ``ftdi://::/1`` is used. See `pyftdi <https://pypi.org/project/pyftdi>`_
        for further details of the naming scheme used.
    :type device: string
    :param bus_speed_hz: SPI bus speed, defaults to 12MHz.
    :type bus_speed_hz: int
    :param gpio_CS: The ADx pin to connect chip select (CS) to (defaults to 3).
    :type gpio_CS: int
    :param gpio_DC: The ADx pin to connect data/command select (DC) to (defaults to 5).
    :type gpio_DC: int
    :param gpio_RST: The ADx pin to connect reset (RES / RST) to (defaults to 6).
    :type gpio_RST: int
        :param reset_hold_time: The number of seconds to hold reset active. Some devices may require
        a duration of 100ms or more to fully reset the display (default:0)
    :type reset_hold_time: float
    :param reset_release_time: The number of seconds to delay afer reset. Some devices may require
        a duration of 150ms or more after reset was triggered before the device can accept the
        initialization sequence (default:0)
    :type reset_release_time: float

    .. versionadded:: 1.9.0
    """
    from pyftdi.spi import SpiController

    controller = SpiController(cs_count=1)
    controller.configure(device)

    slave = controller.get_port(cs=gpio_CS - 3, freq=bus_speed_hz, mode=0)
    gpio = controller.get_gpio()

    # RESET and DC configured as outputs
    pins = _ftdi_pin(gpio_RST) | _ftdi_pin(gpio_DC)
    gpio.set_direction(pins, pins & 0xFF)

    serial = spi(
        __FTDI_WRAPPER_SPI(controller, slave),
        __FTDI_WRAPPER_GPIO(gpio),
        gpio_DC=gpio_DC,
        gpio_RST=gpio_RST,
        reset_hold_time=reset_hold_time,
        reset_release_time=reset_release_time)
    serial._managed = True
    return serial
Exemple #5
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
Exemple #6
0
def ftdi_spi(device='ftdi://::/1',
             bus_speed_hz=12000000,
             gpio_CS=3,
             gpio_DC=5,
             gpio_RST=6):
    """
    Bridges an `SPI <https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus>`_
    (Serial Peripheral Interface) bus over an FTDI USB device to provide :py:func:`data` and
    :py:func:`command` methods.

    :param device: A URI describing the location of the FTDI device. If ``None`` is
        supplied (default), ``ftdi://::/1`` is used. See `pyftdi <https://pypi.python.org/pypi/pyftdi>`_
        for further details of the naming scheme used.
    :type device: string
    :param bus_speed_hz: SPI bus speed, defaults to 12MHz.
    :type bus_speed_hz: int
    :param gpio_CS: The ADx pin to connect chip select (CS) to (defaults to 3).
    :type gpio_CS: int
    :param gpio_DC: The ADx pin to connect data/command select (DC) to (defaults to 5).
    :type gpio_DC: int
    :param gpio_RST: The ADx pin to connect reset (RES / RST) to (defaults to 6).
    :type gpio_RST: int

    .. versionadded:: 1.9.0
    """
    from pyftdi.spi import SpiController

    controller = SpiController(cs_count=1)
    controller.configure(device)

    slave = controller.get_port(cs=gpio_CS - 3, freq=bus_speed_hz, mode=0)
    gpio = controller.get_gpio()

    # RESET and DC configured as outputs
    pins = _ftdi_pin(gpio_RST) | _ftdi_pin(gpio_DC)
    gpio.set_direction(pins, pins & 0xFF)

    serial = spi(__FTDI_WRAPPER_SPI(controller, slave),
                 __FTDI_WRAPPER_GPIO(gpio),
                 gpio_DC=gpio_DC,
                 gpio_RST=gpio_RST)
    serial._managed = True
    return serial
Exemple #7
0
class KU10405:
    """This class controls the KU10405 chip via an FT232H SPI controller. Users should only concern
    themselves with the constructor and set_tap method.

    Example usage:
        from ku10405 import KU10405
        dut = KU10405()
        dut.set_tap(0, 100, 200)
    """

    # The apply pin both applies changes and indicates trim bits being set. When CS is high, a
    # rising edge of the apply pin indicates that settings should be applied. Trim bits are
    # programmed when the apply pin is high while CS is low during the rising edge of the first
    # clock cycle.
    APPLY_PIN = 7

    def __init__(self, readback=True, ftdi_url='ftdi://ftdi:232h/1'):
        """An FT232H chip must be connected to your computer for this constructor to succeed. It
        sets up the SPI/GPIO interface and configures readback.

        When readback is enabled, set_tap will throw an error if the readback of any write does not
        match what was programmed. Readback does have an impact on performance because it requires
        an extra SPI transaction for every call to set_tap, but for safety it should not be disabled
        unless speed is your top priority. Note that for readback to work, the appropriate switch on
        the board must be set.

        Args:
            readback (bool, optional): Set to False to disable readback error checking
        """
        self._readback = readback

        # Setup SPI interface
        self._spi_controller = SpiController()
        self._spi_controller.configure(ftdi_url)
        self._spi = self._spi_controller.get_port(cs=0, mode=0, freq=1e6)

        # Setup GPIO interface
        self._gpio = self._spi_controller.get_gpio()
        self._gpio.set_direction(1 << self.APPLY_PIN,
                                 1 << self.APPLY_PIN)  # make APLS an output
        self._set_apply(False)

        # We're always going to use address 0. This is here just in case that changes.
        self._addr = 0

    def set_tap(self, channel, mag, phase, enable=True, apply=True):
        """Sets the tap at the given channel to the given magnitude and phase. Magnitude and phase
        values control bits, not logical values. Disable the channel by setting enable to False.
        Changes are only applied if apply is set to True. The end result is the same if you always
        set apply to True or only set it to True on the last call of a sequence.

        Args:
            channel (int): Which channel to set (range: [0-3])
            mag (int): 14-bit attenuation (range: [0, 2^14))
            phase (int): 16-bit phase (range: [0, 2^16))
            enable (bool, optional): Set to False to disable this channel
            apply (bool, optional): Set to False if you don't want to apply these changes yet

        Raises:
            TypeError: raised if any arguments have the wrong type
            ValueError: raised if any arguments are out-of-range
        """
        if not all(isinstance(val, int) for val in (channel, mag, phase)):
            raise TypeError('Channel, magnitude, and phase must be integers')
        if not all(isinstance(val, bool) for val in (enable, apply)):
            raise TypeError('Enable and apply must be bools')
        if not 0 <= channel <= 3:
            raise ValueError('Address and channel must be in range [0, 3]')
        if not 0 <= mag <= 16383:
            raise ValueError('Magnitude must be in range [0, 16383]')
        if not 0 <= phase <= 65535:
            raise ValueError('Phase must be in range [0, 65535]')

        coarse_write, _ = \
            self._write(channel, 'coarse', mag >> 9, phase >> 11, enable)
        fine_write, coarse_read = \
            self._write(channel, 'fine', (mag >> 4) & 0x1F, (phase >> 5) & 0x3F)
        trim_write, fine_read = \
            self._write(channel, 'trim', mag & 0xF, phase & 0x1F)

        if self._readback:
            # Do a dummy write so we can get the trim readback. Note that this assumes we are not
            # using address 3!
            trim_read = self._spi.exchange((0xFFFF).to_bytes(2,
                                                             byteorder='big'),
                                           duplex=True)
            trim_read = (trim_read[0] << 8) | trim_read[1]

            if coarse_write != coarse_read or fine_write != fine_read or trim_write != trim_read:
                raise IOError(
                    'Readbacks do not match! Wrote coarse {}, fine {}, trim {}, but '
                    'read coarse {}, fine {}, trim {}'.format(
                        coarse_write, fine_write, trim_write, coarse_read,
                        fine_read, trim_read))

        if apply:
            self._set_apply(True)
            self._set_apply(False)

    def _write(self, channel, reg_type, mag, phase, enable=None):
        if reg_type not in ('coarse', 'fine', 'trim'):
            raise ValueError('Unrecognized register type {}'.format(reg_type))
        if reg_type == 'coarse' and enable is None:
            raise ValueError('Enable must be specified for coarse write')
        if reg_type in ('coarse', 'fine') and not 0 <= mag <= 31:
            raise ValueError('Coarse/fine magnitudes must be in range [0, 31]')
        if reg_type == 'trim' and not 0 <= mag <= 15:
            raise ValueError('Trim magnitude must be in range [0, 15]')
        if reg_type in ('coarse', 'trim') and not 0 <= phase <= 31:
            raise ValueError('Coarse/trim phases must be in range [0, 31]')
        if reg_type == 'fine' and not 0 <= phase <= 63:
            raise ValueError('Fine phase must be in range [0, 63]')

        wdata = (self._addr << 14) | (channel << 12)
        if reg_type == 'coarse':
            wdata |= (enable << 10) | (phase << 5) | mag
        elif reg_type == 'fine':
            wdata |= (1 << 11) | (phase << 5) | mag
        else:  # trim
            wdata |= (phase << 5) | mag
            self._set_apply(True)

        rdata = self._spi.exchange(wdata.to_bytes(2, byteorder='big'),
                                   duplex=True)
        rdata = (rdata[0] << 8) | rdata[1]

        if reg_type == 'trim':
            self._set_apply(False)

        return wdata, rdata

    def _set_apply(self, value):
        if not isinstance(value, bool):
            raise TypeError('GPIO value must be a bool.')
        self._gpio.write(value << self.APPLY_PIN)
Exemple #8
0
class FtdiDevice:
    """FTDI FT2232H SPI master to access FPGA Control/Status Registers (CSR)
    plus some GPIO control.

    SPI parameters:
      - FTDI channel B:
        * BDBUS0 - SCLK
        * BDBUS1 - MOSI
        * BDBUS2 - MISO
        * BDBUS3 - CSn
      - slave;
      - mode 0 only;
      - most significant bit transmitted first;
      - byte order from high to low;
      - SCK frequency must at least 8 lower than system frequency.

    There are 2 types of SPI transactions:
      - incremental burst -- address increments internally after every data (array)
      - fixed burst -- one adreess, multiple data (FIFO)
    Transaction is done with 8-bit address and 16 bit data words.
    Transaction format:
      - control word (3 bytes):
          * bit 23 -- write (1) or read (0)
          * bit 22 -- burst incremental (1) or fixed (0)
          * bit 21 .. 8 -- 14 bit length (0 - 1 data word, 1 - 2 data words, etc)
          * bits 7 .. 0 -- 8 bit address
      - data word (2 bytes) 1 .. N:
          * bits 15 .. 0 -- data to be written or readen

    GPIO parameters:
      - ADBUS7 - output - active low reset for FPGA configuration (ICE_RESET)
      - BDBUS7 - output - active high reset for FPGA logic (ICE_RESET_FT)
    """

    GPIO_RESET_LOGIC_POS = 7
    GPIO_RESET_CONFIG_POS = 7

    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)

    def _int_to_bytes(self, i, length=2):
        """Convert integer to bytes"""
        return int.to_bytes(i, length=length, byteorder='big', signed=False)

    def _words_to_bytes(self, words_list):
        """Convert list with 16 bit words to bytes"""
        bytes_str_list = [self._int_to_bytes(w, length=2) for w in words_list]
        return b''.join(bytes_str_list)  # concatenate all strings

    def _bytes_to_words(self, bytes_str):
        """Convert bytes string to list with 16 bit words"""
        return [
            int.from_bytes(bytes_str[b * 2:b * 2 + 2],
                           byteorder='big',
                           signed=False) for b in range(len(bytes_str) // 2)
        ]

    def _prepare_ctrl_word(self, addr, len, burst, wr):
        """Prepare control word for exchange.

           Keyword arguments:
             addr -- 8 bit address
             len -- number of 16 bit data words to write/read (2^14 max)
             burst -- 'fixed' address the same for every data, 'incr' - address + 1 for every next data word
             wr -- 1 for write operation, 0 - for read
        """
        ctrl_word = 0
        ctrl_word |= (wr << 23)
        ctrl_word |= ((burst == 'incr') << 22)
        ctrl_word |= (((len - 1) & 0x3FFF) << 8)
        ctrl_word |= ((addr & 0xFF) << 0)
        return ctrl_word

    def spi_read(self, addr, len=1, burst='fixed'):
        """Read data from address via SPI.

           Keyword arguments:
             addr -- 8 bit address
             len -- number of 16 bit data words to write/read (2^14 max)
             burst -- 'fixed' address the same for every data, 'incr' - address + 1 for every next data word
           Return:
             list of size 'len' with 16 bit data words
        """
        ctrl_word = self._prepare_ctrl_word(addr, len, burst, wr=0)
        rbytes = self._spi_port.exchange(self._int_to_bytes(ctrl_word, 3),
                                         len * 2)
        return self._bytes_to_words(rbytes)

    def spi_write(self, addr, data, burst='fixed'):
        """Write data to address via SPI.

           Keyword arguments:
             addr -- 8 bit address
             data -- list with 16 bit data words to write (list length 2^14 max)
             burst -- 'fixed' address the same for every data, 'incr' - address + 1 for every next data word
         """
        ctrl_word = self._prepare_ctrl_word(addr, len(data), burst, wr=1)
        wbytes = self._int_to_bytes(ctrl_word, 3) + self._words_to_bytes(data)
        self._spi_port.exchange(wbytes)

    def reset_logic_on(self):
        """Activate reset pin ICE_RESET_FT"""
        self._spi_gpio.write((1 << self.GPIO_RESET_LOGIC_POS)
                             | self._spi_gpio.read())

    def reset_logic_off(self):
        """Deactivate reset pin ICE_RESET_FT"""
        self._spi_gpio.write(~(1 << self.GPIO_RESET_LOGIC_POS)
                             & self._spi_gpio.read())

    def reset_config_on(self):
        """Activate reset pin ICE_RESET"""
        self._gpio_ctrl.write(~(1 << self.GPIO_RESET_CONFIG_POS)
                              & self._gpio_ctrl.read())

    def reset_config_off(self):
        """Deactivate reset pin ICE_RESET"""
        self._gpio_ctrl.write((1 << self.GPIO_RESET_LOGIC_POS)
                              | self._gpio_ctrl.read())

    def close_connection(self):
        """Close FTDI interface"""
        self._spi_ctrl.terminate()
        self._gpio_ctrl.close()
Exemple #9
0
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)
Exemple #10
0
def main():
    done_stdin = False
    parser = argparse.ArgumentParser(
        prog="spitest",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        usage=USAGE,
        description=__doc__)
    parser.add_argument('--version',
                        action='store_true',
                        help='Show version and exit')
    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        help='Verbose output during processing')
    parser.add_argument(
        '-f',
        '--flippy',
        action='store_true',
        help='Flip the SPI/JTAG control GPIO 10 times and exit')
    parser.add_argument(
        '-l',
        '--length',
        type=int,
        action='store',
        help='Construct and send a message of specified length')
    parser.add_argument('-j',
                        '--jtag',
                        action='store_true',
                        help='Set SPI/JTAG control to JTAG and exit')
    parser.add_argument('message',
                        nargs='*',
                        metavar='input',
                        default='1234',
                        help='message to send in 4 byte chunks')
    args = parser.parse_args()

    if args.version:
        show_and_exit(__file__, ["pyftdi"])

    if (args.verbose):
        log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
    else:
        log.basicConfig(format="%(levelname)s: %(message)s")

    # Instanciate a SPI controller
    spi = SpiController(cs_count=1)

    # interfaces start from 1 here, so this is Channel A (called 0 in jtag)
    spi.configure('ftdi://ftdi:2232h/1')

    # Get a port to a SPI slave w/ /CS on A*BUS3 and SPI mode 0 @ 1MHz
    slave = spi.get_port(cs=0, freq=1E6, mode=0)

    # Get GPIO port to manage extra pins
    # BUS4 = JTAG TRST_N, BUS5 = JTAG SRST_N, BUS6 = JTAG_SPIN
    # Note: something makes FTDI default to BUS6 low, selected that for SPI
    # otherwise SRST being default low holds the chip in reset
    # pyftdi Set Direction also forces the output to zero
    # so initially make SRST an input w/pullup in FPGA in case SPI/JTAG was
    # initially JTAG
    gpio = spi.get_gpio()
    gpio.set_direction(0x40, 0x40)
    time.sleep(1)
    gpio.set_direction(0x70, 0x70)

    if args.jtag:
        gpio.write(0x70)
        return

    gpio.write(0x30)

    if args.flippy:
        for i in range(10):
            print("Select SPI")
            gpio.write(0x30)
            time.sleep(2)
            print("Select JTAG")
            gpio.write(0x70)
            time.sleep(2)
        return

    print("Select SPI")
    gpio.write(0x30)
    # Synchronous exchange with the remote SPI slave
    if args.length:
        s = ''
        for i in range(args.length):
            s += hex(i & 15)[-1]
    else:
        s = ''
        for m in args.message:
            s += m + ' '
            s = s[:-1]  # remove extra space put on end
        # pad to ensure multiple of 4 bytes
        filled = len(s) % 4
        if filled:
            s += '....'[filled:]

    while len(s):
        write_buf = bytes(s[:4], encoding='utf8')
        read_buf = slave.exchange(write_buf, duplex=True).tobytes()
        print("Got " + str(read_buf))
        s = s[4:]
Exemple #11
0
class SPI:
    """Custom SPI Class for FT232H"""

    MSB = 0

    def __init__(self):
        # pylint: disable=import-outside-toplevel
        from pyftdi.spi import SpiController

        # pylint: enable=import-outside-toplevel

        self._spi = SpiController(cs_count=1)
        self._spi.configure(get_ftdi_url())
        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()

    # pylint: disable=too-many-arguments,unused-argument
    def init(
        self,
        baudrate=100000,
        polarity=0,
        phase=0,
        bits=8,
        firstbit=MSB,
        sck=None,
        mosi=None,
        miso=None,
    ):
        """Initialize the Port"""
        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

    # pylint: enable=too-many-arguments

    @property
    def frequency(self):
        """Return the current frequency"""
        return self._port.frequency

    def write(self, buf, start=0, end=None):
        """Write data from the buffer to SPI"""
        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:
            rest_start = start + chunks * self._spi.PAYLOAD_MAX_LENGTH
            self._port.write(buf[rest_start:end])

    # pylint: disable=unused-argument
    def readinto(self, buf, start=0, end=None, write_value=0):
        """Read data from SPI and into the buffer"""
        end = end if end else len(buf)
        buffer_out = [write_value] * (end - start)
        result = self._port.exchange(buffer_out, end - start, duplex=True)
        for i, b in enumerate(result):
            buf[start + i] = b

    # pylint: enable=unused-argument

    # pylint: disable=too-many-arguments
    def write_readinto(self,
                       buffer_out,
                       buffer_in,
                       out_start=0,
                       out_end=None,
                       in_start=0,
                       in_end=None):
        """Perform a half-duplex write from buffer_out and then
        read data into buffer_in
        """
        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
Exemple #12
0
    print("Usage: raptor_flash.py <file>")
    sys.exit()

file_path = sys.argv[1]

if not os.path.isfile(file_path):
    print("File not found.")
    sys.exit()

spi = SpiController(cs_count=1, turbo=True)
# spi.configure(vendor=0x0403, product=0x6014, interface=1)
spi.configure('ftdi://ftdi:232h:0/1')
slave = spi.get_port(cs=0, freq=12E6,
                     mode=0)  # Chip select is 0 -- corresponds to D3

gpio = spi.get_gpio()
gpio.set_direction(0x0100, 0x0100)
gpio.write(0x0000)

# time.sleep(1.0)

slave.write([CMD_RESET_CHIP])

jedec = slave.exchange([CMD_JEDEC_DATA], 3)
print("JEDEC = {}".format(binascii.hexlify(jedec)))

if jedec[0] != int('ef', 16) and jedec[0] != int('01', 16) and jedec[0] != int(
        'bf', 16):
    print("Winbond, Microchip or Cypress SRAM not found")
    sys.exit()
Exemple #13
0
class SPIDriver:
    """
    Class that contains the driver for the FTDI SPI + GPIO.
    """
    def __init__(self):
        self.ctrl = None
        self.slave = None
        print('Starting driver ...')
        self.begin()

    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)

    def write_data_out(self, buffer):
        if not isinstance(buffer, bytes):
            raise Exception  # TODO define exception
        try:
            self.slave.write(buffer)
            return True
        except Exception as err:
            #TODO convert to Logging
            print('Error in writing data out to FTDI SPI...:', err)
            return False

    def set_power_on(self):
        self.gpio.write(0x10)
        if self.backup_gpio is not None:
            self.backup_gpio.write(0x10)

    def set_power_off(self):
        self.gpio.write(0x00)
        if self.backup_gpio is not None:
            self.backup_gpio.write(0x00)

    def terminate(self):
        self.ctrl.terminate()
        print('terminated')
Exemple #14
0
class SPI:
    MSB = 0

    baudrate = 100000
    mode = 0
    bits = 8

    def __init__(self):
        # change GPIO controller to SPI
        from pyftdi.spi import SpiController
        self._spi = SpiController(cs_count=1)
        self._spi.configure('ftdi:///1')
        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.cs = 0
        self.freq = baudrate
        if polarity == 0 and phase == 0:
            self.mode = 0
        elif polarity == 0 and phase == 1:
            self.mode = 1
        elif polarity == 1 and phase == 0:
            raise ValueError("SPI mode 2 is not supported.")
        elif polarity == 1 and phase == 1:
            self.mode = 3
        else:
            raise ValueError("Unknown SPI mode.")

    def write(self, buf, start=0, end=None):
        end = end if end else len(buf)
        port = self._spi.get_port(self.cs, self.freq, self.mode)
        port.write(buf[start:end])

    def readinto(self, buf, start=0, end=None, write_value=0):
        end = end if end else len(buf)
        port = self._spi.get_port(self.cs, self.freq, self.mode)
        result = 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)
        port = self._spi.get_port(self.cs, self.freq, self.mode)
        result = port.exchange(buffer_out[out_start:out_end],
                               in_end - in_start)
        for i, b in enumerate(result):
            buffer_in[in_start + i] = b
Exemple #15
0
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)
Exemple #16
0
class SPILink:
    BIT_REV = [int('{:08b}'.format(n)[::-1], 2) for n in range(0,256)]

    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)

    def reset(self):
        self.gpio.write(0x00) # Pull reset line low
        time.sleep(0.1)
        self.gpio.write(0x10) # Reset line high
        time.sleep(0.8) # Wait for cc111x to boot

    def flush(self):
        # Send interrupt command
        _, available = self.exchange([0x99, 1], readlen=2)
        data = self.exchange([], readlen=max(1,available))
        # Discard any remaining data
        _, available = self.exchange([0x99, 0], readlen=2)
        while (available > 0):
            data = self.exchange([], readlen=available)
            if self.debug:
                print("discarding %s" % "".join(["%02x" % x for x in data]))
            _, available = self.exchange([0x99, 0], readlen=2)

    def reverse_bits(self, data):
        return [SPILink.BIT_REV[x] for x in data]

    def as_hex(self, data):
        return "".join(["%02x" % x for x in data])

    def exchange(self, data, readlen=0):
        lsb_send = self.reverse_bits(data)
        lsb_rcv = self.spi.exchange(lsb_send,readlen=readlen, duplex=True)
        return self.reverse_bits(lsb_rcv)

    def send_command(self, command):
        data = command.data()
        if len(data) == 0:
            print("Empty command!")
            return

        if self.debug:
            print("send: %s" % self.as_hex(data))
        response = None
        _, available = self.exchange([0x99, len(data)], readlen=2)
        if available > 0:
            print("Unexpected data available from client while sending command.")
            return
        self.exchange(data, readlen=0)

    def wait_for_response(self, response_type, timeout=1):
        end_time = time.time() + timeout
        response = None
        while(time.time() < end_time and response == None):
            _, available = self.exchange([0x99, 0], readlen=2)
            if (available > 0):
                if self.debug:
                    print("response: available = %d" % available)
                rdata = self.exchange([], readlen=available)
            else:
                time.sleep(0.01)
            if available > 0:
                response = rdata
            data = []

        if response == None:
            raise SPITimeout()
        if self.debug:
            print("recv: %s" % self.as_hex(response))
        response_obj = response_type(response)
        return response_obj

    def do_command(self, command, timeout=1):
        self.send_command(command)
        response = self.wait_for_response(command.response_type(), timeout)
        if response.response_code != ResponseCode.COMMAND_INTERRUPTED:
            return response
        else:
            print("Command interrupted...")
            return self.wait_for_response(command.response_type(), timeout)

    def update_register(self, register, value):
        return self.do_command(UpdateRegisterCommand(register, value))

    def set_base_frequency(self, freq):
        val = int(freq / (RILEYLINK_XTAL_FREQUENCY / pow(2,16)))
        self.update_register(Register.FREQ0, val & 0xff)
        self.update_register(Register.FREQ1, (val >> 8) & 0xff)
        self.update_register(Register.FREQ2, (val >> 16) & 0xff)