aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/shared-bindings/busio
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/shared-bindings/busio')
-rw-r--r--circuitpython/shared-bindings/busio/I2C.c372
-rw-r--r--circuitpython/shared-bindings/busio/I2C.h78
-rw-r--r--circuitpython/shared-bindings/busio/SPI.c463
-rw-r--r--circuitpython/shared-bindings/busio/SPI.h75
-rw-r--r--circuitpython/shared-bindings/busio/UART.c462
-rw-r--r--circuitpython/shared-bindings/busio/UART.h73
-rw-r--r--circuitpython/shared-bindings/busio/__init__.c103
-rw-r--r--circuitpython/shared-bindings/busio/__init__.h34
8 files changed, 1660 insertions, 0 deletions
diff --git a/circuitpython/shared-bindings/busio/I2C.c b/circuitpython/shared-bindings/busio/I2C.c
new file mode 100644
index 0000000..2d67281
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/I2C.c
@@ -0,0 +1,372 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// This file contains all of the Python API definitions for the
+// busio.I2C class.
+
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/busio/I2C.h"
+#include "shared-bindings/util.h"
+
+#include "shared/runtime/buffer_helper.h"
+#include "shared/runtime/context_manager_helpers.h"
+#include "py/runtime.h"
+#include "supervisor/shared/translate.h"
+
+//| class I2C:
+//| """Two wire serial protocol"""
+//|
+//| def __init__(self, scl: microcontroller.Pin, sda: microcontroller.Pin, *, frequency: int = 100000, timeout: int = 255) -> None:
+//|
+//| """I2C is a two-wire protocol for communicating between devices. At the
+//| physical level it consists of 2 wires: SCL and SDA, the clock and data
+//| lines respectively.
+//|
+//| .. seealso:: Using this class directly requires careful lock management.
+//| Instead, use :class:`~adafruit_bus_device.I2CDevice` to
+//| manage locks.
+//|
+//| .. seealso:: Using this class to directly read registers requires manual
+//| bit unpacking. Instead, use an existing driver or make one with
+//| :ref:`Register <register-module-reference>` data descriptors.
+//|
+//| :param ~microcontroller.Pin scl: The clock pin
+//| :param ~microcontroller.Pin sda: The data pin
+//| :param int frequency: The clock frequency in Hertz
+//| :param int timeout: The maximum clock stretching timeut - (used only for
+//| :class:`bitbangio.I2C`; ignored for :class:`busio.I2C`)
+//| """
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+ busio_i2c_obj_t *self = m_new_obj(busio_i2c_obj_t);
+ self->base.type = &busio_i2c_type;
+ enum { ARG_scl, ARG_sda, ARG_frequency, ARG_timeout };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ },
+ { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ },
+ { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100000} },
+ { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 255} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ const mcu_pin_obj_t *scl = validate_obj_is_free_pin(args[ARG_scl].u_obj);
+ const mcu_pin_obj_t *sda = validate_obj_is_free_pin(args[ARG_sda].u_obj);
+
+ common_hal_busio_i2c_construct(self, scl, sda, args[ARG_frequency].u_int, args[ARG_timeout].u_int);
+ return (mp_obj_t)self;
+}
+
+//| def deinit(self) -> None:
+//| """Releases control of the underlying hardware so other classes can use it."""
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_obj_deinit(mp_obj_t self_in) {
+ busio_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_busio_i2c_deinit(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_i2c_deinit_obj, busio_i2c_obj_deinit);
+
+STATIC void check_for_deinit(busio_i2c_obj_t *self) {
+ if (common_hal_busio_i2c_deinited(self)) {
+ raise_deinited_error();
+ }
+}
+
+//| def __enter__(self) -> I2C:
+//| """No-op used in Context Managers."""
+//| ...
+//|
+// Provided by context manager helper.
+
+//| def __exit__(self) -> None:
+//| """Automatically deinitializes the hardware on context exit. See
+//| :ref:`lifetime-and-contextmanagers` for more info."""
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_obj___exit__(size_t n_args, const mp_obj_t *args) {
+ (void)n_args;
+ common_hal_busio_i2c_deinit(MP_OBJ_TO_PTR(args[0]));
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(busio_i2c___exit___obj, 4, 4, busio_i2c_obj___exit__);
+
+static void check_lock(busio_i2c_obj_t *self) {
+ asm ("");
+ if (!common_hal_busio_i2c_has_lock(self)) {
+ mp_raise_RuntimeError(translate("Function requires lock"));
+ }
+}
+
+//| def scan(self) -> List[int]:
+//| """Scan all I2C addresses between 0x08 and 0x77 inclusive and return a
+//| list of those that respond.
+//|
+//| :return: List of device ids on the I2C bus
+//| :rtype: list"""
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_scan(mp_obj_t self_in) {
+ busio_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ check_lock(self);
+ mp_obj_t list = mp_obj_new_list(0, NULL);
+ // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved
+ for (int addr = 0x08; addr < 0x78; ++addr) {
+ bool success = common_hal_busio_i2c_probe(self, addr);
+ if (success) {
+ mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr));
+ }
+ }
+ return list;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_i2c_scan_obj, busio_i2c_scan);
+
+//| def try_lock(self) -> bool:
+//| """Attempts to grab the I2C lock. Returns True on success.
+//|
+//| :return: True when lock has been grabbed
+//| :rtype: bool"""
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_obj_try_lock(mp_obj_t self_in) {
+ busio_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ return mp_obj_new_bool(common_hal_busio_i2c_try_lock(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_i2c_try_lock_obj, busio_i2c_obj_try_lock);
+
+//| def unlock(self) -> None:
+//| """Releases the I2C lock."""
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_obj_unlock(mp_obj_t self_in) {
+ busio_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ common_hal_busio_i2c_unlock(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_i2c_unlock_obj, busio_i2c_obj_unlock);
+
+//| import sys
+//| def readfrom_into(self, address: int, buffer: WriteableBuffer, *, start: int = 0, end: int = sys.maxsize) -> None:
+//| """Read into ``buffer`` from the device selected by ``address``.
+//| At least one byte must be read.
+//|
+//| If ``start`` or ``end`` is provided, then the buffer will be sliced
+//| as if ``buffer[start:end]`` were passed, but without copying the data.
+//| The number of bytes read will be the length of ``buffer[start:end]``.
+//|
+//| :param int address: 7-bit device address
+//| :param WriteableBuffer buffer: buffer to write into
+//| :param int start: beginning of buffer slice
+//| :param int end: end of buffer slice; if not specified, use ``len(buffer)``"""
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_readfrom_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_address, ARG_buffer, ARG_start, ARG_end };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ };
+ busio_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ check_lock(self);
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE);
+
+ size_t length = bufinfo.len;
+ int32_t start = args[ARG_start].u_int;
+ const int32_t end = args[ARG_end].u_int;
+ normalize_buffer_bounds(&start, end, &length);
+ if (length == 0) {
+ mp_raise_ValueError_varg(translate("%q length must be >= 1"), MP_QSTR_buffer);
+ }
+
+ uint8_t status =
+ common_hal_busio_i2c_read(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length);
+ if (status != 0) {
+ mp_raise_OSError(status);
+ }
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(busio_i2c_readfrom_into_obj, 1, busio_i2c_readfrom_into);
+
+//| import sys
+//| def writeto(self, address: int, buffer: ReadableBuffer, *, start: int = 0, end: int = sys.maxsize) -> None:
+//| """Write the bytes from ``buffer`` to the device selected by ``address`` and
+//| then transmit a stop bit.
+//|
+//| If ``start`` or ``end`` is provided, then the buffer will be sliced
+//| as if ``buffer[start:end]`` were passed, but without copying the data.
+//| The number of bytes written will be the length of ``buffer[start:end]``.
+//|
+//| Writing a buffer or slice of length zero is permitted, as it can be used
+//| to poll for the existence of a device.
+//|
+//| :param int address: 7-bit device address
+//| :param ReadableBuffer buffer: buffer containing the bytes to write
+//| :param int start: beginning of buffer slice
+//| :param int end: end of buffer slice; if not specified, use ``len(buffer)``
+//| """
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_writeto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_address, ARG_buffer, ARG_start, ARG_end };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ };
+ busio_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ check_lock(self);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // get the buffer to write the data from
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
+
+ size_t length = bufinfo.len;
+ int32_t start = args[ARG_start].u_int;
+ const int32_t end = args[ARG_end].u_int;
+ normalize_buffer_bounds(&start, end, &length);
+
+ // do the transfer
+ uint8_t status =
+ common_hal_busio_i2c_write(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length);
+
+ if (status != 0) {
+ mp_raise_OSError(status);
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(busio_i2c_writeto_obj, 1, busio_i2c_writeto);
+
+//| import sys
+//| def writeto_then_readfrom(self, address: int, out_buffer: ReadableBuffer, in_buffer: WriteableBuffer, *, out_start: int = 0, out_end: int = sys.maxsize, in_start: int = 0, in_end: int = sys.maxsize) -> None:
+//| """Write the bytes from ``out_buffer`` to the device selected by ``address``, generate no stop
+//| bit, generate a repeated start and read into ``in_buffer``. ``out_buffer`` and
+//| ``in_buffer`` can be the same buffer because they are used sequentially.
+//|
+//| If ``out_start`` or ``out_end`` is provided, then the buffer will be sliced
+//| as if ``out_buffer[out_start:out_end]`` were passed, but without copying the data.
+//| The number of bytes written will be the length of ``out_buffer[start:end]``.
+//|
+//| If ``in_start`` or ``in_end`` is provided, then the input buffer will be sliced
+//| as if ``in_buffer[in_start:in_end]`` were passed,
+//| The number of bytes read will be the length of ``out_buffer[in_start:in_end]``.
+
+//| :param int address: 7-bit device address
+//| :param ~circuitpython_typing.ReadableBuffer out_buffer: buffer containing the bytes to write
+//| :param ~circuitpython_typing.WriteableBuffer in_buffer: buffer to write into
+//| :param int out_start: beginning of ``out_buffer`` slice
+//| :param int out_end: end of ``out_buffer`` slice; if not specified, use ``len(out_buffer)``
+//| :param int in_start: beginning of ``in_buffer`` slice
+//| :param int in_end: end of ``in_buffer slice``; if not specified, use ``len(in_buffer)``
+//| """
+//| ...
+//|
+STATIC mp_obj_t busio_i2c_writeto_then_readfrom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_address, ARG_out_buffer, ARG_in_buffer, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_out_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_in_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ { MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ };
+ busio_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ check_lock(self);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_buffer_info_t out_bufinfo;
+ mp_get_buffer_raise(args[ARG_out_buffer].u_obj, &out_bufinfo, MP_BUFFER_READ);
+
+ size_t out_length = out_bufinfo.len;
+ int32_t out_start = args[ARG_out_start].u_int;
+ const int32_t out_end = args[ARG_out_end].u_int;
+ normalize_buffer_bounds(&out_start, out_end, &out_length);
+
+ mp_buffer_info_t in_bufinfo;
+ mp_get_buffer_raise(args[ARG_in_buffer].u_obj, &in_bufinfo, MP_BUFFER_WRITE);
+
+ size_t in_length = in_bufinfo.len;
+ int32_t in_start = args[ARG_in_start].u_int;
+ const int32_t in_end = args[ARG_in_end].u_int;
+ normalize_buffer_bounds(&in_start, in_end, &in_length);
+ if (in_length == 0) {
+ mp_raise_ValueError_varg(translate("%q length must be >= 1"), MP_QSTR_out_buffer);
+ }
+
+ uint8_t status = common_hal_busio_i2c_write_read(self, args[ARG_address].u_int,
+ ((uint8_t *)out_bufinfo.buf) + out_start, out_length,((uint8_t *)in_bufinfo.buf) + in_start, in_length);
+ if (status != 0) {
+ mp_raise_OSError(status);
+ }
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(busio_i2c_writeto_then_readfrom_obj, 1, busio_i2c_writeto_then_readfrom);
+
+STATIC const mp_rom_map_elem_t busio_i2c_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_i2c_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
+ { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&busio_i2c___exit___obj) },
+ { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&busio_i2c_scan_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_try_lock), MP_ROM_PTR(&busio_i2c_try_lock_obj) },
+ { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&busio_i2c_unlock_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&busio_i2c_readfrom_into_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&busio_i2c_writeto_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeto_then_readfrom), MP_ROM_PTR(&busio_i2c_writeto_then_readfrom_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(busio_i2c_locals_dict, busio_i2c_locals_dict_table);
+
+const mp_obj_type_t busio_i2c_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_I2C,
+ .make_new = busio_i2c_make_new,
+ .locals_dict = (mp_obj_dict_t *)&busio_i2c_locals_dict,
+};
diff --git a/circuitpython/shared-bindings/busio/I2C.h b/circuitpython/shared-bindings/busio/I2C.h
new file mode 100644
index 0000000..0999865
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/I2C.h
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Machine is the HAL for low-level, hardware accelerated functions. It is not
+// meant to simplify APIs, its only meant to unify them so that other modules
+// do not require port specific logic.
+//
+// This file includes externs for all functions a port should implement to
+// support the machine module.
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_I2C_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_I2C_H
+
+#include "py/obj.h"
+
+#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/busio/I2C.h"
+
+// Type object used in Python. Should be shared between ports.
+extern const mp_obj_type_t busio_i2c_type;
+
+// Initializes the hardware peripheral.
+extern void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
+ const mcu_pin_obj_t *scl,
+ const mcu_pin_obj_t *sda,
+ uint32_t frequency,
+ uint32_t timeout);
+
+extern void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self);
+extern bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self);
+
+extern bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self);
+extern bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self);
+extern void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self);
+
+// Probe the bus to see if a device acknowledges the given address.
+extern bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr);
+
+// Write to the device and return 0 on success or an appropriate error code from mperrno.h
+extern uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address,
+ const uint8_t *data, size_t len);
+
+// Reads memory of the i2c device picking up where it left off and return 0 on
+// success or an appropriate error code from mperrno.h
+extern uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address,
+ uint8_t *data, size_t len);
+
+// Do a write and then a read in the same I2C transaction.
+uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t address,
+ uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len);
+
+// This is used by the supervisor to claim I2C devices indefinitely.
+extern void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_I2C_H
diff --git a/circuitpython/shared-bindings/busio/SPI.c b/circuitpython/shared-bindings/busio/SPI.c
new file mode 100644
index 0000000..0a6c32f
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/SPI.c
@@ -0,0 +1,463 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// This file contains all of the Python API definitions for the
+// busio.SPI class.
+
+#include <string.h>
+
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/util.h"
+
+#include "shared/runtime/buffer_helper.h"
+#include "shared/runtime/context_manager_helpers.h"
+#include "py/mperrno.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+#include "supervisor/shared/translate.h"
+
+
+//| class SPI:
+//| """A 3-4 wire serial protocol
+//|
+//| SPI is a serial protocol that has exclusive pins for data in and out of the
+//| main device. It is typically faster than :py:class:`~bitbangio.I2C` because a
+//| separate pin is used to select a device rather than a transmitted
+//| address. This class only manages three of the four SPI lines: `!clock`,
+//| `!MOSI`, `!MISO`. Its up to the client to manage the appropriate
+//| select line, often abbreviated `!CS` or `!SS`. (This is common because
+//| multiple secondaries can share the `!clock`, `!MOSI` and `!MISO` lines
+//| and therefore the hardware.)"""
+//|
+//| def __init__(self, clock: microcontroller.Pin, MOSI: Optional[microcontroller.Pin] = None, MISO: Optional[microcontroller.Pin] = None, half_duplex: bool = False) -> None:
+//|
+//| """Construct an SPI object on the given pins.
+//|
+//| .. note:: The SPI peripherals allocated in order of desirability, if possible,
+//| such as highest speed and not shared use first. For instance, on the nRF52840,
+//| there is a single 32MHz SPI peripheral, and multiple 8MHz peripherals,
+//| some of which may also be used for I2C. The 32MHz SPI peripheral is returned
+//| first, then the exclusive 8MHz SPI peripheral, and finally the shared 8MHz
+//| peripherals.
+//|
+//| .. seealso:: Using this class directly requires careful lock management.
+//| Instead, use :class:`~adafruit_bus_device.SPIDevice` to
+//| manage locks.
+//|
+//| .. seealso:: Using this class to directly read registers requires manual
+//| bit unpacking. Instead, use an existing driver or make one with
+//| :ref:`Register <register-module-reference>` data descriptors.
+//|
+//| :param ~microcontroller.Pin clock: the pin to use for the clock.
+//| :param ~microcontroller.Pin MOSI: the Main Out Selected In pin.
+//| :param ~microcontroller.Pin MISO: the Main In Selected Out pin.
+//| :param bool half_duplex: True when MOSI is used for bidirectional data. False when SPI is full-duplex or simplex."""
+//| ...
+//|
+
+
+// TODO(tannewt): Support LSB SPI.
+STATIC mp_obj_t busio_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+ #if CIRCUITPY_BUSIO_SPI
+ busio_spi_obj_t *self = m_new_obj(busio_spi_obj_t);
+ self->base.type = &busio_spi_type;
+ enum { ARG_clock, ARG_MOSI, ARG_MISO, ARG_half_duplex };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_OBJ },
+ { MP_QSTR_MOSI, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_MISO, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_half_duplex, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_bool = false} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj);
+ const mcu_pin_obj_t *mosi = validate_obj_is_free_pin_or_none(args[ARG_MOSI].u_obj);
+ const mcu_pin_obj_t *miso = validate_obj_is_free_pin_or_none(args[ARG_MISO].u_obj);
+
+ if (!miso && !mosi) {
+ mp_raise_ValueError(translate("Must provide MISO or MOSI pin"));
+ }
+
+ common_hal_busio_spi_construct(self, clock, mosi, miso, args[ARG_half_duplex].u_bool);
+ return MP_OBJ_FROM_PTR(self);
+ #else
+ mp_raise_ValueError(translate("Invalid pins"));
+ #endif // CIRCUITPY_BUSIO_SPI
+}
+
+#if CIRCUITPY_BUSIO_SPI
+//| def deinit(self) -> None:
+//| """Turn off the SPI bus."""
+//| ...
+//|
+STATIC mp_obj_t busio_spi_obj_deinit(mp_obj_t self_in) {
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ common_hal_busio_spi_deinit(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_deinit_obj, busio_spi_obj_deinit);
+
+//| def __enter__(self) -> SPI:
+//| """No-op used by Context Managers.
+//| Provided by context manager helper."""
+//| ...
+//|
+
+//| def __exit__(self) -> None:
+//| """Automatically deinitializes the hardware when exiting a context. See
+//| :ref:`lifetime-and-contextmanagers` for more info."""
+//| ...
+//|
+STATIC mp_obj_t busio_spi_obj___exit__(size_t n_args, const mp_obj_t *args) {
+ (void)n_args;
+ common_hal_busio_spi_deinit(MP_OBJ_TO_PTR(args[0]));
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(busio_spi_obj___exit___obj, 4, 4, busio_spi_obj___exit__);
+
+STATIC void check_lock(busio_spi_obj_t *self) {
+ asm ("");
+ if (!common_hal_busio_spi_has_lock(self)) {
+ mp_raise_RuntimeError(translate("Function requires lock"));
+ }
+}
+
+STATIC void check_for_deinit(busio_spi_obj_t *self) {
+ if (common_hal_busio_spi_deinited(self)) {
+ raise_deinited_error();
+ }
+}
+
+//| def configure(self, *, baudrate: int = 100000, polarity: int = 0, phase: int = 0, bits: int = 8) -> None:
+//| """Configures the SPI bus. The SPI object must be locked.
+//|
+//| :param int baudrate: the desired clock rate in Hertz. The actual clock rate may be higher or lower
+//| due to the granularity of available clock settings.
+//| Check the `frequency` attribute for the actual clock rate.
+//| :param int polarity: the base state of the clock line (0 or 1)
+//| :param int phase: the edge of the clock that data is captured. First (0)
+//| or second (1). Rising or falling depends on clock polarity.
+//| :param int bits: the number of bits per word
+//|
+//| .. note:: On the SAMD21, it is possible to set the baudrate to 24 MHz, but that
+//| speed is not guaranteed to work. 12 MHz is the next available lower speed, and is
+//| within spec for the SAMD21.
+//|
+//| .. note:: On the nRF52840, these baudrates are available: 125kHz, 250kHz, 1MHz, 2MHz, 4MHz,
+//| and 8MHz.
+//| If you pick a a baudrate other than one of these, the nearest lower
+//| baudrate will be chosen, with a minimum of 125kHz.
+//| Two SPI objects may be created, except on the Circuit Playground Bluefruit,
+//| which allows only one (to allow for an additional I2C object)."""
+//| ...
+//|
+
+STATIC mp_obj_t busio_spi_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100000} },
+ { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
+ };
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ check_lock(self);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ uint8_t polarity = args[ARG_polarity].u_int;
+ if (polarity != 0 && polarity != 1) {
+ mp_raise_ValueError(translate("Invalid polarity"));
+ }
+ uint8_t phase = args[ARG_phase].u_int;
+ if (phase != 0 && phase != 1) {
+ mp_raise_ValueError(translate("Invalid phase"));
+ }
+ uint8_t bits = args[ARG_bits].u_int;
+ if (bits != 8 && bits != 9) {
+ mp_raise_ValueError(translate("Invalid number of bits"));
+ }
+
+ if (!common_hal_busio_spi_configure(self, args[ARG_baudrate].u_int,
+ polarity, phase, bits)) {
+ mp_raise_OSError(MP_EIO);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_configure_obj, 1, busio_spi_configure);
+
+//| def try_lock(self) -> bool:
+//| """Attempts to grab the SPI lock. Returns True on success.
+//|
+//| :return: True when lock has been grabbed
+//| :rtype: bool"""
+//| ...
+//|
+
+STATIC mp_obj_t busio_spi_obj_try_lock(mp_obj_t self_in) {
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ return mp_obj_new_bool(common_hal_busio_spi_try_lock(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_try_lock_obj, busio_spi_obj_try_lock);
+
+//| def unlock(self) -> None:
+//| """Releases the SPI lock."""
+//| ...
+//|
+
+STATIC mp_obj_t busio_spi_obj_unlock(mp_obj_t self_in) {
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ common_hal_busio_spi_unlock(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_unlock_obj, busio_spi_obj_unlock);
+
+//| import sys
+//| def write(self, buffer: ReadableBuffer, *, start: int = 0, end: int = sys.maxsize) -> None:
+//| """Write the data contained in ``buffer``. The SPI object must be locked.
+//| If the buffer is empty, nothing happens.
+//|
+//| If ``start`` or ``end`` is provided, then the buffer will be sliced
+//| as if ``buffer[start:end]`` were passed, but without copying the data.
+//| The number of bytes written will be the length of ``buffer[start:end]``.
+//|
+//| :param ReadableBuffer buffer: write out bytes from this buffer
+//| :param int start: beginning of buffer slice
+//| :param int end: end of buffer slice; if not specified, use ``len(buffer)``
+//| """
+//| ...
+//|
+
+STATIC mp_obj_t busio_spi_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_buffer, ARG_start, ARG_end };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ };
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ check_lock(self);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
+ int32_t start = args[ARG_start].u_int;
+ size_t length = bufinfo.len;
+ normalize_buffer_bounds(&start, args[ARG_end].u_int, &length);
+
+ if (length == 0) {
+ return mp_const_none;
+ }
+
+ bool ok = common_hal_busio_spi_write(self, ((uint8_t *)bufinfo.buf) + start, length);
+ if (!ok) {
+ mp_raise_OSError(MP_EIO);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_write_obj, 1, busio_spi_write);
+
+
+//| import sys
+//| def readinto(self, buffer: WriteableBuffer, *, start: int = 0, end: int = sys.maxsize, write_value: int = 0) -> None:
+//| """Read into ``buffer`` while writing ``write_value`` for each byte read.
+//| The SPI object must be locked.
+//| If the number of bytes to read is 0, nothing happens.
+//|
+//| If ``start`` or ``end`` is provided, then the buffer will be sliced
+//| as if ``buffer[start:end]`` were passed.
+//| The number of bytes read will be the length of ``buffer[start:end]``.
+//|
+//| :param WriteableBuffer buffer: read bytes into this buffer
+//| :param int start: beginning of buffer slice
+//| :param int end: end of buffer slice; if not specified, it will be the equivalent value
+//| of ``len(buffer)`` and for any value provided it will take the value of
+//| ``min(end, len(buffer))``
+//| :param int write_value: value to write while reading
+//| """
+//| ...
+//|
+
+STATIC mp_obj_t busio_spi_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_buffer, ARG_start, ARG_end, ARG_write_value };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ { MP_QSTR_write_value,MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ };
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ check_lock(self);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE);
+ int32_t start = args[ARG_start].u_int;
+ size_t length = bufinfo.len;
+ normalize_buffer_bounds(&start, args[ARG_end].u_int, &length);
+
+ if (length == 0) {
+ return mp_const_none;
+ }
+
+ bool ok = common_hal_busio_spi_read(self, ((uint8_t *)bufinfo.buf) + start, length, args[ARG_write_value].u_int);
+ if (!ok) {
+ mp_raise_OSError(MP_EIO);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_readinto_obj, 1, busio_spi_readinto);
+
+//| import sys
+//| def write_readinto(self, out_buffer: ReadableBuffer, in_buffer: WriteableBuffer, *, out_start: int = 0, out_end: int = sys.maxsize, in_start: int = 0, in_end: int = sys.maxsize) -> None:
+//| """Write out the data in ``out_buffer`` while simultaneously reading data into ``in_buffer``.
+//| The SPI object must be locked.
+//|
+//| If ``out_start`` or ``out_end`` is provided, then the buffer will be sliced
+//| as if ``out_buffer[out_start:out_end]`` were passed, but without copying the data.
+//| The number of bytes written will be the length of ``out_buffer[out_start:out_end]``.
+//|
+//| If ``in_start`` or ``in_end`` is provided, then the input buffer will be sliced
+//| as if ``in_buffer[in_start:in_end]`` were passed,
+//| The number of bytes read will be the length of ``out_buffer[in_start:in_end]``.
+//|
+//| The lengths of the slices defined by ``out_buffer[out_start:out_end]``
+//| and ``in_buffer[in_start:in_end]`` must be equal.
+//| If buffer slice lengths are both 0, nothing happens.
+//|
+//| :param ReadableBuffer out_buffer: write out bytes from this buffer
+//| :param WriteableBuffer in_buffer: read bytes into this buffer
+//| :param int out_start: beginning of ``out_buffer`` slice
+//| :param int out_end: end of ``out_buffer`` slice; if not specified, use ``len(out_buffer)``
+//| :param int in_start: beginning of ``in_buffer`` slice
+//| :param int in_end: end of ``in_buffer slice``; if not specified, use ``len(in_buffer)``
+//| """
+//| ...
+//|
+
+STATIC mp_obj_t busio_spi_write_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_out_buffer, ARG_in_buffer, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_out_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_in_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ { MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} },
+ };
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ check_for_deinit(self);
+ check_lock(self);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_buffer_info_t buf_out_info;
+ mp_get_buffer_raise(args[ARG_out_buffer].u_obj, &buf_out_info, MP_BUFFER_READ);
+ int32_t out_start = args[ARG_out_start].u_int;
+ size_t out_length = buf_out_info.len;
+ normalize_buffer_bounds(&out_start, args[ARG_out_end].u_int, &out_length);
+
+ mp_buffer_info_t buf_in_info;
+ mp_get_buffer_raise(args[ARG_in_buffer].u_obj, &buf_in_info, MP_BUFFER_WRITE);
+ int32_t in_start = args[ARG_in_start].u_int;
+ size_t in_length = buf_in_info.len;
+ normalize_buffer_bounds(&in_start, args[ARG_in_end].u_int, &in_length);
+
+ if (out_length != in_length) {
+ mp_raise_ValueError(translate("buffer slices must be of equal length"));
+ }
+
+ if (out_length == 0) {
+ return mp_const_none;
+ }
+
+ bool ok = common_hal_busio_spi_transfer(self,
+ ((uint8_t *)buf_out_info.buf) + out_start,
+ ((uint8_t *)buf_in_info.buf) + in_start,
+ out_length);
+ if (!ok) {
+ mp_raise_OSError(MP_EIO);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_write_readinto_obj, 1, busio_spi_write_readinto);
+
+//| frequency: int
+//| """The actual SPI bus frequency. This may not match the frequency requested
+//| due to internal limitations."""
+//|
+
+STATIC mp_obj_t busio_spi_obj_get_frequency(mp_obj_t self_in) {
+ busio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_busio_spi_get_frequency(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_get_frequency_obj, busio_spi_obj_get_frequency);
+
+MP_PROPERTY_GETTER(busio_spi_frequency_obj,
+ (mp_obj_t)&busio_spi_get_frequency_obj);
+#endif // CIRCUITPY_BUSIO_SPI
+
+
+STATIC const mp_rom_map_elem_t busio_spi_locals_dict_table[] = {
+ #if CIRCUITPY_BUSIO_SPI
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_spi_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
+ { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&busio_spi_obj___exit___obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&busio_spi_configure_obj) },
+ { MP_ROM_QSTR(MP_QSTR_try_lock), MP_ROM_PTR(&busio_spi_try_lock_obj) },
+ { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&busio_spi_unlock_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&busio_spi_readinto_obj) },
+ { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&busio_spi_write_obj) },
+ { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&busio_spi_write_readinto_obj) },
+ { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&busio_spi_frequency_obj) }
+ #endif // CIRCUITPY_BUSIO_SPI
+};
+STATIC MP_DEFINE_CONST_DICT(busio_spi_locals_dict, busio_spi_locals_dict_table);
+
+const mp_obj_type_t busio_spi_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_SPI,
+ .make_new = busio_spi_make_new,
+ .locals_dict = (mp_obj_dict_t *)&busio_spi_locals_dict,
+};
+
+busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj) {
+ if (!mp_obj_is_type(obj, &busio_spi_type)) {
+ mp_raise_TypeError_varg(translate("Expected a %q"), busio_spi_type.name);
+ }
+ return MP_OBJ_TO_PTR(obj);
+}
diff --git a/circuitpython/shared-bindings/busio/SPI.h b/circuitpython/shared-bindings/busio/SPI.h
new file mode 100644
index 0000000..7616658
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/SPI.h
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H
+
+#include "py/obj.h"
+
+#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/busio/SPI.h"
+
+// Type object used in Python. Should be shared between ports.
+extern const mp_obj_type_t busio_spi_type;
+
+// Construct an underlying SPI object.
+extern void common_hal_busio_spi_construct(busio_spi_obj_t *self,
+ const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi,
+ const mcu_pin_obj_t *miso, bool half_duplex);
+
+extern void common_hal_busio_spi_deinit(busio_spi_obj_t *self);
+extern bool common_hal_busio_spi_deinited(busio_spi_obj_t *self);
+
+extern bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits);
+
+extern bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self);
+extern bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self);
+extern void common_hal_busio_spi_unlock(busio_spi_obj_t *self);
+
+// Writes out the given data.
+extern bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len);
+
+// Reads in len bytes while outputting the byte write_value.
+extern bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value);
+
+// Reads and write len bytes simultaneously.
+extern bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len);
+
+// Return actual SPI bus frequency.
+uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self);
+
+// Return SPI bus phase.
+uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self);
+
+// Return SPI bus polarity.
+uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self);
+
+// This is used by the supervisor to claim SPI devices indefinitely.
+extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self);
+
+extern busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj_in);
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H
diff --git a/circuitpython/shared-bindings/busio/UART.c b/circuitpython/shared-bindings/busio/UART.c
new file mode 100644
index 0000000..ae14e31
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/UART.c
@@ -0,0 +1,462 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+
+#include "shared-bindings/busio/UART.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/util.h"
+
+#include "shared/runtime/context_manager_helpers.h"
+#include "shared/runtime/interrupt_char.h"
+
+#include "py/ioctl.h"
+#include "py/objproperty.h"
+#include "py/objtype.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "supervisor/shared/translate.h"
+
+#define STREAM_DEBUG(...) (void)0
+// #define STREAM_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
+
+//| class UART:
+//| """A bidirectional serial protocol"""
+//| def __init__(self, tx: microcontroller.Pin, rx: microcontroller.Pin, *, baudrate: int = 9600, bits: int = 8, parity: Optional[Parity] = None, stop: int = 1, timeout: float = 1, receiver_buffer_size: int = 64) -> None:
+//| """A common bidirectional serial protocol that uses an an agreed upon speed
+//| rather than a shared clock line.
+//|
+//| :param ~microcontroller.Pin tx: the pin to transmit with, or ``None`` if this ``UART`` is receive-only.
+//| :param ~microcontroller.Pin rx: the pin to receive on, or ``None`` if this ``UART`` is transmit-only.
+//| :param ~microcontroller.Pin rts: the pin for rts, or ``None`` if rts not in use.
+//| :param ~microcontroller.Pin cts: the pin for cts, or ``None`` if cts not in use.
+//| :param ~microcontroller.Pin rs485_dir: the output pin for rs485 direction setting, or ``None`` if rs485 not in use.
+//| :param bool rs485_invert: rs485_dir pin active high when set. Active low otherwise.
+//| :param int baudrate: the transmit and receive speed.
+//| :param int bits: the number of bits per byte, 5 to 9.
+//| :param Parity parity: the parity used for error checking.
+//| :param int stop: the number of stop bits, 1 or 2.
+//| :param float timeout: the timeout in seconds to wait for the first character and between subsequent characters when reading. Raises ``ValueError`` if timeout >100 seconds.
+//| :param int receiver_buffer_size: the character length of the read buffer (0 to disable). (When a character is 9 bits the buffer will be 2 * receiver_buffer_size bytes.)
+//|
+//| *New in CircuitPython 4.0:* ``timeout`` has incompatibly changed units from milliseconds to seconds.
+//| The new upper limit on ``timeout`` is meant to catch mistaken use of milliseconds.
+//|
+//| .. note:: RS485 support on i.MX and Raspberry Pi RP2040 is implemented in software.
+//| The timing for the ``rs485_dir`` pin signal is done on a best-effort basis, and may not meet
+//| RS485 specifications intermittently.
+//| """
+//| ...
+//|
+typedef struct {
+ mp_obj_base_t base;
+} busio_uart_parity_obj_t;
+extern const busio_uart_parity_obj_t busio_uart_parity_even_obj;
+extern const busio_uart_parity_obj_t busio_uart_parity_odd_obj;
+
+#if CIRCUITPY_BUSIO_UART
+STATIC void validate_timeout(mp_float_t timeout) {
+ if (timeout < (mp_float_t)0.0f || timeout > (mp_float_t)100.0f) {
+ mp_raise_ValueError(translate("timeout must be 0.0-100.0 seconds"));
+ }
+}
+#endif // CIRCUITPY_BUSIO_UART
+
+STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+ #if CIRCUITPY_BUSIO_UART
+ enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_timeout, ARG_receiver_buffer_size,
+ ARG_rts, ARG_cts, ARG_rs485_dir,ARG_rs485_invert};
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_tx, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_rx, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 9600} },
+ { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
+ { MP_QSTR_parity, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_stop, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
+ { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} },
+ { MP_QSTR_receiver_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
+ { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_rs485_dir, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none } },
+ { MP_QSTR_rs485_invert, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false } },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ const mcu_pin_obj_t *rx = validate_obj_is_free_pin_or_none(args[ARG_rx].u_obj);
+ const mcu_pin_obj_t *tx = validate_obj_is_free_pin_or_none(args[ARG_tx].u_obj);
+
+ if ((tx == NULL) && (rx == NULL)) {
+ mp_raise_ValueError(translate("tx and rx cannot both be None"));
+ }
+
+ if (args[ARG_bits].u_int < 5 || args[ARG_bits].u_int > 9) {
+ mp_raise_ValueError(translate("bits must be in range 5 to 9"));
+ }
+ uint8_t bits = args[ARG_bits].u_int;
+
+ busio_uart_parity_t parity = BUSIO_UART_PARITY_NONE;
+ if (args[ARG_parity].u_obj == MP_OBJ_FROM_PTR(&busio_uart_parity_even_obj)) {
+ parity = BUSIO_UART_PARITY_EVEN;
+ } else if (args[ARG_parity].u_obj == MP_OBJ_FROM_PTR(&busio_uart_parity_odd_obj)) {
+ parity = BUSIO_UART_PARITY_ODD;
+ }
+
+ uint8_t stop = args[ARG_stop].u_int;
+ if (stop != 1 && stop != 2) {
+ mp_raise_ValueError(translate("stop must be 1 or 2"));
+ }
+
+ mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
+ validate_timeout(timeout);
+
+ const mcu_pin_obj_t *rts = validate_obj_is_free_pin_or_none(args[ARG_rts].u_obj);
+ const mcu_pin_obj_t *cts = validate_obj_is_free_pin_or_none(args[ARG_cts].u_obj);
+ const mcu_pin_obj_t *rs485_dir = validate_obj_is_free_pin_or_none(args[ARG_rs485_dir].u_obj);
+
+ const bool rs485_invert = args[ARG_rs485_invert].u_bool;
+
+ // Always initially allocate the UART object within the long-lived heap.
+ // This is needed to avoid crashes with certain UART implementations which
+ // cannot accomodate being moved after creation. (See
+ // https://github.com/adafruit/circuitpython/issues/1056)
+ busio_uart_obj_t *self = m_new_ll_obj_with_finaliser(busio_uart_obj_t);
+ self->base.type = &busio_uart_type;
+
+ common_hal_busio_uart_construct(self, tx, rx, rts, cts, rs485_dir, rs485_invert,
+ args[ARG_baudrate].u_int, bits, parity, stop, timeout,
+ args[ARG_receiver_buffer_size].u_int, NULL, false);
+ return (mp_obj_t)self;
+ #else
+ mp_raise_ValueError(translate("Invalid pins"));
+ #endif // CIRCUITPY_BUSIO_UART
+}
+
+#if CIRCUITPY_BUSIO_UART
+
+// Helper to ensure we have the native super class instead of a subclass.
+STATIC busio_uart_obj_t *native_uart(mp_obj_t uart_obj) {
+ mp_obj_t native_uart = mp_obj_cast_to_native_base(uart_obj, MP_OBJ_FROM_PTR(&busio_uart_type));
+ if (native_uart == MP_OBJ_NULL) {
+ mp_raise_ValueError_varg(translate("Must be a %q subclass."), MP_QSTR_UART);
+ }
+ mp_obj_assert_native_inited(native_uart);
+ return MP_OBJ_TO_PTR(native_uart);
+}
+
+
+//| def deinit(self) -> None:
+//| """Deinitialises the UART and releases any hardware resources for reuse."""
+//| ...
+//|
+STATIC mp_obj_t busio_uart_obj_deinit(mp_obj_t self_in) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ common_hal_busio_uart_deinit(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_deinit_obj, busio_uart_obj_deinit);
+
+STATIC void check_for_deinit(busio_uart_obj_t *self) {
+ if (common_hal_busio_uart_deinited(self)) {
+ raise_deinited_error();
+ }
+}
+
+//| def __enter__(self) -> UART:
+//| """No-op used by Context Managers."""
+//| ...
+//|
+// Provided by context manager helper.
+
+//| def __exit__(self) -> None:
+//| """Automatically deinitializes the hardware when exiting a context. See
+//| :ref:`lifetime-and-contextmanagers` for more info."""
+//| ...
+//|
+STATIC mp_obj_t busio_uart_obj___exit__(size_t n_args, const mp_obj_t *args) {
+ (void)n_args;
+ common_hal_busio_uart_deinit(MP_OBJ_TO_PTR(args[0]));
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(busio_uart___exit___obj, 4, 4, busio_uart_obj___exit__);
+
+// These are standard stream methods. Code is in py/stream.c.
+//
+//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]:
+//| """Read characters. If ``nbytes`` is specified then read at most that many
+//| bytes. Otherwise, read everything that arrives until the connection
+//| times out. Providing the number of bytes expected is highly recommended
+//| because it will be faster.
+//|
+//| :return: Data read
+//| :rtype: bytes or None"""
+//| ...
+//|
+
+//| def readinto(self, buf: WriteableBuffer) -> Optional[int]:
+//| """Read bytes into the ``buf``. Read at most ``len(buf)`` bytes.
+//|
+//| :return: number of bytes read and stored into ``buf``
+//| :rtype: int or None (on a non-blocking error)
+//|
+//| *New in CircuitPython 4.0:* No length parameter is permitted."""
+//| ...
+//|
+
+//| def readline(self) -> bytes:
+//| """Read a line, ending in a newline character, or
+//| return None if a timeout occurs sooner, or
+//| return everything readable if no newline is found and timeout=0
+//|
+//| :return: the line read
+//| :rtype: bytes or None"""
+//| ...
+//|
+
+//| def write(self, buf: WriteableBuffer) -> Optional[int]:
+//| """Write the buffer of bytes to the bus.
+//|
+//| *New in CircuitPython 4.0:* ``buf`` must be bytes, not a string.
+//|
+//| :return: the number of bytes written
+//| :rtype: int or None"""
+//| ...
+//|
+
+// These three methods are used by the shared stream methods.
+STATIC mp_uint_t busio_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
+ STREAM_DEBUG("busio_uart_read stream %d\n", size);
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ byte *buf = buf_in;
+
+ // make sure we want at least 1 char
+ if (size == 0) {
+ return 0;
+ }
+
+ return common_hal_busio_uart_read(self, buf, size, errcode);
+}
+
+STATIC mp_uint_t busio_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ const byte *buf = buf_in;
+
+ return common_hal_busio_uart_write(self, buf, size, errcode);
+}
+
+STATIC mp_uint_t busio_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ mp_uint_t ret;
+ if (request == MP_IOCTL_POLL) {
+ mp_uint_t flags = arg;
+ ret = 0;
+ if ((flags & MP_IOCTL_POLL_RD) && common_hal_busio_uart_rx_characters_available(self) > 0) {
+ ret |= MP_IOCTL_POLL_RD;
+ }
+ if ((flags & MP_IOCTL_POLL_WR) && common_hal_busio_uart_ready_to_tx(self)) {
+ ret |= MP_IOCTL_POLL_WR;
+ }
+ } else {
+ *errcode = MP_EINVAL;
+ ret = MP_STREAM_ERROR;
+ }
+ return ret;
+}
+
+//| baudrate: int
+//| """The current baudrate."""
+//|
+STATIC mp_obj_t busio_uart_obj_get_baudrate(mp_obj_t self_in) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_busio_uart_get_baudrate(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_get_baudrate_obj, busio_uart_obj_get_baudrate);
+
+STATIC mp_obj_t busio_uart_obj_set_baudrate(mp_obj_t self_in, mp_obj_t baudrate) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ common_hal_busio_uart_set_baudrate(self, mp_obj_get_int(baudrate));
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(busio_uart_set_baudrate_obj, busio_uart_obj_set_baudrate);
+
+
+MP_PROPERTY_GETSET(busio_uart_baudrate_obj,
+ (mp_obj_t)&busio_uart_get_baudrate_obj,
+ (mp_obj_t)&busio_uart_set_baudrate_obj);
+
+//| in_waiting: int
+//| """The number of bytes in the input buffer, available to be read"""
+//|
+STATIC mp_obj_t busio_uart_obj_get_in_waiting(mp_obj_t self_in) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ return MP_OBJ_NEW_SMALL_INT(common_hal_busio_uart_rx_characters_available(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_get_in_waiting_obj, busio_uart_obj_get_in_waiting);
+
+MP_PROPERTY_GETTER(busio_uart_in_waiting_obj,
+ (mp_obj_t)&busio_uart_get_in_waiting_obj);
+
+//| timeout: float
+//| """The current timeout, in seconds (float)."""
+//|
+STATIC mp_obj_t busio_uart_obj_get_timeout(mp_obj_t self_in) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ return mp_obj_new_float(common_hal_busio_uart_get_timeout(self));
+}
+MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_get_timeout_obj, busio_uart_obj_get_timeout);
+
+STATIC mp_obj_t busio_uart_obj_set_timeout(mp_obj_t self_in, mp_obj_t timeout) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ mp_float_t timeout_float = mp_obj_get_float(timeout);
+ validate_timeout(timeout_float);
+ common_hal_busio_uart_set_timeout(self, timeout_float);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(busio_uart_set_timeout_obj, busio_uart_obj_set_timeout);
+
+
+MP_PROPERTY_GETSET(busio_uart_timeout_obj,
+ (mp_obj_t)&busio_uart_get_timeout_obj,
+ (mp_obj_t)&busio_uart_set_timeout_obj);
+
+//| def reset_input_buffer(self) -> None:
+//| """Discard any unread characters in the input buffer."""
+//| ...
+//|
+STATIC mp_obj_t busio_uart_obj_reset_input_buffer(mp_obj_t self_in) {
+ busio_uart_obj_t *self = native_uart(self_in);
+ check_for_deinit(self);
+ common_hal_busio_uart_clear_rx_buffer(self);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_reset_input_buffer_obj, busio_uart_obj_reset_input_buffer);
+#endif // CIRCUITPY_BUSIO_UART
+
+//| class Parity:
+//| """Enum-like class to define the parity used to verify correct data transfer."""
+//|
+//| ODD: int
+//| """Total number of ones should be odd."""
+//|
+//| EVEN: int
+//| """Total number of ones should be even."""
+//|
+const mp_obj_type_t busio_uart_parity_type;
+
+const busio_uart_parity_obj_t busio_uart_parity_odd_obj = {
+ { &busio_uart_parity_type },
+};
+
+const busio_uart_parity_obj_t busio_uart_parity_even_obj = {
+ { &busio_uart_parity_type },
+};
+
+STATIC const mp_rom_map_elem_t busio_uart_parity_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_ODD), MP_ROM_PTR(&busio_uart_parity_odd_obj) },
+ { MP_ROM_QSTR(MP_QSTR_EVEN), MP_ROM_PTR(&busio_uart_parity_even_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(busio_uart_parity_locals_dict, busio_uart_parity_locals_dict_table);
+
+STATIC void busio_uart_parity_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+ qstr parity = MP_QSTR_ODD;
+ if (self_in == MP_ROM_PTR(&busio_uart_parity_even_obj)) {
+ parity = MP_QSTR_EVEN;
+ }
+ mp_printf(print, "%q.%q.%q.%q", MP_QSTR_busio, MP_QSTR_UART, MP_QSTR_Parity, parity);
+}
+
+const mp_obj_type_t busio_uart_parity_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_Parity,
+ .print = busio_uart_parity_print,
+ .locals_dict = (mp_obj_dict_t *)&busio_uart_parity_locals_dict,
+};
+
+STATIC const mp_rom_map_elem_t busio_uart_locals_dict_table[] = {
+ #if CIRCUITPY_BUSIO_UART
+ { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&busio_uart_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_uart_deinit_obj) },
+ { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
+ { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&busio_uart___exit___obj) },
+
+ // Standard stream methods.
+ { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)},
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&busio_uart_reset_input_buffer_obj) },
+
+ // Properties
+ { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&busio_uart_baudrate_obj) },
+ { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&busio_uart_in_waiting_obj) },
+ { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&busio_uart_timeout_obj) },
+ #endif // CIRCUITPY_BUSIO_UART
+
+ // Nested Enum-like Classes.
+ { MP_ROM_QSTR(MP_QSTR_Parity), MP_ROM_PTR(&busio_uart_parity_type) },
+};
+STATIC MP_DEFINE_CONST_DICT(busio_uart_locals_dict, busio_uart_locals_dict_table);
+
+#if CIRCUITPY_BUSIO_UART
+STATIC const mp_stream_p_t uart_stream_p = {
+ MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream)
+ .read = busio_uart_read,
+ .write = busio_uart_write,
+ .ioctl = busio_uart_ioctl,
+ .is_text = false,
+ // Disallow optional length argument for .readinto()
+ .pyserial_readinto_compatibility = true,
+};
+
+const mp_obj_type_t busio_uart_type = {
+ { &mp_type_type },
+ .flags = MP_TYPE_FLAG_EXTENDED,
+ .name = MP_QSTR_UART,
+ .make_new = busio_uart_make_new,
+ .locals_dict = (mp_obj_dict_t *)&busio_uart_locals_dict,
+ MP_TYPE_EXTENDED_FIELDS(
+ .getiter = mp_identity_getiter,
+ .iternext = mp_stream_unbuffered_iter,
+ .protocol = &uart_stream_p,
+ ),
+};
+#else
+const mp_obj_type_t busio_uart_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_UART,
+ .make_new = busio_uart_make_new,
+ .locals_dict = (mp_obj_dict_t *)&busio_uart_locals_dict,
+};
+#endif // CIRCUITPY_BUSIO_UART
diff --git a/circuitpython/shared-bindings/busio/UART.h b/circuitpython/shared-bindings/busio/UART.h
new file mode 100644
index 0000000..31b062a
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/UART.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_UART_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_UART_H
+
+#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/busio/UART.h"
+#include "py/ringbuf.h"
+
+extern const mp_obj_type_t busio_uart_type;
+
+typedef enum {
+ BUSIO_UART_PARITY_NONE,
+ BUSIO_UART_PARITY_EVEN,
+ BUSIO_UART_PARITY_ODD
+} busio_uart_parity_t;
+
+// Construct an underlying UART object.
+extern void common_hal_busio_uart_construct(busio_uart_obj_t *self,
+ const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx,
+ const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts,
+ const mcu_pin_obj_t *rs485_dir, bool rs485_invert,
+ uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop,
+ mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer,
+ bool sigint_enabled);
+
+extern void common_hal_busio_uart_deinit(busio_uart_obj_t *self);
+extern bool common_hal_busio_uart_deinited(busio_uart_obj_t *self);
+
+// Read characters. len is in characters NOT bytes!
+extern size_t common_hal_busio_uart_read(busio_uart_obj_t *self,
+ uint8_t *data, size_t len, int *errcode);
+
+// Write characters. len is in characters NOT bytes!
+extern size_t common_hal_busio_uart_write(busio_uart_obj_t *self,
+ const uint8_t *data, size_t len, int *errcode);
+
+extern uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self);
+extern void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate);
+extern mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self);
+extern void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout);
+
+extern uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self);
+extern void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self);
+extern bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self);
+
+extern void common_hal_busio_uart_never_reset(busio_uart_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_UART_H
diff --git a/circuitpython/shared-bindings/busio/__init__.c b/circuitpython/shared-bindings/busio/__init__.c
new file mode 100644
index 0000000..38bbfaf
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/__init__.c
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/busio/__init__.h"
+#include "shared-bindings/busio/I2C.h"
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/busio/UART.h"
+#if CIRCUITPY_ONEWIREIO
+#include "shared-bindings/onewireio/OneWire.h"
+#endif
+
+#include "py/runtime.h"
+
+//| """Hardware accelerated external bus access
+//|
+//| The `busio` module contains classes to support a variety of serial
+//| protocols.
+//|
+//| When the microcontroller does not support the behavior in a hardware
+//| accelerated fashion it may internally use a bitbang routine. However, if
+//| hardware support is available on a subset of pins but not those provided,
+//| then a RuntimeError will be raised. Use the `bitbangio` module to explicitly
+//| bitbang a serial protocol on any general purpose pins.
+//|
+//| All classes change hardware state and should be deinitialized when they
+//| are no longer needed if the program continues after use. To do so, either
+//| call :py:meth:`!deinit` or use a context manager. See
+//| :ref:`lifetime-and-contextmanagers` for more info.
+//|
+//| For example::
+//|
+//| import busio
+//| from board import *
+//|
+//| i2c = busio.I2C(SCL, SDA)
+//| print(i2c.scan())
+//| i2c.deinit()
+//|
+//| This example will initialize the the device, run
+//| :py:meth:`~busio.I2C.scan` and then :py:meth:`~busio.I2C.deinit` the
+//| hardware. The last step is optional because CircuitPython automatically
+//| resets hardware after a program finishes.
+//|
+//| Note that drivers will typically handle communication if provided the bus
+//| instance (such as ``busio.I2C(board.SCL, board.SDA)``), and that many of
+//| the methods listed here are lower level functionalities that are needed
+//| for working with custom drivers.
+//|
+//| Tutorial for I2C and SPI:
+//| https://learn.adafruit.com/circuitpython-basics-i2c-and-spi
+//|
+//| Tutorial for UART:
+//| https://learn.adafruit.com/circuitpython-essentials/circuitpython-uart-serial
+//| """
+//|
+
+STATIC const mp_rom_map_elem_t busio_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_busio) },
+ { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&busio_i2c_type) },
+ { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&busio_spi_type) },
+ #if CIRCUITPY_ONEWIREIO
+ { MP_ROM_QSTR(MP_QSTR_OneWire), MP_ROM_PTR(&onewireio_onewire_type) },
+ #endif
+ { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&busio_uart_type) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(busio_module_globals, busio_module_globals_table);
+
+const mp_obj_module_t busio_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t *)&busio_module_globals,
+};
+
+MP_REGISTER_MODULE(MP_QSTR_busio, busio_module, CIRCUITPY_BUSIO);
diff --git a/circuitpython/shared-bindings/busio/__init__.h b/circuitpython/shared-bindings/busio/__init__.h
new file mode 100644
index 0000000..bcea305
--- /dev/null
+++ b/circuitpython/shared-bindings/busio/__init__.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO___INIT___H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO___INIT___H
+
+#include "py/obj.h"
+
+// Nothing now.
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO___INIT___H