# `CircuitsFT232H.SPI`

Wires the FT232H's MPSSE engine up as an SPI master.

Pin assignments are fixed for v0.1:

| Pin      | Role          |
| -------- | ------------- |
| `ADBUS0` | SCK (output)  |
| `ADBUS1` | MOSI (output) |
| `ADBUS2` | MISO (input)  |
| `ADBUS3` | CS (active-low, output) |

Public API lives on `CircuitsFT232H.SPI.Backend` (a `Circuits.SPI.Backend`
implementation) and `CircuitsFT232H.SPI.Bus` (the struct returned by
`Circuits.SPI.open/2`). This module holds the shared MPSSE encoding logic
and bus-name conventions used by both.

# `config`

```elixir
@type config() :: %{
  mode: 0..3,
  bits_per_word: pos_integer(),
  speed_hz: pos_integer(),
  delay_us: non_neg_integer(),
  lsb_first: boolean(),
  sw_lsb_first: boolean()
}
```

Resolved SPI configuration map — matches `Circuits.SPI`'s `spi_option_map()`.

# `build_config`

```elixir
@spec build_config(keyword()) :: {:ok, config()} | {:error, term()}
```

Validates and merges user options against the SPI defaults.

# `bus_name`

```elixir
@spec bus_name(CircuitsFT232H.Device.id()) :: String.t()
```

Builds the canonical SPI bus name for a device id.

# `close`

```elixir
@spec close(CircuitsFT232H.Device.id(), config()) :: :ok
```

Tears down an SPI bus: deasserts CS and releases the mode lock so the
chip can be reused (e.g. for I2C) without restarting the Device.

# `configure`

```elixir
@spec configure(CircuitsFT232H.Device.id(), config()) :: :ok | {:error, term()}
```

Configures the FT232H for SPI traffic with the given config. Assumes the
chip is already in MPSSE mode and the SPI mode lock has been claimed.

# `find_descriptor`

```elixir
@spec find_descriptor(String.t()) ::
  {:ok, CircuitsFT232H.USB.Descriptor.t()}
  | {:error, :invalid_bus_name | :not_found | term()}
```

Looks up the `USB.Descriptor` whose id matches the SPI bus name.

# `max_transfer_size`

```elixir
@spec max_transfer_size() :: pos_integer()
```

Maximum number of bytes that can be transferred in one MPSSE call.

# `parse_bus_name`

```elixir
@spec parse_bus_name(String.t()) ::
  {:ok, CircuitsFT232H.Device.id()} | {:error, :invalid_bus_name}
```

Parses an SPI bus name into the underlying device id, or returns
`{:error, :invalid_bus_name}`.

# `transfer`

```elixir
@spec transfer(CircuitsFT232H.Device.id(), config(), binary()) ::
  {:ok, binary()} | {:error, term()}
```

Runs a full-duplex SPI transfer. Asserts CS, exchanges `data` for an equal
number of MISO bytes, then deasserts CS.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
