]> Repositories - Adafruit_Blinka-hackapet.git/blob - src/busio.py
More changes and ran pre-commit
[Adafruit_Blinka-hackapet.git] / src / busio.py
1 # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
2 #
3 # SPDX-License-Identifier: MIT
4 """
5 `busio` - Bus protocol support like I2C and SPI
6 =================================================
7
8 See `CircuitPython:busio` in CircuitPython for more details.
9
10 * Author(s): cefn
11 """
12 try:
13     import threading
14 except ImportError:
15     threading = None
16
17 # pylint: disable=unused-import
18 import adafruit_platformdetect.constants.boards as ap_board
19 import adafruit_platformdetect.constants.chips as ap_chip
20 from adafruit_blinka import Enum, Lockable, agnostic
21 from adafruit_blinka.agnostic import board_id, detector
22
23 # pylint: disable=import-outside-toplevel,too-many-branches,too-many-statements
24 # pylint: disable=too-many-arguments,too-many-function-args,too-many-return-statements
25
26
27 class I2C(Lockable):
28     """
29     Busio I2C Class for CircuitPython Compatibility. Used
30     for both MicroPython and Linux.
31
32     NOTE: Frequency has no effect on Linux systems. The argument is only there for compatibility.
33     """
34
35     def __init__(self, scl, sda, frequency=100000):
36         self.init(scl, sda, frequency)
37
38     def init(self, scl, sda, frequency):
39         """Initialization"""
40         self.deinit()
41         if detector.board.ftdi_ft232h:
42             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
43
44             self._i2c = _I2C(frequency=frequency)
45             return
46         if detector.board.binho_nova:
47             from adafruit_blinka.microcontroller.nova.i2c import I2C as _I2C
48
49             self._i2c = _I2C(frequency=frequency)
50             return
51         if detector.board.microchip_mcp2221:
52             from adafruit_blinka.microcontroller.mcp2221.i2c import I2C as _I2C
53
54             self._i2c = _I2C(frequency=frequency)
55             return
56
57         if detector.board.OS_AGNOSTIC_BOARD:
58             from adafruit_blinka.microcontroller.generic_agnostic_board.i2c import (
59                 I2C as _I2C,
60             )
61
62             self._i2c = _I2C(frequency=frequency)
63             return
64
65         if detector.board.greatfet_one:
66             from adafruit_blinka.microcontroller.nxp_lpc4330.i2c import I2C as _I2C
67
68             self._i2c = _I2C(frequency=frequency)
69             return
70         if detector.board.pico_u2if:
71             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import I2C_Pico as _I2C
72
73             self._i2c = _I2C(scl, sda, frequency=frequency)
74             return
75         if detector.board.feather_u2if:
76             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
77                 I2C_Feather as _I2C,
78             )
79
80             self._i2c = _I2C(scl, sda, frequency=frequency)
81             return
82         if detector.board.feather_can_u2if:
83             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
84                 I2C_Feather_CAN as _I2C,
85             )
86
87             self._i2c = _I2C(scl, sda, frequency=frequency)
88             return
89         if detector.board.feather_epd_u2if:
90             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
91                 I2C_Feather_EPD as _I2C,
92             )
93
94             self._i2c = _I2C(scl, sda, frequency=frequency)
95             return
96         if detector.board.feather_rfm_u2if:
97             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
98                 I2C_Feather_RFM as _I2C,
99             )
100
101             self._i2c = _I2C(scl, sda, frequency=frequency)
102             return
103         if detector.board.qtpy_u2if:
104             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import I2C_QTPY as _I2C
105
106             self._i2c = _I2C(scl, sda, frequency=frequency)
107             return
108         if detector.board.itsybitsy_u2if:
109             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
110                 I2C_ItsyBitsy as _I2C,
111             )
112
113             self._i2c = _I2C(scl, sda, frequency=frequency)
114             return
115         if detector.board.macropad_u2if:
116             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
117                 I2C_MacroPad as _I2C,
118             )
119
120             self._i2c = _I2C(scl, sda, frequency=frequency)
121             return
122         if detector.board.qt2040_trinkey_u2if:
123             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
124                 I2C_QT2040_Trinkey as _I2C,
125             )
126
127             self._i2c = _I2C(scl, sda, frequency=frequency)
128             return
129         if detector.board.kb2040_u2if:
130             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
131                 I2C_KB2040 as _I2C,
132             )
133
134             self._i2c = _I2C(scl, sda, frequency=frequency)
135             return
136         if detector.board.radxa_x4_u2if:
137             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
138                 I2C_Radxa_X4 as _I2C,
139             )
140
141             self._i2c = _I2C(scl, sda, frequency=frequency)
142             return
143         if detector.chip.id == ap_chip.RP2040:
144             from adafruit_blinka.microcontroller.rp2040.i2c import I2C as _I2C
145
146             self._i2c = _I2C(scl, sda, frequency=frequency)
147             return
148         if detector.board.any_siemens_iot2000:
149             from adafruit_blinka.microcontroller.am65xx.i2c import I2C as _I2C
150
151             self._i2c = _I2C(frequency=frequency)
152             return
153
154         if detector.board.any_embedded_linux:
155             from adafruit_blinka.microcontroller.generic_linux.i2c import I2C as _I2C
156
157             if frequency == 100000:
158                 frequency = None  # Set to None if default to avoid triggering warning
159         elif detector.board.ftdi_ft2232h:
160             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
161         elif detector.board.ftdi_ft4232h:
162             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
163         else:
164             from adafruit_blinka.microcontroller.generic_micropython.i2c import (
165                 I2C as _I2C,
166             )
167         from microcontroller.pin import i2cPorts
168
169         for portId, portScl, portSda in i2cPorts:
170             try:
171                 # pylint: disable=unexpected-keyword-arg
172                 if scl == portScl and sda == portSda:
173                     self._i2c = _I2C(portId, mode=_I2C.MASTER, baudrate=frequency)
174                     break
175                 # pylint: enable=unexpected-keyword-arg
176             except RuntimeError:
177                 pass
178         else:
179             raise ValueError(
180                 "No Hardware I2C on (scl,sda)={}\nValid I2C ports: {}".format(
181                     (scl, sda), i2cPorts
182                 )
183             )
184         if threading is not None:
185             self._lock = threading.RLock()
186
187     def deinit(self):
188         """Deinitialization"""
189         try:
190             del self._i2c
191         except AttributeError:
192             pass
193
194     def __enter__(self):
195         if threading is not None:
196             self._lock.acquire()
197         return self
198
199     def __exit__(self, exc_type, exc_value, traceback):
200         if threading is not None:
201             self._lock.release()
202         self.deinit()
203
204     def scan(self):
205         """Scan for attached devices"""
206         return self._i2c.scan()
207
208     def readfrom_into(self, address, buffer, *, start=0, end=None):
209         """Read from a device at specified address into a buffer"""
210         if start != 0 or end is not None:
211             if end is None:
212                 end = len(buffer)
213             buffer = memoryview(buffer)[start:end]
214         return self._i2c.readfrom_into(address, buffer, stop=True)
215
216     def writeto(self, address, buffer, *, start=0, end=None):
217         """Write to a device at specified address from a buffer"""
218         if isinstance(buffer, str):
219             buffer = bytes([ord(x) for x in buffer])
220         if start != 0 or end is not None:
221             if end is None:
222                 return self._i2c.writeto(address, memoryview(buffer)[start:], stop=True)
223             return self._i2c.writeto(address, memoryview(buffer)[start:end], stop=True)
224         return self._i2c.writeto(address, buffer, stop=True)
225
226     def writeto_then_readfrom(
227         self,
228         address,
229         buffer_out,
230         buffer_in,
231         *,
232         out_start=0,
233         out_end=None,
234         in_start=0,
235         in_end=None,
236         stop=False,
237     ):
238         """ "Write to a device at specified address from a buffer then read
239         from a device at specified address into a buffer
240         """
241         return self._i2c.writeto_then_readfrom(
242             address,
243             buffer_out,
244             buffer_in,
245             out_start=out_start,
246             out_end=out_end,
247             in_start=in_start,
248             in_end=in_end,
249             stop=stop,
250         )
251
252
253 class SPI(Lockable):
254     """
255     Busio SPI Class for CircuitPython Compatibility. Used
256     for both MicroPython and Linux.
257     """
258
259     def __init__(self, clock, MOSI=None, MISO=None):
260         self.deinit()
261         if detector.board.ftdi_ft232h:
262             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
263             from adafruit_blinka.microcontroller.ftdi_mpsse.ft232h.pin import (
264                 SCK,
265                 MOSI,
266                 MISO,
267             )
268
269             self._spi = _SPI()
270             self._pins = (SCK, MOSI, MISO)
271             return
272         if detector.board.binho_nova:
273             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
274             from adafruit_blinka.microcontroller.nova.pin import SCK, MOSI, MISO
275
276             self._spi = _SPI(clock)
277             self._pins = (SCK, MOSI, MISO)
278             return
279         if detector.board.greatfet_one:
280             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
281             from adafruit_blinka.microcontroller.nxp_lpc4330.pin import SCK, MOSI, MISO
282
283             self._spi = _SPI()
284             self._pins = (SCK, MOSI, MISO)
285             return
286         if detector.board.pico_u2if:
287             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Pico as _SPI
288
289             self._spi = _SPI(clock)  # this is really all that's needed
290             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
291             return
292         if detector.board.feather_u2if:
293             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
294                 SPI_Feather as _SPI,
295             )
296
297             self._spi = _SPI(clock)  # this is really all that's needed
298             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
299             return
300         if detector.board.feather_can_u2if:
301             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
302                 SPI_Feather_CAN as _SPI,
303             )
304
305             self._spi = _SPI(clock)  # this is really all that's needed
306             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
307             return
308         if detector.board.feather_epd_u2if:
309             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
310                 SPI_Feather_EPD as _SPI,
311             )
312
313             self._spi = _SPI(clock)  # this is really all that's needed
314             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
315             return
316         if detector.board.feather_rfm_u2if:
317             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
318                 SPI_Feather_RFM as _SPI,
319             )
320
321             self._spi = _SPI(clock)  # this is really all that's needed
322             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
323             return
324         if detector.board.itsybitsy_u2if:
325             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
326                 SPI_ItsyBitsy as _SPI,
327             )
328
329             self._spi = _SPI(clock)  # this is really all that's needed
330             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
331             return
332         if detector.board.macropad_u2if:
333             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
334                 SPI_MacroPad as _SPI,
335             )
336
337             self._spi = _SPI(clock)  # this is really all that's needed
338             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
339             return
340         if detector.board.qtpy_u2if:
341             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_QTPY as _SPI
342
343             self._spi = _SPI(clock)  # this is really all that's needed
344             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
345             return
346         if detector.board.kb2040_u2if:
347             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
348                 SPI_KB2040 as _SPI,
349             )
350
351             self._spi = _SPI(clock)  # this is really all that's needed
352             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
353             return
354         if detector.board.radxa_x4_u2if:
355             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
356                 SPI_Radxa_X4 as _SPI,
357             )
358
359             self._spi = _SPI(clock)  # this is really all that's needed
360             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
361             return
362         if detector.chip.id == ap_chip.RP2040:
363             from adafruit_blinka.microcontroller.rp2040.spi import SPI as _SPI
364
365             self._spi = _SPI(clock, MOSI, MISO)  # Pins configured on instantiation
366             self._pins = (clock, clock, clock)  # These don't matter, they're discarded
367             return
368         if detector.board.any_siemens_iot2000:
369             from adafruit_blinka.microcontroller.am65xx.spi import SPI as _SPI
370
371             self._spi = _SPI(clock)  # this is really all that's needed
372             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
373             return
374         if detector.board.any_embedded_linux:
375             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
376         elif detector.board.ftdi_ft2232h:
377             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
378         elif detector.board.ftdi_ft4232h:
379             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
380         elif detector.board.OS_AGNOSTIC_BOARD:
381             from adafruit_blinka.microcontroller.generic_agnostic_board.spi import (
382                 SPI as _SPI,
383             )
384         else:
385             from adafruit_blinka.microcontroller.generic_micropython.spi import (
386                 SPI as _SPI,
387             )
388         from microcontroller.pin import spiPorts
389
390         for portId, portSck, portMosi, portMiso in spiPorts:
391             if (
392                 (clock == portSck)
393                 and MOSI in (portMosi, None)  # Clock is required!
394                 and MISO in (portMiso, None)  # But can do with just output
395             ):  # Or just input
396                 self._spi = _SPI(portId)
397                 self._pins = (portSck, portMosi, portMiso)
398                 break
399         else:
400             raise ValueError(
401                 "No Hardware SPI on (SCLK, MOSI, MISO)={}\nValid SPI ports:{}".format(
402                     (clock, MOSI, MISO), spiPorts
403                 )
404             )
405
406     def configure(self, baudrate=100000, polarity=0, phase=0, bits=8):
407         """Update the configuration"""
408         if detector.board.any_nanopi and detector.chip.id == ap_chip.SUN8I:
409             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
410         elif detector.board.ftdi_ft232h:
411             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import (
412                 SPI as _SPI,
413             )
414         elif detector.board.ftdi_ft2232h:
415             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import (
416                 SPI as _SPI,
417             )
418         elif detector.board.ftdi_ft4232h:
419             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import (
420                 SPI as _SPI,
421             )
422         elif detector.board.binho_nova:
423             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
424         elif detector.board.greatfet_one:
425             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
426         elif detector.board.any_lubancat and detector.chip.id == ap_chip.IMX6ULL:
427             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
428         elif detector.board.pico_u2if:
429             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Pico as _SPI
430         elif detector.board.feather_u2if:
431             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
432                 SPI_Feather as _SPI,
433             )
434         elif detector.board.feather_can_u2if:
435             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
436                 SPI_Feather_CAN as _SPI,
437             )
438         elif detector.board.feather_epd_u2if:
439             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
440                 SPI_Feather_EPD as _SPI,
441             )
442         elif detector.board.feather_rfm_u2if:
443             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
444                 SPI_Feather_RFM as _SPI,
445             )
446         elif detector.board.itsybitsy_u2if:
447             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
448                 SPI_ItsyBitsy as _SPI,
449             )
450         elif detector.board.macropad_u2if:
451             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
452                 SPI_MacroPad as _SPI,
453             )
454         elif detector.board.kb2040_u2if:
455             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
456                 SPI_KB2040 as _SPI,
457             )
458         elif detector.board.qtpy_u2if:
459             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_QTPY as _SPI
460         elif detector.board.radxa_x4_u2if:
461             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
462                 SPI_Radxa_X4 as _SPI,
463             )
464         elif detector.chip.id == ap_chip.RP2040:
465             from adafruit_blinka.microcontroller.rp2040.spi import SPI as _SPI
466         elif detector.board.any_siemens_iot2000:
467             from adafruit_blinka.microcontroller.am65xx.spi import SPI as _SPI
468             from adafruit_blinka.microcontroller.am65xx.pin import Pin
469         elif detector.board.any_embedded_linux:
470             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
471         elif detector.board.OS_AGNOSTIC_BOARD:
472             from adafruit_blinka.microcontroller.generic_agnostic_board.spi import (
473                 SPI as _SPI,
474             )
475         else:
476             from adafruit_blinka.microcontroller.generic_micropython.spi import (
477                 SPI as _SPI,
478             )
479
480         if self._locked:
481             # TODO check if #init ignores MOSI=None rather than unsetting, to save _pinIds attribute
482             self._spi.init(
483                 baudrate=baudrate,
484                 polarity=polarity,
485                 phase=phase,
486                 bits=bits,
487                 firstbit=_SPI.MSB,
488             )
489         else:
490             raise RuntimeError("First call try_lock()")
491
492     def deinit(self):
493         """Deinitialization"""
494         self._spi = None
495         self._pinIds = None
496
497     @property
498     def frequency(self):
499         """Return the baud rate if implemented"""
500         try:
501             return self._spi.frequency
502         except AttributeError as error:
503             raise NotImplementedError(
504                 "Frequency attribute not implemented for this platform"
505             ) from error
506
507     def write(self, buf, start=0, end=None):
508         """Write to the SPI device"""
509         return self._spi.write(buf, start, end)
510
511     def readinto(self, buf, start=0, end=None, write_value=0):
512         """Read from the SPI device into a buffer"""
513         return self._spi.readinto(buf, start, end, write_value=write_value)
514
515     def write_readinto(
516         self, buffer_out, buffer_in, out_start=0, out_end=None, in_start=0, in_end=None
517     ):
518         """Write to the SPI device and read from the SPI device into a buffer"""
519         return self._spi.write_readinto(
520             buffer_out, buffer_in, out_start, out_end, in_start, in_end
521         )
522
523
524 class UART(Lockable):
525     """
526     Busio UART Class for CircuitPython Compatibility. Used
527     for MicroPython and a few other non-Linux boards.
528     """
529
530     class Parity(Enum):
531         """Parity Enumeration"""
532
533         pass  # pylint: disable=unnecessary-pass
534
535     Parity.ODD = Parity()
536     Parity.EVEN = Parity()
537
538     def __init__(
539         self,
540         tx,
541         rx,
542         baudrate=9600,
543         bits=8,
544         parity=None,
545         stop=1,
546         timeout=1000,
547         receiver_buffer_size=64,
548         flow=None,
549     ):
550         if detector.board.any_embedded_linux:
551             raise RuntimeError(
552                 "busio.UART not supported on this platform. Please use pyserial instead."
553             )
554         if detector.board.binho_nova:
555             from adafruit_blinka.microcontroller.nova.uart import UART as _UART
556         elif detector.board.greatfet_one:
557             from adafruit_blinka.microcontroller.nxp_lpc4330.uart import UART as _UART
558         elif detector.chip.id == ap_chip.RP2040:
559             from adafruit_blinka.microcontroller.rp2040.uart import UART as _UART
560         else:
561             from machine import UART as _UART
562
563         from microcontroller.pin import uartPorts
564
565         self.baudrate = baudrate
566
567         if flow is not None:  # default 0
568             raise NotImplementedError(
569                 "Parameter '{}' unsupported on {}".format("flow", agnostic.board_id)
570             )
571
572         # translate parity flag for Micropython
573         if parity is UART.Parity.ODD:
574             parity = 1
575         elif parity is UART.Parity.EVEN:
576             parity = 0
577         elif parity is None:
578             pass
579         else:
580             raise ValueError("Invalid parity")
581
582         if detector.chip.id == ap_chip.RP2040:
583             self._uart = _UART(
584                 tx,
585                 rx,
586                 baudrate=baudrate,
587                 bits=bits,
588                 parity=parity,
589                 stop=stop,
590             )
591         else:
592             # check tx and rx have hardware support
593             for portId, portTx, portRx in uartPorts:  #
594                 if portTx == tx and portRx == rx:
595                     self._uart = _UART(
596                         portId,
597                         baudrate,
598                         bits=bits,
599                         parity=parity,
600                         stop=stop,
601                         timeout=timeout,
602                         read_buf_len=receiver_buffer_size,
603                     )
604                     break
605             else:
606                 raise ValueError(
607                     "No Hardware UART on (tx,rx)={}\nValid UART ports: {}".format(
608                         (tx, rx), uartPorts
609                     )
610                 )
611
612     def deinit(self):
613         """Deinitialization"""
614         if detector.board.binho_nova:
615             self._uart.deinit()
616         self._uart = None
617
618     def read(self, nbytes=None):
619         """Read from the UART"""
620         return self._uart.read(nbytes)
621
622     def readinto(self, buf, nbytes=None):
623         """Read from the UART into a buffer"""
624         return self._uart.readinto(buf, nbytes)
625
626     def readline(self):
627         """Read a line of characters up to a newline character from the UART"""
628         return self._uart.readline()
629
630     def write(self, buf):
631         """Write to the UART from a buffer"""
632         return self._uart.write(buf)