Skip to content
Permalink
Browse files

* Add a default log handler to avoid polluting stderr if no logger is…

… defined

* Enforce strong reset on start up
* Log the actual baudrate (vs. requested) as FTDI clock is not able to generate any clock value
* Remove SW flow control option which has never been implemented
* Add a new API to report addressable (vs. configured) GPIO pins on an FTDI interface
* Remove useless `__all__` definitions
* Bump version number
  • Loading branch information...
eblot committed Jan 28, 2019
1 parent c2e4fd0 commit 9b756677aca1a8375ed13dd8889b759e13e04b77
Showing with 117 additions and 36 deletions.
  1. +4 −3 pyftdi/__init__.py
  2. +1 −2 pyftdi/bits.py
  3. +48 −11 pyftdi/ftdi.py
  4. +1 −4 pyftdi/gpio.py
  5. +1 −4 pyftdi/i2c.py
  6. +0 −3 pyftdi/jtag.py
  7. +33 −6 pyftdi/spi.py
  8. +28 −0 pyftdi/tracer.py
  9. +1 −3 pyftdi/usbtools.py
@@ -1,4 +1,4 @@
# Copyright (c) 2010-2018 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2016, Neotion
# All rights reserved.
#
@@ -24,7 +24,7 @@
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__version__ = '0.29.2'
__version__ = '0.29.3'
__title__ = 'PyFtdi'
__description__ = 'FTDI device driver (pure Python)'
__uri__ = 'http://github.com/eblot/pyftdi'
@@ -36,12 +36,13 @@
__copyright__ = 'Copyright (c) 2011-2018 Emmanuel Blot'


from logging import WARNING, getLogger
from logging import WARNING, NullHandler, getLogger


class FtdiLogger:

log = getLogger('pyftdi')
log.addHandler(NullHandler())
log.setLevel(level=WARNING)

@classmethod
@@ -1,4 +1,4 @@
# Copyright (c) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2008-2016, Neotion
# All rights reserved.
#
@@ -32,7 +32,6 @@
from pyftdi.misc import is_iterable, xor


__all__ = ['BitSequence', 'BitZSequence', 'BitSequenceError', 'BitField']


class BitSequenceError(Exception):
@@ -5,7 +5,7 @@
Require: pyusb
"""

# Copyright (C) 2010-2018 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (C) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2016 Emmanuel Bouaziz <ebouaziz@free.fr>
# Originally based on the C libftdi project
# http://www.intra2net.com/en/developer/libftdi/
@@ -35,8 +35,6 @@
from .misc import to_bool
from .usbtools import UsbTools

__all__ = ['Ftdi', 'FtdiError']


class FtdiError(IOError):
"""Base class error for all FTDI device"""
@@ -526,6 +524,12 @@ def open_mpsse(self, vendor, product, index=0, serial=None, interface=1,
# Set chunk size
self.write_data_set_chunksize(512)
self.read_data_set_chunksize(512)
# Reset feature mode
self.set_bitmode(0, Ftdi.BITMODE_RESET)
# Enable MPSSE mode
self.set_bitmode(direction, Ftdi.BITMODE_MPSSE)
# Reset feature mode
self.set_bitmode(0, Ftdi.BITMODE_RESET)
# Drain buffers
self.purge_buffers()
# Disable event and error characters
@@ -766,6 +770,7 @@ def set_baudrate(self, baudrate):
baudrate *= Ftdi.BITBANG_CLOCK_MULTIPLIER
actual, value, index = self._convert_baudrate(baudrate)
delta = 100*abs(float(actual-baudrate))/baudrate
self.log.debug('Actual baudrate: %d %.1f%%', actual, delta)
if delta > Ftdi.BAUDRATE_TOLERANCE:
raise ValueError('Baudrate tolerance exceeded: %.02f%% '
'(wanted %d, achievable %d)' %
@@ -940,11 +945,43 @@ def set_flowctrl(self, flowctrl):
Either hardware flow control through RTS/CTS UART lines,
software or no flow control.
:param str flowctrl: one of 'hw', 'sw', ''
:param str flowctrl: one of 'hw', ''
:raise ValueError: if the flow control argument is invalid
..note:: How does RTS/CTS flow control work (from FTDI FAQ):
FTxxx RTS# pin is an output. It should be connected to the CTS#
input pin of the device at the other end of the UART link.
* If RTS# is logic 0 it is indicating the FTxxx device can
accept more data on the RXD pin.
* If RTS# is logic 1 it is indicating the FTxxx device
cannot accept more data.
RTS# changes state when the chip buffer reaches its last 32
bytes of space to allow time for the external device to stop
sending data to the FTxxx device.
FTxxx CTS# pin is an input. It should be connected to the RTS#
output pin of the device at the other end of the UART link.
* If CTS# is logic 0 it is indicating the external device can
accept more data, and the FTxxx will transmit on the TXD
pin.
* If CTS# is logic 1 it is indicating the external device
cannot accept more data. the FTxxx will stop transmitting
within 0~3 characters, depending on what is in the buffer.
**This potential 3 character overrun does occasionally
present problems.** Customers shoud be made aware the FTxxx
is a USB device and not a "normal" RS232 device as seen on
a PC. As such the device operates on a packet basis as
opposed to a byte basis.
Word to the wise. Not only do RS232 level shifting devices
level shift, but they also invert the signal.
"""
ctrl = {'hw': Ftdi.SIO_RTS_CTS_HS,
'sw': Ftdi.SIO_XON_XOFF_HS,
'': Ftdi.SIO_DISABLE_FLOW_CTRL}
try:
value = ctrl[flowctrl] | self.index
@@ -955,15 +992,15 @@ def set_flowctrl(self, flowctrl):
Ftdi.REQ_OUT, Ftdi.SIO_SET_FLOW_CTRL, 0, value, array('B'),
self.usb_write_timeout):
raise FtdiError('Unable to set flow control')
except usb.core.USBError as ex:
raise FtdiError('UsbError: %s' % str(ex))
except usb.core.USBError as exc:
raise FtdiError('UsbError: %s' % str(exc))

def set_dtr(self, state):
"""Set dtr line
:param bool state: new DTR logical level
"""
value = state and Ftdi.SIO_SET_DTR_HIGH or Ftdi.SIO_SET_DTR_LOW
value = Ftdi.SIO_SET_DTR_HIGH if state else Ftdi.SIO_SET_DTR_LOW
if self._ctrl_transfer_out(Ftdi.SIO_SET_MODEM_CTRL, value):
raise FtdiError('Unable to set DTR line')

@@ -972,7 +1009,7 @@ def set_rts(self, state):
:param bool state: new RTS logical level
"""
value = state and Ftdi.SIO_SET_RTS_HIGH or Ftdi.SIO_SET_RTS_LOW
value = Ftdi.SIO_SET_RTS_HIGH if state else Ftdi.SIO_SET_RTS_LOW
if self._ctrl_transfer_out(Ftdi.SIO_SET_MODEM_CTRL, value):
raise FtdiError('Unable to set RTS line')

@@ -983,8 +1020,8 @@ def set_dtr_rts(self, dtr, rts):
:param bool rts: new RTS logical level
"""
value = 0
value |= dtr and Ftdi.SIO_SET_DTR_HIGH or Ftdi.SIO_SET_DTR_LOW
value |= rts and Ftdi.SIO_SET_RTS_HIGH or Ftdi.SIO_SET_RTS_LOW
value |= Ftdi.SIO_SET_DTR_HIGH if dtr else Ftdi.SIO_SET_DTR_LOW
value |= Ftdi.SIO_SET_RTS_HIGH if rts else Ftdi.SIO_SET_RTS_LOW
if self._ctrl_transfer_out(Ftdi.SIO_SET_FLOW_CTRL, value):
raise FtdiError('Unable to set DTR/RTS lines')

@@ -1,6 +1,6 @@
"""GPIO/BitBang support for PyFdti"""

# Copyright (c) 2014-2018, Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2014-2019, Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.fr>
# All rights reserved.
#
@@ -30,9 +30,6 @@
from pyftdi.ftdi import Ftdi


__all__ = ['GpioController']


class GpioException(IOError):
"""Base class for GPIO errors"""

@@ -1,6 +1,6 @@
"""I2C support for PyFdti"""

# Copyright (c) 2017-2018, Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2017-2019, Emmanuel Blot <emmanuel.blot@free.fr>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -37,9 +37,6 @@
# pylint: disable-msg=consider-using-ternary


__all__ = ['I2cPort', 'I2cController']


class I2cIOError(IOError):
"""I2c I/O error"""

@@ -24,9 +24,6 @@
from pyftdi.bits import BitSequence


__all__ = ['JtagEngine', 'JtagTool']


class JtagError(Exception):
"""Generic JTAG error"""

@@ -1,6 +1,6 @@
"""SPI support for PyFdti"""

# Copyright (c) 2010-2018, Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2019, Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.fr>
# All rights reserved.
#
@@ -33,9 +33,6 @@
from threading import Lock


__all__ = ['SpiPort', 'SpiGpioPort', 'SpiController']


class SpiIOError(FtdiError):
"""SPI I/O error"""

@@ -197,9 +194,24 @@ def __init__(self, controller):

@property
def pins(self):
"""Report the addressable GPIOs as a bitfield."""
"""Report the configured GPIOs as a bitfield."""
return self._controller.gpio_pins

@property
def all_pins(self):
"""Report the addressable GPIOs as a bitfield"""
return self._controller.gpio_all_pins

@property
def width(self):
"""Report the FTDI count of addressable pins.
Note that all pins, including reserved SPI ones, are reported.
:return: the count of IO pins (including SPI ones).
"""
return self._controller.width

@property
def direction(self):
"""Provide the FTDI GPIO direction"""
@@ -366,10 +378,25 @@ def frequency(self):

@property
def gpio_pins(self):
"""Report the addressable GPIOs as a bitfield"""
"""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):
@@ -1,3 +1,31 @@
"""MPSSE command debug tracer."""

# Copyright (c) 2017-2019, Emmanuel Blot <emmanuel.blot@free.fr>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Neotion nor the names of its contributors may
# be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL NEOTION BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


from array import array
from binascii import hexlify
from collections import deque
@@ -1,4 +1,4 @@
# Copyright (C) 2010-2016 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (C) 2010-2019 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (C) 2016 Emmanuel Bouaziz <ebouaziz@free.fr>
# All rights reserved.
#
@@ -24,8 +24,6 @@
from urllib.parse import urlsplit
from .misc import to_int

__all__ = ['UsbTools']


class UsbToolsError(Exception):
"""UsbTools error"""

0 comments on commit 9b75667

Please sign in to comment.
You can’t perform that action at this time.