Coverage for tropicsquare / transports / ftdi_mpsse.py: 88%
33 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
1"""FTDI MPSSE SPI transport."""
3from tropicsquare.transports import L1Transport
4from tropicsquare.exceptions import TropicSquareError
7class FtdiMpsseTransport(L1Transport):
8 """L1 transport for FTDI MPSSE SPI bridges.
10 :param spi: Configured PyFtdi ``SpiPort`` instance
11 """
13 def __init__(self, spi) -> None:
14 self._selected = False
15 self._started = False
16 self._spi = spi
19 def _transfer(self, tx_data: bytes) -> bytes:
20 start = self._consume_start_flag()
21 return self._spi.exchange(
22 tx_data,
23 start=start,
24 stop=False,
25 duplex=True,
26 )
29 def _read(self, length: int) -> bytes:
30 if not self._selected:
31 raise RuntimeError("SPI read attempted with chip select inactive")
33 start = self._consume_start_flag()
34 return self._spi.read(
35 length,
36 start=start,
37 stop=False,
38 )
41 def _cs_low(self) -> None:
42 if self._selected:
43 return
45 self._selected = True
46 self._started = False
49 def _cs_high(self) -> None:
50 if not self._selected:
51 return
53 if self._started:
54 self._spi.force_select(True)
56 self._selected = False
57 self._started = False
60 def _consume_start_flag(self) -> bool:
61 if not self._selected:
62 raise RuntimeError("SPI transfer attempted with chip select inactive")
64 start = not self._started
65 self._started = True
66 return start