CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyftdi

Pure Python FTDI device driver for USB-to-serial/GPIO/SPI/I2C/JTAG bridge devices

Pending
Overview
Eval results
Files

spi.mddocs/

SPI Master Communication

SPI master controller implementation enabling communication with SPI devices. Supports multiple chip selects, simultaneous GPIO access, variable clock speeds up to 30MHz, and advanced features like non-byte-aligned transfers.

Capabilities

SPI Controller Setup

Configure and manage SPI controller with multiple device support.

class SpiController:
    def configure(self, url, **kwargs):
        """
        Configure SPI controller with FTDI device.
        
        Parameters:
        - url: FTDI device URL (e.g., 'ftdi:///1')
        - frequency: Default SPI clock frequency in Hz (default: 6MHz)
        - cs_count: Number of chip select lines (1-8)
        - turbo: Enable turbo mode for higher performance
        - debug: Enable debug output
        
        Raises:
        - SpiIOError: Configuration failed
        - FtdiError: Device access error
        """
    
    def terminate(self):
        """Terminate SPI controller and release device."""
    
    def get_frequency(self) -> float:
        """Get actual configured frequency."""
    
    def get_gpio(self):
        """
        Get GPIO controller for unused pins.
        
        Returns:
        GpioMpsseController: GPIO controller for remaining pins
        """

SPI Port Access

Get SPI port instances for communication with specific devices.

def get_port(self, cs: int, freq: float = 6000000, mode: int = 0) -> 'SpiPort':
    """
    Get SPI port for specific chip select.
    
    Parameters:
    - cs: Chip select number (0-7)
    - freq: SPI clock frequency in Hz
    - mode: SPI mode (0-3)
      - Mode 0: CPOL=0, CPHA=0 (clock idle low, sample on rising edge)
      - Mode 1: CPOL=0, CPHA=1 (clock idle low, sample on falling edge)  
      - Mode 2: CPOL=1, CPHA=0 (clock idle high, sample on falling edge)
      - Mode 3: CPOL=1, CPHA=1 (clock idle high, sample on rising edge)
    
    Returns:
    SpiPort: Port instance for device communication
    """

SPI Data Transfer

Perform SPI transactions with various transfer patterns.

class SpiPort:
    def exchange(self, out: bytes, readlen: int = 0, start: bool = True, 
                stop: bool = True, duplex: bool = False) -> bytes:
        """
        Perform full-duplex SPI transaction.
        
        Parameters:
        - out: Data bytes to transmit
        - readlen: Number of additional bytes to read after transmission
        - start: Assert chip select at start
        - stop: Deassert chip select at end
        - duplex: True for simultaneous read/write, False for write-then-read
        
        Returns:
        bytes: Data received during transaction
        """
    
    def read(self, readlen: int, start: bool = True, stop: bool = True) -> bytes:
        """
        Read data from SPI device.
        
        Parameters:
        - readlen: Number of bytes to read
        - start: Assert chip select at start
        - stop: Deassert chip select at end
        
        Returns:
        bytes: Data read from device
        """
    
    def write(self, out: bytes, start: bool = True, stop: bool = True):
        """
        Write data to SPI device.
        
        Parameters:
        - out: Data bytes to write
        - start: Assert chip select at start  
        - stop: Deassert chip select at end
        """

Advanced SPI Features

Non-standard SPI operations and advanced transfer modes.

def write_readinto(self, out: bytes, read_buf: bytearray, start: bool = True, 
                  stop: bool = True):
    """Write data and read response into existing buffer."""

def flush(self):
    """Flush any pending SPI operations."""

def set_frequency(self, frequency: float) -> float:
    """
    Set SPI clock frequency for this port.
    
    Parameters:
    - frequency: Desired frequency in Hz
    
    Returns:
    float: Actual configured frequency
    """

def get_frequency(self) -> float:
    """Get current SPI clock frequency."""

SPI with GPIO Support

SPI port with additional GPIO control for device management.

class SpiGpioPort(SpiPort):
    """SPI port with GPIO capabilities for control signals."""
    
    def set_gpio_direction(self, pins: int, direction: int):
        """Set GPIO pin directions (0=input, 1=output)."""
    
    def read_gpio(self) -> int:
        """Read current GPIO pin states."""
    
    def write_gpio(self, value: int):
        """Write GPIO pin states."""
    
    def exchange_gpio(self, out: bytes, readlen: int, gpio_dir: int, 
                     gpio_out: int) -> tuple:
        """
        Perform SPI exchange with simultaneous GPIO control.
        
        Returns:
        tuple: (spi_data, gpio_state)
        """

Device Support

Pin Assignments

Standard FTDI SPI pin assignments:

FT232H (8 pins available):

  • SCK: AD0 (SPI Clock)
  • MOSI: AD1 (Master Out, Slave In)
  • MISO: AD2 (Master In, Slave Out)
  • CS0: AD3 (Chip Select 0)
  • CS1: AD4 (Chip Select 1)
  • CS2-CS7: AD5-AD7, AC0-AC2 (Additional chip selects)

FT2232H/FT4232H:

  • Similar pin assignments per interface
  • Multiple interfaces allow independent SPI controllers

Clock Speeds

Supported SPI clock frequencies:

  • FT232H: Up to 30 MHz
  • FT2232H: Up to 30 MHz
  • FT2232C/D: Up to 6 MHz
  • Actual speeds: Depend on FTDI device oscillator and divider limitations

Usage Examples

Basic SPI Communication

from pyftdi.spi import SpiController

# Configure SPI controller
spi = SpiController()
spi.configure('ftdi:///1')

# Get SPI port for device on CS0
device = spi.get_port(cs=0, freq=1E6, mode=0)

# Read device ID (common SPI flash operation)
device_id = device.exchange([0x9F], 3)  # JEDEC ID command
print(f"Device ID: {device_id.hex()}")

# Write enable command
device.write([0x06])

# Read status register
status = device.exchange([0x05], 1)[0]
print(f"Status: 0x{status:02x}")

# Clean up
spi.terminate()

Multiple Device Management

from pyftdi.spi import SpiController

spi = SpiController()
spi.configure('ftdi:///1', cs_count=3)

# Different devices on different chip selects
flash = spi.get_port(cs=0, freq=10E6, mode=0)  # SPI Flash
adc = spi.get_port(cs=1, freq=1E6, mode=1)    # ADC
dac = spi.get_port(cs=2, freq=5E6, mode=2)    # DAC

# Independent communication with each device
flash_id = flash.exchange([0x9F], 3)
adc_value = adc.exchange([0x00, 0x00], 2)
dac.write([0x30, 0xFF, 0x00])  # Set DAC output

spi.terminate()

SPI with GPIO Control

from pyftdi.spi import SpiController

spi = SpiController()
spi.configure('ftdi:///1')

# Get SPI port and GPIO controller
device = spi.get_port(cs=0, freq=6E6, mode=0)
gpio = spi.get_gpio()

# Configure additional pins as GPIO
gpio.set_direction(0xF0, 0xF0)  # Upper 4 bits as outputs

# Control reset line while doing SPI
gpio.write(0x10)  # Assert reset
device.write([0x01, 0x02, 0x03])  # Send data
gpio.write(0x00)  # Release reset

spi.terminate()

High-Performance Transfers

from pyftdi.spi import SpiController

spi = SpiController()
spi.configure('ftdi:///1', turbo=True)  # Enable turbo mode

device = spi.get_port(cs=0, freq=30E6, mode=0)  # Max speed

# Large data transfer
data_out = bytes(range(256))  # 256 bytes of test data
response = device.exchange(data_out, len(data_out), duplex=True)

# Continuous operations without CS toggling
device.write([0x02, 0x00, 0x00], start=True, stop=False)  # Write command
device.write(data_out, start=False, stop=False)           # Data
device.write([0x00], start=False, stop=True)              # End transfer

spi.terminate()

Exception Handling

from pyftdi.spi import SpiController, SpiIOError
from pyftdi.ftdi import FtdiError

try:
    spi = SpiController()
    spi.configure('ftdi:///1')
    
    device = spi.get_port(cs=0)
    data = device.exchange([0x9F], 3)
    
except SpiIOError as e:
    print(f"SPI communication error: {e}")
except FtdiError as e:
    print(f"FTDI device error: {e}")
finally:
    if 'spi' in locals():
        spi.terminate()

Types

# Exception types
class SpiIOError(FtdiError):
    """SPI communication error"""

# SPI mode constants
SPI_MODE_0 = 0  # CPOL=0, CPHA=0
SPI_MODE_1 = 1  # CPOL=0, CPHA=1  
SPI_MODE_2 = 2  # CPOL=1, CPHA=0
SPI_MODE_3 = 3  # CPOL=1, CPHA=1

Install with Tessl CLI

npx tessl i tessl/pypi-pyftdi

docs

core-ftdi.md

eeprom.md

gpio.md

i2c.md

index.md

jtag.md

serial.md

spi.md

usb-tools.md

tile.json