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