예제 #1
0
파일: spi.py 프로젝트: eblot/pyftdi
class SpiController(object):
    """SPI master.

        :param silent_clock: should be set to avoid clocking out SCLK when all
                             /CS signals are released. This clock beat is used
                             to enforce a delay between /CS signal activation.

                             When weird SPI devices are used, SCLK beating may
                             cause trouble. In this case, silent_clock should
                             be set but beware that SCLK line should be fitted
                             with a pull-down resistor, as SCLK is high-Z
                             during this short period of time.
        :param cs_count: is the number of /CS lines (one per device to drive on
                         the SPI bus)
    """

    SCK_BIT = 0x01
    DO_BIT = 0x02
    DI_BIT = 0x04
    CS_BIT = 0x08
    PAYLOAD_MAX_LENGTH = 0x10000  # 16 bits max

    def __init__(self, silent_clock=False, cs_count=4, turbo=True):
        self._ftdi = Ftdi()
        self._cs_bits = ((SpiController.CS_BIT << cs_count) - 1) & ~(SpiController.CS_BIT - 1)
        self._ports = [None] * cs_count
        self._direction = self._cs_bits | SpiController.DO_BIT | SpiController.SCK_BIT
        self._turbo = turbo
        self._cs_high = Array("B")
        if self._turbo:
            if silent_clock:
                # Set SCLK as input to avoid emitting clock beats
                self._cs_high.extend((Ftdi.SET_BITS_LOW, self._cs_bits, self._direction & ~SpiController.SCK_BIT))
            # /CS to SCLK delay, use 8 clock cycles as a HW tempo
            self._cs_high.extend((Ftdi.WRITE_BITS_TMS_NVE, 8 - 1, 0xFF))
        # Restore idle state
        self._cs_high.extend((Ftdi.SET_BITS_LOW, self._cs_bits, self._direction))
        if not self._turbo:
            self._cs_high.append(Ftdi.SEND_IMMEDIATE)
        self._immediate = Array("B", (Ftdi.SEND_IMMEDIATE,))
        self._frequency = 0.0

    def configure(self, vendor, product, interface, **kwargs):
        """Configure the FTDI interface as a SPI master"""
        for k in ("direction", "initial"):
            if k in kwargs:
                del kwargs[k]
        self._frequency = self._ftdi.open_mpsse(
            vendor, product, interface, direction=self._direction, initial=self._cs_bits, **kwargs  # /CS all high
        )

    def terminate(self):
        """Close the FTDI interface"""
        if self._ftdi:
            self._ftdi.close()
            self._ftdi = None

    def get_port(self, cs):
        """Obtain a SPI port to drive a SPI device selected by cs"""
        if not self._ftdi:
            raise SpiIOError("FTDI controller not initialized")
        if cs >= len(self._ports):
            raise SpiIOError("No such SPI port")
        if not self._ports[cs]:
            cs_state = 0xFF & ~((SpiController.CS_BIT << cs) | SpiController.SCK_BIT | SpiController.DO_BIT)
            cs_cmd = Array("B", (Ftdi.SET_BITS_LOW, cs_state, self._direction))
            self._ports[cs] = SpiPort(self, cs_cmd)
            self._flush()
        return self._ports[cs]

    @property
    def frequency_max(self):
        """Returns the maximum SPI clock"""
        return self._ftdi.frequency_max

    @property
    def frequency(self):
        """Returns the current SPI clock"""
        return self._frequency

    def _exchange(self, frequency, out, readlen, cs_cmd=None, complete=True):
        """Perform a half-duplex exchange or transaction with the SPI slave

           :param frequency: SPI bus clock
           :param out: an array of bytes to send to the SPI slave,
                       may be empty to only read out data from the slave
           :param readlen: count of bytes to read out from the slave,
                       may be zero to only write to the slave
           :param cs_cmd: the prolog sequence to activate the /CS line on the
                       SPI bus. May be empty to resume a previously started
                       transaction
           :param complete: whether to send the epilog sequence to move the
                       /CS line back to the idle state. May be force to False
                       if another part of a transaction is expected
           :return: an array of bytes containing the data read out from the
                    slave
        """
        if not self._ftdi:
            raise SpiIOError("FTDI controller not initialized")
        if len(out) > SpiController.PAYLOAD_MAX_LENGTH:
            raise SpiIOError("Output payload is too large")
        if readlen > SpiController.PAYLOAD_MAX_LENGTH:
            raise SpiIOError("Input payload is too large")
        if self._frequency != frequency:
            self._ftdi.set_frequency(frequency)
            # store the requested value, not the actual one (best effort)
            self._frequency = frequency
        cmd = cs_cmd and Array("B", cs_cmd) or Array("B")
        writelen = len(out)
        if writelen:
            write_cmd = struct.pack("<BH", Ftdi.WRITE_BYTES_NVE_MSB, writelen - 1)
            if PY3:
                cmd.frombytes(write_cmd)
            else:
                cmd.fromstring(write_cmd)
            cmd.extend(out)
        if readlen:
            read_cmd = struct.pack("<BH", Ftdi.READ_BYTES_NVE_MSB, readlen - 1)
            if PY3:
                cmd.frombytes(read_cmd)
            else:
                cmd.fromstring(read_cmd)
            cmd.extend(self._immediate)
            if self._turbo:
                if complete:
                    cmd.extend(self._cs_high)
                self._ftdi.write_data(cmd)
            else:
                self._ftdi.write_data(cmd)
                if complete:
                    self._ftdi.write_data(self._cs_high)
            # USB read cycle may occur before the FTDI device has actually
            # sent the data, so try to read more than once if no data is
            # actually received
            data = self._ftdi.read_data_bytes(readlen, 4)
        elif writelen:
            if self._turbo:
                if complete:
                    cmd.extend(self._cs_high)
                self._ftdi.write_data(cmd)
            else:
                self._ftdi.write_data(cmd)
                if complete:
                    self._ftdi.write_data(self._cs_high)
            data = Array("B")
        return data

    def _flush(self):
        """Flush the HW FIFOs"""
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()
예제 #2
0
파일: jtag.py 프로젝트: eblot/pyftdi
class JtagController(object):
    """JTAG master of an FTDI device"""

    TCK_BIT = 0x01  # FTDI output
    TDI_BIT = 0x02  # FTDI output
    TDO_BIT = 0x04  # FTDI input
    TMS_BIT = 0x08  # FTDI output
    TRST_BIT = 0x10  # FTDI output, not available on 2232 JTAG debugger
    JTAG_MASK = 0x1F
    FTDI_PIPE_LEN = 512

    # Private API
    def __init__(self, trst=False, frequency=3.0e6):
        """
        trst uses the nTRST optional JTAG line to hard-reset the TAP
          controller
        """
        self._ftdi = Ftdi()
        self._trst = trst
        self._frequency = frequency
        self.direction = (
            JtagController.TCK_BIT
            | JtagController.TDI_BIT
            | JtagController.TMS_BIT
            | (self._trst and JtagController.TRST_BIT or 0)
        )
        self._last = None  # Last deferred TDO bit
        self._write_buff = Array("B")

    def __del__(self):
        self.close()

    # Public API
    def configure(self, vendor, product, interface):
        """Configure the FTDI interface as a JTAG controller"""
        self._ftdi.open_mpsse(vendor, product, interface, direction=self.direction, frequency=self._frequency)
        # FTDI requires to initialize all GPIOs before MPSSE kicks in
        cmd = Array("B", (Ftdi.SET_BITS_LOW, 0x0, self.direction))
        self._ftdi.write_data(cmd)

    def close(self):
        if self._ftdi:
            self._ftdi.close()
            self._ftdi = None

    def purge(self):
        self._ftdi.purge_buffers()

    def reset(self, sync=False):
        """Reset the attached TAP controller.
           sync sends the command immediately (no caching)
        """
        # we can either send a TRST HW signal or perform 5 cycles with TMS=1
        # to move the remote TAP controller back to 'test_logic_reset' state
        # do both for now
        if not self._ftdi:
            raise JtagError("FTDI controller terminated")
        if self._trst:
            # nTRST
            value = 0
            cmd = Array("B", (Ftdi.SET_BITS_LOW, value, self.direction))
            self._ftdi.write_data(cmd)
            time.sleep(0.1)
            # nTRST should be left to the high state
            value = JtagController.TRST_BIT
            cmd = Array("B", (Ftdi.SET_BITS_LOW, value, self.direction))
            self._ftdi.write_data(cmd)
            time.sleep(0.1)
        # TAP reset (even with HW reset, could be removed though)
        self.write_tms(BitSequence("11111"))
        if sync:
            self.sync()

    def sync(self):
        if not self._ftdi:
            raise JtagError("FTDI controller terminated")
        if self._write_buff:
            self._ftdi.write_data(self._write_buff)
            self._write_buff = Array("B")

    def write_tms(self, tms):
        """Change the TAP controller state"""
        if not isinstance(tms, BitSequence):
            raise JtagError("Expect a BitSequence")
        length = len(tms)
        if not (0 < length < 8):
            raise JtagError("Invalid TMS length")
        out = BitSequence(tms, length=8)
        # apply the last TDO bit
        if self._last is not None:
            out[7] = self._last
        # print_("TMS", tms, (self._last is not None) and 'w/ Last' or '')
        # reset last bit
        self._last = None
        cmd = Array("B", (Ftdi.WRITE_BITS_TMS_NVE, length - 1, out.tobyte()))
        self._stack_cmd(cmd)
        self.sync()

    def read(self, length):
        """Read out a sequence of bits from TDO"""
        byte_count = length // 8
        bit_count = length - 8 * byte_count
        bs = BitSequence()
        if byte_count:
            bytes_ = self._read_bytes(byte_count)
            bs.append(bytes_)
        if bit_count:
            bits = self._read_bits(bit_count)
            bs.append(bits)
        return bs

    def write(self, out, use_last=True):
        """Write a sequence of bits to TDI"""
        if isinstance(out, str):
            if len(out) > 1:
                self._write_bytes_raw(out[:-1])
                out = out[-1]
            out = BitSequence(bytes_=out)
        elif not isinstance(out, BitSequence):
            out = BitSequence(out)
        if use_last:
            (out, self._last) = (out[:-1], bool(out[-1]))
        byte_count = len(out) // 8
        pos = 8 * byte_count
        bit_count = len(out) - pos
        if byte_count:
            self._write_bytes(out[:pos])
        if bit_count:
            self._write_bits(out[pos:])

    def shift_register(self, out, use_last=False):
        """Shift a BitSequence into the current register and retrieve the
           register output"""
        if not isinstance(out, BitSequence):
            return JtagError("Expect a BitSequence")
        length = len(out)
        if use_last:
            (out, self._last) = (out[:-1], int(out[-1]))
        byte_count = len(out) // 8
        pos = 8 * byte_count
        bit_count = len(out) - pos
        if not byte_count and not bit_count:
            raise JtagError("Nothing to shift")
        if byte_count:
            blen = byte_count - 1
            # print_("RW OUT %s" % out[:pos])
            cmd = Array("B", (Ftdi.RW_BYTES_PVE_NVE_LSB, blen, (blen >> 8) & 0xFF))
            cmd.extend(out[:pos].tobytes(msby=True))
            self._stack_cmd(cmd)
            # print_("push %d bytes" % byte_count)
        if bit_count:
            # print_("RW OUT %s" % out[pos:])
            cmd = Array("B", (Ftdi.RW_BITS_PVE_NVE_LSB, bit_count - 1))
            cmd.append(out[pos:].tobyte())
            self._stack_cmd(cmd)
            # print_("push %d bits" % bit_count)
        self.sync()
        bs = BitSequence()
        byte_count = length // 8
        pos = 8 * byte_count
        bit_count = length - pos
        if byte_count:
            data = self._ftdi.read_data_bytes(byte_count, 4)
            if not data:
                raise JtagError("Unable to read data from FTDI")
            byteseq = BitSequence(bytes_=data, length=8 * byte_count)
            # print_("RW IN %s" % byteseq)
            bs.append(byteseq)
            # print_("pop %d bytes" % byte_count)
        if bit_count:
            data = self._ftdi.read_data_bytes(1, 4)
            if not data:
                raise JtagError("Unable to read data from FTDI")
            byte = data[0]
            # need to shift bits as they are shifted in from the MSB in FTDI
            byte >>= 8 - bit_count
            bitseq = BitSequence(byte, length=bit_count)
            bs.append(bitseq)
            # print_("pop %d bits" % bit_count)
        if len(bs) != length:
            raise ValueError("Internal error")
        return bs

    def _stack_cmd(self, cmd):
        if not isinstance(cmd, Array):
            raise TypeError("Expect a byte array")
        if not self._ftdi:
            raise JtagError("FTDI controller terminated")
        # Currrent buffer + new command + send_immediate
        if (len(self._write_buff) + len(cmd) + 1) >= JtagController.FTDI_PIPE_LEN:
            self.sync()
        self._write_buff.extend(cmd)

    def _read_bits(self, length):
        """Read out bits from TDO"""
        if length > 8:
            raise JtagError("Cannot fit into FTDI fifo")
        cmd = Array("B", (Ftdi.READ_BITS_NVE_LSB, length - 1))
        self._stack_cmd(cmd)
        self.sync()
        data = self._ftdi.read_data_bytes(1, 4)
        # need to shift bits as they are shifted in from the MSB in FTDI
        byte = data[0] >> 8 - length
        bs = BitSequence(byte, length=length)
        # print_("READ BITS %s" % bs)
        return bs

    def _write_bits(self, out):
        """Output bits on TDI"""
        length = len(out)
        byte = out.tobyte()
        # print_("WRITE BITS %s" % out)
        cmd = Array("B", (Ftdi.WRITE_BITS_NVE_LSB, length - 1, byte))
        self._stack_cmd(cmd)

    def _read_bytes(self, length):
        """Read out bytes from TDO"""
        if length > JtagController.FTDI_PIPE_LEN:
            raise JtagError("Cannot fit into FTDI fifo")
        alen = length - 1
        cmd = Array("B", (Ftdi.READ_BYTES_NVE_LSB, alen & 0xFF, (alen >> 8) & 0xFF))
        self._stack_cmd(cmd)
        self.sync()
        data = self._ftdi.read_data_bytes(length, 4)
        bs = BitSequence(bytes_=data, length=8 * length)
        # print_("READ BYTES %s" % bs)
        return bs

    def _write_bytes(self, out):
        """Output bytes on TDI"""
        bytes_ = out.tobytes(msby=True)  # don't ask...
        olen = len(bytes_) - 1
        # print_("WRITE BYTES %s" % out)
        cmd = Array("B", (Ftdi.WRITE_BYTES_NVE_LSB, olen & 0xFF, (olen >> 8) & 0xFF))
        cmd.extend(bytes_)
        self._stack_cmd(cmd)

    def _write_bytes_raw(self, out):
        """Output bytes on TDI"""
        olen = len(out) - 1
        cmd = Array("B", (Ftdi.WRITE_BYTES_NVE_LSB, olen & 0xFF, (olen >> 8) & 0xFF))
        cmd.extend(out)
        self._stack_cmd(cmd)
예제 #3
0
파일: jtag.py 프로젝트: pgrudzinski/pyftdi
class JtagController(object):
    """JTAG master of an FTDI device"""

    TCK_BIT = 0x01  # FTDI output
    TDI_BIT = 0x02  # FTDI output
    TDO_BIT = 0x04  # FTDI input
    TMS_BIT = 0x08  # FTDI output
    TRST_BIT = 0x10  # FTDI output, not available on 2232 JTAG debugger
    JTAG_MASK = 0x1f
    FTDI_PIPE_LEN = 512

    # Private API
    def __init__(self, trst=False, frequency=3.0E6):
        """
        trst uses the nTRST optional JTAG line to hard-reset the TAP
          controller
        """
        self._ftdi = Ftdi()
        self._trst = trst
        self._frequency = frequency
        self.direction = JtagController.TCK_BIT | \
                         JtagController.TDI_BIT | \
                         JtagController.TMS_BIT | \
                         (self._trst and JtagController.TRST_BIT or 0)
        self._last = None  # Last deferred TDO bit
        self._write_buff = Array('B')

    def __del__(self):
        self.close()

    # Public API
    def configure(self, vendor, product, interface):
        """Configure the FTDI interface as a JTAG controller"""
        curfreq = self._ftdi.open_mpsse(
            vendor,
            product,
            interface,
            direction=self.direction,
            #initial=0x0,
            frequency=self._frequency)
        # FTDI requires to initialize all GPIOs before MPSSE kicks in
        cmd = Array('B', [Ftdi.SET_BITS_LOW, 0x0, self.direction])
        self._ftdi.write_data(cmd)

    def close(self):
        if self._ftdi:
            self._ftdi.close()
            self._ftdi = None

    def purge(self):
        self._ftdi.purge_buffers()

    def reset(self, sync=False):
        """Reset the attached TAP controller.
           sync sends the command immediately (no caching)
        """
        # we can either send a TRST HW signal or perform 5 cycles with TMS=1
        # to move the remote TAP controller back to 'test_logic_reset' state
        # do both for now
        if not self._ftdi:
            raise JtagError("FTDI controller terminated")
        if self._trst:
            # nTRST
            value = 0
            cmd = Array('B', [Ftdi.SET_BITS_LOW, value, self.direction])
            self._ftdi.write_data(cmd)
            time.sleep(0.1)
            # nTRST should be left to the high state
            value = JtagController.TRST_BIT
            cmd = Array('B', [Ftdi.SET_BITS_LOW, value, self.direction])
            self._ftdi.write_data(cmd)
            time.sleep(0.1)
        # TAP reset (even with HW reset, could be removed though)
        self.write_tms(BitSequence('11111'))
        if sync:
            self.sync()

    def sync(self):
        if not self._ftdi:
            raise JtagError("FTDI controller terminated")
        if self._write_buff:
            self._ftdi.write_data(self._write_buff)
            self._write_buff = Array('B')

    def write_tms(self, tms):
        """Change the TAP controller state"""
        if not isinstance(tms, BitSequence):
            raise JtagError('Expect a BitSequence')
        length = len(tms)
        if not (0 < length < 8):
            raise JtagError('Invalid TMS length')
        out = BitSequence(tms, length=8)
        # apply the last TDO bit
        if self._last is not None:
            out[7] = self._last
        # print "TMS", tms, (self._last is not None) and 'w/ Last' or ''
        # reset last bit
        self._last = None
        cmd = Array('B', [Ftdi.WRITE_BITS_TMS_NVE, length - 1, out.tobyte()])
        self._stack_cmd(cmd)
        self.sync()
        #self._ftdi.validate_mpsse()

    def read(self, length):
        """Read out a sequence of bits from TDO"""
        byte_count = length // 8
        bit_count = length - 8 * byte_count
        bs = BitSequence()
        if byte_count:
            bytes = self._read_bytes(byte_count)
            bs.append(bytes)
        if bit_count:
            bits = self._read_bits(bit_count)
            bs.append(bits)
        return bs

    def write(self, out, use_last=True):
        """Write a sequence of bits to TDI"""
        if isinstance(out, str):
            if len(out) > 1:
                self._write_bytes_raw(out[:-1])
                out = out[-1]
            out = BitSequence(bytes_=out)
        elif not isinstance(out, BitSequence):
            out = BitSequence(out)
        if use_last:
            (out, self._last) = (out[:-1], bool(out[-1]))
        byte_count = len(out) // 8
        pos = 8 * byte_count
        bit_count = len(out) - pos
        if byte_count:
            self._write_bytes(out[:pos])
        if bit_count:
            self._write_bits(out[pos:])

    def shift_register(self, out, use_last=False):
        """Shift a BitSequence into the current register and retrieve the
           register output"""
        if not isinstance(out, BitSequence):
            return JtagError('Expect a BitSequence')
        length = len(out)
        if use_last:
            (out, self._last) = (out[:-1], int(out[-1]))
        byte_count = len(out) // 8
        pos = 8 * byte_count
        bit_count = len(out) - pos
        if not byte_count and not bit_count:
            raise JtagError("Nothing to shift")
        if byte_count:
            blen = byte_count - 1
            #print "RW OUT %s" % out[:pos]
            cmd = Array('B',
                        [Ftdi.RW_BYTES_PVE_NVE_LSB, blen, (blen >> 8) & 0xff])
            cmd.extend(out[:pos].tobytes(msby=True))
            self._stack_cmd(cmd)
            #print "push %d bytes" % byte_count
        if bit_count:
            #print "RW OUT %s" % out[pos:]
            cmd = Array('B', [Ftdi.RW_BITS_PVE_NVE_LSB, bit_count - 1])
            cmd.append(out[pos:].tobyte())
            self._stack_cmd(cmd)
            #print "push %d bits" % bit_count
        self.sync()
        bs = BitSequence()
        byte_count = length // 8
        pos = 8 * byte_count
        bit_count = length - pos
        if byte_count:
            data = self._ftdi.read_data_bytes(byte_count, 4)
            if not data:
                raise JtagError('Unable to read data from FTDI')
            byteseq = BitSequence(bytes_=data, length=8 * byte_count)
            #print "RW IN %s" % byteseq
            bs.append(byteseq)
            #print "pop %d bytes" % byte_count
        if bit_count:
            data = self._ftdi.read_data_bytes(1, 4)
            if not data:
                raise JtagError('Unable to read data from FTDI')
            byte = data[0]
            # need to shift bits as they are shifted in from the MSB in FTDI
            byte >>= 8 - bit_count
            bitseq = BitSequence(byte, length=bit_count)
            bs.append(bitseq)
            #print "pop %d bits" % bit_count
        if len(bs) != length:
            raise AssertionError("Internal error")
        #self._ftdi.validate_mpsse()
        return bs

    def _stack_cmd(self, cmd):
        if not isinstance(cmd, Array):
            raise TypeError('Expect a byte array')
        if not self._ftdi:
            raise JtagError("FTDI controller terminated")
        # Currrent buffer + new command + send_immediate
        if (len(self._write_buff) + len(cmd) +
                1) >= JtagController.FTDI_PIPE_LEN:
            self.sync()
        self._write_buff.extend(cmd)

    def _read_bits(self, length):
        """Read out bits from TDO"""
        data = ''
        if length > 8:
            raise JtagError("Cannot fit into FTDI fifo")
        cmd = Array('B', [Ftdi.READ_BITS_NVE_LSB, length - 1])
        self._stack_cmd(cmd)
        self.sync()
        data = self._ftdi.read_data_bytes(1, 4)
        # need to shift bits as they are shifted in from the MSB in FTDI
        byte = ord(data) >> 8 - bit_count
        bs = BitSequence(byte, length=length)
        #print "READ BITS %s" % (bs)
        return bs

    def _write_bits(self, out):
        """Output bits on TDI"""
        length = len(out)
        byte = out.tobyte()
        #print "WRITE BITS %s" % out
        cmd = Array('B', [Ftdi.WRITE_BITS_NVE_LSB, length - 1, byte])
        self._stack_cmd(cmd)

    def _read_bytes(self, length):
        """Read out bytes from TDO"""
        data = ''
        if length > JtagController.FTDI_PIPE_LEN:
            raise JtagError("Cannot fit into FTDI fifo")
        alen = length - 1
        cmd = Array('B',
                    [Ftdi.READ_BYTES_NVE_LSB, alen & 0xff, (alen >> 8) & 0xff])
        self._stack_cmd(cmd)
        self.sync()
        data = self._ftdi.read_data_bytes(length, 4)
        bs = BitSequence(bytes_=data, length=8 * length)
        #print "READ BYTES %s" % bs
        return bs

    def _write_bytes(self, out):
        """Output bytes on TDI"""
        bytes_ = out.tobytes(msby=True)  # don't ask...
        olen = len(bytes_) - 1
        #print "WRITE BYTES %s" % out
        cmd = Array(
            'B', [Ftdi.WRITE_BYTES_NVE_LSB, olen & 0xff, (olen >> 8) & 0xff])
        cmd.extend(bytes_)
        self._stack_cmd(cmd)

    def _write_bytes_raw(self, out):
        """Output bytes on TDI"""
        olen = len(out) - 1
        cmd = Array(
            'B', [Ftdi.WRITE_BYTES_NVE_LSB, olen & 0xff, (olen >> 8) & 0xff])
        cmd.fromstring(out)
        self._stack_cmd(cmd)
예제 #4
0
파일: spi.py 프로젝트: alexforencich/pyftdi
class SpiController(object):
    """SPI master.

        :param silent_clock: should be set to avoid clocking out SCLK when all
                             /CS signals are released. This clock beat is used
                             to enforce a delay between /CS signal activation.

                             When weird SPI devices are used, SCLK beating may
                             cause trouble. In this case, silent_clock should
                             be set but beware that SCLK line should be fitted
                             with a pull-down resistor, as SCLK is high-Z
                             during this short period of time.
        :param cs_count: is the number of /CS lines (one per device to drive on
                         the SPI bus)
    """

    SCK_BIT = 0x01
    DO_BIT = 0x02
    DI_BIT = 0x04
    CS_BIT = 0x08
    PAYLOAD_MAX_LENGTH = 0x10000  # 16 bits max

    def __init__(self, silent_clock=False, cs_count=4):
        self._ftdi = Ftdi()
        self._cs_bits = ((SpiController.CS_BIT << cs_count) - 1) & ~(SpiController.CS_BIT - 1)
        self._ports = [None] * cs_count
        self._direction = self._cs_bits | SpiController.DO_BIT | SpiController.SCK_BIT
        self._cs_high = Array("B")
        if silent_clock:
            # Set SCLK as input to avoid emitting clock beats
            self._cs_high.extend([Ftdi.SET_BITS_LOW, self._cs_bits, self._direction & ~SpiController.SCK_BIT])
        # /CS to SCLK delay, use 8 clock cycles as a HW tempo
        self._cs_high.extend([Ftdi.WRITE_BITS_TMS_NVE, 8 - 1, 0xFF])
        # Restore idle state
        self._cs_high.extend([Ftdi.SET_BITS_LOW, self._cs_bits, self._direction])
        self._immediate = Array("B", [Ftdi.SEND_IMMEDIATE])
        self._frequency = 0.0

    def configure(self, vendor, product, interface, frequency=6.0e6):
        """Configure the FTDI interface as a SPI master"""
        self._frequency = self._ftdi.open_mpsse(
            vendor,
            product,
            interface,
            direction=self._direction,
            initial=self._cs_bits,  # /CS all high
            frequency=frequency,
        )

    def terminate(self):
        """Close the FTDI interface"""
        if self._ftdi:
            self._ftdi.close()
            self._ftdi = None

    def get_port(self, cs):
        """Obtain a SPI port to drive a SPI device selected by cs"""
        if not self._ftdi:
            raise SpiIOError("FTDI controller not initialized")
        if cs >= len(self._ports):
            raise SpiIOError("No such SPI port")
        if not self._ports[cs]:
            cs_state = 0xFF & ~((SpiController.CS_BIT << cs) | SpiController.SCK_BIT | SpiController.DO_BIT)
            cs_cmd = Array("B", [Ftdi.SET_BITS_LOW, cs_state, self._direction])
            self._ports[cs] = SpiPort(self, cs_cmd)
            self._flush()
        return self._ports[cs]

    @property
    def frequency_max(self):
        """Returns the maximum SPI clock"""
        return self._ftdi.frequency_max

    @property
    def frequency(self):
        """Returns the current SPI clock"""
        return self._frequency

    def _exchange(self, frequency, cs_cmd, out, readlen):
        """Perform a half-duplex transaction with the SPI slave"""
        if not self._ftdi:
            raise SpiIOError("FTDI controller not initialized")
        if len(out) > SpiController.PAYLOAD_MAX_LENGTH:
            raise SpiIOError("Output payload is too large")
        if readlen > SpiController.PAYLOAD_MAX_LENGTH:
            raise SpiIOError("Input payload is too large")
        if self._frequency != frequency:
            freq = self._ftdi.set_frequency(frequency)
            # store the requested value, not the actual one (best effort)
            self._frequency = frequency
        write_cmd = struct.pack("<BH", Ftdi.WRITE_BYTES_NVE_MSB, len(out) - 1)
        if readlen:
            read_cmd = struct.pack("<BH", Ftdi.READ_BYTES_NVE_MSB, readlen - 1)
            cmd = Array("B", cs_cmd)
            cmd.fromstring(write_cmd)
            cmd.extend(out)
            cmd.fromstring(read_cmd)
            cmd.extend(self._immediate)
            cmd.extend(self._cs_high)
            self._ftdi.write_data(cmd)
            # USB read cycle may occur before the FTDI device has actually
            # sent the data, so try to read more than once if no data is
            # actually received
            data = self._ftdi.read_data_bytes(readlen, 4)
        else:
            cmd = Array("B", cs_cmd)
            cmd.fromstring(write_cmd)
            cmd.extend(out)
            cmd.extend(self._cs_high)
            self._ftdi.write_data(cmd)
            data = Array("B")
        return data

    def _flush(self):
        """Flush the HW FIFOs"""
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()
예제 #5
0
파일: spi.py 프로젝트: pgrudzinski/pyftdi
class SpiController(object):
    """SPI master.

        :param silent_clock: should be set to avoid clocking out SCLK when all
                             /CS signals are released. This clock beat is used
                             to enforce a delay between /CS signal activation.

                             When weird SPI devices are used, SCLK beating may
                             cause trouble. In this case, silent_clock should
                             be set but beware that SCLK line should be fitted
                             with a pull-down resistor, as SCLK is high-Z
                             during this short period of time.

                             Note that in this mode, it is recommended to use
                             an external pull-down on SCLK
        :param cs_count: is the number of /CS lines (one per device to drive on
                         the SPI bus)
    """

    SCK_BIT = 0x01
    DO_BIT = 0x02
    DI_BIT = 0x04
    CS_BIT = 0x08
    PAYLOAD_MAX_LENGTH = 0x10000  # 16 bits max

    def __init__(self, silent_clock=False, cs_count=4, turbo=True):
        self._ftdi = Ftdi()
        self._cs_bits = (((SpiController.CS_BIT << cs_count) - 1)
                         & ~(SpiController.CS_BIT - 1))
        self._ports = [None] * cs_count
        self._direction = (self._cs_bits | SpiController.DO_BIT
                           | SpiController.SCK_BIT)
        self._turbo = turbo
        self._cs_high = Array('B')
        if self._turbo:
            if silent_clock:
                # Set SCLK as input to avoid emitting clock beats
                self._cs_high.extend([
                    Ftdi.SET_BITS_LOW, self._cs_bits,
                    self._direction & ~SpiController.SCK_BIT
                ])
            # /CS to SCLK delay, use 8 clock cycles as a HW tempo
            self._cs_high.extend([Ftdi.WRITE_BITS_TMS_NVE, 8 - 1, 0xff])
        # Restore idle state
        self._cs_high.extend(
            [Ftdi.SET_BITS_LOW, self._cs_bits, self._direction])
        if not self._turbo:
            self._cs_high.append(Ftdi.SEND_IMMEDIATE)
        self._immediate = Array('B', [Ftdi.SEND_IMMEDIATE])
        self._frequency = 0.0

    def configure(self, vendor, product, interface, **kwargs):
        """Configure the FTDI interface as a SPI master"""
        for k in ('direction', 'initial'):
            if k in kwargs:
                del kwargs[k]
        self._frequency = \
            self._ftdi.open_mpsse(vendor, product, interface,
                                  direction=self._direction,
                                  initial=self._cs_bits,  # /CS all high
                                  **kwargs)

    def terminate(self):
        """Close the FTDI interface"""
        if self._ftdi:
            self._ftdi.close()
            self._ftdi = None

    def get_port(self, cs):
        """Obtain a SPI port to drive a SPI device selected by cs"""
        if not self._ftdi:
            raise SpiIOError("FTDI controller not initialized")
        if cs >= len(self._ports):
            raise SpiIOError("No such SPI port")
        if not self._ports[cs]:
            cs_state = 0xFF & ~((SpiController.CS_BIT << cs)
                                | SpiController.SCK_BIT | SpiController.DO_BIT)
            cs_cmd = Array('B', [Ftdi.SET_BITS_LOW, cs_state, self._direction])
            self._ports[cs] = SpiPort(self, cs_cmd)
            self._flush()
        return self._ports[cs]

    @property
    def frequency_max(self):
        """Returns the maximum SPI clock"""
        return self._ftdi.frequency_max

    @property
    def frequency(self):
        """Returns the current SPI clock"""
        return self._frequency

    def _exchange(self, frequency, cs_cmd, out, readlen):
        """Perform a half-duplex transaction with the SPI slave"""
        if not self._ftdi:
            raise SpiIOError("FTDI controller not initialized")
        if len(out) > SpiController.PAYLOAD_MAX_LENGTH:
            raise SpiIOError("Output payload is too large")
        if readlen > SpiController.PAYLOAD_MAX_LENGTH:
            raise SpiIOError("Input payload is too large")
        if self._frequency != frequency:
            self._ftdi.set_frequency(frequency)
            # store the requested value, not the actual one (best effort)
            self._frequency = frequency
        write_cmd = struct.pack('<BH', Ftdi.WRITE_BYTES_NVE_MSB, len(out) - 1)
        if readlen:
            read_cmd = struct.pack('<BH', Ftdi.READ_BYTES_NVE_MSB, readlen - 1)
            cmd = Array('B', cs_cmd)
            cmd.fromstring(write_cmd)
            cmd.extend(out)
            cmd.fromstring(read_cmd)
            cmd.extend(self._immediate)
            if self._turbo:
                cmd.extend(self._cs_high)
                self._ftdi.write_data(cmd)
            else:
                self._ftdi.write_data(cmd)
                self._ftdi.write_data(self._cs_high)
            # USB read cycle may occur before the FTDI device has actually
            # sent the data, so try to read more than once if no data is
            # actually received
            data = self._ftdi.read_data_bytes(readlen, 4)
        else:
            cmd = Array('B', cs_cmd)
            cmd.fromstring(write_cmd)
            cmd.extend(out)
            if self._turbo:
                cmd.extend(self._cs_high)
                self._ftdi.write_data(cmd)
            else:
                self._ftdi.write_data(cmd)
                self._ftdi.write_data(self._cs_high)
            data = Array('B')
        return data

    def _flush(self):
        """Flush the HW FIFOs"""
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()
예제 #6
0
파일: clkgen.py 프로젝트: anders-code/stuff
import sys
from array import array

# this library is jacked up, should not have to do this
# I'll probably fork/rewrite it, saving the bits that work
# for now it is quick to start
from pyftdi.ftdi import Ftdi


f = Ftdi()

# reset the FTDI chip
# HA! this can interrupt a continous clock forever mode
# to do something new

f.open_mpsse(0x403, 0x6014)
f.usb_dev.reset()
f.close()
print "reset!"

# here's the normal open
f.open_mpsse(0x403, 0x6014)

if len(sys.argv) > 1:
  freq = int(sys.argv[1])
else:
  freq = 1000 # or whatever

print "set freq: %d" % (freq,)
f.set_frequency(freq)