]> Repositories - Adafruit_Blinka-hackapet.git/blob - src/busio.py
Add support for FT2232H "microcontroller"
[Adafruit_Blinka-hackapet.git] / src / busio.py
1 """
2 `busio` - Bus protocol support like I2C and SPI
3 =================================================
4
5 See `CircuitPython:busio` in CircuitPython for more details.
6
7 * Author(s): cefn
8 """
9
10 import threading
11
12 import adafruit_platformdetect.constants.boards as ap_board
13 import adafruit_platformdetect.constants.chips as ap_chip
14 from adafruit_blinka import Enum, Lockable, agnostic
15 from adafruit_blinka.agnostic import board_id, detector
16
17 # pylint: disable=import-outside-toplevel,too-many-branches,too-many-statements
18 # pylint: disable=too-many-arguments,too-many-function-args
19
20
21 class I2C(Lockable):
22     """
23     Busio I2C Class for CircuitPython Compatibility. Used
24     for both MicroPython and Linux.
25     """
26
27     def __init__(self, scl, sda, frequency=400000):
28         self.init(scl, sda, frequency)
29
30     def init(self, scl, sda, frequency):
31         """Initialization"""
32         self.deinit()
33         if detector.board.ftdi_ft232h:
34             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
35
36             self._i2c = _I2C(frequency=frequency)
37             return
38         if detector.board.binho_nova:
39             from adafruit_blinka.microcontroller.nova.i2c import I2C as _I2C
40
41             self._i2c = _I2C(frequency=frequency)
42             return
43         if detector.board.microchip_mcp2221:
44             from adafruit_blinka.microcontroller.mcp2221.i2c import I2C as _I2C
45
46             self._i2c = _I2C(frequency=frequency)
47             return
48         if detector.board.greatfet_one:
49             from adafruit_blinka.microcontroller.nxp_lpc4330.i2c import I2C as _I2C
50
51             self._i2c = _I2C(frequency=frequency)
52             return
53         if detector.board.any_embedded_linux:
54             from adafruit_blinka.microcontroller.generic_linux.i2c import I2C as _I2C
55         elif detector.board.ftdi_ft2232h:
56             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
57         else:
58             from machine import I2C as _I2C
59         from microcontroller.pin import i2cPorts
60
61         for portId, portScl, portSda in i2cPorts:
62             try:
63                 if scl == portScl and sda == portSda:
64                     self._i2c = _I2C(portId, mode=_I2C.MASTER, baudrate=frequency)
65                     break
66             except RuntimeError:
67                 pass
68         else:
69             raise ValueError(
70                 "No Hardware I2C on (scl,sda)={}\nValid I2C ports: {}".format(
71                     (scl, sda), i2cPorts
72                 )
73             )
74
75         self._lock = threading.RLock()
76
77     def deinit(self):
78         """Deinitialization"""
79         try:
80             del self._i2c
81         except AttributeError:
82             pass
83
84     def __enter__(self):
85         self._lock.acquire()
86         return self
87
88     def __exit__(self, exc_type, exc_value, traceback):
89         self._lock.release()
90         self.deinit()
91
92     def scan(self):
93         """Scan for attached devices"""
94         return self._i2c.scan()
95
96     def readfrom_into(self, address, buffer, *, start=0, end=None):
97         """Read from a device at specified address into a buffer"""
98         if start != 0 or end is not None:
99             if end is None:
100                 end = len(buffer)
101             buffer = memoryview(buffer)[start:end]
102         stop = True  # remove for efficiency later
103         return self._i2c.readfrom_into(address, buffer, stop=stop)
104
105     def writeto(self, address, buffer, *, start=0, end=None, stop=True):
106         """Write to a device at specified address from a buffer"""
107         if isinstance(buffer, str):
108             buffer = bytes([ord(x) for x in buffer])
109         if start != 0 or end is not None:
110             if end is None:
111                 return self._i2c.writeto(address, memoryview(buffer)[start:], stop=stop)
112             return self._i2c.writeto(address, memoryview(buffer)[start:end], stop=stop)
113         return self._i2c.writeto(address, buffer, stop=stop)
114
115     def writeto_then_readfrom(
116         self,
117         address,
118         buffer_out,
119         buffer_in,
120         *,
121         out_start=0,
122         out_end=None,
123         in_start=0,
124         in_end=None,
125         stop=False
126     ):
127         """"Write to a device at specified address from a buffer then read
128         from a device at specified address into a buffer
129         """
130         return self._i2c.writeto_then_readfrom(
131             address,
132             buffer_out,
133             buffer_in,
134             out_start=out_start,
135             out_end=out_end,
136             in_start=in_start,
137             in_end=in_end,
138             stop=stop,
139         )
140
141
142 class SPI(Lockable):
143     """
144     Busio SPI Class for CircuitPython Compatibility. Used
145     for both MicroPython and Linux.
146     """
147
148     def __init__(self, clock, MOSI=None, MISO=None):
149         self.deinit()
150         if detector.board.ftdi_ft232h:
151             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
152             from adafruit_blinka.microcontroller.ftdi_mpsse.ft232h.pin import SCK, MOSI, MISO
153
154             self._spi = _SPI()
155             self._pins = (SCK, MOSI, MISO)
156             return
157         if detector.board.binho_nova:
158             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
159             from adafruit_blinka.microcontroller.nova.pin import SCK, MOSI, MISO
160
161             self._spi = _SPI(clock)
162             self._pins = (SCK, MOSI, MISO)
163             return
164         if detector.board.greatfet_one:
165             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
166             from adafruit_blinka.microcontroller.nxp_lpc4330.pin import SCK, MOSI, MISO
167
168             self._spi = _SPI()
169             self._pins = (SCK, MOSI, MISO)
170             return
171         if detector.board.any_embedded_linux:
172             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
173         elif detector.board.ftdi_ft2232h:
174             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
175         else:
176             from machine import SPI as _SPI
177         from microcontroller.pin import spiPorts
178
179         for portId, portSck, portMosi, portMiso in spiPorts:
180             if (
181                 (clock == portSck)
182                 and MOSI in (portMosi, None)  # Clock is required!
183                 and MISO in (portMiso, None)  # But can do with just output
184             ):  # Or just input
185                 self._spi = _SPI(portId)
186                 self._pins = (portSck, portMosi, portMiso)
187                 break
188         else:
189             raise ValueError(
190                 "No Hardware SPI on (SCLK, MOSI, MISO)={}\nValid SPI ports:{}".format(
191                     (clock, MOSI, MISO), spiPorts
192                 )
193             )
194
195     def configure(self, baudrate=100000, polarity=0, phase=0, bits=8):
196         """Update the configuration"""
197         if detector.board.any_raspberry_pi or detector.board.any_raspberry_pi_40_pin:
198             from adafruit_blinka.microcontroller.bcm283x.pin import Pin
199             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
200         elif detector.board.any_beaglebone:
201             from adafruit_blinka.microcontroller.am335x.pin import Pin
202             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
203         elif detector.board.any_orange_pi and detector.chip.id == ap_chip.SUN8I:
204             from adafruit_blinka.microcontroller.allwinner.h3.pin import Pin
205             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
206         elif board_id == ap_board.GIANT_BOARD:
207             from adafruit_blinka.microcontroller.sama5.pin import Pin
208             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
209         elif board_id == ap_board.CORAL_EDGE_TPU_DEV:
210             from adafruit_blinka.microcontroller.nxp_imx8m.pin import Pin
211             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
212         elif board_id == ap_board.ODROID_C2:
213             from adafruit_blinka.microcontroller.amlogic.s905.pin import Pin
214             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
215         elif board_id == ap_board.ODROID_C4:
216             from adafruit_blinka.microcontroller.amlogic.s905x3.pin import Pin
217             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
218         elif board_id == ap_board.ODROID_XU4:
219             from adafruit_blinka.microcontroller.samsung.exynos5422.pin import Pin
220             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
221         elif board_id == ap_board.DRAGONBOARD_410C:
222             from adafruit_blinka.microcontroller.snapdragon.apq8016.pin import Pin
223             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
224         elif board_id == ap_board.JETSON_NANO:
225             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
226             from adafruit_blinka.microcontroller.tegra.t210.pin import Pin
227         elif board_id == ap_board.JETSON_TX1:
228             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
229             from adafruit_blinka.microcontroller.tegra.t210.pin import Pin
230         elif board_id == ap_board.JETSON_TX2:
231             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
232             from adafruit_blinka.microcontroller.tegra.t186.pin import Pin
233         elif board_id == ap_board.JETSON_XAVIER:
234             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
235             from adafruit_blinka.microcontroller.tegra.t194.pin import Pin
236         elif board_id == ap_board.JETSON_NX:
237             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
238             from adafruit_blinka.microcontroller.tegra.t194.pin import Pin
239         elif detector.board.ROCK_PI_S:
240             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
241             from adafruit_blinka.microcontroller.rockchip.rk3308.pin import Pin
242         elif detector.board.SIFIVE_UNLEASHED:
243             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
244             from adafruit_blinka.microcontroller.hfu540.pin import Pin
245         elif detector.board.ftdi_ft232h:
246             from adafruit_blinka.microcontroller.ftdi_mpsse.ft232h.spi import SPI as _SPI
247             from adafruit_blinka.microcontroller.ftdi_mpsse.ft232h.pin import Pin
248         elif detector.board.ftdi_ft2232h:
249             from adafruit_blinka.microcontroller.ftdi_mpsse.ft2232h.spi import SPI as _SPI
250             from adafruit_blinka.microcontroller.ftdi_mpsse.ft2232h.pin import Pin
251         elif detector.board.binho_nova:
252             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
253             from adafruit_blinka.microcontroller.nova.pin import Pin
254         elif detector.board.greatfet_one:
255             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
256             from adafruit_blinka.microcontroller.nxp_lpc4330.pin import Pin
257         elif board_id in (ap_board.PINE64, ap_board.PINEBOOK, ap_board.PINEPHONE):
258             from adafruit_blinka.microcontroller.allwinner.a64.pin import Pin
259             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
260         elif board_id == ap_board.CLOCKWORK_CPI3:
261             from adafruit_blinka.microcontroller.allwinner.a33.pin import Pin
262             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
263         elif board_id == ap_board.ONION_OMEGA2:
264             from adafruit_blinka.microcontroller.mips24kec.pin import Pin
265             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
266         else:
267             from machine import SPI as _SPI
268             from machine import Pin
269
270         if self._locked:
271             # TODO check if #init ignores MOSI=None rather than unsetting, to save _pinIds attribute
272             self._spi.init(
273                 baudrate=baudrate,
274                 polarity=polarity,
275                 phase=phase,
276                 bits=bits,
277                 firstbit=_SPI.MSB,
278                 sck=Pin(self._pins[0].id),
279                 mosi=Pin(self._pins[1].id),
280                 miso=Pin(self._pins[2].id),
281             )
282         else:
283             raise RuntimeError("First call try_lock()")
284
285     def deinit(self):
286         """Deinitialization"""
287         self._spi = None
288         self._pinIds = None
289
290     @property
291     def frequency(self):
292         """Return the baud rate if implemented"""
293         try:
294             return self._spi.frequency
295         except AttributeError:
296             raise NotImplementedError(
297                 "Frequency attribute not implemented for this platform"
298             )
299
300     def write(self, buf, start=0, end=None):
301         """Write to the SPI device"""
302         return self._spi.write(buf, start, end)
303
304     def readinto(self, buf, start=0, end=None, write_value=0):
305         """Read from the SPI device into a buffer"""
306         return self._spi.readinto(buf, start, end, write_value=write_value)
307
308     def write_readinto(
309         self, buffer_out, buffer_in, out_start=0, out_end=None, in_start=0, in_end=None
310     ):
311         """Write to the SPI device and read from the SPI device into a buffer"""
312         return self._spi.write_readinto(
313             buffer_out, buffer_in, out_start, out_end, in_start, in_end
314         )
315
316
317 class UART(Lockable):
318     """
319     Busio UART Class for CircuitPython Compatibility. Used
320     for MicroPython and a few other non-Linux boards.
321     """
322
323     class Parity(Enum):
324         """Parity Enumeration"""
325
326         pass  # pylint: disable=unnecessary-pass
327
328     Parity.ODD = Parity()
329     Parity.EVEN = Parity()
330
331     def __init__(
332         self,
333         tx,
334         rx,
335         baudrate=9600,
336         bits=8,
337         parity=None,
338         stop=1,
339         timeout=1000,
340         receiver_buffer_size=64,
341         flow=None,
342     ):
343         if detector.board.any_embedded_linux:
344             raise RuntimeError(
345                 "busio.UART not supported on this platform. Please use pyserial instead."
346             )
347         if detector.board.binho_nova:
348             from adafruit_blinka.microcontroller.nova.uart import UART as _UART
349         elif detector.board.greatfet_one:
350             from adafruit_blinka.microcontroller.nxp_lpc4330.uart import UART as _UART
351         else:
352             from machine import UART as _UART
353
354         if detector.board.binho_nova:
355             from adafruit_blinka.microcontroller.nova.pin import uartPorts
356         else:
357             from microcontroller.pin import uartPorts
358
359         self.baudrate = baudrate
360
361         if flow is not None:  # default 0
362             raise NotImplementedError(
363                 "Parameter '{}' unsupported on {}".format("flow", agnostic.board_id)
364             )
365
366         # translate parity flag for Micropython
367         if parity is UART.Parity.ODD:
368             parity = 1
369         elif parity is UART.Parity.EVEN:
370             parity = 0
371         elif parity is None:
372             pass
373         else:
374             raise ValueError("Invalid parity")
375
376         # check tx and rx have hardware support
377         for portId, portTx, portRx in uartPorts:  #
378             if portTx == tx and portRx == rx:
379                 self._uart = _UART(
380                     portId,
381                     baudrate,
382                     bits=bits,
383                     parity=parity,
384                     stop=stop,
385                     timeout=timeout,
386                     read_buf_len=receiver_buffer_size,
387                 )
388                 break
389         else:
390             raise ValueError(
391                 "No Hardware UART on (tx,rx)={}\nValid UART ports: {}".format(
392                     (tx, rx), uartPorts
393                 )
394             )
395
396     def deinit(self):
397         """Deinitialization"""
398         if detector.board.binho_nova:
399             self._uart.deinit()
400         self._uart = None
401
402     def read(self, nbytes=None):
403         """Read from the UART"""
404         return self._uart.read(nbytes)
405
406     def readinto(self, buf, nbytes=None):
407         """Read from the UART into a buffer"""
408         return self._uart.readinto(buf, nbytes)
409
410     def readline(self):
411         """Read a line of characters up to a newline charater from the UART"""
412         return self._uart.readline()
413
414     def write(self, buf):
415         """Write to the UART from a buffer"""
416         return self._uart.write(buf)