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