예제 #1
0
파일: sycamore.py 프로젝트: cospan/sycamore
class Sycamore (object):
	
	SYNC_FIFO_INTERFACE = 0
	SYNC_FIFO_INDEX = 0

	def __init__(self, idVendor, idProduct):
		self.vendor = idVendor
		self.product = idProduct
		self.dev = Ftdi()
		self.open_dev()

	def __del__(self):
		self.dev.close()

	def ping(self):
		data = Array('B', '0000'.decode('hex'))
		print "writing"
		for a in data:
			print "Data: %02X" % (a)

		self.dev.write_data(data)
		print "reading"

		time.sleep(.1)
		response = self.dev.read_data(4)
		rsp = Array('B')
		rsp.fromstring(response)
		print "rsp: " + str(rsp)
#		for a in rsp:
#			print "Data: %02X" % (a)

	def open_dev(self):
		frequency = 30.0E6
		latency = 2
		self.dev.open(self.vendor, self.product, 0)
	# Drain input buffer
		self.dev.purge_buffers()

		# Reset

		# Enable MPSSE mode
		self.dev.set_bitmode(0x00, Ftdi.BITMODE_SYNCFF)
		# Configure clock

		frequency = self.dev._set_frequency(frequency)
		# Set latency timer
		self.dev.set_latency_timer(latency)
		# Set chunk size
		self.dev.write_data_set_chunksize(0x10000)
		self.dev.read_data_set_chunksize(0x10000)
		
		self.dev.set_flowctrl('hw')
		# Configure I/O
#		self.write_data(Array('B', [Ftdi.SET_BITS_LOW, 0x00, 0x00]))
		# Disable loopback
#		self.write_data(Array('B', [Ftdi.LOOPBACK_END]))
#		self.validate_mpsse()
		# Drain input buffer
		self.dev.purge_buffers()
예제 #2
0
 def test_multiple_interface(self):
     # the following calls used to create issues (several interfaces from
     # the same device). The test expect an FTDI 2232H here
     ftdi1 = Ftdi()
     ftdi1.open(vendor=0x403, product=0x6014, interface=1)
     # ftdi2 = Ftdi()
     # ftdi2.open(vendor=0x403, product=0x6014, interface=2)
     import time
     for x in range(5):
         print "poll_modem_status: ", hex(ftdi1.poll_modem_status())
         # print "modem_status: ", ftdi1.modem_status()
         # print "If#2: ", ftdi2.modem_status()
         time.sleep(0.500)
     ftdi1.close()
예제 #3
0
파일: ftdi.py 프로젝트: jhavens1566/pyftdi
 def test_multiple_interface(self):
     # the following calls used to create issues (several interfaces from
     # the same device)
     ftdi1 = Ftdi()
     ftdi1.open(interface=1)
     ftdi2 = Ftdi()
     ftdi2.open(interface=2)
     import time
     for x in range(5):
         print "If#1: ", hex(ftdi1.poll_modem_status())
         print "If#2: ", ftdi2.modem_status()
         time.sleep(0.500)
     ftdi1.close()
     ftdi2.close()
예제 #4
0
파일: ftdi.py 프로젝트: shangdawei/pyftdi
 def test_multiple_interface(self):
     # the following calls used to create issues (several interfaces from
     # the same device). The test expects an FTDI 2232H here
     ftdi1 = Ftdi()
     ftdi1.open(vendor=0x403, product=0x6010, interface=1)
     ftdi2 = Ftdi()
     ftdi2.open(vendor=0x403, product=0x6010, interface=2)
     import time
     for x in range(5):
         print_("If#1: ", hex(ftdi1.poll_modem_status()))
         print_("If#2: ", ftdi2.modem_status())
         time.sleep(0.500)
     ftdi1.close()
     ftdi2.close()
예제 #5
0
 def test_230x(self):
     """Check simple GPIO write and read sequence."""
     # load custom CBUS config, with:
     # CBUS0: GPIO (gpio)
     # CBUS1: GPIO (gpio)
     # CBUS0: DRIVE1 (forced to high level)
     # CBUS0: TXLED  (eq. to highz for tests)
     with open('pyftdi/tests/resources/ft230x_io.yaml', 'rb') as yfp:
         self.loader.load(yfp)
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self.assertEqual(ftdi.has_cbus, True)
     vftdi = self.loader.get_virtual_ftdi(1, 1)
     vport = vftdi.get_port(1)
     # CBUS0: in, CBUS1: out, CBUS2: in, CBUS3: out
     #   however, only CBUS0 and CBUS1 are mapped as GPIO,
     #   CBUS2 forced to 1 and CBUS3 not usable as IO
     #   even if use mask is 1111
     eeprom_mask = 0b0011
     eeprom_force = 0b0100
     cbus_mask = 0b1111
     cbus_dir = 0b1010
     ftdi.set_cbus_direction(cbus_mask, cbus_dir)
     cbus_out = 0b0011
     # CBUS0: 1, CBUS1: 1
     #   however, only CBUS1 is out, so CBUS0 output value should be ignored
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     exp_out |= eeprom_force
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     self.assertEqual(vactive, eeprom_mask | eeprom_force)
     cbus_out = 0b0000
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     exp_out |= eeprom_force
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     cbus_in = 0b0101
     vport.cbus = cbus_in
     cbus = ftdi.get_cbus_gpio()
     exp_in = cbus_in & eeprom_mask
     self.assertEqual(cbus, exp_in)
     ftdi.close()
예제 #6
0
 def test_lc231x(self):
     """Check simple GPIO write and read sequence."""
     # load custom CBUS config, with:
     # CBUS0: GPIO (gpio)
     # CBUS1: TXLED
     # CBUS2: DRIVE0 (to light up RX green led)
     # CBUS3: GPIO (gpio)
     # only CBUS0 and CBUS3 are available on LC231X
     # CBUS1 is connected to TX led, CBUS2 to RX led
     with open('pyftdi/tests/resources/ft231x_cbus.yaml', 'rb') as yfp:
         self.loader.load(yfp)
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self.assertEqual(ftdi.has_cbus, True)
     vftdi = self.loader.get_virtual_ftdi(1, 1)
     vport = vftdi.get_port(1)
     # CBUS0: in, CBUS1: out, CBUS2: in, CBUS3: out
     #   however, only CBUS0 and CBUS3 are mapped as GPIO,
     #   CBUS1 not usable as IO, CBUS2 is fixed to low
     #   even if use mask is 1111
     eeprom_mask = 0b1001
     eeprom_force_low = 0b0100
     cbus_mask = 0b1111
     cbus_dir = 0b1010
     ftdi.set_cbus_direction(cbus_mask, cbus_dir)
     cbus_out = 0b1111
     # however, only CBUS0 & 3 are out, so CBUS1/CBUS2 should be ignored
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     self.assertEqual(vactive, eeprom_mask | eeprom_force_low)
     cbus_out = 0b0000
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     cbus_in = 0b0101
     vport.cbus = cbus_in
     cbus = ftdi.get_cbus_gpio()
     exp_in = cbus_in & eeprom_mask
     self.assertEqual(cbus, exp_in)
     ftdi.close()
예제 #7
0
파일: ftdi.py 프로젝트: zhang-xuting/pyftdi
 def test_close_on_disconnect(self):
     """Validate close after disconnect."""
     log = logging.getLogger('pyftdi.tests.ftdi')
     url = environ.get('FTDI_DEVICE', 'ftdi:///1')
     ftdi = Ftdi()
     ftdi.open_from_url(url)
     self.assertTrue(ftdi.is_connected, 'Unable to connect to FTDI')
     print('Please disconnect FTDI device')
     while ftdi.is_connected:
         try:
             ftdi.poll_modem_status()
         except FtdiError:
             break
         sleep(0.1)
     ftdi.close()
     print('Please reconnect FTDI device')
     while True:
         UsbTools.flush_cache()
         try:
             ftdi.open_from_url(url)
         except (FtdiError, UsbToolsError):
             log.debug('FTDI device not detected')
             sleep(0.1)
         except ValueError:
             log.warning('FTDI device not initialized')
             ftdi.close()
             sleep(0.1)
         else:
             log.info('FTDI device detected')
             break
     ftdi.poll_modem_status()
     ftdi.close()
예제 #8
0
 def test_random_access_read(self):
     """Check EEPROM random read access."""
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self._restore_eeprom(ftdi)
     ref_data = bytes(list(range(256)))
     size = len(ref_data)
     # out of bound
     self.assertRaises(ValueError, ftdi.read_eeprom, size, 2)
     # last bytes
     buf = ftdi.read_eeprom(size - 2, 2)
     self.assertEqual(buf[0:2], ref_data[-2:])
     self.assertEqual(buf[0:2], b'\xfe\xff')
     # out of bound
     self.assertRaises(ValueError, ftdi.read_eeprom, size - 2, 4)
     # unaligned access
     buf = ftdi.read_eeprom(1, 2)
     self.assertEqual(buf[0:2], ref_data[1:3])
     self.assertEqual(buf[0:2], b'\x01\x02')
     # long read, unaligned access, unaligned size
     buf = ftdi.read_eeprom(43, 43)
     self.assertEqual(len(buf), 43)
     self.assertEqual(buf, ref_data[43:86])
     ftdi.close()
예제 #9
0
class Blaster:
    def __init__(self, debug=False):
        self.blaster = Ftdi()
        self.virtual = True
        self.blaster.LATENCY_MIN = 1
        self.blaster.open(0x09FB, 0x6001)
        self.blaster.write_data_set_chunksize(1)
        self._last = None  # Last deferred TDO bit
        self.debug = debug

    def readwritebit(self, tms, tdi):
        val = ENA + tms * TMS + tdi * TDI
        self.blaster._write(bytes([val]))
        val = ENA + tms * TMS + tdi * TDI + TCK + TDO
        self.blaster._write(bytes([val]))
        rd = self.blaster.read_data_bytes(1, attempt=3)
        tdo = ord(rd) & 1
        return tdo

    def writebit(self, tms, tdi):
        val = ENA + tms * TMS + tdi * TDI
        self.blaster._write(bytes([val]))
        val = ENA + tms * TMS + tdi * TDI + TCK
        self.blaster._write(bytes([val]))

    def shiftinoutval(self, length, val):
        outval = 0
        if (self.debug):
            print(">>Out{2:d}:0x{0:x}={0:0{1}b}".format(val, length, length))
        nbytes = length >> 3
        if nbytes > 63:
            raise Exception("length > 63 bytes")
        self.blaster._write(bytes([ENA]))
        valcmd = bytearray([BYTESHIFT | DORDWR | (nbytes & 0x3F)])
        txlen = (nbytes & 0x3F) << 3
        valbytes = (val & ((1 << txlen) - 1)).to_bytes(nbytes & 0x3F, 'little')
        valcmd.extend(valbytes)
        self.blaster._write(valcmd)
        rd = self.blaster.read_data_bytes(nbytes & 0x3F, attempt=3)
        rxlen = len(rd)
        outval += int.from_bytes(rd, 'little')
        val >>= txlen
        if (self.debug):
            print(">>Inbytes{2:d}:0x{0:x}={0:0{1}b}".format(
                outval, rxlen * 8, rxlen))
        length -= txlen

        for i in range(length):
            bit = val & 1
            tdo = self.readwritebit(0, bit)
            val >>= 1
            outval += (tdo << (i + rxlen * 8))
        if (self.debug):
            print(">>In{2:d}:0x{0:x}={0:0{1}b}".format(outval,
                                                       (length + nbytes * 8),
                                                       length))
        return outval

    def shiftinval(self, length, val):
        if (self.debug):
            print(">>Out{2:d}:0x{0:x}={0:0{1}b}".format(val, length, length))
        nbytes = length >> 3
        while nbytes > 0:
            self.blaster._write(bytes([ENA]))
            valcmd = bytearray([BYTESHIFT | (nbytes & 0x3F)])
            len = (nbytes & 0x3F) << 3
            valbytes = (val & ((1 << len) - 1)).to_bytes(
                nbytes & 0x3F, 'little')
            valcmd.extend(valbytes)
            self.blaster._write(valcmd)
            val >>= len
            nbytes -= nbytes & 0x3F
            length -= len
        for i in range(length):
            bit = val & 1
            tdo = self.writebit(0, bit)
            val >>= 1

    def write_tms(self, tms, should_read=False):
        """Change the TAP controller state"""
        if not isinstance(tms, BitSequence):
            raise JtagError('Expect a BitSequence')
        tdo = 0
        for val in tms:
            if (self._last is not None):
                if (should_read):
                    if (self.debug):
                        print(">>Tmsout:" + bin(self._last))
                    tdo = self.readwritebit(val, int(self._last))
                    if (self.debug):
                        print(">>Tmsin:" + bin(tdo))
                else:
                    if (self.debug):
                        print(">>Tmsout:" + bin(self._last))
                    self.writebit(val, int(self._last))
            else:
                self.writebit(val, 0)
            should_read = False
            self._last = None
        return BitSequence(tdo, 1)

    def reset(self):
        """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

        # TAP reset (even with HW reset, could be removed though)
        self.write_tms(BitSequence('11111'))

    def write(self, out, use_last=True):
        if not isinstance(out, BitSequence):
            return JtagError('Expect a BitSequence')
        if use_last:
            (out, self._last) = (out[:-1], bool(out[-1]))
        length = len(out)
        self.shiftinval(length, int(out))

    def writeread(self, out, use_last=True):
        if not isinstance(out, BitSequence):
            return JtagError('Expect a BitSequence')
        if use_last:
            (out, self._last) = (out[:-1], bool(out[-1]))
        length = len(out)
        bs = BitSequence(value=self.shiftinoutval(length, int(out)),
                         length=length)
        return bs

    def read(self, length):
        """Read out a sequence of bits from TDO"""
        bs = BitSequence(value=self.shiftinoutval(length, 0), length=length)
        return bs

    def close(self):
        self.blaster.close()
예제 #10
0
파일: tau.py 프로젝트: ash2703/flirpy
class TeaxGrabber(Tau):
    """
    Data acquisition class for the Teax ThermalCapture Grabber USB
    
    """
    def __init__(self, vid=0x0403, pid=0x6010):
        self.dev = usb.core.find(idVendor=vid, idProduct=pid)

        self._ftdi = None
        
        if self.dev is not None:
            self.connect()
            self._sync_uart()
            #self._sync()
            
    def connect(self):
        reattach = False
        if self.dev.is_kernel_driver_active(0):
            reattach = True
            self.dev.detach_kernel_driver(0)
        
        self._claim_dev()
        
        self._ftdi = Ftdi()
        self._ftdi.open_from_device(self.dev)
        
        self._ftdi.set_bitmode(0xFF, Ftdi.BitMode.RESET)
        self._ftdi.set_bitmode(0xFF, Ftdi.BitMode.SYNCFF)
                  
    def _claim_dev(self):
        self.dev.reset()
        self._release()
        
        self.dev.set_configuration(1)

        usb.util.claim_interface(self.dev, 0)
        usb.util.claim_interface(self.dev, 1)
        
    def _release(self):
        for cfg in self.dev:
            for intf in cfg:
                if self.dev.is_kernel_driver_active(intf.bInterfaceNumber):
                    try:
                        self.dev.detach_kernel_driver(intf.bInterfaceNumber)
                    except usb.core.USBError as e:
                        print("Could not detatch kernel driver from interface({0}): {1}".format(intf.bInterfaceNumber, str(e)))
    
    def _read(self, n_bytes=0, packets_per_transfer=8, num_transfers=256):
        FTDI_PACKET_SIZE = 512
        
        if n_bytes == 0:
            n_bytes = packets_per_transfer * FTDI_PACKET_SIZE
  
        return self._ftdi.read_data(n_bytes)
                
    def _sync(self):
        data = self._read()
        
        magic = b"TEAX"
        
        while data.find(magic) == -1:
            data = self._read()
            
        return data[data.find(magic):]
    
    def _sync_uart(self, allow_timeout=False):
        data = self._read()
        
        magic = b"UART"
        
        t = time.time()
        while data.find(magic) == -1:
            data = self._read()
            
            if allow_timeout and time.time()-t > 0.2:
                break
            
        return data[data.find(magic):]

    def _convert_frame(self, data):
        raw_image = np.frombuffer(data[10:], dtype='uint8').reshape((512,2*642))
        raw_image = 0x3FFF & raw_image.view('uint16')[:,1:-1]
    
        return 0.04*raw_image - 273
    
    def _send_data(self, data):
        # header
        buffer = b"UART"
        buffer += int(len(data)).to_bytes(1, byteorder='big') # doesn't matter
        buffer += data
        #self._sync_uart(allow_timeout=True)
        self._ftdi.write_data(buffer)
        
    def _receive_data(self, length):
        
        length += length*5
        
        data = self._sync_uart()
        
        if len(data) < length:
            data += self._read(n_bytes=length-len(data))
            
            
        return data[:length][5::6]
        
    def grab(self, radiometric=True):
        
        data = b''
        data = self._sync()
        
        while len(data) < 657418:
            data += self._read(657418-len(data))
        
        if radiometric:
            data = self._convert_frame(data)
            
        return data
    
    def close(self):
        if self._ftdi is not None:
            self._ftdi.close()

    def __exit__(self, type, value, traceback):
        self.close()
        log.info("Disconnecting from camera.")
예제 #11
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)
예제 #12
0
파일: mockusb.py 프로젝트: cruizdeg/pyftdi
 def test_open_close(self):
     """Check simple open/close sequence."""
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self.assertEqual(ftdi.usb_path, (4, 5, 0))
     ftdi.close()
예제 #13
0
파일: mockusb.py 프로젝트: cruizdeg/pyftdi
 def test_open_mpsse(self):
     """Check simple MPSSE access."""
     ftdi = Ftdi()
     ftdi.open_mpsse_from_url('ftdi:///1')
     ftdi.close()
예제 #14
0
class I2cController:
    """I2c master.

       An I2c master should be instanciated only once for each FTDI port that
       supports MPSSE (one or two ports, depending on the FTDI device).

       Once configured, :py:func:`get_port` should be invoked to obtain an I2c
       port for each I2c slave to drive. I2c port should handle all I/O requests
       for its associated HW slave.

       It is not recommended to use I2cController :py:func:`read`,
       :py:func:`write` or :py:func:`exchange` directly.

       * ``SCK`` should be connected to ``A*BUS0``, and ``A*BUS7`` if clock
           stretching mode is enabled
       * ``SDA`` should be connected to ``A*BUS1`` **and** ``A*BUS2``
    """

    LOW = 0x00
    HIGH = 0xff
    BIT0 = 0x01
    IDLE = HIGH
    SCL_BIT = 0x01  #AD0
    SDA_O_BIT = 0x02  #AD1
    SDA_I_BIT = 0x04  #AD2
    SCL_FB_BIT = 0x80  #AD7
    PAYLOAD_MAX_LENGTH = 0x10000  # 16 bits max
    HIGHEST_I2C_ADDRESS = 0x78
    DEFAULT_BUS_FREQUENCY = 100000.0
    HIGH_BUS_FREQUENCY = 400000.0
    RETRY_COUNT = 3

    I2C_MASK = SCL_BIT | SDA_O_BIT | SDA_I_BIT
    I2C_MASK_CS = SCL_BIT | SDA_O_BIT | SDA_I_BIT | SCL_FB_BIT
    I2C_DIR = SCL_BIT | SDA_O_BIT

    I2C_100K = I2CTimings(4.0E-6, 4.7E-6, 4.0E-6, 4.7E-6)
    I2C_400K = I2CTimings(0.6E-6, 0.6E-6, 0.6E-6, 1.3E-6)
    I2C_1M = I2CTimings(0.26E-6, 0.26E-6, 0.26E-6, 0.5E-6)

    def __init__(self):
        self._ftdi = Ftdi()
        self._lock = Lock()
        self.log = getLogger('pyftdi.i2c')
        self._gpio_port = None
        self._gpio_dir = 0
        self._gpio_low = 0
        self._gpio_mask = 0
        self._i2c_mask = 0
        self._wide_port = False
        self._slaves = {}
        self._retry_count = self.RETRY_COUNT
        self._frequency = 0.0
        self._immediate = (Ftdi.SEND_IMMEDIATE, )
        self._read_bit = (Ftdi.READ_BITS_PVE_MSB, 0)
        self._read_byte = (Ftdi.READ_BYTES_PVE_MSB, 0, 0)
        self._write_byte = (Ftdi.WRITE_BYTES_NVE_MSB, 0, 0)
        self._nack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.HIGH)
        self._ack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.LOW)
        self._ck_delay = 1
        self._tristate = None
        self._tx_size = 1
        self._rx_size = 1
        self._ck_hd_sta = 0
        self._ck_su_sto = 0
        self._ck_idle = 0

    def set_retry_count(self, count):
        """Change the default retry count when a communication error occurs,
           before bailing out.
           :param int count: count of retries
        """
        if not isinstance(count, int) or not 0 < count <= 16:
            raise ValueError('Invalid retry count')
        self._retry_count = count

    def configure(self, url, **kwargs):
        """Configure the FTDI interface as a I2c master.

           :param str url: FTDI URL string, such as 'ftdi://ftdi:232h/1'
           :param kwargs: options to configure the I2C bus

           Accepted options:

           * ``frequency`` float value the I2C bus frequency in Hz
           * ``clockstretching`` boolean value to enable clockstreching.
             xD7 (GPIO7) pin should be connected back to xD0 (SCK)
        """
        for k in ('direction', 'initial'):
            if k in kwargs:
                del kwargs[k]
        if 'frequency' in kwargs:
            frequency = kwargs['frequency']
            del kwargs['frequency']
        else:
            frequency = self.DEFAULT_BUS_FREQUENCY
        # Fix frequency for 3-phase clock
        if frequency <= 100E3:
            timings = self.I2C_100K
        elif frequency <= 400E3:
            timings = self.I2C_100K
        else:
            timings = self.I2C_100K
        if 'clockstretching' in kwargs:
            clkstrch = bool(kwargs['clockstretching'])
            del kwargs['clockstretching']
        else:
            clkstrch = False
        self._ck_hd_sta = self._compute_delay_cycles(timings.t_hd_sta)
        self._ck_su_sto = self._compute_delay_cycles(timings.t_su_sto)
        ck_su_sta = self._compute_delay_cycles(timings.t_su_sta)
        ck_buf = self._compute_delay_cycles(timings.t_buf)
        self._ck_idle = max(ck_su_sta, ck_buf)
        self._ck_delay = ck_buf
        # as 3-phase clock frequency mode is required for I2C mode, the
        # FTDI clock should be adapted to match the required frequency.
        frequency = self._ftdi.open_mpsse_from_url(
            url,
            direction=self.I2C_DIR,
            initial=self.IDLE,
            frequency=(3.0 * frequency) / 2.0,
            **kwargs)
        self._frequency = (2.0 * frequency) / 3.0
        self._tx_size, self._rx_size = self._ftdi.fifo_sizes
        self._ftdi.enable_adaptive_clock(clkstrch)
        self._ftdi.enable_3phase_clock(True)
        try:
            self._ftdi.enable_drivezero_mode(self.SCL_BIT | self.SDA_O_BIT
                                             | self.SDA_I_BIT)
        except FtdiFeatureError:
            self._tristate = (Ftdi.SET_BITS_LOW, self.LOW, self.SCL_BIT)
        self._wide_port = self._ftdi.has_wide_port
        if clkstrch:
            self._i2c_mask = self.I2C_MASK_CS
        else:
            self._i2c_mask = self.I2C_MASK

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

    def get_port(self, address):
        """Obtain an I2cPort to drive an I2c slave.

           :param int address: the address on the I2C bus
           :return: an I2cPort instance
           :rtype: :py:class:`I2cPort`
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address not in self._slaves:
            self._slaves[address] = I2cPort(self, address)
        return self._slaves[address]

    def get_gpio(self):
        """Retrieve the GPIO port.

           :return GPIO port
        """
        with self._lock:
            if not self._ftdi:
                raise I2cIOError("FTDI controller not initialized")
            if not self._gpio_port:
                self._gpio_port = I2cGpioPort(self)
            return self._gpio_port

    @property
    def configured(self):
        """Test whether the device has been properly configured.

           :return: True if configured
        """
        return bool(self._ftdi) and bool(self._start)

    @classmethod
    def validate_address(cls, address):
        """Assert an I2C slave address is in the supported range.
           None is a special bypass address.

           :param address: the address on the I2C bus
           :type address: int or None
           :raise I2cIOError: if the I2C slave address is not supported
        """
        if address is None:
            return
        if address > cls.HIGHEST_I2C_ADDRESS:
            raise I2cIOError("No such I2c slave: 0x%02x" % address)

    @property
    def frequency_max(self):
        """Provides the maximum I2c clock frequency.
        """
        return self._ftdi.frequency_max

    @property
    def frequency(self):
        """Provides the current I2c clock frequency.

           :return: the I2C bus clock
           :rtype: float
        """
        return self._frequency

    @property
    def direction(self):
        """Provide the FTDI GPIO direction"""
        return self.I2C_DIR | self._gpio_dir

    @property
    def gpio_pins(self):
        """Report the configured GPIOs as a bitfield"""
        with self._lock:
            return self._gpio_mask

    @property
    def gpio_all_pins(self):
        """Report the addressable GPIOs as a bitfield"""
        mask = (1 << self.width) - 1
        with self._lock:
            return mask & ~self._i2c_mask

    @property
    def width(self):
        """Report the FTDI count of addressable pins.

           :return: the count of IO pins (including I2C ones).
        """
        return 16 if self._wide_port else 8

    def read(self, address, readlen=1, relax=True):
        """Read one or more bytes from a remote slave

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param int readlen: count of bytes to read out.
           :param bool relax: not used
           :return: read bytes
           :rtype: array
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)

           Most I2C devices require a register address to read out
           check out the exchange() method.
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            i2caddress |= self.BIT0
        retries = self._retry_count
        do_epilog = True
        with self._lock:
            while True:
                try:
                    self._do_prolog(i2caddress)
                    data = self._do_read(readlen)
                    do_epilog = relax
                    return data
                except I2cNackError:
                    retries -= 1
                    if not retries:
                        raise
                    self.log.warning('Retry read')
                finally:
                    if do_epilog:
                        self._do_epilog()

    def write(self, address, out, relax=True):
        """Write one or more bytes to a remote slave

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param out: the byte buffer to send
           :type out: array or bytes or list(int)
           :param bool relax: whether to relax the bus (emit STOP) or not
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)

           Most I2C devices require a register address to write into. It should
           be added as the first (byte)s of the output buffer.
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
        retries = self._retry_count
        do_epilog = True
        with self._lock:
            while True:
                try:
                    self._do_prolog(i2caddress)
                    self._do_write(out)
                    do_epilog = relax
                    return
                except I2cNackError:
                    retries -= 1
                    if not retries:
                        raise
                    self.log.warning('Retry write')
                finally:
                    if do_epilog:
                        self._do_epilog()

    def exchange(self, address, out, readlen=0, relax=True):
        """Send a byte sequence to a remote slave followed with
           a read request of one or more bytes.

           This command is useful to tell the slave what data
           should be read out.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param out: the byte buffer to send
           :type out: array or bytes or list(int)
           :param int readlen: count of bytes to read out.
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: read bytes
           :rtype: array
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if readlen < 1:
            raise I2cIOError('Nothing to read')
        if readlen > (self.PAYLOAD_MAX_LENGTH / 3 - 1):
            raise I2cIOError("Input payload is too large")
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
        retries = self._retry_count
        do_epilog = True
        with self._lock:
            while True:
                try:
                    self._do_prolog(i2caddress)
                    self._do_write(out)
                    self._do_prolog(i2caddress | self.BIT0)
                    if readlen:
                        data = self._do_read(readlen)
                    do_epilog = relax
                    return data
                except I2cNackError:
                    retries -= 1
                    if not retries:
                        raise
                    self.log.warning('Retry exchange')
                finally:
                    if do_epilog:
                        self._do_epilog()

    def poll(self, address, write=False, relax=True):
        """Poll a remote slave, expect ACK or NACK.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param bool write: poll in write mode (vs. read)
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: True if the slave acknowledged, False otherwise
           :rtype: bool
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            if not write:
                i2caddress |= self.BIT0
        do_epilog = True
        with self._lock:
            try:
                self._do_prolog(i2caddress)
                do_epilog = relax
                return True
            except I2cNackError:
                self.log.info('Not ready')
                return False
            finally:
                if do_epilog:
                    self._do_epilog()

    def poll_cond(self, address, fmt, mask, value, count, relax=True):
        """Poll a remove slave, watching for condition to satisfy.
           On each poll cycle, a repeated start condition is emitted, without
           releasing the I2C bus, and an ACK is returned to the slave.

           If relax is set, this method releases the I2C bus however it leaves.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param str fmt: struct format for poll register
           :param int mask: binary mask to apply on the condition register
                before testing for the value
           :param int value: value to test the masked condition register
                against. Condition is satisfied when register & mask == value
           :param int count: maximum poll count before raising a timeout
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: the polled register value, or None if poll failed
           :rtype: array or None
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            i2caddress |= self.BIT0
        do_epilog = True
        with self._lock:
            try:
                retry = 0
                while retry < count:
                    retry += 1
                    size = scalc(fmt)
                    self._do_prolog(i2caddress)
                    data = self._do_read(size)
                    self.log.debug("Poll data: %s", hexlify(data).decode())
                    cond, = sunpack(fmt, data)
                    if (cond & mask) == value:
                        self.log.debug('Poll condition matched')
                        break
                    else:
                        data = None
                        self.log.debug('Poll condition not fulfilled: %x/%x',
                                       cond & mask, value)
                do_epilog = relax
                if not data:
                    self.log.warning('Poll condition failed')
                return data
            except I2cNackError:
                self.log.info('Not ready')
                return None
            finally:
                if do_epilog:
                    self._do_epilog()

    def flush(self):
        """Flush the HW FIFOs
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        with self._lock:
            self._ftdi.write_data(self._immediate)
            self._ftdi.purge_buffers()

    def read_gpio(self, with_output=False):
        """Read GPIO port

           :param bool with_output: set to unmask output pins
           :return: the GPIO port pins as a bitfield
           :rtype: int
        """
        with self._lock:
            data = self._read_raw(self._wide_port)
        value = data & self._gpio_mask
        if not with_output:
            value &= ~self._gpio_dir
        return value

    def write_gpio(self, value):
        """Write GPIO port

           :param int value: the GPIO port pins as a bitfield
        """
        with self._lock:
            if (value & self._gpio_dir) != value:
                raise I2cIOError('No such GPO pins: %04x/%04x' %
                                 (self._gpio_dir, value))
            # perform read-modify-write
            use_high = self._wide_port and (self.direction & 0xff00)
            data = self._read_raw(use_high)
            data &= ~self._gpio_mask
            data |= value
            self._write_raw(data, use_high)
            self._gpio_low = data & 0xFF & ~self._i2c_mask

    def set_gpio_direction(self, pins, direction):
        """Change the direction of the GPIO pins

           :param int pins: which GPIO pins should be reconfigured
           :param int direction: direction bitfield (on for output)
        """
        with self._lock:
            if pins & self._i2c_mask:
                raise I2cIOError('Cannot access I2C pins as GPIO')
            gpio_width = self._wide_port and 16 or 8
            gpio_mask = (1 << gpio_width) - 1
            gpio_mask &= ~self._i2c_mask
            if (pins & gpio_mask) != pins:
                raise I2cIOError('No such GPIO pin(s)')
            self._gpio_dir &= ~pins
            self._gpio_dir |= (pins & direction)
            self._gpio_mask = gpio_mask & pins

    @property
    def _data_lo(self):
        return (Ftdi.SET_BITS_LOW, self.SCL_BIT | self._gpio_low,
                self.I2C_DIR | (self._gpio_dir & 0xFF))

    @property
    def _clk_lo_data_hi(self):
        return (Ftdi.SET_BITS_LOW, self.SDA_O_BIT | self._gpio_low,
                self.I2C_DIR | (self._gpio_dir & 0xFF))

    @property
    def _clk_lo_data_lo(self):
        return (Ftdi.SET_BITS_LOW, self._gpio_low,
                self.I2C_DIR | (self._gpio_dir & 0xFF))

    @property
    def _idle(self):
        return (Ftdi.SET_BITS_LOW, self.I2C_DIR | self._gpio_low,
                self.I2C_DIR | (self._gpio_dir & 0xFF))

    @property
    def _start(self):
        return self._data_lo * self._ck_hd_sta + \
               self._clk_lo_data_lo * self._ck_hd_sta

    @property
    def _stop(self):
        return self._clk_lo_data_hi * self._ck_hd_sta + \
               self._data_lo * self._ck_su_sto + \
               self._idle * self._ck_idle

    def _compute_delay_cycles(self, value):
        # approx ceiling without relying on math module
        # the bit delay is far from being precisely known anyway
        bit_delay = self._ftdi.mpsse_bit_delay
        return max(1, int((value + bit_delay) / bit_delay))

    def _read_raw(self, read_high):
        if read_high:
            cmd = array(
                'B',
                [Ftdi.GET_BITS_LOW, Ftdi.GET_BITS_HIGH, Ftdi.SEND_IMMEDIATE])
            fmt = '<H'
        else:
            cmd = array('B', [Ftdi.GET_BITS_LOW, Ftdi.SEND_IMMEDIATE])
            fmt = 'B'
        self._ftdi.write_data(cmd)
        size = scalc(fmt)
        data = self._ftdi.read_data_bytes(size, 4)
        if len(data) != size:
            raise I2cIOError('Cannot read GPIO')
        value, = sunpack(fmt, data)
        return value

    def _write_raw(self, data, write_high):
        direction = self.direction
        low_data = data & 0xFF
        low_dir = direction & 0xFF
        if write_high:
            high_data = (data >> 8) & 0xFF
            high_dir = (direction >> 8) & 0xFF
            cmd = array('B', [
                Ftdi.SET_BITS_LOW, low_data, low_dir, Ftdi.SET_BITS_HIGH,
                high_data, high_dir
            ])
        else:
            cmd = array('B', [Ftdi.SET_BITS_LOW, low_data, low_dir])
        self._ftdi.write_data(cmd)

    def _do_prolog(self, i2caddress):
        if i2caddress is None:
            return
        self.log.debug('   prolog 0x%x', i2caddress >> 1)
        cmd = bytearray(self._idle)
        cmd.extend(self._start)
        cmd.extend(self._write_byte)
        cmd.append(i2caddress)
        if self._tristate:
            cmd.extend(self._tristate)
            cmd.extend(self._read_bit)
            cmd.extend(self._clk_lo_data_hi)
        else:
            cmd.extend(self._clk_lo_data_hi)
            cmd.extend(self._read_bit)
        cmd.extend(self._immediate)
        self._ftdi.write_data(cmd)
        ack = self._ftdi.read_data_bytes(1, 4)
        if not ack:
            raise I2cIOError('No answer from FTDI')
        if ack[0] & self.BIT0:
            self.log.warning('NACK @ 0x%02x', (i2caddress >> 1))
            raise I2cNackError('NACK from slave')

    def _do_epilog(self):
        self.log.debug('   epilog')
        cmd = bytearray(self._stop)
        self._ftdi.write_data(cmd)
        # be sure to purge the MPSSE reply
        self._ftdi.read_data_bytes(1, 1)

    def _do_read(self, readlen):
        self.log.debug('- read %d byte(s)', readlen)
        if not readlen:
            # force a real read request on device, but discard any result
            cmd = bytearray()
            cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            self._ftdi.read_data_bytes(0, 4)
            return bytearray()
        if self._tristate:
            read_byte = self._tristate + \
                        self._read_byte + \
                        self._clk_lo_data_hi
            read_not_last = \
                read_byte + self._ack + self._clk_lo_data_lo * self._ck_delay
            read_last = \
                read_byte + self._nack + self._clk_lo_data_hi * self._ck_delay
        else:
            read_not_last = \
                self._read_byte + self._ack + \
                self._clk_lo_data_hi * self._ck_delay
            read_last = \
                self._read_byte + self._nack + \
                self._clk_lo_data_hi * self._ck_delay
        # maximum RX size to fit in FTDI FIFO, minus 2 status bytes
        chunk_size = self._rx_size - 2
        cmd_size = len(read_last)
        # limit RX chunk size to the count of I2C packable commands in the FTDI
        # TX FIFO (minus one byte for the last 'send immediate' command)
        tx_count = (self._tx_size - 1) // cmd_size
        chunk_size = min(tx_count, chunk_size)
        chunks = []
        cmd = None
        rem = readlen
        while rem:
            if rem > chunk_size:
                if not cmd:
                    cmd = bytearray()
                    cmd.extend(read_not_last * chunk_size)
                    size = chunk_size
            else:
                cmd = bytearray()
                cmd.extend(read_not_last * (rem - 1))
                cmd.extend(read_last)
                cmd.extend(self._immediate)
                size = rem
            self._ftdi.write_data(cmd)
            buf = self._ftdi.read_data_bytes(size, 4)
            self.log.debug('- read %d byte(s): %s', len(buf),
                           hexlify(buf).decode())
            chunks.append(buf)
            rem -= size
        return bytearray(b''.join(chunks))

    def _do_write(self, out):
        if not isinstance(out, array):
            out = bytearray(out)
        if not out:
            return
        self.log.debug('- write %d byte(s): %s', len(out),
                       hexlify(out).decode())
        for byte in out:
            cmd = bytearray(self._write_byte)
            cmd.append(byte)
            if self._tristate:
                cmd.extend(self._tristate)
                cmd.extend(self._read_bit)
                cmd.extend(self._clk_lo_data_hi)
            else:
                cmd.extend(self._clk_lo_data_hi)
                cmd.extend(self._read_bit)
            cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            ack = self._ftdi.read_data_bytes(1, 4)
            if not ack:
                msg = 'No answer from FTDI'
                self.log.critical(msg)
                raise I2cIOError(msg)
            if ack[0] & self.BIT0:
                msg = 'NACK from slave'
                self.log.warning(msg)
                raise I2cNackError(msg)
예제 #15
0
파일: i2c.py 프로젝트: ebouaziz/pyftdi
class I2cController:
    """I2c master.

       An I2c master should be instanciated only once for each FTDI port that
       supports MPSSE (one or two ports, depending on the FTDI device).

       Once configured, :py:func:`get_port` should be invoked to obtain an I2c
       port for each I2c slave to drive. I2cport should handle all I/O requests
       for its associated HW slave.

       It is not recommended to use I2cController :py:func:`read`,
       :py:func:`write` or :py:func:`exchange` directly.
    """

    LOW = 0x00
    HIGH = 0xff
    BIT0 = 0x01
    IDLE = HIGH
    SCL_BIT = 0x01
    SDA_O_BIT = 0x02
    SDA_I_BIT = 0x04
    PAYLOAD_MAX_LENGTH = 0x10000  # 16 bits max
    HIGHEST_I2C_ADDRESS = 0x78
    DEFAULT_BUS_FREQUENCY = 100000.0
    HIGH_BUS_FREQUENCY = 400000.0
    RETRY_COUNT = 3

    I2C_100K = I2CTimings(4.0E-6, 4.7E-6, 4.0E-6, 4.7E-6)
    I2C_400K = I2CTimings(0.6E-6, 0.6E-6, 0.6E-6, 1.3E-6)
    I2C_1M = I2CTimings(0.26E-6, 0.26E-6, 0.26E-6, 0.5E-6)

    def __init__(self):
        self._ftdi = Ftdi()
        self.log = getLogger('pyftdi.i2c')
        self._slaves = {}
        self._retry_count = self.RETRY_COUNT
        self._frequency = 0.0
        self._direction = self.SCL_BIT | self.SDA_O_BIT
        self._immediate = (Ftdi.SEND_IMMEDIATE,)
        self._idle = (Ftdi.SET_BITS_LOW, self.IDLE, self._direction)
        self._data_lo = (Ftdi.SET_BITS_LOW,
                         self.IDLE & ~self.SDA_O_BIT, self._direction)
        self._clk_lo_data_lo = (Ftdi.SET_BITS_LOW,
                                self.IDLE & ~(self.SDA_O_BIT | self.SCL_BIT),
                                self._direction)
        self._clk_lo_data_hi = (Ftdi.SET_BITS_LOW,
                                self.IDLE & ~self.SCL_BIT,
                                self._direction)
        self._read_bit = (Ftdi.READ_BITS_PVE_MSB, 0)
        self._read_byte = (Ftdi.READ_BYTES_PVE_MSB, 0, 0)
        self._write_byte = (Ftdi.WRITE_BYTES_NVE_MSB, 0, 0)
        self._nack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.HIGH)
        self._ack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.LOW)
        self._start = None
        self._stop = None
        self._ck_delay = 1
        self._tristate = None
        self._tx_size = 1
        self._rx_size = 1

    def _compute_delay_cycles(self, value):
        # approx ceiling without relying on math module
        # the bit delay is far from being precisely known anyway
        bit_delay = self._ftdi.mpsse_bit_delay
        return max(1, int((value + bit_delay) / bit_delay))

    def set_retry_count(self, count):
        """Change the default retry count when a communication error occurs,
           before bailing out.
           :param int count: count of retries
        """
        if not isinstance(count, int) or not 0 < count <= 16:
            raise ValueError('Invalid retry count')
        self._retry_count = count

    def configure(self, url, **kwargs):
        """Configure the FTDI interface as a I2c master.

           :param str url: FTDI URL string, such as 'ftdi://ftdi:232h/1'
           :param kwargs: options to configure the I2C bus

           Accepted options:

           * ``frequency`` the I2C bus frequency in Hz
        """
        for k in ('direction', 'initial'):
            if k in kwargs:
                del kwargs[k]
        if 'frequency' in kwargs:
            frequency = kwargs['frequency']
            del kwargs['frequency']
        else:
            frequency = self.DEFAULT_BUS_FREQUENCY
        # Fix frequency for 3-phase clock
        if frequency <= 100E3:
            timings = self.I2C_100K
        elif frequency <= 400E3:
            timings = self.I2C_100K
        else:
            timings = self.I2C_100K
        ck_hd_sta = self._compute_delay_cycles(timings.t_hd_sta)
        ck_su_sta = self._compute_delay_cycles(timings.t_su_sta)
        ck_su_sto = self._compute_delay_cycles(timings.t_su_sto)
        ck_buf = self._compute_delay_cycles(timings.t_buf)
        ck_idle = max(ck_su_sta, ck_buf)
        self._ck_delay = ck_buf
        self._start = (self._data_lo * ck_hd_sta +
                       self._clk_lo_data_lo * ck_hd_sta)
        self._stop = (self._clk_lo_data_lo * ck_hd_sta +
                      self._data_lo*ck_su_sto +
                      self._idle*ck_idle)
        frequency = (3.0*frequency)/2.0
        self._frequency = self._ftdi.open_mpsse_from_url(
            url, direction=self._direction, initial=self.IDLE,
            frequency=frequency, **kwargs)
        self._tx_size, self._rx_size = self._ftdi.fifo_sizes
        self._ftdi.enable_adaptive_clock(False)
        self._ftdi.enable_3phase_clock(True)
        try:
            self._ftdi.enable_drivezero_mode(self.SCL_BIT |
                                             self.SDA_O_BIT |
                                             self.SDA_I_BIT)
        except FtdiFeatureError:
            self._tristate = (Ftdi.SET_BITS_LOW, self.LOW, self.SCL_BIT)

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

    def get_port(self, address):
        """Obtain an I2cPort to drive an I2c slave.

           :param int address: the address on the I2C bus
           :return: an I2cPort instance
           :rtype: :py:class:`I2cPort`
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address not in self._slaves:
            self._slaves[address] = I2cPort(self, address)
        return self._slaves[address]

    @property
    def configured(self):
        """Test whether the device has been properly configured.

           :return: True if configured
        """
        return bool(self._ftdi) and bool(self._start)

    @classmethod
    def validate_address(cls, address):
        """Assert an I2C slave address is in the supported range.
           None is a special bypass address.

           :param address: the address on the I2C bus
           :type address: int or None
           :raise I2cIOError: if the I2C slave address is not supported
        """
        if address is None:
            return
        if address > cls.HIGHEST_I2C_ADDRESS:
            raise I2cIOError("No such I2c slave: 0x%02x" % address)

    @property
    def frequency_max(self):
        """Provides the maximum I2c clock frequency.
        """
        return self._ftdi.frequency_max

    @property
    def frequency(self):
        """Provides the current I2c clock frequency.

           :return: the I2C bus clock
           :rtype: float
        """
        return self._frequency

    def read(self, address, readlen=1, relax=True):
        """Read one or more bytes from a remote slave

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param int readlen: count of bytes to read out.
           :param bool relax: not used
           :return: read bytes
           :rtype: array
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)

           Most I2C devices require a register address to read out
           check out the exchange() method.
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            i2caddress |= self.BIT0
        retries = self._retry_count
        do_epilog = True
        while True:
            try:
                self._do_prolog(i2caddress)
                data = self._do_read(readlen)
                do_epilog = relax
                return data
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry read')
            finally:
                if do_epilog:
                    self._do_epilog()

    def write(self, address, out, relax=True):
        """Write one or more bytes to a remote slave

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param out: the byte buffer to send
           :type out: array or bytes or list(int)
           :param bool relax: whether to relax the bus (emit STOP) or not
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)

           Most I2C devices require a register address to write into. It should
           be added as the first (byte)s of the output buffer.
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
        retries = self._retry_count
        do_epilog = True
        while True:
            try:
                self._do_prolog(i2caddress)
                self._do_write(out)
                do_epilog = relax
                return
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry write')
            finally:
                if do_epilog:
                    self._do_epilog()

    def exchange(self, address, out, readlen=0, relax=True):
        """Send a byte sequence to a remote slave followed with
           a read request of one or more bytes.

           This command is useful to tell the slave what data
           should be read out.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param out: the byte buffer to send
           :type out: array or bytes or list(int)
           :param int readlen: count of bytes to read out.
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: read bytes
           :rtype: array
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if readlen < 1:
            raise I2cIOError('Nothing to read')
        if readlen > (I2cController.PAYLOAD_MAX_LENGTH/3-1):
            raise I2cIOError("Input payload is too large")
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
        retries = self._retry_count
        do_epilog = True
        while True:
            try:
                self._do_prolog(i2caddress)
                self._do_write(out)
                self._do_prolog(i2caddress | self.BIT0)
                if readlen:
                    data = self._do_read(readlen)
                do_epilog = relax
                return data
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry exchange')
            finally:
                if do_epilog:
                    self._do_epilog()

    def poll(self, address, write=False, relax=True):
        """Poll a remote slave, expect ACK or NACK.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param bool write: poll in write mode (vs. read)
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: True if the slave acknowledged, False otherwise
           :rtype: bool
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            if not write:
                i2caddress |= self.BIT0
        do_epilog = True
        try:
            self._do_prolog(i2caddress)
            do_epilog = relax
            return True
        except I2cNackError:
            self.log.info('Not ready')
            return False
        finally:
            if do_epilog:
                self._do_epilog()

    def poll_cond(self, address, fmt, mask, value, count, relax=True):
        """Poll a remove slave, watching for condition to satisfy.
           On each poll cycle, a repeated start condition is emitted, without
           releasing the I2C bus, and an ACK is returned to the slave.

           If relax is set, this method releases the I2C bus however it leaves.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param str fmt: struct format for poll register
           :param int mask: binary mask to apply on the condition register
                before testing for the value
           :param int value: value to test the masked condition register
                against. Condition is satisfied when register & mask == value
           :param int count: maximum poll count before raising a timeout
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: the polled register value, or None if poll failed
           :rtype: array or None
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            i2caddress |= self.BIT0
        do_epilog = True
        try:
            retry = 0
            while retry < count:
                retry += 1
                size = scalc(fmt)
                self._do_prolog(i2caddress)
                data = self._do_read(size)
                self.log.debug("Poll data: %s", hexlify(data).decode())
                cond, = sunpack(fmt, data)
                if (cond & mask) == value:
                    self.log.debug('Poll condition matched')
                    break
                else:
                    data = None
                    self.log.debug('Poll condition not fulfilled: %x/%x',
                                   cond & mask, value)
            do_epilog = relax
            if not data:
                self.log.warning('Poll condition failed')
            return data
        except I2cNackError:
            self.log.info('Not ready')
            return None
        finally:
            if do_epilog:
                self._do_epilog()

    def flush(self):
        """Flush the HW FIFOs
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()

    def _do_prolog(self, i2caddress):
        if i2caddress is None:
            return
        self.log.debug('   prolog 0x%x', i2caddress >> 1)
        cmd = array('B', self._idle)
        cmd.extend(self._start)
        cmd.extend(self._write_byte)
        cmd.append(i2caddress)
        if self._tristate:
            cmd.extend(self._tristate)
            cmd.extend(self._read_bit)
            cmd.extend(self._clk_lo_data_hi)
        else:
            cmd.extend(self._clk_lo_data_hi)
            cmd.extend(self._read_bit)
        cmd.extend(self._immediate)
        self._ftdi.write_data(cmd)
        ack = self._ftdi.read_data_bytes(1, 4)
        if not ack:
            raise I2cIOError('No answer from FTDI')
        if ack[0] & self.BIT0:
            self.log.warning('NACK')
            raise I2cNackError('NACK from slave')

    def _do_epilog(self):
        self.log.debug('   epilog')
        cmd = array('B', self._stop)
        self._ftdi.write_data(cmd)
        # be sure to purge the MPSSE reply
        self._ftdi.read_data_bytes(1, 1)

    def _do_read(self, readlen):
        self.log.debug('- read %d byte(s)', readlen)
        if not readlen:
            # force a real read request on device, but discard any result
            cmd = array('B')
            cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            self._ftdi.read_data_bytes(0, 4)
            return array('B')
        if self._tristate:
            read_byte = self._tristate + \
                        self._read_byte + \
                        self._clk_lo_data_hi
            read_not_last = \
                read_byte + self._ack + self._clk_lo_data_lo * self._ck_delay
            read_last = \
                read_byte + self._nack + self._clk_lo_data_hi * self._ck_delay
        else:
            read_not_last = \
                self._read_byte + self._ack + \
                self._clk_lo_data_hi * self._ck_delay
            read_last = \
                self._read_byte + self._nack + \
                self._clk_lo_data_hi * self._ck_delay
        # maximum RX size to fit in FTDI FIFO, minus 2 status bytes
        chunk_size = self._rx_size-2
        cmd_size = len(read_last)
        # limit RX chunk size to the count of I2C packable commands in the FTDI
        # TX FIFO (minus one byte for the last 'send immediate' command)
        tx_count = (self._tx_size-1) // cmd_size
        chunk_size = min(tx_count, chunk_size)
        chunks = []
        cmd = None
        rem = readlen
        while rem:
            if rem > chunk_size:
                if not cmd:
                    cmd = array('B')
                    cmd.extend(read_not_last * chunk_size)
                    size = chunk_size
            else:
                cmd = array('B')
                cmd.extend(read_not_last * (rem-1))
                cmd.extend(read_last)
                cmd.extend(self._immediate)
                size = rem
            self._ftdi.write_data(cmd)
            buf = self._ftdi.read_data_bytes(size, 4)
            self.log.debug('- read %d byte(s): %s',
                           len(buf), hexlify(buf).decode())
            chunks.append(buf)
            rem -= size
        return array('B', b''.join(chunks))

    def _do_write(self, out):
        if not isinstance(out, array):
            out = array('B', out)
        if not out:
            return
        self.log.debug('- write %d byte(s): %s',
                       len(out), hexlify(out).decode())
        for byte in out:
            cmd = array('B', self._write_byte)
            cmd.append(byte)
            if self._tristate:
                cmd.extend(self._tristate)
                cmd.extend(self._read_bit)
                cmd.extend(self._clk_lo_data_hi)
            else:
                cmd.extend(self._clk_lo_data_hi)
                cmd.extend(self._read_bit)
            cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            ack = self._ftdi.read_data_bytes(1, 4)
            if not ack:
                msg = 'No answer from FTDI'
                self.log.critical(msg)
                raise I2cIOError(msg)
            if ack[0] & self.BIT0:
                msg = 'NACK from slave'
                self.log.warning(msg)
                raise I2cNackError(msg)
예제 #16
0
                stop = True

    result = b''
    frame_ = False
    while not frame_:
        res = ret.read_data(65536)
        if res != b'':
            result += res
            print(res)
            if len(result) >= datasize:
                frame_ = True

    result = result[:361744]
    #print(len(result))
    result = result[16:-(16 + 752)]
    #print(len(result))
    np_im = np.fromstring(result, dtype=np.uint8).reshape((752, 480))

    img = cv2.cvtColor(np_im, cv2.COLOR_GRAY2BGR)
    img = np.rot90(img)

    pyimg = pygame.surfarray.make_surface(img)
    screen.blit(pyimg, (0, 0))
    pygame.display.flip()

ret.write_data(b'P')
ret.close()

#pygame.quit()

#print(len(result))
예제 #17
0
class I2cController:
    """I2c master.

       An I2c master should be instanciated only once for each FTDI port that
       supports MPSSE (one or two ports, depending on the FTDI device).

       Once configured, :py:func:`get_port` should be invoked to obtain an I2c
       port for each I2c slave to drive. I2cport should handle all I/O requests
       for its associated HW slave.

       It is not recommended to use I2cController :py:func:`read`,
       :py:func:`write` or :py:func:`exchange` directly.
    """

    LOW = 0x00
    HIGH = 0xff
    BIT0 = 0x01
    IDLE = HIGH
    SCL_BIT = 0x01
    SDA_O_BIT = 0x02
    SDA_I_BIT = 0x04
    PAYLOAD_MAX_LENGTH = 0x10000  # 16 bits max
    HIGHEST_I2C_ADDRESS = 0x78
    DEFAULT_BUS_FREQUENCY = 100000.0
    HIGH_BUS_FREQUENCY = 400000.0
    RETRY_COUNT = 3

    I2C_100K = I2CTimings(4.0E-6, 4.7E-6, 4.0E-6, 4.7E-6)
    I2C_400K = I2CTimings(0.6E-6, 0.6E-6, 0.6E-6, 1.3E-6)
    I2C_1M = I2CTimings(0.26E-6, 0.26E-6, 0.26E-6, 0.5E-6)

    def __init__(self, ftdi = None):
        if not ftdi:
            self._ftdi = Ftdi()
        else:
            self._ftdi = ftdi

        self.log = getLogger('pyftdi.i2c')
        self._slaves = {}
        self._retry_count = self.RETRY_COUNT
        self._frequency = 0.0
        self._direction = self.SCL_BIT | self.SDA_O_BIT
        self._immediate = (Ftdi.SEND_IMMEDIATE,)
        self._idle = (Ftdi.SET_BITS_LOW, self.IDLE, self._direction)
        self._data_lo = (Ftdi.SET_BITS_LOW,
                         self.IDLE & ~self.SDA_O_BIT, self._direction)
        self._clk_lo_data_lo = (Ftdi.SET_BITS_LOW,
                                self.IDLE & ~(self.SDA_O_BIT | self.SCL_BIT),
                                self._direction)
        self._clk_lo_data_hi = (Ftdi.SET_BITS_LOW,
                                self.IDLE & ~self.SCL_BIT,
                                self._direction)
        self._read_bit = (Ftdi.READ_BITS_PVE_MSB, 0)
        self._read_byte = (Ftdi.READ_BYTES_PVE_MSB, 0, 0)
        self._write_byte = (Ftdi.WRITE_BYTES_NVE_MSB, 0, 0)
        self._nack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.HIGH)
        self._ack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.LOW)
        self._start = None
        self._stop = None
        self._ck_delay = 1
        self._tristate = None
        self._tx_size = 1
        self._rx_size = 1

    def _compute_delay_cycles(self, value):
        # approx ceiling without relying on math module
        # the bit delay is far from being precisely known anyway
        bit_delay = self._ftdi.mpsse_bit_delay
        return max(1, int((value + bit_delay) / bit_delay))

    def set_retry_count(self, count):
        """Change the default retry count when a communication error occurs,
           before bailing out.
           :param int count: count of retries
        """
        if not isinstance(count, int) or not 0 < count <= 16:
            raise ValueError('Invalid retry count')
        self._retry_count = count

    def configure(self, **kwargs):
        """Configure the FTDI interface as a I2c master.

           :param str url: FTDI URL string, such as 'ftdi://ftdi:232h/1'
           :param kwargs: options to configure the I2C bus

           Accepted options:

           * ``frequency`` the I2C bus frequency in Hz
        """
        for k in ('direction', 'initial'):
            if k in kwargs:
                del kwargs[k]
        if 'url' in kwargs:
            url = kwargs['url']
            del kwargs['url']
        else:
            url = None
        if 'frequency' in kwargs:
            frequency = kwargs['frequency']
            del kwargs['frequency']
        else:
            frequency = self.DEFAULT_BUS_FREQUENCY
        # Fix frequency for 3-phase clock
        if frequency <= 100E3:
            timings = self.I2C_100K
        elif frequency <= 400E3:
            timings = self.I2C_100K
        else:
            timings = self.I2C_100K
        ck_hd_sta = self._compute_delay_cycles(timings.t_hd_sta)
        ck_su_sta = self._compute_delay_cycles(timings.t_su_sta)
        ck_su_sto = self._compute_delay_cycles(timings.t_su_sto)
        ck_buf = self._compute_delay_cycles(timings.t_buf)
        ck_idle = max(ck_su_sta, ck_buf)
        self._ck_delay = ck_buf
        self._start = (self._data_lo * ck_hd_sta +
                       self._clk_lo_data_lo * ck_hd_sta)
        self._stop = (self._clk_lo_data_lo * ck_hd_sta +
                      self._data_lo*ck_su_sto +
                      self._idle*ck_idle)
        frequency = (3.0*frequency)/2.0
        if url:
            self._frequency = self._ftdi.open_mpsse_from_url(
                url, direction=self._direction, initial=self.IDLE,
                frequency=frequency, **kwargs)
        else:
            self._frequency = self._ftdi.init_mpsse(direction=self._direction,
                    initial=self.IDLE, frequency=frequency, **kwargs)
        self._tx_size, self._rx_size = self._ftdi.fifo_sizes
        self._ftdi.enable_adaptive_clock(False)
        self._ftdi.enable_3phase_clock(True)
        try:
            self._ftdi.enable_drivezero_mode(self.SCL_BIT |
                                             self.SDA_O_BIT |
                                             self.SDA_I_BIT)
        except FtdiFeatureError:
            self._tristate = (Ftdi.SET_BITS_LOW, self.LOW, self.SCL_BIT)

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

    def get_port(self, address):
        """Obtain an I2cPort to drive an I2c slave.

           :param int address: the address on the I2C bus
           :return: an I2cPort instance
           :rtype: :py:class:`I2cPort`
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address not in self._slaves:
            self._slaves[address] = I2cPort(self, address)
        return self._slaves[address]

    @property
    def configured(self):
        """Test whether the device has been properly configured.

           :return: True if configured
        """
        return bool(self._ftdi) and bool(self._start)

    @classmethod
    def validate_address(cls, address):
        """Assert an I2C slave address is in the supported range.
           None is a special bypass address.

           :param address: the address on the I2C bus
           :type address: int or None
           :raise I2cIOError: if the I2C slave address is not supported
        """
        if address is None:
            return
        if address > cls.HIGHEST_I2C_ADDRESS:
            raise I2cIOError("No such I2c slave: 0x%02x" % address)

    @property
    def frequency_max(self):
        """Provides the maximum I2c clock frequency.
        """
        return self._ftdi.frequency_max

    @property
    def frequency(self):
        """Provides the current I2c clock frequency.

           :return: the I2C bus clock
           :rtype: float
        """
        return self._frequency

    def read(self, address, readlen=1, relax=True):
        """Read one or more bytes from a remote slave

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param int readlen: count of bytes to read out.
           :param bool relax: not used
           :return: read bytes
           :rtype: array
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)

           Most I2C devices require a register address to read out
           check out the exchange() method.
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            i2caddress |= self.BIT0
        retries = self._retry_count
        do_epilog = True
        while True:
            try:
                self._do_prolog(i2caddress)
                data = self._do_read(readlen)
                do_epilog = relax
                return data
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry read')
            finally:
                if do_epilog:
                    self._do_epilog()

    def write(self, address, out, relax=True):
        """Write one or more bytes to a remote slave

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param out: the byte buffer to send
           :type out: array or bytes or list(int)
           :param bool relax: whether to relax the bus (emit STOP) or not
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)

           Most I2C devices require a register address to write into. It should
           be added as the first (byte)s of the output buffer.
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
        retries = self._retry_count
        do_epilog = True
        while True:
            try:
                self._do_prolog(i2caddress)
                self._do_write(out)
                do_epilog = relax
                return
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry write')
            finally:
                if do_epilog:
                    self._do_epilog()

    def exchange(self, address, out, readlen=0, relax=True):
        """Send a byte sequence to a remote slave followed with
           a read request of one or more bytes.

           This command is useful to tell the slave what data
           should be read out.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param out: the byte buffer to send
           :type out: array or bytes or list(int)
           :param int readlen: count of bytes to read out.
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: read bytes
           :rtype: array
           :raise I2cIOError: if device is not configured or input parameters
                              are invalid

           Address is a logical slave address (0x7f max)
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if readlen < 1:
            raise I2cIOError('Nothing to read')
        if readlen > (I2cController.PAYLOAD_MAX_LENGTH/3-1):
            raise I2cIOError("Input payload is too large")
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
        retries = self._retry_count
        do_epilog = True
        while True:
            try:
                self._do_prolog(i2caddress)
                self._do_write(out)
                self._do_prolog(i2caddress | self.BIT0)
                if readlen:
                    data = self._do_read(readlen)
                do_epilog = relax
                return data
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry exchange')
            finally:
                if do_epilog:
                    self._do_epilog()

    def poll(self, address, write=False, relax=True):
        """Poll a remote slave, expect ACK or NACK.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param bool write: poll in write mode (vs. read)
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: True if the slave acknowledged, False otherwise
           :rtype: bool
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            if not write:
                i2caddress |= self.BIT0
        do_epilog = True
        try:
            self._do_prolog(i2caddress)
            do_epilog = relax
            return True
        except I2cNackError:
            self.log.info('Not ready')
            return False
        finally:
            if do_epilog:
                self._do_epilog()

    def poll_cond(self, address, fmt, mask, value, count, relax=True):
        """Poll a remove slave, watching for condition to satisfy.
           On each poll cycle, a repeated start condition is emitted, without
           releasing the I2C bus, and an ACK is returned to the slave.

           If relax is set, this method releases the I2C bus however it leaves.

           :param address: the address on the I2C bus, or None to discard start
           :type address: int or None
           :param str fmt: struct format for poll register
           :param int mask: binary mask to apply on the condition register
                before testing for the value
           :param int value: value to test the masked condition register
                against. Condition is satisfied when register & mask == value
           :param int count: maximum poll count before raising a timeout
           :param bool relax: whether to relax the bus (emit STOP) or not
           :return: the polled register value, or None if poll failed
           :rtype: array or None
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address is None:
            i2caddress = None
        else:
            i2caddress = (address << 1) & self.HIGH
            i2caddress |= self.BIT0
        do_epilog = True
        try:
            retry = 0
            while retry < count:
                retry += 1
                size = scalc(fmt)
                self._do_prolog(i2caddress)
                data = self._do_read(size)
                self.log.debug("Poll data: %s", hexlify(data).decode())
                cond, = sunpack(fmt, data)
                if (cond & mask) == value:
                    self.log.debug('Poll condition matched')
                    break
                else:
                    data = None
                    self.log.debug('Poll condition not fulfilled: %x/%x',
                                   cond & mask, value)
            do_epilog = relax
            if not data:
                self.log.warning('Poll condition failed')
            return data
        except I2cNackError:
            self.log.info('Not ready')
            return None
        finally:
            if do_epilog:
                self._do_epilog()

    def flush(self):
        """Flush the HW FIFOs
        """
        if not self.configured:
            raise I2cIOError("FTDI controller not initialized")
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()

    def _do_prolog(self, i2caddress):
        if i2caddress is None:
            return
        self.log.debug('   prolog 0x%x', i2caddress >> 1)
        cmd = array('B', self._idle)
        cmd.extend(self._start)
        cmd.extend(self._write_byte)
        cmd.append(i2caddress)
        if self._tristate:
            cmd.extend(self._tristate)
            cmd.extend(self._read_bit)
            cmd.extend(self._clk_lo_data_hi)
        else:
            cmd.extend(self._clk_lo_data_hi)
            cmd.extend(self._read_bit)
        cmd.extend(self._immediate)
        self._ftdi.write_data(cmd)
        ack = self._ftdi.read_data_bytes(1, 4)
        if not ack:
            raise I2cIOError('No answer from FTDI')
        if ack[0] & self.BIT0:
            self.log.warning('NACK')
            raise I2cNackError('NACK from slave')

    def _do_epilog(self):
        self.log.debug('   epilog')
        cmd = array('B', self._stop)
        self._ftdi.write_data(cmd)
        # be sure to purge the MPSSE reply
        self._ftdi.read_data_bytes(1, 1)

    def _do_read(self, readlen):
        self.log.debug('- read %d byte(s)', readlen)
        if not readlen:
            # force a real read request on device, but discard any result
            cmd = array('B')
            cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            self._ftdi.read_data_bytes(0, 4)
            return array('B')
        if self._tristate:
            read_byte = self._tristate + \
                        self._read_byte + \
                        self._clk_lo_data_hi
            read_not_last = \
                read_byte + self._ack + self._clk_lo_data_lo * self._ck_delay
            read_last = \
                read_byte + self._nack + self._clk_lo_data_hi * self._ck_delay
        else:
            read_not_last = \
                self._read_byte + self._ack + \
                self._clk_lo_data_hi * self._ck_delay
            read_last = \
                self._read_byte + self._nack + \
                self._clk_lo_data_hi * self._ck_delay
        # maximum RX size to fit in FTDI FIFO, minus 2 status bytes
        chunk_size = self._rx_size-2
        cmd_size = len(read_last)
        # limit RX chunk size to the count of I2C packable commands in the FTDI
        # TX FIFO (minus one byte for the last 'send immediate' command)
        tx_count = (self._tx_size-1) // cmd_size
        chunk_size = min(tx_count, chunk_size)
        chunks = []
        cmd = None
        rem = readlen
        while rem:
            if rem > chunk_size:
                if not cmd:
                    cmd = array('B')
                    cmd.extend(read_not_last * chunk_size)
                    size = chunk_size
            else:
                cmd = array('B')
                cmd.extend(read_not_last * (rem-1))
                cmd.extend(read_last)
                cmd.extend(self._immediate)
                size = rem
            self._ftdi.write_data(cmd)
            buf = self._ftdi.read_data_bytes(size, 4)
            self.log.debug('- read %d byte(s): %s',
                           len(buf), hexlify(buf).decode())
            chunks.append(buf)
            rem -= size
        return array('B', b''.join(chunks))

    def _do_write(self, out):
        if not isinstance(out, array):
            out = array('B', out)
        if not out:
            return
        self.log.debug('- write %d byte(s): %s',
                       len(out), hexlify(out).decode())
        for byte in out:
            cmd = array('B', self._write_byte)
            cmd.append(byte)
            if self._tristate:
                cmd.extend(self._tristate)
                cmd.extend(self._read_bit)
                cmd.extend(self._clk_lo_data_hi)
            else:
                cmd.extend(self._clk_lo_data_hi)
                cmd.extend(self._read_bit)
            cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            ack = self._ftdi.read_data_bytes(1, 4)
            if not ack:
                msg = 'No answer from FTDI'
                self.log.critical(msg)
                raise I2cIOError(msg)
            if ack[0] & self.BIT0:
                msg = 'NACK from slave'
                self.log.warning(msg)
                raise I2cNackError(msg)
예제 #18
0
class JtagController:
    """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 write and read FIFO byte lengths
    FTDI_WRITE_PIPE_LEN = 0
    FTDI_READ_PIPE_LEN = 0
    
    FTDI_WR_BUFFER_MAX_LEN = 0 # maximum byte length of write data to FTDI
    FTDI_RD_BUFFER_MAX_LEN = 0 # maximum byte length of read data from FTDI
    
    # Private API
    def __init__(self, trst=False, frequency=3.0E6, usb_read_timeout=5000, usb_write_timeout=5000, debug=False):
        """
        trst uses the nTRST optional JTAG line to hard-reset the TAP
          controller
        """
        self._ftdi = Ftdi()
        self._lock = Lock()
        self._ftdi.timeouts = (usb_read_timeout, usb_write_timeout)
        self._trst = trst
        self._frequency = frequency
        self._ftdi_opened = False
        self._immediate = bytes((Ftdi.SEND_IMMEDIATE,))
        self.direction = (JtagController.TCK_BIT |
                          JtagController.TDI_BIT |
                          JtagController.TMS_BIT |
                          (self._trst and JtagController.TRST_BIT or 0))
        # all JTAG outputs are low - Additionally, setting this upper
        # bits as outputs and then having a specific initial value
        # gets the PYNQ-Z1 board working
        self.direction |= 0x90
        self.initialout = 0xe0  
        #@@@#self.initialout = self.direction
        self._last = None  # Last deferred TDO bit
        self._write_buff = array('B')
        self._debug = debug
        
    # Public API
    def configure(self, url):
        """Configure the FTDI interface as a JTAG controller"""
        print('Configure with Freq: {}'.format(self._frequency))

        if (self._debug):
            FtdiLogger.log.addHandler(logging.StreamHandler(stdout))
            #@@@#level = environ.get('FTDI_LOGLEVEL', 'info').upper()
            level = 'DEBUG'
            try:
                loglevel = getattr(logging, level)
            except AttributeError:
                raise ValueError('Invalid log level: %s', level)
            FtdiLogger.set_level(loglevel)

        print('Opening MPSSE:  direction ("1" is out): 0x{:02x}  Initial Output: 0x{:02x}'.format(self.direction, self.initialout))

        with self._lock:
            self._ftdi.open_mpsse_from_url(
                url, direction=self.direction, frequency=self._frequency, debug=self._debug, latency=12) # @@@@@
            self._ftdi_opened = True

            # FTDI requires to initialize all GPIOs before MPSSE kicks in
            cmd = array('B', (Ftdi.SET_BITS_LOW, self.initialout, self.direction))
            self._ftdi.write_data(cmd)

            # Read the FIFO sizes and save them
            (self.FTDI_WRITE_PIPE_LEN, self.FTDI_READ_PIPE_LEN) = self._ftdi.fifo_sizes

            # Set the FTDI read/write chunksizes to be the same as the FTDI FIFO lengths
            self._ftdi.write_data_set_chunksize(self.FTDI_WRITE_PIPE_LEN)
            self._ftdi.read_data_set_chunksize(self.FTDI_READ_PIPE_LEN)

            # "-3" on self.FTDI_WRITE_PIPE_LEN accounts for the command
            # byte plus 2 length bytes which must also fit in the WRITE
            # FIFO.
            self.FTDI_WR_BUFFER_MAX_LEN = self.FTDI_WRITE_PIPE_LEN-3

            # "-2" on self.FTDI_READ_PIPE_LEN accounts for the two status
            # bytes, even though they are not returned by the FTDI
            # read_bytes function. They are still taking up space in the
            # FTDI's READ FIFO.
            self.FTDI_RD_BUFFER_MAX_LEN = self.FTDI_READ_PIPE_LEN-2


    def close(self):
        if self._ftdi_opened:
            self._ftdi.close()
            self._ftdi_opened = False

    def set_frequency(self, frequency):
        # Configure clock
        self._frequency = frequency

        print('FTDI USB Timeouts: read={} write={}'.format(self._ftdi.timeouts[0], self._ftdi.timeouts[1]))
        print('FTDI USB Fifo Len: read={} write={}'.format(self.FTDI_READ_PIPE_LEN, self.FTDI_WRITE_PIPE_LEN))

        return self._ftdi.set_frequency(self._frequency)

    @property
    def max_byte_sizes(self):
        """Return the 3-tuple of maximum bytes from (TMS, TDI (output) and TDO (input))

           :return: 3-tuple of write, read buffer sizes in bytes
           :rtype: tuple(int, int)
        """
        ## Make the TMS buffer size the same as TDI which is the WRITE
        ## Buffer size. Since only send the TMS bits that are '1',
        ## essentially, this size doe snot really matter since Python
        ## will handle it.
        return (self.FTDI_WR_BUFFER_MAX_LEN, self.FTDI_WR_BUFFER_MAX_LEN, self.FTDI_RD_BUFFER_MAX_LEN)

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

    ## Write out the data and clear the internal buffer for more data
    def sync(self):
        if not self._ftdi:
            raise JtagError("FTDI controller terminated")
        if self._write_buff:
            try:
                with self._lock:
                    self._write_buff.extend(self._immediate)
                    self._ftdi.write_data(self._write_buff)
                    self._write_buff = array('B')
            except usb.core.USBError:
                pass            # FTDI should be catching the error

    # Concatenate cmd bytes. If cmd > Write FIFO size, write data and
    # clear cmd array so more bytes can be added (which will need to
    # be sent with a sync() outside of this function)
    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) >= self.FTDI_WRITE_PIPE_LEN:
            self.sync()
        self._write_buff.extend(cmd)


    def write_tms_tdi_read_tdo(self, tms, tdi):
        """Write out TMS bits while holding TDI constant and reading back in TDO"""
        if not (isinstance(tms, BitStream) or isinstance(tms, BitArray)):
            raise JtagError('Expect a BitStream or BitArray')
        length = len(tms)
        if not (0 < length < 8):
            raise JtagError('Invalid TMS length')
        tms.reverse()           # must reverse bits since only lsb write seems to be supported
        tms.prepend(8-len(tms)) # prepend 0's to be 8 bits long

        # left-most bit will be for TDI
        if isinstance(tdi, BitStream) or isinstance(tdi, BitArray):
            tms[0] = tdi[0]
        elif isinstance(tdi, bool):
            tms[0] = tdi            
        else:
            raise JtagError('Incorrect type for tdi - must be BitStream, BitArray or bool')
        
        # apply the last TDI 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

        ## Send the byte to the FTDI
        cmd = array('B', (Ftdi.RW_BITS_TMS_PVE_NVE, length-1, tms.uint))
        self._stack_cmd(cmd)
        self.sync()

        ## Read the response from FTDI
        data = self._ftdi.read_data_bytes(1, 4)
        if (len(data) != 1):
            raise JtagError('Not all data read! Expected {} bytes but only read {} bytes'.format(1,len(data)))

        tdo = BitArray(data)

        # FTDI handles returned LSB bit data by putting the first bit
        # in bit 7 and shifting to the right with every new bit. So
        # the first bit clocked will be in the lowest bit number, but
        # which bit number it will be in depends on how many bits
        # clocked. [It is kinda stupid, if you ask me. I think the bit
        # order should be the same as tehe bit written, but they
        # aren't.]
        tdo = tdo[:length]
        
        # return to bitstring bit ordering with left-most bit the lsb
        tdo.reverse()
        return tdo

    def write_tdi_read_tdo(self, out, use_last=False):
        """ Output a sequence of bits to TDI while reading the TDO input bits. Automatically break any byte writes based on adapter FIFO sizes. """

        if not (isinstance(out, BitStream) or isinstance(out, BitArray)):
            raise JtagError('Expect a BitStream or BitArray')

        ## @@@ Not used at the moment
        #if use_last:
        #    #(out, self._last) = (out[:-1], bool(out[-1]))
        #    self._last = out[-1]

        byte_count = out.len//8
        pos = 8*byte_count
        bit_count = out.len-pos

        # Separate into BYTE and BIT commands
        tdo = BitArray()
        if byte_count:
            ## Since TDO bit length will be equal to TDI bit length,
            ## set max_rw_bits to the minimum of the TDI or TDO bit
            ## sizes.
            max_rw_bits = min(self.max_byte_sizes[1:3])*8

            # Start head and tail at the beginning bit index
            head = 0
            tail = 0

            #@@@while(tdo.len//8 < byte_count):
            while(head < pos):
                # Set tail to either be the maximum bytes to
                # read/write or the final bit, pos, whichever is
                # smaller. These are bit indexes.
                tail = min((head+max_rw_bits),pos)
                tdo += self._write_read_bytes(out[head:tail])
                head = tail

        if bit_count:
            # Do not have to deal with bit length here because already know bit_count is b/w 0 and 7
            tdo += self._write_read_bits(out[pos:])

        return tdo

    def _write_read_bits(self, out):
        """Output bits on TDI while reading TDO bits in"""

        # a bitstring.BitStream() has first bit in left-most array position (ie. msb first)
        length = out.len
        byte = BitArray(out)    # copy out so can modify it
        byte.append(8-length)   # pad 0's to be 8 bits long

        # Check number of bits.
        if not (0 < length <= 8):
            raise JtagError('Wrong number of bits: {}'.format(length))

        #@@@#print('out: ', out, 'length: ', length, 'byte: ', byte, 'byte as uint: ', byte.uint)
        
        # length of 0 for 1 bit, length of 7 for 8 bits, etc.
        cmd = array('B', (Ftdi.RW_BITS_PVE_NVE_MSB, length-1, byte.uint))

        #@@@#print('cmd: ', cmd)
        
        self._stack_cmd(cmd)
        self.sync()
        
        data = self._ftdi.read_data_bytes(1, 4)
        if (len(data) != 1):
            raise JtagError('Not all data read! Expected {} bytes but only read {} bytes'.format(1,len(data)))

        tdo = BitArray(data)

        # Only pass back the same number of bits as clocked
        # out. Although MSB, a MSB bit read left shifts the bits
        # starting at bit 0. So it is right shifted MSB from reads by
        # left shifted MSB on bit writes. (Go Fugure?)
        tdo = tdo[8-length:]
        return tdo

    def _write_read_bytes(self, out):
        """Output bytes on TDI while reading TDO bits in"""

        # a bitstring.BitStream() has first bit in left-most array position (ie. msb first)
        bytes_ = out.bytes
        
        olen = len(bytes_)
        #print("WRITE {} BYTES: 0x{}".format(olen, bytes_.hex()))
        #print("WRITE {} BYTES".format(olen))

        # Check number of bits.
        #
        # If this function was a write-only function, could smartly
        # handle writing more bytes than will fit into the write FIFO
        # by sending them in waves. However, this function writes and
        # reads the same number of bytes. So the byte length of the
        # out vector must be less than the size of both the Write and
        # Read FIFO.
        #
        # What's more, when writing, also need to write a command byte
        # and two length bytes. So account for them as well so that
        # the entire write will fit in the Write FIFO. This makes it
        # so that there is a USB read for every USB write. May not be
        # completely necessary but seems to be a little more
        # efficient.
        #
        if olen > self.FTDI_RD_BUFFER_MAX_LEN:
            raise JtagError("Byte length of Read data ({}) is larger than Read buffer ({})".format(olen, self.FTDI_RD_BUFFER_MAX_LEN))
        if olen > self.FTDI_WR_BUFFER_MAX_LEN:
            raise JtagError("Byte length of Write data ({}) is larger then Write Buffer ({})".format(olen, self.FTDI_WR_BUFFER_MAX_LEN))

        # The byte length to pass to the MPSSE is -1 the actual length, LSB first
        cmd = array('B', (Ftdi.RW_BYTES_PVE_NVE_MSB, (olen-1) & 0xff,
                          ((olen-1) >> 8) & 0xff))
        cmd.extend(bytes_)
        self._stack_cmd(cmd)
        self.sync()

        data = self._ftdi.read_data_bytes(olen, 4)
        if (len(data) != olen):
            raise JtagError('Not all data read! Expected {} bytes but only read {} bytes'.format(olen,len(data)))

        #print("READ {} BYTES:  0x{}".format(len(data), data.tobytes().hex()))
        #print("READ {} BYTES".format(len(data)))

        return BitArray(data)
예제 #19
0
class ReaderBoard:
    _unlockTimeouts: Dict[int, List[LockTimeout]]

    def __init__(self, deviceid):
        """

        :rtype: object
        """
        # self.input = queue.Queue(20)
        self.packetCallback = None
        self.errorCallback = None

        self._commandLock = Lock()
        self._packetReadEvent = Event()
        self._startedEvent = Event()
        self._stoppedEvent = Event()
        self._run = True
        self.device_id = deviceid
        self._ftdi = Ftdi()
        self.numScanners = 0
        self.numRelays = 0
        self.relaystatus = {}
        self.version = "0.00"
        self.model = "unknown"
        self._unlockTimeouts = {}

        self._parserState = ParserState.BEGIN
        self._firstChar = ' '
        self._body = bytearray()

        # self.ftdi.open(vendor=0x0403,product=0x6001, serial=self.device)
        self._ftdi.open_from_url(self.device_id)
        self._ftdi.set_baudrate(57600)

        self._backgroundThread = Thread(target=self.background)
        self._backgroundThread.start()
        self._otherThread = Thread(target=self.lock_watch_loop)
        self._otherThread.start()

        if not self._startedEvent.wait(3.0):
            raise RuntimeError("Unable to startup board!")
        else:
            logger.log(logging.DEBUG, "Started up, disabling echo")
            self.send_command('e', '0')

            def get_device_info(fc, b, me):
                if fc == 'I':
                    info = {i.split(':')[0]: i.split(':')[1] for i in b.split(',')}
                    self.model = info['m']
                    self.version = info['v']
                    self.numRelays = int(info['r'])
                    self.numScanners = int(info['s'])
                    for a in range(self.numRelays):
                        self._unlockTimeouts[a] = []
                        self.relaystatus[a] = False

            self.packetCallback = get_device_info
            logger.info("calling getting device info")
            self.send_command('i', '')
            logger.info("done interrogating")

            self.packetCallback = None

    def shutdown(self):
        self._run = False
        self._commandLock.acquire()
        if not self._stoppedEvent.wait(3.0):
            raise TimeoutError("Failed to shut down board in a timely manner")
        self._backgroundThread.join()

    def background(self):
        self._ftdi.set_dtr(False)
        sleep(0.1)
        self._ftdi.set_dtr(True)  # Reset the device
        totaltime = 0
        ready_bytes = bytearray()
        started_up = False
        while totaltime < 5:
            sleep(0.1)
            totaltime += 0.1
            in_bytes = self._ftdi.read_data_bytes(50)
            if len(in_bytes) > 0:
                ready_bytes.extend(in_bytes)
                s = ready_bytes.decode(encoding="utf-8")
                if '? for help' in s:
                    totaltime = 1000
                    started_up = True
                    self._startedEvent.set()
                    try:
                        self.parse_loop()
                    except pyftdi.ftdi.FtdiError as error:
                        logger.fatal(f"USB ERROR! {error}")
                        if self.errorCallback is not None:
                            self.errorCallback(error)

        if not started_up:
            print("Failed to startup device!")
        logger.log(logging.DEBUG, "Shutting down FTDI device...")
        self._ftdi.close()
        self._commandLock.release()
        logger.log(logging.DEBUG, "Done shutting down hardware interface")

    def lock_watch_loop(self):
        if not self._startedEvent.wait(5000):
            logger.fatal("Didn't start up!")
        else:
            while self._run:
                now = time()
                tos: List[LockTimeout]
                for (r, tos) in self._unlockTimeouts.items():
                    if any(tos):
                        for t in tos:
                            if now >= t.timeout:
                                tos.remove(t)
                                if len(tos) == 0:
                                    # close the door
                                    self.send_command('o', str(r))
                                    self.relaystatus[r] = False
                    elif self.relaystatus[r]:
                        self.send_command('o', str(r))
                        self.relaystatus[r] = False
                sleep(0.25)

    def parse_loop(self):
        totaltime = 0
        while self._run:
            sleep(0.1)
            totaltime += 0.1
            in_bytes = self._ftdi.read_data_bytes(50)
            self.parse(in_bytes)

            # Check out open/close queue
        self._stoppedEvent.set()

    def parse(self, in_bytes):
        if len(in_bytes) > 0:
            for b in in_bytes:
                c = chr(b)

                if self._parserState == ParserState.BEGIN or self._parserState == ParserState.FOUND_NEWLINE:
                    self._body.clear()
                    self._firstChar = c
                    self._parserState = ParserState.FOUND_FIRST
                elif self._parserState == ParserState.FOUND_FIRST:
                    if b != 0x0D:  # '\r'
                        self._body.append(b)
                    else:
                        self._parserState = ParserState.FOUND_CARRIAGE
                elif self._parserState == ParserState.FOUND_CARRIAGE:
                    if b != 0x0A:  # \n
                        # RAISE PARSING ERROR
                        self._parserState = ParserState.BEGIN
                    else:
                        self._parserState = ParserState.FOUND_NEWLINE
                        logger.log(logging.DEBUG, f"Read a packet: {self._firstChar}, {self._body}")
                        if self.packetCallback is not None:
                            try:
                                self.packetCallback(self._firstChar, self._body.decode("utf-8"), self.device_id)
                            except Exception as e:
                                logger.log(logging.FATAL, f"Failed calling packet callback: {e}")
                        if not (self._firstChar == 'e' and len(self._body) == 1 and self._body[0] == ord('0')):
                            self._packetReadEvent.set()

    def send_command(self, c: str, data: str):
        if len(c) != 1 or not c.isalpha():
            raise ValueError("c must be a single character!")

        message = f"{c}{data}\r"  # we won't add a \n, serial comms don't do that normally
        self._packetReadEvent.clear()
        d = message.encode("utf-8")
        if self._commandLock.acquire(timeout=1.0):
            if self._run:
                self._ftdi.write_data(d)
                if self._packetReadEvent.wait(3.0):
                    self._commandLock.release()
                    return self._body
                else:
                    logger.log(logging.ERROR, "Failure to get response from board, it's down?")
                    # raise RuntimeError()
                    self._commandLock.release()
            else:
                self._commandLock.release()
        else:
            if self._run:
                raise SystemError("Sync Error")

    def __repr__(self):
        return f"{self.device_id} - {self.model} v{self.version}: {self.numScanners} scanners, {self.numRelays} relays"

    def Unlock(self, relay, duration, credential=None):
        if relay > self.numRelays:
            logger.error(f"Attempt to activate a relay board {self.device_id} doesn't have. {relay}")
            return
        now = time()
        self._unlockTimeouts[relay].append(LockTimeout(now + duration, credential))
        self.send_command('c', str(relay))
        self.relaystatus[relay] = True

    def Lock(self, relay, credential=None):
        if relay > self.numRelays:
            logger.error(f"Attempt to activate a relay board {self.device_id} doesn't have. {relay}")
            return
        if credential is None: #This only is raised by the webpanel, clear our all entries, and the relay
            # will be relocked by the loop
            self._unlockTimeouts[relay].clear()
        else:
            for tos in self._unlockTimeouts[relay]:
                if tos.credential == credential: # Clear all pending timeouts for the credential
                    self._unlockTimeouts[relay].remove(tos)
예제 #20
0
class SpiController(object):
    """SPI master.

        :param silent_clock: deprecated.
        :param int cs_count: is the number of /CS lines (one per device to
            drive on the SPI bus)
        :param boolean turbo: to be documented
    """

    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')
        # 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
        self._clock_phase = False

    @property
    def direction(self):
        return self._direction

    def configure(self, url, **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_from_url(
            # /CS all high
            url,
            direction=self._direction,
            initial=self._cs_bits,
            **kwargs)
        self._ftdi.enable_adaptive_clock(False)

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

    def get_port(self, cs, freq=None, mode=0):
        """Obtain a SPI port to drive a SPI device selected by cs

           :param int cs: chip select slot, starting from 0
           :param int freq: SPI bus frequency for this slave
           :param int mode: SPI mode [0,1,3]
           :rtype: SpiPort
        """
        if not self._ftdi:
            raise SpiIOError("FTDI controller not initialized")
        if cs >= len(self._ports):
            raise SpiIOError("No such SPI port")
        if not (0 <= mode <= 3):
            raise SpiIOError("Invalid SPI mode")
        if (mode & 0x2) and not self._ftdi.is_H_series:
            raise SpiIOError("SPI with CPHA high is not supported by "
                             "this FTDI device")
        if mode == 2:
            raise SpiIOError("SPI mode 2 has no known workaround with FTDI "
                             "devices")
        if not self._ports[cs]:
            freq = min(freq or self.frequency_max, self.frequency_max)
            hold = freq and (1 + int(1E6 / freq))
            self._ports[cs] = SpiPort(self, cs, cs_hold=hold, spi_mode=mode)
            self._ports[cs].set_frequency(freq)
            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,
                  cs_release=None,
                  cpol=False,
                  cpha=False):
        """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 cs_release: the epilog sequence to send to move the
                       /CS line back to the idle state. May be empty to 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 cpha:
            # to enable CPHA, we need to use a workaround with FTDI device,
            # that is enable 3-phase clocking (which is usually dedicated to
            # I2C support). This mode use use 3 clock period instead of 2,
            # which implies the FTDI frequency should be fixed to match the
            # requested one.
            frequency = (3 * frequency) // 2
        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')
        if cs_release:
            epilog = Array('B', cs_release)
            epilog.extend(self._cs_high)
        else:
            epilog = None
        writelen = len(out)
        if self._clock_phase != cpha:
            self._ftdi.enable_3phase_clock(cpha)
            self._clock_phase = cpha
        if writelen:
            wcmd = (cpol ^ cpha) and \
                Ftdi.WRITE_BYTES_PVE_MSB or Ftdi.WRITE_BYTES_NVE_MSB
            write_cmd = struct.pack('<BH', wcmd, writelen - 1)
            cmd.frombytes(write_cmd)
            cmd.extend(out)
        if readlen:
            rcmd = (cpol ^ cpha) and \
                Ftdi.READ_BYTES_PVE_MSB or Ftdi.READ_BYTES_NVE_MSB
            read_cmd = struct.pack('<BH', rcmd, readlen - 1)
            cmd.frombytes(read_cmd)
            cmd.extend(self._immediate)
            if self._turbo:
                if epilog:
                    cmd.extend(epilog)
                self._ftdi.write_data(cmd)
            else:
                self._ftdi.write_data(cmd)
                if epilog:
                    self._ftdi.write_data(epilog)
            # 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:
            if writelen:
                if self._turbo:
                    if epilog:
                        cmd.extend(epilog)
                    self._ftdi.write_data(cmd)
                else:
                    self._ftdi.write_data(cmd)
                    if epilog:
                        self._ftdi.write_data(epilog)
            data = Array('B')
        return data

    def _flush(self):
        """Flush the HW FIFOs"""
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()
예제 #21
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()
예제 #22
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()
예제 #23
0
파일: spi.py 프로젝트: ebouaziz/pyftdi
class SpiController:
    """SPI master.

        :param silent_clock: deprecated.
        :param int cs_count: is the number of /CS lines (one per device to
            drive on the SPI bus)
        :param boolean turbo: to be documented
    """

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

    def __init__(self, silent_clock=False, cs_count=4, turbo=True):
        self.log = getLogger('pyftdi.spi.ctrl')
        self._ftdi = Ftdi()
        self._lock = Lock()
        self._gpio_port = None
        self._gpio_dir = 0
        self._gpio_mask = 0
        self._gpio_low = 0
        self._wide_port = False
        self._cs_count = cs_count
        self._turbo = turbo
        self._immediate = bytes((Ftdi.SEND_IMMEDIATE,))
        self._frequency = 0.0
        self._clock_phase = False

    @property
    def direction(self):
        """Provide the FTDI GPIO direction"""
        return self._spi_dir | self._gpio_dir

    def configure(self, url, **kwargs):
        """Configure the FTDI interface as a SPI master

           :param str url: FTDI URL string, such as 'ftdi://ftdi:232h/1'
           :param kwargs: options to configure the SPI bus

           Accepted options:

           * ``frequency`` the SPI bus frequency in Hz. Note that each slave
                          may reconfigure the SPI bus with a specialized
                          frequency.
           * ``cs_count`` count of chip select signals dedicated to select
                          SPI slave devices
           * ``turbo`` whether to enable or disable turbo mode
           * ``debug`` for extra debug output
        """
        # it is better to specify CS and turbo in configure, but the older
        # API where these parameters are specified at instanciation has been
        # preserved
        self._cs_count = int(kwargs.get('cs_count', self._cs_count))
        if not (1 <= self._cs_count <= 5):
            raise ValueError('Unsupported CS line count: %d' % self._cs_count)
        self._turbo = bool(kwargs.get('turbo', self._turbo))
        for k in ('direction', 'initial', 'cs_count', 'turbo'):
            if k in kwargs:
                del kwargs[k]
        with self._lock:
            if self._frequency > 0.0:
                raise SpiIOError('Already configured')
            self._cs_bits = (((SpiController.CS_BIT << self._cs_count) - 1) &
                             ~(SpiController.CS_BIT - 1))
            self._spi_ports = [None] * self._cs_count
            self._spi_dir = (self._cs_bits |
                             SpiController.DO_BIT |
                             SpiController.SCK_BIT)
            self._spi_mask = self._cs_bits | self.SPI_BITS
            self._frequency = self._ftdi.open_mpsse_from_url(
                # /CS all high
                url, direction=self._spi_dir, initial=self._cs_bits, **kwargs)
            self._ftdi.enable_adaptive_clock(False)
            self._wide_port = self._ftdi.has_wide_port

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

    def get_port(self, cs, freq=None, mode=0):
        """Obtain a SPI port to drive a SPI device selected by Chip Select.

           :note: SPI mode 2 is not supported.

           :param int cs: chip select slot, starting from 0
           :param float freq: SPI bus frequency for this slave in Hz
           :param int mode: SPI mode [0,1,3]
           :rtype: SpiPort
        """
        with self._lock:
            if not self._ftdi:
                raise SpiIOError("FTDI controller not initialized")
            if cs >= len(self._spi_ports):
                raise SpiIOError("No such SPI port")
            if not (0 <= mode <= 3):
                raise SpiIOError("Invalid SPI mode")
            if (mode & 0x2) and not self._ftdi.is_H_series:
                raise SpiIOError("SPI with CPHA high is not supported by "
                                 "this FTDI device")
            if mode == 2:
                raise SpiIOError("SPI mode 2 has no known workaround with "
                                 "FTDI devices")
            if not self._spi_ports[cs]:
                freq = min(freq or self._frequency, self.frequency_max)
                hold = freq and (1+int(1E6/freq))
                self._spi_ports[cs] = SpiPort(self, cs, cs_hold=hold,
                                              spi_mode=mode)
                self._spi_ports[cs].set_frequency(freq)
                self._flush()
            return self._spi_ports[cs]

    def get_gpio(self):
        with self._lock:
            if not self._ftdi:
                raise SpiIOError("FTDI controller not initialized")
            if not self._gpio_port:
                self._gpio_port = SpiGpioPort(self)
            return self._gpio_port

    @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

    @property
    def gpio_pins(self):
        """Report the addressable GPIOs as a bitfield"""
        with self._lock:
            return self._gpio_mask

    def exchange(self, frequency, out, readlen,
                 cs_prolog=None, cs_epilog=None,
                 cpol=False, cpha=False, duplex=False):
        if duplex:
            if readlen > len(out):
                tmp = array('B', out)
                tmp.extend([0] * (readlen - len(out)))
                out = tmp
            elif not readlen:
                readlen = len(out)
        with self._lock:
            if duplex:
                data = self._exchange_full_duplex(frequency, out,
                                                  cs_prolog, cs_epilog,
                                                  cpol, cpha)
                return data[:readlen]
            else:
                return self._exchange_half_duplex(frequency, out, readlen,
                                                  cs_prolog, cs_epilog,
                                                  cpol, cpha)

    def read_gpio(self, with_output=False):
        """Read GPIO port

           :param bool with_output: set to unmask output pins
           :return: the GPIO port pins as a bitfield
           :rtype: int
        """
        with self._lock:
            data = self._read_raw(self._wide_port)
        value = data & self._gpio_mask
        if not with_output:
            value &= ~self._gpio_dir
        return value

    def write_gpio(self, value):
        """Write GPIO port

           :param int value: the GPIO port pins as a bitfield
        """
        with self._lock:
            if (value & self._gpio_dir) != value:
                raise SpiIOError('No such GPO pins: %04x/%04x' %
                                 (self._gpio_dir, value))
            # perform read-modify-write
            use_high = self._wide_port and (self.direction & 0xff00)
            data = self._read_raw(use_high)
            data &= ~self._gpio_mask
            data |= value
            self._write_raw(data, use_high)
            self._gpio_low = data & 0xFF & ~self._spi_mask

    def set_gpio_direction(self, pins, direction):
        """Change the direction of the GPIO pins

           :param int pins: which GPIO pins should be reconfigured
           :param int direction: direction bitfield (on for output)
        """
        with self._lock:
            if pins & self._spi_mask:
                raise SpiIOError('Cannot access SPI pins as GPIO')
            gpio_width = self._wide_port and 16 or 8
            gpio_mask = (1 << gpio_width) - 1
            gpio_mask &= ~self._spi_mask
            if (pins & gpio_mask) != pins:
                raise SpiIOError('No such GPIO pin(s)')
            self._gpio_dir &= ~pins
            self._gpio_dir |= (pins & direction)
            self._gpio_mask = gpio_mask & pins

    def _read_raw(self, read_high):
        if read_high:
            cmd = array('B', [Ftdi.GET_BITS_LOW,
                              Ftdi.GET_BITS_HIGH,
                              Ftdi.SEND_IMMEDIATE])
            fmt = '<H'
        else:
            cmd = array('B', [Ftdi.GET_BITS_LOW,
                              Ftdi.SEND_IMMEDIATE])
            fmt = 'B'
        self._ftdi.write_data(cmd)
        size = scalc(fmt)
        data = self._ftdi.read_data_bytes(size, 4)
        if len(data) != size:
            raise SpiIOError('Cannot read GPIO')
        value, = sunpack(fmt, data)
        return value

    def _write_raw(self, data, write_high):
        direction = self.direction
        low_data = data & 0xFF
        low_dir = direction & 0xFF
        if write_high:
            high_data = (data >> 8) & 0xFF
            high_dir = (direction >> 8) & 0xFF
            cmd = array('B', [Ftdi.SET_BITS_LOW, low_data, low_dir,
                              Ftdi.SET_BITS_HIGH, high_data, high_dir])
        else:
            cmd = array('B', [Ftdi.SET_BITS_LOW, low_data, low_dir])
        self._ftdi.write_data(cmd)

    def _exchange_half_duplex(self, frequency, out, readlen,
                              cs_prolog, cs_epilog, cpol, cpha):
        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 cpha:
            # to enable CPHA, we need to use a workaround with FTDI device,
            # that is enable 3-phase clocking (which is usually dedicated to
            # I2C support). This mode use use 3 clock period instead of 2,
            # which implies the FTDI frequency should be fixed to match the
            # requested one.
            frequency = (3*frequency)//2
        if self._frequency != frequency:
            self._ftdi.set_frequency(frequency)
            # store the requested value, not the actual one (best effort),
            # to avoid setting unavailable values on each call.
            self._frequency = frequency
        direction = self.direction & 0xFF  # low bits only
        cmd = array('B')
        for ctrl in cs_prolog or []:
            ctrl &= self._spi_mask
            ctrl |= self._gpio_low
            cmd.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
        epilog = array('B')
        if cs_epilog:
            for ctrl in cs_epilog:
                ctrl &= self._spi_mask
                ctrl |= self._gpio_low
                epilog.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
            # Restore idle state
            cs_high = [Ftdi.SET_BITS_LOW, self._cs_bits | self._gpio_low,
                       direction]
            if not self._turbo:
                cs_high.append(Ftdi.SEND_IMMEDIATE)
            epilog.extend(cs_high)
        writelen = len(out)
        if self._clock_phase != cpha:
            self._ftdi.enable_3phase_clock(cpha)
            self._clock_phase = cpha
        if writelen:
            wcmd = (cpol ^ cpha) and \
                Ftdi.WRITE_BYTES_PVE_MSB or Ftdi.WRITE_BYTES_NVE_MSB
            write_cmd = spack('<BH', wcmd, writelen-1)
            cmd.frombytes(write_cmd)
            cmd.extend(out)
        if readlen:
            rcmd = (cpol ^ cpha) and \
                Ftdi.READ_BYTES_PVE_MSB or Ftdi.READ_BYTES_NVE_MSB
            read_cmd = spack('<BH', rcmd, readlen-1)
            cmd.frombytes(read_cmd)
            cmd.extend(self._immediate)
            if self._turbo:
                if epilog:
                    cmd.extend(epilog)
                self._ftdi.write_data(cmd)
            else:
                self._ftdi.write_data(cmd)
                if epilog:
                    self._ftdi.write_data(epilog)
            # 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:
            if writelen:
                if self._turbo:
                    if epilog:
                        cmd.extend(epilog)
                    self._ftdi.write_data(cmd)
                else:
                    self._ftdi.write_data(cmd)
                    if epilog:
                        self._ftdi.write_data(epilog)
            data = array('B')
        return data

    def _exchange_full_duplex(self, frequency, out,
                              cs_prolog, cs_epilog, cpol, cpha):
        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 cpha:
            # to enable CPHA, we need to use a workaround with FTDI device,
            # that is enable 3-phase clocking (which is usually dedicated to
            # I2C support). This mode use use 3 clock period instead of 2,
            # which implies the FTDI frequency should be fixed to match the
            # requested one.
            frequency = (3*frequency)//2
        if self._frequency != frequency:
            self._ftdi.set_frequency(frequency)
            # store the requested value, not the actual one (best effort),
            # to avoid setting unavailable values on each call.
            self._frequency = frequency
        direction = self.direction & 0xFF  # low bits only
        cmd = array('B')
        for ctrl in cs_prolog or []:
            ctrl &= self._spi_mask
            ctrl |= self._gpio_low
            cmd.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
        epilog = array('B')
        if cs_epilog:
            for ctrl in cs_epilog:
                ctrl &= self._spi_mask
                ctrl |= self._gpio_low
                epilog.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
            # Restore idle state
            cs_high = [Ftdi.SET_BITS_LOW, self._cs_bits | self._gpio_low,
                       direction]
            if not self._turbo:
                cs_high.append(Ftdi.SEND_IMMEDIATE)
            epilog.extend(cs_high)
        writelen = len(out)
        if self._clock_phase != cpha:
            self._ftdi.enable_3phase_clock(cpha)
            self._clock_phase = cpha
        wcmd = (cpol ^ cpha) and \
            Ftdi.RW_BYTES_NVE_PVE_MSB or Ftdi.RW_BYTES_PVE_NVE_MSB
        write_cmd = spack('<BH', wcmd, writelen-1)
        cmd.frombytes(write_cmd)
        cmd.extend(out)
        cmd.extend(self._immediate)
        if self._turbo:
            if epilog:
                cmd.extend(epilog)
            self._ftdi.write_data(cmd)
        else:
            self._ftdi.write_data(cmd)
            if epilog:
                self._ftdi.write_data(epilog)
        # 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(len(out), 4)
        return data

    def _flush(self):
        """Flush the HW FIFOs"""
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()
예제 #24
0
class TeaxGrabber(Tau):
    """
    Data acquisition class for the Teax ThermalCapture Grabber USB
    
    """
    def __init__(self, vid=0x0403, pid=0x6010, width=512, height=640):
        self.dev = usb.core.find(idVendor=vid, idProduct=pid)

        self._ftdi = None
        self.frame_size = 2*height*width+2058 # probably wrong for 320x256
        
        if self.dev is not None:
            self.connect()
            # Check for UART and TEAX magic strings, but 
            # it's OK if we timeout here
            self._sync_uart()
            # For some reason if we frame sync here, everything dies
            #self._sync(allow_timeout=True)
            
    def connect(self):
        if self.dev.is_kernel_driver_active(0):
            self.dev.detach_kernel_driver(0)
        
        self._claim_dev()
        
        self._ftdi = Ftdi()
        self._ftdi.open_from_device(self.dev)
        
        self._ftdi.set_bitmode(0xFF, Ftdi.BitMode.RESET)
        self._ftdi.set_bitmode(0xFF, Ftdi.BitMode.SYNCFF)
                  
    def _claim_dev(self):
        self.dev.reset()
        self._release()
        
        self.dev.set_configuration(1)

        usb.util.claim_interface(self.dev, 0)
        usb.util.claim_interface(self.dev, 1)
        
    def _release(self):
        for cfg in self.dev:
            for intf in cfg:
                if self.dev.is_kernel_driver_active(intf.bInterfaceNumber):
                    try:
                        self.dev.detach_kernel_driver(intf.bInterfaceNumber)
                    except usb.core.USBError as e:
                        print("Could not detatch kernel driver from interface({0}): {1}".format(intf.bInterfaceNumber, str(e)))
    
    def _read(self, n_bytes=0, packets_per_transfer=8, num_transfers=256):
        FTDI_PACKET_SIZE = 512
        
        if n_bytes == 0:
            n_bytes = packets_per_transfer * FTDI_PACKET_SIZE
  
        return self._ftdi.read_data(n_bytes)
                
    def _sync(self, allow_timeout=False):
        data = self._read()
        
        magic = b"TEAX"
        
        t = time.time()
        while data.find(magic) == -1:
            data = self._read()
            
            if not allow_timeout and time.time()-t > 0.2:
                log.warn("Timeout in frame sync")
                break
            elif time.time() -t > 0.2:
                break
            
        return data[data.find(magic):]
    
    def _sync_uart(self, allow_timeout=False):
        data = self._read()
        
        magic = b"UART"
        
        t = time.time()
        while data.find(magic) == -1:
            data = self._read()
            
            if not allow_timeout and time.time()-t > 0.2:
                log.warn("Timeout in command sync")
                break
            elif time.time() -t > 0.2:
                break
        
        return data[data.find(magic):]

    def _convert_frame(self, data, to_temperature=True):
        raw_image = np.frombuffer(data[10:], dtype='uint8').reshape((512,2*642))
        raw_image = 0x3FFF & raw_image.view('uint16')[:,1:-1]

        if to_temperature:
            return 0.04*raw_image - 273
        else:
            return raw_image
    
    def _send_data(self, data):
        # header
        buffer = b"UART"
        buffer += int(len(data)).to_bytes(1, byteorder='big') # doesn't matter
        buffer += data
        self._ftdi.write_data(buffer)
        
    def _receive_data(self, length):
        
        length += length*5
        
        data = self._sync_uart()
        
        if len(data) < length:
            data += self._read(n_bytes=length-len(data))
            
            
        return data[:length][5::6]
        
    def grab(self, to_temperature=True, retries=5):

        # Note that in TeAx's official driver, they use a threaded loop
        # to read data as it streams from the camera and they simply
        # process images/commands as they come back. There isn't the same 
        # sort of query/response structure that you'd normally see with
        # a serial device as the camera basically vomits data as soon as
        # the port opens.
        # 
        # The current approach here aims to allow a more structured way of
        # interacting with the camera by synchronising with the stream whenever
        # some particular data is requested. However in the future it may be better
        # if this is moved to a threaded function that continually services the
        # serial stream and we have some kind of helper function which responds 
        # to commands and waits to see the answer from the camera.
         
        for i in range(retries):

            data = b''
            data = self._sync()

            t = time.time()
            while len(data) < self.frame_size:
                data += self._read(n_bytes=self.frame_size-len(data))

                if time.time()-t > 0.2:
                    data = None
                    log.warn("Timeout in image capture")
                    break
            
            if len(data) == self.frame_size:
                break
            else:
                log.warn("Data size incorrect")
                data = None

        log.debug("Retries: {}".format(i))

        if data is not None:
            data = self._convert_frame(data, to_temperature=to_temperature)
            
        return data
    
    def close(self):
        if self._ftdi is not None:
            self._ftdi.close()

    def __exit__(self, type, value, traceback):
        self.close()
        log.info("Disconnecting from camera.")
예제 #25
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)
예제 #26
0
class LaserPointer:
    """Class for controlling a laser pointer via an FTDI device.

    This class allows a laser pointer to be controlled by toggling the state of a pin on an FTDI
    USB Serial device in bitbang mode.

    Attributes:
        laser_pin: An integer bitmask with a single bit set corresponding to the pin that the laser
            pointer is controlled by. See the set of constants defined at the top of this source
            file.
        ftdi: An object of type pyftdi.ftdi.Ftdi.
    """

    def __init__(self, ftdi_pid='232r', serial_num=None, laser_pin=PIN_CTS):
        """Inits LaserPointer object.

        Initializes a LaserPointer object by constructing an Ftdi object and opening the desired
        FTDI device. Configures the FTDI device to bitbang mode.

        Args:
            ftdi_pid: Product ID string for the FTDI device.
            serial_num: String containing the serial number of the FTDI device. If None, any
                serial number will be accepted.
            laser_pin: Integer bit mask with a single bit set corresponding to the pin on the FTDI
                device that controls the laser pointer.
        """
        self.laser_pin = laser_pin

        self.ftdi = Ftdi()

        self.ftdi.open_bitbang(
            vendor=Ftdi.VENDOR_IDS['ftdi'],
            product=Ftdi.PRODUCT_IDS[Ftdi.FTDI_VENDOR][ftdi_pid],
            serial=serial_num,        # serial number of FT232RQ in the laser FTDI cable
            latency=Ftdi.LATENCY_MAX, # 255ms; reduce if necessary (at cost of higher CPU usage)
        )

        # set laser_pin as output, all others as inputs
        self.ftdi.set_bitmode(self.laser_pin, Ftdi.BitMode.BITBANG)
        assert self.ftdi.bitbang_enabled

        # make sure laser is disabled by default
        self.set(False)

    def __del__(self):
        """Try to make sure laser is disabled on shutdown."""
        try:
            self.set(False)
            self.ftdi.close()
        except AttributeError:  # happens when no laser was present at construction time
            pass
        except Exception as e:  # pylint: disable=broad-except
            print('Got exception while trying to disable laser pointer: ' + str(e))

    def set(self, enable):
        """Sets the state of the laser pointer.

        Args:
            enable: Laser pointer will be turned on when True and off when False.
        """
        if enable:
            self.ftdi.write_data(bytes([0x00]))
        else:
            self.ftdi.write_data(bytes([self.laser_pin]))

    def get(self):
        """Gets the current state of the laser pointer.

        This reads the current state from FTDI device and does not rely on any state information
        in this class.

        Returns:
            The current state of the laser pointer as a boolean.
        """
        return (self.ftdi.read_pins() & self.laser_pin) == 0x0
예제 #27
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()
예제 #28
0
파일: clkgen.py 프로젝트: anders-code/stuff
# 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)

# key #1 - must set pins to output
# this drives the TMS high also, will force JTAG reset in short order
예제 #29
0
class SpiController:
    """SPI master.

        :param silent_clock: deprecated.
        :param int cs_count: is the number of /CS lines (one per device to
            drive on the SPI bus)
        :param boolean turbo: to be documented
    """

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

    def __init__(self, silent_clock=False, cs_count=1, turbo=True):
        self.log = getLogger('pyftdi.spi.ctrl')
        self._ftdi = Ftdi()
        self._lock = Lock()
        self._gpio_port = None
        self._gpio_dir = 0
        self._gpio_mask = 0
        self._gpio_low = 0
        self._wide_port = False
        self._cs_count = cs_count
        self._turbo = turbo
        self._immediate = bytes((Ftdi.SEND_IMMEDIATE, ))
        self._frequency = 0.0
        self._clock_phase = False

    def configure(self, url, **kwargs):
        """Configure the FTDI interface as a SPI master

           :param str url: FTDI URL string, such as 'ftdi://ftdi:232h/1'
           :param kwargs: options to configure the SPI bus

           Accepted options:

           * ``frequency`` the SPI bus frequency in Hz. Note that each slave
                          may reconfigure the SPI bus with a specialized
                          frequency.
           * ``cs_count`` count of chip select signals dedicated to select
                          SPI slave devices, starting from A*BUS3 pin
           * ``turbo`` whether to enable or disable turbo mode
           * ``debug`` for extra debug output
        """
        # it is better to specify CS and turbo in configure, but the older
        # API where these parameters are specified at instanciation has been
        # preserved
        self._cs_count = int(kwargs.get('cs_count', self._cs_count))
        if not (1 <= self._cs_count <= 5):
            raise ValueError('Unsupported CS line count: %d' % self._cs_count)
        self._turbo = bool(kwargs.get('turbo', self._turbo))
        for k in ('direction', 'initial', 'cs_count', 'turbo'):
            if k in kwargs:
                del kwargs[k]
        with self._lock:
            if self._frequency > 0.0:
                raise SpiIOError('Already configured')
            self._cs_bits = (((SpiController.CS_BIT << self._cs_count) - 1)
                             & ~(SpiController.CS_BIT - 1))
            self._spi_ports = [None] * self._cs_count
            self._spi_dir = (self._cs_bits | SpiController.DO_BIT
                             | SpiController.SCK_BIT)
            self._spi_mask = self._cs_bits | self.SPI_BITS
            self._frequency = self._ftdi.open_mpsse_from_url(
                # /CS all high
                url,
                direction=self._spi_dir,
                initial=self._cs_bits,
                **kwargs)
            self._ftdi.enable_adaptive_clock(False)
            self._wide_port = self._ftdi.has_wide_port

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

    def get_port(self, cs, freq=None, mode=0):
        """Obtain a SPI port to drive a SPI device selected by Chip Select.

           :note: SPI mode 1 and 3 are not officially supported.

           :param int cs: chip select slot, starting from 0
           :param float freq: SPI bus frequency for this slave in Hz
           :param int mode: SPI mode [0, 1, 2, 3]
           :rtype: SpiPort
        """
        with self._lock:
            if not self._ftdi:
                raise SpiIOError("FTDI controller not initialized")
            if cs >= len(self._spi_ports):
                if cs < 5:
                    # increase cs_count (up to 4) to reserve more /CS channels
                    raise SpiIOError("/CS pin %d not reserved for SPI" % cs)
                raise SpiIOError("No such SPI port: %d" % cs)
            if not (0 <= mode <= 3):
                raise SpiIOError("Invalid SPI mode")
            if (mode & 0x2) and not self._ftdi.is_H_series:
                raise SpiIOError("SPI with CPHA high is not supported by "
                                 "this FTDI device")
            if not self._spi_ports[cs]:
                freq = min(freq or self._frequency, self.frequency_max)
                hold = freq and (1 + int(1E6 / freq))
                self._spi_ports[cs] = SpiPort(self,
                                              cs,
                                              cs_hold=hold,
                                              spi_mode=mode)
                self._spi_ports[cs].set_frequency(freq)
                self._flush()
            return self._spi_ports[cs]

    def get_gpio(self):
        with self._lock:
            if not self._ftdi:
                raise SpiIOError("FTDI controller not initialized")
            if not self._gpio_port:
                self._gpio_port = SpiGpioPort(self)
            return self._gpio_port

    @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

    @property
    def direction(self):
        """Provide the FTDI GPIO direction"""
        return self._spi_dir | self._gpio_dir

    @property
    def gpio_pins(self):
        """Report the configured GPIOs as a bitfield"""
        with self._lock:
            return self._gpio_mask

    @property
    def gpio_all_pins(self):
        """Report the addressable GPIOs as a bitfield"""
        mask = (1 << self.width) - 1
        with self._lock:
            return mask & ~self._spi_mask

    @property
    def width(self):
        """Report the FTDI count of addressable pins.

           :return: the count of IO pins (including SPI ones).
        """
        return 16 if self._wide_port else 8

    def exchange(self,
                 frequency,
                 out,
                 readlen,
                 cs_prolog=None,
                 cs_epilog=None,
                 cpol=False,
                 cpha=False,
                 duplex=False):
        if duplex:
            if readlen > len(out):
                tmp = bytearray(out)
                tmp.extend([0] * (readlen - len(out)))
                out = tmp
            elif not readlen:
                readlen = len(out)
        with self._lock:
            if duplex:
                data = self._exchange_full_duplex(frequency, out, cs_prolog,
                                                  cs_epilog, cpol, cpha)
                return data[:readlen]
            else:
                return self._exchange_half_duplex(frequency, out, readlen,
                                                  cs_prolog, cs_epilog, cpol,
                                                  cpha)

    def read_gpio(self, with_output=False):
        """Read GPIO port

           :param bool with_output: set to unmask output pins
           :return: the GPIO port pins as a bitfield
           :rtype: int
        """
        with self._lock:
            data = self._read_raw(self._wide_port)
        value = data & self._gpio_mask
        if not with_output:
            value &= ~self._gpio_dir
        return value

    def write_gpio(self, value):
        """Write GPIO port

           :param int value: the GPIO port pins as a bitfield
        """
        with self._lock:
            if (value & self._gpio_dir) != value:
                raise SpiIOError('No such GPO pins: %04x/%04x' %
                                 (self._gpio_dir, value))
            # perform read-modify-write
            use_high = self._wide_port and (self.direction & 0xff00)
            data = self._read_raw(use_high)
            data &= ~self._gpio_mask
            data |= value
            self._write_raw(data, use_high)
            self._gpio_low = data & 0xFF & ~self._spi_mask

    def set_gpio_direction(self, pins, direction):
        """Change the direction of the GPIO pins

           :param int pins: which GPIO pins should be reconfigured
           :param int direction: direction bitfield (on for output)
        """
        with self._lock:
            if pins & self._spi_mask:
                raise SpiIOError('Cannot access SPI pins as GPIO')
            gpio_width = self._wide_port and 16 or 8
            gpio_mask = (1 << gpio_width) - 1
            gpio_mask &= ~self._spi_mask
            if (pins & gpio_mask) != pins:
                raise SpiIOError('No such GPIO pin(s)')
            self._gpio_dir &= ~pins
            self._gpio_dir |= (pins & direction)
            self._gpio_mask = gpio_mask & pins

    def _read_raw(self, read_high):
        if read_high:
            cmd = bytearray(
                [Ftdi.GET_BITS_LOW, Ftdi.GET_BITS_HIGH, Ftdi.SEND_IMMEDIATE])
            fmt = '<H'
        else:
            cmd = bytearray([Ftdi.GET_BITS_LOW, Ftdi.SEND_IMMEDIATE])
            fmt = 'B'
        self._ftdi.write_data(cmd)
        size = scalc(fmt)
        data = self._ftdi.read_data_bytes(size, 4)
        if len(data) != size:
            raise SpiIOError('Cannot read GPIO')
        value, = sunpack(fmt, data)
        return value

    def _write_raw(self, data, write_high):
        direction = self.direction
        low_data = data & 0xFF
        low_dir = direction & 0xFF
        if write_high:
            high_data = (data >> 8) & 0xFF
            high_dir = (direction >> 8) & 0xFF
            cmd = bytearray([
                Ftdi.SET_BITS_LOW, low_data, low_dir, Ftdi.SET_BITS_HIGH,
                high_data, high_dir
            ])
        else:
            cmd = bytearray([Ftdi.SET_BITS_LOW, low_data, low_dir])
        self._ftdi.write_data(cmd)

    def _exchange_half_duplex(self, frequency, out, readlen, cs_prolog,
                              cs_epilog, cpol, cpha):
        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 cpha:
            # to enable CPHA, we need to use a workaround with FTDI device,
            # that is enable 3-phase clocking (which is usually dedicated to
            # I2C support). This mode use use 3 clock period instead of 2,
            # which implies the FTDI frequency should be fixed to match the
            # requested one.
            frequency = (3 * frequency) // 2
        if self._frequency != frequency:
            self._ftdi.set_frequency(frequency)
            # store the requested value, not the actual one (best effort),
            # to avoid setting unavailable values on each call.
            self._frequency = frequency
        direction = self.direction & 0xFF  # low bits only
        cmd = bytearray()
        for ctrl in cs_prolog or []:
            ctrl &= self._spi_mask
            ctrl |= self._gpio_low
            cmd.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
        epilog = bytearray()
        if cs_epilog:
            for ctrl in cs_epilog:
                ctrl &= self._spi_mask
                ctrl |= self._gpio_low
                epilog.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
            # Restore idle state
            cs_high = [
                Ftdi.SET_BITS_LOW, self._cs_bits | self._gpio_low, direction
            ]
            if not self._turbo:
                cs_high.append(Ftdi.SEND_IMMEDIATE)
            epilog.extend(cs_high)
        writelen = len(out)
        if self._clock_phase != cpha:
            self._ftdi.enable_3phase_clock(cpha)
            self._clock_phase = cpha
        if writelen:
            wcmd = (Ftdi.WRITE_BYTES_NVE_MSB
                    if not cpol else Ftdi.WRITE_BYTES_PVE_MSB)
            write_cmd = spack('<BH', wcmd, writelen - 1)
            cmd.append(write_cmd)
            cmd.extend(out)
        if readlen:
            rcmd = (Ftdi.READ_BYTES_NVE_MSB
                    if not cpol else Ftdi.READ_BYTES_PVE_MSB)
            read_cmd = spack('<BH', rcmd, readlen - 1)
            cmd.append(read_cmd)
            cmd.extend(self._immediate)
            if self._turbo:
                if epilog:
                    cmd.extend(epilog)
                self._ftdi.write_data(cmd)
            else:
                self._ftdi.write_data(cmd)
                if epilog:
                    self._ftdi.write_data(epilog)
            # 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:
            if writelen:
                if self._turbo:
                    if epilog:
                        cmd.extend(epilog)
                    self._ftdi.write_data(cmd)
                else:
                    self._ftdi.write_data(cmd)
                    if epilog:
                        self._ftdi.write_data(epilog)
            data = bytearray()
        return data

    def _exchange_full_duplex(self, frequency, out, cs_prolog, cs_epilog, cpol,
                              cpha):
        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 cpha:
            # to enable CPHA, we need to use a workaround with FTDI device,
            # that is enable 3-phase clocking (which is usually dedicated to
            # I2C support). This mode use use 3 clock period instead of 2,
            # which implies the FTDI frequency should be fixed to match the
            # requested one.
            frequency = (3 * frequency) // 2
        if self._frequency != frequency:
            self._ftdi.set_frequency(frequency)
            # store the requested value, not the actual one (best effort),
            # to avoid setting unavailable values on each call.
            self._frequency = frequency
        direction = self.direction & 0xFF  # low bits only
        cmd = bytearray()
        for ctrl in cs_prolog or []:
            ctrl &= self._spi_mask
            ctrl |= self._gpio_low
            cmd.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
        epilog = bytearray()
        if cs_epilog:
            for ctrl in cs_epilog:
                ctrl &= self._spi_mask
                ctrl |= self._gpio_low
                epilog.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
            # Restore idle state
            cs_high = [
                Ftdi.SET_BITS_LOW, self._cs_bits | self._gpio_low, direction
            ]
            if not self._turbo:
                cs_high.append(Ftdi.SEND_IMMEDIATE)
            epilog.extend(cs_high)
        writelen = len(out)
        if self._clock_phase != cpha:
            self._ftdi.enable_3phase_clock(cpha)
            self._clock_phase = cpha
        wcmd = (Ftdi.RW_BYTES_PVE_NVE_MSB
                if not cpol else Ftdi.RW_BYTES_NVE_PVE_MSB)
        write_cmd = spack('<BH', wcmd, writelen - 1)
        cmd.extend(write_cmd)
        cmd.extend(out)
        cmd.extend(self._immediate)
        if self._turbo:
            if epilog:
                cmd.extend(epilog)
            self._ftdi.write_data(cmd)
        else:
            self._ftdi.write_data(cmd)
            if epilog:
                self._ftdi.write_data(epilog)
        # 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(len(out), 4)
        return data

    def _flush(self):
        """Flush the HW FIFOs"""
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()
예제 #30
0
from pyftdi.ftdi import Ftdi
from time import sleep

ftdi1 = Ftdi()
ftdi1.open(vendor=0x403, product=0x6015, interface=1)

vps = [(0x0403, 0x6015)]

for x in range(5):
    print("If#1: ", hex(ftdi1.poll_modem_status()))
    print("Read Pins:", bin(ftdi1.read_pins()))
    print("Devices:", bin(ftdi1.find_all(vps)))
    sleep(0.500)

ftdi1.close()
예제 #31
0
파일: mockusb.py 프로젝트: cruizdeg/pyftdi
 def test_open_bitbang(self):
     """Check simple open/close BitBang sequence."""
     ftdi = Ftdi()
     ftdi.open_bitbang_from_url('ftdi:///1')
     ftdi.close()
예제 #32
0
        else:
            print("\t...")

    print("\n... Reading more...")
    while True:
        time.sleep(2)
        aux = ft.read_data_bytes(1).tolist()
        if (len(aux) == 0): break
        print("read = {}".format(aux))
        fifo_rd = fifo_rd + aux

    print("\n>>> write fifo:\nlen = {}\ndata = {}".format(
        len(fifo_wr), fifo_wr))
    print("\n>>> read fifo:\nlen = {}\ndata = {}".format(
        len(fifo_rd), fifo_rd))

    ft.close()
    del ft
    exit()

# from pyftdi.ftdi import Ftdi
# ft = Ftdi()
# ft.open(vendor=0x0403,product=0x6010,interface=2)
# ft.find_all([(0x0403,0x6010)],True)
# ft.set_baudrate(9600)
# rd_data = ft.read_data_bytes(1).tolist()
# rd_data
# wr_data = 131
# ft.write_data(bytes([wr_data]))
# ft.close()
예제 #33
0
파일: i2c.py 프로젝트: valeruozi/pyftdi
class I2cController(object):
    """I2c master.
    """

    LOW = 0x00
    HIGH = 0xff
    BIT0 = 0x01
    IDLE = HIGH
    SCL_BIT = 0x01
    SDA_O_BIT = 0x02
    SDA_I_BIT = 0x04
    PAYLOAD_MAX_LENGTH = 0x10000  # 16 bits max
    HIGHEST_I2C_ADDRESS = 0x7f
    DEFAULT_BUS_FREQUENCY = 100000.0
    HIGH_BUS_FREQUENCY = 400000.0
    RETRY_COUNT = 3

    def __init__(self):
        self._ftdi = Ftdi()
        self.log = getLogger('pyftdi.i2c')
        self._slaves = {}
        self._frequency = 0.0
        self._direction = I2cController.SCL_BIT | I2cController.SDA_O_BIT
        self._immediate = (Ftdi.SEND_IMMEDIATE, )
        self._idle = (Ftdi.SET_BITS_LOW, self.IDLE, self._direction)
        data_low = (Ftdi.SET_BITS_LOW, self.IDLE & ~self.SDA_O_BIT,
                    self._direction)
        clock_low_data_low = (Ftdi.SET_BITS_LOW,
                              self.IDLE & ~(self.SDA_O_BIT | self.SCL_BIT),
                              self._direction)
        self._clock_low_data_high = (Ftdi.SET_BITS_LOW,
                                     self.IDLE & ~self.SCL_BIT,
                                     self._direction)
        self._read_bit = (Ftdi.READ_BITS_PVE_MSB, 0)
        self._read_byte = (Ftdi.READ_BYTES_PVE_MSB, 0, 0)
        self._write_byte = (Ftdi.WRITE_BYTES_NVE_MSB, 0, 0)
        self._nack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.HIGH)
        self._ack = (Ftdi.WRITE_BITS_NVE_MSB, 0, self.LOW)
        self._start = data_low * 4 + clock_low_data_low * 4
        self._stop = clock_low_data_low * 4 + data_low * 4 + self._idle * 4
        self._tx_size = 1
        self._rx_size = 1

    def configure(self, url, **kwargs):
        """Configure the FTDI interface as a I2c master.

            :param url: FTDI URL string, such as 'ftdi://ftdi:232h/1'
            :param frequency: frequency of the I2C bus.
        """
        for k in ('direction', 'initial'):
            if k in kwargs:
                del kwargs[k]
        if 'frequency' in kwargs:
            frequency = kwargs['frequency']
            del kwargs['frequency']
        else:
            frequency = self.DEFAULT_BUS_FREQUENCY
        # Fix frequency for 3-phase clock
        frequency = (3.0 * frequency) / 2.0
        self._frequency = self._ftdi.open_mpsse_from_url(
            url,
            direction=self._direction,
            initial=self.IDLE,
            frequency=frequency,
            **kwargs)
        self._tx_size, self._rx_size = self._ftdi.fifo_sizes
        self._ftdi.enable_adaptive_clock(False)
        self._ftdi.enable_3phase_clock(True)
        self._ftdi.enable_drivezero_mode(self.SCL_BIT | self.SDA_O_BIT
                                         | self.SDA_I_BIT)

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

    def get_port(self, address):
        """Obtain a I2cPort to to drive an I2c slave.

           :param address: the address on the I2C bus
           :return a I2cPort instance
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if address not in self._slaves:
            self._slaves[address] = I2cPort(self, address)
        return self._slaves[address]

    @classmethod
    def validate_address(cls, address):
        if address > cls.HIGHEST_I2C_ADDRESS:
            raise I2cIOError("No such I2c slave")

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

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

    def read(self, address, readlen=1):
        """Read one or more bytes from a remote slave

           :param address: the address on the I2C bus
           :param readlen: count of bytes to read out.

           Address is a logical address (0x7f max)

           Most I2C devices require a register address to read out
           check out the exchange() method.
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if readlen < 1:
            raise I2cIOError('Nothing to read')
        i2caddress = (address << 1) & self.HIGH
        i2caddress |= self.BIT0
        retries = self.RETRY_COUNT
        while True:
            try:
                self._do_prolog(i2caddress)
                data = self._do_read(readlen)
                return bytes(data)
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry read')
            finally:
                self._do_epilog()

    def write(self, address, out):
        """Write one or more bytes to a remote slave

           :param address: the address on the I2C bus
           :param out: the byte buffer to send

           Address is a logical address (0x7f max)

           Most I2C devices require a register address to write
           into. It should be added as the first (byte)s of the
           output buffer.
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if not out or len(out) < 1:
            raise I2cIOError('Nothing to write')
        i2caddress = (address << 1) & self.HIGH
        retries = self.RETRY_COUNT
        while True:
            try:
                self._do_prolog(i2caddress)
                self._do_write(out)
                return
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry write')
            finally:
                self._do_epilog()

    def exchange(self, address, out, readlen=1):
        """Send a byte sequence to a remote slave followed with
           a read request of one or more bytes.

           This command is useful to tell the slave what data
           should be read out.

           :param address: the address on the I2C bus
           :param out: the byte buffer to send
           :param readlen: count of bytes to read out.

           Address is a logical address (0x7f max)
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        if not out or not len(out):
            raise I2cIOError('Nothing to write')
        if readlen < 1:
            raise I2cIOError('Nothing to read')
        if readlen > (I2cController.PAYLOAD_MAX_LENGTH / 3 - 1):
            raise I2cIOError("Input payload is too large")
        i2caddress = (address << 1) & self.HIGH
        retries = self.RETRY_COUNT
        while True:
            try:
                self._do_prolog(i2caddress)
                self._do_write(out)
                self._do_prolog(i2caddress | self.BIT0)
                data = self._do_read(readlen)
                return data
            except I2cNackError:
                retries -= 1
                if not retries:
                    raise
                self.log.warning('Retry exchange')
            finally:
                self._do_epilog()

    def poll(self, address):
        """Poll a remote slave, expect ACK or NACK.

           :param address: the address on the I2C bus
           :return: True if the slave acknowledged, False otherwise
        """
        if not self._ftdi:
            raise I2cIOError("FTDI controller not initialized")
        self.validate_address(address)
        i2caddress = (address << 1) & self.HIGH
        i2caddress |= self.BIT0
        self.log.debug('- poll 0x%x', i2caddress >> 1)
        try:
            self._do_prolog(i2caddress)
            return True
        except I2cNackError:
            self.log.info('Not ready')
            return False
        finally:
            self._do_epilog()

    def flush(self):
        """Flush the HW FIFOs
        """
        self._ftdi.write_data(self._immediate)
        self._ftdi.purge_buffers()

    def _do_prolog(self, i2caddress):
        self.log.debug('   prolog 0x%x', i2caddress >> 1)
        cmd = Array('B', self._idle)
        cmd.extend(self._start)
        cmd.extend(self._write_byte)
        cmd.append(i2caddress)
        cmd.extend(self._clock_low_data_high)
        cmd.extend(self._read_bit)
        cmd.extend(self._immediate)
        self._ftdi.write_data(cmd)
        ack = self._ftdi.read_data_bytes(1, 4)
        if not ack:
            raise I2cIOError('No answer from FTDI')
        if ack[0] & self.BIT0:
            self.log.warning('NACK')
            raise I2cNackError('NACK from slave')

    def _do_epilog(self):
        self.log.debug('   epilog')
        cmd = Array('B', self._stop)
        self._ftdi.write_data(cmd)
        # be sure to purge the MPSSE reply
        self._ftdi.read_data_bytes(1, 1)

    def _do_read(self, readlen):
        self.log.debug('- read %d bytes', readlen)
        read_not_last = self._read_byte + self._ack + self._clock_low_data_high
        read_last = self._read_byte + self._nack + self._clock_low_data_high
        chunk_size = self._rx_size - 2
        cmd_size = len(read_last)
        # limit RX chunk size to the count of I2C packable ommands in the FTDI
        # TX FIFO (minus one byte for the last 'send immediate' command)
        tx_count = (self._tx_size - 1) // cmd_size
        chunk_size = min(tx_count, chunk_size)
        chunks = []
        last_count = 0
        last_cmd = None
        count = readlen
        while count:
            block_count = min(count, chunk_size)
            count -= block_count
            if last_count != block_count:
                cmd = Array('B')
                cmd.extend(read_not_last * (block_count - 1))
                cmd.extend(read_last)
                last_cmd = cmd
            else:
                cmd = last_cmd
            if count <= 0:
                # only force immediate read out on last chunk
                cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            buf = self._ftdi.read_data_bytes(block_count, 4)
            chunks.append(buf)
        return b''.join(chunks)

    def _do_write(self, out):
        self.log.debug('- write %d bytes: %s', len(out), hexlify(out).decode())
        for byte in out:
            cmd = Array('B', self._write_byte)
            cmd.append(byte)
            cmd.extend(self._clock_low_data_high)
            cmd.extend(self._read_bit)
            cmd.extend(self._immediate)
            self._ftdi.write_data(cmd)
            ack = self._ftdi.read_data_bytes(1, 4)
            if not ack:
                msg = 'No answer from FTDI'
                self.log.critical(msg)
                raise I2cIOError(msg)
            if ack[0] & self.BIT0:
                msg = 'NACK from slave'
                self.log.warning(msg)
                raise I2cNackError(msg)
예제 #34
0
class PyFtdiBootloader(JennicProtocol):
    AUTO_PROG_MODE = True
    VENDOR_ID = 0x0403
    PRODICT_ID = 0x6001

    def __init__(self,
                 pid=0x6001,
                 devno=1,
                 flag=None,
                 baud_fast=True,
                 ftdi_obj=None):
        self.DEFAULT_TIMEOUT = 1  # 100 for debug
        self.MAX_TIMEOUT = 3  # 100 fpr debug
        self.MAX_TIMEOUT_ERASEFULL = 8
        self.MAX_TIMEOUT_ERASESECT = 2
        self.BAUD_DEFAULT = 38400

        self.b_ftdi_obj = False
        self.devno = 1
        self.pid = pid
        self.isopen = False
        self.baud_fast = baud_fast

        if ftdi_obj is not None:
            self.ser = ftdi_obj
            self.b_ftdi_obj = True
            self.isopen = True
        else:
            if pid == None: pid = self.PRODICT_ID
            self.devno = None
            if devno.__class__ == int:
                self.devno = devno
            elif devno.__class__ == str:
                pass
                #self.devno = find_devno_by_serial(self.VENDOR_ID, pid, devno)
            else:
                self.devno = 1

            self.pid = pid
            self.isopen = False
            self.baud_fast = baud_fast

            self.ser = Ftdi()
            self.open(baud=self.BAUD_DEFAULT)

        self.dev_prog()

        self.baud_default_to_fast()

        JennicProtocol.__init__(self)

    def open(self, baud=None):
        if self.b_ftdi_obj:
            if baud is not None:
                self.ser.set_baudrate(baud)
            return self.isopen

        if self.ser is not None:
            if self.isopen: self.close()

        if self.ser is None:
            self.ser = Ftdi()

        self.isopen = False
        try:
            self.ser.open(vendor=self.VENDOR_ID,
                          product=self.pid,
                          interface=self.devno)
            self.isopen = True
        except:
            pass

        if baud is None:
            baud = self.self.BAUD_DEFAULT

        self.ser.set_baudrate(baud)

        return self.isopen

    def close(self, destruct=False):
        if self.b_ftdi_obj:
            pass
        else:
            try:
                if self.ser is not None:
                    self.ser.close()
            except:
                pass

            if destruct:
                try:
                    if self.ser is not None:
                        del self.ser
                except:
                    pass

                self.ser = None

            self.isopen = False

    def baud_set(self, baud, reopen=False):
        if reopen:
            self.close()
            self.open(baud)
        else:
            self.ser.set_baudrate(baud)

    def baud_default(self):
        self.bause_set(self.BAUD_DEFAULT)

    def baud_default_to_fast(self):
        if self.baud_fast:
            self.change_baud(1000000)

    def __del__(self):
        self.close()

    """
        TWE をリセットする
    """

    def dev_reset(self):
        # RESET
        self.ser.set_bitmode(0xFB, self.ser.BitMode.CBUS)
        sleep(0.05)
        self.ser.set_bitmode(0xFF, self.ser.BitMode.CBUS)

    """
        TWE をプログラムモードに設定する
    """

    def dev_prog(self):
        # FIRMWARE PROGRAM'.',
        self.ser.set_bitmode(0xF3, self.ser.BitMode.CBUS)
        sleep(0.05)
        self.ser.set_bitmode(0xF7, self.ser.BitMode.CBUS)
        sleep(0.2)
        self.ser.set_bitmode(0xFF, self.ser.BitMode.CBUS)
        sleep(0.05)

    """
        シリアルポートに書き出す
        data: byte列を入力
    """

    def write(self, data):
        self.ser.write_data(data)

    """
        シリアルポートから読み出す
        timeout: タイムアウト秒 (float)
        raise_error: タイムアウト時に例外を発生させる
        例外: TypeError タイムアウト
    """

    def read(self, size, timeout=None, raise_error=True):
        d = b''
        ts = time.monotonic()

        if timeout is None: timeout = self.DEFAULT_TIMEOUT

        while True:
            d += self.ser.read_data(size - len(d))
            if len(d) >= size:
                break
            else:
                # timeout check
                if time.monotonic() - ts > timeout:
                    if raise_error: raise TypeError()
                    break
                else:
                    sleep(.01)

        return d

    """
      serial ポートから \n が来るまで読み出す。
      戻り値:str (iso-8859-1)
    """

    def readline(self, timeout=None):
        d = b''
        ts = time.monotonic()
        if timeout is None: timeout = 1

        while True:
            c = self.ser.read_data(1)
            d += c

            if c == b'\n':
                break
            elif c == b'':
                sleep(0.01)

            # timeout check
            if time.monotonic() - ts > timeout:
                break
            else:
                pass

        return d.decode('iso-8859-1')

    """
        JN51XX のシリアルプロトコル
    """

    def talk(self,
             typ,
             anstype,
             addr=None,
             mlen=None,
             data=None,
             max_tim=None):
        length = 3

        if addr != None: length += 4
        if mlen != None: length += 2
        if data != None: length += len(data)

        msg = pack('<BB', length - 1, typ)
        if addr != None: msg += pack('<I', addr)
        if mlen != None: msg += pack('<H', mlen)
        if data != None:
            if data.__class__ == str:
                msg += data.encode()
            else:
                # msg += pack('<%is' % len(data), "".join(map(chr, data)))
                msg += bytes(data)

        msg += pack('<B', self.crc(msg, len(msg)))

        if anstype == None:
            self.write(msg)
            return []

        try:
            #self.ser.timeout = self.DEFAULT_TIMEOUT
            self.write(msg)
            n = self.read(1)
            ans = b''
            if (len(n) == 1): n = n[0]
            else: raise TypeError()
            while len(ans) < n:  # TODO: problematic
                ans += self.read(n)

        except TypeError:  # thrown when self.ser.read() gets nothing
            timeout = self.MAX_TIMEOUT
            if max_tim != None:
                timeout = max_tim

            n = self.read(1, timeout)
            ans = b''
            if (len(n) == 1): n = n[0]
            else: raise TypeError()
            while len(ans) < n:  # TODO: problematic
                ans += self.read(n)

        return ans[1:-1]