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