# `serialext` - UART API

There is no dedicated module for the UART API, as PyFtdi acts as a backend of the well-known pyserial module.

The pyserial backend module is implemented as the serialext.protocol_ftdi module. It is not documented here as no direct call to this module is required, as the UART client should use the regular pyserial API.

## Usage

To enable PyFtdi as a pyserial backend, use the following import:

    import pyftdi.serialext

Then use

    pyftdi.serialext.serial_for_url(url, **options)

to open a pyserial serial port instance.

## Quickstart

    # Enable pyserial extensions
    import pyftdi.serialext

    # Open a serial port on the second FTDI device interface (IF/2) @ 3Mbaud
    port = pyftdi.serialext.serial_for_url('ftdi://ftdi:2232h/2', baudrate=3000000)

    # Send bytes
    port.write(b'Hello World')

    # Receive bytes
    data = port.read(1024)

## GPIO access

UART mode, the primary function of FTDI *232* devices, is somewhat limited when it comes to GPIO management, as opposed to alternative mode such as I2C, SPI and JTAG. It is not possible to assign the unused pins of an UART mode to arbitrary GPIO functions.

All the 8 lower pins of an UART port are dedicated to the UART function, although most of them are seldomely used, as dedicated to manage a modem or a legacy DCE device. Upper pins (b7..b15), on devices that have ones, cannot be driven while UART port is enabled.

It is nevertheless possible to have limited access to the lower pins as GPIO, with many limitations:

- the GPIO direction of each pin is hardcoded and cannot be changed

- GPIO pins cannot be addressed atomically: it is possible to read the state of an input GPIO, or to change the state of an output GPIO, one after another. This means than obtaining the state of several input GPIOs or changing the state of several output GPIO at once is not possible.

- some pins cannot be used as GPIO is hardware flow control is enabled. Keep in mind However that HW flow control with FTDI is not reliable, see the Hardware flow control section.

Accessing those GPIO pins is done through the UART extended pins, using their UART assigned name, as PySerial port attributes. See the table below:

Bit | UART | Direction | API
--- | --- | --- | ---
b0 | TX | Out | `port.write(buffer)`
b1 | RX | In | `buffer = port.read(count)`
b2 | RTS | Out | `port.rts = state`
b3 | CTS | In | `state = port.cts`
b4 | DTR | Out | `port.dtr = state`
b5 | DSR | In | `state = port.dsr`
b6 | DCD | In | `state = port.dcd`
b7 | RI | In | `state = port.ri`

### CBUS support

Some FTDI devices (FT232R, FT232H, FT230X, FT231X) support additional CBUS pins, which can be used as regular GPIOs pins. See CBUS GPIO for details.

## Mini serial terminal

`pyterm.py` is a simple serial terminal that can be used to test the serial port feature. See the Tools chapter to locate this tool.

    Usage: pyterm.py [-h] [-f] [-b BAUDRATE] [-w] [-e] [-r] [-l] [-s] [-P VIDPID]
                     [-V VIRTUAL] [-v] [-d]
                     [device]

    Simple Python serial terminal

    positional arguments:
      device                serial port device name (default: ftdi:///1)

    optional arguments:
      -h, --help            show this help message and exit
      -f, --fullmode        use full terminal mode, exit with [Ctrl]+B
      -b BAUDRATE, --baudrate BAUDRATE
                            serial port baudrate (default: 115200)
      -w, --hwflow          hardware flow control
      -e, --localecho       local echo mode (print all typed chars)
      -r, --crlf            prefix LF with CR char, use twice to replace all LF
                            with CR chars
      -l, --loopback        loopback mode (send back all received chars)
      -s, --silent          silent mode
      -P VIDPID, --vidpid VIDPID
                            specify a custom VID:PID device ID, may be repeated
      -V VIRTUAL, --virtual VIRTUAL
                            use a virtual device, specified as YaML
      -v, --verbose         increase verbosity
      -d, --debug           enable debug mode

If the PyFtdi module is not yet installed and `pyterm.py` is run from the archive directory, `PYTHONPATH` should be defined to the current directory:

    PYTHONPATH=$PWD pyftdi/bin/pyterm.py ftdi:///?

The above command lists all the available FTDI device ports.

To start up a serial terminal session, specify the FTDI port to use, for example:

    # detect all FTDI connected devices
    PYTHONPATH=. python3 pyftdi/bin/ftdi_urls.py
    # use the first interface of the first FT2232H as a serial port
    PYTHONPATH=$PWD pyftdi/bin/pyterm.py ftdi://ftdi:2232/1

## Limitations

Although the FTDI H series are in theory capable of 12 MBps baudrate, baudrates above 6 Mbps are barely usable.

See the following table for details.

Requ. bps | HW capability | 9-bit time | Real bps | Duty cycle | Stable
--- | --- | --- | --- | --- | ---
115.2 Kbps | 115.2 Kbps | 78.08 µs | 115.26 Kbps | 49.9% | Yes
460.8 Kbps | 461.54 Kbps | 19.49 µs | 461.77 Kbps | 49.9% | Yes
1 Mbps | 1 Mbps | 8.98 µs | 1.002 Mbps | 49.5% | Yes
4 Mbps | 4 Mbps | 2.24 µs | 4.018 Mbps | 48% | Yes
5 Mbps | 5.052 Mbps | 1.78 µs | 5.056 Mbps | 50% | Yes
6 Mbps | 6 Mbps | 1.49 µs | 6.040 Mbps | 48.5% | Yes
7 Mbps | 6.857 Mbps | 1.11 µs | 8.108 Mbps | 44% | No
8 Mbps | 8 Mbps | 1.11 µs | 8.108 Mbps | 44%-48% | No
8.8 Mbps | 8.727 Mbps | 1.13 µs | 7.964 Mbps | 44% | No
9.6 Mbps | 9.6 Mbps | 1.12 µs | 8.036 Mbps | 48% | No
10.5 Mbps | 10.667 Mbps | 1.11 µs | 8.108 Mbps | 44% | No
12 Mbps | 12 Mbps | 0.75 µs | 12 Mbps | 43% | Yes

> - 9-bit time is the measured time @ FTDI output pins for a 8-bit character (start bit + 8 bit data)

> - Duty cycle is the ratio between a low-bit duration and a high-bit duration, a good UART should exhibit the same duration for low bits and high bits, i.e. a duty cycle close to 50%.

> - Stability reports whether subsequent runs, with the very same HW settings, produce the same timings.

Achieving a reliable connection over 6 Mbps has proven difficult, if not impossible: Any baudrate greater than 6 Mbps (except the upper 12 Mbps limit) results into an actual baudrate of about 8 Mbps, and suffer from clock fluterring [7.95 .. 8.1Mbps].

### Hardware flow control

Moreover, as the hardware flow control of the FTDI device is not a true HW flow control. Quoting FTDI application note:

> 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.