Beispiel #1
1
class ft2232:

    def open_ft2232(self, vps, itf, sn):
        # find the device on USB
        devices = usbtools.UsbTools.find_all(vps)
        if sn is not None:
            # filter based on device serial number
            devices = [dev for dev in devices if dev[2] == sn]
        if len(devices) == 0:
            raise IOError("No such device")
        self.vid = devices[0][0]
        self.pid = devices[0][1]
        self.sn = devices[0][2]
        self.ftdi = Ftdi()
        self.freq = self.ftdi.open_mpsse(self.vid, self.pid, itf, serial = self.sn, frequency = _FREQ)
        self.wrbuf = array.array('B')
        self.gpio_init()
        self.state_reset()
        self.sir_end_state = 'IDLE'
        self.sdr_end_state = 'IDLE'

    def __del__(self):
        if self.ftdi:
            self.ftdi.close()

    def flush(self):
        """flush the write buffer to the ft2232"""
        if len(self.wrbuf) > 0:
            self.ftdi.write_data(self.wrbuf)
            del self.wrbuf[0:]

    def write(self, buf, flush = False):
        """queue write data to send to ft2232"""
        self.wrbuf.extend(buf)
        if flush:
            self.flush()

    def state_x(self, dst):
        """change the TAP state from self.state to dst"""
        if self.state == dst:
          return
        tms = tms_mpsse(tap.lookup(self.state, dst))
        cmd = _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG
        self.write((cmd, tms[0], tms[1]), True)
        self.state = dst

    def state_reset(self):
        """from *any* state go to the reset state"""
        self.state = '*'
        self.state_x('RESET')

    def shift_data(self, tdi, tdo, end_state):
        """
        write (and possibly read) a bit stream from the JTAGkey
        tdi - bit buffer of data to be written to the JTAG TDI pin
        tdo - bit buffer for the data read from the JTAG TDO pin (optional)
        end_state - leave the TAP state machine in this state
        """
        wr = tdi.get()
        io_bits = tdi.n - 1
        io_bytes = io_bits >> 3
        io_bits &= 0x07
        last_bit = (wr[io_bytes] << (7 - io_bits)) & 128

        if tdo is not None:
            read_cmd = _MPSSE_DO_READ
            read_len = io_bytes + 1
            if io_bits:
                read_len += 1
        else:
            read_cmd = 0
            read_len = 0

        # write out the full bytes
        if io_bytes:
            cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_WRITE_NEG
            num = io_bytes - 1
            self.write((cmd, _lsb(num), _msb(num)))
            self.write(wr[0:io_bytes])

        # write out the remaining bits
        if io_bits:
            cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_BITMODE | _MPSSE_WRITE_NEG
            self.write((cmd, io_bits - 1, wr[io_bytes]))

        # the last bit of output data is bit 7 of the tms value (goes onto tdi)
        # continue to read to get the last bit of tdo data
        cmd = read_cmd | _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG
        tms = tms_mpsse(tap.lookup(self.state, end_state))
        self.write((cmd, tms[0], tms[1] | last_bit))
        self.state = end_state

        # if we are only writing, return
        if tdo is None:
            self.flush()
            return

        # make the ft2232 flush its data back to the PC
        self.write((Ftdi.SEND_IMMEDIATE,), True)
        rd = self.ftdi.read_data_bytes(read_len, _READ_RETRIES)

        if io_bits:
            # the n partial bits are in the top n bits of the byte
            # move them down to the bottom
            rd[-2] >>= (8 - io_bits)

        # get the last bit from the tms response byte (last byte)
        last_bit = (rd[-1] >> (7 - tms[0])) & 1
        last_bit <<= io_bits

        # add the last bit
        if io_bits:
            # drop the tms response byte
            del rd[-1]
            # or it onto the io_bits byte
            rd[-1] |= last_bit
        else:
            # replace the tms response byte
            rd[-1] = last_bit

        # copy to the bit buffer
        tdo.set(tdi.n, rd)

    def scan_ir(self, tdi, tdo = None):
        """write (and possibly read) a bit stream through the IR in the JTAG chain"""
        self.state_x('IRSHIFT')
        self.shift_data(tdi, tdo, self.sir_end_state)

    def scan_dr(self, tdi, tdo = None):
        """write (and possibly read) a bit stream through the DR in the JTAG chain"""
        self.state_x('DRSHIFT')
        self.shift_data(tdi, tdo, self.sdr_end_state)

    def gpio_init(self):
        """setup the gpio lines"""
        self.gpio_dir = _TCK | _TDI | _TMS
        self.gpio_val = 0
        self.ftdi.write_data((Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir)))
        self.ftdi.write_data((Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir)))

    def gpio_wr(self, gpio, val):
        """write a gpio pin"""
        self.gpio_dir |= gpio
        if val:
            self.gpio_val |= gpio
        else:
            self.gpio_val &= ~gpio
        if gpio <= _GPIOL3:
            self.ftdi.write_data((Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir)))
        else:
            self.ftdi.write_data((Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir)))

    def gpio_rd(self, gpio):
        """read a gpio pin"""
        if gpio <= _GPIOL3:
            self.ftdi.write_data((Ftdi.GET_BITS_LOW,))
            val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0]
        else:
            self.ftdi.write_data((Ftdi.GET_BITS_HIGH,))
            val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0]
            val <<= 8
        return (val & gpio) != 0
Beispiel #2
0
class ft2232:
    def open_ft2232(self, vps, itf, sn):
        # find the device on USB
        devices = usbtools.UsbTools.find_all(vps)
        if sn is not None:
            # filter based on device serial number
            devices = [dev for dev in devices if dev[2] == sn]
        if len(devices) == 0:
            raise IOError("No such device")
        self.vid = devices[0][0]
        self.pid = devices[0][1]
        self.sn = devices[0][2]
        self.ftdi = Ftdi()
        self.freq = self.ftdi.open_mpsse(self.vid,
                                         self.pid,
                                         itf,
                                         serial=self.sn,
                                         frequency=_FREQ)
        self.wrbuf = array.array('B')
        self.gpio_init()
        self.state_reset()
        self.sir_end_state = 'IDLE'
        self.sdr_end_state = 'IDLE'

    def __del__(self):
        if self.ftdi:
            self.ftdi.close()

    def flush(self):
        """flush the write buffer to the ft2232"""
        if len(self.wrbuf) > 0:
            self.ftdi.write_data(self.wrbuf)
            del self.wrbuf[0:]

    def write(self, buf, flush=False):
        """queue write data to send to ft2232"""
        self.wrbuf.extend(buf)
        if flush:
            self.flush()

    def state_x(self, dst):
        """change the TAP state from self.state to dst"""
        if self.state == dst:
            return
        tms = tms_mpsse(tap.lookup(self.state, dst))
        cmd = _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG
        self.write((cmd, tms[0], tms[1]), True)
        self.state = dst

    def state_reset(self):
        """from *any* state go to the reset state"""
        self.state = '*'
        self.state_x('RESET')

    def shift_data(self, tdi, tdo, end_state):
        """
        write (and possibly read) a bit stream from the JTAGkey
        tdi - bit buffer of data to be written to the JTAG TDI pin
        tdo - bit buffer for the data read from the JTAG TDO pin (optional)
        end_state - leave the TAP state machine in this state
        """
        wr = tdi.get()
        io_bits = tdi.n - 1
        io_bytes = io_bits >> 3
        io_bits &= 0x07
        last_bit = (wr[io_bytes] << (7 - io_bits)) & 128

        if tdo is not None:
            read_cmd = _MPSSE_DO_READ
            read_len = io_bytes + 1
            if io_bits:
                read_len += 1
        else:
            read_cmd = 0
            read_len = 0

        # write out the full bytes
        if io_bytes:
            cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_WRITE_NEG
            num = io_bytes - 1
            self.write((cmd, _lsb(num), _msb(num)))
            self.write(wr[0:io_bytes])

        # write out the remaining bits
        if io_bits:
            cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_BITMODE | _MPSSE_WRITE_NEG
            self.write((cmd, io_bits - 1, wr[io_bytes]))

        # the last bit of output data is bit 7 of the tms value (goes onto tdi)
        # continue to read to get the last bit of tdo data
        cmd = read_cmd | _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG
        tms = tms_mpsse(tap.lookup(self.state, end_state))
        self.write((cmd, tms[0], tms[1] | last_bit))
        self.state = end_state

        # if we are only writing, return
        if tdo is None:
            self.flush()
            return

        # make the ft2232 flush its data back to the PC
        self.write((Ftdi.SEND_IMMEDIATE, ), True)
        rd = self.ftdi.read_data_bytes(read_len, _READ_RETRIES)

        if io_bits:
            # the n partial bits are in the top n bits of the byte
            # move them down to the bottom
            rd[-2] >>= (8 - io_bits)

        # get the last bit from the tms response byte (last byte)
        last_bit = (rd[-1] >> (7 - tms[0])) & 1
        last_bit <<= io_bits

        # add the last bit
        if io_bits:
            # drop the tms response byte
            del rd[-1]
            # or it onto the io_bits byte
            rd[-1] |= last_bit
        else:
            # replace the tms response byte
            rd[-1] = last_bit

        # copy to the bit buffer
        tdo.set(tdi.n, rd)

    def scan_ir(self, tdi, tdo=None):
        """write (and possibly read) a bit stream through the IR in the JTAG chain"""
        self.state_x('IRSHIFT')
        self.shift_data(tdi, tdo, self.sir_end_state)

    def scan_dr(self, tdi, tdo=None):
        """write (and possibly read) a bit stream through the DR in the JTAG chain"""
        self.state_x('DRSHIFT')
        self.shift_data(tdi, tdo, self.sdr_end_state)

    def gpio_init(self):
        """setup the gpio lines"""
        self.gpio_dir = _TCK | _TDI | _TMS
        self.gpio_val = 0
        self.ftdi.write_data(
            (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir)))
        self.ftdi.write_data(
            (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir)))

    def gpio_wr(self, gpio, val):
        """write a gpio pin"""
        self.gpio_dir |= gpio
        if val:
            self.gpio_val |= gpio
        else:
            self.gpio_val &= ~gpio
        if gpio <= _GPIOL3:
            self.ftdi.write_data(
                (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir)))
        else:
            self.ftdi.write_data(
                (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir)))

    def gpio_rd(self, gpio):
        """read a gpio pin"""
        if gpio <= _GPIOL3:
            self.ftdi.write_data((Ftdi.GET_BITS_LOW, ))
            val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0]
        else:
            self.ftdi.write_data((Ftdi.GET_BITS_HIGH, ))
            val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0]
            val <<= 8
        return (val & gpio) != 0
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()
class BitBangController(object):
    DONE_PIN            = 0x10
    PROGRAM_PIN         = 0x40
    SOFT_RESET_PIN      = 0x80

    def __init__(self, vendor_id, product_id, interface, debug = False):
        self.vendor = vendor_id
        self.product = product_id
        self.interface = interface
        self.f = Ftdi()
        self.debug = True
        self.f.open_bitbang(vendor_id, product_id, interface)

    def hiz(self):
        pass

    def is_in_bitbang(self):
        return self.f.bitbang_enabled()

    def read_pins(self):
        return self.f.read_pins()

    def read_program_pin(self):
        return (self.PROGAM_PIN & self.f.read_pins() > 0)

    def read_soft_reset_pin(self):
        return (self.SOFT_RESET_PIN & self.f.read_pins() > 0)

    def read_done_pin(self):
        return (self.DONE_PIN & self.f.read_pins() > 0)

    def wait_for_done(self):
        while not self.read_done_pin():
            print ".",
            time.sleep(200)
        print "Done"

    def soft_reset_high(self):
        pins = self.f.read_pins()
        pins |= self.SOFT_RESET_PIN
        self.f.write_data(Array('B', [0x01, pins]))

    def soft_reset_low(self):
        pins = self.f.read_pins()
        pins &= ~(self.SOFT_RESET_PIN)
        self.f.write_data(Array('B', [0x00, pins]))

    def program_high(self):
        pins = self.f.read_pins()
        pins |= self.PROGRAM_PIN
        self.f.write_data(Array('B', [0x01, pins]))

    def program_low(self):
        pins = self.f.read_pins()
        pins &= ~(self.PROGRAM_PIN)
        self.f.write_data(Array('B', [0x00, pins]))

    def set_soft_reset_to_output(self):
        pin_dir = self.SOFT_RESET_PIN
        self.f.open_bitbang(self.vendor,
                            self.product,
                            self.interface,
                            direction = pin_dir)

    def set_program_pin_to_output(self):
        pin_dir = self.PROGRAM_PIN
        self.f.open_bitbang(self.vendor,
                            self.product,
                            self.interface,
                            direction = pin_dir)

    def set_pins_to_input(self):
        self.f.open_bitbang(self.vendor,
                            self.product,
                            self.interface,
                            direction = 0x00)

    def set_pins_to_output(self):
        self.f.open_bitbang(self.vendor,
                            self.product,
                            self.interface,
                            direction = 0xFF)

    def pins_on(self):
        """
        Set all pins high
        """
        self.f.write_data(Array('B', [0x01, 0xFF]))

    def pins_off(self):
        """
        Set all pins low
        """
        self.f.write_data(Array('B', [0x01, 0x00]))
Beispiel #5
0
class jtag_driver:
    def __init__(self, vendor, product, interface):
        self.ftdi = Ftdi()
        try:
            self.ftdi.open_mpsse(vendor, product, interface)
        except IOError as e:
            print "ft2232 jtag driver: %s" % str(e)
            self.ftdi = None
            sys.exit(0)
        self.wrbuf = array.array('B')
        self.gpio_init()
        self.tap = tap.tap()
        self.state_reset()
        self.sir_end_state = 'IDLE'
        self.sdr_end_state = 'IDLE'

    def __del__(self):
        if self.ftdi:
            self.ftdi.close()

    def __str__(self):
        """return a string describing the device"""
        return self.ftdi.type

    def flush(self):
        """flush the write buffer to the ft2232"""
        if len(self.wrbuf) > 0:
            self.ftdi.write_data(self.wrbuf)
            del self.wrbuf[0:]

    def write(self, buf, flush=False):
        """queue write data to send to ft2232"""
        self.wrbuf.extend(buf)
        if flush:
            self.flush()

    def state_x(self, dst):
        """change the TAP state from self.state to dst"""
        bits = self.tap.tms(self.state, dst)
        if not bits:
            # no state change
            assert self.state == dst
            return
        tms = tms_mpsse(bits)
        cmd = _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG
        self.write((cmd, tms[0], tms[1]), True)
        self.state = dst

    def state_reset(self):
        """from *any* state go to the reset state"""
        self.state = '*'
        self.state_x('RESET')

    def shift_data(self, tdi, tdo, end_state):
        """
        write (and possibly read) a bit stream from the JTAGkey
        tdi - bit buffer of data to be written to the JTAG TDI pin
        tdo - bit buffer for the data read from the JTAG TDO pin (optional)
        end_state - leave the TAP state machine in this state
        """
        wr = tdi.get()
        io_bits = tdi.n - 1
        io_bytes = io_bits >> 3
        io_bits &= 0x07
        last_bit = (wr[io_bytes] << (7 - io_bits)) & 128

        if tdo is not None:
            read_cmd = _MPSSE_DO_READ
            read_len = io_bytes + 1
            if io_bits:
                read_len += 1
        else:
            read_cmd = 0
            read_len = 0

        # write out the full bytes
        if io_bytes:
            cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_WRITE_NEG
            num = io_bytes - 1
            self.write((cmd, _lsb(num), _msb(num)))
            self.write(wr[0:io_bytes])

        # write out the remaining bits
        if io_bits:
            cmd = read_cmd | _MPSSE_DO_WRITE | _MPSSE_LSB | _MPSSE_BITMODE | _MPSSE_WRITE_NEG
            self.write((cmd, io_bits - 1, wr[io_bytes]))

        # the last bit of output data is bit 7 of the tms value (goes onto tdi)
        # continue to read to get the last bit of tdo data
        cmd = read_cmd | _MPSSE_WRITE_TMS | _MPSSE_BITMODE | _MPSSE_LSB | _MPSSE_WRITE_NEG
        tms = tms_mpsse(self.tap.tms(self.state, end_state))
        self.write((cmd, tms[0], tms[1] | last_bit))
        self.state = end_state

        # if we are only writing, return
        if tdo is None:
            self.flush()
            return

        # make the ft2232 flush its data back to the PC
        self.write((Ftdi.SEND_IMMEDIATE, ), True)
        rd = self.ftdi.read_data_bytes(read_len, _READ_RETRIES)

        if io_bits:
            # the n partial bits are in the top n bits of the byte
            # move them down to the bottom
            rd[-2] >>= (8 - io_bits)

        # get the last bit from the tms response byte (last byte)
        last_bit = (rd[-1] >> (7 - tms[0])) & 1
        last_bit <<= io_bits

        # add the last bit
        if io_bits:
            # drop the tms response byte
            del rd[-1]
            # or it onto the io_bits byte
            rd[-1] |= last_bit
        else:
            # replace the tms response byte
            rd[-1] = last_bit

        # copy to the bit buffer
        tdo.set(tdi.n, rd)

    def scan_ir(self, tdi, tdo=None):
        """write (and possibly read) a bit stream through the IR in the JTAG chain"""
        self.state_x('IRSHIFT')
        self.shift_data(tdi, tdo, self.sir_end_state)

    def scan_dr(self, tdi, tdo=None):
        """write (and possibly read) a bit stream through the DR in the JTAG chain"""
        self.state_x('DRSHIFT')
        self.shift_data(tdi, tdo, self.sdr_end_state)

    def test_reset(self, val):
        """control the test reset line"""
        # TODO
        pass

    def reset_jtag(self):
        """reset the TAP of all JTAG devices in the chain"""
        self.test_reset(True)
        time.sleep(_TRST_TIME)
        self.test_reset(False)
        self.state_reset()

    def gpio_init(self):
        """setup the gpio lines"""
        self.gpio_dir = _TCK | _TDI | _TMS
        self.gpio_val = 0
        self.ftdi.write_data(
            (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir)))
        self.ftdi.write_data(
            (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir)))

    def gpio_out(self, gpio):
        if gpio <= _GPIOL3:
            self.ftdi.write_data(
                (Ftdi.SET_BITS_LOW, _lsb(self.gpio_val), _lsb(self.gpio_dir)))
        else:
            self.ftdi.write_data(
                (Ftdi.SET_BITS_HIGH, _msb(self.gpio_val), _msb(self.gpio_dir)))

    def gpio_set(self, gpio):
        """set a gpio pin"""
        self.gpio_dir |= gpio
        self.gpio_val |= gpio
        self.gpio_out(gpio)

    def gpio_clr(self, gpio):
        """clear a gpio pin"""
        self.gpio_dir |= gpio
        self.gpio_val &= ~gpio
        self.gpio_out(gpio)

    def gpio_rd(self, gpio):
        """read a gpio pin"""
        if gpio <= _GPIOL3:
            self.ftdi.write_data((Ftdi.GET_BITS_LOW, ))
            val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0]
        else:
            self.ftdi.write_data((Ftdi.GET_BITS_HIGH, ))
            val = self.ftdi.read_data_bytes(1, _READ_RETRIES)[0]
            val <<= 8
        return (val & gpio) != 0
Beispiel #6
0
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()