diff options
Diffstat (limited to 'circuitpython/shared-bindings')
436 files changed, 47390 insertions, 0 deletions
diff --git a/circuitpython/shared-bindings/__future__/__init__.c b/circuitpython/shared-bindings/__future__/__init__.c new file mode 100644 index 0000000..ad1bb3b --- /dev/null +++ b/circuitpython/shared-bindings/__future__/__init__.c @@ -0,0 +1,60 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert 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 "extmod/vfs.h" +#include "py/mpstate.h" +#include "py/obj.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/__future__/__init__.h" + +//| """Language features module +//| +//| The `__future__` module is used by other Python implementations to +//| enable forward compatibility for features enabled by default in an upcoming version. +//| """ +//| +//| annotations: Any +//| """In CPython, ``from __future import annotations`` +//| indicates that evaluation of annotations is postponed, as described in PEP 563. +//| CircuitPython (and MicroPython) ignore annotations entirely, whether or not this feature is imported. +//| This is a limitation of CircuitPython and MicroPython for efficiency reasons. +//| """ + +STATIC const mp_rom_map_elem_t future_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR___future__) }, + + { MP_ROM_QSTR(MP_QSTR_annotations), mp_const_true }, +}; + +STATIC MP_DEFINE_CONST_DICT(future_module_globals, future_module_globals_table); + +const mp_obj_module_t future_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&future_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR___future__, future_module, CIRCUITPY_FUTURE); diff --git a/circuitpython/shared-bindings/__future__/__init__.h b/circuitpython/shared-bindings/__future__/__init__.h new file mode 100644 index 0000000..c1dd0e1 --- /dev/null +++ b/circuitpython/shared-bindings/__future__/__init__.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Dan Halbert 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___FUTURE_____INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS___FUTURE_____INIT___H + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS___FUTURE_____INIT___H diff --git a/circuitpython/shared-bindings/_bleio/Adapter.c b/circuitpython/shared-bindings/_bleio/Adapter.c new file mode 100644 index 0000000..ce10809 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Adapter.c @@ -0,0 +1,468 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 <string.h> + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Adapter.h" + +#define ADV_INTERVAL_MIN (0.02f) +#define ADV_INTERVAL_MIN_STRING "0.02" +#define ADV_INTERVAL_MAX (10.24f) +#define ADV_INTERVAL_MAX_STRING "10.24" +// 20ms is recommended by Apple +#define ADV_INTERVAL_DEFAULT (0.1f) + +#define INTERVAL_DEFAULT (0.1f) +#define INTERVAL_MIN (0.0025f) +#define INTERVAL_MIN_STRING "0.0025" +#define INTERVAL_MAX (40.959375f) +#define INTERVAL_MAX_STRING "40.959375" +#define WINDOW_DEFAULT (0.1f) + +//| class Adapter: +//| """ +//| The BLE Adapter object manages the discovery and connection to other nearby Bluetooth Low Energy devices. +//| This part of the Bluetooth Low Energy Specification is known as Generic Access Profile (GAP). +//| +//| Discovery of other devices happens during a scanning process that listens for small packets of +//| information, known as advertisements, that are broadcast unencrypted. The advertising packets +//| have two different uses. The first is to broadcast a small piece of data to anyone who cares and +//| and nothing more. These are known as beacons. The second class of advertisement is to promote +//| additional functionality available after the devices establish a connection. For example, a +//| BLE heart rate monitor would advertise that it provides the standard BLE Heart Rate Service. +//| +//| The Adapter can do both parts of this process: it can scan for other device +//| advertisements and it can advertise its own data. Furthermore, Adapters can accept incoming +//| connections and also initiate connections.""" +//| + +//| def __init__(self, *, uart: busio.UART, rts: digitalio.DigitalInOut, cts: digitalio.DigitalInOut) -> None: +//| """On boards that do not have native BLE, you can use an HCI co-processor. +//| Pass the uart and pins used to communicate with the co-processor, such as an Adafruit AirLift. +//| The co-processor must have been reset and put into BLE mode beforehand +//| by the appropriate pin manipulation. +//| The ``uart``, ``rts``, and ``cts`` objects are used to +//| communicate with the HCI co-processor in HCI mode. +//| The `Adapter` object is enabled during this call. +//| +//| After instantiating an Adapter, call `_bleio.set_adapter()` to set `_bleio.adapter` +//| +//| On boards with native BLE, you cannot create an instance of `_bleio.Adapter`; +//| this constructor will raise `NotImplementedError`. +//| Use `_bleio.adapter` to access the sole instance already available. +//| """ +//| ... +//| +STATIC mp_obj_t bleio_adapter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + #if CIRCUITPY_BLEIO_HCI + bleio_adapter_obj_t *self = common_hal_bleio_allocate_adapter_or_raise(); + + enum { ARG_uart, ARG_rts, ARG_cts }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_uart, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + busio_uart_obj_t *uart = mp_arg_validate_type(args[ARG_uart].u_obj, &busio_uart_type, MP_QSTR_uart); + + digitalio_digitalinout_obj_t *rts = mp_arg_validate_type(args[ARG_rts].u_obj, &digitalio_digitalinout_type, MP_QSTR_rts); + digitalio_digitalinout_obj_t *cts = mp_arg_validate_type(args[ARG_cts].u_obj, &digitalio_digitalinout_type, MP_QSTR_cts); + + // Will enable the adapter. + common_hal_bleio_adapter_construct_hci_uart(self, uart, rts, cts); + + return MP_OBJ_FROM_PTR(self); + #else + mp_raise_NotImplementedError(translate("Cannot create a new Adapter; use _bleio.adapter;")); + return mp_const_none; + #endif // CIRCUITPY_BLEIO_HCI +} + +//| +//| enabled: bool +//| """State of the BLE adapter.""" +//| +STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) { + return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_enabled_obj, bleio_adapter_get_enabled); + +static mp_obj_t bleio_adapter_set_enabled(mp_obj_t self, mp_obj_t value) { + const bool enabled = mp_obj_is_true(value); + + common_hal_bleio_adapter_set_enabled(self, enabled); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_enabled_obj, bleio_adapter_set_enabled); + +MP_PROPERTY_GETSET(bleio_adapter_enabled_obj, + (mp_obj_t)&bleio_adapter_get_enabled_obj, + (mp_obj_t)&bleio_adapter_set_enabled_obj); + +//| address: Address +//| """MAC address of the BLE adapter.""" +//| +STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) { + return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_address(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address); + +STATIC mp_obj_t bleio_adapter_set_address(mp_obj_t self, mp_obj_t new_address) { + if (!common_hal_bleio_adapter_set_address(self, new_address)) { + mp_raise_bleio_BluetoothError(translate("Could not set address")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_address_obj, bleio_adapter_set_address); + +MP_PROPERTY_GETSET(bleio_adapter_address_obj, + (mp_obj_t)&bleio_adapter_get_address_obj, + (mp_obj_t)&bleio_adapter_set_address_obj); + +//| name: str +//| """name of the BLE adapter used once connected. +//| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``, +//| to make it easy to distinguish multiple CircuitPython boards.""" +//| +STATIC mp_obj_t bleio_adapter_get_name(mp_obj_t self) { + return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_name(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_name_obj, bleio_adapter_get_name); + + +STATIC mp_obj_t bleio_adapter_set_name(mp_obj_t self, mp_obj_t new_name) { + common_hal_bleio_adapter_set_name(self, mp_obj_str_get_str(new_name)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_name_obj, bleio_adapter_set_name); + +MP_PROPERTY_GETSET(bleio_adapter_name_obj, + (mp_obj_t)&bleio_adapter_get_name_obj, + (mp_obj_t)&bleio_adapter_set_name_obj); + +//| def start_advertising(self, data: ReadableBuffer, *, +//| scan_response: Optional[ReadableBuffer] = None, connectable: bool = True, +//| anonymous: bool = False, timeout: int = 0, interval: float = 0.1, +//| tx_power: int = 0, directed_to: Optional[Address] = None) -> None: +//| """Starts advertising until `stop_advertising` is called or if connectable, another device +//| connects to us. +//| +//| .. warning:: If data is longer than 31 bytes, then this will automatically advertise as an +//| extended advertisement that older BLE 4.x clients won't be able to scan for. +//| +//| .. note:: If you set ``anonymous=True``, then a timeout must be specified. If no timeout is +//| specified, then the maximum allowed timeout will be selected automatically. +//| +//| :param ~circuitpython_typing.ReadableBuffer data: advertising data packet bytes +//| :param ~circuitpython_typing.ReadableBuffer scan_response: scan response data packet bytes. ``None`` if no scan response is needed. +//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral. +//| :param bool anonymous: If `True` then this device's MAC address is randomized before advertising. +//| :param int timeout: If set, we will only advertise for this many seconds. Zero means no timeout. +//| :param float interval: advertising interval, in seconds +//| :param tx_power int: transmitter power while advertising in dBm +//| :param directed_to Address: peer to advertise directly to""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_anonymous, ARG_timeout, ARG_interval, ARG_tx_power, ARG_directed_to }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_scan_response, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_anonymous, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_tx_power, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_directed_to, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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 data_bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); + + // Pass an empty buffer if scan_response not provided. + mp_buffer_info_t scan_response_bufinfo = { 0 }; + if (args[ARG_scan_response].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_scan_response].u_obj, &scan_response_bufinfo, MP_BUFFER_READ); + } + + if (args[ARG_interval].u_obj == MP_OBJ_NULL) { + args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT); + } + + const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) { + mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), + ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING); + } + + bool connectable = args[ARG_connectable].u_bool; + bool anonymous = args[ARG_anonymous].u_bool; + uint32_t timeout = args[ARG_timeout].u_int; + if (data_bufinfo.len > 31 && connectable && scan_response_bufinfo.len > 0) { + mp_raise_bleio_BluetoothError(translate("Cannot have scan responses for extended, connectable advertisements.")); + } + + const bleio_address_obj_t *address = NULL; + if (args[ARG_directed_to].u_obj != mp_const_none) { + if (!connectable) { + mp_raise_bleio_BluetoothError(translate("Only connectable advertisements can be directed")); + } + address = mp_arg_validate_type(args[ARG_directed_to].u_obj, &bleio_address_type, MP_QSTR_directed_to); + } + + common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, timeout, interval, + &data_bufinfo, &scan_response_bufinfo, args[ARG_tx_power].u_int, address); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_advertising_obj, 1, bleio_adapter_start_advertising); + +//| def stop_advertising(self) -> None: +//| """Stop sending advertising packets.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_stop_advertising(mp_obj_t self_in) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_adapter_stop_advertising(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_stop_advertising_obj, bleio_adapter_stop_advertising); + +//| def start_scan(self, prefixes: ReadableBuffer = b"", *, buffer_size: int = 512, extended: bool = False, timeout: Optional[float] = None, interval: float = 0.1, window: float = 0.1, minimum_rssi: int = -80, active: bool = True) -> Iterable[ScanEntry]: +//| """Starts a BLE scan and returns an iterator of results. Advertisements and scan responses are +//| filtered and returned separately. +//| +//| :param ~circuitpython_typing.ReadableBuffer prefixes: Sequence of byte string prefixes to filter advertising packets +//| with. A packet without an advertising structure that matches one of the prefixes is +//| ignored. Format is one byte for length (n) and n bytes of prefix and can be repeated. +//| :param int buffer_size: the maximum number of advertising bytes to buffer. +//| :param bool extended: When True, support extended advertising packets. Increasing buffer_size is recommended when this is set. +//| :param float timeout: the scan timeout in seconds. If None or zero, will scan until `stop_scan` is called. +//| :param float interval: the interval (in seconds) between the start of two consecutive scan windows +//| Must be in the range 0.0025 - 40.959375 seconds. +//| :param float window: the duration (in seconds) to scan a single BLE channel. +//| window must be <= interval. +//| :param int minimum_rssi: the minimum rssi of entries to return. +//| :param bool active: retrieve scan responses for scannable advertisements. +//| :returns: an iterable of `_bleio.ScanEntry` objects +//| :rtype: iterable""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_prefixes, ARG_buffer_size, ARG_extended, ARG_timeout, ARG_interval, ARG_window, ARG_minimum_rssi, ARG_active }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_prefixes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 512} }, + { MP_QSTR_extended, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_window, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_minimum_rssi, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -80} }, + { MP_QSTR_active, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + }; + + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_float_t timeout = 0.0f; + if (args[ARG_timeout].u_obj != mp_const_none) { + timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + } + + if (args[ARG_interval].u_obj == MP_OBJ_NULL) { + args[ARG_interval].u_obj = mp_obj_new_float(INTERVAL_DEFAULT); + } + + if (args[ARG_window].u_obj == MP_OBJ_NULL) { + args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT); + } + + const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { + mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING); + } + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" + if (timeout != 0.0f && timeout < interval) { + mp_raise_ValueError(translate("non-zero timeout must be >= interval")); + } + #pragma GCC diagnostic pop + + const mp_float_t window = mp_obj_get_float(args[ARG_window].u_obj); + if (window > interval) { + mp_raise_ValueError(translate("window must be <= interval")); + } + + mp_buffer_info_t prefix_bufinfo; + prefix_bufinfo.len = 0; + if (args[ARG_prefixes].u_obj != MP_OBJ_NULL) { + mp_get_buffer_raise(args[ARG_prefixes].u_obj, &prefix_bufinfo, MP_BUFFER_READ); + if (gc_nbytes(prefix_bufinfo.buf) == 0) { + mp_raise_ValueError(translate("Prefix buffer must be on the heap")); + } + } + + return common_hal_bleio_adapter_start_scan(self, prefix_bufinfo.buf, prefix_bufinfo.len, args[ARG_extended].u_bool, args[ARG_buffer_size].u_int, timeout, interval, window, args[ARG_minimum_rssi].u_int, args[ARG_active].u_bool); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_scan_obj, 1, bleio_adapter_start_scan); + +//| def stop_scan(self) -> None: +//| """Stop the current scan.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_stop_scan(mp_obj_t self_in) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_adapter_stop_scan(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_stop_scan_obj, bleio_adapter_stop_scan); + +//| advertising: bool +//| """True when the adapter is currently advertising. (read-only)""" +//| +STATIC mp_obj_t bleio_adapter_get_advertising(mp_obj_t self) { + return mp_obj_new_bool(common_hal_bleio_adapter_get_advertising(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_advertising_obj, bleio_adapter_get_advertising); + +MP_PROPERTY_GETTER(bleio_adapter_advertising_obj, + (mp_obj_t)&bleio_adapter_get_advertising_obj); + +//| connected: bool +//| """True when the adapter is connected to another device regardless of who initiated the +//| connection. (read-only)""" +//| +STATIC mp_obj_t bleio_adapter_get_connected(mp_obj_t self) { + return mp_obj_new_bool(common_hal_bleio_adapter_get_connected(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_connected_obj, bleio_adapter_get_connected); + +MP_PROPERTY_GETTER(bleio_adapter_connected_obj, + (mp_obj_t)&bleio_adapter_get_connected_obj); + +//| connections: Tuple[Connection] +//| """Tuple of active connections including those initiated through +//| :py:meth:`_bleio.Adapter.connect`. (read-only)""" +//| +STATIC mp_obj_t bleio_adapter_get_connections(mp_obj_t self) { + return common_hal_bleio_adapter_get_connections(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_connections_obj, bleio_adapter_get_connections); + +MP_PROPERTY_GETTER(bleio_adapter_connections_obj, + (mp_obj_t)&bleio_adapter_get_connections_obj); + +//| def connect(self, address: Address, *, timeout: float) -> Connection: +//| """Attempts a connection to the device with the given address. +//| +//| :param Address address: The address of the peripheral to connect to +//| :param float/int timeout: Try to connect for timeout seconds.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_address, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + bleio_address_obj_t *address = mp_arg_validate_type(args[ARG_address].u_obj, &bleio_address_type, MP_QSTR_address); + mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + + return common_hal_bleio_adapter_connect(self, address, timeout); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_connect_obj, 1, bleio_adapter_connect); + +//| def erase_bonding(self) -> None: +//| """Erase all bonding information stored in flash memory.""" +//| ... +//| +STATIC mp_obj_t bleio_adapter_erase_bonding(mp_obj_t self_in) { + bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_bleio_adapter_erase_bonding(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_erase_bonding_obj, bleio_adapter_erase_bonding); + +STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_adapter_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_adapter_name_obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_adapter_start_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_adapter_stop_advertising_obj) }, + { MP_ROM_QSTR(MP_QSTR_advertising), MP_ROM_PTR(&bleio_adapter_advertising_obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_scan), MP_ROM_PTR(&bleio_adapter_start_scan_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_scan), MP_ROM_PTR(&bleio_adapter_stop_scan_obj) }, + + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_adapter_connect_obj) }, + + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_adapter_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_connections), MP_ROM_PTR(&bleio_adapter_connections_obj) }, + + { MP_ROM_QSTR(MP_QSTR_erase_bonding), MP_ROM_PTR(&bleio_adapter_erase_bonding_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table); + +const mp_obj_type_t bleio_adapter_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Adapter, + .make_new = bleio_adapter_make_new, + .locals_dict = (mp_obj_t)&bleio_adapter_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_bleio/Adapter.h b/circuitpython/shared-bindings/_bleio/Adapter.h new file mode 100644 index 0000000..1dce615 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Adapter.h @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_BLEIO_ADAPTER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H + +#include <stdint.h> + +#include "common-hal/_bleio/Adapter.h" + +#include "py/objstr.h" +#include "shared-module/_bleio/Address.h" + +extern const mp_obj_type_t bleio_adapter_type; + +#if CIRCUITPY_BLEIO_HCI +void common_hal_bleio_adapter_construct_hci_uart(bleio_adapter_obj_t *self, busio_uart_obj_t *uart, digitalio_digitalinout_obj_t *rts, digitalio_digitalinout_obj_t *cts); +#endif // CIRCUITPY_BLEIO_HCI + +extern bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self); +extern bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self); +extern void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled); +extern mp_int_t common_hal_bleio_adapter_get_tx_power(bleio_adapter_obj_t *self); +extern void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t tx_power); +extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self); +extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self); +extern bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address); + +extern mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self); +extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name); + +extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, float interval, + const uint8_t *advertising_data, uint16_t advertising_data_len, + const uint8_t *scan_response_data, uint16_t scan_response_data_len, + mp_int_t tx_power, const bleio_address_obj_t *directed_to); + +extern void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, + mp_buffer_info_t *advertising_data_bufinfo, + mp_buffer_info_t *scan_response_data_bufinfo, + mp_int_t tx_power, const bleio_address_obj_t *directed_to); +extern void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self); + +extern mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t *prefixes, size_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active); +extern void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self); + +extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self); +extern mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self); +extern mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout); + +extern void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self); +extern bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H diff --git a/circuitpython/shared-bindings/_bleio/Address.c b/circuitpython/shared-bindings/_bleio/Address.c new file mode 100644 index 0000000..9673642 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Address.c @@ -0,0 +1,219 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 <string.h> +#include <stdio.h> + +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-module/_bleio/Address.h" + +//| class Address: +//| """Encapsulates the address of a BLE device.""" +//| + +//| def __init__(self, address: ReadableBuffer, address_type: int) -> None: +//| """Create a new Address object encapsulating the address value. +//| The value itself can be one of: +//| +//| :param ~circuitpython_typing.ReadableBuffer address: The address value to encapsulate. A buffer object (bytearray, bytes) of 6 bytes. +//| :param int address_type: one of the integer values: `PUBLIC`, `RANDOM_STATIC`, +//| `RANDOM_PRIVATE_RESOLVABLE`, or `RANDOM_PRIVATE_NON_RESOLVABLE`.""" +//| ... +//| +STATIC mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_address, ARG_address_type }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_address_type, MP_ARG_INT, {.u_int = BLEIO_ADDRESS_TYPE_PUBLIC } }, + }; + + 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); + + bleio_address_obj_t *self = m_new_obj(bleio_address_obj_t); + self->base.type = &bleio_address_type; + + const mp_obj_t address = args[ARG_address].u_obj; + mp_buffer_info_t buf_info; + mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); + if (buf_info.len != NUM_BLEIO_ADDRESS_BYTES) { + mp_raise_ValueError_varg(translate("Address must be %d bytes long"), NUM_BLEIO_ADDRESS_BYTES); + } + + const mp_int_t address_type = args[ARG_address_type].u_int; + if (address_type < BLEIO_ADDRESS_TYPE_MIN || address_type > BLEIO_ADDRESS_TYPE_MAX) { + mp_raise_ValueError(translate("Address type out of range")); + } + + common_hal_bleio_address_construct(self, buf_info.buf, address_type); + + return MP_OBJ_FROM_PTR(self); +} + +//| address_bytes: bytes +//| """The bytes that make up the device address (read-only). +//| +//| Note that the ``bytes`` object returned is in little-endian order: +//| The least significant byte is ``address_bytes[0]``. So the address will +//| appear to be reversed if you print the raw ``bytes`` object. If you print +//| or use `str()` on the :py:class:`~_bleio.Attribute` object itself, the address will be printed +//| in the expected order. For example: +//| +//| .. code-block:: python +//| +//| >>> import _bleio +//| >>> _bleio.adapter.address +//| <Address c8:1d:f5:ed:a8:35> +//| >>> _bleio.adapter.address.address_bytes +//| b'5\\xa8\\xed\\xf5\\x1d\\xc8'""" +//| +STATIC mp_obj_t bleio_address_get_address_bytes(mp_obj_t self_in) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_address_get_address_bytes(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_address_bytes_obj, bleio_address_get_address_bytes); + +MP_PROPERTY_GETTER(bleio_address_address_bytes_obj, + (mp_obj_t)&bleio_address_get_address_bytes_obj); + +//| type: int +//| """The address type (read-only). +//| +//| One of the integer values: `PUBLIC`, `RANDOM_STATIC`, `RANDOM_PRIVATE_RESOLVABLE`, +//| or `RANDOM_PRIVATE_NON_RESOLVABLE`.""" +//| +STATIC mp_obj_t bleio_address_get_type(mp_obj_t self_in) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_address_get_type(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_address_get_type_obj, bleio_address_get_type); + +MP_PROPERTY_GETTER(bleio_address_type_obj, + (mp_obj_t)&bleio_address_get_type_obj); + +//| def __eq__(self, other: object) -> bool: +//| """Two Address objects are equal if their addresses and address types are equal.""" +//| ... +//| +STATIC mp_obj_t bleio_address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + // Two Addresses are equal if their address bytes and address_type are equal + case MP_BINARY_OP_EQUAL: + if (mp_obj_is_type(rhs_in, &bleio_address_type)) { + bleio_address_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); + bleio_address_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool( + mp_obj_equal(common_hal_bleio_address_get_address_bytes(lhs), + common_hal_bleio_address_get_address_bytes(rhs)) && + common_hal_bleio_address_get_type(lhs) == + common_hal_bleio_address_get_type(rhs)); + + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __hash__(self) -> int: +//| """Returns a hash for the Address data.""" +//| ... +//| +STATIC mp_obj_t bleio_address_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + switch (op) { + // Two Addresses are equal if their address bytes and address_type are equal + case MP_UNARY_OP_HASH: { + mp_obj_t bytes = common_hal_bleio_address_get_address_bytes(MP_OBJ_TO_PTR(self_in)); + GET_STR_HASH(bytes, h); + if (h == 0) { + GET_STR_DATA_LEN(bytes, data, len); + h = qstr_compute_hash(data, len); + } + h ^= common_hal_bleio_address_get_type(MP_OBJ_TO_PTR(self_in)); + return MP_OBJ_NEW_SMALL_INT(h); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t buf_info; + mp_obj_t address_bytes = common_hal_bleio_address_get_address_bytes(self); + mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ); + + const uint8_t *buf = (uint8_t *)buf_info.buf; + mp_printf(print, "<Address %02x:%02x:%02x:%02x:%02x:%02x>", + buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]); +} + +//| PUBLIC: int +//| """A publicly known address, with a company ID (high 24 bits)and company-assigned part (low 24 bits).""" +//| +//| RANDOM_STATIC: int +//| """A randomly generated address that does not change often. It may never change or may change after +//| a power cycle.""" +//| +//| RANDOM_PRIVATE_RESOLVABLE: int +//| """An address that is usable when the peer knows the other device's secret Identity Resolving Key (IRK).""" +//| +//| RANDOM_PRIVATE_NON_RESOLVABLE: int +//| """A randomly generated address that changes on every connection.""" +//| +STATIC const mp_rom_map_elem_t bleio_address_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_address_bytes), MP_ROM_PTR(&bleio_address_address_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&bleio_address_type_obj) }, + // These match the BLE_GAP_ADDR_TYPES values used by the nRF library. + { MP_ROM_QSTR(MP_QSTR_PUBLIC), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_STATIC), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_RESOLVABLE), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_RANDOM_PRIVATE_NON_RESOLVABLE), MP_ROM_INT(3) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_address_locals_dict, bleio_address_locals_dict_table); + +const mp_obj_type_t bleio_address_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Address, + .make_new = bleio_address_make_new, + .print = bleio_address_print, + .locals_dict = (mp_obj_dict_t *)&bleio_address_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = bleio_address_unary_op, + .binary_op = bleio_address_binary_op, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/Address.h b/circuitpython/shared-bindings/_bleio/Address.h new file mode 100644 index 0000000..98b6f80 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Address.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_BLEIO_ADDRESS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H + +#include "py/objtype.h" +#include "shared-module/_bleio/Address.h" + +#define BLEIO_ADDRESS_TYPE_PUBLIC (0) +#define BLEIO_ADDRESS_TYPE_RANDOM_STATIC (1) +#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE (2) +#define BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE (3) + +#define BLEIO_ADDRESS_TYPE_MIN BLEIO_ADDRESS_TYPE_PUBLIC +#define BLEIO_ADDRESS_TYPE_MAX BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE + +extern const mp_obj_type_t bleio_address_type; + +extern void common_hal_bleio_address_construct(bleio_address_obj_t *self, uint8_t *bytes, uint8_t address_type); +extern mp_obj_t common_hal_bleio_address_get_address_bytes(bleio_address_obj_t *self); +extern uint8_t common_hal_bleio_address_get_type(bleio_address_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADDRESS_H diff --git a/circuitpython/shared-bindings/_bleio/Attribute.c b/circuitpython/shared-bindings/_bleio/Attribute.c new file mode 100644 index 0000000..d639f2a --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Attribute.c @@ -0,0 +1,82 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert 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 "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class Attribute: +//| """Definitions associated with all BLE attributes: characteristics, descriptors, etc. +//| +//| :py:class:`~_bleio.Attribute` is, notionally, a superclass of +//| :py:class:`~Characteristic` and :py:class:`~Descriptor`, +//| but is not defined as a Python superclass of those classes.""" +//| +//| def __init__(self) -> None: +//| """You cannot create an instance of :py:class:`~_bleio.Attribute`.""" +//| ... +//| + +STATIC const mp_rom_map_elem_t bleio_attribute_locals_dict_table[] = { + +//| NO_ACCESS: int +//| """security mode: access not allowed""" +//| +//| OPEN: int +//| """security_mode: no security (link is not encrypted)""" +//| +//| ENCRYPT_NO_MITM: int +//| """security_mode: unauthenticated encryption, without man-in-the-middle protection""" +//| +//| ENCRYPT_WITH_MITM: int +//| """security_mode: authenticated encryption, with man-in-the-middle protection""" +//| +//| LESC_ENCRYPT_WITH_MITM: int +//| """security_mode: LESC encryption, with man-in-the-middle protection""" +//| +//| SIGNED_NO_MITM: int +//| """security_mode: unauthenticated data signing, without man-in-the-middle protection""" +//| +//| SIGNED_WITH_MITM: int +//| """security_mode: authenticated data signing, without man-in-the-middle protection""" +//| + { MP_ROM_QSTR(MP_QSTR_NO_ACCESS), MP_ROM_INT(SECURITY_MODE_NO_ACCESS) }, + { MP_ROM_QSTR(MP_QSTR_OPEN), MP_ROM_INT(SECURITY_MODE_OPEN) }, + { MP_ROM_QSTR(MP_QSTR_ENCRYPT_NO_MITM), MP_ROM_INT(SECURITY_MODE_ENC_NO_MITM) }, + { MP_ROM_QSTR(MP_QSTR_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_ENC_WITH_MITM) }, + { MP_ROM_QSTR(MP_QSTR_LESC_ENCRYPT_WITH_MITM), MP_ROM_INT(SECURITY_MODE_LESC_ENC_WITH_MITM) }, + { MP_ROM_QSTR(MP_QSTR_SIGNED_NO_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_NO_MITM) }, + { MP_ROM_QSTR(MP_QSTR_SIGNED_WITH_MITM), MP_ROM_INT(SECURITY_MODE_SIGNED_WITH_MITM) }, + +}; +STATIC MP_DEFINE_CONST_DICT(bleio_attribute_locals_dict, bleio_attribute_locals_dict_table); + +const mp_obj_type_t bleio_attribute_type = { + { &mp_type_type }, + .name = MP_QSTR_Attribute, + .locals_dict = (mp_obj_dict_t *)&bleio_attribute_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_bleio/Attribute.h b/circuitpython/shared-bindings/_bleio/Attribute.h new file mode 100644 index 0000000..a0ce045 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Attribute.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert 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_BLEIO_ATTRIBUTE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H + +#include "py/obj.h" + +#include "common-hal/_bleio/Attribute.h" +#include "shared-module/_bleio/Attribute.h" + +extern const mp_obj_type_t bleio_attribute_type; + +extern void common_hal_bleio_attribute_security_mode_check_valid(bleio_attribute_security_mode_t security_mode); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ATTRIBUTE_H diff --git a/circuitpython/shared-bindings/_bleio/Characteristic.c b/circuitpython/shared-bindings/_bleio/Characteristic.c new file mode 100644 index 0000000..4ff37f0 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Characteristic.c @@ -0,0 +1,334 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class Characteristic: +//| """Stores information about a BLE service characteristic and allows reading +//| and writing of the characteristic's value.""" +//| +//| def __init__(self) -> None: +//| """There is no regular constructor for a Characteristic. A new local Characteristic can be created +//| and attached to a Service by calling `add_to_service()`. +//| Remote Characteristic objects are created by `Connection.discover_remote_services()` +//| as part of remote Services.""" +//| ... +//| + +//| def add_to_service(self, service: Service, uuid: UUID, *, properties: int = 0, +//| read_perm: int = Attribute.OPEN, write_perm: int = Attribute.OPEN, +//| max_length: int = 20, fixed_length: bool = False, +//| initial_value: Optional[ReadableBuffer] = None, +//| user_description: Optional[str] = None) -> Characteristic: +//| """Create a new Characteristic object, and add it to this Service. +//| +//| :param Service service: The service that will provide this characteristic +//| :param UUID uuid: The uuid of the characteristic +//| :param int properties: The properties of the characteristic, +//| specified as a bitmask of these values bitwise-or'd together: +//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. +//| :param int read_perm: Specifies whether the characteristic can be read by a client, and if so, which +//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, +//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. +//| :param int write_perm: Specifies whether the characteristic can be written by a client, and if so, which +//| security mode is required. Values allowed are the same as ``read_perm``. +//| :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed is +//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum +//| number of data bytes that fit in a single BLE 4.x ATT packet. +//| :param bool fixed_length: True if the characteristic value is of fixed length. +//| :param ~circuitpython_typing.ReadableBuffer initial_value: The initial value for this characteristic. If not given, will be +//| filled with zeros. +//| :param str user_description: User friendly description of the characteristic +//| +//| :return: the new Characteristic.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_service, ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value, ARG_user_description }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_properties, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_read_perm, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_write_perm, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_max_length, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 20} }, + { MP_QSTR_fixed_length, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_initial_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_user_description, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + bleio_service_obj_t *service = mp_arg_validate_type(args[ARG_service].u_obj, &bleio_service_type, MP_QSTR_service); + + bleio_uuid_obj_t *uuid = mp_arg_validate_type(args[ARG_uuid].u_obj, &bleio_uuid_type, MP_QSTR_uuid); + + const bleio_characteristic_properties_t properties = args[ARG_properties].u_int; + if (properties & ~CHAR_PROP_ALL) { + mp_raise_ValueError(translate("Invalid properties")); + } + + const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(read_perm); + + const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(write_perm); + + const mp_int_t max_length_int = args[ARG_max_length].u_int; + if (max_length_int < 0) { + mp_raise_ValueError(translate("max_length must be >= 0")); + } + const size_t max_length = (size_t)max_length_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + if (initial_value_bufinfo.len > max_length || + (fixed_length && initial_value_bufinfo.len != max_length)) { + mp_raise_ValueError(translate("initial_value length is wrong")); + } + + const char *user_description = NULL; + if (args[ARG_user_description].u_obj != mp_const_none) { + user_description = mp_obj_str_get_str(args[ARG_user_description].u_obj); + } + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + characteristic->base.type = &bleio_characteristic_type; + + // Range checking on max_length arg is done by the common_hal layer, because + // it may vary depending on underlying BLE implementation. + common_hal_bleio_characteristic_construct( + characteristic, service, 0, uuid, + properties, read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo, + user_description); + + return MP_OBJ_FROM_PTR(characteristic); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_add_to_service_fun_obj, 1, bleio_characteristic_add_to_service); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_characteristic_add_to_service_obj, MP_ROM_PTR(&bleio_characteristic_add_to_service_fun_obj)); + + + +//| properties: int +//| """An int bitmask representing which properties are set, specified as bitwise or'ing of +//| of these possible values. +//| `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`.""" +//| +STATIC mp_obj_t bleio_characteristic_get_properties(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_get_properties(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_properties_obj, bleio_characteristic_get_properties); + +MP_PROPERTY_GETTER(bleio_characteristic_properties_obj, + (mp_obj_t)&bleio_characteristic_get_properties_obj); + +//| uuid: Optional[UUID] +//| """The UUID of this characteristic. (read-only) +//| +//| Will be ``None`` if the 128-bit UUID for this characteristic is not known.""" +//| +STATIC mp_obj_t bleio_characteristic_get_uuid(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_characteristic_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_uuid_obj, bleio_characteristic_get_uuid); + +MP_PROPERTY_GETTER(bleio_characteristic_uuid_obj, + (mp_obj_t)&bleio_characteristic_get_uuid_obj); + +//| value: bytearray +//| """The value of this characteristic.""" +//| +STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uint8_t temp[512]; + size_t actual_len = common_hal_bleio_characteristic_get_value(self, temp, sizeof(temp)); + return mp_obj_new_bytearray(actual_len, temp); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_value_obj, bleio_characteristic_get_value); + +STATIC mp_obj_t bleio_characteristic_set_value(mp_obj_t self_in, mp_obj_t value_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); + + common_hal_bleio_characteristic_set_value(self, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_characteristic_set_value_obj, bleio_characteristic_set_value); + +MP_PROPERTY_GETSET(bleio_characteristic_value_obj, + (mp_obj_t)&bleio_characteristic_get_value_obj, + (mp_obj_t)&bleio_characteristic_set_value_obj); + +//| max_length: int +//| """The max length of this characteristic.""" +//| +STATIC mp_obj_t bleio_characteristic_get_max_length(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_get_max_length(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_max_length_obj, bleio_characteristic_get_max_length); + +MP_PROPERTY_GETTER(bleio_characteristic_max_length_obj, + (mp_obj_t)&bleio_characteristic_get_max_length_obj); + +//| descriptors: Descriptor +//| """A tuple of :py:class:`Descriptor` objects related to this characteristic. (read-only)""" +//| +STATIC mp_obj_t bleio_characteristic_get_descriptors(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + // Return list as a tuple so user won't be able to change it. + return MP_OBJ_FROM_PTR(common_hal_bleio_characteristic_get_descriptors(self)); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_descriptors_obj, bleio_characteristic_get_descriptors); + +MP_PROPERTY_GETTER(bleio_characteristic_descriptors_obj, + (mp_obj_t)&bleio_characteristic_get_descriptors_obj); + +//| service: Service +//| """The Service this Characteristic is a part of.""" +//| +STATIC mp_obj_t bleio_characteristic_get_service(mp_obj_t self_in) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_characteristic_get_service(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_service_obj, bleio_characteristic_get_service); + +MP_PROPERTY_GETTER(bleio_characteristic_service_obj, + (mp_obj_t)&bleio_characteristic_get_service_obj); + +//| def set_cccd(self, *, notify: bool = False, indicate: bool = False) -> None: +//| """Set the remote characteristic's CCCD to enable or disable notification and indication. +//| +//| :param bool notify: True if Characteristic should receive notifications of remote writes +//| :param float indicate: True if Characteristic should receive indications of remote writes""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_set_cccd(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_notify, ARG_indicate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_notify, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_indicate, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + 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); + + common_hal_bleio_characteristic_set_cccd(self, args[ARG_notify].u_bool, args[ARG_indicate].u_bool); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_characteristic_set_cccd); + +STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) }, + { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_properties_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) }, + + // Bitmask constants to represent properties +//| BROADCAST: int +//| """property: allowed in advertising packets""" +//| +//| INDICATE: int +//| """property: server will indicate to the client when the value is set and wait for a response""" +//| +//| NOTIFY: int +//| """property: server will notify the client when the value is set""" +//| +//| READ: int +//| """property: clients may read this characteristic""" +//| +//| WRITE: int +//| """property: clients may write this characteristic; a response will be sent back""" +//| +//| WRITE_NO_RESPONSE: int +//| """property: clients may write this characteristic; no response will be sent back""" +//| + { MP_ROM_QSTR(MP_QSTR_BROADCAST), MP_ROM_INT(CHAR_PROP_BROADCAST) }, + { MP_ROM_QSTR(MP_QSTR_INDICATE), MP_ROM_INT(CHAR_PROP_INDICATE) }, + { MP_ROM_QSTR(MP_QSTR_NOTIFY), MP_ROM_INT(CHAR_PROP_NOTIFY) }, + { MP_ROM_QSTR(MP_QSTR_READ), MP_ROM_INT(CHAR_PROP_READ) }, + { MP_ROM_QSTR(MP_QSTR_WRITE), MP_ROM_INT(CHAR_PROP_WRITE) }, + { MP_ROM_QSTR(MP_QSTR_WRITE_NO_RESPONSE), MP_ROM_INT(CHAR_PROP_WRITE_NO_RESPONSE) }, + +}; +STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_locals_dict, bleio_characteristic_locals_dict_table); + +STATIC void bleio_characteristic_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Characteristic("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, "<Characteristic with Unregistered UUID>"); + } +} + +const mp_obj_type_t bleio_characteristic_type = { + { &mp_type_type }, + .name = MP_QSTR_Characteristic, + .print = bleio_characteristic_print, + .locals_dict = (mp_obj_dict_t *)&bleio_characteristic_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_bleio/Characteristic.h b/circuitpython/shared-bindings/_bleio/Characteristic.h new file mode 100644 index 0000000..349dd5f --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Characteristic.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_BLEIO_CHARACTERISTIC_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H + +#include "py/objtuple.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-module/_bleio/Characteristic.h" +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Service.h" + +extern const mp_obj_type_t bleio_characteristic_type; + +extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self); +extern mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self); +extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self); +extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self); +extern size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t *self); +extern size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len); +extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor); +extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, const char *user_description); +extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); +extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTIC_H diff --git a/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.c b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.c new file mode 100644 index 0000000..78ff07f --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.c @@ -0,0 +1,242 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert 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 "py/mperrno.h" +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-bindings/util.h" + +STATIC void raise_error_if_not_connected(bleio_characteristic_buffer_obj_t *self) { + if (!common_hal_bleio_characteristic_buffer_connected(self)) { + mp_raise_ConnectionError(translate("Not connected")); + } +} + +//| class CharacteristicBuffer: +//| """Accumulates a Characteristic's incoming values in a FIFO buffer.""" +//| +//| def __init__(self, characteristic: Characteristic, *, timeout: int = 1, buffer_size: int = 64) -> None: +//| +//| """Monitor the given Characteristic. Each time a new value is written to the Characteristic +//| add the newly-written bytes to a FIFO buffer. +//| +//| :param Characteristic characteristic: The Characteristic to monitor. +//| It may be a local Characteristic provided by a Peripheral Service, or a remote Characteristic +//| in a remote Service that a Central has connected to. +//| :param int timeout: the timeout in seconds to wait for the first character and between subsequent characters. +//| :param int buffer_size: Size of ring buffer that stores incoming data coming from client. +//| Must be >= 1.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_buffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_characteristic, ARG_timeout, ARG_buffer_size, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + { MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + + 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); + + bleio_characteristic_obj_t *characteristic = mp_arg_validate_type(args[ARG_characteristic].u_obj, &bleio_characteristic_type, MP_QSTR_characteristic); + + mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + if (timeout < 0.0f) { + mp_raise_ValueError(translate("timeout must be >= 0.0")); + } + + const int buffer_size = args[ARG_buffer_size].u_int; + if (buffer_size < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_buffer_size); + } + + bleio_characteristic_buffer_obj_t *self = m_new_obj(bleio_characteristic_buffer_obj_t); + self->base.type = &bleio_characteristic_buffer_type; + + common_hal_bleio_characteristic_buffer_construct(self, characteristic, timeout, buffer_size); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void check_for_deinit(bleio_characteristic_buffer_obj_t *self) { + if (common_hal_bleio_characteristic_buffer_deinited(self)) { + raise_deinited_error(); + } +} + +// 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)""" +//| ... +//| +//| def readline(self) -> bytes: +//| """Read a line, ending in a newline character. +//| +//| :return: the line read +//| :rtype: int or None""" +//| ... +//| + +// These three methods are used by the shared stream methods. +STATIC mp_uint_t bleio_characteristic_buffer_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + raise_error_if_not_connected(self); + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + return common_hal_bleio_characteristic_buffer_read(self, buf, size, errcode); +} + +STATIC mp_uint_t bleio_characteristic_buffer_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + mp_raise_NotImplementedError(translate("CharacteristicBuffer writing not provided")); + return 0; +} + +STATIC mp_uint_t bleio_characteristic_buffer_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + raise_error_if_not_connected(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_bleio_characteristic_buffer_rx_characters_available(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } +// No writing provided. +// 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; +} + +//| in_waiting: int +//| """The number of bytes in the input buffer, available to be read""" +//| +STATIC mp_obj_t bleio_characteristic_buffer_obj_get_in_waiting(mp_obj_t self_in) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_characteristic_buffer_rx_characters_available(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_get_in_waiting_obj, bleio_characteristic_buffer_obj_get_in_waiting); + +MP_PROPERTY_GETTER(bleio_characteristic_buffer_in_waiting_obj, + (mp_obj_t)&bleio_characteristic_buffer_get_in_waiting_obj); + +//| def reset_input_buffer(self) -> None: +//| """Discard any unread characters in the input buffer.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_buffer_obj_reset_input_buffer(mp_obj_t self_in) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_bleio_characteristic_buffer_clear_rx_buffer(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_reset_input_buffer_obj, bleio_characteristic_buffer_obj_reset_input_buffer); + +//| def deinit(self) -> None: +//| """Disable permanently.""" +//| ... +//| +STATIC mp_obj_t bleio_characteristic_buffer_deinit(mp_obj_t self_in) { + bleio_characteristic_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_bleio_characteristic_buffer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_buffer_deinit_obj, bleio_characteristic_buffer_deinit); + +STATIC const mp_rom_map_elem_t bleio_characteristic_buffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bleio_characteristic_buffer_deinit_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) }, + // CharacteristicBuffer is currently read-only. + // { 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(&bleio_characteristic_buffer_reset_input_buffer_obj) }, + // Properties + { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&bleio_characteristic_buffer_in_waiting_obj) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_characteristic_buffer_locals_dict, bleio_characteristic_buffer_locals_dict_table); + +STATIC const mp_stream_p_t characteristic_buffer_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = bleio_characteristic_buffer_read, + .write = bleio_characteristic_buffer_write, + .ioctl = bleio_characteristic_buffer_ioctl, + .is_text = false, + // Disallow readinto() size parameter. + .pyserial_readinto_compatibility = true, +}; + + +const mp_obj_type_t bleio_characteristic_buffer_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_CharacteristicBuffer, + .make_new = bleio_characteristic_buffer_make_new, + .locals_dict = (mp_obj_dict_t *)&bleio_characteristic_buffer_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &characteristic_buffer_stream_p, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.h b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.h new file mode 100644 index 0000000..de85f01 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/CharacteristicBuffer.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert 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_BLEIO_CHARACTERISTICBUFFER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H + +#include "common-hal/_bleio/CharacteristicBuffer.h" + +extern const mp_obj_type_t bleio_characteristic_buffer_type; + +void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + uint8_t *buffer, size_t buffer_size, + void *static_handler_entry); +void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + size_t buffer_size); +uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode); +uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self); +void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self); +bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self); +void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self); +bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CHARACTERISTICBUFFER_H diff --git a/circuitpython/shared-bindings/_bleio/Connection.c b/circuitpython/shared-bindings/_bleio/Connection.c new file mode 100644 index 0000000..7a85ccd --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Connection.c @@ -0,0 +1,260 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 "shared-bindings/_bleio/Connection.h" + +#include <string.h> +#include <stdio.h> + +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" + +//| class Connection: +//| """A BLE connection to another device. Used to discover and interact with services on the other +//| device. +//| +//| Usage:: +//| +//| import _bleio +//| +//| my_entry = None +//| for entry in _bleio.adapter.scan(2.5): +//| if entry.name is not None and entry.name == 'InterestingPeripheral': +//| my_entry = entry +//| break +//| +//| if not my_entry: +//| raise Exception("'InterestingPeripheral' not found") +//| +//| connection = _bleio.adapter.connect(my_entry.address, timeout=10)""" +//| + +void bleio_connection_ensure_connected(bleio_connection_obj_t *self) { + if (!common_hal_bleio_connection_get_connected(self)) { + mp_raise_ConnectionError(translate("Connection has been disconnected and can no longer be used. Create a new connection.")); + } +} + +//| def __init__(self) -> None: +//| """Connections cannot be made directly. Instead, to initiate a connection use `Adapter.connect`. +//| Connections may also be made when another device initiates a connection. To use a Connection +//| created by a peer, read the `Adapter.connections` property.""" +//| ... +//| +//| def disconnect(self) -> None: +//| """Disconnects from the remote peripheral. Does nothing if already disconnected.""" +//| ... +//| +STATIC mp_obj_t bleio_connection_disconnect(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + // common_hal_bleio_connection_disconnect() does nothing if already disconnected. + common_hal_bleio_connection_disconnect(self->connection); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_disconnect_obj, bleio_connection_disconnect); + + +//| def pair(self, *, bond: bool = True) -> None: +//| """Pair to the peer to improve security.""" +//| ... +//| +STATIC mp_obj_t bleio_connection_pair(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_bond }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bond, MP_ARG_BOOL, {.u_bool = true} }, + }; + + 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); + + bleio_connection_ensure_connected(self); + + common_hal_bleio_connection_pair(self->connection, args[ARG_bond].u_bool); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_connection_pair_obj, 1, bleio_connection_pair); + +//| def discover_remote_services(self, service_uuids_whitelist: Optional[Iterable[UUID]] = None) -> Tuple[Service, ...]: +//| """Do BLE discovery for all services or for the given service UUIDS, +//| to find their handles and characteristics, and return the discovered services. +//| `Connection.connected` must be True. +//| +//| :param iterable service_uuids_whitelist: +//| +//| an iterable of :py:class:`UUID` objects for the services provided by the peripheral +//| that you want to use. +//| +//| The peripheral may provide more services, but services not listed are ignored +//| and will not be returned. +//| +//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be +//| slow. +//| +//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you +//| you must have already created a :py:class:`UUID` object for that UUID in order for the +//| service or characteristic to be discovered. Creating the UUID causes the UUID to be +//| registered for use. (This restriction may be lifted in the future.) +//| +//| :return: A tuple of `_bleio.Service` objects provided by the remote peripheral.""" +//| ... +//| +STATIC mp_obj_t bleio_connection_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_service_uuids_whitelist }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service_uuids_whitelist, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + bleio_connection_ensure_connected(self); + + return MP_OBJ_FROM_PTR(common_hal_bleio_connection_discover_remote_services( + self, + args[ARG_service_uuids_whitelist].u_obj)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_connection_discover_remote_services_obj, 1, bleio_connection_discover_remote_services); + +//| connected: bool +//| """True if connected to the remote peer.""" +//| +STATIC mp_obj_t bleio_connection_get_connected(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_connection_get_connected(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_connected_obj, bleio_connection_get_connected); + +MP_PROPERTY_GETTER(bleio_connection_connected_obj, + (mp_obj_t)&bleio_connection_get_connected_obj); + + +//| paired: bool +//| """True if paired to the remote peer.""" +//| +STATIC mp_obj_t bleio_connection_get_paired(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_connection_get_paired(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_paired_obj, bleio_connection_get_paired); + +MP_PROPERTY_GETTER(bleio_connection_paired_obj, + (mp_obj_t)&bleio_connection_get_paired_obj); + + +//| connection_interval: float +//| """Time between transmissions in milliseconds. Will be multiple of 1.25ms. Lower numbers +//| increase speed and decrease latency but increase power consumption. +//| +//| When setting connection_interval, the peer may reject the new interval and +//| `connection_interval` will then remain the same. +//| +//| Apple has additional guidelines that dictate should be a multiple of 15ms except if HID is +//| available. When HID is available Apple devices may accept 11.25ms intervals.""" +//| +STATIC mp_obj_t bleio_connection_get_connection_interval(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_connection_ensure_connected(self); + return mp_obj_new_float(common_hal_bleio_connection_get_connection_interval(self->connection)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_connection_interval_obj, bleio_connection_get_connection_interval); + +//| max_packet_length: int +//| """The maximum number of data bytes that can be sent in a single transmission, +//| not including overhead bytes. +//| +//| This is the maximum number of bytes that can be sent in a notification, +//| which must be sent in a single packet. +//| But for a regular characteristic read or write, may be sent in multiple packets, +//| so this limit does not apply.""" +//| +STATIC mp_obj_t bleio_connection_get_max_packet_length(mp_obj_t self_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_connection_ensure_connected(self); + return mp_obj_new_int(common_hal_bleio_connection_get_max_packet_length(self->connection)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_max_packet_length_obj, bleio_connection_get_max_packet_length); + + +STATIC mp_obj_t bleio_connection_set_connection_interval(mp_obj_t self_in, mp_obj_t interval_in) { + bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_float_t interval = mp_obj_get_float(interval_in); + + bleio_connection_ensure_connected(self); + common_hal_bleio_connection_set_connection_interval(self->connection, interval); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_connection_set_connection_interval_obj, bleio_connection_set_connection_interval); + +MP_PROPERTY_GETSET(bleio_connection_connection_interval_obj, + (mp_obj_t)&bleio_connection_get_connection_interval_obj, + (mp_obj_t)&bleio_connection_set_connection_interval_obj); + +MP_PROPERTY_GETTER(bleio_connection_max_packet_length_obj, + (mp_obj_t)&bleio_connection_get_max_packet_length_obj); + +STATIC const mp_rom_map_elem_t bleio_connection_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_pair), MP_ROM_PTR(&bleio_connection_pair_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_connection_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_connection_discover_remote_services_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_connection_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_paired), MP_ROM_PTR(&bleio_connection_paired_obj) }, + { MP_ROM_QSTR(MP_QSTR_connection_interval), MP_ROM_PTR(&bleio_connection_connection_interval_obj) }, + { MP_ROM_QSTR(MP_QSTR_max_packet_length), MP_ROM_PTR(&bleio_connection_max_packet_length_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_connection_locals_dict, bleio_connection_locals_dict_table); + +const mp_obj_type_t bleio_connection_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Connection, + .locals_dict = (mp_obj_dict_t *)&bleio_connection_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_generic_unary_op, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/Connection.h b/circuitpython/shared-bindings/_bleio/Connection.h new file mode 100644 index 0000000..a5313a9 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Connection.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_BLEIO_CONNECTION_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CONNECTION_H + +#include "py/objtuple.h" +#include "common-hal/_bleio/Connection.h" +#include "common-hal/_bleio/Service.h" + +extern const mp_obj_type_t bleio_connection_type; + +void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond); +void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self); +bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self); +mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_internal_t *self); +bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self); +mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist); + +mp_float_t common_hal_bleio_connection_get_connection_interval(bleio_connection_internal_t *self); +void common_hal_bleio_connection_set_connection_interval(bleio_connection_internal_t *self, mp_float_t new_interval); + +void bleio_connection_ensure_connected(bleio_connection_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CONNECTION_H diff --git a/circuitpython/shared-bindings/_bleio/Descriptor.c b/circuitpython/shared-bindings/_bleio/Descriptor.c new file mode 100644 index 0000000..f2ef6b4 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Descriptor.c @@ -0,0 +1,216 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class Descriptor: +//| """Stores information about a BLE descriptor. +//| +//| Descriptors are attached to BLE characteristics and provide contextual +//| information about the characteristic.""" +//| +//| def __init__(self) -> None: +//| """There is no regular constructor for a Descriptor. A new local Descriptor can be created +//| and attached to a Characteristic by calling `add_to_characteristic()`. +//| Remote Descriptor objects are created by `Connection.discover_remote_services()` +//| as part of remote Characteristics in the remote Services that are discovered.""" +//| +//| @classmethod +//| def add_to_characteristic(cls, characteristic: Characteristic, uuid: UUID, *, read_perm: int = Attribute.OPEN, write_perm: int = Attribute.OPEN, max_length: int = 20, fixed_length: bool = False, initial_value: ReadableBuffer = b'') -> Descriptor: +//| """Create a new Descriptor object, and add it to this Service. +//| +//| :param Characteristic characteristic: The characteristic that will hold this descriptor +//| :param UUID uuid: The uuid of the descriptor +//| :param int read_perm: Specifies whether the descriptor can be read by a client, and if so, which +//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, +//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. +//| :param int write_perm: Specifies whether the descriptor can be written by a client, and if so, which +//| security mode is required. Values allowed are the same as ``read_perm``. +//| :param int max_length: Maximum length in bytes of the descriptor value. The maximum allowed is +//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum +//| number of data bytes that fit in a single BLE 4.x ATT packet. +//| :param bool fixed_length: True if the descriptor value is of fixed length. +//| :param ~circuitpython_typing.ReadableBuffer initial_value: The initial value for this descriptor. +//| +//| :return: the new Descriptor.""" +//| ... +//| +STATIC mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_characteristic, ARG_uuid, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_read_perm, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_write_perm, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_max_length, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 20} }, + { MP_QSTR_fixed_length, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_initial_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, + }; + + 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); + + bleio_characteristic_obj_t *characteristic = mp_arg_validate_type(args[ARG_characteristic].u_obj, &bleio_characteristic_type, MP_QSTR_characteristic); + + bleio_uuid_obj_t *uuid = mp_arg_validate_type(args[ARG_uuid].u_obj, &bleio_uuid_type, MP_QSTR_uuid); + + const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(read_perm); + + const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(write_perm); + + const mp_int_t max_length_int = args[ARG_max_length].u_int; + if (max_length_int < 0) { + mp_raise_ValueError(translate("max_length must be >= 0")); + } + const size_t max_length = (size_t)max_length_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + if (initial_value_bufinfo.len > max_length || + (fixed_length && initial_value_bufinfo.len != max_length)) { + mp_raise_ValueError(translate("initial_value length is wrong")); + } + + bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); + descriptor->base.type = &bleio_descriptor_type; + + // Range checking on max_length arg is done by the common_hal layer, because + // it may vary depending on underlying BLE implementation. + common_hal_bleio_descriptor_construct( + descriptor, characteristic, uuid, + read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo); + + common_hal_bleio_characteristic_add_descriptor(characteristic, descriptor); + + return MP_OBJ_FROM_PTR(descriptor); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_descriptor_add_to_characteristic_fun_obj, 1, bleio_descriptor_add_to_characteristic); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_descriptor_add_to_characteristic_obj, MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_fun_obj)); + +//| uuid: UUID +//| """The descriptor uuid. (read-only)""" +//| +STATIC mp_obj_t bleio_descriptor_get_uuid(mp_obj_t self_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_descriptor_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_uuid_obj, bleio_descriptor_get_uuid); + +MP_PROPERTY_GETTER(bleio_descriptor_uuid_obj, + (mp_obj_t)&bleio_descriptor_get_uuid_obj); + +//| characteristic: Characteristic +//| """The Characteristic this Descriptor is a part of.""" +//| +STATIC mp_obj_t bleio_descriptor_get_characteristic(mp_obj_t self_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_bleio_descriptor_get_characteristic(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_characteristic_obj, bleio_descriptor_get_characteristic); + +MP_PROPERTY_GETTER(bleio_descriptor_characteristic_obj, + (mp_obj_t)&bleio_descriptor_get_characteristic_obj); + +//| value: bytearray +//| """The value of this descriptor.""" +//| +STATIC mp_obj_t bleio_descriptor_get_value(mp_obj_t self_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uint8_t temp[512]; + size_t actual_len = common_hal_bleio_descriptor_get_value(self, temp, sizeof(temp)); + return mp_obj_new_bytearray(actual_len, temp); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_value_obj, bleio_descriptor_get_value); + +STATIC mp_obj_t bleio_descriptor_set_value(mp_obj_t self_in, mp_obj_t value_in) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value_in, &bufinfo, MP_BUFFER_READ); + + common_hal_bleio_descriptor_set_value(self, &bufinfo); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_descriptor_set_value_obj, bleio_descriptor_set_value); + +MP_PROPERTY_GETSET(bleio_descriptor_value_obj, + (mp_obj_t)&bleio_descriptor_get_value_obj, + (mp_obj_t)&bleio_descriptor_set_value_obj); + +STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_to_characteristic), MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_descriptor_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_characteristic), MP_ROM_PTR(&bleio_descriptor_characteristic_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_descriptor_value_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_descriptor_locals_dict, bleio_descriptor_locals_dict_table); + +STATIC void bleio_descriptor_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Descriptor("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, "<Descriptor with Unregistered UUID>"); + } +} + +const mp_obj_type_t bleio_descriptor_type = { + { &mp_type_type }, + .name = MP_QSTR_Descriptor, + .print = bleio_descriptor_print, + .locals_dict = (mp_obj_dict_t *)&bleio_descriptor_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/Descriptor.h b/circuitpython/shared-bindings/_bleio/Descriptor.h new file mode 100644 index 0000000..bda381e --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Descriptor.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_BLEIO_DESCRIPTOR_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H + +#include "shared-module/_bleio/Attribute.h" +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Descriptor.h" +#include "common-hal/_bleio/UUID.h" + +extern const mp_obj_type_t bleio_descriptor_type; + +extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo); +extern bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self); +extern bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self); +extern size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t *buf, size_t len); +extern void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H diff --git a/circuitpython/shared-bindings/_bleio/PacketBuffer.c b/circuitpython/shared-bindings/_bleio/PacketBuffer.c new file mode 100644 index 0000000..9e64661 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/PacketBuffer.c @@ -0,0 +1,239 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "py/mperrno.h" +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-bindings/util.h" + +//| class PacketBuffer: +//| """Accumulates a Characteristic's incoming packets in a FIFO buffer and facilitates packet aware +//| outgoing writes. A packet's size is either the characteristic length or the maximum transmission +//| unit (MTU) minus overhead, whichever is smaller. The MTU can change so check `incoming_packet_length` +//| and `outgoing_packet_length` before creating a buffer to store data. +//| +//| When we're the server, we ignore all connections besides the first to subscribe to +//| notifications.""" +//| +//| def __init__(self, characteristic: Characteristic, *, buffer_size: int, max_packet_size: Optional[int] = None) -> None: +//| """Monitor the given Characteristic. Each time a new value is written to the Characteristic +//| add the newly-written bytes to a FIFO buffer. +//| +//| Monitor the given Characteristic. Each time a new value is written to the Characteristic +//| add the newly-written packet of bytes to a FIFO buffer. +//| +//| :param Characteristic characteristic: The Characteristic to monitor. +//| It may be a local Characteristic provided by a Peripheral Service, or a remote Characteristic +//| in a remote Service that a Central has connected to. +//| :param int buffer_size: Size of ring buffer (in packets of the Characteristic's maximum +//| length) that stores incoming packets coming from the peer. +//| :param int max_packet_size: Maximum size of packets. Overrides value from the characteristic. +//| (Remote characteristics may not have the correct length.)""" +//| ... +//| +STATIC mp_obj_t bleio_packet_buffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_characteristic, ARG_buffer_size, ARG_max_packet_size }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_max_packet_size, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none}}, + }; + + 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); + + bleio_characteristic_obj_t *characteristic = mp_arg_validate_type(args[ARG_characteristic].u_obj, &bleio_characteristic_type, MP_QSTR_characteristic); + + const mp_int_t buffer_size = args[ARG_buffer_size].u_int; + if (buffer_size < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_buffer_size); + } + + size_t max_packet_size = common_hal_bleio_characteristic_get_max_length(characteristic); + if (args[ARG_max_packet_size].u_obj != mp_const_none) { + max_packet_size = mp_obj_get_int(args[ARG_max_packet_size].u_obj); + } + + bleio_packet_buffer_obj_t *self = m_new_obj(bleio_packet_buffer_obj_t); + self->base.type = &bleio_packet_buffer_type; + + common_hal_bleio_packet_buffer_construct(self, characteristic, buffer_size, max_packet_size); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void check_for_deinit(bleio_packet_buffer_obj_t *self) { + if (common_hal_bleio_packet_buffer_deinited(self)) { + raise_deinited_error(); + } +} + +//| def readinto(self, buf: WriteableBuffer) -> int: +//| """Reads a single BLE packet into the ``buf``. Raises an exception if the next packet is longer +//| than the given buffer. Use `incoming_packet_length` to read the maximum length of a single packet. +//| +//| :return: number of bytes read and stored into ``buf`` +//| :rtype: int""" +//| ... +//| +STATIC mp_obj_t bleio_packet_buffer_readinto(mp_obj_t self_in, mp_obj_t buffer_obj) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buffer_obj, &bufinfo, MP_BUFFER_WRITE); + + mp_int_t size = common_hal_bleio_packet_buffer_readinto(self, bufinfo.buf, bufinfo.len); + if (size < 0) { + mp_raise_ValueError_varg(translate("Buffer too short by %d bytes"), size * -1); + } + + return MP_OBJ_NEW_SMALL_INT(size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bleio_packet_buffer_readinto_obj, bleio_packet_buffer_readinto); + +//| def write(self, data: ReadableBuffer, *, header: Optional[bytes] = None) -> int: +//| """Writes all bytes from data into the same outgoing packet. The bytes from header are included +//| before data when the pending packet is currently empty. +//| +//| This does not block until the data is sent. It only blocks until the data is pending. +//| +//| :return: number of bytes written. May include header bytes when packet is empty. +//| :rtype: int""" +//| ... +//| +// TODO: Add a kwarg `merge=False` to dictate whether subsequent writes are merged into a pending +// one. +STATIC mp_obj_t bleio_packet_buffer_write(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_data, ARG_header }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_header, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none}}, + }; + + 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); + + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + mp_buffer_info_t data_bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); + + mp_buffer_info_t header_bufinfo; + header_bufinfo.len = 0; + if (args[ARG_header].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_header].u_obj, &header_bufinfo, MP_BUFFER_READ); + } + + mp_int_t num_bytes_written = common_hal_bleio_packet_buffer_write( + self, data_bufinfo.buf, data_bufinfo.len, header_bufinfo.buf, header_bufinfo.len); + if (num_bytes_written < 0) { + // TODO: Raise an error if not connected. Right now the not-connected error + // is unreliable, because common_hal_bleio_packet_buffer_write() + // checks for conn_handle being set, but setting that + // can be delayed because conn_handle is discovered by spying on + // gatts write events, which may not have been sent yet. + // + // IDEAL: + // mp_raise_ConnectionError(translate("Not connected")); + // TEMPORARY: + num_bytes_written = 0; + } + return MP_OBJ_NEW_SMALL_INT(num_bytes_written); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_packet_buffer_write_obj, 1, bleio_packet_buffer_write); + +//| def deinit(self) -> None: +//| """Disable permanently.""" +//| ... +STATIC mp_obj_t bleio_packet_buffer_deinit(mp_obj_t self_in) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_bleio_packet_buffer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_packet_buffer_deinit_obj, bleio_packet_buffer_deinit); + +//| incoming_packet_length: int +//| """Maximum length in bytes of a packet we are reading.""" +//| +STATIC mp_obj_t bleio_packet_buffer_get_incoming_packet_length(mp_obj_t self_in) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t size = common_hal_bleio_packet_buffer_get_incoming_packet_length(self); + if (size < 0) { + mp_raise_ValueError(translate("No connection: length cannot be determined")); + } + return MP_OBJ_NEW_SMALL_INT(size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_packet_buffer_get_incoming_packet_length_obj, bleio_packet_buffer_get_incoming_packet_length); + +MP_PROPERTY_GETTER(bleio_packet_buffer_incoming_packet_length_obj, + (mp_obj_t)&bleio_packet_buffer_get_incoming_packet_length_obj); + +//| outgoing_packet_length: int +//| """Maximum length in bytes of a packet we are writing.""" +//| +STATIC mp_obj_t bleio_packet_buffer_get_outgoing_packet_length(mp_obj_t self_in) { + bleio_packet_buffer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t size = common_hal_bleio_packet_buffer_get_outgoing_packet_length(self); + if (size < 0) { + mp_raise_ValueError(translate("No connection: length cannot be determined")); + } + return MP_OBJ_NEW_SMALL_INT(size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_packet_buffer_get_outgoing_packet_length_obj, bleio_packet_buffer_get_outgoing_packet_length); + +MP_PROPERTY_GETTER(bleio_packet_buffer_outgoing_packet_length_obj, + (mp_obj_t)&bleio_packet_buffer_get_outgoing_packet_length_obj); + +STATIC const mp_rom_map_elem_t bleio_packet_buffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bleio_packet_buffer_deinit_obj) }, + + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bleio_packet_buffer_readinto_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&bleio_packet_buffer_write_obj) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_incoming_packet_length), MP_ROM_PTR(&bleio_packet_buffer_incoming_packet_length_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_outgoing_packet_length), MP_ROM_PTR(&bleio_packet_buffer_outgoing_packet_length_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_packet_buffer_locals_dict, bleio_packet_buffer_locals_dict_table); + + +const mp_obj_type_t bleio_packet_buffer_type = { + { &mp_type_type }, + .name = MP_QSTR_PacketBuffer, + .make_new = bleio_packet_buffer_make_new, + .locals_dict = (mp_obj_dict_t *)&bleio_packet_buffer_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/PacketBuffer.h b/circuitpython/shared-bindings/_bleio/PacketBuffer.h new file mode 100644 index 0000000..adead29 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/PacketBuffer.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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_BLEIO_PACKETBUFFER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PACKETBUFFER_H + +#include "common-hal/_bleio/PacketBuffer.h" + +extern const mp_obj_type_t bleio_packet_buffer_type; + +void common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + size_t buffer_size, size_t max_packet_size); +// Allocation free +void _common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + uint32_t *incoming_buffer, size_t incoming_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t outgoing_buffer_size, + void *static_handler_entry); +mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len); +mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len); +mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self); +mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_buffer_obj_t *self); +void common_hal_bleio_packet_buffer_flush(bleio_packet_buffer_obj_t *self); +bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self); +void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PACKETBUFFER_H diff --git a/circuitpython/shared-bindings/_bleio/ScanEntry.c b/circuitpython/shared-bindings/_bleio/ScanEntry.c new file mode 100644 index 0000000..d9434f3 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanEntry.c @@ -0,0 +1,152 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 <string.h> + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-module/_bleio/ScanEntry.h" + +//| class ScanEntry: +//| """Encapsulates information about a device that was received during scanning. It can be +//| advertisement or scan response data. This object may only be created by a `_bleio.ScanResults`: +//| it has no user-visible constructor.""" +//| + +//| def __init__(self) -> None: +//| """Cannot be instantiated directly. Use `_bleio.Adapter.start_scan`.""" +//| ... +//| +//| def matches(self, prefixes: ScanEntry, *, match_all: bool = True) -> bool: +//| """Returns True if the ScanEntry matches all prefixes when ``match_all`` is True. This is stricter +//| than the scan filtering which accepts any advertisements that match any of the prefixes +//| where ``match_all`` is False. +//| +//| ``all`` also works for ``match_all`` but will be removed in CircuitPython 8.""" +//| ... +//| +STATIC mp_obj_t bleio_scanentry_matches(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_prefixes, ARG_all, ARG_match_all }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_prefixes, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_all, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_match_all, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + }; + + 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); + + bool match_all = args[ARG_all].u_bool && args[ARG_match_all].u_bool; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_prefixes].u_obj, &bufinfo, MP_BUFFER_READ); + return mp_obj_new_bool(common_hal_bleio_scanentry_matches(self, bufinfo.buf, bufinfo.len, match_all)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_scanentry_matches_obj, 1, bleio_scanentry_matches); + +//| address: Address +//| """The address of the device (read-only), of type `_bleio.Address`.""" +//| +STATIC mp_obj_t bleio_scanentry_get_address(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_bleio_scanentry_get_address(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_address_obj, bleio_scanentry_get_address); + +MP_PROPERTY_GETTER(bleio_scanentry_address_obj, + (mp_obj_t)&bleio_scanentry_get_address_obj); + +//| advertisement_bytes: bytes +//| """All the advertisement data present in the packet, returned as a ``bytes`` object. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_advertisement_bytes(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_bleio_scanentry_get_advertisement_bytes(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_advertisement_bytes_obj, scanentry_get_advertisement_bytes); + +MP_PROPERTY_GETTER(bleio_scanentry_advertisement_bytes_obj, + (mp_obj_t)&bleio_scanentry_get_advertisement_bytes_obj); + +//| rssi: int +//| """The signal strength of the device at the time of the scan, in integer dBm. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_rssi(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_bleio_scanentry_get_rssi(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_rssi_obj, scanentry_get_rssi); + +MP_PROPERTY_GETTER(bleio_scanentry_rssi_obj, + (mp_obj_t)&bleio_scanentry_get_rssi_obj); + +//| connectable: bool +//| """True if the device can be connected to. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_connectable(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_bleio_scanentry_get_connectable(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_connectable_obj, scanentry_get_connectable); + +MP_PROPERTY_GETTER(bleio_scanentry_connectable_obj, + (mp_obj_t)&bleio_scanentry_get_connectable_obj); + +//| scan_response: bool +//| """True if the entry was a scan response. (read-only)""" +//| +STATIC mp_obj_t scanentry_get_scan_response(mp_obj_t self_in) { + bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_bleio_scanentry_get_scan_response(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_scan_response_obj, scanentry_get_scan_response); + +MP_PROPERTY_GETTER(bleio_scanentry_scan_response_obj, + (mp_obj_t)&bleio_scanentry_get_scan_response_obj); + +STATIC const mp_rom_map_elem_t bleio_scanentry_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_scanentry_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_advertisement_bytes), MP_ROM_PTR(&bleio_scanentry_advertisement_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&bleio_scanentry_rssi_obj) }, + { MP_ROM_QSTR(MP_QSTR_connectable), MP_ROM_PTR(&bleio_scanentry_connectable_obj) }, + { MP_ROM_QSTR(MP_QSTR_scan_response), MP_ROM_PTR(&bleio_scanentry_scan_response_obj) }, + { MP_ROM_QSTR(MP_QSTR_matches), MP_ROM_PTR(&bleio_scanentry_matches_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_scanentry_locals_dict, bleio_scanentry_locals_dict_table); + +const mp_obj_type_t bleio_scanentry_type = { + { &mp_type_type }, + .name = MP_QSTR_ScanEntry, + .locals_dict = (mp_obj_dict_t *)&bleio_scanentry_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/ScanEntry.h b/circuitpython/shared-bindings/_bleio/ScanEntry.h new file mode 100644 index 0000000..b376433 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanEntry.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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_BLEIO_SCANENTRY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H + +#include "py/obj.h" +#include "shared-module/_bleio/ScanEntry.h" + +extern const mp_obj_type_t bleio_scanentry_type; + +mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self); +mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self); +mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self); +bool common_hal_bleio_scanentry_get_connectable(bleio_scanentry_obj_t *self); +bool common_hal_bleio_scanentry_get_scan_response(bleio_scanentry_obj_t *self); +bool common_hal_bleio_scanentry_matches(bleio_scanentry_obj_t *self, const uint8_t *prefixes, size_t prefixes_len, bool match_all); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H diff --git a/circuitpython/shared-bindings/_bleio/ScanResults.c b/circuitpython/shared-bindings/_bleio/ScanResults.c new file mode 100644 index 0000000..bee6cfa --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanResults.c @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 <string.h> + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/ScanResults.h" + +//| class ScanResults: +//| """Iterates over advertising data received while scanning. This object is always created +//| by a `_bleio.Adapter`: it has no user-visible constructor.""" +//| +STATIC mp_obj_t scanresults_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &bleio_scanresults_type)); + bleio_scanresults_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t scan_entry = common_hal_bleio_scanresults_next(self); + if (scan_entry != mp_const_none) { + return scan_entry; + } + return MP_OBJ_STOP_ITERATION; +} + +//| def __init__(self) -> None: +//| """Cannot be instantiated directly. Use `_bleio.Adapter.start_scan`.""" +//| ... +//| +//| def __iter__(self) -> Iterator[ScanEntry]: +//| """Returns itself since it is the iterator.""" +//| ... +//| +//| def __next__(self) -> ScanEntry: +//| """Returns the next `_bleio.ScanEntry`. Blocks if none have been received and scanning is still +//| active. Raises `StopIteration` if scanning is finished and no other results are available.""" +//| ... +//| + +const mp_obj_type_t bleio_scanresults_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_ScanResults, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = scanresults_iternext, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/ScanResults.h b/circuitpython/shared-bindings/_bleio/ScanResults.h new file mode 100644 index 0000000..a8c88c2 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/ScanResults.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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_BLEIO_SCANRESULTS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H + +#include "py/obj.h" +#include "shared-module/_bleio/ScanResults.h" + +extern const mp_obj_type_t bleio_scanresults_type; + +mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H diff --git a/circuitpython/shared-bindings/_bleio/Service.c b/circuitpython/shared-bindings/_bleio/Service.c new file mode 100644 index 0000000..ddbdecd --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Service.c @@ -0,0 +1,154 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class Service: +//| """Stores information about a BLE service and its characteristics.""" +//| +//| def __init__(self, uuid: UUID, *, secondary: bool = False) -> None: +//| """Create a new Service identified by the specified UUID. It can be accessed by all +//| connections. This is known as a Service server. Client Service objects are created via +//| `Connection.discover_remote_services`. +//| +//| To mark the Service as secondary, pass `True` as :py:data:`secondary`. +//| +//| :param UUID uuid: The uuid of the service +//| :param bool secondary: If the service is a secondary one +//| +//| :return: the new Service""" +//| ... +//| +STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_uuid, ARG_secondary }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_secondary, 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); + + bleio_uuid_obj_t *uuid = mp_arg_validate_type(args[ARG_uuid].u_obj, &bleio_uuid_type, MP_QSTR_uuid); + + const bool is_secondary = args[ARG_secondary].u_bool; + + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + + common_hal_bleio_service_construct(service, uuid, is_secondary); + + return MP_OBJ_FROM_PTR(service); +} + +//| characteristics: Tuple[Characteristic, ...] +//| """A tuple of :py:class:`Characteristic` designating the characteristics that are offered by +//| this service. (read-only)""" +//| +STATIC mp_obj_t bleio_service_get_characteristics(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(common_hal_bleio_service_get_characteristics(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_characteristics_obj, bleio_service_get_characteristics); + +MP_PROPERTY_GETTER(bleio_service_characteristics_obj, + (mp_obj_t)&bleio_service_get_characteristics_obj); + +//| remote: bool +//| """True if this is a service provided by a remote device. (read-only)""" +//| +STATIC mp_obj_t bleio_service_get_remote(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_service_get_is_remote(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_remote_obj, bleio_service_get_remote); + +MP_PROPERTY_GETTER(bleio_service_remote_obj, + (mp_obj_t)&bleio_service_get_remote_obj); + +//| secondary: bool +//| """True if this is a secondary service. (read-only)""" +//| +STATIC mp_obj_t bleio_service_get_secondary(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_bleio_service_get_is_secondary(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_secondary_obj, bleio_service_get_secondary); + +MP_PROPERTY_GETTER(bleio_service_secondary_obj, + (mp_obj_t)&bleio_service_get_secondary_obj); + +//| uuid: Optional[UUID] +//| """The UUID of this service. (read-only) +//| +//| Will be ``None`` if the 128-bit UUID for this service is not known.""" +//| +STATIC mp_obj_t bleio_service_get_uuid(mp_obj_t self_in) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + + bleio_uuid_obj_t *uuid = common_hal_bleio_service_get_uuid(self); + return uuid ? MP_OBJ_FROM_PTR(uuid) : mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_service_get_uuid_obj, bleio_service_get_uuid); + +MP_PROPERTY_GETTER(bleio_service_uuid_obj, + (mp_obj_t)&bleio_service_get_uuid_obj); + + +STATIC const mp_rom_map_elem_t bleio_service_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) }, + { MP_ROM_QSTR(MP_QSTR_secondary), MP_ROM_PTR(&bleio_service_secondary_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) }, + { MP_ROM_QSTR(MP_QSTR_remote), MP_ROM_PTR(&bleio_service_remote_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict_table); + +STATIC void bleio_service_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->uuid) { + mp_printf(print, "Service("); + bleio_uuid_print(print, MP_OBJ_FROM_PTR(self->uuid), kind); + mp_printf(print, ")"); + } else { + mp_printf(print, "<Service with unregistered UUID>"); + } +} + +const mp_obj_type_t bleio_service_type = { + { &mp_type_type }, + .name = MP_QSTR_Service, + .make_new = bleio_service_make_new, + .print = bleio_service_print, + .locals_dict = (mp_obj_dict_t *)&bleio_service_locals_dict +}; diff --git a/circuitpython/shared-bindings/_bleio/Service.h b/circuitpython/shared-bindings/_bleio/Service.h new file mode 100644 index 0000000..eb1d29b --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/Service.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_BLEIO_SERVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H + +#include "common-hal/_bleio/Characteristic.h" +#include "common-hal/_bleio/Connection.h" +#include "common-hal/_bleio/Service.h" + +#include "py/objtuple.h" + +extern const mp_obj_type_t bleio_service_type; + +// Private version that doesn't allocate on the heap +extern uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t *characteristic_list); +extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary); +extern void common_hal_bleio_service_from_remote_service(bleio_service_obj_t *self, bleio_connection_obj_t *connection, bleio_uuid_obj_t *uuid, bool is_secondary); +extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self); +extern mp_obj_tuple_t *common_hal_bleio_service_get_characteristics(bleio_service_obj_t *self); +extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self); +extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self); +extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo, const char *user_description); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H diff --git a/circuitpython/shared-bindings/_bleio/UUID.c b/circuitpython/shared-bindings/_bleio/UUID.c new file mode 100644 index 0000000..599a89a --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/UUID.c @@ -0,0 +1,300 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 <string.h> + +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/UUID.h" + +//| class UUID: +//| """A 16-bit or 128-bit UUID. Can be used for services, characteristics, descriptors and more.""" +//| +//| def __init__(self, value: Union[int, ReadableBuffer, str]) -> None: +//| """Create a new UUID or UUID object encapsulating the uuid value. +//| The value can be one of: +//| +//| - an `int` value in range 0 to 0xFFFF (Bluetooth SIG 16-bit UUID) +//| - a buffer object (bytearray, bytes) of 16 bytes in little-endian order (128-bit UUID) +//| - a string of hex digits of the form 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' +//| +//| Creating a 128-bit UUID registers the UUID with the onboard BLE software, and provides a +//| temporary 16-bit UUID that can be used in place of the full 128-bit UUID. +//| +//| :param value: The uuid value to encapsulate +//| :type value: int, ~circuitpython_typing.ReadableBuffer or str""" +//| ... +//| +STATIC mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + bleio_uuid_obj_t *self = m_new_obj(bleio_uuid_obj_t); + self->base.type = type; + + const mp_obj_t value = all_args[0]; + uint8_t uuid128[16]; + + if (mp_obj_is_int(value)) { + mp_int_t uuid16 = mp_obj_get_int(value); + if (uuid16 < 0 || uuid16 > 0xffff) { + mp_raise_ValueError(translate("UUID integer value must be 0-0xffff")); + } + + // NULL means no 128-bit value. + common_hal_bleio_uuid_construct(self, uuid16, NULL); + + } else { + if (mp_obj_is_str(value)) { + // 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + GET_STR_DATA_LEN(value, chars, len); + char hex[32]; + // Validate length, hyphens, and hex digits. + bool good_uuid = + len == 36 && chars[8] == '-' && chars[13] == '-' && chars[18] == '-' && chars[23] == '-'; + if (good_uuid) { + size_t hex_idx = 0; + for (size_t i = 0; i < len; i++) { + if (unichar_isxdigit(chars[i])) { + hex[hex_idx] = chars[i]; + hex_idx++; + } + } + good_uuid = hex_idx == 32; + } + if (!good_uuid) { + mp_raise_ValueError(translate("UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'")); + } + + size_t hex_idx = 0; + for (int i = 15; i >= 0; i--) { + uuid128[i] = (unichar_xdigit_value(hex[hex_idx]) << 4) | unichar_xdigit_value(hex[hex_idx + 1]); + hex_idx += 2; + } + } else { + // Last possibility is that it's a buf. + mp_buffer_info_t bufinfo; + if (!mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) { + mp_raise_ValueError(translate("UUID value is not str, int or byte buffer")); + } + + if (bufinfo.len != 16) { + mp_raise_ValueError(translate("Byte buffer must be 16 bytes.")); + } + + memcpy(uuid128, bufinfo.buf, 16); + } + + // Str and bytes both get constructed the same way here. + uint32_t uuid16 = (uuid128[13] << 8) | uuid128[12]; + uuid128[12] = 0; + uuid128[13] = 0; + common_hal_bleio_uuid_construct(self, uuid16, uuid128); + } + + return MP_OBJ_FROM_PTR(self); +} + +//| uuid16: int +//| """The 16-bit part of the UUID. (read-only) +//| +//| :type: int""" +//| +STATIC mp_obj_t bleio_uuid_get_uuid16(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid16_obj, bleio_uuid_get_uuid16); + +MP_PROPERTY_GETTER(bleio_uuid_uuid16_obj, + (mp_obj_t)&bleio_uuid_get_uuid16_obj); + +//| uuid128: bytes +//| """The 128-bit value of the UUID +//| Raises AttributeError if this is a 16-bit UUID. (read-only) +//| +//| :type: bytes""" +//| +STATIC mp_obj_t bleio_uuid_get_uuid128(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uint8_t uuid128[16]; + if (common_hal_bleio_uuid_get_size(self) != 128) { + mp_raise_AttributeError(translate("not a 128-bit UUID")); + } + common_hal_bleio_uuid_get_uuid128(self, uuid128); + return mp_obj_new_bytes(uuid128, 16); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_uuid128_obj, bleio_uuid_get_uuid128); + +MP_PROPERTY_GETTER(bleio_uuid_uuid128_obj, + (mp_obj_t)&bleio_uuid_get_uuid128_obj); + +//| size: int +//| """128 if this UUID represents a 128-bit vendor-specific UUID. 16 if this UUID represents a +//| 16-bit Bluetooth SIG assigned UUID. (read-only) 32-bit UUIDs are not currently supported. +//| +//| :type: int""" +//| +STATIC mp_obj_t bleio_uuid_get_size(mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_size(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(bleio_uuid_get_size_obj, bleio_uuid_get_size); + +MP_PROPERTY_GETTER(bleio_uuid_size_obj, + (mp_obj_t)&bleio_uuid_get_size_obj); + + +//| def pack_into(self, buffer: WriteableBuffer, offset: int = 0) -> None: +//| """Packs the UUID into the given buffer at the given offset.""" +//| ... +//| +STATIC mp_obj_t bleio_uuid_pack_into(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_buffer, ARG_offset }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + 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 offset = args[ARG_offset].u_int; + if (offset + common_hal_bleio_uuid_get_size(self) / 8 > bufinfo.len) { + mp_raise_ValueError(translate("Buffer + offset too small %d %d %d")); + } + + common_hal_bleio_uuid_pack_into(self, bufinfo.buf + offset); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_uuid_pack_into_obj, 1, bleio_uuid_pack_into); + +STATIC const mp_rom_map_elem_t bleio_uuid_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_uuid16), MP_ROM_PTR(&bleio_uuid_uuid16_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid128), MP_ROM_PTR(&bleio_uuid_uuid128_obj) }, + { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&bleio_uuid_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&bleio_uuid_pack_into_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bleio_uuid_locals_dict, bleio_uuid_locals_dict_table); + +STATIC mp_obj_t bleio_uuid_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_HASH: + if (common_hal_bleio_uuid_get_size(self) == 16) { + return MP_OBJ_NEW_SMALL_INT(common_hal_bleio_uuid_get_uuid16(self)); + } else { + union { + uint8_t uuid128_bytes[16]; + uint16_t uuid128_uint16[8]; + } uuid128; + common_hal_bleio_uuid_get_uuid128(self, uuid128.uuid128_bytes); + int hash = 0; + for (size_t i = 0; i < MP_ARRAY_SIZE(uuid128.uuid128_uint16); i++) { + hash += uuid128.uuid128_uint16[i]; + } + return MP_OBJ_NEW_SMALL_INT(hash); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __eq__(self, other: object) -> bool: +//| """Two UUID objects are equal if their values match and they are both 128-bit or both 16-bit.""" +//| ... +//| +STATIC mp_obj_t bleio_uuid_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + // Two UUID's are equal if their uuid16 values match or their uuid128 values match. + case MP_BINARY_OP_EQUAL: + if (mp_obj_is_type(rhs_in, &bleio_uuid_type)) { + if (common_hal_bleio_uuid_get_size(lhs_in) == 16 && + common_hal_bleio_uuid_get_size(rhs_in) == 16) { + return mp_obj_new_bool(common_hal_bleio_uuid_get_uuid16(lhs_in) == + common_hal_bleio_uuid_get_uuid16(rhs_in)); + } + uint8_t lhs[16]; + uint8_t rhs[16]; + common_hal_bleio_uuid_get_uuid128(lhs_in, lhs); + common_hal_bleio_uuid_get_uuid128(rhs_in, rhs); + return mp_obj_new_bool(memcmp(lhs, rhs, sizeof(lhs)) == 0); + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t size = common_hal_bleio_uuid_get_size(self); + if (size == 16) { + mp_printf(print, "UUID(0x%04x)", common_hal_bleio_uuid_get_uuid16(self)); + } else { + uint8_t uuid128[16]; + common_hal_bleio_uuid_get_uuid128(self, uuid128); + mp_printf(print, "UUID('" + "%02x%02x%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x%02x%02x%02x%02x')", + uuid128[15], uuid128[14], uuid128[13], uuid128[12], + uuid128[11], uuid128[10], + uuid128[9], uuid128[8], + uuid128[7], uuid128[6], + uuid128[5], uuid128[4], uuid128[3], uuid128[2], uuid128[1], uuid128[0]); + } +} + +const mp_obj_type_t bleio_uuid_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_UUID, + .print = bleio_uuid_print, + .make_new = bleio_uuid_make_new, + .locals_dict = (mp_obj_dict_t *)&bleio_uuid_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = bleio_uuid_unary_op, + .binary_op = bleio_uuid_binary_op, + ), +}; diff --git a/circuitpython/shared-bindings/_bleio/UUID.h b/circuitpython/shared-bindings/_bleio/UUID.h new file mode 100644 index 0000000..d07e903 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/UUID.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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_BLEIO_UUID_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H + +#include "common-hal/_bleio/UUID.h" + +void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); + +extern const mp_obj_type_t bleio_uuid_type; + +extern void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, const uint8_t uuid128[16]); +extern uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self); +extern void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]); +extern uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self); + +void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t *buf); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H diff --git a/circuitpython/shared-bindings/_bleio/__init__.c b/circuitpython/shared-bindings/_bleio/__init__.c new file mode 100644 index 0000000..72d9684 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/__init__.c @@ -0,0 +1,205 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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 <stdarg.h> + +#include "py/objexcept.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/ScanEntry.h" +#include "shared-bindings/_bleio/ScanResults.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" + +//| """Bluetooth Low Energy (BLE) communication +//| +//| The `_bleio` module provides necessary low-level functionality for communicating +//| using Bluetooth Low Energy (BLE). The '_' prefix indicates this module is meant +//| for internal use by libraries but not by the end user. Its API may change incompatibly +//| between minor versions of CircuitPython. +//| Please use the +//| `adafruit_ble <https://circuitpython.readthedocs.io/projects/ble/en/latest/>`_ +//| CircuitPython library instead, which builds on `_bleio`, and +//| provides higher-level convenience functionality, including predefined beacons, clients, +//| servers.""" +//| + +//| adapter: Adapter +//| """BLE Adapter used to manage device discovery and connections. +//| This object is the sole instance of `_bleio.Adapter`.""" +//| + +//| class BluetoothError(Exception): +//| """Catchall exception for Bluetooth related errors.""" +//| ... +MP_DEFINE_BLEIO_EXCEPTION(BluetoothError, Exception) +NORETURN void mp_raise_bleio_BluetoothError(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_BluetoothError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +//| class RoleError(BluetoothError): +//| """Raised when a resource is used as the mismatched role. For example, if a local CCCD is +//| attempted to be set but they can only be set when remote.""" +//| ... +//| +MP_DEFINE_BLEIO_EXCEPTION(RoleError, bleio_BluetoothError) +NORETURN void mp_raise_bleio_RoleError(const compressed_string_t *msg) { + mp_raise_msg(&mp_type_bleio_RoleError, msg); +} + +//| class SecurityError(BluetoothError): +//| """Raised when a security related error occurs.""" +//| ... +//| +MP_DEFINE_BLEIO_EXCEPTION(SecurityError, bleio_BluetoothError) +NORETURN void mp_raise_bleio_SecurityError(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_SecurityError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +// Called when _bleio is imported. +STATIC mp_obj_t bleio___init__(void) { +// HCI cannot be enabled on import, because we need to setup the HCI adapter first. + #if !CIRCUITPY_BLEIO_HCI + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true); + #endif + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(bleio___init___obj, bleio___init__); + + +// Need a forward reference due to mutual references. +#if CIRCUITPY_BLEIO_HCI +STATIC mp_obj_dict_t bleio_module_globals; +#endif + +//| def set_adapter(adapter: Optional[_bleio.Adapter]) -> None: +//| """Set the adapter to use for BLE, such as when using an HCI adapter. +//| Raises `NotImplementedError` when the adapter is a singleton and cannot be set.""" +//| ... +//| +mp_obj_t bleio_set_adapter(mp_obj_t adapter_obj) { + #if CIRCUITPY_BLEIO_HCI + if (adapter_obj != mp_const_none && !mp_obj_is_type(adapter_obj, &bleio_adapter_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), bleio_adapter_type.name); + } + + // Equivalent of: + // bleio.adapter = adapter_obj + mp_map_elem_t *elem = mp_map_lookup(&bleio_module_globals.map, MP_ROM_QSTR(MP_QSTR_adapter), MP_MAP_LOOKUP); + if (elem) { + elem->value = adapter_obj; + } + #else + mp_raise_NotImplementedError(translate("Not settable")); + #endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bleio_set_adapter_obj, bleio_set_adapter); + +#if CIRCUITPY_BLEIO_HCI +// Make the module dictionary be in RAM, so that _bleio.adapter can be set. +// Use a local macro to define how table entries should be converted. +#define OBJ_FROM_PTR MP_OBJ_FROM_PTR +STATIC mp_map_elem_t bleio_module_globals_table[] = { +#else +#define OBJ_FROM_PTR MP_ROM_PTR +STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = { + #endif + // Name must be the first entry so that the exception printing below is correct. + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__bleio) }, + { MP_ROM_QSTR(MP_QSTR_Adapter), OBJ_FROM_PTR(&bleio_adapter_type) }, + { MP_ROM_QSTR(MP_QSTR_Address), OBJ_FROM_PTR(&bleio_address_type) }, + { MP_ROM_QSTR(MP_QSTR_Attribute), OBJ_FROM_PTR(&bleio_attribute_type) }, + { MP_ROM_QSTR(MP_QSTR_Connection), OBJ_FROM_PTR(&bleio_connection_type) }, + { MP_ROM_QSTR(MP_QSTR_Characteristic), OBJ_FROM_PTR(&bleio_characteristic_type) }, + { MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), OBJ_FROM_PTR(&bleio_characteristic_buffer_type) }, + { MP_ROM_QSTR(MP_QSTR_Descriptor), OBJ_FROM_PTR(&bleio_descriptor_type) }, + { MP_ROM_QSTR(MP_QSTR_PacketBuffer), OBJ_FROM_PTR(&bleio_packet_buffer_type) }, + { MP_ROM_QSTR(MP_QSTR_ScanEntry), OBJ_FROM_PTR(&bleio_scanentry_type) }, + { MP_ROM_QSTR(MP_QSTR_ScanResults), OBJ_FROM_PTR(&bleio_scanresults_type) }, + { MP_ROM_QSTR(MP_QSTR_Service), OBJ_FROM_PTR(&bleio_service_type) }, + { MP_ROM_QSTR(MP_QSTR_UUID), OBJ_FROM_PTR(&bleio_uuid_type) }, + + #if CIRCUITPY_BLEIO_HCI + // For HCI, _bleio.adapter is settable, and starts as None. + { MP_ROM_QSTR(MP_QSTR_adapter), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_set_adapter), (mp_obj_t)&bleio_set_adapter_obj }, + #else + // For non-HCI _bleio.adapter is a fixed singleton, and is not settable. + // _bleio.set_adapter will raise NotImplementedError. + { MP_ROM_QSTR(MP_QSTR_adapter), MP_ROM_PTR(&common_hal_bleio_adapter_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_adapter), MP_ROM_PTR(&bleio_set_adapter_obj) }, + #endif + + // Errors + { MP_ROM_QSTR(MP_QSTR_BluetoothError), OBJ_FROM_PTR(&mp_type_bleio_BluetoothError) }, + { MP_ROM_QSTR(MP_QSTR_RoleError), OBJ_FROM_PTR(&mp_type_bleio_RoleError) }, + { MP_ROM_QSTR(MP_QSTR_SecurityError), OBJ_FROM_PTR(&mp_type_bleio_SecurityError) }, + + // Initialization + { MP_ROM_QSTR(MP_QSTR___init__), OBJ_FROM_PTR(&bleio___init___obj) }, +}; + +#if CIRCUITPY_BLEIO_HCI +// Module dict is mutable to allow setting _bleio.adapter. +STATIC MP_DEFINE_MUTABLE_DICT(bleio_module_globals, bleio_module_globals_table); +#else +STATIC MP_DEFINE_CONST_DICT(bleio_module_globals, bleio_module_globals_table); +#endif + +void bleio_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; + bool is_subclass = kind & PRINT_EXC_SUBCLASS; + if (!is_subclass && (k == PRINT_EXC)) { + mp_print_str(print, qstr_str(MP_OBJ_QSTR_VALUE(bleio_module_globals_table[0].value))); + mp_print_str(print, "."); + } + mp_obj_exception_print(print, o_in, kind); +} + +const mp_obj_module_t bleio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&bleio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR__bleio, bleio_module, CIRCUITPY_BLEIO); diff --git a/circuitpython/shared-bindings/_bleio/__init__.h b/circuitpython/shared-bindings/_bleio/__init__.h new file mode 100644 index 0000000..f34df30 --- /dev/null +++ b/circuitpython/shared-bindings/_bleio/__init__.h @@ -0,0 +1,83 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2016 Glenn Ruben Bakke + * + * 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_BLEIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H + +#include "py/objlist.h" + +#include "shared-bindings/_bleio/Adapter.h" + +#include "common-hal/_bleio/__init__.h" +#include "common-hal/_bleio/Adapter.h" + +extern bleio_adapter_obj_t common_hal_bleio_adapter_obj; + +void bleio_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); + +#define MP_DEFINE_BLEIO_EXCEPTION(exc_name, base_name) \ + const mp_obj_type_t mp_type_bleio_##exc_name = { \ + { &mp_type_type }, \ + .name = MP_QSTR_##exc_name, \ + .print = bleio_exception_print, \ + .make_new = mp_obj_exception_make_new, \ + .attr = mp_obj_exception_attr, \ + .parent = &mp_type_##base_name, \ + }; + +extern const mp_obj_type_t mp_type_bleio_BluetoothError; +extern const mp_obj_type_t mp_type_bleio_RoleError; +extern const mp_obj_type_t mp_type_bleio_SecurityError; + +// Resets all user created BLE state in preparation for the heap disappearing. +// It will maintain BLE workflow and connections. +void bleio_user_reset(void); + +// Completely resets the BLE stack including BLE connections. +void bleio_reset(void); + +extern mp_obj_t bleio_set_adapter(mp_obj_t adapter_obj); + +NORETURN void mp_raise_bleio_BluetoothError(const compressed_string_t *msg, ...); +NORETURN void mp_raise_bleio_RoleError(const compressed_string_t *msg); +NORETURN void mp_raise_bleio_SecurityError(const compressed_string_t *msg, ...); + +bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void); +void common_hal_bleio_check_connected(uint16_t conn_handle); + +uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); +void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); + +size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); +size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); + +void common_hal_bleio_gc_collect(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H diff --git a/circuitpython/shared-bindings/_eve/__init__.c b/circuitpython/shared-bindings/_eve/__init__.c new file mode 100644 index 0000000..13e51cb --- /dev/null +++ b/circuitpython/shared-bindings/_eve/__init__.c @@ -0,0 +1,1109 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 James Bowman for Excamera Labs + * + * 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 <stdio.h> +#include <assert.h> +#include <string.h> + +#include "py/runtime.h" +#include "py/binary.h" + +#include "shared-module/_eve/__init__.h" +#include "shared-bindings/_eve/__init__.h" + +//| """Low-level BridgeTek EVE bindings +//| +//| The `_eve` module provides a class _EVE which +//| contains methods for constructing EVE command +//| buffers and appending basic graphics commands.""" +//| + +//| class _EVE: +//| + +typedef struct _mp_obj__EVE_t { + mp_obj_base_t base; + common_hal__eve_t _eve; +} mp_obj__EVE_t; + +STATIC const mp_obj_type_t _EVE_type; + +#define EVEHAL(s) \ + (&((mp_obj__EVE_t *)mp_obj_cast_to_native_base((s), &_EVE_type))->_eve) + +//| def register(self, o: object) -> None: +//| ... +//| +STATIC mp_obj_t _register(mp_obj_t self, mp_obj_t o) { + common_hal__eve_t *eve = EVEHAL(self); + mp_load_method(o, MP_QSTR_write, eve->dest); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(register_obj, _register); + +//| def flush(self) -> None: +//| """Send any queued drawing commands directly to the hardware. +//| +//| :param int width: The width of the grid in tiles, or 1 for sprites.""" +//| ... +//| +STATIC mp_obj_t _flush(mp_obj_t self) { + common_hal__eve_flush(EVEHAL(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(flush_obj, _flush); + +//| def cc(self, b: ReadableBuffer) -> None: +//| """Append bytes to the command FIFO. +//| +//| :param ~circuitpython_typing.ReadableBuffer b: The bytes to add""" +//| ... +//| +STATIC mp_obj_t _cc(mp_obj_t self, mp_obj_t b) { + mp_buffer_info_t buffer_info; + mp_get_buffer_raise(b, &buffer_info, MP_BUFFER_READ); + common_hal__eve_add(EVEHAL(self), buffer_info.len, buffer_info.buf); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc_obj, _cc); + +// { + +//| def AlphaFunc(self, func: int, ref: int) -> None: +//| """Set the alpha test function +//| +//| :param int func: specifies the test function, one of ``NEVER``, ``LESS``, ``LEQUAL``, ``GREATER``, ``GEQUAL``, ``EQUAL``, ``NOTEQUAL``, or ``ALWAYS``. Range 0-7. The initial value is ALWAYS(7) +//| :param int ref: specifies the reference value for the alpha test. Range 0-255. The initial value is 0 +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _alphafunc(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t func = mp_obj_get_int_truncated(a0); + uint32_t ref = mp_obj_get_int_truncated(a1); + common_hal__eve_AlphaFunc(EVEHAL(self), func, ref); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(alphafunc_obj, _alphafunc); + +//| def Begin(self, prim: int) -> None: +//| """Begin drawing a graphics primitive +//| +//| :param int prim: graphics primitive. +//| +//| Valid primitives are ``BITMAPS``, ``POINTS``, ``LINES``, ``LINE_STRIP``, ``EDGE_STRIP_R``, ``EDGE_STRIP_L``, ``EDGE_STRIP_A``, ``EDGE_STRIP_B`` and ``RECTS``.""" +//| ... +//| + +STATIC mp_obj_t _begin(mp_obj_t self, mp_obj_t a0) { + uint32_t prim = mp_obj_get_int_truncated(a0); + common_hal__eve_Begin(EVEHAL(self), prim); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(begin_obj, _begin); + +//| def BitmapExtFormat(self, format: int) -> None: +//| """Set the bitmap format +//| +//| :param int format: bitmap pixel format.""" +//| ... +//| + +STATIC mp_obj_t _bitmapextformat(mp_obj_t self, mp_obj_t a0) { + uint32_t fmt = mp_obj_get_int_truncated(a0); + common_hal__eve_BitmapExtFormat(EVEHAL(self), fmt); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bitmapextformat_obj, _bitmapextformat); + +//| def BitmapHandle(self, handle: int) -> None: +//| """Set the bitmap handle +//| +//| :param int handle: bitmap handle. Range 0-31. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _bitmaphandle(mp_obj_t self, mp_obj_t a0) { + uint32_t handle = mp_obj_get_int_truncated(a0); + common_hal__eve_BitmapHandle(EVEHAL(self), handle); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bitmaphandle_obj, _bitmaphandle); + +//| def BitmapLayoutH(self, linestride: int, height: int) -> None: +//| """Set the source bitmap memory format and layout for the current handle. high bits for large bitmaps +//| +//| :param int linestride: high part of bitmap line stride, in bytes. Range 0-7 +//| :param int height: high part of bitmap height, in lines. Range 0-3""" +//| ... +//| + +STATIC mp_obj_t _bitmaplayouth(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t linestride = mp_obj_get_int_truncated(a0); + uint32_t height = mp_obj_get_int_truncated(a1); + common_hal__eve_BitmapLayoutH(EVEHAL(self), linestride, height); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bitmaplayouth_obj, _bitmaplayouth); + +//| def BitmapLayout(self, format: int, linestride: int, height: int) -> None: +//| """Set the source bitmap memory format and layout for the current handle +//| +//| :param int format: bitmap pixel format, or GLFORMAT to use BITMAP_EXT_FORMAT instead. Range 0-31 +//| :param int linestride: bitmap line stride, in bytes. Range 0-1023 +//| :param int height: bitmap height, in lines. Range 0-511""" +//| ... +//| + +STATIC mp_obj_t _bitmaplayout(size_t n_args, const mp_obj_t *args) { + uint32_t format = mp_obj_get_int_truncated(args[1]); + uint32_t linestride = mp_obj_get_int_truncated(args[2]); + uint32_t height = mp_obj_get_int_truncated(args[3]); + common_hal__eve_BitmapLayout(EVEHAL(args[0]), format, linestride, height); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bitmaplayout_obj, 4, 4, _bitmaplayout); + +//| def BitmapSizeH(self, width: int, height: int) -> None: +//| """Set the screen drawing of bitmaps for the current handle. high bits for large bitmaps +//| +//| :param int width: high part of drawn bitmap width, in pixels. Range 0-3 +//| :param int height: high part of drawn bitmap height, in pixels. Range 0-3""" +//| ... +//| + +STATIC mp_obj_t _bitmapsizeh(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t width = mp_obj_get_int_truncated(a0); + uint32_t height = mp_obj_get_int_truncated(a1); + common_hal__eve_BitmapSizeH(EVEHAL(self), width, height); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bitmapsizeh_obj, _bitmapsizeh); + +//| def BitmapSize(self, filter: int, wrapx: int, wrapy: int, width: int, height: int) -> None: +//| """Set the screen drawing of bitmaps for the current handle +//| +//| :param int filter: bitmap filtering mode, one of ``NEAREST`` or ``BILINEAR``. Range 0-1 +//| :param int wrapx: bitmap :math:`x` wrap mode, one of ``REPEAT`` or ``BORDER``. Range 0-1 +//| :param int wrapy: bitmap :math:`y` wrap mode, one of ``REPEAT`` or ``BORDER``. Range 0-1 +//| :param int width: drawn bitmap width, in pixels. Range 0-511 +//| :param int height: drawn bitmap height, in pixels. Range 0-511""" +//| ... +//| + +STATIC mp_obj_t _bitmapsize(size_t n_args, const mp_obj_t *args) { + uint32_t filter = mp_obj_get_int_truncated(args[1]); + uint32_t wrapx = mp_obj_get_int_truncated(args[2]); + uint32_t wrapy = mp_obj_get_int_truncated(args[3]); + uint32_t width = mp_obj_get_int_truncated(args[4]); + uint32_t height = mp_obj_get_int_truncated(args[5]); + common_hal__eve_BitmapSize(EVEHAL(args[0]), filter, wrapx, wrapy, width, height); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bitmapsize_obj, 6, 6, _bitmapsize); + +//| def BitmapSource(self, addr: int) -> None: +//| """Set the source address for bitmap graphics +//| +//| :param int addr: Bitmap start address, pixel-aligned. May be in SRAM or flash. Range 0-16777215""" +//| ... +//| + +STATIC mp_obj_t _bitmapsource(mp_obj_t self, mp_obj_t a0) { + uint32_t addr = mp_obj_get_int_truncated(a0); + common_hal__eve_BitmapSource(EVEHAL(self), addr); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bitmapsource_obj, _bitmapsource); + +//| def BitmapSwizzle(self, r: int, g: int, b: int, a: int) -> None: +//| """Set the source for the r,g,b and a channels of a bitmap +//| +//| :param int r: red component source channel. Range 0-7 +//| :param int g: green component source channel. Range 0-7 +//| :param int b: blue component source channel. Range 0-7 +//| :param int a: alpha component source channel. Range 0-7""" +//| ... +//| + +STATIC mp_obj_t _bitmapswizzle(size_t n_args, const mp_obj_t *args) { + uint32_t r = mp_obj_get_int_truncated(args[1]); + uint32_t g = mp_obj_get_int_truncated(args[2]); + uint32_t b = mp_obj_get_int_truncated(args[3]); + uint32_t a = mp_obj_get_int_truncated(args[4]); + common_hal__eve_BitmapSwizzle(EVEHAL(args[0]), r, g, b, a); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bitmapswizzle_obj, 5, 5, _bitmapswizzle); + +//| def BitmapTransformA(self, p: int, v: int) -> None: +//| """Set the :math:`a` component of the bitmap transform matrix +//| +//| :param int p: precision control: 0 is 8.8, 1 is 1.15. Range 0-1. The initial value is 0 +//| :param int v: The :math:`a` component of the bitmap transform matrix, in signed 8.8 or 1.15 bit fixed-point form. Range 0-131071. The initial value is 256 +//| +//| The initial value is **p** = 0, **v** = 256. This represents the value 1.0. +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _bitmaptransforma(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t p = mp_obj_get_int_truncated(a0); + uint32_t v = mp_obj_get_int_truncated(a1); + common_hal__eve_BitmapTransformA(EVEHAL(self), p, v); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bitmaptransforma_obj, _bitmaptransforma); + +//| def BitmapTransformB(self, p: int, v: int) -> None: +//| """Set the :math:`b` component of the bitmap transform matrix +//| +//| :param int p: precision control: 0 is 8.8, 1 is 1.15. Range 0-1. The initial value is 0 +//| :param int v: The :math:`b` component of the bitmap transform matrix, in signed 8.8 or 1.15 bit fixed-point form. Range 0-131071. The initial value is 0 +//| +//| The initial value is **p** = 0, **v** = 0. This represents the value 0.0. +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _bitmaptransformb(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t p = mp_obj_get_int_truncated(a0); + uint32_t v = mp_obj_get_int_truncated(a1); + common_hal__eve_BitmapTransformB(EVEHAL(self), p, v); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bitmaptransformb_obj, _bitmaptransformb); + +//| def BitmapTransformC(self, v: int) -> None: +//| """Set the :math:`c` component of the bitmap transform matrix +//| +//| :param int v: The :math:`c` component of the bitmap transform matrix, in signed 15.8 bit fixed-point form. Range 0-16777215. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _bitmaptransformc(mp_obj_t self, mp_obj_t a0) { + uint32_t v = mp_obj_get_int_truncated(a0); + common_hal__eve_BitmapTransformC(EVEHAL(self), v); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bitmaptransformc_obj, _bitmaptransformc); + +//| def BitmapTransformD(self, p: int, v: int) -> None: +//| """Set the :math:`d` component of the bitmap transform matrix +//| +//| :param int p: precision control: 0 is 8.8, 1 is 1.15. Range 0-1. The initial value is 0 +//| :param int v: The :math:`d` component of the bitmap transform matrix, in signed 8.8 or 1.15 bit fixed-point form. Range 0-131071. The initial value is 0 +//| +//| The initial value is **p** = 0, **v** = 0. This represents the value 0.0. +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _bitmaptransformd(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t p = mp_obj_get_int_truncated(a0); + uint32_t v = mp_obj_get_int_truncated(a1); + common_hal__eve_BitmapTransformD(EVEHAL(self), p, v); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bitmaptransformd_obj, _bitmaptransformd); + +//| def BitmapTransformE(self, p: int, v: int) -> None: +//| """Set the :math:`e` component of the bitmap transform matrix +//| +//| :param int p: precision control: 0 is 8.8, 1 is 1.15. Range 0-1. The initial value is 0 +//| :param int v: The :math:`e` component of the bitmap transform matrix, in signed 8.8 or 1.15 bit fixed-point form. Range 0-131071. The initial value is 256 +//| +//| The initial value is **p** = 0, **v** = 256. This represents the value 1.0. +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _bitmaptransforme(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t p = mp_obj_get_int_truncated(a0); + uint32_t v = mp_obj_get_int_truncated(a1); + common_hal__eve_BitmapTransformE(EVEHAL(self), p, v); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bitmaptransforme_obj, _bitmaptransforme); + +//| def BitmapTransformF(self, v: int) -> None: +//| """Set the :math:`f` component of the bitmap transform matrix +//| +//| :param int v: The :math:`f` component of the bitmap transform matrix, in signed 15.8 bit fixed-point form. Range 0-16777215. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _bitmaptransformf(mp_obj_t self, mp_obj_t a0) { + uint32_t v = mp_obj_get_int_truncated(a0); + common_hal__eve_BitmapTransformF(EVEHAL(self), v); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(bitmaptransformf_obj, _bitmaptransformf); + +//| def BlendFunc(self, src: int, dst: int) -> None: +//| """Set pixel arithmetic +//| +//| :param int src: specifies how the source blending factor is computed. One of ``ZERO``, ``ONE``, ``SRC_ALPHA``, ``DST_ALPHA``, ``ONE_MINUS_SRC_ALPHA`` or ``ONE_MINUS_DST_ALPHA``. Range 0-7. The initial value is SRC_ALPHA(2) +//| :param int dst: specifies how the destination blending factor is computed, one of the same constants as **src**. Range 0-7. The initial value is ONE_MINUS_SRC_ALPHA(4) +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _blendfunc(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t src = mp_obj_get_int_truncated(a0); + uint32_t dst = mp_obj_get_int_truncated(a1); + common_hal__eve_BlendFunc(EVEHAL(self), src, dst); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(blendfunc_obj, _blendfunc); + +//| def Call(self, dest: int) -> None: +//| """Execute a sequence of commands at another location in the display list +//| +//| :param int dest: display list address. Range 0-65535""" +//| ... +//| + +STATIC mp_obj_t _call(mp_obj_t self, mp_obj_t a0) { + uint32_t dest = mp_obj_get_int_truncated(a0); + common_hal__eve_Call(EVEHAL(self), dest); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(call_obj, _call); + +//| def Cell(self, cell: int) -> None: +//| """Set the bitmap cell number for the vertex2f command +//| +//| :param int cell: bitmap cell number. Range 0-127. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _cell(mp_obj_t self, mp_obj_t a0) { + uint32_t cell = mp_obj_get_int_truncated(a0); + common_hal__eve_Cell(EVEHAL(self), cell); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(cell_obj, _cell); + +//| def ClearColorA(self, alpha: int) -> None: +//| """Set clear value for the alpha channel +//| +//| :param int alpha: alpha value used when the color buffer is cleared. Range 0-255. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _clearcolora(mp_obj_t self, mp_obj_t a0) { + uint32_t alpha = mp_obj_get_int_truncated(a0); + common_hal__eve_ClearColorA(EVEHAL(self), alpha); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(clearcolora_obj, _clearcolora); + +//| def ClearColorRGB(self, red: int, green: int, blue: int) -> None: +//| """Set clear values for red, green and blue channels +//| +//| :param int red: red value used when the color buffer is cleared. Range 0-255. The initial value is 0 +//| :param int green: green value used when the color buffer is cleared. Range 0-255. The initial value is 0 +//| :param int blue: blue value used when the color buffer is cleared. Range 0-255. The initial value is 0 +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _clearcolorrgb(size_t n_args, const mp_obj_t *args) { + uint32_t red = mp_obj_get_int_truncated(args[1]); + uint32_t green = mp_obj_get_int_truncated(args[2]); + uint32_t blue = mp_obj_get_int_truncated(args[3]); + common_hal__eve_ClearColorRGB(EVEHAL(args[0]), red, green, blue); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(clearcolorrgb_obj, 4, 4, _clearcolorrgb); + +//| def Clear(self, c: int, s: int, t: int) -> None: +//| """Clear buffers to preset values +//| +//| :param int c: clear color buffer. Range 0-1 +//| :param int s: clear stencil buffer. Range 0-1 +//| :param int t: clear tag buffer. Range 0-1""" +//| ... +//| + +STATIC mp_obj_t _clear(size_t n_args, const mp_obj_t *args) { + uint32_t c = (n_args > 1) ? mp_obj_get_int_truncated(args[1]) : 1; + uint32_t s = (n_args > 2) ? mp_obj_get_int_truncated(args[2]) : 1; + uint32_t t = (n_args > 3) ? mp_obj_get_int_truncated(args[3]) : 1; + common_hal__eve_Clear(EVEHAL(args[0]), c, s, t); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(clear_obj, 1, 4, _clear); + +//| def ClearStencil(self, s: int) -> None: +//| """Set clear value for the stencil buffer +//| +//| :param int s: value used when the stencil buffer is cleared. Range 0-255. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _clearstencil(mp_obj_t self, mp_obj_t a0) { + uint32_t s = mp_obj_get_int_truncated(a0); + common_hal__eve_ClearStencil(EVEHAL(self), s); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(clearstencil_obj, _clearstencil); + +//| def ClearTag(self, s: int) -> None: +//| """Set clear value for the tag buffer +//| +//| :param int s: value used when the tag buffer is cleared. Range 0-255. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| + +STATIC mp_obj_t _cleartag(mp_obj_t self, mp_obj_t a0) { + uint32_t s = mp_obj_get_int_truncated(a0); + common_hal__eve_ClearTag(EVEHAL(self), s); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(cleartag_obj, _cleartag); + +//| def ColorA(self, alpha: int) -> None: +//| """Set the current color alpha +//| +//| :param int alpha: alpha for the current color. Range 0-255. The initial value is 255 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _colora(mp_obj_t self, mp_obj_t a0) { + uint32_t alpha = mp_obj_get_int_truncated(a0); + common_hal__eve_ColorA(EVEHAL(self), alpha); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(colora_obj, _colora); + +//| def ColorMask(self, r: int, g: int, b: int, a: int) -> None: +//| """Enable and disable writing of frame buffer color components +//| +//| :param int r: allow updates to the frame buffer red component. Range 0-1. The initial value is 1 +//| :param int g: allow updates to the frame buffer green component. Range 0-1. The initial value is 1 +//| :param int b: allow updates to the frame buffer blue component. Range 0-1. The initial value is 1 +//| :param int a: allow updates to the frame buffer alpha component. Range 0-1. The initial value is 1 +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _colormask(size_t n_args, const mp_obj_t *args) { + uint32_t r = mp_obj_get_int_truncated(args[1]); + uint32_t g = mp_obj_get_int_truncated(args[2]); + uint32_t b = mp_obj_get_int_truncated(args[3]); + uint32_t a = mp_obj_get_int_truncated(args[4]); + common_hal__eve_ColorMask(EVEHAL(args[0]), r, g, b, a); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(colormask_obj, 5, 5, _colormask); + +//| def ColorRGB(self, red: int, green: int, blue: int) -> None: +//| """Set the drawing color +//| +//| :param int red: red value for the current color. Range 0-255. The initial value is 255 +//| :param int green: green for the current color. Range 0-255. The initial value is 255 +//| :param int blue: blue for the current color. Range 0-255. The initial value is 255 +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _colorrgb(size_t n_args, const mp_obj_t *args) { + uint32_t red = mp_obj_get_int_truncated(args[1]); + uint32_t green = mp_obj_get_int_truncated(args[2]); + uint32_t blue = mp_obj_get_int_truncated(args[3]); + common_hal__eve_ColorRGB(EVEHAL(args[0]), red, green, blue); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(colorrgb_obj, 4, 4, _colorrgb); + +//| def Display(self) -> None: +//| """End the display list""" +//| ... + +STATIC mp_obj_t _display(mp_obj_t self) { + + common_hal__eve_Display(EVEHAL(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(display_obj, _display); + +//| def End(self) -> None: +//| """End drawing a graphics primitive +//| +//| :meth:`Vertex2ii` and :meth:`Vertex2f` calls are ignored until the next :meth:`Begin`.""" +//| ... +//| + +STATIC mp_obj_t _end(mp_obj_t self) { + + common_hal__eve_End(EVEHAL(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(end_obj, _end); + +//| def Jump(self, dest: int) -> None: +//| """Execute commands at another location in the display list +//| +//| :param int dest: display list address. Range 0-65535""" +//| ... +//| + +STATIC mp_obj_t _jump(mp_obj_t self, mp_obj_t a0) { + uint32_t dest = mp_obj_get_int_truncated(a0); + common_hal__eve_Jump(EVEHAL(self), dest); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(jump_obj, _jump); + +//| def Macro(self, m: int) -> None: +//| """Execute a single command from a macro register +//| +//| :param int m: macro register to read. Range 0-1""" +//| ... +//| + +STATIC mp_obj_t _macro(mp_obj_t self, mp_obj_t a0) { + uint32_t m = mp_obj_get_int_truncated(a0); + common_hal__eve_Macro(EVEHAL(self), m); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(macro_obj, _macro); + +//| def Nop(self) -> None: +//| """No operation""" +//| ... +//| + +STATIC mp_obj_t _nop(mp_obj_t self) { + + common_hal__eve_Nop(EVEHAL(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(nop_obj, _nop); + +//| def PaletteSource(self, addr: int) -> None: +//| """Set the base address of the palette +//| +//| :param int addr: Address in graphics SRAM, 2-byte aligned. Range 0-4194303. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _palettesource(mp_obj_t self, mp_obj_t a0) { + uint32_t addr = mp_obj_get_int_truncated(a0); + common_hal__eve_PaletteSource(EVEHAL(self), addr); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(palettesource_obj, _palettesource); + +//| def RestoreContext(self) -> None: +//| """Restore the current graphics context from the context stack""" +//| ... +//| + +STATIC mp_obj_t _restorecontext(mp_obj_t self) { + + common_hal__eve_RestoreContext(EVEHAL(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(restorecontext_obj, _restorecontext); + +//| def Return(self) -> None: +//| """Return from a previous call command""" +//| ... +//| + +STATIC mp_obj_t _return(mp_obj_t self) { + + common_hal__eve_Return(EVEHAL(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(return_obj, _return); + +//| def SaveContext(self) -> None: +//| """Push the current graphics context on the context stack""" +//| ... +//| + +STATIC mp_obj_t _savecontext(mp_obj_t self) { + + common_hal__eve_SaveContext(EVEHAL(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(savecontext_obj, _savecontext); + +//| def ScissorSize(self, width: int, height: int) -> None: +//| """Set the size of the scissor clip rectangle +//| +//| :param int width: The width of the scissor clip rectangle, in pixels. Range 0-4095. The initial value is hsize +//| :param int height: The height of the scissor clip rectangle, in pixels. Range 0-4095. The initial value is 2048 +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _scissorsize(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t width = mp_obj_get_int_truncated(a0); + uint32_t height = mp_obj_get_int_truncated(a1); + common_hal__eve_ScissorSize(EVEHAL(self), width, height); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(scissorsize_obj, _scissorsize); + +//| def ScissorXY(self, x: int, y: int) -> None: +//| """Set the top left corner of the scissor clip rectangle +//| +//| :param int x: The :math:`x` coordinate of the scissor clip rectangle, in pixels. Range 0-2047. The initial value is 0 +//| :param int y: The :math:`y` coordinate of the scissor clip rectangle, in pixels. Range 0-2047. The initial value is 0 +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _scissorxy(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t x = mp_obj_get_int_truncated(a0); + uint32_t y = mp_obj_get_int_truncated(a1); + common_hal__eve_ScissorXY(EVEHAL(self), x, y); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(scissorxy_obj, _scissorxy); + +//| def StencilFunc(self, func: int, ref: int, mask: int) -> None: +//| """Set function and reference value for stencil testing +//| +//| :param int func: specifies the test function, one of ``NEVER``, ``LESS``, ``LEQUAL``, ``GREATER``, ``GEQUAL``, ``EQUAL``, ``NOTEQUAL``, or ``ALWAYS``. Range 0-7. The initial value is ALWAYS(7) +//| :param int ref: specifies the reference value for the stencil test. Range 0-255. The initial value is 0 +//| :param int mask: specifies a mask that is ANDed with the reference value and the stored stencil value. Range 0-255. The initial value is 255 +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _stencilfunc(size_t n_args, const mp_obj_t *args) { + uint32_t func = mp_obj_get_int_truncated(args[1]); + uint32_t ref = mp_obj_get_int_truncated(args[2]); + uint32_t mask = mp_obj_get_int_truncated(args[3]); + common_hal__eve_StencilFunc(EVEHAL(args[0]), func, ref, mask); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stencilfunc_obj, 4, 4, _stencilfunc); + +//| def StencilMask(self, mask: int) -> None: +//| """Control the writing of individual bits in the stencil planes +//| +//| :param int mask: the mask used to enable writing stencil bits. Range 0-255. The initial value is 255 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _stencilmask(mp_obj_t self, mp_obj_t a0) { + uint32_t mask = mp_obj_get_int_truncated(a0); + common_hal__eve_StencilMask(EVEHAL(self), mask); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(stencilmask_obj, _stencilmask); + +//| def StencilOp(self, sfail: int, spass: int) -> None: +//| """Set stencil test actions +//| +//| :param int sfail: specifies the action to take when the stencil test fails, one of ``KEEP``, ``ZERO``, ``REPLACE``, ``INCR``, ``INCR_WRAP``, ``DECR``, ``DECR_WRAP``, and ``INVERT``. Range 0-7. The initial value is KEEP(1) +//| :param int spass: specifies the action to take when the stencil test passes, one of the same constants as **sfail**. Range 0-7. The initial value is KEEP(1) +//| +//| These values are part of the graphics context and are saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _stencilop(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + uint32_t sfail = mp_obj_get_int_truncated(a0); + uint32_t spass = mp_obj_get_int_truncated(a1); + common_hal__eve_StencilOp(EVEHAL(self), sfail, spass); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(stencilop_obj, _stencilop); + +//| def TagMask(self, mask: int) -> None: +//| """Control the writing of the tag buffer +//| +//| :param int mask: allow updates to the tag buffer. Range 0-1. The initial value is 1 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _tagmask(mp_obj_t self, mp_obj_t a0) { + uint32_t mask = mp_obj_get_int_truncated(a0); + common_hal__eve_TagMask(EVEHAL(self), mask); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(tagmask_obj, _tagmask); + +//| def Tag(self, s: int) -> None: +//| """Set the current tag value +//| +//| :param int s: tag value. Range 0-255. The initial value is 255 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _tag(mp_obj_t self, mp_obj_t a0) { + uint32_t s = mp_obj_get_int_truncated(a0); + common_hal__eve_Tag(EVEHAL(self), s); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(tag_obj, _tag); + +STATIC mp_obj_t _vertexformat(mp_obj_t self, mp_obj_t a0) { + uint32_t frac = mp_obj_get_int_truncated(a0); + common_hal__eve_VertexFormat(EVEHAL(self), frac); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vertexformat_obj, _vertexformat); + +//| def Vertex2ii(self, x: int, y: int, handle: int, cell: int) -> None: +//| """:param int x: x-coordinate in pixels. Range 0-511 +//| :param int y: y-coordinate in pixels. Range 0-511 +//| :param int handle: bitmap handle. Range 0-31 +//| :param int cell: cell number. Range 0-127 +//| +//| This method is an alternative to :meth:`Vertex2f`.""" +//| ... +//| + +STATIC mp_obj_t _vertex2ii(size_t n_args, const mp_obj_t *args) { + uint32_t x = mp_obj_get_int_truncated(args[1]); + uint32_t y = mp_obj_get_int_truncated(args[2]); + uint32_t handle = (n_args > 3) ? mp_obj_get_int_truncated(args[3]) : 0; + uint32_t cell = (n_args > 4) ? mp_obj_get_int_truncated(args[4]) : 0; + common_hal__eve_Vertex2ii(EVEHAL(args[0]), x, y, handle, cell); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(vertex2ii_obj, 3, 5, _vertex2ii); + +#define ROM_DECLS \ + { MP_ROM_QSTR(MP_QSTR_AlphaFunc), MP_ROM_PTR(&alphafunc_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Begin), MP_ROM_PTR(&begin_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapExtFormat), MP_ROM_PTR(&bitmapextformat_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapHandle), MP_ROM_PTR(&bitmaphandle_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapLayoutH), MP_ROM_PTR(&bitmaplayouth_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapLayout), MP_ROM_PTR(&bitmaplayout_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapSizeH), MP_ROM_PTR(&bitmapsizeh_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapSize), MP_ROM_PTR(&bitmapsize_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapSource), MP_ROM_PTR(&bitmapsource_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapSwizzle), MP_ROM_PTR(&bitmapswizzle_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapTransformA), MP_ROM_PTR(&bitmaptransforma_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapTransformB), MP_ROM_PTR(&bitmaptransformb_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapTransformC), MP_ROM_PTR(&bitmaptransformc_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapTransformD), MP_ROM_PTR(&bitmaptransformd_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapTransformE), MP_ROM_PTR(&bitmaptransforme_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BitmapTransformF), MP_ROM_PTR(&bitmaptransformf_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_BlendFunc), MP_ROM_PTR(&blendfunc_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Call), MP_ROM_PTR(&call_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Cell), MP_ROM_PTR(&cell_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ClearColorA), MP_ROM_PTR(&clearcolora_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ClearColorRGB), MP_ROM_PTR(&clearcolorrgb_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Clear), MP_ROM_PTR(&clear_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ClearStencil), MP_ROM_PTR(&clearstencil_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ClearTag), MP_ROM_PTR(&cleartag_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ColorA), MP_ROM_PTR(&colora_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ColorMask), MP_ROM_PTR(&colormask_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ColorRGB), MP_ROM_PTR(&colorrgb_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&display_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_End), MP_ROM_PTR(&end_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Jump), MP_ROM_PTR(&jump_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_LineWidth), MP_ROM_PTR(&linewidth_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Macro), MP_ROM_PTR(¯o_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Nop), MP_ROM_PTR(&nop_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_PaletteSource), MP_ROM_PTR(&palettesource_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_PointSize), MP_ROM_PTR(&pointsize_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_RestoreContext), MP_ROM_PTR(&restorecontext_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Return), MP_ROM_PTR(&return_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_SaveContext), MP_ROM_PTR(&savecontext_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ScissorSize), MP_ROM_PTR(&scissorsize_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_ScissorXY), MP_ROM_PTR(&scissorxy_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_StencilFunc), MP_ROM_PTR(&stencilfunc_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_StencilMask), MP_ROM_PTR(&stencilmask_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_StencilOp), MP_ROM_PTR(&stencilop_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_TagMask), MP_ROM_PTR(&tagmask_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Tag), MP_ROM_PTR(&tag_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_VertexTranslateX), MP_ROM_PTR(&vertextranslatex_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_VertexTranslateY), MP_ROM_PTR(&vertextranslatey_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_VertexFormat), MP_ROM_PTR(&vertexformat_obj) }, \ + { MP_ROM_QSTR(MP_QSTR_Vertex2ii), MP_ROM_PTR(&vertex2ii_obj) } + +// } + +// Hand-written functions { + +//| def Vertex2f(self, b: float) -> None: +//| """Draw a point. +//| +//| :param float x: pixel x-coordinate +//| :param float y: pixel y-coordinate""" +//| ... +//| +STATIC mp_obj_t _vertex2f(mp_obj_t self, mp_obj_t a0, mp_obj_t a1) { + mp_float_t x = mp_obj_get_float(a0); + mp_float_t y = mp_obj_get_float(a1); + common_hal__eve_Vertex2f(EVEHAL(self), x, y); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(vertex2f_obj, _vertex2f); + +//| def LineWidth(self, width: float) -> None: +//| """Set the width of rasterized lines +//| +//| :param float width: line width in pixels. Range 0-511. The initial value is 1 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _linewidth(mp_obj_t self, mp_obj_t a0) { + mp_float_t width = mp_obj_get_float(a0); + common_hal__eve_LineWidth(EVEHAL(self), width); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(linewidth_obj, _linewidth); + +//| def PointSize(self, size: float) -> None: +//| """Set the diameter of rasterized points +//| +//| :param float size: point diameter in pixels. Range 0-1023. The initial value is 1 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _pointsize(mp_obj_t self, mp_obj_t a0) { + mp_float_t size = mp_obj_get_float(a0); + common_hal__eve_PointSize(EVEHAL(self), size); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pointsize_obj, _pointsize); + +//| def VertexTranslateX(self, x: float) -> None: +//| """Set the vertex transformation's x translation component +//| +//| :param float x: signed x-coordinate in pixels. Range ±4095. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +STATIC mp_obj_t _vertextranslatex(mp_obj_t self, mp_obj_t a0) { + mp_float_t x = mp_obj_get_float(a0); + common_hal__eve_VertexTranslateX(EVEHAL(self), x); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vertextranslatex_obj, _vertextranslatex); + +//| def VertexTranslateY(self, y: float) -> None: +//| """Set the vertex transformation's y translation component +//| +//| :param float y: signed y-coordinate in pixels. Range ±4095. The initial value is 0 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + + +STATIC mp_obj_t _vertextranslatey(mp_obj_t self, mp_obj_t a0) { + mp_float_t y = mp_obj_get_float(a0); + common_hal__eve_VertexTranslateY(EVEHAL(self), y); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(vertextranslatey_obj, _vertextranslatey); + +//| def VertexFormat(self, frac: int) -> None: +//| """Set the precision of vertex2f coordinates +//| +//| :param int frac: Number of fractional bits in X,Y coordinates, 0-4. Range 0-7. The initial value is 4 +//| +//| This value is part of the graphics context and is saved and restored by :meth:`SaveContext` and :meth:`RestoreContext`.""" +//| ... +//| + +// } + +// Append an object x to the FIFO +#define ADD_X(self, x) \ + common_hal__eve_add(EVEHAL(self), sizeof(x), &(x)); + +//| def cmd0(self, n: int) -> None: +//| """Append the command word n to the FIFO +//| +//| :param int n: The command code +//| +//| This method is used by the ``eve`` module to efficiently add +//| commands to the FIFO.""" +//| ... +//| + +STATIC mp_obj_t _cmd0(mp_obj_t self, mp_obj_t n) { + uint32_t code = 0xffffff00 | mp_obj_get_int_truncated(n); + ADD_X(self, code); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(cmd0_obj, _cmd0); + +//| def cmd(self, n: int, fmt: str, args: Tuple[str, ...]) -> None: +//| """Append a command packet to the FIFO. +//| +//| :param int n: The command code +//| :param str fmt: The command format `struct` layout +//| :param args: The command's arguments +//| :type args: tuple(str, ...) +//| +//| Supported format codes: h, H, i, I. +//| +//| This method is used by the ``eve`` module to efficiently add +//| commands to the FIFO.""" +//| ... +//| +STATIC mp_obj_t _cmd(size_t n_args, const mp_obj_t *args) { + mp_obj_t self = args[0]; + mp_obj_t num = args[1]; + mp_buffer_info_t fmt; + mp_get_buffer_raise(args[2], &fmt, MP_BUFFER_READ); + size_t len; + mp_obj_t *items; + mp_obj_tuple_get(args[3], &len, &items); + + // Count how many 32-bit words required + size_t n = 0; + for (size_t i = 0; i < fmt.len; n++) { + switch (((char *)fmt.buf)[i]) { + case 'I': + case 'i': + i += 1; + break; + case 'H': + case 'h': + i += 2; + break; + default: + break; + } + } + + uint32_t buf[16]; + uint32_t *p = buf; + *p++ = 0xffffff00 | mp_obj_get_int_truncated(num); + mp_obj_t *a = items; + uint32_t lo; + + for (size_t i = 0; i < fmt.len;) { + switch (((char *)fmt.buf)[i]) { + case 'I': + case 'i': + *p++ = mp_obj_get_int_truncated(*a++); + i += 1; + break; + case 'H': + case 'h': + lo = mp_obj_get_int_truncated(*a++) & 0xffff; + *p++ = lo | (mp_obj_get_int_truncated(*a++) << 16); + i += 2; + break; + default: + break; + } + } + + common_hal__eve_add(EVEHAL(self), sizeof(uint32_t) * (1 + n), buf); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(cmd_obj, 4, 4, _cmd); + +STATIC const mp_rom_map_elem_t _EVE_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(®ister_obj) }, + { MP_ROM_QSTR(MP_QSTR_cc), MP_ROM_PTR(&cc_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_Vertex2f), MP_ROM_PTR(&vertex2f_obj) }, + { MP_ROM_QSTR(MP_QSTR_cmd), MP_ROM_PTR(&cmd_obj) }, + { MP_ROM_QSTR(MP_QSTR_cmd0), MP_ROM_PTR(&cmd0_obj) }, + ROM_DECLS +}; +STATIC MP_DEFINE_CONST_DICT(_EVE_locals_dict, _EVE_locals_dict_table); + +STATIC mp_obj_t _EVE_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // mp_arg_check_num(n_args, kw_args, 1, 1, false); + mp_obj__EVE_t *o = m_new_obj(mp_obj__EVE_t); + o->base.type = &_EVE_type; + o->_eve.n = 0; + o->_eve.vscale = 16; + return MP_OBJ_FROM_PTR(o); +} + +STATIC const mp_obj_type_t _EVE_type = { + { &mp_type_type }, + .name = MP_QSTR__EVE, + .make_new = _EVE_make_new, + .locals_dict = (void *)&_EVE_locals_dict, +}; + +STATIC const mp_rom_map_elem_t mp_module__eve_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__eve) }, + { MP_ROM_QSTR(MP_QSTR__EVE), MP_OBJ_FROM_PTR(&_EVE_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module__eve_globals, mp_module__eve_globals_table); + +const mp_obj_module_t _eve_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module__eve_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR__eve, _eve_module, CIRCUITPY__EVE); diff --git a/circuitpython/shared-bindings/_eve/__init__.h b/circuitpython/shared-bindings/_eve/__init__.h new file mode 100644 index 0000000..96a10f0 --- /dev/null +++ b/circuitpython/shared-bindings/_eve/__init__.h @@ -0,0 +1,86 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 James Bowman for Excamera Labs + * + * 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__EVE___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS__EVE___INIT___H + +#include "shared-module/_eve/__init__.h" + +void common_hal__eve_flush(common_hal__eve_t *eve); +void common_hal__eve_add(common_hal__eve_t *eve, size_t len, void *buf); +void common_hal__eve_Vertex2f(common_hal__eve_t *eve, mp_float_t x, mp_float_t y); + +void common_hal__eve_AlphaFunc(common_hal__eve_t *eve, uint32_t func, uint32_t ref); +void common_hal__eve_Begin(common_hal__eve_t *eve, uint32_t prim); +void common_hal__eve_BitmapExtFormat(common_hal__eve_t *eve, uint32_t fmt); +void common_hal__eve_BitmapHandle(common_hal__eve_t *eve, uint32_t handle); +void common_hal__eve_BitmapLayoutH(common_hal__eve_t *eve, uint32_t linestride, uint32_t height); +void common_hal__eve_BitmapLayout(common_hal__eve_t *eve, uint32_t format, uint32_t linestride, uint32_t height); +void common_hal__eve_BitmapSizeH(common_hal__eve_t *eve, uint32_t width, uint32_t height); +void common_hal__eve_BitmapSize(common_hal__eve_t *eve, uint32_t filter, uint32_t wrapx, uint32_t wrapy, uint32_t width, uint32_t height); +void common_hal__eve_BitmapSource(common_hal__eve_t *eve, uint32_t addr); +void common_hal__eve_BitmapSwizzle(common_hal__eve_t *eve, uint32_t r, uint32_t g, uint32_t b, uint32_t a); +void common_hal__eve_BitmapTransformA(common_hal__eve_t *eve, uint32_t p, uint32_t v); +void common_hal__eve_BitmapTransformB(common_hal__eve_t *eve, uint32_t p, uint32_t v); +void common_hal__eve_BitmapTransformC(common_hal__eve_t *eve, uint32_t v); +void common_hal__eve_BitmapTransformD(common_hal__eve_t *eve, uint32_t p, uint32_t v); +void common_hal__eve_BitmapTransformE(common_hal__eve_t *eve, uint32_t p, uint32_t v); +void common_hal__eve_BitmapTransformF(common_hal__eve_t *eve, uint32_t v); +void common_hal__eve_BlendFunc(common_hal__eve_t *eve, uint32_t src, uint32_t dst); +void common_hal__eve_Call(common_hal__eve_t *eve, uint32_t dest); +void common_hal__eve_Cell(common_hal__eve_t *eve, uint32_t cell); +void common_hal__eve_ClearColorA(common_hal__eve_t *eve, uint32_t alpha); +void common_hal__eve_ClearColorRGB(common_hal__eve_t *eve, uint32_t red, uint32_t green, uint32_t blue); +void common_hal__eve_Clear(common_hal__eve_t *eve, uint32_t c, uint32_t s, uint32_t t); +void common_hal__eve_ClearStencil(common_hal__eve_t *eve, uint32_t s); +void common_hal__eve_ClearTag(common_hal__eve_t *eve, uint32_t s); +void common_hal__eve_ColorA(common_hal__eve_t *eve, uint32_t alpha); +void common_hal__eve_ColorMask(common_hal__eve_t *eve, uint32_t r, uint32_t g, uint32_t b, uint32_t a); +void common_hal__eve_ColorRGB(common_hal__eve_t *eve, uint32_t red, uint32_t green, uint32_t blue); +void common_hal__eve_Display(common_hal__eve_t *eve); +void common_hal__eve_End(common_hal__eve_t *eve); +void common_hal__eve_Jump(common_hal__eve_t *eve, uint32_t dest); +void common_hal__eve_LineWidth(common_hal__eve_t *eve, mp_float_t width); +void common_hal__eve_Macro(common_hal__eve_t *eve, uint32_t m); +void common_hal__eve_Nop(common_hal__eve_t *eve); +void common_hal__eve_PaletteSource(common_hal__eve_t *eve, uint32_t addr); +void common_hal__eve_PointSize(common_hal__eve_t *eve, mp_float_t size); +void common_hal__eve_RestoreContext(common_hal__eve_t *eve); +void common_hal__eve_Return(common_hal__eve_t *eve); +void common_hal__eve_SaveContext(common_hal__eve_t *eve); +void common_hal__eve_ScissorSize(common_hal__eve_t *eve, uint32_t width, uint32_t height); +void common_hal__eve_ScissorXY(common_hal__eve_t *eve, uint32_t x, uint32_t y); +void common_hal__eve_StencilFunc(common_hal__eve_t *eve, uint32_t func, uint32_t ref, uint32_t mask); +void common_hal__eve_StencilMask(common_hal__eve_t *eve, uint32_t mask); +void common_hal__eve_StencilOp(common_hal__eve_t *eve, uint32_t sfail, uint32_t spass); +void common_hal__eve_TagMask(common_hal__eve_t *eve, uint32_t mask); +void common_hal__eve_Tag(common_hal__eve_t *eve, uint32_t s); +void common_hal__eve_VertexTranslateX(common_hal__eve_t *eve, mp_float_t x); +void common_hal__eve_VertexTranslateY(common_hal__eve_t *eve, mp_float_t y); +void common_hal__eve_VertexFormat(common_hal__eve_t *eve, uint32_t frac); +void common_hal__eve_Vertex2ii(common_hal__eve_t *eve, uint32_t x, uint32_t y, uint32_t handle, uint32_t cell); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS__EVE___INIT___H diff --git a/circuitpython/shared-bindings/_pew/PewPew.c b/circuitpython/shared-bindings/_pew/PewPew.c new file mode 100644 index 0000000..4015d31 --- /dev/null +++ b/circuitpython/shared-bindings/_pew/PewPew.c @@ -0,0 +1,140 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Radomir Dopieralski + * + * 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 "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/gc.h" +#include "py/mpstate.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/util.h" +#include "PewPew.h" +#include "common-hal/_pew/PewPew.h" +#include "supervisor/shared/translate.h" + +//| class PewPew: +//| """This is an internal module to be used by the ``pew.py`` library from +//| https://github.com/pewpew-game/pew-pewpew-standalone-10.x to handle the +//| LED matrix display and buttons on the ``pewpew10`` board. +//| +//| Usage:: +//| +//| This singleton class is instantiated by the ``pew`` library, and +//| used internally by it. All user-visible interactions are done through +//| that library.""" +//| +//| def __init__( +//| self, +//| buffer: ReadableBuffer, +//| rows: List[digitalio.DigitalInOut], +//| cols: List[digitalio.DigitalInOut], +//| buttons: digitalio.DigitalInOut, +//| ) -> None: +//| """Initializes matrix scanning routines. +//| +//| The ``buffer`` is a 64 byte long ``bytearray`` that stores what should +//| be displayed on the matrix. ``rows`` and ``cols`` are both lists of +//| eight ``DigitalInputOutput`` objects that are connected to the matrix +//| rows and columns. ``buttons`` is a ``DigitalInputOutput`` object that +//| is connected to the common side of all buttons (the other sides of the +//| buttons are connected to rows of the matrix).""" +//| ... +//| +STATIC mp_obj_t pewpew_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_buffer, ARG_rows, ARG_cols, ARG_buttons }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_rows, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_cols, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_buttons, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + 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); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + + size_t rows_size = 0; + mp_obj_t *rows; + mp_obj_get_array(args[ARG_rows].u_obj, &rows_size, &rows); + + size_t cols_size = 0; + mp_obj_t *cols; + mp_obj_get_array(args[ARG_cols].u_obj, &cols_size, &cols); + + if (bufinfo.len != rows_size * cols_size) { + mp_raise_ValueError(translate("Incorrect buffer size")); + } + + for (size_t i = 0; i < rows_size; ++i) { + digitalio_digitalinout_obj_t *pin = mp_arg_validate_type(rows[i], &digitalio_digitalinout_type, MP_QSTR_rows); + if (common_hal_digitalio_digitalinout_deinited(pin)) { + raise_deinited_error(); + } + } + + for (size_t i = 0; i < cols_size; ++i) { + digitalio_digitalinout_obj_t *pin = mp_arg_validate_type(cols[i], &digitalio_digitalinout_type, MP_QSTR_cols); + if (common_hal_digitalio_digitalinout_deinited(pin)) { + raise_deinited_error(); + } + } + + digitalio_digitalinout_obj_t *buttons = mp_arg_validate_type(args[ARG_buttons].u_obj, &digitalio_digitalinout_type, MP_QSTR_buttons); + if (common_hal_digitalio_digitalinout_deinited(buttons)) { + raise_deinited_error(); + } + + pew_obj_t *pew = MP_STATE_VM(pew_singleton); + if (!pew) { + pew = m_new_obj(pew_obj_t); + pew->base.type = &pewpew_type; + pew = gc_make_long_lived(pew); + MP_STATE_VM(pew_singleton) = pew; + } + + pew->buffer = bufinfo.buf; + pew->rows = rows; + pew->rows_size = rows_size; + pew->cols = cols; + pew->cols_size = cols_size; + pew->buttons = buttons; + pew->pressed = 0; + pew_init(); + + return MP_OBJ_FROM_PTR(pew); +} + + +STATIC const mp_rom_map_elem_t pewpew_locals_dict_table[] = { +}; +STATIC MP_DEFINE_CONST_DICT(pewpew_locals_dict, pewpew_locals_dict_table); +const mp_obj_type_t pewpew_type = { + { &mp_type_type }, + .name = MP_QSTR_PewPew, + .make_new = pewpew_make_new, + .locals_dict = (mp_obj_dict_t *)&pewpew_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_pew/PewPew.h b/circuitpython/shared-bindings/_pew/PewPew.h new file mode 100644 index 0000000..f763847 --- /dev/null +++ b/circuitpython/shared-bindings/_pew/PewPew.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Radomir Dopieralski + * + * 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_PEW_PEWPEW_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PEW_PEWPEW_H + +extern const mp_obj_type_t pewpew_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PEW_PEWPEW_H diff --git a/circuitpython/shared-bindings/_pew/__init__.c b/circuitpython/shared-bindings/_pew/__init__.c new file mode 100644 index 0000000..e338869 --- /dev/null +++ b/circuitpython/shared-bindings/_pew/__init__.c @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Radomir Dopieralski + * + * 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 "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "PewPew.h" +#include "common-hal/_pew/PewPew.h" + + +STATIC mp_obj_t get_pressed(void) { + pew_obj_t *pew = MP_STATE_VM(pew_singleton); + if (!pew) { + return mp_const_none; + } + uint8_t pressed = pew->pressed; + pew->pressed = 0; + return mp_obj_new_int(pressed); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(get_pressed_obj, get_pressed); + + +STATIC mp_obj_t get_ticks(void) { + return mp_obj_new_int(pew_get_ticks()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(get_ticks_obj, get_ticks); + + +//| """LED matrix driver""" +//| +STATIC const mp_rom_map_elem_t pew_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__pew) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PewPew), MP_ROM_PTR(&pewpew_type)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_get_pressed), MP_ROM_PTR(&get_pressed_obj)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_get_ticks), MP_ROM_PTR(&get_ticks_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(pew_module_globals, + pew_module_globals_table); + +const mp_obj_module_t pew_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&pew_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR__pew, pew_module, CIRCUITPY_PEW); diff --git a/circuitpython/shared-bindings/_stage/Layer.c b/circuitpython/shared-bindings/_stage/Layer.c new file mode 100644 index 0000000..c3c0aa1 --- /dev/null +++ b/circuitpython/shared-bindings/_stage/Layer.c @@ -0,0 +1,129 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Radomir Dopieralski + * + * 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 <py/runtime.h> + +#include "__init__.h" +#include "Layer.h" +#include "supervisor/shared/translate.h" + +//| class Layer: +//| """Keep information about a single layer of graphics""" +//| +//| def __init__(self, width: int, height: int, graphic: ReadableBuffer, palette: ReadableBuffer, grid: ReadableBuffer) -> None: +//| """Keep internal information about a layer of graphics (either a +//| ``Grid`` or a ``Sprite``) in a format suitable for fast rendering +//| with the ``render()`` function. +//| +//| :param int width: The width of the grid in tiles, or 1 for sprites. +//| :param int height: The height of the grid in tiles, or 1 for sprites. +//| :param ~circuitpython_typing.ReadableBuffer graphic: The graphic data of the tiles. +//| :param ~circuitpython_typing.ReadableBuffer palette: The color palette to be used. +//| :param ~circuitpython_typing.ReadableBuffer grid: The contents of the grid map. +//| +//| This class is intended for internal use in the ``stage`` library and +//| it shouldn't be used on its own.""" +//| ... +//| +STATIC mp_obj_t layer_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 4, 5, false); + + layer_obj_t *self = m_new_obj(layer_obj_t); + self->base.type = type; + + self->width = mp_obj_get_int(args[0]); + self->height = mp_obj_get_int(args[1]); + self->x = 0; + self->y = 0; + self->frame = 0; + self->rotation = false; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + self->graphic = bufinfo.buf; + if (bufinfo.len != 2048) { + mp_raise_ValueError(translate("graphic must be 2048 bytes long")); + } + + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + self->palette = bufinfo.buf; + if (bufinfo.len != 32) { + mp_raise_ValueError(translate("palette must be 32 bytes long")); + } + + if (n_args > 4) { + mp_get_buffer_raise(args[4], &bufinfo, MP_BUFFER_READ); + self->map = bufinfo.buf; + if (bufinfo.len < (self->width * self->height) / 2) { + mp_raise_ValueError(translate("map buffer too small")); + } + } else { + self->map = NULL; + } + + return MP_OBJ_FROM_PTR(self); +} + +//| def move(self, x: int, y: int) -> None: +//| """Set the offset of the layer to the specified values.""" +//| ... +//| +STATIC mp_obj_t layer_move(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) { + layer_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->x = mp_obj_get_int(x_in); + self->y = mp_obj_get_int(y_in); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(layer_move_obj, layer_move); + +//| def frame(self, frame: int, rotation: int) -> None: +//| """Set the animation frame of the sprite, and optionally rotation its +//| graphic.""" +//| ... +//| +STATIC mp_obj_t layer_frame(mp_obj_t self_in, mp_obj_t frame_in, + mp_obj_t rotation_in) { + layer_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->frame = mp_obj_get_int(frame_in); + self->rotation = mp_obj_get_int(rotation_in); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(layer_frame_obj, layer_frame); + + +STATIC const mp_rom_map_elem_t layer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_move), MP_ROM_PTR(&layer_move_obj) }, + { MP_ROM_QSTR(MP_QSTR_frame), MP_ROM_PTR(&layer_frame_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(layer_locals_dict, layer_locals_dict_table); + +const mp_obj_type_t mp_type_layer = { + { &mp_type_type }, + .name = MP_QSTR_Layer, + .make_new = layer_make_new, + .locals_dict = (mp_obj_dict_t *)&layer_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_stage/Layer.h b/circuitpython/shared-bindings/_stage/Layer.h new file mode 100644 index 0000000..6d15dfb --- /dev/null +++ b/circuitpython/shared-bindings/_stage/Layer.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Radomir Dopieralski + * + * 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__STAGE_LAYER_H +#define MICROPY_INCLUDED__STAGE_LAYER_H + +#include "shared-module/_stage/Layer.h" + +extern const mp_obj_type_t mp_type_layer; + +#endif // MICROPY_INCLUDED__STAGE_LAYER diff --git a/circuitpython/shared-bindings/_stage/Text.c b/circuitpython/shared-bindings/_stage/Text.c new file mode 100644 index 0000000..d6b2217 --- /dev/null +++ b/circuitpython/shared-bindings/_stage/Text.c @@ -0,0 +1,108 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Radomir Dopieralski + * + * 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 <py/runtime.h> + +#include "__init__.h" +#include "Text.h" +#include "supervisor/shared/translate.h" + +//| class Text: +//| """Keep information about a single grid of text""" +//| +//| def __init__(self, width: int, height: int, font: ReadableBuffer, palette: ReadableBuffer, chars: ReadableBuffer) -> None: +//| """Keep internal information about a grid of text +//| in a format suitable for fast rendering +//| with the ``render()`` function. +//| +//| :param int width: The width of the grid in tiles, or 1 for sprites. +//| :param int height: The height of the grid in tiles, or 1 for sprites. +//| :param ~circuitpython_typing.ReadableBuffer font: The font data of the characters. +//| :param ~circuitpython_typing.ReadableBuffer palette: The color palette to be used. +//| :param ~circuitpython_typing.ReadableBuffer chars: The contents of the character grid. +//| +//| This class is intended for internal use in the ``stage`` library and +//| it shouldn't be used on its own.""" +//| ... +//| +STATIC mp_obj_t text_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 5, 5, false); + + text_obj_t *self = m_new_obj(text_obj_t); + self->base.type = type; + + self->width = mp_obj_get_int(args[0]); + self->height = mp_obj_get_int(args[1]); + self->x = 0; + self->y = 0; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + self->font = bufinfo.buf; + if (bufinfo.len != 2048) { + mp_raise_ValueError(translate("font must be 2048 bytes long")); + } + + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + self->palette = bufinfo.buf; + if (bufinfo.len != 32) { + mp_raise_ValueError(translate("palette must be 32 bytes long")); + } + + mp_get_buffer_raise(args[4], &bufinfo, MP_BUFFER_READ); + self->chars = bufinfo.buf; + if (bufinfo.len < self->width * self->height) { + mp_raise_ValueError(translate("chars buffer too small")); + } + + return MP_OBJ_FROM_PTR(self); +} + +//| def move(self, x: int, y: int) -> None: +//| """Set the offset of the text to the specified values.""" +//| ... +//| +STATIC mp_obj_t text_move(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) { + text_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->x = mp_obj_get_int(x_in); + self->y = mp_obj_get_int(y_in); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(text_move_obj, text_move); + + +STATIC const mp_rom_map_elem_t text_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_move), MP_ROM_PTR(&text_move_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(text_locals_dict, text_locals_dict_table); + +const mp_obj_type_t mp_type_text = { + { &mp_type_type }, + .name = MP_QSTR_Text, + .make_new = text_make_new, + .locals_dict = (mp_obj_dict_t *)&text_locals_dict, +}; diff --git a/circuitpython/shared-bindings/_stage/Text.h b/circuitpython/shared-bindings/_stage/Text.h new file mode 100644 index 0000000..77de62a --- /dev/null +++ b/circuitpython/shared-bindings/_stage/Text.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Radomir Dopieralski + * + * 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__STAGE_TEXT_H +#define MICROPY_INCLUDED__STAGE_TEXT_H + +#include "shared-module/_stage/Text.h" + +extern const mp_obj_type_t mp_type_text; + +#endif // MICROPY_INCLUDED__STAGE_TEXT diff --git a/circuitpython/shared-bindings/_stage/__init__.c b/circuitpython/shared-bindings/_stage/__init__.c new file mode 100644 index 0000000..ebaa068 --- /dev/null +++ b/circuitpython/shared-bindings/_stage/__init__.c @@ -0,0 +1,111 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Radomir Dopieralski + * + * 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 "__init__.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/displayio/Display.h" +#include "shared-module/_stage/__init__.h" +#include "shared-module/displayio/display_core.h" +#include "Layer.h" +#include "Text.h" + +//| """C-level helpers for animation of sprites on a stage +//| +//| The `_stage` module contains native code to speed-up the ```stage`` Library +//| <https://github.com/python-ugame/circuitpython-stage>`_.""" +//| +//| def render(x0: int, y0: int, x1: int, y1: int, layers: List[Layer], buffer: WriteableBuffer, display: displayio.Display, scale: int, background: int) -> None: +//| """Render and send to the display a fragment of the screen. +//| +//| :param int x0: Left edge of the fragment. +//| :param int y0: Top edge of the fragment. +//| :param int x1: Right edge of the fragment. +//| :param int y1: Bottom edge of the fragment. +//| :param layers: A list of the :py:class:`~_stage.Layer` objects. +//| :type layers: list[Layer] +//| :param ~circuitpython_typing.WriteableBuffer buffer: A buffer to use for rendering. +//| :param ~displayio.Display display: The display to use. +//| :param int scale: How many times should the image be scaled up. +//| :param int background: What color to display when nothing is there. +//| +//| There are also no sanity checks, outside of the basic overflow +//| checking. The caller is responsible for making the passed parameters +//| valid. +//| +//| This function is intended for internal use in the ``stage`` library +//| and all the necessary checks are performed there.""" +//| +STATIC mp_obj_t stage_render(size_t n_args, const mp_obj_t *args) { + uint16_t x0 = mp_obj_get_int(args[0]); + uint16_t y0 = mp_obj_get_int(args[1]); + uint16_t x1 = mp_obj_get_int(args[2]); + uint16_t y1 = mp_obj_get_int(args[3]); + + size_t layers_size = 0; + mp_obj_t *layers; + mp_obj_get_array(args[4], &layers_size, &layers); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[5], &bufinfo, MP_BUFFER_WRITE); + uint16_t *buffer = bufinfo.buf; + size_t buffer_size = bufinfo.len / 2; // 16-bit indexing + + mp_obj_t native_display = mp_obj_cast_to_native_base(args[6], + &displayio_display_type); + if (!mp_obj_is_type(native_display, &displayio_display_type)) { + mp_raise_TypeError(translate("argument num/types mismatch")); + } + displayio_display_obj_t *display = MP_OBJ_TO_PTR(native_display); + uint8_t scale = mp_obj_get_int(args[7]); + int16_t vx = mp_obj_get_int(args[8]); + int16_t vy = mp_obj_get_int(args[9]); + uint16_t background = 0; + + render_stage(x0, y0, x1, y1, vx, vy, layers, layers_size, + buffer, buffer_size, display, scale, background); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stage_render_obj, 10, 10, stage_render); + + +STATIC const mp_rom_map_elem_t stage_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__stage) }, + { MP_ROM_QSTR(MP_QSTR_Layer), MP_ROM_PTR(&mp_type_layer) }, + { MP_ROM_QSTR(MP_QSTR_Text), MP_ROM_PTR(&mp_type_text) }, + { MP_ROM_QSTR(MP_QSTR_render), MP_ROM_PTR(&stage_render_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(stage_module_globals, stage_module_globals_table); + +const mp_obj_module_t stage_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&stage_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR__stage, stage_module, CIRCUITPY_STAGE); diff --git a/circuitpython/shared-bindings/_stage/__init__.h b/circuitpython/shared-bindings/_stage/__init__.h new file mode 100644 index 0000000..2df81cb --- /dev/null +++ b/circuitpython/shared-bindings/_stage/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Radomir Dopieralski + * + * 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__STAGE_H +#define MICROPY_INCLUDED__STAGE_H + +#include "shared-module/_stage/__init__.h" + +#endif // MICROPY_INCLUDED__STAGE diff --git a/circuitpython/shared-bindings/adafruit_bus_device/__init__.c b/circuitpython/shared-bindings/adafruit_bus_device/__init__.c new file mode 100644 index 0000000..be2378b --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_bus_device/__init__.c @@ -0,0 +1,82 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Mark Komus + * + * 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 "py/mphal.h" +#include "py/objproperty.h" + +#include "shared-bindings/adafruit_bus_device/__init__.h" +#include "shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.h" +#include "shared-bindings/adafruit_bus_device/spi_device/SPIDevice.h" + +STATIC const mp_rom_map_elem_t adafruit_bus_device_i2c_device_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_i2c_device) }, + { MP_ROM_QSTR(MP_QSTR_I2CDevice), MP_ROM_PTR(&adafruit_bus_device_i2cdevice_type) }, +}; +STATIC MP_DEFINE_CONST_DICT(adafruit_bus_device_i2c_device_globals, adafruit_bus_device_i2c_device_globals_table); + +const mp_obj_module_t adafruit_bus_device_i2c_device_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&adafruit_bus_device_i2c_device_globals, +}; + +STATIC const mp_rom_map_elem_t adafruit_bus_device_spi_device_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_spi_device) }, + { MP_ROM_QSTR(MP_QSTR_SPIDevice), MP_ROM_PTR(&adafruit_bus_device_spidevice_type) }, +}; +STATIC MP_DEFINE_CONST_DICT(adafruit_bus_device_spi_device_globals, adafruit_bus_device_spi_device_globals_table); + +const mp_obj_module_t adafruit_bus_device_spi_device_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&adafruit_bus_device_spi_device_globals, +}; + +//| """Hardware accelerated external bus access +//| +//| The I2CDevice and SPIDevice helper classes make managing transaction state on a bus easy. +//| For example, they manage locking the bus to prevent other concurrent access. For SPI +//| devices, it manages the chip select and protocol changes such as mode. For I2C, it +//| manages the device address.""" +//| +STATIC const mp_rom_map_elem_t adafruit_bus_device_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_adafruit_bus_device) }, + { MP_ROM_QSTR(MP_QSTR_i2c_device), MP_ROM_PTR(&adafruit_bus_device_i2c_device_module) }, + { MP_ROM_QSTR(MP_QSTR_spi_device), MP_ROM_PTR(&adafruit_bus_device_spi_device_module) }, +}; + +STATIC MP_DEFINE_CONST_DICT(adafruit_bus_device_module_globals, adafruit_bus_device_module_globals_table); + +const mp_obj_module_t adafruit_bus_device_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&adafruit_bus_device_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_adafruit_bus_device, adafruit_bus_device_module, CIRCUITPY_BUSDEVICE); +MP_REGISTER_MODULE(MP_QSTR_adafruit_bus_device_dot_i2c_device, adafruit_bus_device_i2c_device_module, CIRCUITPY_BUSDEVICE); +MP_REGISTER_MODULE(MP_QSTR_adafruit_bus_device_dot_spi_device, adafruit_bus_device_spi_device_module, CIRCUITPY_BUSDEVICE); diff --git a/circuitpython/shared-bindings/adafruit_bus_device/__init__.h b/circuitpython/shared-bindings/adafruit_bus_device/__init__.h new file mode 100644 index 0000000..4a66980 --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_bus_device/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Mark Komus + * + * 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_BUSDEVICE___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSDEVICE___INIT___H + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSDEVICE___INIT___H diff --git a/circuitpython/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c b/circuitpython/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c new file mode 100644 index 0000000..327e8e7 --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c @@ -0,0 +1,288 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Mark Komus + * + * 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/adafruit_bus_device/i2c_device/I2CDevice.h" +#include "shared-bindings/util.h" +#include "shared-module/adafruit_bus_device/i2c_device/I2CDevice.h" + +#include "shared/runtime/buffer_helper.h" +#include "shared/runtime/context_manager_helpers.h" +#include "py/runtime.h" +#include "py/smallint.h" +#include "supervisor/shared/translate.h" + + +//| class I2CDevice: +//| """I2C Device Manager""" +//| +//| def __init__(self, i2c: busio.I2C, device_address: int, probe: bool = True) -> None: +//| +//| """Represents a single I2C device and manages locking the bus and the device +//| address. +//| +//| :param ~busio.I2C i2c: The I2C bus the device is on +//| :param int device_address: The 7 bit device address +//| :param bool probe: Probe for the device upon object creation, default is true +//| +//| Example:: +//| +//| import busio +//| from board import * +//| from adafruit_bus_device.i2c_device import I2CDevice +//| with busio.I2C(SCL, SDA) as i2c: +//| device = I2CDevice(i2c, 0x70) +//| bytes_read = bytearray(4) +//| with device: +//| device.readinto(bytes_read) +//| # A second transaction +//| with device: +//| device.write(bytes_read) +//| """ +//| ... +//| +STATIC mp_obj_t adafruit_bus_device_i2cdevice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + adafruit_bus_device_i2cdevice_obj_t *self = m_new_obj(adafruit_bus_device_i2cdevice_obj_t); + self->base.type = &adafruit_bus_device_i2cdevice_type; + enum { ARG_i2c, ARG_device_address, ARG_probe }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_device_address, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_probe, MP_ARG_BOOL, {.u_bool = true} }, + }; + 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); + + mp_obj_t *i2c = args[ARG_i2c].u_obj; + + common_hal_adafruit_bus_device_i2cdevice_construct(MP_OBJ_TO_PTR(self), i2c, args[ARG_device_address].u_int); + if (args[ARG_probe].u_bool == true) { + common_hal_adafruit_bus_device_i2cdevice_probe_for_device(self); + } + + return (mp_obj_t)self; +} + +//| def __enter__(self) -> I2CDevice: +//| """Context manager entry to lock bus.""" +//| ... +//| +STATIC mp_obj_t adafruit_bus_device_i2cdevice_obj___enter__(mp_obj_t self_in) { + adafruit_bus_device_i2cdevice_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_adafruit_bus_device_i2cdevice_lock(self); + return self; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(adafruit_bus_device_i2cdevice___enter___obj, adafruit_bus_device_i2cdevice_obj___enter__); + +//| def __exit__(self) -> None: +//| """Automatically unlocks the bus on exit.""" +//| ... +//| +STATIC mp_obj_t adafruit_bus_device_i2cdevice_obj___exit__(size_t n_args, const mp_obj_t *args) { + common_hal_adafruit_bus_device_i2cdevice_unlock(MP_OBJ_TO_PTR(args[0])); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(adafruit_bus_device_i2cdevice___exit___obj, 4, 4, adafruit_bus_device_i2cdevice_obj___exit__); + +//| import sys +//| def readinto(self, buffer: WriteableBuffer, *, start: int = 0, end: int = sys.maxsize) -> None: +//| """Read into ``buffer`` from the device. +//| +//| 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, use ``len(buffer)`` +//| """ +//| ... +//| +STATIC mp_obj_t adafruit_bus_device_i2cdevice_readinto(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 }, + { 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} }, + }; + + adafruit_bus_device_i2cdevice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + 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_obj_t dest[8]; + uint8_t num_kws = 1; + + mp_load_method(self->i2c, MP_QSTR_readfrom_into, dest); + dest[2] = MP_OBJ_NEW_SMALL_INT(self->device_address); + dest[3] = args[ARG_buffer].u_obj; + // dest[4] = mp_obj_new_str("start", 5); + dest[4] = MP_OBJ_NEW_QSTR(MP_QSTR_start); + dest[5] = MP_OBJ_NEW_SMALL_INT(args[ARG_start].u_int); + if (args[ARG_end].u_int != INT_MAX) { + dest[6] = MP_OBJ_NEW_QSTR(MP_QSTR_end); + dest[7] = MP_OBJ_NEW_SMALL_INT(args[ARG_end].u_int); + num_kws++; + } + mp_call_method_n_kw(2, num_kws, dest); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(adafruit_bus_device_i2cdevice_readinto_obj, 1, adafruit_bus_device_i2cdevice_readinto); + +//| import sys +//| def write(self, buffer: ReadableBuffer, *, start: int = 0, end: int = sys.maxsize) -> None: +//| """Write the bytes from ``buffer`` to the device, 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]``. +//| +//| :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 adafruit_bus_device_i2cdevice_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 }, + { 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} }, + }; + adafruit_bus_device_i2cdevice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + 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_obj_t dest[8]; + uint8_t num_kws = 1; + + mp_load_method(self->i2c, MP_QSTR_writeto, dest); + dest[2] = MP_OBJ_NEW_SMALL_INT(self->device_address); + dest[3] = args[ARG_buffer].u_obj; + dest[4] = MP_OBJ_NEW_QSTR(MP_QSTR_start); + dest[5] = MP_OBJ_NEW_SMALL_INT(args[ARG_start].u_int); + if (args[ARG_end].u_int != INT_MAX) { + dest[6] = MP_OBJ_NEW_QSTR(MP_QSTR_end); + dest[7] = MP_OBJ_NEW_SMALL_INT(args[ARG_end].u_int); + num_kws++; + } + + mp_call_method_n_kw(2, num_kws, dest); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(adafruit_bus_device_i2cdevice_write_obj, 1, adafruit_bus_device_i2cdevice_write); + + +//| import sys +//| def write_then_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 the bytes from ``out_buffer`` to the device, then immediately +//| reads into ``in_buffer`` from the device. +//| +//| 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]``. +//| +//| :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 adafruit_bus_device_i2cdevice_write_then_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 }, + { MP_QSTR_in_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { 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} }, + }; + adafruit_bus_device_i2cdevice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + 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_obj_t dest[13]; + uint8_t num_kws = 2; + uint8_t index = 2; + + mp_load_method(self->i2c, MP_QSTR_writeto_then_readfrom, dest); + dest[index++] = MP_OBJ_NEW_SMALL_INT(self->device_address); + dest[index++] = args[ARG_out_buffer].u_obj; + dest[index++] = args[ARG_in_buffer].u_obj; + dest[index++] = MP_OBJ_NEW_QSTR(MP_QSTR_out_start); + dest[index++] = MP_OBJ_NEW_SMALL_INT(args[ARG_out_start].u_int); + if (args[ARG_out_end].u_int != INT_MAX) { + dest[index++] = MP_OBJ_NEW_QSTR(MP_QSTR_out_end); + dest[index++] = MP_OBJ_NEW_SMALL_INT(args[ARG_out_end].u_int); + num_kws++; + } + dest[index++] = MP_OBJ_NEW_QSTR(MP_QSTR_in_start); + dest[index++] = MP_OBJ_NEW_SMALL_INT(args[ARG_in_start].u_int); + if (args[ARG_in_end].u_int != INT_MAX) { + dest[index++] = MP_OBJ_NEW_QSTR(MP_QSTR_in_end); + dest[index++] = MP_OBJ_NEW_SMALL_INT(args[ARG_in_end].u_int); + num_kws++; + } + + mp_call_method_n_kw(3, num_kws, dest); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(adafruit_bus_device_i2cdevice_write_then_readinto_obj, 1, adafruit_bus_device_i2cdevice_write_then_readinto); + +STATIC const mp_rom_map_elem_t adafruit_bus_device_i2cdevice_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&adafruit_bus_device_i2cdevice___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&adafruit_bus_device_i2cdevice___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&adafruit_bus_device_i2cdevice_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&adafruit_bus_device_i2cdevice_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_then_readinto), MP_ROM_PTR(&adafruit_bus_device_i2cdevice_write_then_readinto_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(adafruit_bus_device_i2cdevice_locals_dict, adafruit_bus_device_i2cdevice_locals_dict_table); + +const mp_obj_type_t adafruit_bus_device_i2cdevice_type = { + { &mp_type_type }, + .name = MP_QSTR_I2CDevice, + .make_new = adafruit_bus_device_i2cdevice_make_new, + .locals_dict = (mp_obj_dict_t *)&adafruit_bus_device_i2cdevice_locals_dict, +}; diff --git a/circuitpython/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.h b/circuitpython/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.h new file mode 100644 index 0000000..71c169a --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Mark Komus + * + * 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_BUSDEVICE_I2CDEVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSDEVICE_I2CDEVICE_H + +#include "py/obj.h" + +#include "shared-module/adafruit_bus_device/i2c_device/I2CDevice.h" +// #include "shared-bindings/busio/I2C.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_type_t adafruit_bus_device_i2cdevice_type; + +// Initializes the hardware peripheral. +extern void common_hal_adafruit_bus_device_i2cdevice_construct(adafruit_bus_device_i2cdevice_obj_t *self, mp_obj_t *i2c, uint8_t device_address); +extern void common_hal_adafruit_bus_device_i2cdevice_lock(adafruit_bus_device_i2cdevice_obj_t *self); +extern void common_hal_adafruit_bus_device_i2cdevice_unlock(adafruit_bus_device_i2cdevice_obj_t *self); +extern void common_hal_adafruit_bus_device_i2cdevice_probe_for_device(adafruit_bus_device_i2cdevice_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSDEVICE_I2CDEVICE_H diff --git a/circuitpython/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c b/circuitpython/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c new file mode 100644 index 0000000..2c5708b --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c @@ -0,0 +1,140 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Mark Komus + * + * 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 "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/adafruit_bus_device/spi_device/SPIDevice.h" +#include "shared-bindings/util.h" +#include "shared-module/adafruit_bus_device/spi_device/SPIDevice.h" +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + + +#include "shared/runtime/buffer_helper.h" +#include "shared/runtime/context_manager_helpers.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +//| class SPIDevice: +//| """SPI Device Manager""" +//| +//| def __init__(self, spi: busio.SPI, chip_select: microcontroller.Pin, *, baudrate: int = 100000, polarity: int = 0, phase: int = 0, extra_clocks : int = 0) -> None: +//| +//| """ +//| Represents a single SPI device and manages locking the bus and the device address. +//| +//| :param ~busio.SPI spi: The SPI bus the device is on +//| :param ~digitalio.DigitalInOut chip_select: The chip select pin object that implements the DigitalInOut API. +//| :param bool cs_active_value: Set to true if your device requires CS to be active high. Defaults to false. +//| :param int extra_clocks: The minimum number of clock cycles to cycle the bus after CS is high. (Used for SD cards.) +//| +//| Example:: +//| +//| import busio +//| import digitalio +//| from board import * +//| from adafruit_bus_device.spi_device import SPIDevice +//| with busio.SPI(SCK, MOSI, MISO) as spi_bus: +//| cs = digitalio.DigitalInOut(D10) +//| device = SPIDevice(spi_bus, cs) +//| bytes_read = bytearray(4) +//| # The object assigned to spi in the with statements below +//| # is the original spi_bus object. We are using the busio.SPI +//| # operations busio.SPI.readinto() and busio.SPI.write(). +//| with device as spi: +//| spi.readinto(bytes_read) +//| # A second transaction +//| with device as spi: +//| spi.write(bytes_read)""" +//| ... +//| +STATIC mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + adafruit_bus_device_spidevice_obj_t *self = m_new_obj(adafruit_bus_device_spidevice_obj_t); + self->base.type = &adafruit_bus_device_spidevice_type; + enum { ARG_spi, ARG_chip_select, ARG_cs_active_value, ARG_baudrate, ARG_polarity, ARG_phase, ARG_extra_clocks }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_spi, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_chip_select, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_cs_active_value, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { 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_extra_clocks, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + 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); + + busio_spi_obj_t *spi = args[ARG_spi].u_obj; + + common_hal_adafruit_bus_device_spidevice_construct(MP_OBJ_TO_PTR(self), spi, args[ARG_chip_select].u_obj, args[ARG_cs_active_value].u_bool, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, + args[ARG_phase].u_int, args[ARG_extra_clocks].u_int); + + if (args[ARG_chip_select].u_obj != MP_OBJ_NULL) { + digitalinout_result_t result = common_hal_digitalio_digitalinout_switch_to_output(MP_OBJ_TO_PTR(args[ARG_chip_select].u_obj), + true, DRIVE_MODE_PUSH_PULL); + if (result == DIGITALINOUT_INPUT_ONLY) { + mp_raise_NotImplementedError(translate("Pin is input only")); + } + } + + return (mp_obj_t)self; +} + +//| def __enter__(self) -> busio.SPI: +//| """Starts a SPI transaction by configuring the SPI and asserting chip select.""" +//| ... +//| +STATIC mp_obj_t adafruit_bus_device_spidevice_obj___enter__(mp_obj_t self_in) { + adafruit_bus_device_spidevice_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_adafruit_bus_device_spidevice_enter(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(adafruit_bus_device_spidevice___enter___obj, adafruit_bus_device_spidevice_obj___enter__); + + +//| def __exit__(self) -> None: +//| """Ends a SPI transaction by deasserting chip select. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t adafruit_bus_device_spidevice_obj___exit__(size_t n_args, const mp_obj_t *args) { + common_hal_adafruit_bus_device_spidevice_exit(MP_OBJ_TO_PTR(args[0])); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(adafruit_bus_device_spidevice___exit___obj, 4, 4, adafruit_bus_device_spidevice_obj___exit__); + +STATIC const mp_rom_map_elem_t adafruit_bus_device_spidevice_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&adafruit_bus_device_spidevice___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&adafruit_bus_device_spidevice___exit___obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(adafruit_bus_device_spidevice_locals_dict, adafruit_bus_device_spidevice_locals_dict_table); + +const mp_obj_type_t adafruit_bus_device_spidevice_type = { + { &mp_type_type }, + .name = MP_QSTR_SPIDevice, + .make_new = adafruit_bus_device_spidevice_make_new, + .locals_dict = (mp_obj_dict_t *)&adafruit_bus_device_spidevice_locals_dict, +}; diff --git a/circuitpython/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.h b/circuitpython/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.h new file mode 100644 index 0000000..b92a5eb --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Mark Komus + * + * 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_BUSDEVICE_SPIDEVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSDEVICE_SPIDEVICE_H + +#include "py/obj.h" + +#include "shared-module/adafruit_bus_device/spi_device/SPIDevice.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_type_t adafruit_bus_device_spidevice_type; + +// Initializes the hardware peripheral. +extern void common_hal_adafruit_bus_device_spidevice_construct(adafruit_bus_device_spidevice_obj_t *self, busio_spi_obj_t *spi, digitalio_digitalinout_obj_t *cs, + bool cs_active_value, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t extra_clocks); +extern mp_obj_t common_hal_adafruit_bus_device_spidevice_enter(adafruit_bus_device_spidevice_obj_t *self); +extern void common_hal_adafruit_bus_device_spidevice_exit(adafruit_bus_device_spidevice_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSDEVICE_SPIDEVICE_H diff --git a/circuitpython/shared-bindings/adafruit_pixelbuf/PixelBuf.c b/circuitpython/shared-bindings/adafruit_pixelbuf/PixelBuf.c new file mode 100644 index 0000000..d9436ea --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_pixelbuf/PixelBuf.c @@ -0,0 +1,377 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * + * 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 "py/obj.h" +#include "py/objarray.h" +#include "py/objtype.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/gc.h" + +#include <string.h> + +#include "shared-bindings/adafruit_pixelbuf/PixelBuf.h" +#include "shared-module/adafruit_pixelbuf/PixelBuf.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + +#ifdef CIRCUITPY_ULAB +#include "extmod/ulab/code/ndarray.h" +#endif + +static void parse_byteorder(mp_obj_t byteorder_obj, pixelbuf_byteorder_details_t *parsed); + +//| class PixelBuf: +//| """A fast RGB[W] pixel buffer for LED and similar devices.""" +//| +//| def __init__(self, size: int, *, byteorder: str = "BGR", brightness: float = 0, auto_write: bool = False, header: ReadableBuffer = b"", trailer: ReadableBuffer = b"") -> None: +//| """Create a PixelBuf object of the specified size, byteorder, and bits per pixel. +//| +//| When brightness is less than 1.0, a second buffer will be used to store the color values +//| before they are adjusted for brightness. +//| +//| When ``P`` (PWM duration) is present as the 4th character of the byteorder +//| string, the 4th value in the tuple/list for a pixel is the individual pixel +//| brightness (0.0-1.0) and will enable a Dotstar compatible 1st byte for each +//| pixel. +//| +//| :param int size: Number of pixels +//| :param str byteorder: Byte order string (such as "RGB", "RGBW" or "PBGR") +//| :param float brightness: Brightness (0 to 1.0, default 1.0) +//| :param bool auto_write: Whether to automatically write pixels (Default False) +//| :param ~circuitpython_typing.ReadableBuffer header: Sequence of bytes to always send before pixel values. +//| :param ~circuitpython_typing.ReadableBuffer trailer: Sequence of bytes to always send after pixel values.""" +//| ... +//| +STATIC mp_obj_t pixelbuf_pixelbuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_size, ARG_byteorder, ARG_brightness, ARG_auto_write, ARG_header, ARG_trailer }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_size, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_byteorder, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_BGR) } }, + { MP_QSTR_brightness, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + { MP_QSTR_auto_write, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_header, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + { MP_QSTR_trailer, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + }; + 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); + pixelbuf_byteorder_details_t byteorder_details; + + parse_byteorder(args[ARG_byteorder].u_obj, &byteorder_details); + + mp_buffer_info_t header_bufinfo; + mp_buffer_info_t trailer_bufinfo; + + if (!mp_get_buffer(args[ARG_header].u_obj, &header_bufinfo, MP_BUFFER_READ)) { + header_bufinfo.buf = NULL; + header_bufinfo.len = 0; + } + if (!mp_get_buffer(args[ARG_trailer].u_obj, &trailer_bufinfo, MP_BUFFER_READ)) { + trailer_bufinfo.buf = NULL; + trailer_bufinfo.len = 0; + } + + float brightness = 1.0; + if (args[ARG_brightness].u_obj != mp_const_none) { + brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + if (brightness < 0) { + brightness = 0; + } else if (brightness > 1) { + brightness = 1; + } + } + + // Validation complete, allocate and populate object. + pixelbuf_pixelbuf_obj_t *self = m_new_obj(pixelbuf_pixelbuf_obj_t); + self->base.type = &pixelbuf_pixelbuf_type; + common_hal_adafruit_pixelbuf_pixelbuf_construct(self, args[ARG_size].u_int, + &byteorder_details, brightness, args[ARG_auto_write].u_bool, header_bufinfo.buf, + header_bufinfo.len, trailer_bufinfo.buf, trailer_bufinfo.len); + + return MP_OBJ_FROM_PTR(self); +} + +static void parse_byteorder(mp_obj_t byteorder_obj, pixelbuf_byteorder_details_t *parsed) { + if (!mp_obj_is_str(byteorder_obj)) { + mp_raise_TypeError(translate("byteorder is not a string")); + } + + size_t bo_len; + const char *byteorder = mp_obj_str_get_data(byteorder_obj, &bo_len); + if (bo_len < 3 || bo_len > 4) { + mp_raise_ValueError(translate("Invalid byteorder string")); + } + parsed->order_string = byteorder_obj; + + parsed->bpp = bo_len; + char *dotstar = strchr(byteorder, 'P'); + char *r = strchr(byteorder, 'R'); + char *g = strchr(byteorder, 'G'); + char *b = strchr(byteorder, 'B'); + char *w = strchr(byteorder, 'W'); + int num_chars = (dotstar ? 1 : 0) + (w ? 1 : 0) + (r ? 1 : 0) + (g ? 1 : 0) + (b ? 1 : 0); + if ((num_chars < parsed->bpp) || !(r && b && g)) { + mp_raise_ValueError(translate("Invalid byteorder string")); + } + parsed->is_dotstar = dotstar ? true : false; + parsed->has_white = w ? true : false; + parsed->byteorder.r = r - byteorder; + parsed->byteorder.g = g - byteorder; + parsed->byteorder.b = b - byteorder; + parsed->byteorder.w = w ? w - byteorder : 0; + // The dotstar brightness byte is always first (as it goes with the pixel start bits) + if (dotstar && byteorder[0] != 'P') { + mp_raise_ValueError(translate("Invalid byteorder string")); + } + if (parsed->has_white && parsed->is_dotstar) { + mp_raise_ValueError(translate("Invalid byteorder string")); + } +} + +//| bpp: int +//| """The number of bytes per pixel in the buffer (read-only)""" +//| +STATIC mp_obj_t pixelbuf_pixelbuf_obj_get_bpp(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(common_hal_adafruit_pixelbuf_pixelbuf_get_bpp(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pixelbuf_pixelbuf_get_bpp_obj, pixelbuf_pixelbuf_obj_get_bpp); + +MP_PROPERTY_GETTER(pixelbuf_pixelbuf_bpp_obj, + (mp_obj_t)&pixelbuf_pixelbuf_get_bpp_obj); + + +//| brightness: float +//| """Float value between 0 and 1. Output brightness. +//| +//| When brightness is less than 1.0, a second buffer will be used to store the color values +//| before they are adjusted for brightness.""" +//| +STATIC mp_obj_t pixelbuf_pixelbuf_obj_get_brightness(mp_obj_t self_in) { + return mp_obj_new_float(common_hal_adafruit_pixelbuf_pixelbuf_get_brightness(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pixelbuf_pixelbuf_get_brightness_obj, pixelbuf_pixelbuf_obj_get_brightness); + + +STATIC mp_obj_t pixelbuf_pixelbuf_obj_set_brightness(mp_obj_t self_in, mp_obj_t value) { + mp_float_t brightness = mp_obj_get_float(value); + if (brightness > 1) { + brightness = 1; + } else if (brightness < 0) { + brightness = 0; + } + common_hal_adafruit_pixelbuf_pixelbuf_set_brightness(self_in, brightness); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pixelbuf_pixelbuf_set_brightness_obj, pixelbuf_pixelbuf_obj_set_brightness); + +MP_PROPERTY_GETSET(pixelbuf_pixelbuf_brightness_obj, + (mp_obj_t)&pixelbuf_pixelbuf_get_brightness_obj, + (mp_obj_t)&pixelbuf_pixelbuf_set_brightness_obj); + +//| auto_write: bool +//| """Whether to automatically write the pixels after each update.""" +//| +STATIC mp_obj_t pixelbuf_pixelbuf_obj_get_auto_write(mp_obj_t self_in) { + return mp_obj_new_bool(common_hal_adafruit_pixelbuf_pixelbuf_get_auto_write(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pixelbuf_pixelbuf_get_auto_write_obj, pixelbuf_pixelbuf_obj_get_auto_write); + + +STATIC mp_obj_t pixelbuf_pixelbuf_obj_set_auto_write(mp_obj_t self_in, mp_obj_t value) { + common_hal_adafruit_pixelbuf_pixelbuf_set_auto_write(self_in, mp_obj_is_true(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pixelbuf_pixelbuf_set_auto_write_obj, pixelbuf_pixelbuf_obj_set_auto_write); + +MP_PROPERTY_GETSET(pixelbuf_pixelbuf_auto_write_obj, + (mp_obj_t)&pixelbuf_pixelbuf_get_auto_write_obj, + (mp_obj_t)&pixelbuf_pixelbuf_set_auto_write_obj); + +//| byteorder: str +//| """byteorder string for the buffer (read-only)""" +//| +STATIC mp_obj_t pixelbuf_pixelbuf_obj_get_byteorder(mp_obj_t self_in) { + return common_hal_adafruit_pixelbuf_pixelbuf_get_byteorder_string(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(pixelbuf_pixelbuf_get_byteorder_str, pixelbuf_pixelbuf_obj_get_byteorder); + +MP_PROPERTY_GETTER(pixelbuf_pixelbuf_byteorder_str, + (mp_obj_t)&pixelbuf_pixelbuf_get_byteorder_str); + +STATIC mp_obj_t pixelbuf_pixelbuf_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_const_true; + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(common_hal_adafruit_pixelbuf_pixelbuf_get_len(self_in)); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def show(self) -> None: +//| """Transmits the color data to the pixels so that they are shown. This is done automatically +//| when `auto_write` is True.""" +//| ... +//| + +STATIC mp_obj_t pixelbuf_pixelbuf_show(mp_obj_t self_in) { + common_hal_adafruit_pixelbuf_pixelbuf_show(self_in); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pixelbuf_pixelbuf_show_obj, pixelbuf_pixelbuf_show); + +//| def fill(self, color: Union[int, Tuple[int, int, int], Tuple[int, int, int, float]]) -> None: +//| """Fills the given pixelbuf with the given color.""" +//| ... +//| + +STATIC mp_obj_t pixelbuf_pixelbuf_fill(mp_obj_t self_in, mp_obj_t value) { + common_hal_adafruit_pixelbuf_pixelbuf_fill(self_in, value); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pixelbuf_pixelbuf_fill_obj, pixelbuf_pixelbuf_fill); + +//| @overload +//| def __getitem__(self, index: slice) -> Union[Tuple[Tuple[int, int, int], ...], Tuple[Tuple[int, int, int, float], ...]]: ... +//| @overload +//| def __getitem__(self, index: int) -> Union[Tuple[int, int, int], Tuple[int, int, int, float]]: +//| """Returns the pixel value at the given index as a tuple of (Red, Green, Blue[, White]) values +//| between 0 and 255. When in PWM (DotStar) mode, the 4th tuple value is a float of the pixel +//| intensity from 0-1.0.""" +//| ... +//| +//| @overload +//| def __setitem__(self, index: slice, value: Tuple[Union[int, Tuple[float, ...], List[float]], ...]) -> None: ... +//| @overload +//| def __setitem__(self, index: slice, value: List[Union[int, Tuple[float, ...], List[float]]]) -> None: ... +//| @overload +//| def __setitem__(self, index: int, value: Union[int, Tuple[float, ...], List[float]]) -> None: +//| """Sets the pixel value at the given index. Value can either be a tuple or integer. Tuples are +//| The individual (Red, Green, Blue[, White]) values between 0 and 255. If given an integer, the +//| red, green and blue values are packed into the lower three bytes (0xRRGGBB). +//| For RGBW byteorders, if given only RGB values either as an int or as a tuple, the white value +//| is used instead when the red, green, and blue values are the same.""" +//| ... +//| +STATIC mp_obj_t pixelbuf_pixelbuf_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete item + // slice deletion + return MP_OBJ_NULL; // op not supported + } + + if (0) { + #if MICROPY_PY_BUILTINS_SLICE + } else if (mp_obj_is_type(index_in, &mp_type_slice)) { + mp_bound_slice_t slice; + + size_t length = common_hal_adafruit_pixelbuf_pixelbuf_get_len(self_in); + mp_seq_get_fast_slice_indexes(length, index_in, &slice); + static mp_obj_tuple_t flat_item_tuple = { + .base = {&mp_type_tuple}, + .len = 0, + .items = { + mp_const_none, + mp_const_none, + mp_const_none, + mp_const_none, + } + }; + + size_t slice_len; + if (slice.step > 0) { + slice_len = slice.stop - slice.start; + } else { + slice_len = 1 + slice.start - slice.stop; + } + if (slice.step > 1 || slice.step < -1) { + size_t step = slice.step > 0 ? slice.step : slice.step * -1; + slice_len = (slice_len / step) + (slice_len % step ? 1 : 0); + } + + if (value == MP_OBJ_SENTINEL) { // Get + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(slice_len, NULL)); + for (uint i = 0; i < slice_len; i++) { + t->items[i] = common_hal_adafruit_pixelbuf_pixelbuf_get_pixel(self_in, i * slice.step + slice.start); + } + return MP_OBJ_FROM_PTR(t); + } else { // Set + #if MICROPY_PY_ARRAY_SLICE_ASSIGN + + size_t num_items = mp_obj_get_int(mp_obj_len(value)); + + if (num_items != slice_len && num_items != (slice_len * common_hal_adafruit_pixelbuf_pixelbuf_get_bpp(self_in))) { + mp_raise_ValueError_varg(translate("Unmatched number of items on RHS (expected %d, got %d)."), slice_len, num_items); + } + common_hal_adafruit_pixelbuf_pixelbuf_set_pixels(self_in, slice.start, slice.step, slice_len, value, + num_items != slice_len ? &flat_item_tuple : mp_const_none); + return mp_const_none; + #else + return MP_OBJ_NULL; // op not supported + #endif + } + #endif + } else { // Single index rather than slice. + size_t length = common_hal_adafruit_pixelbuf_pixelbuf_get_len(self_in); + size_t index = mp_get_index(mp_obj_get_type(self_in), length, index_in, false); + + if (value == MP_OBJ_SENTINEL) { // Get + return common_hal_adafruit_pixelbuf_pixelbuf_get_pixel(self_in, index); + } else { // Store + common_hal_adafruit_pixelbuf_pixelbuf_set_pixel(self_in, index, value); + return mp_const_none; + } + } +} + +STATIC const mp_rom_map_elem_t pixelbuf_pixelbuf_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_auto_write), MP_ROM_PTR(&pixelbuf_pixelbuf_auto_write_obj)}, + { MP_ROM_QSTR(MP_QSTR_bpp), MP_ROM_PTR(&pixelbuf_pixelbuf_bpp_obj)}, + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&pixelbuf_pixelbuf_brightness_obj)}, + { MP_ROM_QSTR(MP_QSTR_byteorder), MP_ROM_PTR(&pixelbuf_pixelbuf_byteorder_str)}, + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&pixelbuf_pixelbuf_show_obj)}, + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&pixelbuf_pixelbuf_fill_obj)}, +}; + +STATIC MP_DEFINE_CONST_DICT(pixelbuf_pixelbuf_locals_dict, pixelbuf_pixelbuf_locals_dict_table); + + +const mp_obj_type_t pixelbuf_pixelbuf_type = { + { &mp_type_type }, + .name = MP_QSTR_PixelBuf, + .flags = MP_TYPE_FLAG_EXTENDED, + .locals_dict = (mp_obj_t)&pixelbuf_pixelbuf_locals_dict, + .make_new = pixelbuf_pixelbuf_make_new, + MP_TYPE_EXTENDED_FIELDS( + .subscr = pixelbuf_pixelbuf_subscr, + .unary_op = pixelbuf_pixelbuf_unary_op, + .getiter = mp_obj_new_generic_iterator, + ), +}; diff --git a/circuitpython/shared-bindings/adafruit_pixelbuf/PixelBuf.h b/circuitpython/shared-bindings/adafruit_pixelbuf/PixelBuf.h new file mode 100644 index 0000000..7ae3d6a --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_pixelbuf/PixelBuf.h @@ -0,0 +1,52 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * + * 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 CP_SHARED_BINDINGS_PIXELBUF_PIXELBUF_H +#define CP_SHARED_BINDINGS_PIXELBUF_PIXELBUF_H + +#include "shared-module/adafruit_pixelbuf/PixelBuf.h" + +extern const mp_obj_type_t pixelbuf_pixelbuf_type; + +void common_hal_adafruit_pixelbuf_pixelbuf_construct(pixelbuf_pixelbuf_obj_t *self, size_t n, + pixelbuf_byteorder_details_t *byteorder, mp_float_t brightness, bool auto_write, uint8_t *header, + size_t header_len, uint8_t *trailer, size_t trailer_len); + +// These take mp_obj_t because they are called on subclasses of PixelBuf. +uint8_t common_hal_adafruit_pixelbuf_pixelbuf_get_bpp(mp_obj_t self); +mp_float_t common_hal_adafruit_pixelbuf_pixelbuf_get_brightness(mp_obj_t self); +void common_hal_adafruit_pixelbuf_pixelbuf_set_brightness(mp_obj_t self, mp_float_t brightness); +bool common_hal_adafruit_pixelbuf_pixelbuf_get_auto_write(mp_obj_t self); +void common_hal_adafruit_pixelbuf_pixelbuf_set_auto_write(mp_obj_t self, bool auto_write); +size_t common_hal_adafruit_pixelbuf_pixelbuf_get_len(mp_obj_t self_in); +mp_obj_t common_hal_adafruit_pixelbuf_pixelbuf_get_byteorder_string(mp_obj_t self); +void common_hal_adafruit_pixelbuf_pixelbuf_fill(mp_obj_t self, mp_obj_t item); +void common_hal_adafruit_pixelbuf_pixelbuf_show(mp_obj_t self); +mp_obj_t common_hal_adafruit_pixelbuf_pixelbuf_get_pixel(mp_obj_t self, size_t index); +void common_hal_adafruit_pixelbuf_pixelbuf_set_pixel(mp_obj_t self, size_t index, mp_obj_t item); +void common_hal_adafruit_pixelbuf_pixelbuf_set_pixels(mp_obj_t self_in, size_t start, mp_int_t step, size_t slice_len, mp_obj_t *values, mp_obj_tuple_t *flatten_to); + +#endif // CP_SHARED_BINDINGS_PIXELBUF_PIXELBUF_H diff --git a/circuitpython/shared-bindings/adafruit_pixelbuf/__init__.c b/circuitpython/shared-bindings/adafruit_pixelbuf/__init__.c new file mode 100644 index 0000000..7b8d0da --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_pixelbuf/__init__.c @@ -0,0 +1,58 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * + * 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 "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "py/objproperty.h" + +#include "shared-bindings/adafruit_pixelbuf/__init__.h" +#include "shared-bindings/adafruit_pixelbuf/PixelBuf.h" + + +//| """A fast RGB(W) pixel buffer library for like NeoPixel and DotStar +//| +//| The `adafruit_pixelbuf` module provides the :py:class:`PixelBuf` class to accelerate +//| RGB(W) strip/matrix manipulation, such as DotStar and Neopixel. +//| +//| Also available as ``_pixelbuf``. This usage has been deprecated. +//| +//| Byteorders are configured with strings, such as "RGB" or "RGBD".""" +// TODO: Pull in docs from adafruit_pixelbuf. + +STATIC const mp_rom_map_elem_t pixelbuf_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_adafruit_pixelbuf) }, + { MP_ROM_QSTR(MP_QSTR_PixelBuf), MP_ROM_PTR(&pixelbuf_pixelbuf_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pixelbuf_module_globals, pixelbuf_module_globals_table); + +const mp_obj_module_t pixelbuf_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&pixelbuf_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_adafruit_pixelbuf, pixelbuf_module, CIRCUITPY_PIXELBUF); diff --git a/circuitpython/shared-bindings/adafruit_pixelbuf/__init__.h b/circuitpython/shared-bindings/adafruit_pixelbuf/__init__.h new file mode 100644 index 0000000..9a84bc6 --- /dev/null +++ b/circuitpython/shared-bindings/adafruit_pixelbuf/__init__.h @@ -0,0 +1,30 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * + * 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 CP_SHARED_BINDINGS_PIXELBUF_INIT_H +#define CP_SHARED_BINDINGS_PIXELBUF_INIT_H + +#endif // CP_SHARED_BINDINGS_PIXELBUF_INIT_H diff --git a/circuitpython/shared-bindings/aesio/__init__.c b/circuitpython/shared-bindings/aesio/__init__.c new file mode 100644 index 0000000..716d203 --- /dev/null +++ b/circuitpython/shared-bindings/aesio/__init__.c @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "__init__.h" + +//| """AES encryption routines +//| +//| The `AES` module contains classes used to implement encryption +//| and decryption. It aims to be low overhead in terms of memory.""" + + +STATIC const mp_obj_tuple_t mp_aes_key_size_obj = { + {&mp_type_tuple}, + 3, + { + MP_OBJ_NEW_SMALL_INT(16), + MP_OBJ_NEW_SMALL_INT(24), + MP_OBJ_NEW_SMALL_INT(32), + } +}; + +STATIC const mp_rom_map_elem_t aesio_module_globals_table[] = { + {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_aesio)}, + {MP_ROM_QSTR(MP_QSTR_AES), MP_ROM_PTR(&aesio_aes_type) }, + {MP_ROM_QSTR(MP_QSTR_MODE_ECB), MP_ROM_INT(AES_MODE_ECB)}, + {MP_ROM_QSTR(MP_QSTR_MODE_CBC), MP_ROM_INT(AES_MODE_CBC)}, + {MP_ROM_QSTR(MP_QSTR_MODE_CTR), MP_ROM_INT(AES_MODE_CTR)}, + {MP_ROM_QSTR(MP_QSTR_block_size), MP_ROM_INT(AES_BLOCKLEN)}, + {MP_ROM_QSTR(MP_QSTR_key_size), (mp_obj_t)&mp_aes_key_size_obj}, +}; + +STATIC MP_DEFINE_CONST_DICT(aesio_module_globals, aesio_module_globals_table); + +const mp_obj_module_t aesio_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&aesio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_aesio, aesio_module, CIRCUITPY_AESIO); diff --git a/circuitpython/shared-bindings/aesio/__init__.h b/circuitpython/shared-bindings/aesio/__init__.h new file mode 100644 index 0000000..60f6b83 --- /dev/null +++ b/circuitpython/shared-bindings/aesio/__init__.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AESIO_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AESIO_H + +#include "shared-module/aesio/__init__.h" + +extern const mp_obj_type_t aesio_aes_type; + +void common_hal_aesio_aes_construct(aesio_aes_obj_t *self, + const uint8_t *key, + uint32_t key_length, + const uint8_t *iv, + int mode, + int counter); +void common_hal_aesio_aes_rekey(aesio_aes_obj_t *self, + const uint8_t *key, + uint32_t key_length, + const uint8_t *iv); +void common_hal_aesio_aes_set_mode(aesio_aes_obj_t *self, + int mode); +void common_hal_aesio_aes_encrypt(aesio_aes_obj_t *self, + uint8_t *buffer, + size_t len); +void common_hal_aesio_aes_decrypt(aesio_aes_obj_t *self, + uint8_t *buffer, + size_t len); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AESIO_H diff --git a/circuitpython/shared-bindings/aesio/aes.c b/circuitpython/shared-bindings/aesio/aes.c new file mode 100644 index 0000000..bb72b10 --- /dev/null +++ b/circuitpython/shared-bindings/aesio/aes.c @@ -0,0 +1,268 @@ +#include <stdint.h> +#include <string.h> + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/aesio/__init__.h" + +// Defined at the end of this file + +//| MODE_ECB: int +//| MODE_CBC: int +//| MODE_CTR: int +//| +//| class AES: +//| """Encrypt and decrypt AES streams""" +//| +//| def __init__(self, key: ReadableBuffer, mode: int = 0, iv: Optional[ReadableBuffer] = None, segment_size: int = 8) -> None: +//| """Create a new AES state with the given key. +//| +//| :param ~circuitpython_typing.ReadableBuffer key: A 16-, 24-, or 32-byte key +//| :param int mode: AES mode to use. One of: `MODE_ECB`, `MODE_CBC`, or +//| `MODE_CTR` +//| :param ~circuitpython_typing.ReadableBuffer iv: Initialization vector to use for CBC or CTR mode +//| +//| Additional arguments are supported for legacy reasons. +//| +//| Encrypting a string:: +//| +//| import aesio +//| from binascii import hexlify +//| +//| key = b'Sixteen byte key' +//| inp = b'CircuitPython!!!' # Note: 16-bytes long +//| outp = bytearray(len(inp)) +//| cipher = aesio.AES(key, aesio.MODE_ECB) +//| cipher.encrypt_into(inp, outp) +//| hexlify(outp)""" +//| ... +//| + +STATIC mp_obj_t aesio_aes_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + (void)type; + enum { ARG_key, ARG_mode, ARG_IV, ARG_counter, ARG_segment_size }; + static const mp_arg_t allowed_args[] = { + {MP_QSTR_key, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_mode, MP_ARG_INT, {.u_int = AES_MODE_ECB} }, + {MP_QSTR_IV, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_counter, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_segment_size, MP_ARG_INT, {.u_int = 8}}, + }; + 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); + + aesio_aes_obj_t *self = m_new_obj(aesio_aes_obj_t); + self->base.type = &aesio_aes_type; + + mp_buffer_info_t bufinfo; + + const uint8_t *key = NULL; + uint32_t key_length = 0; + if (mp_get_buffer(args[ARG_key].u_obj, &bufinfo, MP_BUFFER_READ)) { + if ((bufinfo.len != 16) && (bufinfo.len != 24) && (bufinfo.len != 32)) { + mp_raise_TypeError(translate("Key must be 16, 24, or 32 bytes long")); + } + key = bufinfo.buf; + key_length = bufinfo.len; + } else { + mp_raise_TypeError(translate("No key was specified")); + } + + int mode = args[ARG_mode].u_int; + switch (args[ARG_mode].u_int) { + case AES_MODE_CBC: + case AES_MODE_ECB: + case AES_MODE_CTR: + break; + default: + mp_raise_TypeError(translate("Requested AES mode is unsupported")); + } + + // IV is required for CBC mode and is ignored for other modes. + const uint8_t *iv = NULL; + if (args[ARG_IV].u_obj != NULL && + mp_get_buffer(args[ARG_IV].u_obj, &bufinfo, MP_BUFFER_READ)) { + if (bufinfo.len != AES_BLOCKLEN) { + mp_raise_TypeError_varg(translate("IV must be %d bytes long"), + AES_BLOCKLEN); + } + iv = bufinfo.buf; + } + + common_hal_aesio_aes_construct(self, key, key_length, iv, mode, + args[ARG_counter].u_int); + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t aesio_aes_rekey(size_t n_args, const mp_obj_t *pos_args) { + aesio_aes_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(pos_args[1], &bufinfo, MP_BUFFER_READ); + const uint8_t *key = bufinfo.buf; + size_t key_length = bufinfo.len; + if (key == NULL) { + mp_raise_ValueError(translate("No key was specified")); + } + if ((key_length != 16) && (key_length != 24) && (key_length != 32)) { + mp_raise_TypeError(translate("Key must be 16, 24, or 32 bytes long")); + } + + const uint8_t *iv = NULL; + if (n_args > 2) { + mp_get_buffer_raise(pos_args[2], &bufinfo, MP_BUFFER_READ); + size_t iv_length = bufinfo.len; + iv = (const uint8_t *)bufinfo.buf; + if (iv_length != AES_BLOCKLEN) { + mp_raise_TypeError_varg(translate("IV must be %d bytes long"), + AES_BLOCKLEN); + } + } + + common_hal_aesio_aes_rekey(self, key, key_length, iv); + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_VAR(aesio_aes_rekey_obj, 2, aesio_aes_rekey); + +STATIC void validate_length(aesio_aes_obj_t *self, size_t src_length, + size_t dest_length) { + if (src_length != dest_length) { + mp_raise_ValueError( + translate("Source and destination buffers must be the same length")); + } + + switch (self->mode) { + case AES_MODE_ECB: + if (src_length != 16) { + mp_raise_msg(&mp_type_ValueError, + translate("ECB only operates on 16 bytes at a time")); + } + break; + case AES_MODE_CBC: + if ((src_length & 15) != 0) { + mp_raise_msg(&mp_type_ValueError, + translate("CBC blocks must be multiples of 16 bytes")); + } + break; + case AES_MODE_CTR: + break; + } +} + +//| def encrypt_into(self, src: ReadableBuffer, dest: WriteableBuffer) -> None: +//| """Encrypt the buffer from ``src`` into ``dest``. +//| +//| For ECB mode, the buffers must be 16 bytes long. For CBC mode, the +//| buffers must be a multiple of 16 bytes, and must be equal length. For +//| CTX mode, there are no restrictions.""" +//| ... +//| +STATIC mp_obj_t aesio_aes_encrypt_into(mp_obj_t aesio_obj, mp_obj_t src, + mp_obj_t dest) { + if (!mp_obj_is_type(aesio_obj, &aesio_aes_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), aesio_aes_type.name); + } + // Convert parameters into expected types. + aesio_aes_obj_t *aes = MP_OBJ_TO_PTR(aesio_obj); + + mp_buffer_info_t srcbufinfo, destbufinfo; + mp_get_buffer_raise(src, &srcbufinfo, MP_BUFFER_READ); + mp_get_buffer_raise(dest, &destbufinfo, MP_BUFFER_WRITE); + validate_length(aes, srcbufinfo.len, destbufinfo.len); + + memcpy(destbufinfo.buf, srcbufinfo.buf, srcbufinfo.len); + + common_hal_aesio_aes_encrypt(aes, (uint8_t *)destbufinfo.buf, + destbufinfo.len); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_3(aesio_aes_encrypt_into_obj, + aesio_aes_encrypt_into); + +//| def decrypt_into(self, src: ReadableBuffer, dest: WriteableBuffer) -> None: +//| """Decrypt the buffer from ``src`` into ``dest``. +//| For ECB mode, the buffers must be 16 bytes long. For CBC mode, the +//| buffers must be a multiple of 16 bytes, and must be equal length. For +//| CTX mode, there are no restrictions.""" +//| ... +//| +STATIC mp_obj_t aesio_aes_decrypt_into(mp_obj_t aesio_obj, mp_obj_t src, + mp_obj_t dest) { + if (!mp_obj_is_type(aesio_obj, &aesio_aes_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), aesio_aes_type.name); + } + // Convert parameters into expected types. + aesio_aes_obj_t *aes = MP_OBJ_TO_PTR(aesio_obj); + + mp_buffer_info_t srcbufinfo, destbufinfo; + mp_get_buffer_raise(src, &srcbufinfo, MP_BUFFER_READ); + mp_get_buffer_raise(dest, &destbufinfo, MP_BUFFER_WRITE); + validate_length(aes, srcbufinfo.len, destbufinfo.len); + + memcpy(destbufinfo.buf, srcbufinfo.buf, srcbufinfo.len); + + common_hal_aesio_aes_decrypt(aes, (uint8_t *)destbufinfo.buf, + destbufinfo.len); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_3(aesio_aes_decrypt_into_obj, + aesio_aes_decrypt_into); + +STATIC mp_obj_t aesio_aes_get_mode(mp_obj_t aesio_obj) { + if (!mp_obj_is_type(aesio_obj, &aesio_aes_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), aesio_aes_type.name); + } + aesio_aes_obj_t *self = MP_OBJ_TO_PTR(aesio_obj); + return MP_OBJ_NEW_SMALL_INT(self->mode); +} +MP_DEFINE_CONST_FUN_OBJ_1(aesio_aes_get_mode_obj, aesio_aes_get_mode); + +STATIC mp_obj_t aesio_aes_set_mode(mp_obj_t aesio_obj, mp_obj_t mode_obj) { + if (!mp_obj_is_type(aesio_obj, &aesio_aes_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), aesio_aes_type.name); + } + aesio_aes_obj_t *self = MP_OBJ_TO_PTR(aesio_obj); + + int mode = mp_obj_get_int(mode_obj); + switch (mode) { + case AES_MODE_CBC: + case AES_MODE_ECB: + case AES_MODE_CTR: + break; + default: + mp_raise_TypeError(translate("Requested AES mode is unsupported")); + } + + common_hal_aesio_aes_set_mode(self, mode); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(aesio_aes_set_mode_obj, aesio_aes_set_mode); + +MP_PROPERTY_GETSET(aesio_aes_mode_obj, + (mp_obj_t)&aesio_aes_get_mode_obj, + (mp_obj_t)&aesio_aes_set_mode_obj); + +STATIC const mp_rom_map_elem_t aesio_locals_dict_table[] = { + // Methods + {MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_AES)}, + {MP_ROM_QSTR(MP_QSTR_encrypt_into), (mp_obj_t)&aesio_aes_encrypt_into_obj}, + {MP_ROM_QSTR(MP_QSTR_decrypt_into), (mp_obj_t)&aesio_aes_decrypt_into_obj}, + {MP_ROM_QSTR(MP_QSTR_rekey), (mp_obj_t)&aesio_aes_rekey_obj}, + {MP_ROM_QSTR(MP_QSTR_mode), (mp_obj_t)&aesio_aes_mode_obj}, +}; +STATIC MP_DEFINE_CONST_DICT(aesio_locals_dict, aesio_locals_dict_table); + +const mp_obj_type_t aesio_aes_type = { + {&mp_type_type}, + .name = MP_QSTR_AES, + .make_new = aesio_aes_make_new, + .locals_dict = (mp_obj_dict_t *)&aesio_locals_dict, +}; diff --git a/circuitpython/shared-bindings/alarm/SleepMemory.c b/circuitpython/shared-bindings/alarm/SleepMemory.c new file mode 100644 index 0000000..7de36f0 --- /dev/null +++ b/circuitpython/shared-bindings/alarm/SleepMemory.c @@ -0,0 +1,184 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Dan Halbert 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 "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" + +#include "shared-bindings/alarm/SleepMemory.h" +#include "supervisor/shared/translate.h" + +//| class SleepMemory: +//| """Store raw bytes in RAM that persists during deep sleep. +//| The class acts as a ``bytearray``. +//| If power is lost, the memory contents are lost. +//| +//| Note that this class can't be imported and used directly. The sole +//| instance of :class:`SleepMemory` is available at +//| :attr:`alarm.sleep_memory`. +//| +//| Usage:: +//| +//| import alarm +//| alarm.sleep_memory[0] = True +//| alarm.sleep_memory[1] = 12 +//| """ + +//| def __init__(self) -> None: +//| """Not used. Access the sole instance through `alarm.sleep_memory`.""" +//| ... +//| +//| def __bool__(self) -> bool: +//| """``sleep_memory`` is ``True`` if its length is greater than zero. +//| This is an easy way to check for its existence. +//| """ +//| ... +//| +//| def __len__(self) -> int: +//| """Return the length. This is used by (`len`)""" +//| ... +//| +STATIC mp_obj_t alarm_sleep_memory_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + alarm_sleep_memory_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint16_t len = common_hal_alarm_sleep_memory_get_length(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC const mp_rom_map_elem_t alarm_sleep_memory_locals_dict_table[] = { +}; + +STATIC MP_DEFINE_CONST_DICT(alarm_sleep_memory_locals_dict, alarm_sleep_memory_locals_dict_table); + +//| @overload +//| def __getitem__(self, index: slice) -> bytearray: ... +//| @overload +//| def __getitem__(self, index: int) -> int: +//| """Returns the value at the given index.""" +//| ... +//| +//| @overload +//| def __setitem__(self, index: slice, value: ReadableBuffer) -> None: ... +//| @overload +//| def __setitem__(self, index: int, value: int) -> None: +//| """Set the value at the given index.""" +//| ... +//| +STATIC mp_obj_t alarm_sleep_memory_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete item + // slice deletion + return MP_OBJ_NULL; // op not supported + } else { + alarm_sleep_memory_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (0) { + #if MICROPY_PY_BUILTINS_SLICE + } else if (mp_obj_is_type(index_in, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(common_hal_alarm_sleep_memory_get_length(self), index_in, &slice)) { + mp_raise_NotImplementedError(translate("only slices with step=1 (aka None) are supported")); + } + if (value != MP_OBJ_SENTINEL) { + #if MICROPY_PY_ARRAY_SLICE_ASSIGN + // Assign + size_t src_len = slice.stop - slice.start; + uint8_t *src_items; + if (mp_obj_is_type(value, &mp_type_array) || + mp_obj_is_type(value, &mp_type_bytearray) || + mp_obj_is_type(value, &mp_type_memoryview) || + mp_obj_is_type(value, &mp_type_bytes)) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != src_len) { + mp_raise_ValueError(translate("Slice and value different lengths.")); + } + src_len = bufinfo.len; + src_items = bufinfo.buf; + if (1 != mp_binary_get_size('@', bufinfo.typecode, NULL)) { + mp_raise_ValueError(translate("Array values should be single bytes.")); + } + } else { + mp_raise_NotImplementedError(translate("array/bytes required on right side")); + } + + if (!common_hal_alarm_sleep_memory_set_bytes(self, slice.start, src_items, src_len)) { + mp_raise_RuntimeError(translate("Unable to write to sleep_memory.")); + } + return mp_const_none; + #else + return MP_OBJ_NULL; // op not supported + #endif + } else { + // Read slice. + size_t len = slice.stop - slice.start; + uint8_t *items = m_new(uint8_t, len); + common_hal_alarm_sleep_memory_get_bytes(self, slice.start, items, len); + return mp_obj_new_bytearray_by_ref(len, items); + } + #endif + } else { + // Single index rather than slice. + size_t index = mp_get_index(self->base.type, common_hal_alarm_sleep_memory_get_length(self), + index_in, false); + if (value == MP_OBJ_SENTINEL) { + // load + uint8_t value_out; + common_hal_alarm_sleep_memory_get_bytes(self, index, &value_out, 1); + return MP_OBJ_NEW_SMALL_INT(value_out); + } else { + // store + mp_int_t byte_value = mp_obj_get_int(value); + if (byte_value > 0xff || byte_value < 0) { + mp_raise_ValueError(translate("Bytes must be between 0 and 255.")); + } + uint8_t short_value = byte_value; + if (!common_hal_alarm_sleep_memory_set_bytes(self, index, &short_value, 1)) { + mp_raise_RuntimeError(translate("Unable to write to sleep_memory.")); + } + return mp_const_none; + } + } + } +} + +const mp_obj_type_t alarm_sleep_memory_type = { + { &mp_type_type }, + .name = MP_QSTR_SleepMemory, + .flags = MP_TYPE_FLAG_EXTENDED, + .locals_dict = (mp_obj_t)&alarm_sleep_memory_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = alarm_sleep_memory_subscr, + .unary_op = alarm_sleep_memory_unary_op, + ), +}; diff --git a/circuitpython/shared-bindings/alarm/SleepMemory.h b/circuitpython/shared-bindings/alarm/SleepMemory.h new file mode 100644 index 0000000..c1667ec --- /dev/null +++ b/circuitpython/shared-bindings/alarm/SleepMemory.h @@ -0,0 +1,40 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 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_ALARM_SLEEPMEMORY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_SLEEPMEMORY_H + +#include "common-hal/alarm/SleepMemory.h" + +extern const mp_obj_type_t alarm_sleep_memory_type; + +uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self); + +bool common_hal_alarm_sleep_memory_set_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, const uint8_t *values, uint32_t len); +void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, uint8_t *values, uint32_t len); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_SLEEPMEMORY_H diff --git a/circuitpython/shared-bindings/alarm/__init__.c b/circuitpython/shared-bindings/alarm/__init__.c new file mode 100644 index 0000000..7fd14b7 --- /dev/null +++ b/circuitpython/shared-bindings/alarm/__init__.c @@ -0,0 +1,261 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert 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 "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/alarm/__init__.h" +#include "shared-bindings/alarm/SleepMemory.h" +#include "shared-bindings/alarm/pin/PinAlarm.h" +#include "shared-bindings/alarm/time/TimeAlarm.h" +#include "shared-bindings/alarm/touch/TouchAlarm.h" +#include "shared-bindings/supervisor/Runtime.h" +#include "shared-bindings/time/__init__.h" +#include "supervisor/shared/workflow.h" + +//| """Alarms and sleep +//| +//| Provides alarms that trigger based on time intervals or on external events, such as pin +//| changes. +//| The program can simply wait for these alarms, or go to sleep and be awoken when they trigger. +//| +//| There are two supported levels of sleep: light sleep and deep sleep. +//| +//| Light sleep keeps sufficient state so the program can resume after sleeping. +//| It does not shut down WiFi, BLE, or other communications, or ongoing activities such +//| as audio playback. It reduces power consumption to the extent possible that leaves +//| these continuing activities running. In some cases there may be no decrease in power consumption. +//| +//| Deep sleep shuts down power to nearly all of the microcontroller including the CPU and RAM. This can save +//| a more significant amount of power, but CircuitPython must restart ``code.py`` from the beginning when +//| awakened. +//| +//| For both light sleep and deep sleep, if CircuitPython is connected to a host computer, +//| maintaining the connection takes priority and power consumption may not be reduced. +//| +//| For more information about working with alarms and light/deep sleep in CircuitPython, +//| see `this Learn guide <https://learn.adafruit.com/deep-sleep-with-circuitpython>`_. +//| """ + +//| sleep_memory: SleepMemory +//| """Memory that persists during deep sleep. +//| This object is the sole instance of `alarm.SleepMemory`.""" +//| + +//| wake_alarm: Optional[circuitpython_typing.Alarm] +//| """The most recently triggered alarm. If CircuitPython was sleeping, the alarm that woke it from sleep. +//| If no alarm occured since the last hard reset or soft restart, value is ``None``. +//| """ +//| + +// wake_alarm is implemented as a dictionary entry, so there's no code here. + +STATIC void validate_objs_are_alarms(size_t n_args, const mp_obj_t *objs) { + for (size_t i = 0; i < n_args; i++) { + if (mp_obj_is_type(objs[i], &alarm_pin_pinalarm_type) || + mp_obj_is_type(objs[i], &alarm_time_timealarm_type) || + mp_obj_is_type(objs[i], &alarm_touch_touchalarm_type)) { + continue; + } + mp_raise_TypeError_varg(translate("Expected an alarm")); + } +} + +//| def light_sleep_until_alarms(*alarms: circuitpython_typing.Alarm) -> circuitpython_typing.Alarm: +//| """Go into a light sleep until awakened one of the alarms. The alarm causing the wake-up +//| is returned, and is also available as `alarm.wake_alarm`. +//| +//| If no alarms are specified, return immediately. +//| +//| **If CircuitPython is connected to a host computer, the connection will be maintained, +//| and the microcontroller may not actually go into a light sleep.** +//| This allows the user to interrupt an existing program with ctrl-C, +//| and to edit the files in CIRCUITPY, which would not be possible in true light sleep. +//| Thus, to use light sleep and save significant power, +//| it may be necessary to disconnect from the host. +//| """ +//| ... +//| +STATIC mp_obj_t alarm_light_sleep_until_alarms(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + return mp_const_none; + } + + validate_objs_are_alarms(n_args, args); + + mp_obj_t alarm = common_hal_alarm_light_sleep_until_alarms(n_args, args); + shared_alarm_save_wake_alarm(alarm); + return alarm; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_light_sleep_until_alarms_obj, 1, MP_OBJ_FUN_ARGS_MAX, alarm_light_sleep_until_alarms); + +//| def exit_and_deep_sleep_until_alarms(*alarms: circuitpython_typing.Alarm) -> None: +//| """Exit the program and go into a deep sleep, until awakened by one of the alarms. +//| This function does not return. +//| +//| When awakened, the microcontroller will restart and will run ``boot.py`` and ``code.py`` +//| from the beginning. +//| +//| After restart, an alarm *equivalent* to the one that caused the wake-up +//| will be available as `alarm.wake_alarm`. +//| Its type and/or attributes may not correspond exactly to the original alarm. +//| For time-base alarms, currently, an `alarm.time.TimeAlarm()` is created. +//| +//| If no alarms are specified, the microcontroller will deep sleep until reset. +//| +//| **If CircuitPython is connected to a host computer via USB or BLE +//| the first time a deep sleep is requested, +//| the connection will be maintained and the system will not go into deep sleep.** +//| This allows the user to interrupt an existing program with ctrl-C, +//| and to edit the files in CIRCUITPY, which would not be possible in true deep sleep. +//| +//| If CircuitPython goes into a true deep sleep, and USB or BLE is reconnected, +//| the next deep sleep will still be a true deep sleep. You must do a hard reset +//| or power-cycle to exit a true deep sleep loop. +//| +//| Here is skeletal example that deep-sleeps and restarts every 60 seconds: +//| +//| .. code-block:: python +//| +//| import alarm +//| import time +//| +//| print("Waking up") +//| +//| # Set an alarm for 60 seconds from now. +//| time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 60) +//| +//| # Deep sleep until the alarm goes off. Then restart the program. +//| alarm.exit_and_deep_sleep_until_alarms(time_alarm) +//| """ +//| ... +//| +STATIC mp_obj_t alarm_exit_and_deep_sleep_until_alarms(size_t n_args, const mp_obj_t *args) { + validate_objs_are_alarms(n_args, args); + + // Validate the alarms and set them. + common_hal_alarm_set_deep_sleep_alarms(n_args, args); + + // Raise an exception, which will be processed in main.c. + mp_raise_type_arg(&mp_type_DeepSleepRequest, NULL); + + // Doesn't get here. + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_exit_and_deep_sleep_until_alarms_obj, 1, MP_OBJ_FUN_ARGS_MAX, alarm_exit_and_deep_sleep_until_alarms); + +STATIC const mp_map_elem_t alarm_pin_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pin) }, + + { MP_ROM_QSTR(MP_QSTR_PinAlarm), MP_OBJ_FROM_PTR(&alarm_pin_pinalarm_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(alarm_pin_globals, alarm_pin_globals_table); + +STATIC const mp_obj_module_t alarm_pin_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&alarm_pin_globals, +}; + +STATIC const mp_map_elem_t alarm_time_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_time) }, + + { MP_ROM_QSTR(MP_QSTR_TimeAlarm), MP_OBJ_FROM_PTR(&alarm_time_timealarm_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(alarm_time_globals, alarm_time_globals_table); + +STATIC const mp_obj_module_t alarm_time_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&alarm_time_globals, +}; + +STATIC const mp_map_elem_t alarm_touch_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_touch) }, + { MP_ROM_QSTR(MP_QSTR_TouchAlarm), MP_OBJ_FROM_PTR(&alarm_touch_touchalarm_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(alarm_touch_globals, alarm_touch_globals_table); + +STATIC const mp_obj_module_t alarm_touch_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&alarm_touch_globals, +}; + +// The module table is mutable because .wake_alarm is a mutable attribute. +STATIC mp_map_elem_t alarm_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alarm) }, + + // wake_alarm is a mutable attribute. + { MP_ROM_QSTR(MP_QSTR_wake_alarm), mp_const_none }, + + { MP_ROM_QSTR(MP_QSTR_light_sleep_until_alarms), MP_OBJ_FROM_PTR(&alarm_light_sleep_until_alarms_obj) }, + { MP_ROM_QSTR(MP_QSTR_exit_and_deep_sleep_until_alarms), + MP_OBJ_FROM_PTR(&alarm_exit_and_deep_sleep_until_alarms_obj) }, + + { MP_ROM_QSTR(MP_QSTR_pin), MP_OBJ_FROM_PTR(&alarm_pin_module) }, + { MP_ROM_QSTR(MP_QSTR_time), MP_OBJ_FROM_PTR(&alarm_time_module) }, + { MP_ROM_QSTR(MP_QSTR_touch), MP_OBJ_FROM_PTR(&alarm_touch_module) }, + + { MP_ROM_QSTR(MP_QSTR_SleepMemory), MP_OBJ_FROM_PTR(&alarm_sleep_memory_type) }, + { MP_ROM_QSTR(MP_QSTR_sleep_memory), MP_OBJ_FROM_PTR(&alarm_sleep_memory_obj) }, +}; +STATIC MP_DEFINE_MUTABLE_DICT(alarm_module_globals, alarm_module_globals_table); + +// Fetch value from module dict. +mp_obj_t shared_alarm_get_wake_alarm(void) { + mp_map_elem_t *elem = + mp_map_lookup(&alarm_module_globals.map, MP_ROM_QSTR(MP_QSTR_wake_alarm), MP_MAP_LOOKUP); + if (elem) { + return elem->value; + } else { + return NULL; + } +} + +// Initialize .wake_alarm value. +void shared_alarm_save_wake_alarm(mp_obj_t alarm) { + // Equivalent of: + // alarm.wake_alarm = alarm + mp_map_elem_t *elem = + mp_map_lookup(&alarm_module_globals.map, MP_ROM_QSTR(MP_QSTR_wake_alarm), MP_MAP_LOOKUP); + if (elem) { + elem->value = alarm; + } +} + +const mp_obj_module_t alarm_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&alarm_module_globals, +}; + +extern void port_idle_until_interrupt(void); + +MP_WEAK void common_hal_alarm_pretending_deep_sleep(void) { + port_idle_until_interrupt(); +} + +MP_REGISTER_MODULE(MP_QSTR_alarm, alarm_module, CIRCUITPY_ALARM); diff --git a/circuitpython/shared-bindings/alarm/__init__.h b/circuitpython/shared-bindings/alarm/__init__.h new file mode 100644 index 0000000..eb67917 --- /dev/null +++ b/circuitpython/shared-bindings/alarm/__init__.h @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert 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_ALARM___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ALARM___INIT___H + +#include "py/obj.h" + +#include "common-hal/alarm/__init__.h" + +// Light sleep fully self-contained and does not exit user code. It will return +// the same alarm object that was orignally passed in, unlike deep sleep, which +// must create an identical copy due to the VM reset +extern mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms); + +// Deep sleep is a two step process. Alarms are set when the VM is valid but +// everything is reset before entering deep sleep. Furthermore, deep sleep may +// not actually happen if the user is connected to the device. In this case, the +// supervisor will idle using `port_wait_for_interrupt`. After each call, it will +// call alarm_woken_from_sleep to see if we've been woken by an alarm and if so, +// it will exit idle as if deep sleep was exited +extern void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms); + +extern NORETURN void common_hal_alarm_enter_deep_sleep(void); + +// May be used to re-initialize peripherals like GPIO, if the VM reset returned +// them to a default state +extern void common_hal_alarm_pretending_deep_sleep(void); + +// Fetches value from module dict. +extern mp_obj_t shared_alarm_get_wake_alarm(void); + +// Creates a new alarm object after exiting deep sleep (real or fake) +extern mp_obj_t common_hal_alarm_create_wake_alarm(void); + +// Saves alarm to global array +void shared_alarm_save_wake_alarm(mp_obj_t alarm); + +// True if an alarm is alerting. Used in light sleep/fake deep sleep +extern bool common_hal_alarm_woken_from_sleep(void); + +extern void common_hal_alarm_gc_collect(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ALARM___INIT___H diff --git a/circuitpython/shared-bindings/alarm/pin/PinAlarm.c b/circuitpython/shared-bindings/alarm/pin/PinAlarm.c new file mode 100644 index 0000000..ff34716 --- /dev/null +++ b/circuitpython/shared-bindings/alarm/pin/PinAlarm.c @@ -0,0 +1,127 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert 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 "shared-bindings/board/__init__.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/alarm/pin/PinAlarm.h" + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +//| class PinAlarm: +//| """Trigger an alarm when a pin changes state.""" +//| +//| def __init__(self, pin: microcontroller.Pin, value: bool, edge: bool = False, pull: bool = False) -> None: +//| """Create an alarm triggered by a `microcontroller.Pin` level. The alarm is not active +//| until it is passed to an `alarm`-enabling function, such as `alarm.light_sleep_until_alarms()` or +//| `alarm.exit_and_deep_sleep_until_alarms()`. +//| +//| :param microcontroller.Pin pin: The pin to monitor. On some ports, the choice of pin +//| may be limited due to hardware restrictions, particularly for deep-sleep alarms. +//| :param bool value: When active, trigger when the pin value is high (``True``) or low (``False``). +//| On some ports, multiple `PinAlarm` objects may need to have coordinated values +//| for deep-sleep alarms. +//| :param bool edge: If ``True``, trigger only when there is a transition to the specified +//| value of ``value``. If ``True``, if the alarm becomes active when the pin value already +//| matches ``value``, the alarm is not triggered: the pin must transition from ``not value`` +//| to ``value`` to trigger the alarm. On some ports, edge-triggering may not be available, +//| particularly for deep-sleep alarms. +//| :param bool pull: Enable a pull-up or pull-down which pulls the pin to the level opposite +//| that of ``value``. For instance, if ``value`` is set to ``True``, setting ``pull`` +//| to ``True`` will enable a pull-down, to hold the pin low normally until an outside signal +//| pulls it high. +//| """ +//| ... +//| +STATIC mp_obj_t alarm_pin_pinalarm_make_new(const mp_obj_type_t *type, mp_uint_t n_args, size_t n_kw, const mp_obj_t *all_args) { + alarm_pin_pinalarm_obj_t *self = m_new_obj(alarm_pin_pinalarm_obj_t); + self->base.type = &alarm_pin_pinalarm_type; + enum { ARG_pin, ARG_value, ARG_edge, ARG_pull }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_BOOL }, + { MP_QSTR_edge, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_pull, 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 *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + + common_hal_alarm_pin_pinalarm_construct(self, + pin, + args[ARG_value].u_bool, + args[ARG_edge].u_bool, + args[ARG_pull].u_bool); + + return MP_OBJ_FROM_PTR(self); +} + +//| pin: microcontroller.Pin +//| """The trigger pin.""" +//| +STATIC mp_obj_t alarm_pin_pinalarm_obj_get_pin(mp_obj_t self_in) { + alarm_pin_pinalarm_obj_t *self = MP_OBJ_TO_PTR(self_in); + const mcu_pin_obj_t *pin = common_hal_alarm_pin_pinalarm_get_pin(self); + if (pin == NULL) { + return mp_const_none; + } + return MP_OBJ_FROM_PTR(pin); +} +MP_DEFINE_CONST_FUN_OBJ_1(alarm_pin_pinalarm_get_pin_obj, alarm_pin_pinalarm_obj_get_pin); + +MP_PROPERTY_GETTER(alarm_pin_pinalarm_pin_obj, + (mp_obj_t)&alarm_pin_pinalarm_get_pin_obj); + +//| value: bool +//| """The value on which to trigger.""" +//| +STATIC mp_obj_t alarm_pin_pinalarm_obj_get_value(mp_obj_t self_in) { + alarm_pin_pinalarm_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_alarm_pin_pinalarm_get_value(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(alarm_pin_pinalarm_get_value_obj, alarm_pin_pinalarm_obj_get_value); + +MP_PROPERTY_GETTER(alarm_pin_pinalarm_value_obj, + (mp_obj_t)&alarm_pin_pinalarm_get_value_obj); + +STATIC const mp_rom_map_elem_t alarm_pin_pinalarm_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_pin), MP_ROM_PTR(&alarm_pin_pinalarm_pin_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&alarm_pin_pinalarm_value_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(alarm_pin_pinalarm_locals_dict, alarm_pin_pinalarm_locals_dict_table); + +const mp_obj_type_t alarm_pin_pinalarm_type = { + { &mp_type_type }, + .name = MP_QSTR_PinAlarm, + .make_new = alarm_pin_pinalarm_make_new, + .locals_dict = (mp_obj_t)&alarm_pin_pinalarm_locals_dict, +}; diff --git a/circuitpython/shared-bindings/alarm/pin/PinAlarm.h b/circuitpython/shared-bindings/alarm/pin/PinAlarm.h new file mode 100644 index 0000000..ba74bab --- /dev/null +++ b/circuitpython/shared-bindings/alarm/pin/PinAlarm.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert 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_ALARM_PIN_PINALARM_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_PIN_PINALARM_H + +#include "py/obj.h" +#include "py/objtuple.h" +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/alarm/pin/PinAlarm.h" + +extern const mp_obj_type_t alarm_pin_pinalarm_type; + +void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, const mcu_pin_obj_t *pin, bool value, bool edge, bool pull); +extern const mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self); +extern bool common_hal_alarm_pin_pinalarm_get_value(alarm_pin_pinalarm_obj_t *self); +extern bool common_hal_alarm_pin_pinalarm_get_edge(alarm_pin_pinalarm_obj_t *self); +extern bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_PIN_PINALARM_H diff --git a/circuitpython/shared-bindings/alarm/time/TimeAlarm.c b/circuitpython/shared-bindings/alarm/time/TimeAlarm.c new file mode 100644 index 0000000..ab0274a --- /dev/null +++ b/circuitpython/shared-bindings/alarm/time/TimeAlarm.c @@ -0,0 +1,140 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert 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 "py/nlr.h" +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/alarm/time/TimeAlarm.h" +#include "shared-bindings/rtc/__init__.h" +#include "shared-bindings/time/__init__.h" + +#include "supervisor/shared/translate.h" + +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE +mp_obj_t MP_WEAK rtc_get_time_source_time(void) { + mp_raise_RuntimeError(translate("RTC is not supported on this board")); +} +#endif + +//| class TimeAlarm: +//| """Trigger an alarm when the specified time is reached.""" +//| +//| def __init__(self, monotonic_time: Optional[float] = None, epoch_time: Optional[int] = None) -> None: +//| """Create an alarm that will be triggered when `time.monotonic()` would equal +//| ``monotonic_time``, or when `time.time()` would equal ``epoch_time``. +//| Only one of the two arguments can be given. +//| The alarm is not active until it is passed to an +//| `alarm`-enabling function, such as `alarm.light_sleep_until_alarms()` or +//| `alarm.exit_and_deep_sleep_until_alarms()`. +//| +//| If the given time is in the past when sleep occurs, the alarm will be triggered +//| immediately. +//| """ +//| ... +//| +STATIC mp_obj_t alarm_time_timealarm_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + alarm_time_timealarm_obj_t *self = m_new_obj(alarm_time_timealarm_obj_t); + self->base.type = &alarm_time_timealarm_type; + + enum { ARG_monotonic_time, ARG_epoch_time }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_monotonic_time, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_epoch_time, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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); + + bool have_monotonic = args[ARG_monotonic_time].u_obj != mp_const_none; + bool have_epoch = args[ARG_epoch_time].u_obj != mp_const_none; + + if (!(have_monotonic ^ have_epoch)) { + mp_raise_ValueError(translate("Supply one of monotonic_time or epoch_time")); + } + + mp_float_t monotonic_time = 0; // To avoid compiler warning. + if (have_monotonic) { + monotonic_time = mp_obj_get_float(args[ARG_monotonic_time].u_obj); + } + + mp_float_t monotonic_time_now = common_hal_time_monotonic_ms() / 1000.0; + + if (have_epoch) { + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE + mp_raise_ValueError(translate("epoch_time not supported on this board")); + #else + mp_uint_t epoch_time_secs = mp_obj_int_get_checked(args[ARG_epoch_time].u_obj); + + timeutils_struct_time_t tm; + struct_time_to_tm(rtc_get_time_source_time(), &tm); + mp_uint_t epoch_secs_now = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + // How far in the future (in secs) is the requested time? + mp_int_t epoch_diff = epoch_time_secs - epoch_secs_now; + // Convert it to a future monotonic time. + monotonic_time = monotonic_time_now + epoch_diff; + #endif + } + + if (monotonic_time < monotonic_time_now) { + mp_raise_ValueError(translate("Time is in the past.")); + } + + common_hal_alarm_time_timealarm_construct(self, monotonic_time); + + return MP_OBJ_FROM_PTR(self); +} + +//| monotonic_time: float +//| """When this time is reached, the alarm will trigger, based on the `time.monotonic()` clock. +//| The time may be given as ``epoch_time`` in the constructor, but it is returned +//| by this property only as a `time.monotonic()` time. +//| """ +//| +STATIC mp_obj_t alarm_time_timealarm_obj_get_monotonic_time(mp_obj_t self_in) { + alarm_time_timealarm_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_float(common_hal_alarm_time_timealarm_get_monotonic_time(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(alarm_time_timealarm_get_monotonic_time_obj, alarm_time_timealarm_obj_get_monotonic_time); + +MP_PROPERTY_GETTER(alarm_time_timealarm_monotonic_time_obj, + (mp_obj_t)&alarm_time_timealarm_get_monotonic_time_obj); + +STATIC const mp_rom_map_elem_t alarm_time_timealarm_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_monotonic_time), MP_ROM_PTR(&alarm_time_timealarm_monotonic_time_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(alarm_time_timealarm_locals_dict, alarm_time_timealarm_locals_dict_table); + +const mp_obj_type_t alarm_time_timealarm_type = { + { &mp_type_type }, + .name = MP_QSTR_TimeAlarm, + .make_new = alarm_time_timealarm_make_new, + .locals_dict = (mp_obj_t)&alarm_time_timealarm_locals_dict, +}; diff --git a/circuitpython/shared-bindings/alarm/time/TimeAlarm.h b/circuitpython/shared-bindings/alarm/time/TimeAlarm.h new file mode 100644 index 0000000..0a4b9ca --- /dev/null +++ b/circuitpython/shared-bindings/alarm/time/TimeAlarm.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert 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_ALARM_TIME_TIMEALARM_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_TIME_TIMEALARM_H + +#include "py/obj.h" + +#include "common-hal/alarm/time/TimeAlarm.h" + +extern const mp_obj_type_t alarm_time_timealarm_type; + +extern void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_time); +extern mp_float_t common_hal_alarm_time_timealarm_get_monotonic_time(alarm_time_timealarm_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_TIME_TIMEALARM_H diff --git a/circuitpython/shared-bindings/alarm/touch/TouchAlarm.c b/circuitpython/shared-bindings/alarm/touch/TouchAlarm.c new file mode 100644 index 0000000..ce5074f --- /dev/null +++ b/circuitpython/shared-bindings/alarm/touch/TouchAlarm.c @@ -0,0 +1,87 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 microDev + * + * 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 "shared-bindings/alarm/touch/TouchAlarm.h" +#include "shared-bindings/board/__init__.h" + +#include "py/objproperty.h" + +//| class TouchAlarm: +//| """Trigger an alarm when touch is detected.""" +//| +//| def __init__(self, *pin: microcontroller.Pin) -> None: +//| """Create an alarm that will be triggered when the given pin is touched. +//| The alarm is not active until it is passed to an `alarm`-enabling function, such as +//| `alarm.light_sleep_until_alarms()` or `alarm.exit_and_deep_sleep_until_alarms()`. +//| +//| :param microcontroller.Pin pin: The pin to monitor. On some ports, the choice of pin +//| may be limited due to hardware restrictions, particularly for deep-sleep alarms. +//| """ +//| ... +//| +STATIC mp_obj_t alarm_touch_touchalarm_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + alarm_touch_touchalarm_obj_t *self = m_new_obj(alarm_touch_touchalarm_obj_t); + self->base.type = &alarm_touch_touchalarm_type; + + enum { ARG_pin }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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 *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + + common_hal_alarm_touch_touchalarm_construct(self, pin); + + return MP_OBJ_FROM_PTR(self); +} + +//| pin: microcontroller.Pin +//| """The trigger pin.""" +//| +STATIC mp_obj_t alarm_touch_touchalarm_obj_get_pin(mp_obj_t self_in) { + alarm_touch_touchalarm_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(self->pin); +} +MP_DEFINE_CONST_FUN_OBJ_1(alarm_touch_touchalarm_get_pin_obj, alarm_touch_touchalarm_obj_get_pin); + +MP_PROPERTY_GETTER(alarm_touch_touchalarm_pin_obj, + (mp_obj_t)&alarm_touch_touchalarm_get_pin_obj); + +STATIC const mp_rom_map_elem_t alarm_touch_touchalarm_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_pin), MP_ROM_PTR(&alarm_touch_touchalarm_pin_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(alarm_touch_touchalarm_locals_dict, alarm_touch_touchalarm_locals_dict_table); + +const mp_obj_type_t alarm_touch_touchalarm_type = { + { &mp_type_type }, + .name = MP_QSTR_TouchAlarm, + .make_new = alarm_touch_touchalarm_make_new, + .locals_dict = (mp_obj_t)&alarm_touch_touchalarm_locals_dict, +}; diff --git a/circuitpython/shared-bindings/alarm/touch/TouchAlarm.h b/circuitpython/shared-bindings/alarm/touch/TouchAlarm.h new file mode 100644 index 0000000..1b70d4d --- /dev/null +++ b/circuitpython/shared-bindings/alarm/touch/TouchAlarm.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 microDev + * + * 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_ALARM_TOUCH_TOUCHALARM_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_TOUCH_TOUCHALARM_H + +#include "py/obj.h" +#include "py/runtime.h" + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/alarm/touch/TouchAlarm.h" + +extern const mp_obj_type_t alarm_touch_touchalarm_type; + +extern void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_TOUCH_TOUCHALARM_H diff --git a/circuitpython/shared-bindings/analogio/AnalogIn.c b/circuitpython/shared-bindings/analogio/AnalogIn.c new file mode 100644 index 0000000..f2c888f --- /dev/null +++ b/circuitpython/shared-bindings/analogio/AnalogIn.c @@ -0,0 +1,160 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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 <string.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/mphal.h" +#include "py/nlr.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/analogio/AnalogIn.h" +#include "shared-bindings/util.h" + +//| class AnalogIn: +//| """Read analog voltage levels +//| +//| Usage:: +//| +//| import analogio +//| from board import * +//| +//| adc = analogio.AnalogIn(A1) +//| val = adc.value""" +//| + +//| def __init__(self, pin: microcontroller.Pin) -> None: +//| """Use the AnalogIn on the given pin. The reference voltage varies by +//| platform so use ``reference_voltage`` to read the configured setting. +//| +//| :param ~microcontroller.Pin pin: the pin to read from""" +//| ... +//| +STATIC mp_obj_t analogio_analogin_make_new(const mp_obj_type_t *type, + mp_uint_t n_args, size_t n_kw, const mp_obj_t *args) { + // check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + // 1st argument is the pin + const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[0]); + + analogio_analogin_obj_t *self = m_new_obj(analogio_analogin_obj_t); + self->base.type = &analogio_analogin_type; + common_hal_analogio_analogin_construct(self, pin); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Turn off the AnalogIn and release the pin for other use.""" +//| ... +//| +STATIC mp_obj_t analogio_analogin_deinit(mp_obj_t self_in) { + analogio_analogin_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_analogio_analogin_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(analogio_analogin_deinit_obj, analogio_analogin_deinit); + +STATIC void check_for_deinit(analogio_analogin_obj_t *self) { + if (common_hal_analogio_analogin_deinited(self)) { + raise_deinited_error(); + } +} +//| def __enter__(self) -> AnalogIn: +//| """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 analogio_analogin___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_analogio_analogin_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(analogio_analogin___exit___obj, 4, 4, analogio_analogin___exit__); + +//| value: int +//| """The value on the analog pin between 0 and 65535 inclusive (16-bit). (read-only) +//| +//| Even if the underlying analog to digital converter (ADC) is lower +//| resolution, the value is 16-bit.""" +//| +STATIC mp_obj_t analogio_analogin_obj_get_value(mp_obj_t self_in) { + analogio_analogin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_analogio_analogin_get_value(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(analogio_analogin_get_value_obj, analogio_analogin_obj_get_value); + +MP_PROPERTY_GETTER(analogio_analogin_value_obj, + (mp_obj_t)&analogio_analogin_get_value_obj); + +//| reference_voltage: float +//| """The maximum voltage measurable (also known as the reference voltage) as a +//| `float` in Volts. Note the ADC value may not scale to the actual voltage linearly +//| at ends of the analog range.""" +//| +STATIC mp_obj_t analogio_analogin_obj_get_reference_voltage(mp_obj_t self_in) { + analogio_analogin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + float reference_voltage = common_hal_analogio_analogin_get_reference_voltage(self); + if (reference_voltage <= 0.0f) { + return mp_const_none; + } else { + return mp_obj_new_float(reference_voltage); + } +} +MP_DEFINE_CONST_FUN_OBJ_1(analogio_analogin_get_reference_voltage_obj, + analogio_analogin_obj_get_reference_voltage); + +MP_PROPERTY_GETTER(analogio_analogin_reference_voltage_obj, + (mp_obj_t)&analogio_analogin_get_reference_voltage_obj); + +STATIC const mp_rom_map_elem_t analogio_analogin_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&analogio_analogin_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&analogio_analogin___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&analogio_analogin_value_obj)}, + { MP_ROM_QSTR(MP_QSTR_reference_voltage), MP_ROM_PTR(&analogio_analogin_reference_voltage_obj)}, +}; + +STATIC MP_DEFINE_CONST_DICT(analogio_analogin_locals_dict, analogio_analogin_locals_dict_table); + +const mp_obj_type_t analogio_analogin_type = { + { &mp_type_type }, + .name = MP_QSTR_AnalogIn, + .make_new = analogio_analogin_make_new, + .locals_dict = (mp_obj_t)&analogio_analogin_locals_dict, +}; diff --git a/circuitpython/shared-bindings/analogio/AnalogIn.h b/circuitpython/shared-bindings/analogio/AnalogIn.h new file mode 100644 index 0000000..9f80416 --- /dev/null +++ b/circuitpython/shared-bindings/analogio/AnalogIn.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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_ANALOGIO_ANALOGIN_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGIO_ANALOGIN_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/analogio/AnalogIn.h" + +extern const mp_obj_type_t analogio_analogin_type; + +void common_hal_analogio_analogin_construct(analogio_analogin_obj_t *self, const mcu_pin_obj_t *pin); +void common_hal_analogio_analogin_deinit(analogio_analogin_obj_t *self); +bool common_hal_analogio_analogin_deinited(analogio_analogin_obj_t *self); +uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self); +float common_hal_analogio_analogin_get_reference_voltage(analogio_analogin_obj_t *self); + +#endif // __MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGIO_ANALOGIN_H__ diff --git a/circuitpython/shared-bindings/analogio/AnalogOut.c b/circuitpython/shared-bindings/analogio/AnalogOut.c new file mode 100644 index 0000000..31173f8 --- /dev/null +++ b/circuitpython/shared-bindings/analogio/AnalogOut.c @@ -0,0 +1,141 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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 <string.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/analogio/AnalogOut.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class AnalogOut: +//| """Output analog values (a specific voltage). +//| +//| Example usage:: +//| +//| import analogio +//| from board import * +//| +//| dac = analogio.AnalogOut(A2) # output on pin A2 +//| dac.value = 32768 # makes A2 1.65V""" +//| +//| def __init__(self, pin: microcontroller.Pin) -> None: +//| """Use the AnalogOut on the given pin. +//| +//| :param ~microcontroller.Pin pin: the pin to output to""" +//| ... +//| +STATIC mp_obj_t analogio_analogout_make_new(const mp_obj_type_t *type, mp_uint_t n_args, size_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[0]); + + analogio_analogout_obj_t *self = m_new_obj(analogio_analogout_obj_t); + self->base.type = &analogio_analogout_type; + common_hal_analogio_analogout_construct(self, pin); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Turn off the AnalogOut and release the pin for other use.""" +//| ... +//| +STATIC mp_obj_t analogio_analogout_deinit(mp_obj_t self_in) { + analogio_analogout_obj_t *self = self_in; + + common_hal_analogio_analogout_deinit(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(analogio_analogout_deinit_obj, analogio_analogout_deinit); + +//| def __enter__(self) -> AnalogOut: +//| """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 analogio_analogout___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_analogio_analogout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(analogio_analogout___exit___obj, 4, 4, analogio_analogout___exit__); + +//| value: int +//| """The value on the analog pin between 0 and 65535 inclusive (16-bit). (write-only) +//| +//| Even if the underlying digital to analog converter (DAC) is lower +//| resolution, the value is 16-bit.""" +//| +STATIC mp_obj_t analogio_analogout_obj_set_value(mp_obj_t self_in, mp_obj_t value) { + analogio_analogout_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (common_hal_analogio_analogout_deinited(self)) { + raise_deinited_error(); + } + uint32_t v = mp_obj_get_int(value); + if (v >= (1 << 16)) { + mp_raise_ValueError(translate("AnalogOut is only 16 bits. Value must be less than 65536.")); + } + common_hal_analogio_analogout_set_value(self, v); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(analogio_analogout_set_value_obj, analogio_analogout_obj_set_value); + +MP_PROPERTY_GETSET(analogio_analogout_value_obj, + MP_ROM_NONE, + (mp_obj_t)&analogio_analogout_set_value_obj); + +STATIC const mp_rom_map_elem_t analogio_analogout_locals_dict_table[] = { + // instance methods + { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&analogio_analogout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&analogio_analogout___exit___obj) }, + + // Properties + { MP_OBJ_NEW_QSTR(MP_QSTR_value), (mp_obj_t)&analogio_analogout_value_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(analogio_analogout_locals_dict, analogio_analogout_locals_dict_table); + +const mp_obj_type_t analogio_analogout_type = { + { &mp_type_type }, + .name = MP_QSTR_AnalogOut, + .make_new = analogio_analogout_make_new, + .locals_dict = (mp_obj_t)&analogio_analogout_locals_dict, +}; diff --git a/circuitpython/shared-bindings/analogio/AnalogOut.h b/circuitpython/shared-bindings/analogio/AnalogOut.h new file mode 100644 index 0000000..736afd0 --- /dev/null +++ b/circuitpython/shared-bindings/analogio/AnalogOut.h @@ -0,0 +1,40 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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_ANALOGIO_ANALOGOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGIO_ANALOGOUT_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/analogio/AnalogOut.h" + +extern const mp_obj_type_t analogio_analogout_type; + +void common_hal_analogio_analogout_construct(analogio_analogout_obj_t *self, const mcu_pin_obj_t *pin); +void common_hal_analogio_analogout_deinit(analogio_analogout_obj_t *self); +bool common_hal_analogio_analogout_deinited(analogio_analogout_obj_t *self); +void common_hal_analogio_analogout_set_value(analogio_analogout_obj_t *self, uint16_t value); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGIO_ANALOGOUT_H diff --git a/circuitpython/shared-bindings/analogio/__init__.c b/circuitpython/shared-bindings/analogio/__init__.c new file mode 100644 index 0000000..eb956d0 --- /dev/null +++ b/circuitpython/shared-bindings/analogio/__init__.c @@ -0,0 +1,83 @@ +/* + * 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/analogio/__init__.h" +#include "shared-bindings/analogio/AnalogIn.h" +#include "shared-bindings/analogio/AnalogOut.h" + +//| """Analog hardware support +//| +//| The `analogio` module contains classes to provide access to analog IO +//| typically implemented with digital-to-analog (DAC) and analog-to-digital +//| (ADC) converters. +//| +//| 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 analogio +//| from board import * +//| +//| pin = analogio.AnalogIn(A0) +//| print(pin.value) +//| pin.deinit() +//| +//| This example will initialize the the device, read +//| :py:data:`~analogio.AnalogIn.value` and then +//| :py:meth:`~analogio.AnalogIn.deinit` the hardware. The last step is optional +//| because CircuitPython will do it automatically after the program finishes. +//| +//| For the essentials of `analogio`, see the `CircuitPython Essentials +//| Learn guide <https://learn.adafruit.com/circuitpython-essentials/circuitpython-analog-in>`_ +//| +//| For more information on using `analogio`, see `this additional Learn guide +//| <https://learn.adafruit.com/circuitpython-basics-analog-inputs-and-outputs>`_ +//| """ +//| + +STATIC const mp_rom_map_elem_t analogio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_analogio) }, + { MP_ROM_QSTR(MP_QSTR_AnalogIn), MP_ROM_PTR(&analogio_analogin_type) }, + { MP_ROM_QSTR(MP_QSTR_AnalogOut), MP_ROM_PTR(&analogio_analogout_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(analogio_module_globals, analogio_module_globals_table); + +const mp_obj_module_t analogio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&analogio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_analogio, analogio_module, CIRCUITPY_ANALOGIO); diff --git a/circuitpython/shared-bindings/analogio/__init__.h b/circuitpython/shared-bindings/analogio/__init__.h new file mode 100644 index 0000000..284a95b --- /dev/null +++ b/circuitpython/shared-bindings/analogio/__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_ANALOGIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ANALOGIO___INIT___H diff --git a/circuitpython/shared-bindings/atexit/__init__.c b/circuitpython/shared-bindings/atexit/__init__.c new file mode 100644 index 0000000..dd27ada --- /dev/null +++ b/circuitpython/shared-bindings/atexit/__init__.c @@ -0,0 +1,96 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "shared-module/atexit/__init__.h" + +//| """Atexit Module +//| +//| This module defines functions to register and unregister cleanup functions. +//| Functions thus registered are automatically executed upon normal vm termination. +//| +//| These functions are run in the reverse order in which they were registered; +//| if you register ``A``, ``B``, and ``C``, they will be run in the order ``C``, ``B``, ``A``. +//| +//| |see_cpython_module| :mod:`cpython:atexit`. +//| """ +//| ... +//| + +//| def register(func: Callable[..., Any], *args: Optional[Any], **kwargs: Optional[Any]) -> Callable[..., Any]: +//| +//| """Register func as a function to be executed at termination. +//| +//| Any optional arguments that are to be passed to func must be passed as arguments to `register()`. +//| It is possible to register the same function and arguments more than once. +//| +//| At normal program termination (for instance, if `sys.exit()` is called or the vm execution completes), +//| all functions registered are called in last in, first out order. +//| +//| If an exception is raised during execution of the exit handler, +//| a traceback is printed (unless `SystemExit` is raised) and the execution stops. +//| +//| This function returns func, which makes it possible to use it as a decorator. +//| +//| """ +//| ... +//| +STATIC mp_obj_t atexit_register(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + shared_module_atexit_register(pos_args[0], (n_args - 1), ((n_args > 1) ? &pos_args[1] : NULL), kw_args); + return pos_args[0]; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(atexit_register_obj, 1, atexit_register); + +//| def unregister(func: Callable[..., Any]) -> None: +//| +//| """Remove func from the list of functions to be run at termination. +//| +//| `unregister()` silently does nothing if func was not previously registered. If func has been registered more than once, +//| every occurrence of that function in the atexit call stack will be removed. +//| +//| """ +//| ... +//| +STATIC mp_obj_t atexit_unregister(const mp_obj_t self_in) { + shared_module_atexit_unregister(&self_in); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(atexit_unregister_obj, atexit_unregister); + +STATIC const mp_rom_map_elem_t atexit_module_globals_table[] = { + // module name + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_atexit) }, + // module functions + { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&atexit_register_obj) }, + { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&atexit_unregister_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(atexit_module_globals, atexit_module_globals_table); + +const mp_obj_module_t atexit_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&atexit_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_atexit, atexit_module, CIRCUITPY_ATEXIT); diff --git a/circuitpython/shared-bindings/audiobusio/I2SOut.c b/circuitpython/shared-bindings/audiobusio/I2SOut.c new file mode 100644 index 0000000..93c316d --- /dev/null +++ b/circuitpython/shared-bindings/audiobusio/I2SOut.c @@ -0,0 +1,279 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiobusio/I2SOut.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class I2SOut: +//| """Output an I2S audio signal""" +//| +//| def __init__(self, bit_clock: microcontroller.Pin, word_select: microcontroller.Pin, data: microcontroller.Pin, *, left_justified: bool) -> None: +//| """Create a I2SOut object associated with the given pins. +//| +//| :param ~microcontroller.Pin bit_clock: The bit clock (or serial clock) pin +//| :param ~microcontroller.Pin word_select: The word select (or left/right clock) pin +//| :param ~microcontroller.Pin data: The data pin +//| :param bool left_justified: True when data bits are aligned with the word select clock. False +//| when they are shifted by one to match classic I2S protocol. +//| +//| Simple 8ksps 440 Hz sine wave on `Metro M0 Express <https://www.adafruit.com/product/3505>`_ +//| using `UDA1334 Breakout <https://www.adafruit.com/product/3678>`_:: +//| +//| import audiobusio +//| import audiocore +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wave. +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| i2s = audiobusio.I2SOut(board.D1, board.D0, board.D9) +//| i2s.play(sine_wave, loop=True) +//| time.sleep(1) +//| i2s.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import audiobusio +//| import digitalio +//| +//| +//| f = open("cplay-5.1-16bit-16khz.wav", "rb") +//| wav = audiocore.WaveFile(f) +//| +//| a = audiobusio.I2SOut(board.D1, board.D0, board.D9) +//| +//| print("playing") +//| a.play(wav) +//| while a.playing: +//| pass +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t audiobusio_i2sout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + #if !CIRCUITPY_AUDIOBUSIO_I2SOUT + mp_raise_NotImplementedError(translate("I2SOut not available")); + return NULL; // Not reachable. + #else + enum { ARG_bit_clock, ARG_word_select, ARG_data, ARG_left_justified }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bit_clock, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_word_select, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_left_justified, 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 *bit_clock = validate_obj_is_free_pin(args[ARG_bit_clock].u_obj); + const mcu_pin_obj_t *word_select = validate_obj_is_free_pin(args[ARG_word_select].u_obj); + const mcu_pin_obj_t *data = validate_obj_is_free_pin(args[ARG_data].u_obj); + + audiobusio_i2sout_obj_t *self = m_new_obj_with_finaliser(audiobusio_i2sout_obj_t); + self->base.type = &audiobusio_i2sout_type; + common_hal_audiobusio_i2sout_construct(self, bit_clock, word_select, data, args[ARG_left_justified].u_bool); + + return MP_OBJ_FROM_PTR(self); + #endif +} + +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +//| def deinit(self) -> None: +//| """Deinitialises the I2SOut and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t audiobusio_i2sout_deinit(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiobusio_i2sout_deinit(self); + return mp_const_none; + +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_deinit_obj, audiobusio_i2sout_deinit); + +STATIC void check_for_deinit(audiobusio_i2sout_obj_t *self) { + if (common_hal_audiobusio_i2sout_deinited(self)) { + raise_deinited_error(); + } +} +//| def __enter__(self) -> I2SOut: +//| """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 audiobusio_i2sout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiobusio_i2sout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiobusio_i2sout___exit___obj, 4, 4, audiobusio_i2sout_obj___exit__); + + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples.""" +//| ... +//| +STATIC mp_obj_t audiobusio_i2sout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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_obj_t sample = args[ARG_sample].u_obj; + common_hal_audiobusio_i2sout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiobusio_i2sout_play_obj, 1, audiobusio_i2sout_obj_play); + +//| def stop(self) -> None: +//| """Stops playback.""" +//| ... +//| +STATIC mp_obj_t audiobusio_i2sout_obj_stop(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_audiobusio_i2sout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_stop_obj, audiobusio_i2sout_obj_stop); + +//| playing: bool +//| """True when the audio sample is being output. (read-only)""" +//| +STATIC mp_obj_t audiobusio_i2sout_obj_get_playing(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiobusio_i2sout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_get_playing_obj, audiobusio_i2sout_obj_get_playing); + +MP_PROPERTY_GETTER(audiobusio_i2sout_playing_obj, + (mp_obj_t)&audiobusio_i2sout_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +STATIC mp_obj_t audiobusio_i2sout_obj_pause(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (!common_hal_audiobusio_i2sout_get_playing(self)) { + mp_raise_RuntimeError(translate("Not playing")); + } + common_hal_audiobusio_i2sout_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_pause_obj, audiobusio_i2sout_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +STATIC mp_obj_t audiobusio_i2sout_obj_resume(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (common_hal_audiobusio_i2sout_get_paused(self)) { + common_hal_audiobusio_i2sout_resume(self); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_resume_obj, audiobusio_i2sout_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +STATIC mp_obj_t audiobusio_i2sout_obj_get_paused(mp_obj_t self_in) { + audiobusio_i2sout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiobusio_i2sout_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_i2sout_get_paused_obj, audiobusio_i2sout_obj_get_paused); + +MP_PROPERTY_GETTER(audiobusio_i2sout_paused_obj, + (mp_obj_t)&audiobusio_i2sout_get_paused_obj); +#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT + +STATIC const mp_rom_map_elem_t audiobusio_i2sout_locals_dict_table[] = { + // Methods + #if CIRCUITPY_AUDIOBUSIO_I2SOUT + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&audiobusio_i2sout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiobusio_i2sout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiobusio_i2sout___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiobusio_i2sout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiobusio_i2sout_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audiobusio_i2sout_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audiobusio_i2sout_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiobusio_i2sout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audiobusio_i2sout_paused_obj) }, + #endif // CIRCUITPY_AUDIOBUSIO_I2SOUT +}; +STATIC MP_DEFINE_CONST_DICT(audiobusio_i2sout_locals_dict, audiobusio_i2sout_locals_dict_table); + +const mp_obj_type_t audiobusio_i2sout_type = { + { &mp_type_type }, + .name = MP_QSTR_I2SOut, + .make_new = audiobusio_i2sout_make_new, + .locals_dict = (mp_obj_dict_t *)&audiobusio_i2sout_locals_dict, +}; diff --git a/circuitpython/shared-bindings/audiobusio/I2SOut.h b/circuitpython/shared-bindings/audiobusio/I2SOut.h new file mode 100644 index 0000000..64dcc3b --- /dev/null +++ b/circuitpython/shared-bindings/audiobusio/I2SOut.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 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_AUDIOBUSIO_I2SOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_I2SOUT_H + +#include "common-hal/audiobusio/I2SOut.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t audiobusio_i2sout_type; + +// Some boards don't have the I2SOut pins available. +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, + const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select, const mcu_pin_obj_t *data, + bool left_justified); + +void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self); +bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t *self); +void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t *self); +bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t *self); +void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t *self); +void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t *self); +bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t *self); + +#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_I2SOUT_H diff --git a/circuitpython/shared-bindings/audiobusio/PDMIn.c b/circuitpython/shared-bindings/audiobusio/PDMIn.c new file mode 100644 index 0000000..aa810f2 --- /dev/null +++ b/circuitpython/shared-bindings/audiobusio/PDMIn.c @@ -0,0 +1,250 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/mphal.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiobusio/PDMIn.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class PDMIn: +//| """Record an input PDM audio stream""" +//| +//| def __init__(self, clock_pin: microcontroller.Pin, data_pin: microcontroller.Pin, *, sample_rate: int = 16000, bit_depth: int = 8, mono: bool = True, oversample: int = 64, startup_delay: float = 0.11) -> None: +//| """Create a PDMIn object associated with the given pins. This allows you to +//| record audio signals from the given pins. Individual ports may put further +//| restrictions on the recording parameters. The overall sample rate is +//| determined by `sample_rate` x ``oversample``, and the total must be 1MHz or +//| higher, so `sample_rate` must be a minimum of 16000. +//| +//| :param ~microcontroller.Pin clock_pin: The pin to output the clock to +//| :param ~microcontroller.Pin data_pin: The pin to read the data from +//| :param int sample_rate: Target sample_rate of the resulting samples. Check `sample_rate` for actual value. +//| Minimum sample_rate is about 16000 Hz. +//| :param int bit_depth: Final number of bits per sample. Must be divisible by 8 +//| :param bool mono: True when capturing a single channel of audio, captures two channels otherwise +//| :param int oversample: Number of single bit samples to decimate into a final sample. Must be divisible by 8 +//| :param float startup_delay: seconds to wait after starting microphone clock +//| to allow microphone to turn on. Most require only 0.01s; some require 0.1s. Longer is safer. +//| Must be in range 0.0-1.0 seconds.""" +//| + +//| """Record 8-bit unsigned samples to buffer:: +//| +//| import audiobusio +//| import board +//| +//| # Prep a buffer to record into +//| b = bytearray(200) +//| with audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000) as mic: +//| mic.record(b, len(b)) +//| +//| Record 16-bit unsigned samples to buffer:: +//| +//| import audiobusio +//| import board +//| +//| # Prep a buffer to record into. The array interface doesn't allow for +//| # constructing with a set size so we append to it until we have the size +//| # we want. +//| b = array.array("H") +//| for i in range(200): +//| b.append(0) +//| with audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16) as mic: +//| mic.record(b, len(b))""" +//| ... +//| +STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + #if !CIRCUITPY_AUDIOBUSIO_PDMIN + mp_raise_NotImplementedError(translate("PDMIn not available")); + #else + enum { ARG_clock_pin, ARG_data_pin, ARG_sample_rate, ARG_bit_depth, ARG_mono, ARG_oversample, ARG_startup_delay }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_data_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_sample_rate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16000} }, + { MP_QSTR_bit_depth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_mono, MP_ARG_KW_ONLY | MP_ARG_BOOL,{.u_bool = true} }, + { MP_QSTR_oversample, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + { MP_QSTR_startup_delay, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + // Default microphone startup delay is 110msecs. Have seen mics that need 100 msecs plus a bit. + static const float STARTUP_DELAY_DEFAULT = 0.110F; + + 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_pin = validate_obj_is_free_pin(args[ARG_clock_pin].u_obj); + const mcu_pin_obj_t *data_pin = validate_obj_is_free_pin(args[ARG_data_pin].u_obj); + + // create PDMIn object from the given pin + audiobusio_pdmin_obj_t *self = m_new_obj(audiobusio_pdmin_obj_t); + self->base.type = &audiobusio_pdmin_type; + + uint32_t sample_rate = args[ARG_sample_rate].u_int; + uint8_t bit_depth = args[ARG_bit_depth].u_int; + if (bit_depth % 8 != 0) { + mp_raise_ValueError(translate("Bit depth must be multiple of 8.")); + } + uint8_t oversample = args[ARG_oversample].u_int; + if (oversample % 8 != 0) { + mp_raise_ValueError(translate("Oversample must be multiple of 8.")); + } + bool mono = args[ARG_mono].u_bool; + + mp_float_t startup_delay = (args[ARG_startup_delay].u_obj == MP_OBJ_NULL) + ? (mp_float_t)STARTUP_DELAY_DEFAULT + : mp_obj_get_float(args[ARG_startup_delay].u_obj); + if (startup_delay < 0.0 || startup_delay > 1.0) { + mp_raise_ValueError(translate("Microphone startup delay must be in range 0.0 to 1.0")); + } + + common_hal_audiobusio_pdmin_construct(self, clock_pin, data_pin, sample_rate, + bit_depth, mono, oversample); + + // Wait for the microphone to start up. Some start in 10 msecs; some take as much as 100 msecs. + mp_hal_delay_ms(startup_delay * 1000); + + return MP_OBJ_FROM_PTR(self); + #endif +} + +#if CIRCUITPY_AUDIOBUSIO_PDMIN +//| def deinit(self) -> None: +//| """Deinitialises the PDMIn and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t audiobusio_pdmin_deinit(mp_obj_t self_in) { + audiobusio_pdmin_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiobusio_pdmin_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_pdmin_deinit_obj, audiobusio_pdmin_deinit); + +STATIC void check_for_deinit(audiobusio_pdmin_obj_t *self) { + if (common_hal_audiobusio_pdmin_deinited(self)) { + raise_deinited_error(); + } +} +//| def __enter__(self) -> PDMIn: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context.""" +//| ... +//| +STATIC mp_obj_t audiobusio_pdmin_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiobusio_pdmin_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiobusio_pdmin___exit___obj, 4, 4, audiobusio_pdmin_obj___exit__); + + +//| def record(self, destination: WriteableBuffer, destination_length: int) -> None: +//| """Records destination_length bytes of samples to destination. This is +//| blocking. +//| +//| An IOError may be raised when the destination is too slow to record the +//| audio at the given rate. For internal flash, writing all 1s to the file +//| before recording is recommended to speed up writes. +//| +//| :return: The number of samples recorded. If this is less than ``destination_length``, +//| some samples were missed due to processing time.""" +//| ... +//| +STATIC mp_obj_t audiobusio_pdmin_obj_record(mp_obj_t self_obj, mp_obj_t destination, mp_obj_t destination_length) { + audiobusio_pdmin_obj_t *self = MP_OBJ_TO_PTR(self_obj); + check_for_deinit(self); + if (!mp_obj_is_small_int(destination_length) || MP_OBJ_SMALL_INT_VALUE(destination_length) < 0) { + mp_raise_TypeError(translate("destination_length must be an int >= 0")); + } + uint32_t length = MP_OBJ_SMALL_INT_VALUE(destination_length); + + mp_buffer_info_t bufinfo; + if (mp_obj_is_type(destination, &mp_type_fileio)) { + mp_raise_NotImplementedError(translate("Cannot record to a file")); + } else if (mp_get_buffer(destination, &bufinfo, MP_BUFFER_WRITE)) { + if (bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL) < length) { + mp_raise_ValueError(translate("Destination capacity is smaller than destination_length.")); + } + uint8_t bit_depth = common_hal_audiobusio_pdmin_get_bit_depth(self); + if (bufinfo.typecode != 'H' && bit_depth == 16) { + mp_raise_ValueError(translate("destination buffer must be an array of type 'H' for bit_depth = 16")); + } else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE && bit_depth == 8) { + mp_raise_ValueError(translate("destination buffer must be a bytearray or array of type 'B' for bit_depth = 8")); + } + // length is the buffer length in slots, not bytes. + uint32_t length_written = + common_hal_audiobusio_pdmin_record_to_buffer(self, bufinfo.buf, length); + return MP_OBJ_NEW_SMALL_INT(length_written); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(audiobusio_pdmin_record_obj, audiobusio_pdmin_obj_record); + +//| sample_rate: int +//| """The actual sample_rate of the recording. This may not match the constructed +//| sample rate due to internal clock limitations.""" +//| +STATIC mp_obj_t audiobusio_pdmin_obj_get_sample_rate(mp_obj_t self_in) { + audiobusio_pdmin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audiobusio_pdmin_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiobusio_pdmin_get_sample_rate_obj, audiobusio_pdmin_obj_get_sample_rate); + +MP_PROPERTY_GETTER(audiobusio_pdmin_sample_rate_obj, + (mp_obj_t)&audiobusio_pdmin_get_sample_rate_obj); + +STATIC const mp_rom_map_elem_t audiobusio_pdmin_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiobusio_pdmin_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiobusio_pdmin___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_record), MP_ROM_PTR(&audiobusio_pdmin_record_obj) }, + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audiobusio_pdmin_sample_rate_obj) } +}; +STATIC MP_DEFINE_CONST_DICT(audiobusio_pdmin_locals_dict, audiobusio_pdmin_locals_dict_table); +#endif + +const mp_obj_type_t audiobusio_pdmin_type = { + { &mp_type_type }, + .name = MP_QSTR_PDMIn, + .make_new = audiobusio_pdmin_make_new, + #if CIRCUITPY_AUDIOBUSIO_PDMIN + .locals_dict = (mp_obj_dict_t *)&audiobusio_pdmin_locals_dict, + #endif +}; diff --git a/circuitpython/shared-bindings/audiobusio/PDMIn.h b/circuitpython/shared-bindings/audiobusio/PDMIn.h new file mode 100644 index 0000000..0516bce --- /dev/null +++ b/circuitpython/shared-bindings/audiobusio/PDMIn.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOBUSIO_AUDIOOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_AUDIOOUT_H + +#include "common-hal/audiobusio/PDMIn.h" +#include "common-hal/microcontroller/Pin.h" +#include "extmod/vfs_fat.h" + +extern const mp_obj_type_t audiobusio_pdmin_type; + +#if CIRCUITPY_AUDIOBUSIO_PDMIN +void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, + const mcu_pin_obj_t *clock_pin, const mcu_pin_obj_t *data_pin, + uint32_t sample_rate, uint8_t bit_depth, bool mono, uint8_t oversample); +void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t *self); +bool common_hal_audiobusio_pdmin_deinited(audiobusio_pdmin_obj_t *self); +uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t *self, + uint16_t *buffer, uint32_t length); +uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t *self); +uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t *self); +// TODO(tannewt): Add record to file +#endif + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_AUDIOOUT_H diff --git a/circuitpython/shared-bindings/audiobusio/__init__.c b/circuitpython/shared-bindings/audiobusio/__init__.c new file mode 100644 index 0000000..34ce9ef --- /dev/null +++ b/circuitpython/shared-bindings/audiobusio/__init__.c @@ -0,0 +1,62 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/audiobusio/__init__.h" +#include "shared-bindings/audiobusio/I2SOut.h" +#include "shared-bindings/audiobusio/PDMIn.h" + +//| """Support for audio input and output over digital buses +//| +//| The `audiobusio` module contains classes to provide access to audio IO +//| over digital buses. These protocols are used to communicate audio to other +//| chips in the same circuit. It doesn't include audio interconnect protocols +//| such as S/PDIF. +//| +//| All classes change hardware state and should be deinitialized when they +//| are no longer needed. To do so, either call :py:meth:`!deinit` or use a +//| context manager.""" +//| + +STATIC const mp_rom_map_elem_t audiobusio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiobusio) }, + { MP_ROM_QSTR(MP_QSTR_I2SOut), MP_ROM_PTR(&audiobusio_i2sout_type) }, + { MP_ROM_QSTR(MP_QSTR_PDMIn), MP_ROM_PTR(&audiobusio_pdmin_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audiobusio_module_globals, audiobusio_module_globals_table); + +const mp_obj_module_t audiobusio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiobusio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiobusio, audiobusio_module, CIRCUITPY_AUDIOBUSIO); diff --git a/circuitpython/shared-bindings/audiobusio/__init__.h b/circuitpython/shared-bindings/audiobusio/__init__.h new file mode 100644 index 0000000..f7a0c3c --- /dev/null +++ b/circuitpython/shared-bindings/audiobusio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOBUSIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO___INIT___H diff --git a/circuitpython/shared-bindings/audiocore/RawSample.c b/circuitpython/shared-bindings/audiocore/RawSample.c new file mode 100644 index 0000000..82c0245 --- /dev/null +++ b/circuitpython/shared-bindings/audiocore/RawSample.c @@ -0,0 +1,190 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "supervisor/shared/translate.h" + +//| class RawSample: +//| """A raw audio sample buffer in memory""" +//| +//| def __init__(self, buffer: ReadableBuffer, *, channel_count: int = 1, sample_rate: int = 8000) -> None: +//| """Create a RawSample based on the given buffer of signed values. If channel_count is more than +//| 1 then each channel's samples should alternate. In other words, for a two channel buffer, the +//| first sample will be for channel 1, the second sample will be for channel two, the third for +//| channel 1 and so on. +//| +//| :param ~circuitpython_typing.ReadableBuffer buffer: A buffer with samples +//| :param int channel_count: The number of channels in the buffer +//| :param int sample_rate: The desired playback sample rate +//| +//| Simple 8ksps 440 Hz sin wave:: +//| +//| import audiocore +//| import audioio +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wav. +//| length = 8000 // 440 +//| sine_wave = array.array("h", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15)) +//| +//| dac = audioio.AudioOut(board.SPEAKER) +//| sine_wave = audiocore.RawSample(sine_wave) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop()""" +//| ... +//| +STATIC mp_obj_t audioio_rawsample_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_buffer, ARG_channel_count, ARG_sample_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1 } }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, + }; + 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); + + audioio_rawsample_obj_t *self = m_new_obj(audioio_rawsample_obj_t); + self->base.type = &audioio_rawsample_type; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + uint8_t bytes_per_sample = 1; + bool signed_samples = bufinfo.typecode == 'b' || bufinfo.typecode == 'h'; + if (bufinfo.typecode == 'h' || bufinfo.typecode == 'H') { + bytes_per_sample = 2; + } else if (bufinfo.typecode != 'b' && bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) { + mp_raise_ValueError(translate("sample_source buffer must be a bytearray or array of type 'h', 'H', 'b' or 'B'")); + } + common_hal_audioio_rawsample_construct(self, ((uint8_t *)bufinfo.buf), bufinfo.len, + bytes_per_sample, signed_samples, args[ARG_channel_count].u_int, + args[ARG_sample_rate].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the RawSample and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t audioio_rawsample_deinit(mp_obj_t self_in) { + audioio_rawsample_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_rawsample_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_rawsample_deinit_obj, audioio_rawsample_deinit); + +STATIC void check_for_deinit(audioio_rawsample_obj_t *self) { + if (common_hal_audioio_rawsample_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> RawSample: +//| """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 audioio_rawsample_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audioio_rawsample_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_rawsample___exit___obj, 4, 4, audioio_rawsample_obj___exit__); + +//| sample_rate: Optional[int] +//| """32 bit value that dictates how quickly samples are played in Hertz (cycles per second). +//| When the sample is looped, this can change the pitch output without changing the underlying +//| sample. This will not change the sample rate of any active playback. Call ``play`` again to +//| change it.""" +//| +STATIC mp_obj_t audioio_rawsample_obj_get_sample_rate(mp_obj_t self_in) { + audioio_rawsample_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_rawsample_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_rawsample_get_sample_rate_obj, audioio_rawsample_obj_get_sample_rate); + +STATIC mp_obj_t audioio_rawsample_obj_set_sample_rate(mp_obj_t self_in, mp_obj_t sample_rate) { + audioio_rawsample_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_audioio_rawsample_set_sample_rate(self, mp_obj_get_int(sample_rate)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audioio_rawsample_set_sample_rate_obj, audioio_rawsample_obj_set_sample_rate); + +MP_PROPERTY_GETSET(audioio_rawsample_sample_rate_obj, + (mp_obj_t)&audioio_rawsample_get_sample_rate_obj, + (mp_obj_t)&audioio_rawsample_set_sample_rate_obj); + +STATIC const mp_rom_map_elem_t audioio_rawsample_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_rawsample_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_rawsample___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_rawsample_sample_rate_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_rawsample_locals_dict, audioio_rawsample_locals_dict_table); + +STATIC const audiosample_p_t audioio_rawsample_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .sample_rate = (audiosample_sample_rate_fun)common_hal_audioio_rawsample_get_sample_rate, + .bits_per_sample = (audiosample_bits_per_sample_fun)common_hal_audioio_rawsample_get_bits_per_sample, + .channel_count = (audiosample_channel_count_fun)common_hal_audioio_rawsample_get_channel_count, + .reset_buffer = (audiosample_reset_buffer_fun)audioio_rawsample_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audioio_rawsample_get_buffer, + .get_buffer_structure = (audiosample_get_buffer_structure_fun)audioio_rawsample_get_buffer_structure, +}; + +const mp_obj_type_t audioio_rawsample_type = { + { &mp_type_type }, + .name = MP_QSTR_RawSample, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = audioio_rawsample_make_new, + .locals_dict = (mp_obj_dict_t *)&audioio_rawsample_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &audioio_rawsample_proto, + ), +}; diff --git a/circuitpython/shared-bindings/audiocore/RawSample.h b/circuitpython/shared-bindings/audiocore/RawSample.h new file mode 100644 index 0000000..71056f3 --- /dev/null +++ b/circuitpython/shared-bindings/audiocore/RawSample.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOIO_RAWSAMPLE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_RAWSAMPLE_H + +#include "shared-module/audiocore/RawSample.h" + +extern const mp_obj_type_t audioio_rawsample_type; + +void common_hal_audioio_rawsample_construct(audioio_rawsample_obj_t *self, + uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample, bool samples_signed, + uint8_t channel_count, uint32_t sample_rate); + +void common_hal_audioio_rawsample_deinit(audioio_rawsample_obj_t *self); +bool common_hal_audioio_rawsample_deinited(audioio_rawsample_obj_t *self); +uint32_t common_hal_audioio_rawsample_get_sample_rate(audioio_rawsample_obj_t *self); +uint8_t common_hal_audioio_rawsample_get_bits_per_sample(audioio_rawsample_obj_t *self); +uint8_t common_hal_audioio_rawsample_get_channel_count(audioio_rawsample_obj_t *self); +void common_hal_audioio_rawsample_set_sample_rate(audioio_rawsample_obj_t *self, uint32_t sample_rate); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_RAWSAMPLE_H diff --git a/circuitpython/shared-bindings/audiocore/WaveFile.c b/circuitpython/shared-bindings/audiocore/WaveFile.c new file mode 100644 index 0000000..285bd7c --- /dev/null +++ b/circuitpython/shared-bindings/audiocore/WaveFile.c @@ -0,0 +1,214 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/audiocore/WaveFile.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class WaveFile: +//| """Load a wave file for audio playback +//| +//| A .wav file prepped for audio playback. Only mono and stereo files are supported. Samples must +//| be 8 bit unsigned or 16 bit signed. If a buffer is provided, it will be used instead of allocating +//| an internal buffer, which can prevent memory fragmentation.""" +//| +//| def __init__(self, file: typing.BinaryIO, buffer: WriteableBuffer) -> None: +//| """Load a .wav file for playback with `audioio.AudioOut` or `audiobusio.I2SOut`. +//| +//| :param typing.BinaryIO file: Already opened wave file +//| :param ~circuitpython_typing.WriteableBuffer buffer: Optional pre-allocated buffer, +//| that will be split in half and used for double-buffering of the data. +//| The buffer must be 8 to 1024 bytes long. +//| If not provided, two 256 byte buffers are initially allocated internally. +//| +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import audioio +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| data = open("cplay-5.1-16bit-16khz.wav", "rb") +//| wav = audiocore.WaveFile(data) +//| a = audioio.AudioOut(board.A0) +//| +//| print("playing") +//| a.play(wav) +//| while a.playing: +//| pass +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t audioio_wavefile_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 2, false); + + audioio_wavefile_obj_t *self = m_new_obj(audioio_wavefile_obj_t); + self->base.type = &audioio_wavefile_type; + if (!mp_obj_is_type(args[0], &mp_type_fileio)) { + mp_raise_TypeError(translate("file must be a file opened in byte mode")); + } + uint8_t *buffer = NULL; + size_t buffer_size = 0; + if (n_args >= 2) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + buffer = bufinfo.buf; + buffer_size = mp_arg_validate_length_range(bufinfo.len, 8, 1024, MP_QSTR_buffer); + } + common_hal_audioio_wavefile_construct(self, MP_OBJ_TO_PTR(args[0]), + buffer, buffer_size); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the WaveFile and releases all memory resources for reuse.""" +//| ... +STATIC mp_obj_t audioio_wavefile_deinit(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_wavefile_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_deinit_obj, audioio_wavefile_deinit); + +STATIC void check_for_deinit(audioio_wavefile_obj_t *self) { + if (common_hal_audioio_wavefile_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> WaveFile: +//| """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 audioio_wavefile_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audioio_wavefile_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_wavefile___exit___obj, 4, 4, audioio_wavefile_obj___exit__); + +//| sample_rate: int +//| """32 bit value that dictates how quickly samples are loaded into the DAC +//| in Hertz (cycles per second). When the sample is looped, this can change +//| the pitch output without changing the underlying sample.""" +//| +STATIC mp_obj_t audioio_wavefile_obj_get_sample_rate(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_wavefile_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_get_sample_rate_obj, audioio_wavefile_obj_get_sample_rate); + +STATIC mp_obj_t audioio_wavefile_obj_set_sample_rate(mp_obj_t self_in, mp_obj_t sample_rate) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_audioio_wavefile_set_sample_rate(self, mp_obj_get_int(sample_rate)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audioio_wavefile_set_sample_rate_obj, audioio_wavefile_obj_set_sample_rate); + +MP_PROPERTY_GETSET(audioio_wavefile_sample_rate_obj, + (mp_obj_t)&audioio_wavefile_get_sample_rate_obj, + (mp_obj_t)&audioio_wavefile_set_sample_rate_obj); + +//| bits_per_sample: int +//| """Bits per sample. (read only)""" +//| +STATIC mp_obj_t audioio_wavefile_obj_get_bits_per_sample(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_wavefile_get_bits_per_sample(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_get_bits_per_sample_obj, audioio_wavefile_obj_get_bits_per_sample); + +MP_PROPERTY_GETTER(audioio_wavefile_bits_per_sample_obj, + (mp_obj_t)&audioio_wavefile_get_bits_per_sample_obj); +//| channel_count: int +//| """Number of audio channels. (read only)""" +//| +STATIC mp_obj_t audioio_wavefile_obj_get_channel_count(mp_obj_t self_in) { + audioio_wavefile_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audioio_wavefile_get_channel_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_wavefile_get_channel_count_obj, audioio_wavefile_obj_get_channel_count); + +MP_PROPERTY_GETTER(audioio_wavefile_channel_count_obj, + (mp_obj_t)&audioio_wavefile_get_channel_count_obj); + + +STATIC const mp_rom_map_elem_t audioio_wavefile_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_wavefile_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_wavefile___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audioio_wavefile_sample_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_bits_per_sample), MP_ROM_PTR(&audioio_wavefile_bits_per_sample_obj) }, + { MP_ROM_QSTR(MP_QSTR_channel_count), MP_ROM_PTR(&audioio_wavefile_channel_count_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_wavefile_locals_dict, audioio_wavefile_locals_dict_table); + +STATIC const audiosample_p_t audioio_wavefile_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .sample_rate = (audiosample_sample_rate_fun)common_hal_audioio_wavefile_get_sample_rate, + .bits_per_sample = (audiosample_bits_per_sample_fun)common_hal_audioio_wavefile_get_bits_per_sample, + .channel_count = (audiosample_channel_count_fun)common_hal_audioio_wavefile_get_channel_count, + .reset_buffer = (audiosample_reset_buffer_fun)audioio_wavefile_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audioio_wavefile_get_buffer, + .get_buffer_structure = (audiosample_get_buffer_structure_fun)audioio_wavefile_get_buffer_structure, +}; + + +const mp_obj_type_t audioio_wavefile_type = { + { &mp_type_type }, + .name = MP_QSTR_WaveFile, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = audioio_wavefile_make_new, + .locals_dict = (mp_obj_dict_t *)&audioio_wavefile_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &audioio_wavefile_proto, + ), +}; diff --git a/circuitpython/shared-bindings/audiocore/WaveFile.h b/circuitpython/shared-bindings/audiocore/WaveFile.h new file mode 100644 index 0000000..29140b4 --- /dev/null +++ b/circuitpython/shared-bindings/audiocore/WaveFile.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOIO_WAVEFILE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_WAVEFILE_H + +#include "py/obj.h" +#include "extmod/vfs_fat.h" + +#include "shared-module/audiocore/WaveFile.h" + +extern const mp_obj_type_t audioio_wavefile_type; + +void common_hal_audioio_wavefile_construct(audioio_wavefile_obj_t *self, + pyb_file_obj_t *file, uint8_t *buffer, size_t buffer_size); + +void common_hal_audioio_wavefile_deinit(audioio_wavefile_obj_t *self); +bool common_hal_audioio_wavefile_deinited(audioio_wavefile_obj_t *self); +uint32_t common_hal_audioio_wavefile_get_sample_rate(audioio_wavefile_obj_t *self); +void common_hal_audioio_wavefile_set_sample_rate(audioio_wavefile_obj_t *self, uint32_t sample_rate); +uint8_t common_hal_audioio_wavefile_get_bits_per_sample(audioio_wavefile_obj_t *self); +uint8_t common_hal_audioio_wavefile_get_channel_count(audioio_wavefile_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_WAVEFILE_H diff --git a/circuitpython/shared-bindings/audiocore/__init__.c b/circuitpython/shared-bindings/audiocore/__init__.c new file mode 100644 index 0000000..f03c64c --- /dev/null +++ b/circuitpython/shared-bindings/audiocore/__init__.c @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/audiocore/__init__.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/audiocore/WaveFile.h" +// #include "shared-bindings/audiomixer/Mixer.h" + +//| """Support for audio samples""" +//| + +STATIC const mp_rom_map_elem_t audiocore_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiocore) }, + { MP_ROM_QSTR(MP_QSTR_RawSample), MP_ROM_PTR(&audioio_rawsample_type) }, + { MP_ROM_QSTR(MP_QSTR_WaveFile), MP_ROM_PTR(&audioio_wavefile_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audiocore_module_globals, audiocore_module_globals_table); + +const mp_obj_module_t audiocore_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiocore_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiocore, audiocore_module, CIRCUITPY_AUDIOCORE); diff --git a/circuitpython/shared-bindings/audiocore/__init__.h b/circuitpython/shared-bindings/audiocore/__init__.h new file mode 100644 index 0000000..02437cd --- /dev/null +++ b/circuitpython/shared-bindings/audiocore/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOCORE___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOCORE___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOCORE___INIT___H diff --git a/circuitpython/shared-bindings/audioio/AudioOut.c b/circuitpython/shared-bindings/audioio/AudioOut.c new file mode 100644 index 0000000..0b4ed7b --- /dev/null +++ b/circuitpython/shared-bindings/audioio/AudioOut.c @@ -0,0 +1,269 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audioio/AudioOut.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class AudioOut: +//| """Output an analog audio signal""" +//| +//| def __init__(self, left_channel: microcontroller.Pin, *, right_channel: Optional[microcontroller.Pin] = None, quiescent_value: int = 0x8000) -> None: +//| """Create a AudioOut object associated with the given pin(s). This allows you to +//| play audio signals out on the given pin(s). +//| +//| :param ~microcontroller.Pin left_channel: The pin to output the left channel to +//| :param ~microcontroller.Pin right_channel: The pin to output the right channel to +//| :param int quiescent_value: The output value when no signal is present. Samples should start +//| and end with this value to prevent audible popping. +//| +//| Simple 8ksps 440 Hz sin wave:: +//| +//| import audiocore +//| import audioio +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wav. +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| dac = audioio.AudioOut(board.SPEAKER) +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audioio +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| data = open("cplay-5.1-16bit-16khz.wav", "rb") +//| wav = audiocore.WaveFile(data) +//| a = audioio.AudioOut(board.A0) +//| +//| print("playing") +//| a.play(wav) +//| while a.playing: +//| pass +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_left_channel, ARG_right_channel, ARG_quiescent_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_left_channel, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_right_channel, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_quiescent_value, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x8000} }, + }; + 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 *left_channel_pin = validate_obj_is_free_pin(args[ARG_left_channel].u_obj); + const mcu_pin_obj_t *right_channel_pin = validate_obj_is_free_pin_or_none(args[ARG_right_channel].u_obj); + + // create AudioOut object from the given pin + audioio_audioout_obj_t *self = m_new_obj(audioio_audioout_obj_t); + self->base.type = &audioio_audioout_type; + common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the AudioOut and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t audioio_audioout_deinit(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audioio_audioout_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_deinit_obj, audioio_audioout_deinit); + +STATIC void check_for_deinit(audioio_audioout_obj_t *self) { + if (common_hal_audioio_audioout_deinited(self)) { + raise_deinited_error(); + } +} +//| def __enter__(self) -> AudioOut: +//| """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 audioio_audioout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audioio_audioout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_audioout___exit___obj, 4, 4, audioio_audioout_obj___exit__); + + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 16 bit samples. Microcontrollers with a lower output +//| resolution will use the highest order bits to output. For example, the SAMD21 has a 10 bit +//| DAC that ignores the lowest 6 bits when playing 16 bit samples.""" +//| ... +//| +STATIC mp_obj_t audioio_audioout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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_obj_t sample = args[ARG_sample].u_obj; + common_hal_audioio_audioout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audioio_audioout_play_obj, 1, audioio_audioout_obj_play); + +//| def stop(self) -> None: +//| """Stops playback and resets to the start of the sample.""" +//| ... +//| +STATIC mp_obj_t audioio_audioout_obj_stop(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_audioio_audioout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_stop_obj, audioio_audioout_obj_stop); + +//| playing: bool +//| """True when an audio sample is being output even if `paused`. (read-only)""" +//| +STATIC mp_obj_t audioio_audioout_obj_get_playing(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audioio_audioout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_get_playing_obj, audioio_audioout_obj_get_playing); + +MP_PROPERTY_GETTER(audioio_audioout_playing_obj, + (mp_obj_t)&audioio_audioout_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +STATIC mp_obj_t audioio_audioout_obj_pause(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (!common_hal_audioio_audioout_get_playing(self)) { + mp_raise_RuntimeError(translate("Not playing")); + } + common_hal_audioio_audioout_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_pause_obj, audioio_audioout_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +STATIC mp_obj_t audioio_audioout_obj_resume(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (common_hal_audioio_audioout_get_paused(self)) { + common_hal_audioio_audioout_resume(self); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_resume_obj, audioio_audioout_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +STATIC mp_obj_t audioio_audioout_obj_get_paused(mp_obj_t self_in) { + audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audioio_audioout_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_get_paused_obj, audioio_audioout_obj_get_paused); + +MP_PROPERTY_GETTER(audioio_audioout_paused_obj, + (mp_obj_t)&audioio_audioout_get_paused_obj); + +STATIC const mp_rom_map_elem_t audioio_audioout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audioio_audioout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audioio_audioout___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audioio_audioout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audioio_audioout_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audioio_audioout_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audioio_audioout_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audioio_audioout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audioio_audioout_paused_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audioio_audioout_locals_dict, audioio_audioout_locals_dict_table); + +const mp_obj_type_t audioio_audioout_type = { + { &mp_type_type }, + .name = MP_QSTR_AudioOut, + .make_new = audioio_audioout_make_new, + .locals_dict = (mp_obj_dict_t *)&audioio_audioout_locals_dict, +}; diff --git a/circuitpython/shared-bindings/audioio/AudioOut.h b/circuitpython/shared-bindings/audioio/AudioOut.h new file mode 100644 index 0000000..163d784 --- /dev/null +++ b/circuitpython/shared-bindings/audioio/AudioOut.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOIO_AUDIOOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H + +#include "common-hal/audioio/AudioOut.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" + +extern const mp_obj_type_t audioio_audioout_type; + +// left_channel will always be non-NULL but right_channel may be for mono output. +void common_hal_audioio_audioout_construct(audioio_audioout_obj_t *self, + const mcu_pin_obj_t *left_channel, const mcu_pin_obj_t *right_channel, uint16_t default_value); + +void common_hal_audioio_audioout_deinit(audioio_audioout_obj_t *self); +bool common_hal_audioio_audioout_deinited(audioio_audioout_obj_t *self); +void common_hal_audioio_audioout_play(audioio_audioout_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audioio_audioout_stop(audioio_audioout_obj_t *self); +bool common_hal_audioio_audioout_get_playing(audioio_audioout_obj_t *self); +void common_hal_audioio_audioout_pause(audioio_audioout_obj_t *self); +void common_hal_audioio_audioout_resume(audioio_audioout_obj_t *self); +bool common_hal_audioio_audioout_get_paused(audioio_audioout_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_AUDIOOUT_H diff --git a/circuitpython/shared-bindings/audioio/__init__.c b/circuitpython/shared-bindings/audioio/__init__.c new file mode 100644 index 0000000..9f8411f --- /dev/null +++ b/circuitpython/shared-bindings/audioio/__init__.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/audioio/__init__.h" +#include "shared-bindings/audioio/AudioOut.h" + +//| """Support for audio output +//| +//| The `audioio` module contains classes to provide access to audio IO. +//| +//| 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. +//| +//| Since CircuitPython 5, `RawSample` and `WaveFile` are moved +//| to :mod:`audiocore`, and `Mixer` is moved to :mod:`audiomixer`. +//| +//| For compatibility with CircuitPython 4.x, some builds allow the items in +//| `audiocore` to be imported from `audioio`. This will be removed for all +//| boards in a future build of CircuitPython.""" +//| + +STATIC const mp_rom_map_elem_t audioio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audioio) }, + { MP_ROM_QSTR(MP_QSTR_AudioOut), MP_ROM_PTR(&audioio_audioout_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audioio_module_globals, audioio_module_globals_table); + +const mp_obj_module_t audioio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audioio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audioio, audioio_module, CIRCUITPY_AUDIOIO); diff --git a/circuitpython/shared-bindings/audioio/__init__.h b/circuitpython/shared-bindings/audioio/__init__.h new file mode 100644 index 0000000..e4b7067 --- /dev/null +++ b/circuitpython/shared-bindings/audioio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO___INIT___H diff --git a/circuitpython/shared-bindings/audiomixer/Mixer.c b/circuitpython/shared-bindings/audiomixer/Mixer.c new file mode 100644 index 0000000..a165435 --- /dev/null +++ b/circuitpython/shared-bindings/audiomixer/Mixer.c @@ -0,0 +1,294 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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 "shared-bindings/audiomixer/Mixer.h" +#include "shared-bindings/audiomixer/MixerVoice.h" +#include "shared-module/audiomixer/MixerVoice.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Mixer: +//| """Mixes one or more audio samples together into one sample.""" +//| +//| def __init__(self, voice_count: int = 2, buffer_size: int = 1024, channel_count: int = 2, bits_per_sample: int = 16, samples_signed: bool = True, sample_rate: int = 8000) -> None: +//| """Create a Mixer object that can mix multiple channels with the same sample rate. +//| Samples are accessed and controlled with the mixer's `audiomixer.MixerVoice` objects. +//| +//| :param int voice_count: The maximum number of voices to mix +//| :param int buffer_size: The total size in bytes of the buffers to mix into +//| :param int channel_count: The number of channels the source samples contain. 1 = mono; 2 = stereo. +//| :param int bits_per_sample: The bits per sample of the samples being played +//| :param bool samples_signed: Samples are signed (True) or unsigned (False) +//| :param int sample_rate: The sample rate to be used for all samples +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audioio +//| import audiocore +//| import audiomixer +//| import digitalio +//| +//| a = audioio.AudioOut(board.A0) +//| music = audiocore.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) +//| drum = audiocore.WaveFile(open("drum.wav", "rb")) +//| mixer = audiomixer.Mixer(voice_count=2, sample_rate=16000, channel_count=1, +//| bits_per_sample=16, samples_signed=True) +//| +//| print("playing") +//| # Have AudioOut play our Mixer source +//| a.play(mixer) +//| # Play the first sample voice +//| mixer.voice[0].play(music) +//| while mixer.playing: +//| # Play the second sample voice +//| mixer.voice[1].play(drum) +//| time.sleep(1) +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t audiomixer_mixer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_voice_count, ARG_buffer_size, ARG_channel_count, ARG_bits_per_sample, ARG_samples_signed, ARG_sample_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} }, + { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1024} }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2} }, + { MP_QSTR_bits_per_sample, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_samples_signed, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, + }; + 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); + + mp_int_t voice_count = args[ARG_voice_count].u_int; + if (voice_count < 1 || voice_count > 255) { + mp_raise_ValueError(translate("Invalid voice count")); + } + + mp_int_t channel_count = args[ARG_channel_count].u_int; + if (channel_count < 1 || channel_count > 2) { + mp_raise_ValueError(translate("Invalid channel count")); + } + mp_int_t sample_rate = args[ARG_sample_rate].u_int; + if (sample_rate < 1) { + mp_raise_ValueError(translate("Sample rate must be positive")); + } + mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int; + if (bits_per_sample != 8 && bits_per_sample != 16) { + mp_raise_ValueError(translate("bits_per_sample must be 8 or 16")); + } + audiomixer_mixer_obj_t *self = m_new_obj_var(audiomixer_mixer_obj_t, mp_obj_t, voice_count); + self->base.type = &audiomixer_mixer_type; + common_hal_audiomixer_mixer_construct(self, voice_count, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + + for (int v = 0; v < voice_count; v++) { + self->voice[v] = audiomixer_mixervoice_type.make_new(&audiomixer_mixervoice_type, 0, 0, NULL); + common_hal_audiomixer_mixervoice_set_parent(self->voice[v], self); + } + self->voice_tuple = mp_obj_new_tuple(self->voice_count, self->voice); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the Mixer and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t audiomixer_mixer_deinit(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiomixer_mixer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_deinit_obj, audiomixer_mixer_deinit); + +STATIC void check_for_deinit(audiomixer_mixer_obj_t *self) { + if (common_hal_audiomixer_mixer_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> Mixer: +//| """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 audiomixer_mixer_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiomixer_mixer_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiomixer_mixer___exit___obj, 4, 4, audiomixer_mixer_obj___exit__); + +//| playing: bool +//| """True when any voice is being output. (read-only)""" +//| +STATIC mp_obj_t audiomixer_mixer_obj_get_playing(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiomixer_mixer_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_get_playing_obj, audiomixer_mixer_obj_get_playing); + +MP_PROPERTY_GETTER(audiomixer_mixer_playing_obj, + (mp_obj_t)&audiomixer_mixer_get_playing_obj); + +//| sample_rate: int +//| """32 bit value that dictates how quickly samples are played in Hertz (cycles per second).""" +//| +STATIC mp_obj_t audiomixer_mixer_obj_get_sample_rate(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audiomixer_mixer_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_get_sample_rate_obj, audiomixer_mixer_obj_get_sample_rate); + +MP_PROPERTY_GETTER(audiomixer_mixer_sample_rate_obj, + (mp_obj_t)&audiomixer_mixer_get_sample_rate_obj); + +//| voice: Tuple[MixerVoice, ...] +//| """A tuple of the mixer's `audiomixer.MixerVoice` object(s). +//| +//| .. code-block:: python +//| +//| >>> mixer.voice +//| (<MixerVoice>,)""" +STATIC mp_obj_t audiomixer_mixer_obj_get_voice(mp_obj_t self_in) { + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return self->voice_tuple; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixer_get_voice_obj, audiomixer_mixer_obj_get_voice); + +MP_PROPERTY_GETTER(audiomixer_mixer_voice_obj, + (mp_obj_t)&audiomixer_mixer_get_voice_obj); + +//| def play(self, sample: circuitpython_typing.AudioSample, *, voice: int = 0, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample must match the Mixer's encoding settings given in the constructor.""" +//| ... +//| +STATIC mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_voice, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_voice, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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 v = args[ARG_voice].u_int; + if (v > (self->voice_count - 1)) { + mp_raise_ValueError(translate("Invalid voice")); + } + audiomixer_mixervoice_obj_t *voice = MP_OBJ_TO_PTR(self->voice[v]); + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audiomixer_mixervoice_play(voice, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixer_play_obj, 1, audiomixer_mixer_obj_play); + +//| def stop_voice(self, voice: int = 0) -> None: +//| """Stops playback of the sample on the given voice.""" +//| ... +//| +STATIC mp_obj_t audiomixer_mixer_obj_stop_voice(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_voice }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, + }; + audiomixer_mixer_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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 v = args[ARG_voice].u_int; + if (v > (self->voice_count - 1)) { + mp_raise_ValueError(translate("Invalid voice")); + } + audiomixer_mixervoice_obj_t *voice = MP_OBJ_TO_PTR(self->voice[v]); + common_hal_audiomixer_mixervoice_stop(voice); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixer_stop_voice_obj, 1, audiomixer_mixer_obj_stop_voice); + + +STATIC const mp_rom_map_elem_t audiomixer_mixer_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiomixer_mixer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiomixer_mixer___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiomixer_mixer_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_voice), MP_ROM_PTR(&audiomixer_mixer_stop_voice_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiomixer_mixer_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audiomixer_mixer_sample_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_voice), MP_ROM_PTR(&audiomixer_mixer_voice_obj) } +}; +STATIC MP_DEFINE_CONST_DICT(audiomixer_mixer_locals_dict, audiomixer_mixer_locals_dict_table); + +STATIC const audiosample_p_t audiomixer_mixer_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .sample_rate = (audiosample_sample_rate_fun)common_hal_audiomixer_mixer_get_sample_rate, + .bits_per_sample = (audiosample_bits_per_sample_fun)common_hal_audiomixer_mixer_get_bits_per_sample, + .channel_count = (audiosample_channel_count_fun)common_hal_audiomixer_mixer_get_channel_count, + .reset_buffer = (audiosample_reset_buffer_fun)audiomixer_mixer_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiomixer_mixer_get_buffer, + .get_buffer_structure = (audiosample_get_buffer_structure_fun)audiomixer_mixer_get_buffer_structure, +}; + +const mp_obj_type_t audiomixer_mixer_type = { + { &mp_type_type }, + .name = MP_QSTR_Mixer, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = audiomixer_mixer_make_new, + .locals_dict = (mp_obj_dict_t *)&audiomixer_mixer_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &audiomixer_mixer_proto, + ), +}; diff --git a/circuitpython/shared-bindings/audiomixer/Mixer.h b/circuitpython/shared-bindings/audiomixer/Mixer.h new file mode 100644 index 0000000..88592a1 --- /dev/null +++ b/circuitpython/shared-bindings/audiomixer/Mixer.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_AUDIOMIXER_MIXER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER_MIXER_H + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audiomixer/Mixer.h" +#include "shared-bindings/audiocore/RawSample.h" + +extern const mp_obj_type_t audiomixer_mixer_type; +extern const mp_obj_type_t audiomixer_mixervoice_type; + +void common_hal_audiomixer_mixer_construct(audiomixer_mixer_obj_t *self, + uint8_t voice_count, + uint32_t buffer_size, + uint8_t bits_per_sample, + bool samples_signed, + uint8_t channel_count, + uint32_t sample_rate); + +void common_hal_audiomixer_mixer_deinit(audiomixer_mixer_obj_t *self); +bool common_hal_audiomixer_mixer_deinited(audiomixer_mixer_obj_t *self); + +bool common_hal_audiomixer_mixer_get_playing(audiomixer_mixer_obj_t *self); +uint32_t common_hal_audiomixer_mixer_get_sample_rate(audiomixer_mixer_obj_t *self); +uint8_t common_hal_audiomixer_mixer_get_channel_count(audiomixer_mixer_obj_t *self); +uint8_t common_hal_audiomixer_mixer_get_bits_per_sample(audiomixer_mixer_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER_MIXER_H diff --git a/circuitpython/shared-bindings/audiomixer/MixerVoice.c b/circuitpython/shared-bindings/audiomixer/MixerVoice.c new file mode 100644 index 0000000..f02b952 --- /dev/null +++ b/circuitpython/shared-bindings/audiomixer/MixerVoice.c @@ -0,0 +1,168 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM 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 "shared-bindings/audiomixer/Mixer.h" +#include "shared-bindings/audiomixer/MixerVoice.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class MixerVoice: +//| """Voice objects used with Mixer +//| +//| Used to access and control samples with `audiomixer.Mixer`.""" +//| +//| def __init__(self) -> None: +//| """MixerVoice instance object(s) created by `audiomixer.Mixer`.""" +//| ... +//| +// TODO: support mono or stereo voices +STATIC mp_obj_t audiomixer_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 0, 0, false); + audiomixer_mixervoice_obj_t *self = m_new_obj(audiomixer_mixervoice_obj_t); + self->base.type = &audiomixer_mixervoice_type; + + common_hal_audiomixer_mixervoice_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when ``loop=False``, and continuously when ``loop=True``. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample must match the `audiomixer.Mixer`'s encoding settings given in the constructor.""" +//| ... +//| +STATIC mp_obj_t audiomixer_mixervoice_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_obj_t sample = args[ARG_sample].u_obj; + common_hal_audiomixer_mixervoice_play(self, sample, args[ARG_loop].u_bool); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixervoice_play_obj, 1, audiomixer_mixervoice_obj_play); + +//| def stop(self) -> None: +//| """Stops playback of the sample on this voice.""" +//| ... +//| +STATIC mp_obj_t audiomixer_mixervoice_obj_stop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_voice }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_voice, MP_ARG_INT, {.u_int = 0} }, + }; + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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); + + common_hal_audiomixer_mixervoice_stop(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixervoice_stop_obj, 1, audiomixer_mixervoice_obj_stop); + +//| level: float +//| """The volume level of a voice, as a floating point number between 0 and 1.""" +//| +STATIC mp_obj_t audiomixer_mixervoice_obj_get_level(mp_obj_t self_in) { + return mp_obj_new_float(common_hal_audiomixer_mixervoice_get_level(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixervoice_get_level_obj, audiomixer_mixervoice_obj_get_level); + +STATIC mp_obj_t audiomixer_mixervoice_obj_set_level(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_level }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_level, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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); + + float level = mp_obj_get_float(args[ARG_level].u_obj); + + if (level > 1 || level < 0) { + mp_raise_ValueError(translate("level must be between 0 and 1")); + } + + common_hal_audiomixer_mixervoice_set_level(self, level); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixervoice_set_level_obj, 1, audiomixer_mixervoice_obj_set_level); + +MP_PROPERTY_GETSET(audiomixer_mixervoice_level_obj, + (mp_obj_t)&audiomixer_mixervoice_get_level_obj, + (mp_obj_t)&audiomixer_mixervoice_set_level_obj); + +//| playing: bool +//| """True when this voice is being output. (read-only)""" +//| + +STATIC mp_obj_t audiomixer_mixervoice_obj_get_playing(mp_obj_t self_in) { + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_audiomixer_mixervoice_get_playing(self)); + +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixervoice_get_playing_obj, audiomixer_mixervoice_obj_get_playing); + +MP_PROPERTY_GETTER(audiomixer_mixervoice_playing_obj, + (mp_obj_t)&audiomixer_mixervoice_get_playing_obj); + +STATIC const mp_rom_map_elem_t audiomixer_mixervoice_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiomixer_mixervoice_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiomixer_mixervoice_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiomixer_mixervoice_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audiomixer_mixervoice_level_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audiomixer_mixervoice_locals_dict, audiomixer_mixervoice_locals_dict_table); + +const mp_obj_type_t audiomixer_mixervoice_type = { + { &mp_type_type }, + .name = MP_QSTR_MixerVoice, + .make_new = audiomixer_mixervoice_make_new, + .locals_dict = (mp_obj_dict_t *)&audiomixer_mixervoice_locals_dict, +}; diff --git a/circuitpython/shared-bindings/audiomixer/MixerVoice.h b/circuitpython/shared-bindings/audiomixer/MixerVoice.h new file mode 100644 index 0000000..bce4632 --- /dev/null +++ b/circuitpython/shared-bindings/audiomixer/MixerVoice.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 DeanM 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 SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_ +#define SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_ + +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" + +#include "shared-module/audiomixer/MixerVoice.h" +#include "shared-module/audiomixer/Mixer.h" + +extern const mp_obj_type_t audiomixer_mixer_type; +extern const mp_obj_type_t audiomixer_mixervoice_type; + +void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *self); +void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t *self, audiomixer_mixer_obj_t *parent); +void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t *self); +float common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self); +void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, float gain); + +bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t *self); + +#endif /* SHARED_BINDINGS_AUDIOMIXER_MIXERVOICE_H_ */ diff --git a/circuitpython/shared-bindings/audiomixer/__init__.c b/circuitpython/shared-bindings/audiomixer/__init__.c new file mode 100644 index 0000000..a29d4f1 --- /dev/null +++ b/circuitpython/shared-bindings/audiomixer/__init__.c @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Michael Schroeder + * + * 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/audiomixer/Mixer.h" + +//| """Support for audio mixing""" +//| + +STATIC const mp_rom_map_elem_t audiomixer_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiomixer) }, + { MP_ROM_QSTR(MP_QSTR_Mixer), MP_ROM_PTR(&audiomixer_mixer_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audiomixer_module_globals, audiomixer_module_globals_table); + +const mp_obj_module_t audiomixer_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiomixer_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiomixer, audiomixer_module, CIRCUITPY_AUDIOMIXER); diff --git a/circuitpython/shared-bindings/audiomixer/__init__.h b/circuitpython/shared-bindings/audiomixer/__init__.h new file mode 100644 index 0000000..35a9044 --- /dev/null +++ b/circuitpython/shared-bindings/audiomixer/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Michael Schroeder + * + * 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_AUDIOMIXER___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMIXER___INIT___H diff --git a/circuitpython/shared-bindings/audiomp3/MP3Decoder.c b/circuitpython/shared-bindings/audiomp3/MP3Decoder.c new file mode 100644 index 0000000..16efa36 --- /dev/null +++ b/circuitpython/shared-bindings/audiomp3/MP3Decoder.c @@ -0,0 +1,286 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2019 Jeff Epler 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/audiomp3/MP3Decoder.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class MP3Decoder: +//| """Load a mp3 file for audio playback +//| +//| .. note:: +//| +//| ``MP3Decoder`` uses a lot of contiguous memory, so care should be given to +//| optimizing memory usage. More information and recommendations can be found here: +//| https://learn.adafruit.com/Memory-saving-tips-for-CircuitPython/reducing-memory-fragmentation +//| """ +//| +//| def __init__(self, file: typing.BinaryIO, buffer: WriteableBuffer) -> None: +//| +//| """Load a .mp3 file for playback with `audioio.AudioOut` or `audiobusio.I2SOut`. +//| +//| :param typing.BinaryIO file: Already opened mp3 file +//| :param ~circuitpython_typing.WriteableBuffer buffer: Optional pre-allocated buffer, that will be split in half and used for double-buffering of the data. If not provided, two buffers are allocated internally. The specific buffer size required depends on the mp3 file. +//| +//| Playback of mp3 audio is CPU intensive, and the +//| exact limit depends on many factors such as the particular +//| microcontroller, SD card or flash performance, and other +//| code in use such as displayio. If playback is garbled, +//| skips, or plays as static, first try using a "simpler" mp3: +//| +//| * Use constant bit rate (CBR) not VBR or ABR (variable or average bit rate) when encoding your mp3 file +//| * Use a lower sample rate (e.g., 11.025kHz instead of 48kHz) +//| * Use a lower bit rate (e.g., 32kbit/s instead of 256kbit/s) +//| +//| Reduce activity taking place at the same time as +//| mp3 playback. For instance, only update small portions of a +//| displayio screen if audio is playing. Disable auto-refresh +//| and explicitly call refresh. +//| +//| Playing a mp3 file from flash:: +//| +//| import board +//| import audiomp3 +//| import audioio +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| data = open("cplay-16bit-16khz-64kbps.mp3", "rb") +//| mp3 = audiomp3.MP3Decoder(data) +//| a = audioio.AudioOut(board.A0) +//| +//| print("playing") +//| a.play(mp3) +//| while a.playing: +//| pass +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t audiomp3_mp3file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 2, false); + + audiomp3_mp3file_obj_t *self = m_new_obj(audiomp3_mp3file_obj_t); + self->base.type = &audiomp3_mp3file_type; + if (!mp_obj_is_type(args[0], &mp_type_fileio)) { + mp_raise_TypeError(translate("file must be a file opened in byte mode")); + } + uint8_t *buffer = NULL; + size_t buffer_size = 0; + if (n_args >= 2) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + buffer = bufinfo.buf; + buffer_size = bufinfo.len; + } + common_hal_audiomp3_mp3file_construct(self, MP_OBJ_TO_PTR(args[0]), + buffer, buffer_size); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the MP3 and releases all memory resources for reuse.""" +//| ... +//| +STATIC mp_obj_t audiomp3_mp3file_deinit(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiomp3_mp3file_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_deinit_obj, audiomp3_mp3file_deinit); + +STATIC void check_for_deinit(audiomp3_mp3file_obj_t *self) { + if (common_hal_audiomp3_mp3file_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> MP3Decoder: +//| """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 audiomp3_mp3file_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiomp3_mp3file_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiomp3_mp3file___exit___obj, 4, 4, audiomp3_mp3file_obj___exit__); + +//| file: typing.BinaryIO +//| """File to play back.""" +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_file(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return self->file; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_file_obj, audiomp3_mp3file_obj_get_file); + +STATIC mp_obj_t audiomp3_mp3file_obj_set_file(mp_obj_t self_in, mp_obj_t file) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (!mp_obj_is_type(file, &mp_type_fileio)) { + mp_raise_TypeError(translate("file must be a file opened in byte mode")); + } + common_hal_audiomp3_mp3file_set_file(self, file); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiomp3_mp3file_set_file_obj, audiomp3_mp3file_obj_set_file); + +MP_PROPERTY_GETSET(audiomp3_mp3file_file_obj, + (mp_obj_t)&audiomp3_mp3file_get_file_obj, + (mp_obj_t)&audiomp3_mp3file_set_file_obj); + + + +//| sample_rate: int +//| """32 bit value that dictates how quickly samples are loaded into the DAC +//| in Hertz (cycles per second). When the sample is looped, this can change +//| the pitch output without changing the underlying sample.""" +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_sample_rate(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audiomp3_mp3file_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_sample_rate_obj, audiomp3_mp3file_obj_get_sample_rate); + +STATIC mp_obj_t audiomp3_mp3file_obj_set_sample_rate(mp_obj_t self_in, mp_obj_t sample_rate) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_audiomp3_mp3file_set_sample_rate(self, mp_obj_get_int(sample_rate)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiomp3_mp3file_set_sample_rate_obj, audiomp3_mp3file_obj_set_sample_rate); + +MP_PROPERTY_GETSET(audiomp3_mp3file_sample_rate_obj, + (mp_obj_t)&audiomp3_mp3file_get_sample_rate_obj, + (mp_obj_t)&audiomp3_mp3file_set_sample_rate_obj); + +//| bits_per_sample: int +//| """Bits per sample. (read only)""" +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_bits_per_sample(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audiomp3_mp3file_get_bits_per_sample(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_bits_per_sample_obj, audiomp3_mp3file_obj_get_bits_per_sample); + +MP_PROPERTY_GETTER(audiomp3_mp3file_bits_per_sample_obj, + (mp_obj_t)&audiomp3_mp3file_get_bits_per_sample_obj); + +//| channel_count: int +//| """Number of audio channels. (read only)""" +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_channel_count(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audiomp3_mp3file_get_channel_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_channel_count_obj, audiomp3_mp3file_obj_get_channel_count); + +MP_PROPERTY_GETTER(audiomp3_mp3file_channel_count_obj, + (mp_obj_t)&audiomp3_mp3file_get_channel_count_obj); + +//| rms_level: float +//| """The RMS audio level of a recently played moment of audio. (read only)""" +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_rms_level(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_float(common_hal_audiomp3_mp3file_get_rms_level(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_rms_level_obj, audiomp3_mp3file_obj_get_rms_level); + +MP_PROPERTY_GETTER(audiomp3_mp3file_rms_level_obj, + (mp_obj_t)&audiomp3_mp3file_get_rms_level_obj); + +//| samples_decoded: int +//| """The number of audio samples decoded from the current file. (read only)""" +//| +STATIC mp_obj_t audiomp3_mp3file_obj_get_samples_decoded(mp_obj_t self_in) { + audiomp3_mp3file_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_audiomp3_mp3file_get_samples_decoded(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomp3_mp3file_get_samples_decoded_obj, audiomp3_mp3file_obj_get_samples_decoded); + +MP_PROPERTY_GETTER(audiomp3_mp3file_samples_decoded_obj, + (mp_obj_t)&audiomp3_mp3file_get_samples_decoded_obj); + +STATIC const mp_rom_map_elem_t audiomp3_mp3file_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiomp3_mp3file_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiomp3_mp3file___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_file), MP_ROM_PTR(&audiomp3_mp3file_file_obj) }, + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audiomp3_mp3file_sample_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_bits_per_sample), MP_ROM_PTR(&audiomp3_mp3file_bits_per_sample_obj) }, + { MP_ROM_QSTR(MP_QSTR_channel_count), MP_ROM_PTR(&audiomp3_mp3file_channel_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_rms_level), MP_ROM_PTR(&audiomp3_mp3file_rms_level_obj) }, + { MP_ROM_QSTR(MP_QSTR_samples_decoded), MP_ROM_PTR(&audiomp3_mp3file_samples_decoded_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audiomp3_mp3file_locals_dict, audiomp3_mp3file_locals_dict_table); + +STATIC const audiosample_p_t audiomp3_mp3file_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .sample_rate = (audiosample_sample_rate_fun)common_hal_audiomp3_mp3file_get_sample_rate, + .bits_per_sample = (audiosample_bits_per_sample_fun)common_hal_audiomp3_mp3file_get_bits_per_sample, + .channel_count = (audiosample_channel_count_fun)common_hal_audiomp3_mp3file_get_channel_count, + .reset_buffer = (audiosample_reset_buffer_fun)audiomp3_mp3file_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiomp3_mp3file_get_buffer, + .get_buffer_structure = (audiosample_get_buffer_structure_fun)audiomp3_mp3file_get_buffer_structure, +}; + +const mp_obj_type_t audiomp3_mp3file_type = { + { &mp_type_type }, + .name = MP_QSTR_MP3Decoder, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = audiomp3_mp3file_make_new, + .locals_dict = (mp_obj_dict_t *)&audiomp3_mp3file_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &audiomp3_mp3file_proto, + ), +}; diff --git a/circuitpython/shared-bindings/audiomp3/MP3Decoder.h b/circuitpython/shared-bindings/audiomp3/MP3Decoder.h new file mode 100644 index 0000000..e1f4fcf --- /dev/null +++ b/circuitpython/shared-bindings/audiomp3/MP3Decoder.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2019 Jeff Epler 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_AUDIOIO_MP3FILE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MP3FILE_H + +#include "py/obj.h" +#include "extmod/vfs_fat.h" + +#include "shared-module/audiomp3/MP3Decoder.h" + +extern const mp_obj_type_t audiomp3_mp3file_type; + +void common_hal_audiomp3_mp3file_construct(audiomp3_mp3file_obj_t *self, + pyb_file_obj_t *file, uint8_t *buffer, size_t buffer_size); + +void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t *self, pyb_file_obj_t *file); +void common_hal_audiomp3_mp3file_deinit(audiomp3_mp3file_obj_t *self); +bool common_hal_audiomp3_mp3file_deinited(audiomp3_mp3file_obj_t *self); +uint32_t common_hal_audiomp3_mp3file_get_sample_rate(audiomp3_mp3file_obj_t *self); +void common_hal_audiomp3_mp3file_set_sample_rate(audiomp3_mp3file_obj_t *self, uint32_t sample_rate); +uint8_t common_hal_audiomp3_mp3file_get_bits_per_sample(audiomp3_mp3file_obj_t *self); +uint8_t common_hal_audiomp3_mp3file_get_channel_count(audiomp3_mp3file_obj_t *self); +float common_hal_audiomp3_mp3file_get_rms_level(audiomp3_mp3file_obj_t *self); +uint32_t common_hal_audiomp3_mp3file_get_samples_decoded(audiomp3_mp3file_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO_MP3FILE_H diff --git a/circuitpython/shared-bindings/audiomp3/__init__.c b/circuitpython/shared-bindings/audiomp3/__init__.c new file mode 100644 index 0000000..13f02b1 --- /dev/null +++ b/circuitpython/shared-bindings/audiomp3/__init__.c @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler 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/audiomp3/MP3Decoder.h" + +//| """Support for MP3-compressed audio files +//| +//| For more infomration about working with MP3 files in CircuitPython, +//| see `this CircuitPython Essentials Learn guide page +//| <https://learn.adafruit.com/circuitpython-essentials/circuitpython-mp3-audio>`_. +//| """ +//| + +STATIC const mp_rom_map_elem_t audiomp3_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiomp3) }, + { MP_ROM_QSTR(MP_QSTR_MP3Decoder), MP_ROM_PTR(&audiomp3_mp3file_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audiomp3_module_globals, audiomp3_module_globals_table); + +const mp_obj_module_t audiomp3_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiomp3_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiomp3, audiomp3_module, CIRCUITPY_AUDIOMP3); diff --git a/circuitpython/shared-bindings/audiomp3/__init__.h b/circuitpython/shared-bindings/audiomp3/__init__.h new file mode 100644 index 0000000..9026af6 --- /dev/null +++ b/circuitpython/shared-bindings/audiomp3/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler 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_AUDIOMP3___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMP3___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOMP3___INIT___H diff --git a/circuitpython/shared-bindings/audiopwmio/PWMAudioOut.c b/circuitpython/shared-bindings/audiopwmio/PWMAudioOut.c new file mode 100644 index 0000000..8118eb4 --- /dev/null +++ b/circuitpython/shared-bindings/audiopwmio/PWMAudioOut.c @@ -0,0 +1,270 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiopwmio/PWMAudioOut.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class PWMAudioOut: +//| """Output an analog audio signal by varying the PWM duty cycle.""" +//| +//| def __init__(self, left_channel: microcontroller.Pin, *, right_channel: Optional[microcontroller.Pin] = None, quiescent_value: int = 0x8000) -> None: +//| """Create a PWMAudioOut object associated with the given pin(s). This allows you to +//| play audio signals out on the given pin(s). In contrast to mod:`audioio`, +//| the pin(s) specified are digital pins, and are driven with a device-dependent PWM +//| signal. +//| +//| :param ~microcontroller.Pin left_channel: The pin to output the left channel to +//| :param ~microcontroller.Pin right_channel: The pin to output the right channel to +//| :param int quiescent_value: The output value when no signal is present. Samples should start +//| and end with this value to prevent audible popping. +//| +//| Simple 8ksps 440 Hz sin wave:: +//| +//| import audiocore +//| import audiopwmio +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wav. +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| dac = audiopwmio.PWMAudioOut(board.SPEAKER) +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import audiopwmio +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| data = open("cplay-5.1-16bit-16khz.wav", "rb") +//| wav = audiocore.WaveFile(data) +//| a = audiopwmio.PWMAudioOut(board.SPEAKER) +//| +//| print("playing") +//| a.play(wav) +//| while a.playing: +//| pass +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_left_channel, ARG_right_channel, ARG_quiescent_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_left_channel, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_right_channel, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_quiescent_value, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x8000} }, + }; + 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 *left_channel_pin = validate_obj_is_free_pin(args[ARG_left_channel].u_obj); + const mcu_pin_obj_t *right_channel_pin = validate_obj_is_free_pin_or_none(args[ARG_right_channel].u_obj); + + // create AudioOut object from the given pin + audiopwmio_pwmaudioout_obj_t *self = m_new_obj(audiopwmio_pwmaudioout_obj_t); + self->base.type = &audiopwmio_pwmaudioout_type; + common_hal_audiopwmio_pwmaudioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the PWMAudioOut and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_deinit(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiopwmio_pwmaudioout_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_deinit_obj, audiopwmio_pwmaudioout_deinit); + +STATIC void check_for_deinit(audiopwmio_pwmaudioout_obj_t *self) { + if (common_hal_audiopwmio_pwmaudioout_deinited(self)) { + raise_deinited_error(); + } +} +//| def __enter__(self) -> PWMAudioOut: +//| """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 audiopwmio_pwmaudioout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiopwmio_pwmaudioout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiopwmio_pwmaudioout___exit___obj, 4, 4, audiopwmio_pwmaudioout_obj___exit__); + + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 16 bit samples. Microcontrollers with a lower output +//| resolution will use the highest order bits to output.""" +//| ... +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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_obj_t sample = args[ARG_sample].u_obj; + common_hal_audiopwmio_pwmaudioout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiopwmio_pwmaudioout_play_obj, 1, audiopwmio_pwmaudioout_obj_play); + +//| def stop(self) -> None: +//| """Stops playback and resets to the start of the sample.""" +//| ... +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_stop(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_audiopwmio_pwmaudioout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_stop_obj, audiopwmio_pwmaudioout_obj_stop); + +//| playing: bool +//| """True when an audio sample is being output even if `paused`. (read-only)""" +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_get_playing(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiopwmio_pwmaudioout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_get_playing_obj, audiopwmio_pwmaudioout_obj_get_playing); + +MP_PROPERTY_GETTER(audiopwmio_pwmaudioout_playing_obj, + (mp_obj_t)&audiopwmio_pwmaudioout_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_pause(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (!common_hal_audiopwmio_pwmaudioout_get_playing(self)) { + mp_raise_RuntimeError(translate("Not playing")); + } + common_hal_audiopwmio_pwmaudioout_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_pause_obj, audiopwmio_pwmaudioout_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_resume(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (common_hal_audiopwmio_pwmaudioout_get_paused(self)) { + common_hal_audiopwmio_pwmaudioout_resume(self); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_resume_obj, audiopwmio_pwmaudioout_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_get_paused(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiopwmio_pwmaudioout_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_get_paused_obj, audiopwmio_pwmaudioout_obj_get_paused); + +MP_PROPERTY_GETTER(audiopwmio_pwmaudioout_paused_obj, + (mp_obj_t)&audiopwmio_pwmaudioout_get_paused_obj); + +STATIC const mp_rom_map_elem_t audiopwmio_pwmaudioout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiopwmio_pwmaudioout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiopwmio_pwmaudioout___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiopwmio_pwmaudioout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiopwmio_pwmaudioout_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audiopwmio_pwmaudioout_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audiopwmio_pwmaudioout_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiopwmio_pwmaudioout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audiopwmio_pwmaudioout_paused_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audiopwmio_pwmaudioout_locals_dict, audiopwmio_pwmaudioout_locals_dict_table); + +const mp_obj_type_t audiopwmio_pwmaudioout_type = { + { &mp_type_type }, + .name = MP_QSTR_PWMAudioOut, + .make_new = audiopwmio_pwmaudioout_make_new, + .locals_dict = (mp_obj_dict_t *)&audiopwmio_pwmaudioout_locals_dict, +}; diff --git a/circuitpython/shared-bindings/audiopwmio/PWMAudioOut.h b/circuitpython/shared-bindings/audiopwmio/PWMAudioOut.h new file mode 100644 index 0000000..15d1fa9 --- /dev/null +++ b/circuitpython/shared-bindings/audiopwmio/PWMAudioOut.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOPWMIO_AUDIOOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOPWMIO_AUDIOOUT_H + +#include "common-hal/audiopwmio/PWMAudioOut.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" + +extern const mp_obj_type_t audiopwmio_pwmaudioout_type; + +// left_channel will always be non-NULL but right_channel may be for mono output. +void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *self, + const mcu_pin_obj_t *left_channel, const mcu_pin_obj_t *right_channel, uint16_t default_value); + +void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t *self); +bool common_hal_audiopwmio_pwmaudioout_deinited(audiopwmio_pwmaudioout_obj_t *self); +void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audiopwmio_pwmaudioout_stop(audiopwmio_pwmaudioout_obj_t *self); +bool common_hal_audiopwmio_pwmaudioout_get_playing(audiopwmio_pwmaudioout_obj_t *self); +void common_hal_audiopwmio_pwmaudioout_pause(audiopwmio_pwmaudioout_obj_t *self); +void common_hal_audiopwmio_pwmaudioout_resume(audiopwmio_pwmaudioout_obj_t *self); +bool common_hal_audiopwmio_pwmaudioout_get_paused(audiopwmio_pwmaudioout_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOPWMIO_AUDIOOUT_H diff --git a/circuitpython/shared-bindings/audiopwmio/__init__.c b/circuitpython/shared-bindings/audiopwmio/__init__.c new file mode 100644 index 0000000..41a756e --- /dev/null +++ b/circuitpython/shared-bindings/audiopwmio/__init__.c @@ -0,0 +1,61 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/audiopwmio/__init__.h" +#include "shared-bindings/audiopwmio/PWMAudioOut.h" + +//| """Audio output via digital PWM +//| +//| The `audiopwmio` module contains classes to provide access to audio IO. +//| +//| 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. +//| +//| Since CircuitPython 5, `Mixer`, `RawSample` and `WaveFile` are moved +//| to :mod:`audiocore`.""" +//| + +STATIC const mp_rom_map_elem_t audiopwmio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiopwmio) }, + { MP_ROM_QSTR(MP_QSTR_PWMAudioOut), MP_ROM_PTR(&audiopwmio_pwmaudioout_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audiopwmio_module_globals, audiopwmio_module_globals_table); + +const mp_obj_module_t audiopwmio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiopwmio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiopwmio, audiopwmio_module, CIRCUITPY_AUDIOPWMIO); diff --git a/circuitpython/shared-bindings/audiopwmio/__init__.h b/circuitpython/shared-bindings/audiopwmio/__init__.h new file mode 100644 index 0000000..d7956d3 --- /dev/null +++ b/circuitpython/shared-bindings/audiopwmio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_AUDIOPWMIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOPWMIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOPWMIO___INIT___H diff --git a/circuitpython/shared-bindings/bitbangio/I2C.c b/circuitpython/shared-bindings/bitbangio/I2C.c new file mode 100644 index 0000000..c65b184 --- /dev/null +++ b/circuitpython/shared-bindings/bitbangio/I2C.c @@ -0,0 +1,351 @@ +/* + * 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 +// bitbangio.I2C class. + +#include "shared-bindings/bitbangio/I2C.h" +#include "shared-bindings/microcontroller/Pin.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/runtime.h" +#include "supervisor/shared/translate.h" + +//| class I2C: +//| """Two wire serial protocol""" +//| +//| def __init__(self, scl: microcontroller.Pin, sda: microcontroller.Pin, *, frequency: int = 400000, 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.i2c_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 of the bus +//| :param int timeout: The maximum clock stretching timeout in microseconds""" +//| ... +//| +STATIC mp_obj_t bitbangio_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + 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 = 400000} }, + { 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); + + bitbangio_i2c_obj_t *self = m_new_obj(bitbangio_i2c_obj_t); + self->base.type = &bitbangio_i2c_type; + shared_module_bitbangio_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 bitbangio_i2c_obj_deinit(mp_obj_t self_in) { + bitbangio_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + shared_module_bitbangio_i2c_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bitbangio_i2c_deinit_obj, bitbangio_i2c_obj_deinit); + +STATIC void check_for_deinit(bitbangio_i2c_obj_t *self) { + if (shared_module_bitbangio_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 bitbangio_i2c_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + shared_module_bitbangio_i2c_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bitbangio_i2c_obj___exit___obj, 4, 4, bitbangio_i2c_obj___exit__); + +static void check_lock(bitbangio_i2c_obj_t *self) { + if (!shared_module_bitbangio_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. A device responds if it pulls the SDA line low after +//| its address (including a read bit) is sent on the bus.""" +//| ... +//| +STATIC mp_obj_t bitbangio_i2c_scan(mp_obj_t self_in) { + bitbangio_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 = shared_module_bitbangio_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(bitbangio_i2c_scan_obj, bitbangio_i2c_scan); + +//| def try_lock(self) -> bool: +//| """Attempts to grab the I2C lock. Returns True on success.""" +//| ... +//| +STATIC mp_obj_t bitbangio_i2c_obj_try_lock(mp_obj_t self_in) { + bitbangio_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(shared_module_bitbangio_i2c_try_lock(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bitbangio_i2c_try_lock_obj, bitbangio_i2c_obj_try_lock); + +//| def unlock(self) -> None: +//| """Releases the I2C lock.""" +//| ... +//| +STATIC mp_obj_t bitbangio_i2c_obj_unlock(mp_obj_t self_in) { + bitbangio_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + shared_module_bitbangio_i2c_unlock(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bitbangio_i2c_unlock_obj, bitbangio_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``. +//| The number of bytes read will be the length of ``buffer``. +//| At least one byte must be read. +//| +//| If ``start`` or ``end`` is provided, then the buffer will be sliced +//| as if ``buffer[start:end]``. This will not cause an allocation like +//| ``buf[start:end]`` will so it saves memory. +//| +//| :param int address: 7-bit device address +//| :param WriteableBuffer buffer: buffer to write into +//| :param int start: Index to start writing at +//| :param int end: Index to write up to but not include""" +//| ... +//| +// Shared arg parsing for readfrom_into and writeto_then_readfrom. +STATIC void readfrom(bitbangio_i2c_obj_t *self, mp_int_t address, mp_obj_t buffer, int32_t start, mp_int_t end) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_WRITE); + + size_t length = bufinfo.len; + normalize_buffer_bounds(&start, end, &length); + if (length == 0) { + mp_raise_ValueError(translate("Buffer must be at least length 1")); + } + + uint8_t status = shared_module_bitbangio_i2c_read(self, address, ((uint8_t *)bufinfo.buf) + start, length); + if (status != 0) { + mp_raise_OSError(status); + } +} + +STATIC mp_obj_t bitbangio_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} }, + }; + bitbangio_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); + + readfrom(self, args[ARG_address].u_int, args[ARG_buffer].u_obj, args[ARG_start].u_int, + args[ARG_end].u_int); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(bitbangio_i2c_readfrom_into_obj, 1, bitbangio_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 transmits a +//| stop bit. Use `writeto_then_readfrom` when needing a write, no stop and repeated start +//| before a 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 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)`` +//| """ +//| ... +//| +// Shared arg parsing for writeto and writeto_then_readfrom. +STATIC void writeto(bitbangio_i2c_obj_t *self, mp_int_t address, mp_obj_t buffer, int32_t start, mp_int_t end, bool stop) { + // get the buffer to write the data from + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_READ); + + size_t length = bufinfo.len; + normalize_buffer_bounds(&start, end, &length); + + // do the transfer + uint8_t status = shared_module_bitbangio_i2c_write(self, address, + ((uint8_t *)bufinfo.buf) + start, length, + stop); + if (status != 0) { + mp_raise_OSError(status); + } +} + +STATIC mp_obj_t bitbangio_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} }, + }; + bitbangio_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); + + writeto(self, args[ARG_address].u_int, args[ARG_buffer].u_obj, args[ARG_start].u_int, + args[ARG_end].u_int, true); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bitbangio_i2c_writeto_obj, 1, bitbangio_i2c_writeto); + + +//| import sys +//| def writeto_then_readfrom(self, address: int, out_buffer: ReadableBuffer, in_buffer: ReadableBuffer, *, 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 bitbangio_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} }, + }; + bitbangio_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); + + writeto(self, args[ARG_address].u_int, args[ARG_out_buffer].u_obj, args[ARG_out_start].u_int, + args[ARG_out_end].u_int, false); + readfrom(self, args[ARG_address].u_int, args[ARG_in_buffer].u_obj, args[ARG_in_start].u_int, + args[ARG_in_end].u_int); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(bitbangio_i2c_writeto_then_readfrom_obj, 1, bitbangio_i2c_writeto_then_readfrom); + +STATIC const mp_rom_map_elem_t bitbangio_i2c_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bitbangio_i2c_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&bitbangio_i2c_obj___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&bitbangio_i2c_scan_obj) }, + + { MP_ROM_QSTR(MP_QSTR_try_lock), MP_ROM_PTR(&bitbangio_i2c_try_lock_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&bitbangio_i2c_unlock_obj) }, + + { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&bitbangio_i2c_writeto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&bitbangio_i2c_readfrom_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeto_then_readfrom), MP_ROM_PTR(&bitbangio_i2c_writeto_then_readfrom_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bitbangio_i2c_locals_dict, bitbangio_i2c_locals_dict_table); + +const mp_obj_type_t bitbangio_i2c_type = { + { &mp_type_type }, + .name = MP_QSTR_I2C, + .make_new = bitbangio_i2c_make_new, + .locals_dict = (mp_obj_dict_t *)&bitbangio_i2c_locals_dict, +}; diff --git a/circuitpython/shared-bindings/bitbangio/I2C.h b/circuitpython/shared-bindings/bitbangio/I2C.h new file mode 100644 index 0000000..d950180 --- /dev/null +++ b/circuitpython/shared-bindings/bitbangio/I2C.h @@ -0,0 +1,65 @@ +/* + * 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_BITBANGIO_I2C_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BITBANGIO_I2C_H + +#include "py/obj.h" + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/bitbangio/I2C.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_type_t bitbangio_i2c_type; + +// Initializes the hardware peripheral. +extern void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, + const mcu_pin_obj_t *scl, + const mcu_pin_obj_t *sda, + uint32_t frequency, + uint32_t us_timeout); + +extern void shared_module_bitbangio_i2c_deinit(bitbangio_i2c_obj_t *self); +extern bool shared_module_bitbangio_i2c_deinited(bitbangio_i2c_obj_t *self); + +extern bool shared_module_bitbangio_i2c_try_lock(bitbangio_i2c_obj_t *self); +extern bool shared_module_bitbangio_i2c_has_lock(bitbangio_i2c_obj_t *self); +extern void shared_module_bitbangio_i2c_unlock(bitbangio_i2c_obj_t *self); + +// Probe the bus to see if a device acknowledges the given address. +extern bool shared_module_bitbangio_i2c_probe(bitbangio_i2c_obj_t *self, uint8_t addr); + +extern uint8_t shared_module_bitbangio_i2c_write(bitbangio_i2c_obj_t *self, + uint16_t address, + const uint8_t *data, size_t len, + bool stop); + +// Reads memory of the i2c device picking up where it left off. +extern uint8_t shared_module_bitbangio_i2c_read(bitbangio_i2c_obj_t *self, + uint16_t address, + uint8_t *data, size_t len); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITBANGIO_I2C_H diff --git a/circuitpython/shared-bindings/bitbangio/SPI.c b/circuitpython/shared-bindings/bitbangio/SPI.c new file mode 100644 index 0000000..2c29c09 --- /dev/null +++ b/circuitpython/shared-bindings/bitbangio/SPI.c @@ -0,0 +1,383 @@ +/* + * 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 +// bitbangio.SPI class. + +#include <string.h> + +#include "shared-bindings/bitbangio/SPI.h" +#include "shared-bindings/microcontroller/Pin.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/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) -> None: +//| """Construct an SPI object on the given pins. +//| +//| .. seealso:: Using this class directly requires careful lock management. +//| Instead, use :class:`~adafruit_bus_device.spi_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.""" +//| ... +//| + +// TODO(tannewt): Support LSB SPI. +STATIC mp_obj_t bitbangio_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_clock, ARG_MOSI, ARG_MISO, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit }; + 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_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); + + bitbangio_spi_obj_t *self = m_new_obj(bitbangio_spi_obj_t); + self->base.type = &bitbangio_spi_type; + shared_module_bitbangio_spi_construct(self, clock, mosi, miso); + return (mp_obj_t)self; +} + +//| def deinit(self) -> None: +//| """Turn off the SPI bus.""" +//| ... +//| +STATIC mp_obj_t bitbangio_spi_obj_deinit(mp_obj_t self_in) { + bitbangio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + shared_module_bitbangio_spi_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bitbangio_spi_deinit_obj, bitbangio_spi_obj_deinit); + +STATIC void check_for_deinit(bitbangio_spi_obj_t *self) { + if (shared_module_bitbangio_spi_deinited(self)) { + raise_deinited_error(); + } +} + +//| 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 bitbangio_spi_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + shared_module_bitbangio_spi_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bitbangio_spi_obj___exit___obj, 4, 4, bitbangio_spi_obj___exit__); + + +static void check_lock(bitbangio_spi_obj_t *self) { + if (!shared_module_bitbangio_spi_has_lock(self)) { + mp_raise_RuntimeError(translate("Function requires lock")); + } +} + +//| def configure(self, *, baudrate: int = 100000, polarity: int = 0, phase: int = 0, bits: int = 8) -> None: +//| """Configures the SPI bus. Only valid when locked. +//| +//| :param int baudrate: the clock rate in Hertz +//| :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""" +//| ... +//| +STATIC mp_obj_t bitbangio_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} }, + }; + bitbangio_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")); + } + + shared_module_bitbangio_spi_configure(self, args[ARG_baudrate].u_int, polarity, phase, bits); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(bitbangio_spi_configure_obj, 1, bitbangio_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 bitbangio_spi_obj_try_lock(mp_obj_t self_in) { + bitbangio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(shared_module_bitbangio_spi_try_lock(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(bitbangio_spi_try_lock_obj, bitbangio_spi_obj_try_lock); + +//| def unlock(self) -> None: +//| """Releases the SPI lock.""" +//| ... +//| +STATIC mp_obj_t bitbangio_spi_obj_unlock(mp_obj_t self_in) { + bitbangio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + shared_module_bitbangio_spi_unlock(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(bitbangio_spi_unlock_obj, bitbangio_spi_obj_unlock); + +//| def write(self, buf: ReadableBuffer) -> None: +//| """Write the data contained in ``buf``. Requires the SPI being locked. +//| If the buffer is empty, nothing happens.""" +//| ... +//| +STATIC mp_obj_t bitbangio_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} }, + }; + bitbangio_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 = shared_module_bitbangio_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(bitbangio_spi_write_obj, 1, bitbangio_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, use ``len(buffer)`` +//| :param int write_value: value to write while reading +//| """ +//| ... +//| +STATIC mp_obj_t bitbangio_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} }, + }; + bitbangio_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 = shared_module_bitbangio_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(bitbangio_spi_readinto_obj, 1, bitbangio_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 bitbangio_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} }, + }; + bitbangio_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 = shared_module_bitbangio_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(bitbangio_spi_write_readinto_obj, 1, bitbangio_spi_write_readinto); + +STATIC const mp_rom_map_elem_t bitbangio_spi_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bitbangio_spi_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&bitbangio_spi_obj___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&bitbangio_spi_configure_obj) }, + { MP_ROM_QSTR(MP_QSTR_try_lock), MP_ROM_PTR(&bitbangio_spi_try_lock_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&bitbangio_spi_unlock_obj) }, + + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bitbangio_spi_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&bitbangio_spi_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&bitbangio_spi_write_readinto_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(bitbangio_spi_locals_dict, bitbangio_spi_locals_dict_table); + +const mp_obj_type_t bitbangio_spi_type = { + { &mp_type_type }, + .name = MP_QSTR_SPI, + .make_new = bitbangio_spi_make_new, + .locals_dict = (mp_obj_dict_t *)&bitbangio_spi_locals_dict, +}; diff --git a/circuitpython/shared-bindings/bitbangio/SPI.h b/circuitpython/shared-bindings/bitbangio/SPI.h new file mode 100644 index 0000000..2d0d75e --- /dev/null +++ b/circuitpython/shared-bindings/bitbangio/SPI.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BITBANGIO_SPI_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BITBANGIO_SPI_H + +#include "py/obj.h" + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/bitbangio/SPI.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_type_t bitbangio_spi_type; + +// Construct an underlying SPI object. +extern void shared_module_bitbangio_spi_construct(bitbangio_spi_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso); + +extern void shared_module_bitbangio_spi_deinit(bitbangio_spi_obj_t *self); +extern bool shared_module_bitbangio_spi_deinited(bitbangio_spi_obj_t *self); + +extern void shared_module_bitbangio_spi_configure(bitbangio_spi_obj_t *self, + uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits); + +extern bool shared_module_bitbangio_spi_try_lock(bitbangio_spi_obj_t *self); +extern bool shared_module_bitbangio_spi_has_lock(bitbangio_spi_obj_t *self); +extern void shared_module_bitbangio_spi_unlock(bitbangio_spi_obj_t *self); + +// Writes out the given data. +extern bool shared_module_bitbangio_spi_write(bitbangio_spi_obj_t *self, const uint8_t *data, size_t len); + +// Reads in len bytes while outputting zeroes. +extern bool shared_module_bitbangio_spi_read(bitbangio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value); + +// Transfer out len bytes while reading len bytes +extern bool shared_module_bitbangio_spi_transfer(bitbangio_spi_obj_t *self, const uint8_t *dout, uint8_t *din, size_t len); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITBANGIO_SPI_H diff --git a/circuitpython/shared-bindings/bitbangio/__init__.c b/circuitpython/shared-bindings/bitbangio/__init__.c new file mode 100644 index 0000000..57348a3 --- /dev/null +++ b/circuitpython/shared-bindings/bitbangio/__init__.c @@ -0,0 +1,88 @@ +/* + * 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. + */ + +// bitbangio implements some standard protocols in the processor. Its only +// dependency is digitalio. + +#include <stdint.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/bitbangio/__init__.h" +#include "shared-bindings/bitbangio/I2C.h" +#include "shared-bindings/onewireio/OneWire.h" +#include "shared-bindings/bitbangio/SPI.h" + +#include "py/runtime.h" + +//| """Digital protocols implemented by the CPU +//| +//| The `bitbangio` module contains classes to provide digital bus protocol +//| support regardless of whether the underlying hardware exists to use the +//| protocol. +//| +//| First try to use `busio` module instead which may utilize peripheral +//| hardware to implement the protocols. Native implementations will be faster +//| than bitbanged versions and have more capabilities. +//| +//| 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 bitbangio +//| from board import * +//| +//| i2c = bitbangio.I2C(SCL, SDA) +//| print(i2c.scan()) +//| i2c.deinit() +//| +//| This example will initialize the the device, run +//| :py:meth:`~bitbangio.I2C.scan` and then :py:meth:`~bitbangio.I2C.deinit` the +//| hardware. The last step is optional because CircuitPython automatically +//| resets hardware after a program finishes.""" +//| + +STATIC const mp_rom_map_elem_t bitbangio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bitbangio) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&bitbangio_i2c_type) }, + #if CIRCUITPY_ONEWIREIO + { MP_ROM_QSTR(MP_QSTR_OneWire), MP_ROM_PTR(&onewireio_onewire_type) }, + #endif + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&bitbangio_spi_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bitbangio_module_globals, bitbangio_module_globals_table); + +const mp_obj_module_t bitbangio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&bitbangio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_bitbangio, bitbangio_module, CIRCUITPY_BITBANGIO); diff --git a/circuitpython/shared-bindings/bitbangio/__init__.h b/circuitpython/shared-bindings/bitbangio/__init__.h new file mode 100644 index 0000000..0e9206a --- /dev/null +++ b/circuitpython/shared-bindings/bitbangio/__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_BITBANGIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BITBANGIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITBANGIO___INIT___H diff --git a/circuitpython/shared-bindings/bitmaptools/__init__.c b/circuitpython/shared-bindings/bitmaptools/__init__.c new file mode 100644 index 0000000..5122d03 --- /dev/null +++ b/circuitpython/shared-bindings/bitmaptools/__init__.c @@ -0,0 +1,766 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Kevin Matocha + * + * 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 "shared-bindings/displayio/Bitmap.h" +#include "shared-bindings/displayio/Palette.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/bitmaptools/__init__.h" + +#include <stdint.h> + +#include "py/binary.h" +#include "py/enum.h" +#include "py/obj.h" +#include "py/runtime.h" + +#if MICROPY_VFS +#include "extmod/vfs.h" +#endif +#if defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX +#include "extmod/vfs_posix.h" +#endif + +//| """Collection of bitmap manipulation tools +//| +//| .. note:: If you're looking for information about displaying bitmaps on +//| screens in CircuitPython, see `this Learn guide +//| <https://learn.adafruit.com/circuitpython-display-support-using-displayio>`_ +//| for information about using the :py:mod:`displayio` module. +//| """ +//| + +STATIC int16_t validate_point(mp_obj_t point, int16_t default_value) { + // Checks if point is None and returns default_value, otherwise decodes integer value + if (point == mp_const_none) { + return default_value; + } + return mp_obj_get_int(point); +} + +STATIC void extract_tuple(mp_obj_t xy_tuple, int16_t *x, int16_t *y, int16_t x_default, int16_t y_default) { + // Helper function for rotozoom + // Extract x,y values from a tuple or default if None + if (xy_tuple == mp_const_none) { + *x = x_default; + *y = y_default; + } else if (!mp_obj_is_obj(xy_tuple)) { + mp_raise_ValueError(translate("clip point must be (x,y) tuple")); + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(xy_tuple, 2, &items); + *x = mp_obj_get_int(items[0]); + *y = mp_obj_get_int(items[1]); + } +} + +STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tuple, int16_t *clip0_x, int16_t *clip0_y, + mp_obj_t clip1_tuple, int16_t *clip1_x, int16_t *clip1_y) { + // Helper function for rotozoom + // 1. Extract the clip x,y points from the two clip tuples + // 2. Rearrange values such that clip0_ < clip1_ + // 3. Constrain the clip points to within the bitmap + + extract_tuple(clip0_tuple, clip0_x, clip0_y, 0, 0); + extract_tuple(clip1_tuple, clip1_x, clip1_y, bitmap->width, bitmap->height); + + // Ensure the value for clip0 is less than clip1 (for both x and y) + if (*clip0_x > *clip1_x) { + int16_t temp_value = *clip0_x; // swap values + *clip0_x = *clip1_x; + *clip1_x = temp_value; + } + if (*clip0_y > *clip1_y) { + int16_t temp_value = *clip0_y; // swap values + *clip0_y = *clip1_y; + *clip1_y = temp_value; + } + + // Constrain the clip window to within the bitmap boundaries + if (*clip0_x < 0) { + *clip0_x = 0; + } + if (*clip0_y < 0) { + *clip0_y = 0; + } + if (*clip0_x > bitmap->width) { + *clip0_x = bitmap->width; + } + if (*clip0_y > bitmap->height) { + *clip0_y = bitmap->height; + } + if (*clip1_x < 0) { + *clip1_x = 0; + } + if (*clip1_y < 0) { + *clip1_y = 0; + } + if (*clip1_x > bitmap->width) { + *clip1_x = bitmap->width; + } + if (*clip1_y > bitmap->height) { + *clip1_y = bitmap->height; + } + +} + +//| +//| def rotozoom( +//| dest_bitmap: displayio.Bitmap, source_bitmap: displayio.Bitmap, +//| *, +//| ox: int, oy: int, dest_clip0: Tuple[int, int], dest_clip1: Tuple[int, int], +//| px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], +//| angle: float, scale: float, skip_index: int) -> None: +//| """Inserts the source bitmap region into the destination bitmap with rotation +//| (angle), scale and clipping (both on source and destination bitmaps). +//| +//| :param bitmap dest_bitmap: Destination bitmap that will be copied into +//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied +//| :param int ox: Horizontal pixel location in destination bitmap where source bitmap +//| point (px,py) is placed +//| :param int oy: Vertical pixel location in destination bitmap where source bitmap +//| point (px,py) is placed +//| :param Tuple[int,int] dest_clip0: First corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :param Tuple[int,int] dest_clip1: Second corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :param int px: Horizontal pixel location in source bitmap that is placed into the +//| destination bitmap at (ox,oy) +//| :param int py: Vertical pixel location in source bitmap that is placed into the +//| destination bitmap at (ox,oy) +//| :param Tuple[int,int] source_clip0: First corner of rectangular source clipping +//| region that constrains region of reading from the source bitmap +//| :param Tuple[int,int] source_clip1: Second corner of rectangular source clipping +//| region that constrains region of reading from the source bitmap +//| :param float angle: Angle of rotation, in radians (positive is clockwise direction) +//| :param float scale: Scaling factor +//| :param int skip_index: Bitmap palette index in the source that will not be copied, +//| set to None to copy all pixels""" +//| ... +//| +STATIC mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_dest_bitmap, ARG_source_bitmap, + ARG_ox, ARG_oy, ARG_dest_clip0, ARG_dest_clip1, + ARG_px, ARG_py, ARG_source_clip0, ARG_source_clip1, + ARG_angle, ARG_scale, ARG_skip_index}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + + {MP_QSTR_ox, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->width / 2 + {MP_QSTR_oy, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->height / 2 + {MP_QSTR_dest_clip0, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + {MP_QSTR_dest_clip1, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + + {MP_QSTR_px, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->width / 2 + {MP_QSTR_py, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->height / 2 + {MP_QSTR_source_clip0, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + {MP_QSTR_source_clip1, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + + {MP_QSTR_angle, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to 0.0 + {MP_QSTR_scale, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to 1.0 + {MP_QSTR_skip_index, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + + displayio_bitmap_t *source = MP_OBJ_TO_PTR(args[ARG_source_bitmap].u_obj); // the source bitmap + + // ensure that the destination bitmap has at least as many `bits_per_value` as the source + if (destination->bits_per_value < source->bits_per_value) { + mp_raise_ValueError(translate("source palette too large")); + } + + // Confirm the destination location target (ox,oy); if None, default to bitmap midpoint + int16_t ox, oy; + ox = validate_point(args[ARG_ox].u_obj, destination->width / 2); + oy = validate_point(args[ARG_oy].u_obj, destination->height / 2); + + // Confirm the source location target (px,py); if None, default to bitmap midpoint + int16_t px, py; + px = validate_point(args[ARG_px].u_obj, source->width / 2); + py = validate_point(args[ARG_py].u_obj, source->height / 2); + + // Validate the clipping regions for the destination bitmap + int16_t dest_clip0_x, dest_clip0_y, dest_clip1_x, dest_clip1_y; + + validate_clip_region(destination, args[ARG_dest_clip0].u_obj, &dest_clip0_x, &dest_clip0_y, + args[ARG_dest_clip1].u_obj, &dest_clip1_x, &dest_clip1_y); + + // Validate the clipping regions for the source bitmap + int16_t source_clip0_x, source_clip0_y, source_clip1_x, source_clip1_y; + + validate_clip_region(source, args[ARG_source_clip0].u_obj, &source_clip0_x, &source_clip0_y, + args[ARG_source_clip1].u_obj, &source_clip1_x, &source_clip1_y); + + // Confirm the angle value + mp_float_t angle = 0.0; + if (args[ARG_angle].u_obj != mp_const_none) { + angle = mp_obj_get_float(args[ARG_angle].u_obj); + } + + // Confirm the scale value + mp_float_t scale = 1.0; + if (args[ARG_scale].u_obj != mp_const_none) { + scale = mp_obj_get_float(args[ARG_scale].u_obj); + } + if (scale < 0) { // ensure scale >= 0 + scale = 1.0; + } + + uint32_t skip_index; + bool skip_index_none; // Flag whether input skip_value was None + if (args[ARG_skip_index].u_obj == mp_const_none) { + skip_index = 0; + skip_index_none = true; + } else { + skip_index = mp_obj_get_int(args[ARG_skip_index].u_obj); + skip_index_none = false; + } + + common_hal_bitmaptools_rotozoom(destination, ox, oy, + dest_clip0_x, dest_clip0_y, + dest_clip1_x, dest_clip1_y, + source, px, py, + source_clip0_x, source_clip0_y, + source_clip1_x, source_clip1_y, + angle, + scale, + skip_index, skip_index_none); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_rotozoom_obj, 0, bitmaptools_obj_rotozoom); +// requires at least 2 arguments (destination bitmap and source bitmap) + +//| +//| def alphablend(dest_bitmap: displayio.Bitmap , source_bitmap_1: displayio.Bitmap, source_bitmap_2: displayio.Bitmap, colorspace: displayio.Colorspace, factor1: float=.5, factor2: float=None) -> None: +//| """Alpha blend the two source bitmaps into the destination. +//| +//| It is permitted for the destination bitmap to be one of the two +//| source bitmaps. +//| +//| :param bitmap dest_bitmap: Destination bitmap that will be written into +//| :param bitmap source_bitmap_1: The first source bitmap +//| :param bitmap source_bitmap_2: The second source bitmap +//| :param float factor1: The proportion of bitmap 1 to mix in +//| :param float factor2: The proportion of bitmap 2 to mix in. If specified as `None`, ``1-factor1`` is used. Usually the proportions should sum to 1. +//| :param displayio.Colorspace colorspace: The colorspace of the bitmaps. They must all have the same colorspace. Only the following colorspaces are permitted: ``L8``, ``RGB565``, ``RGB565_SWAPPED``, ``BGR565`` and ``BGR565_SWAPPED``. +//| +//| For the L8 colorspace, the bitmaps must have a bits-per-value of 8. +//| For the RGB colorspaces, they must have a bits-per-value of 16.""" +//| + +STATIC mp_obj_t bitmaptools_alphablend(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_dest_bitmap, ARG_source_bitmap_1, ARG_source_bitmap_2, ARG_colorspace, ARG_factor_1, ARG_factor_2}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = NULL}}, + {MP_QSTR_source_bitmap_1, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = NULL}}, + {MP_QSTR_source_bitmap_2, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = NULL}}, + {MP_QSTR_colorspace, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = NULL}}, + {MP_QSTR_factor_1, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}}, + {MP_QSTR_factor_2, MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap + displayio_bitmap_t *source1 = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_source_bitmap_1].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap_1)); // the first source bitmap + displayio_bitmap_t *source2 = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_source_bitmap_2].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap_2)); // the second source bitmap + + mp_float_t factor1 = (args[ARG_factor_1].u_obj == mp_const_none) ? MICROPY_FLOAT_CONST(.5) : mp_obj_get_float(args[ARG_factor_1].u_obj); + mp_float_t factor2 = (args[ARG_factor_2].u_obj == mp_const_none) ? 1 - factor1 : mp_obj_get_float(args[ARG_factor_2].u_obj); + + displayio_colorspace_t colorspace = (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_colorspace].u_obj); + + if (destination->width != source1->width + || destination->height != source1->height + || destination->bits_per_value != source1->bits_per_value + || destination->width != source2->width + || destination->height != source2->height + || destination->bits_per_value != source2->bits_per_value + ) { + mp_raise_ValueError(translate("Bitmap size and bits per value must match")); + } + + switch (colorspace) { + case DISPLAYIO_COLORSPACE_L8: + if (destination->bits_per_value != 8) { + mp_raise_ValueError(translate("For L8 colorspace, input bitmap must have 8 bits per pixel")); + } + break; + + case DISPLAYIO_COLORSPACE_RGB565: + case DISPLAYIO_COLORSPACE_RGB565_SWAPPED: + case DISPLAYIO_COLORSPACE_BGR565: + case DISPLAYIO_COLORSPACE_BGR565_SWAPPED: + if (destination->bits_per_value != 16) { + mp_raise_ValueError(translate("For RGB colorspaces, input bitmap must have 16 bits per pixel")); + } + break; + + default: + mp_raise_ValueError(translate("Unsupported colorspace")); + } + + common_hal_bitmaptools_alphablend(destination, source1, source2, colorspace, factor1, factor2); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_alphablend_obj, 0, bitmaptools_alphablend); + +//| +//| def fill_region( +//| dest_bitmap: displayio.Bitmap, +//| x1: int, y1: int, +//| x2: int, y2: int, +//| value: int) -> None: +//| """Draws the color value into the destination bitmap within the +//| rectangular region bounded by (x1,y1) and (x2,y2), exclusive. +//| +//| :param bitmap dest_bitmap: Destination bitmap that will be written into +//| :param int x1: x-pixel position of the first corner of the rectangular fill region +//| :param int y1: y-pixel position of the first corner of the rectangular fill region +//| :param int x2: x-pixel position of the second corner of the rectangular fill region (exclusive) +//| :param int y2: y-pixel position of the second corner of the rectangular fill region (exclusive) +//| :param int value: Bitmap palette index that will be written into the rectangular +//| fill region in the destination bitmap""" +//| ... +//| +STATIC mp_obj_t bitmaptools_obj_fill_region(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_dest_bitmap, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_value}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + + uint32_t value, color_depth; + value = args[ARG_value].u_int; + color_depth = (1 << destination->bits_per_value); + if (color_depth <= value) { + mp_raise_ValueError(translate("out of range of target")); + } + + int16_t x1 = args[ARG_x1].u_int; + int16_t y1 = args[ARG_y1].u_int; + int16_t x2 = args[ARG_x2].u_int; + int16_t y2 = args[ARG_y2].u_int; + + common_hal_bitmaptools_fill_region(destination, x1, y1, x2, y2, value); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_fill_region_obj, 0, bitmaptools_obj_fill_region); +//| +//| def boundary_fill( +//| dest_bitmap: displayio.Bitmap, +//| x: int, y: int, +//| fill_color_value: int, replaced_color_value: int) -> None: +//| """Draws the color value into the destination bitmap enclosed +//| area of pixels of the background_value color. Like "Paint Bucket" +//| fill tool. +//| +//| :param bitmap dest_bitmap: Destination bitmap that will be written into +//| :param int x: x-pixel position of the first pixel to check and fill if needed +//| :param int y: y-pixel position of the first pixel to check and fill if needed +//| :param int fill_color_value: Bitmap palette index that will be written into the +//| enclosed area in the destination bitmap +//| :param int replaced_color_value: Bitmap palette index that will filled with the +//| value color in the enclosed area in the destination bitmap""" +//| ... +//| +STATIC mp_obj_t bitmaptools_obj_boundary_fill(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_dest_bitmap, ARG_x, ARG_y, ARG_fill_color_value, ARG_replaced_color_value}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_fill_color_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_replaced_color_value, MP_ARG_INT, {.u_int = INT_MAX} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap + + uint32_t fill_color_value, color_depth; + fill_color_value = args[ARG_fill_color_value].u_int; + color_depth = (1 << destination->bits_per_value); + if (color_depth <= fill_color_value) { + mp_raise_ValueError(translate("value out of range of target")); + } + + uint32_t replaced_color_value; + replaced_color_value = args[ARG_replaced_color_value].u_int; + if (replaced_color_value != INT_MAX && color_depth <= replaced_color_value) { + mp_raise_ValueError(translate("background value out of range of target")); + } + + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + + if (x < 0 || x >= destination->width) { + mp_raise_ValueError(translate("out of range of target")); + } + if (y < 0 || y >= destination->height) { + mp_raise_ValueError(translate("out of range of target")); + } + + common_hal_bitmaptools_boundary_fill(destination, x, y, fill_color_value, replaced_color_value); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_boundary_fill_obj, 0, bitmaptools_obj_boundary_fill); +// requires all 6 arguments + +//| +//| def draw_line( +//| dest_bitmap: displayio.Bitmap, +//| x1: int, y1: int, +//| x2: int, y2: int, +//| value: int) -> None: +//| """Draws a line into a bitmap specified two endpoints (x1,y1) and (x2,y2). +//| +//| :param bitmap dest_bitmap: Destination bitmap that will be written into +//| :param int x1: x-pixel position of the line's first endpoint +//| :param int y1: y-pixel position of the line's first endpoint +//| :param int x2: x-pixel position of the line's second endpoint +//| :param int y2: y-pixel position of the line's second endpoint +//| :param int value: Bitmap palette index that will be written into the +//| line in the destination bitmap""" +//| ... +//| +STATIC mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_dest_bitmap, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_value}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + + uint32_t value, color_depth; + value = args[ARG_value].u_int; + color_depth = (1 << destination->bits_per_value); + if (color_depth <= value) { + mp_raise_ValueError(translate("out of range of target")); + } + + int16_t x1 = args[ARG_x1].u_int; + int16_t y1 = args[ARG_y1].u_int; + int16_t x2 = args[ARG_x2].u_int; + int16_t y2 = args[ARG_y2].u_int; + + // verify points are within the bitmap boundary (inclusive) + if ((x1 < 0) || (x2 < 0) || (y1 < 0) || (y2 < 0) || + (x1 >= destination->width) || (x2 >= destination->width) || + (y1 >= destination->height) || (y2 >= destination->height)) { + mp_raise_ValueError(translate("out of range of target")); + } + + common_hal_bitmaptools_draw_line(destination, x1, y1, x2, y2, value); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_line_obj, 0, bitmaptools_obj_draw_line); +// requires all 6 arguments + +//| def arrayblit(bitmap: displayio.Bitmap, data: ReadableBuffer, x1: int=0, y1: int=0, x2: Optional[int]=None, y2: Optional[int]=None, skip_index:Optional[int]=None) -> None: +//| """Inserts pixels from ``data`` into the rectangle of width×height pixels with the upper left corner at ``(x,y)`` +//| +//| The values from ``data`` are taken modulo the number of color values +//| avalable in the destination bitmap. +//| +//| If x1 or y1 are not specified, they are taken as 0. If x2 or y2 +//| are not specified, or are given as -1, they are taken as the width +//| and height of the image. +//| +//| The coordinates affected by the blit are ``x1 <= x < x2`` and ``y1 <= y < y2``. +//| +//| ``data`` must contain at least as many elements as required. If it +//| contains excess elements, they are ignored. +//| +//| The blit takes place by rows, so the first elements of ``data`` go +//| to the first row, the next elements to the next row, and so on. +//| +//| :param displayio.Bitmap bitmap: A writable bitmap +//| :param ReadableBuffer data: Buffer containing the source pixel values +//| :param int x1: The left corner of the area to blit into (inclusive) +//| :param int y1: The top corner of the area to blit into (inclusive) +//| :param int x2: The right of the area to blit into (exclusive) +//| :param int y2: The bottom corner of the area to blit into (exclusive) +//| :param int skip_index: Bitmap palette index in the source that will not be copied, +//| set to None to copy all pixels +//| """ +//| ... +//| +STATIC mp_obj_t bitmaptools_arrayblit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_bitmap, ARG_data, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_x1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_x2, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_y2, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_skip_index, MP_ARG_OBJ, {.u_obj = mp_const_none } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *bitmap = mp_arg_validate_type(args[ARG_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_bitmap); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + + int x1 = args[ARG_x1].u_int; + int y1 = args[ARG_y1].u_int; + int x2 = args[ARG_x2].u_int == -1 ? bitmap->width : args[ARG_x2].u_int; + int y2 = args[ARG_y2].u_int == -1 ? bitmap->height : args[ARG_y2].u_int; + + if ((x1 < 0) || (y1 < 0) || (x1 > x2) || (y1 > y2) || (x2 > bitmap->width) || (y2 > bitmap->height)) { + mp_raise_IndexError(translate("pixel coordinates out of bounds")); + } + + size_t output_element_count = (x2 - x1) * (y2 - y1); + size_t element_size = mp_binary_get_size('@', bufinfo.typecode, NULL); + size_t input_element_count = bufinfo.len / element_size; + + bool skip_specified = args[ARG_skip_index].u_obj != mp_const_none; + uint32_t skip_index = skip_specified ? mp_obj_get_int(args[ARG_skip_index].u_obj) : 0; + if (input_element_count < output_element_count) { + mp_raise_IndexError(translate("index out of range")); + } + + common_hal_bitmaptools_arrayblit(bitmap, bufinfo.buf, element_size, x1, y1, x2, y2, skip_specified, skip_index); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_arrayblit_obj, 0, bitmaptools_arrayblit); + + +//| def readinto(bitmap: displayio.Bitmap, file: typing.BinaryIO, bits_per_pixel: int, element_size: int = 1, reverse_pixels_in_element: bool = False, swap_bytes_in_element: bool = False, reverse_rows: bool = False) -> None: +//| """Reads from a binary file into a bitmap. +//| +//| The file must be positioned so that it consists of ``bitmap.height`` rows of pixel data, where each row is the smallest multiple of ``element_size`` bytes that can hold ``bitmap.width`` pixels. +//| +//| The bytes in an element can be optionally swapped, and the pixels in an element can be reversed. Also, the +//| row loading direction can be reversed, which may be requires for loading certain bitmap files. +//| +//| This function doesn't parse image headers, but is useful to speed up loading of uncompressed image formats such as PCF glyph data. +//| +//| :param displayio.Bitmap bitmap: A writable bitmap +//| :param typing.BinaryIO file: A file opened in binary mode +//| :param int bits_per_pixel: Number of bits per pixel. Values 1, 2, 4, 8, 16, 24, and 32 are supported; +//| :param int element_size: Number of bytes per element. Values of 1, 2, and 4 are supported, except that 24 ``bits_per_pixel`` requires 1 byte per element. +//| :param bool reverse_pixels_in_element: If set, the first pixel in a word is taken from the Most Signficant Bits; otherwise, it is taken from the Least Significant Bits. +//| :param bool swap_bytes_in_element: If the ``element_size`` is not 1, then reverse the byte order of each element read. +//| :param bool reverse_rows: Reverse the direction of the row loading (required for some bitmap images). +//| """ +//| ... +//| + +STATIC mp_obj_t bitmaptools_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_bitmap, ARG_file, ARG_bits_per_pixel, ARG_element_size, ARG_reverse_pixels_in_element, ARG_swap_bytes_in_element, ARG_reverse_rows }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_file, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_bits_per_pixel, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_element_size, MP_ARG_INT, { .u_int = 1 } }, + { MP_QSTR_reverse_pixels_in_element, MP_ARG_BOOL, { .u_bool = false } }, + { MP_QSTR_swap_bytes_in_element, MP_ARG_BOOL, { .u_bool = false } }, + { MP_QSTR_reverse_rows, MP_ARG_BOOL, { .u_bool = false } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *bitmap = mp_arg_validate_type(args[ARG_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_bitmap); + + mp_obj_t *file = args[ARG_file].u_obj; + + int element_size = args[ARG_element_size].u_int; + if (element_size != 1 && element_size != 2 && element_size != 4) { + mp_raise_ValueError_varg(translate("invalid element_size %d, must be, 1, 2, or 4"), element_size); + } + + int bits_per_pixel = args[ARG_bits_per_pixel].u_int; + switch (bits_per_pixel) { + case 24: + if (element_size != 1) { + mp_raise_ValueError_varg(translate("invalid element size %d for bits_per_pixel %d\n"), element_size, bits_per_pixel); + } + break; + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + break; + default: + mp_raise_ValueError_varg(translate("invalid bits_per_pixel %d, must be, 1, 2, 4, 8, 16, 24, or 32"), bits_per_pixel); + } + + bool reverse_pixels_in_element = args[ARG_reverse_pixels_in_element].u_bool; + bool swap_bytes_in_element = args[ARG_swap_bytes_in_element].u_bool; + bool reverse_rows = args[ARG_reverse_rows].u_bool; + + common_hal_bitmaptools_readinto(bitmap, file, element_size, bits_per_pixel, reverse_pixels_in_element, swap_bytes_in_element, reverse_rows); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_readinto_obj, 0, bitmaptools_readinto); + +//| class DitherAlgorithm: +//| """Identifies the algorith for dither to use""" +//| +//| Atkinson: "DitherAlgorithm" +//| """The classic Atkinson dither, often associated with the Hypercard esthetic""" +//| +//| FloydStenberg: "DitherAlgorithm" +//| """The Floyd-Stenberg dither""" +//| +MAKE_ENUM_VALUE(bitmaptools_dither_algorithm_type, dither_algorithm, Atkinson, DITHER_ALGORITHM_ATKINSON); +MAKE_ENUM_VALUE(bitmaptools_dither_algorithm_type, dither_algorithm, FloydStenberg, DITHER_ALGORITHM_FLOYD_STENBERG); + +MAKE_ENUM_MAP(bitmaptools_dither_algorithm) { + MAKE_ENUM_MAP_ENTRY(dither_algorithm, Atkinson), + MAKE_ENUM_MAP_ENTRY(dither_algorithm, FloydStenberg), +}; +STATIC MP_DEFINE_CONST_DICT(bitmaptools_dither_algorithm_locals_dict, bitmaptools_dither_algorithm_locals_table); + +MAKE_PRINTER(bitmaptools, bitmaptools_dither_algorithm); + +MAKE_ENUM_TYPE(bitmaptools, DitherAlgorithm, bitmaptools_dither_algorithm); + +//| def dither(dest_bitmap: displayio.Bitmap, source_bitmapp: displayio.Bitmap, source_colorspace: displayio.Colorspace, algorithm: DitherAlgorithm=DitherAlgorithm.Atkinson) -> None: +//| """Convert the input image into a 2-level output image using the given dither algorithm. +//| +//| :param bitmap dest_bitmap: Destination bitmap. It must have a value_count of 2 or 65536. The stored values are 0 and the maximum pixel value. +//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be dithered. It must have a value_count of 65536. +//| :param colorspace: The colorspace of the image. The supported colorspaces are ``RGB565``, ``BGR565``, ``RGB565_SWAPPED``, and ``BGR565_SWAPPED`` +//| :param algorithm: The dither algorithm to use, one of the `DitherAlgorithm` values. +//| """ +//| ... +//| +STATIC mp_obj_t bitmaptools_dither(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_dest_bitmap, ARG_source_bitmap, ARG_source_colorspace, ARG_algorithm }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_source_colorspace, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_algorithm, MP_ARG_OBJ, { .u_obj = MP_ROM_PTR((void *)&dither_algorithm_Atkinson_obj) } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + displayio_bitmap_t *source_bitmap = mp_arg_validate_type(args[ARG_source_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap); + displayio_bitmap_t *dest_bitmap = mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap); + bitmaptools_dither_algorithm_t algorithm = cp_enum_value(&bitmaptools_dither_algorithm_type, args[ARG_algorithm].u_obj); + displayio_colorspace_t colorspace = cp_enum_value(&displayio_colorspace_type, args[ARG_source_colorspace].u_obj); + + if (source_bitmap->width != dest_bitmap->width || source_bitmap->height != dest_bitmap->height) { + mp_raise_TypeError(translate("bitmap sizes must match")); + } + + if (dest_bitmap->bits_per_value != 16 && dest_bitmap->bits_per_value != 1) { + mp_raise_TypeError(translate("source_bitmap must have value_count of 2 or 65536")); + } + + + switch (colorspace) { + case DISPLAYIO_COLORSPACE_RGB565: + case DISPLAYIO_COLORSPACE_RGB565_SWAPPED: + case DISPLAYIO_COLORSPACE_BGR565: + case DISPLAYIO_COLORSPACE_BGR565_SWAPPED: + if (source_bitmap->bits_per_value != 16) { + mp_raise_TypeError(translate("source_bitmap must have value_count of 65536")); + } + break; + + case DISPLAYIO_COLORSPACE_L8: + if (source_bitmap->bits_per_value != 8) { + mp_raise_TypeError(translate("source_bitmap must have value_count of 8")); + } + break; + + default: + mp_raise_TypeError(translate("unsupported colorspace for dither")); + } + + + common_hal_bitmaptools_dither(dest_bitmap, source_bitmap, colorspace, algorithm); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_dither_obj, 0, bitmaptools_dither); + + +STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bitmaptools) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bitmaptools_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotozoom), MP_ROM_PTR(&bitmaptools_rotozoom_obj) }, + { MP_ROM_QSTR(MP_QSTR_arrayblit), MP_ROM_PTR(&bitmaptools_arrayblit_obj) }, + { MP_ROM_QSTR(MP_QSTR_alphablend), MP_ROM_PTR(&bitmaptools_alphablend_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) }, + { MP_ROM_QSTR(MP_QSTR_boundary_fill), MP_ROM_PTR(&bitmaptools_boundary_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) }, + { MP_ROM_QSTR(MP_QSTR_dither), MP_ROM_PTR(&bitmaptools_dither_obj) }, + { MP_ROM_QSTR(MP_QSTR_DitherAlgorithm), MP_ROM_PTR(&bitmaptools_dither_algorithm_type) }, +}; +STATIC MP_DEFINE_CONST_DICT(bitmaptools_module_globals, bitmaptools_module_globals_table); + +const mp_obj_module_t bitmaptools_module = { + .base = {&mp_type_module }, + .globals = (mp_obj_dict_t *)&bitmaptools_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_bitmaptools, bitmaptools_module, CIRCUITPY_BITMAPTOOLS); diff --git a/circuitpython/shared-bindings/bitmaptools/__init__.h b/circuitpython/shared-bindings/bitmaptools/__init__.h new file mode 100644 index 0000000..fb5c789 --- /dev/null +++ b/circuitpython/shared-bindings/bitmaptools/__init__.h @@ -0,0 +1,73 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Kevin Matocha + * + * 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_BITMAPTOOLS__INIT__H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H + +#include "shared-module/displayio/Bitmap.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/__init__.h" +#include "shared-module/displayio/Palette.h" +#include "py/obj.h" +#include "extmod/vfs_fat.h" + +typedef enum { + DITHER_ALGORITHM_ATKINSON, DITHER_ALGORITHM_FLOYD_STENBERG, +} bitmaptools_dither_algorithm_t; + +extern const mp_obj_type_t bitmaptools_dither_algorithm_type; + +void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16_t oy, + int16_t dest_clip0_x, int16_t dest_clip0_y, + int16_t dest_clip1_x, int16_t dest_clip1_y, + displayio_bitmap_t *source, int16_t px, int16_t py, + int16_t source_clip0_x, int16_t source_clip0_y, + int16_t source_clip1_x, int16_t source_clip1_y, + mp_float_t angle, + mp_float_t scale, + uint32_t skip_index, bool skip_index_none); + +void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination, + int16_t x1, int16_t y1, + int16_t x2, int16_t y2, + uint32_t value); + +void common_hal_bitmaptools_boundary_fill(displayio_bitmap_t *destination, + int16_t x, int16_t y, + uint32_t fill_color_value, uint32_t replaced_color_value); + +void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination, + int16_t x0, int16_t y0, + int16_t x1, int16_t y1, + uint32_t value); + +void common_hal_bitmaptools_readinto(displayio_bitmap_t *self, mp_obj_t *file, int element_size, int bits_per_pixel, bool reverse_pixels_in_word, bool swap_bytes, bool reverse_rows); +void common_hal_bitmaptools_arrayblit(displayio_bitmap_t *self, void *data, int element_size, int x1, int y1, int x2, int y2, bool skip_specified, uint32_t skip_index); +void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bitmap_t *source_bitmap, displayio_colorspace_t colorspace, bitmaptools_dither_algorithm_t algorithm); + +void common_hal_bitmaptools_alphablend(displayio_bitmap_t *destination, displayio_bitmap_t *source1, displayio_bitmap_t *source2, displayio_colorspace_t colorspace, mp_float_t factor1, mp_float_t factor2); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H diff --git a/circuitpython/shared-bindings/bitops/__init__.c b/circuitpython/shared-bindings/bitops/__init__.c new file mode 100644 index 0000000..5b455dc --- /dev/null +++ b/circuitpython/shared-bindings/bitops/__init__.c @@ -0,0 +1,103 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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 "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/bitops/__init__.h" + +//| """Routines for low-level manipulation of binary data""" +//| +//| + +//| def bit_transpose(input: ReadableBuffer, output: WriteableBuffer, width:int = 8) -> WriteableBuffer: +//| """"Transpose" a buffer by assembling each output byte with bits taken from each of ``width`` different input bytes. +//| +//| This can be useful to convert a sequence of pixel values into a single +//| stream of bytes suitable for sending via a parallel conversion method. +//| +//| The number of bytes in the input buffer must be a multiple of the width, +//| and the width can be any value from 2 to 8. If the width is fewer than 8, +//| then the remaining (less significant) bits of the output are set to zero. +//| +//| Let ``stride = len(input)//width``. Then the first byte is made out of the +//| most significant bits of ``[input[0], input[stride], input[2*stride], ...]``. +//| The second byte is made out of the second bits, and so on until the 8th output +//| byte which is made of the first bits of ``input[1], input[1+stride, +//| input[2*stride], ...]``. +//| +//| The required output buffer size is ``len(input) * 8 // width``. +//| +//| Returns the output buffer.""" +//| ... + +STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_input, ARG_output, ARG_width }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_input, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_output, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_width, MP_ARG_INT, { .u_int = 8 } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int width = args[ARG_width].u_int; + if (width < 2 || width > 8) { + mp_raise_ValueError_varg(translate("width must be from 2 to 8 (inclusive), not %d"), width); + } + + mp_buffer_info_t input_bufinfo; + mp_get_buffer_raise(args[ARG_input].u_obj, &input_bufinfo, MP_BUFFER_READ); + int inlen = input_bufinfo.len; + if (inlen % width != 0) { + mp_raise_ValueError_varg(translate("Input buffer length (%d) must be a multiple of the strand count (%d)"), inlen, width); + } + + mp_buffer_info_t output_bufinfo; + mp_get_buffer_raise(args[ARG_output].u_obj, &output_bufinfo, MP_BUFFER_WRITE); + int avail = output_bufinfo.len; + int outlen = 8 * (inlen / width); + if (avail < outlen) { + mp_raise_ValueError_varg(translate("Output buffer must be at least %d bytes"), outlen); + } + common_hal_bitops_bit_transpose(output_bufinfo.buf, input_bufinfo.buf, inlen, width); + return args[ARG_output].u_obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bitops_bit_transpose_obj, 0, bit_transpose); + +STATIC const mp_rom_map_elem_t bitops_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bitops) }, + { MP_ROM_QSTR(MP_QSTR_bit_transpose), MP_ROM_PTR(&bitops_bit_transpose_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bitops_module_globals, bitops_module_globals_table); + +const mp_obj_module_t bitops_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&bitops_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_bitops, bitops_module, CIRCUITPY_BITOPS); diff --git a/circuitpython/shared-bindings/bitops/__init__.h b/circuitpython/shared-bindings/bitops/__init__.h new file mode 100644 index 0000000..4d61d52 --- /dev/null +++ b/circuitpython/shared-bindings/bitops/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * 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. + */ + +#pragma once + +#include <stdint.h> +#include <stdlib.h> + +void common_hal_bitops_bit_transpose(uint8_t *result, const uint8_t *src, size_t inlen, size_t num_strands); diff --git a/circuitpython/shared-bindings/board/__init__.c b/circuitpython/shared-bindings/board/__init__.c new file mode 100644 index 0000000..e4003d4 --- /dev/null +++ b/circuitpython/shared-bindings/board/__init__.c @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/board/__init__.h" +#if CIRCUITPY_BOARD_I2C +#include "shared-bindings/busio/I2C.h" +#endif +#if CIRCUITPY_BOARD_SPI +#include "shared-bindings/busio/SPI.h" +#endif +#if CIRCUITPY_BOARD_UART +#include "shared-bindings/busio/UART.h" +#endif + +//| """Board specific pin names +//| +//| Common container for board base pin names. These will vary from board to +//| board so don't expect portability when using this module. +//| +//| Another common use of this module is to use serial communciation buses with +//| the default pins and settings. For more information about serial communcication +//| in CircuitPython, see the :mod:`busio`. +//| +//| For more information regarding the typical usage of :py:mod:`board`, refer to the `CircuitPython +//| Essentials Learn guide +//| <https://learn.adafruit.com/circuitpython-essentials/circuitpython-pins-and-modules>`_ +//| +//| .. warning:: The board module varies by board. The APIs documented here may or may not be +//| available on a specific board.""" + +//| board_id: str +//| """Board ID string. The unique identifier for the board model in +//| circuitpython, as well as on circuitpython.org. +//| Example: "hallowing_m0_express".""" + +//| def I2C() -> busio.I2C: +//| """Returns the `busio.I2C` object for the board's designated I2C bus(es). +//| The object created is a singleton, and uses the default parameter values for `busio.I2C`.""" +//| ... +//| +#if CIRCUITPY_BOARD_I2C +STATIC mp_obj_t board_i2c_0(void) { + return common_hal_board_create_i2c(0); +} +#else +STATIC mp_obj_t board_i2c_0(void) { + mp_raise_NotImplementedError_varg(translate("No default %q bus"), MP_QSTR_I2C); + return MP_ROM_NONE; +} +#endif +MP_DEFINE_CONST_FUN_OBJ_0(board_i2c_obj, board_i2c_0); + +//| def SPI() -> busio.SPI: +//| """Returns the `busio.SPI` object for the board's designated SPI bus(es). +//| The object created is a singleton, and uses the default parameter values for `busio.SPI`.""" +//| ... +//| +#if CIRCUITPY_BOARD_SPI +STATIC mp_obj_t board_spi_0(void) { + return common_hal_board_create_spi(0); +} +#else +STATIC mp_obj_t board_spi_0(void) { + mp_raise_NotImplementedError_varg(translate("No default %q bus"), MP_QSTR_SPI); + return MP_ROM_NONE; +} +#endif +MP_DEFINE_CONST_FUN_OBJ_0(board_spi_obj, board_spi_0); + +//| def UART() -> busio.UART: +//| """Returns the `busio.UART` object for the board's designated UART bus(es). +//| The object created is a singleton, and uses the default parameter values for `busio.UART`.""" +//| ... +//| +#if CIRCUITPY_BOARD_UART +STATIC mp_obj_t board_uart_0(void) { + return common_hal_board_create_uart(0); +} +#else +STATIC mp_obj_t board_uart_0(void) { + mp_raise_NotImplementedError_varg(translate("No default %q bus"), MP_QSTR_UART); + return MP_ROM_NONE; +} +#endif +MP_DEFINE_CONST_FUN_OBJ_0(board_uart_obj, board_uart_0); + +const mp_obj_module_t board_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&board_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_board, board_module, CIRCUITPY_BOARD); diff --git a/circuitpython/shared-bindings/board/__init__.h b/circuitpython/shared-bindings/board/__init__.h new file mode 100644 index 0000000..b668e0a --- /dev/null +++ b/circuitpython/shared-bindings/board/__init__.h @@ -0,0 +1,66 @@ +/* + * 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_BOARD___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BOARD___INIT___H + +#include "py/obj.h" +#include "py/objstr.h" + +#include "shared-bindings/microcontroller/Pin.h" // for the pin definitions + +extern const mp_obj_dict_t board_module_globals; +STATIC const MP_DEFINE_STR_OBJ(board_module_id_obj, CIRCUITPY_BOARD_ID); + +bool common_hal_board_is_i2c(mp_obj_t obj); +mp_obj_t common_hal_board_get_i2c(const mp_int_t instance); +mp_obj_t common_hal_board_create_i2c(const mp_int_t instance); +mp_obj_t board_i2c(size_t n_args, const mp_obj_t *args); +MP_DECLARE_CONST_FUN_OBJ_0(board_i2c_obj); + +bool common_hal_board_is_spi(mp_obj_t obj); +mp_obj_t common_hal_board_get_spi(const mp_int_t instance); +mp_obj_t common_hal_board_create_spi(const mp_int_t instance); +mp_obj_t board_spi(size_t n_args, const mp_obj_t *args); +MP_DECLARE_CONST_FUN_OBJ_0(board_spi_obj); + +bool common_hal_board_is_uart(mp_obj_t obj); +mp_obj_t common_hal_board_get_uart(const mp_int_t instance); +mp_obj_t common_hal_board_create_uart(const mp_int_t instance); +mp_obj_t board_uart(size_t n_args, const mp_obj_t *args); +MP_DECLARE_CONST_FUN_OBJ_0(board_uart_obj); + +#define CIRCUITPY_BOARD_BUS_SINGLETON(name, bus, instance) \ + STATIC mp_obj_t board_##name(void) { \ + return common_hal_board_create_##bus(instance); \ + } \ + MP_DEFINE_CONST_FUN_OBJ_0(board_##name##_obj, board_##name); + +#define CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS \ + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_board) }, \ + { MP_ROM_QSTR(MP_QSTR_board_id), MP_ROM_PTR(&board_module_id_obj) }, + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BOARD___INIT___H 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 diff --git a/circuitpython/shared-bindings/camera/Camera.c b/circuitpython/shared-bindings/camera/Camera.c new file mode 100644 index 0000000..f7bc9ee --- /dev/null +++ b/circuitpython/shared-bindings/camera/Camera.c @@ -0,0 +1,132 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright 2020 Sony Semiconductor Solutions Corporation + * + * 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 "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/camera/Camera.h" +#include "shared-bindings/util.h" + +//| class Camera: +//| """The class to control camera. +//| +//| Usage:: +//| +//| import board +//| import sdioio +//| import storage +//| import camera +//| +//| sd = sdioio.SDCard( +//| clock=board.SDIO_CLOCK, +//| command=board.SDIO_COMMAND, +//| data=board.SDIO_DATA, +//| frequency=25000000) +//| vfs = storage.VfsFat(sd) +//| storage.mount(vfs, '/sd') +//| +//| cam = camera.Camera() +//| +//| buffer = bytearray(512 * 1024) +//| file = open("/sd/image.jpg","wb") +//| size = cam.take_picture(buffer, width=1920, height=1080, format=camera.ImageFormat.JPG) +//| file.write(buffer, size) +//| file.close()""" +//| + +//| def __init__(self) -> None: +//| """Initialize camera.""" +//| ... +//| +STATIC mp_obj_t camera_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + camera_obj_t *self = m_new_obj(camera_obj_t); + self->base.type = &camera_type; + // No arguments + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + common_hal_camera_construct(self); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """De-initialize camera.""" +//| ... +//| +STATIC mp_obj_t camera_obj_deinit(mp_obj_t self_in) { + camera_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_camera_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(camera_deinit_obj, camera_obj_deinit); + +STATIC void check_for_deinit(camera_obj_t *self) { + if (common_hal_camera_deinited(self)) { + raise_deinited_error(); + } +} + +//| def take_picture(self, buf: WriteableBuffer, format: ImageFormat) -> int: +//| """Take picture and save to ``buf`` in the given ``format``. The size of the picture +//| taken is ``width`` by ``height`` in pixels. +//| +//| :return: the number of bytes written into buf +//| :rtype: int""" +//| ... +//| +STATIC mp_obj_t camera_obj_take_picture(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_width, ARG_height, ARG_format }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_format, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + camera_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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); + + camera_imageformat_t format = camera_imageformat_obj_to_type(args[ARG_format].u_obj); + + return MP_OBJ_NEW_SMALL_INT(common_hal_camera_take_picture(self, (uint8_t *)bufinfo.buf, bufinfo.len, args[ARG_width].u_int, args[ARG_height].u_int, format)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(camera_take_picture_obj, 1, camera_obj_take_picture); + +STATIC const mp_rom_map_elem_t camera_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&camera_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_take_picture), MP_ROM_PTR(&camera_take_picture_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(camera_locals_dict, camera_locals_dict_table); + +const mp_obj_type_t camera_type = { + { &mp_type_type }, + .name = MP_QSTR_Camera, + .make_new = camera_make_new, + .locals_dict = (mp_obj_dict_t *)&camera_locals_dict, +}; diff --git a/circuitpython/shared-bindings/camera/Camera.h b/circuitpython/shared-bindings/camera/Camera.h new file mode 100644 index 0000000..8102672 --- /dev/null +++ b/circuitpython/shared-bindings/camera/Camera.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright 2020 Sony Semiconductor Solutions Corporation + * + * 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_CAMERA_CAMERA_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_CAMERA_CAMERA_H + +#include "common-hal/camera/Camera.h" +#include "shared-bindings/camera/ImageFormat.h" + +extern const mp_obj_type_t camera_type; + +void common_hal_camera_construct(camera_obj_t *self); +void common_hal_camera_deinit(camera_obj_t *self); +bool common_hal_camera_deinited(camera_obj_t *self); +size_t common_hal_camera_take_picture(camera_obj_t *self, uint8_t *buffer, size_t len, uint16_t width, uint16_t height, camera_imageformat_t format); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_CAMERA_CAMERA_H diff --git a/circuitpython/shared-bindings/camera/ImageFormat.c b/circuitpython/shared-bindings/camera/ImageFormat.c new file mode 100644 index 0000000..3ff687b --- /dev/null +++ b/circuitpython/shared-bindings/camera/ImageFormat.c @@ -0,0 +1,92 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright 2020 Sony Semiconductor Solutions Corporation + * + * 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 "shared-bindings/camera/ImageFormat.h" + +//| class ImageFormat: +//| """Image format""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define the image format.""" +//| +//| JPG: ImageFormat +//| """JPG format.""" +//| +//| RGB565: ImageFormat +//| """RGB565 format.""" +//| + +const camera_imageformat_obj_t camera_imageformat_jpg_obj = { + { &camera_imageformat_type }, +}; + +const camera_imageformat_obj_t camera_imageformat_rgb565_obj = { + { &camera_imageformat_type }, +}; + +camera_imageformat_t camera_imageformat_obj_to_type(mp_obj_t obj) { + if (obj == MP_ROM_PTR(&camera_imageformat_jpg_obj)) { + return IMAGEFORMAT_JPG; + } else if (obj == MP_ROM_PTR(&camera_imageformat_rgb565_obj)) { + return IMAGEFORMAT_RGB565; + } + return IMAGEFORMAT_NONE; +} + +mp_obj_t camera_imageformat_type_to_obj(camera_imageformat_t format) { + switch (format) { + case IMAGEFORMAT_JPG: + return (mp_obj_t)MP_ROM_PTR(&camera_imageformat_jpg_obj); + case IMAGEFORMAT_RGB565: + return (mp_obj_t)MP_ROM_PTR(&camera_imageformat_rgb565_obj); + case IMAGEFORMAT_NONE: + default: + return MP_ROM_NONE; + } +} + +STATIC const mp_rom_map_elem_t camera_imageformat_locals_dict_table[] = { + {MP_ROM_QSTR(MP_QSTR_JPG), MP_ROM_PTR(&camera_imageformat_jpg_obj)}, + {MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_PTR(&camera_imageformat_rgb565_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(camera_imageformat_locals_dict, camera_imageformat_locals_dict_table); + +STATIC void camera_imageformat_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr format = MP_QSTR_None; + if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&camera_imageformat_jpg_obj)) { + format = MP_QSTR_JPG; + } else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&camera_imageformat_rgb565_obj)) { + format = MP_QSTR_RGB565; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_camera, MP_QSTR_ImageSize, format); +} + +const mp_obj_type_t camera_imageformat_type = { + { &mp_type_type }, + .name = MP_QSTR_ImageFormat, + .print = camera_imageformat_print, + .locals_dict = (mp_obj_t)&camera_imageformat_locals_dict, +}; diff --git a/circuitpython/shared-bindings/camera/ImageFormat.h b/circuitpython/shared-bindings/camera/ImageFormat.h new file mode 100644 index 0000000..32a3635 --- /dev/null +++ b/circuitpython/shared-bindings/camera/ImageFormat.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright 2020 Sony Semiconductor Solutions Corporation + * + * 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_CAMERA_IMAGEFORMAT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_CAMERA_IMAGEFORMAT_H + +#include "py/obj.h" + +typedef enum { + IMAGEFORMAT_NONE, + IMAGEFORMAT_JPG, + IMAGEFORMAT_RGB565, +} camera_imageformat_t; + +extern const mp_obj_type_t camera_imageformat_type; + +camera_imageformat_t camera_imageformat_obj_to_type(mp_obj_t obj); +mp_obj_t camera_imageformat_type_to_obj(camera_imageformat_t mode); + +typedef struct { + mp_obj_base_t base; +} camera_imageformat_obj_t; +extern const camera_imageformat_obj_t camera_imageformat_jpg_obj; +extern const camera_imageformat_obj_t camera_imageformat_rgb565_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_CAMERA_IMAGEFORMAT_H diff --git a/circuitpython/shared-bindings/camera/__init__.c b/circuitpython/shared-bindings/camera/__init__.c new file mode 100644 index 0000000..12cebae --- /dev/null +++ b/circuitpython/shared-bindings/camera/__init__.c @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright 2020 Sony Semiconductor Solutions Corporation + * + * 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 "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "shared-bindings/camera/Camera.h" +#include "shared-bindings/util.h" + +//| """Support for camera input +//| +//| The `camera` module contains classes to control the camera and take pictures.""" +//| +STATIC const mp_rom_map_elem_t camera_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_camera) }, + { MP_ROM_QSTR(MP_QSTR_Camera), MP_ROM_PTR(&camera_type) }, + + // Enum-like Classes. + { MP_ROM_QSTR(MP_QSTR_ImageFormat), MP_ROM_PTR(&camera_imageformat_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(camera_module_globals, camera_module_globals_table); + +const mp_obj_module_t camera_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&camera_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_camera, camera_module, CIRCUITPY_CAMERA); diff --git a/circuitpython/shared-bindings/canio/CAN.c b/circuitpython/shared-bindings/canio/CAN.c new file mode 100644 index 0000000..e8aec7f --- /dev/null +++ b/circuitpython/shared-bindings/canio/CAN.c @@ -0,0 +1,360 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "py/enum.h" +#include "common-hal/canio/CAN.h" +#include "common-hal/canio/Listener.h" +#include "shared-bindings/canio/__init__.h" +#include "shared-bindings/canio/CAN.h" +#include "shared-bindings/canio/Listener.h" +#include "shared-bindings/canio/Match.h" +#include "shared-bindings/canio/Message.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "py/objproperty.h" +#include "py/runtime.h" + +//| +//| class CAN: +//| """CAN bus protocol""" +//| +//| def __init__(self, +//| tx: microcontroller.Pin, +//| rx: microcontroller.Pin, +//| *, +//| baudrate: int = 250000, +//| loopback: bool = False, +//| silent: bool = False, +//| auto_restart: bool = False, +//| ) -> None: +//| """A common shared-bus protocol. The rx and tx pins are generally +//| connected to a transceiver which controls the H and L pins on a +//| shared bus. +//| +//| :param ~microcontroller.Pin rx: the pin to receive with +//| :param ~microcontroller.Pin tx: the pin to transmit with +//| :param int baudrate: The bit rate of the bus in Hz. All devices on the bus must agree on this value. +//| :param bool loopback: When True the ``rx`` pin's value is ignored, and the device receives the packets it sends. +//| :param bool silent: When True the ``tx`` pin is always driven to the high logic level. This mode can be used to "sniff" a CAN bus without interfering. +//| :param bool auto_restart: If True, will restart communications after entering bus-off state +//| """ +//| ... +//| +STATIC mp_obj_t canio_can_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_loopback, ARG_silent, ARG_auto_restart, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_tx, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_rx, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 250000} }, + { MP_QSTR_loopback, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_silent, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_auto_restart, MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_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_pin = validate_obj_is_free_pin_or_none(args[ARG_rx].u_obj); + const mcu_pin_obj_t *tx_pin = validate_obj_is_free_pin_or_none(args[ARG_tx].u_obj); + if (!rx_pin && !tx_pin) { + mp_raise_ValueError(translate("tx and rx cannot both be None")); + } + + canio_can_obj_t *self = m_new_obj(canio_can_obj_t); + self->base.type = &canio_can_type; + common_hal_canio_can_construct(self, tx_pin, rx_pin, args[ARG_baudrate].u_int, args[ARG_loopback].u_bool, args[ARG_silent].u_bool); + + common_hal_canio_can_auto_restart_set(self, args[ARG_auto_restart].u_bool); + + return MP_OBJ_FROM_PTR(self); +} + + +//| auto_restart: bool +//| """If True, will restart communications after entering bus-off state""" +//| +STATIC mp_obj_t canio_can_auto_restart_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_can_auto_restart_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_auto_restart_get_obj, canio_can_auto_restart_get); + +STATIC mp_obj_t canio_can_auto_restart_set(mp_obj_t self_in, mp_obj_t flag_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + common_hal_canio_can_auto_restart_set(self, mp_obj_is_true(flag_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_can_auto_restart_set_obj, canio_can_auto_restart_set); + +MP_PROPERTY_GETSET(canio_can_auto_restart_obj, + (mp_obj_t)&canio_can_auto_restart_get_obj, + (mp_obj_t)&canio_can_auto_restart_set_obj); + + +//| baudrate: int +//| """The baud rate (read-only)""" +//| +STATIC mp_obj_t canio_can_baudrate_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_baudrate_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_baudrate_get_obj, canio_can_baudrate_get); + +MP_PROPERTY_GETTER(canio_can_baudrate_obj, + (mp_obj_t)&canio_can_baudrate_get_obj); + +//| transmit_error_count: int +//| """The number of transmit errors (read-only). Increased for a detected transmission error, decreased for successful transmission. Limited to the range from 0 to 255 inclusive. Also called TEC.""" +//| +STATIC mp_obj_t canio_can_transmit_error_count_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_transmit_error_count_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_transmit_error_count_get_obj, canio_can_transmit_error_count_get); + +MP_PROPERTY_GETTER(canio_can_transmit_error_count_obj, + (mp_obj_t)&canio_can_transmit_error_count_get_obj); + +//| receive_error_count: int +//| """The number of receive errors (read-only). Increased for a detected reception error, decreased for successful reception. Limited to the range from 0 to 255 inclusive. Also called REC.""" +//| +STATIC mp_obj_t canio_can_receive_error_count_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_can_receive_error_count_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_receive_error_count_get_obj, canio_can_receive_error_count_get); + +MP_PROPERTY_GETTER(canio_can_receive_error_count_obj, + (mp_obj_t)&canio_can_receive_error_count_get_obj); + +//| state: BusState +//| """The current state of the bus. (read-only)""" +STATIC mp_obj_t canio_can_state_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return cp_enum_find(&canio_bus_state_type, common_hal_canio_can_state_get(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_state_get_obj, canio_can_state_get); + +MP_PROPERTY_GETTER(canio_can_state_obj, + (mp_obj_t)&canio_can_state_get_obj); + + +//| def restart(self) -> None: +//| """If the device is in the bus off state, restart it.""" +//| ... +//| +STATIC mp_obj_t canio_can_restart(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + common_hal_canio_can_restart(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_restart_obj, canio_can_restart); + +//| def listen(self, matches: Optional[Sequence[Match]]=None, *, timeout: float=10) -> Listener: +//| """Start receiving messages that match any one of the filters. +//| +//| Creating a listener is an expensive operation and can interfere with reception of messages by other listeners. +//| +//| There is an implementation-defined maximum number of listeners and limit to the complexity of the filters. +//| +//| If the hardware cannot support all the requested matches, a ValueError is raised. Note that generally there are some number of hardware filters shared among all fifos. +//| +//| A message can be received by at most one Listener. If more than one listener matches a message, it is undefined which one actually receives it. +//| +//| An empty filter list causes all messages to be accepted. +//| +//| Timeout dictates how long receive() and next() will block. +//| +//| Platform specific notes: +//| +//| SAM E5x supports two Listeners. Filter blocks are shared between the two +//| listeners. There are 4 standard filter blocks and 4 extended filter blocks. +//| Each block can either match 2 single addresses or a mask of addresses. +//| The number of filter blocks can be increased, up to a hardware maximum, by +//| rebuilding CircuitPython, but this decreases the CircuitPython free +//| memory even if canio is not used. +//| +//| STM32F405 supports two Listeners. Filter blocks are shared between the two listeners. +//| There are 14 filter blocks. Each block can match 2 standard addresses with +//| mask or 1 extended address with mask. +//| +//| ESP32S2 supports one Listener. There is a single filter block, which can either match a +//| standard address with mask or an extended address with mask. +//| """ +//| ... +//| +STATIC mp_obj_t canio_can_listen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + common_hal_canio_can_check_for_deinit(self); + + enum { ARG_matches, ARG_timeout, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_matches, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + size_t nmatch = 0; + mp_obj_t *match_objects = NULL; + + if (args[ARG_matches].u_obj) { + mp_obj_get_array(args[ARG_matches].u_obj, &nmatch, &match_objects); + } + + canio_match_obj_t *matches[nmatch]; + for (size_t i = 0; i < nmatch; i++) { + const mp_obj_type_t *type = mp_obj_get_type(match_objects[i]); + if (type != &canio_match_type) { + mp_raise_TypeError_varg(translate("expected '%q' but got '%q'"), MP_QSTR_Match, type->name); + } + matches[i] = MP_OBJ_TO_PTR(match_objects[i]); + } + + float timeout = args[ARG_timeout].u_obj ? mp_obj_get_float(args[ARG_timeout].u_obj) : 10.0f; + canio_listener_obj_t *listener = m_new_obj(canio_listener_obj_t); + listener->base.type = &canio_listener_type; + common_hal_canio_listener_construct(listener, self, nmatch, matches, timeout); + return listener; +} +MP_DEFINE_CONST_FUN_OBJ_KW(canio_can_listen_obj, 1, canio_can_listen); + +//| loopback: bool +//| """True if the device was created in loopback mode, False +//| otherwise (read-only)""" +//| +STATIC mp_obj_t canio_can_loopback_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_can_loopback_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_loopback_get_obj, canio_can_loopback_get); + +MP_PROPERTY_GETTER(canio_can_loopback_obj, + (mp_obj_t)&canio_can_loopback_get_obj); + + +//| def send(self, message: Union[RemoteTransmissionRequest, Message]) -> None: +//| """Send a message on the bus with the given data and id. +//| If the message could not be sent due to a full fifo or a bus error condition, RuntimeError is raised. +//| """ +//| ... +//| +STATIC mp_obj_t canio_can_send(mp_obj_t self_in, mp_obj_t message_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + const mp_obj_type_t *message_type = mp_obj_get_type(message_in); + if (message_type != &canio_message_type && message_type != &canio_remote_transmission_request_type) { + mp_raise_TypeError_varg(translate("expected '%q' or '%q' but got '%q'"), MP_QSTR_Message, MP_QSTR_RemoteTransmissionRequest, message_type->name); + } + + canio_message_obj_t *message = message_in; + common_hal_canio_can_send(self, message); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_can_send_obj, canio_can_send); + +//| silent: bool +//| """True if the device was created in silent mode, False +//| otherwise (read-only)""" +//| +STATIC mp_obj_t canio_can_silent_get(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return mp_obj_new_bool(common_hal_canio_can_silent_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_can_silent_get_obj, canio_can_silent_get); + +MP_PROPERTY_GETTER(canio_can_silent_obj, + (mp_obj_t)&canio_can_silent_get_obj); + + +//| def deinit(self) -> None: +//| """Deinitialize this object, freeing its hardware resources""" +//| ... +//| +STATIC mp_obj_t canio_can_deinit(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_deinit_obj, canio_can_deinit); + +//| def __enter__(self) -> CAN: +//| """Returns self, to allow the object to be used in a `with` statement for resource control""" +//| ... +//| +STATIC mp_obj_t canio_can_enter(mp_obj_t self_in) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_can_check_for_deinit(self); + return self_in; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_can_enter_obj, canio_can_enter); + +//| def __exit__(self, unused1: Optional[Type[BaseException]], unused2: Optional[BaseException], unused3: Optional[TracebackType]) -> None: +//| """Calls deinit()""" +//| ... +STATIC mp_obj_t canio_can_exit(size_t num_args, const mp_obj_t args[]) { + canio_can_obj_t *self = MP_OBJ_TO_PTR(args[0]); + common_hal_canio_can_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canio_can_exit_obj, 4, 4, canio_can_exit); + +STATIC const mp_rom_map_elem_t canio_can_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&canio_can_enter_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&canio_can_exit_obj) }, + { MP_ROM_QSTR(MP_QSTR_auto_restart), MP_ROM_PTR(&canio_can_auto_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&canio_can_baudrate_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&canio_can_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&canio_can_listen_obj) }, + { MP_ROM_QSTR(MP_QSTR_loopback), MP_ROM_PTR(&canio_can_loopback_obj) }, + { MP_ROM_QSTR(MP_QSTR_receive_error_count), MP_ROM_PTR(&canio_can_receive_error_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_restart), MP_ROM_PTR(&canio_can_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&canio_can_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_silent), MP_ROM_PTR(&canio_can_silent_obj) }, + { MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&canio_can_state_obj) }, + { MP_ROM_QSTR(MP_QSTR_transmit_error_count), MP_ROM_PTR(&canio_can_transmit_error_count_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_can_locals_dict, canio_can_locals_dict_table); + +const mp_obj_type_t canio_can_type = { + { &mp_type_type }, + .name = MP_QSTR_CAN, + .make_new = canio_can_make_new, + .locals_dict = (mp_obj_t)&canio_can_locals_dict, +}; diff --git a/circuitpython/shared-bindings/canio/CAN.h b/circuitpython/shared-bindings/canio/CAN.h new file mode 100644 index 0000000..b25d2fd --- /dev/null +++ b/circuitpython/shared-bindings/canio/CAN.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/canio/__init__.h" +#include "shared-bindings/canio/Message.h" + +extern const mp_obj_type_t canio_can_type; + +typedef struct canio_can_obj canio_can_obj_t; + +void common_hal_canio_can_construct(canio_can_obj_t *self, const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent); +bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self); +bool common_hal_canio_can_deinited(canio_can_obj_t *self); +int common_hal_canio_can_baudrate_get(canio_can_obj_t *self); +bool common_hal_canio_can_loopback_get(canio_can_obj_t *self); +int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self); +canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self); +bool common_hal_canio_can_silent_get(canio_can_obj_t *self); +int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self); +void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool auto_restart); +void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self); +void common_hal_canio_can_deinit(canio_can_obj_t *self); +void common_hal_canio_can_restart(canio_can_obj_t *self); +void common_hal_canio_can_send(canio_can_obj_t *self, mp_obj_t message); +void common_hal_canio_reset(void); diff --git a/circuitpython/shared-bindings/canio/Listener.c b/circuitpython/shared-bindings/canio/Listener.c new file mode 100644 index 0000000..e59801d --- /dev/null +++ b/circuitpython/shared-bindings/canio/Listener.c @@ -0,0 +1,178 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "shared-bindings/canio/Listener.h" +#include "shared-bindings/canio/Message.h" +#include "common-hal/canio/Listener.h" + +#include "py/runtime.h" +#include "py/objproperty.h" + +//| class Listener: +//| """Listens for CAN message +//| +//| `canio.Listener` is not constructed directly, but instead by calling +//| `canio.CAN.listen`. +//| +//| In addition to using the `receive` method to retrieve a message or +//| the `in_waiting` method to check for an available message, a +//| listener can be used as an iterable, yielding messages until no +//| message arrives within ``self.timeout`` seconds.""" +//| + +//| def receive(self) -> Optional[Union[RemoteTransmissionRequest,Message]]: +//| """Reads a message, after waiting up to ``self.timeout`` seconds +//| +//| If no message is received in time, `None` is returned. Otherwise, +//| a `Message` or `RemoteTransmissionRequest` is returned.""" +//| ... +//| +STATIC mp_obj_t canio_listener_receive(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + + mp_obj_t message = common_hal_canio_listener_receive(self); + // note: receive fills out the type field of the message + + if (message) { + return message; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_receive_obj, canio_listener_receive); + +//| def in_waiting(self) -> int: +//| """Returns the number of messages (including remote +//| transmission requests) waiting""" +//| ... +//| +STATIC mp_obj_t canio_listener_in_waiting(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_listener_in_waiting(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_in_waiting_obj, canio_listener_in_waiting); + +//| def __iter__(self) -> Listener: +//| """Returns self +//| +//| This method exists so that `Listener` can be used as an +//| iterable""" +//| ... +//| +//| def __next__(self) -> Union[RemoteTransmissionRequest,Message]: +//| """Reads a message, after waiting up to self.timeout seconds +//| +//| If no message is received in time, raises StopIteration. Otherwise, +//| a Message or is returned. +//| +//| This method enables the `Listener` to be used as an +//| iterable, for instance in a for-loop.""" +//| ... +//| +STATIC mp_obj_t canio_iternext(mp_obj_t self_in) { + mp_obj_t result = canio_listener_receive(self_in); + if (result == mp_const_none) { + return MP_OBJ_STOP_ITERATION; + } + return result; +} + +//| def deinit(self) -> None: +//| """Deinitialize this object, freeing its hardware resources""" +//| ... +//| +STATIC mp_obj_t canio_listener_deinit(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_deinit_obj, canio_listener_deinit); + +//| def __enter__(self) -> CAN: +//| """Returns self, to allow the object to be used in a `with` statement for resource control""" +//| ... +//| +STATIC mp_obj_t canio_listener_enter(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + return self_in; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_enter_obj, canio_listener_enter); + +//| def __exit__(self, unused1: Optional[Type[BaseException]], unused2: Optional[BaseException], unused3: Optional[TracebackType]) -> None: +//| """Calls deinit()""" +//| ... +STATIC mp_obj_t canio_listener_exit(size_t num_args, const mp_obj_t args[]) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(args[0]); + common_hal_canio_listener_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(canio_listener_exit_obj, 4, 4, canio_listener_exit); + + +//| timeout : float +STATIC mp_obj_t canio_listener_timeout_get(mp_obj_t self_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + return mp_obj_new_float(common_hal_canio_listener_get_timeout(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(canio_listener_timeout_get_obj, canio_listener_timeout_get); + +STATIC mp_obj_t canio_listener_timeout_set(mp_obj_t self_in, mp_obj_t timeout_in) { + canio_listener_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_canio_listener_check_for_deinit(self); + common_hal_canio_listener_set_timeout(self, mp_obj_get_float(timeout_in)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(canio_listener_timeout_set_obj, canio_listener_timeout_set); + +MP_PROPERTY_GETSET(canio_listener_timeout_obj, + (mp_obj_t)&canio_listener_timeout_get_obj, + (mp_obj_t)&canio_listener_timeout_set_obj); + + + +STATIC const mp_rom_map_elem_t canio_listener_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&canio_listener_enter_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&canio_listener_exit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&canio_listener_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&canio_listener_in_waiting_obj) }, + { MP_ROM_QSTR(MP_QSTR_receive), MP_ROM_PTR(&canio_listener_receive_obj) }, + { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&canio_listener_timeout_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_listener_locals_dict, canio_listener_locals_dict_table); + +const mp_obj_type_t canio_listener_type = { + { &mp_type_type }, + .name = MP_QSTR_Listener, + .flags = MP_TYPE_FLAG_EXTENDED, + .locals_dict = (mp_obj_dict_t *)&canio_listener_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = canio_iternext, + ), +}; diff --git a/circuitpython/shared-bindings/canio/Listener.h b/circuitpython/shared-bindings/canio/Listener.h new file mode 100644 index 0000000..527ffe4 --- /dev/null +++ b/circuitpython/shared-bindings/canio/Listener.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/canio/CAN.h" +#include "shared-bindings/canio/Match.h" + +extern const mp_obj_type_t canio_listener_type; + +typedef struct canio_listener_obj canio_listener_obj_t; + +void common_hal_canio_listener_construct(canio_listener_obj_t *self, canio_can_obj_t *can, size_t nmatch, canio_match_obj_t **matches, float timeout); +void common_hal_canio_listener_check_for_deinit(canio_listener_obj_t *self); +void common_hal_canio_listener_deinit(canio_listener_obj_t *self); +mp_obj_t common_hal_canio_listener_receive(canio_listener_obj_t *self); +int common_hal_canio_listener_in_waiting(canio_listener_obj_t *self); +float common_hal_canio_listener_get_timeout(canio_listener_obj_t *self); +void common_hal_canio_listener_set_timeout(canio_listener_obj_t *self, float timeout); diff --git a/circuitpython/shared-bindings/canio/Match.c b/circuitpython/shared-bindings/canio/Match.c new file mode 100644 index 0000000..52262b0 --- /dev/null +++ b/circuitpython/shared-bindings/canio/Match.c @@ -0,0 +1,127 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "shared-bindings/canio/Match.h" + +#include "py/objproperty.h" +#include "py/runtime.h" + +//| class Match: +//| """Describe CAN bus messages to match""" +//| +//| +//| def __init__(self, id: int, *, mask: Optional[int] = None, extended: bool = False) -> None: +//| """Construct a Match with the given properties. +//| +//| If mask is not None, then the filter is for any id which matches all +//| the nonzero bits in mask. Otherwise, it matches exactly the given id. +//| If extended is true then only extended ids are matched, otherwise +//| only standard ids are matched.""" +//| + +STATIC mp_obj_t canio_match_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_mask, ARG_extended, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_mask, MP_ARG_OBJ, {.u_obj = mp_const_none } }, + { MP_QSTR_extended, MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int id_bits = args[ARG_extended].u_bool ? 0x1fffffff : 0x7ff; + int id = args[ARG_id].u_int; + int mask = args[ARG_mask].u_obj == mp_const_none ? id_bits : mp_obj_get_int(args[ARG_mask].u_obj); + + if (id & ~id_bits) { + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_id); + } + + if (mask & ~id_bits) { + mp_raise_ValueError_varg(translate("%q out of range"), MP_QSTR_mask); + } + + canio_match_obj_t *self = m_new_obj(canio_match_obj_t); + self->base.type = &canio_match_type; + common_hal_canio_match_construct(self, id, mask, args[ARG_extended].u_bool); + return self; +} + +//| id: int +//| """The id to match""" +//| + +STATIC mp_obj_t canio_match_id_get(mp_obj_t self_in) { + canio_match_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_match_get_id(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_match_id_get_obj, canio_match_id_get); + +MP_PROPERTY_GETTER(canio_match_id_obj, + (mp_obj_t)&canio_match_id_get_obj); + +//| +//| mask: int +//| """The optional mask of ids to match""" +//| + +STATIC mp_obj_t canio_match_mask_get(mp_obj_t self_in) { + canio_match_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_match_get_mask(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_match_mask_get_obj, canio_match_mask_get); + +MP_PROPERTY_GETTER(canio_match_mask_obj, + (mp_obj_t)&canio_match_mask_get_obj); + +//| extended: bool +//| """True to match extended ids, False to match standard ides""" +//| + +STATIC mp_obj_t canio_match_extended_get(mp_obj_t self_in) { + canio_match_obj_t *self = self_in; + return mp_obj_new_bool(common_hal_canio_match_get_extended(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_match_extended_get_obj, canio_match_extended_get); + +MP_PROPERTY_GETTER(canio_match_extended_obj, + (mp_obj_t)&canio_match_extended_get_obj); + +STATIC const mp_rom_map_elem_t canio_match_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&canio_match_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_mask), MP_ROM_PTR(&canio_match_mask_obj) }, + { MP_ROM_QSTR(MP_QSTR_extended), MP_ROM_PTR(&canio_match_extended_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_match_locals_dict, canio_match_locals_dict_table); + +const mp_obj_type_t canio_match_type = { + { &mp_type_type }, + .name = MP_QSTR_Match, + .make_new = canio_match_make_new, + .locals_dict = (mp_obj_dict_t *)&canio_match_locals_dict, +}; diff --git a/circuitpython/shared-bindings/canio/Match.h b/circuitpython/shared-bindings/canio/Match.h new file mode 100644 index 0000000..01a75fd --- /dev/null +++ b/circuitpython/shared-bindings/canio/Match.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-module/canio/Match.h" + +extern const mp_obj_type_t canio_match_type; + +void common_hal_canio_match_construct(canio_match_obj_t *self, int id, int mask, bool extended); +int common_hal_canio_match_get_id(const canio_match_obj_t *self); +int common_hal_canio_match_get_mask(const canio_match_obj_t *self); +bool common_hal_canio_match_get_extended(const canio_match_obj_t *self); diff --git a/circuitpython/shared-bindings/canio/Message.c b/circuitpython/shared-bindings/canio/Message.c new file mode 100644 index 0000000..64d4094 --- /dev/null +++ b/circuitpython/shared-bindings/canio/Message.c @@ -0,0 +1,150 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "shared-bindings/canio/Message.h" + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +//| class Message: +//| def __init__(self, id: int, data: bytes, *, extended: bool = False) -> None: +//| """Construct a Message to send on a CAN bus. +//| +//| :param int id: The numeric ID of the message +//| :param bytes data: The content of the message +//| :param bool extended: True if the message has an extended identifier, False if it has a standard identifier +//| +//| In CAN, messages can have a length from 0 to 8 bytes. +//| """ +//| ... +//| +STATIC mp_obj_t canio_message_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_data, ARG_extended, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_extended, MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t data; + mp_get_buffer_raise(args[ARG_data].u_obj, &data, MP_BUFFER_READ); + + if (data.len > 8) { + mp_raise_ValueError(translate("Messages limited to 8 bytes")); + } + + canio_message_obj_t *self = m_new_obj(canio_message_obj_t); + self->base.type = &canio_message_type; + common_hal_canio_message_construct(self, args[ARG_id].u_int, data.buf, data.len, args[ARG_extended].u_bool); + return self; +} + +//| id: int +//| """The numeric ID of the message""" +//| +STATIC mp_obj_t canio_message_id_get(const mp_obj_t self_in) { + canio_message_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_message_get_id(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_message_id_get_obj, canio_message_id_get); + +STATIC mp_obj_t canio_message_id_set(const mp_obj_t self_in, const mp_obj_t id) { + canio_message_obj_t *self = self_in; + common_hal_canio_message_set_id(self, mp_obj_get_int(id)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_message_id_set_obj, canio_message_id_set); + +MP_PROPERTY_GETSET(canio_message_id_obj, + (mp_obj_t)&canio_message_id_get_obj, + (mp_obj_t)&canio_message_id_set_obj); + +//| data: bytes +//| """The content of the message""" +//| +STATIC mp_obj_t canio_message_data_get(const mp_obj_t self_in) { + canio_message_obj_t *self = self_in; + return mp_obj_new_bytes((const byte *)common_hal_canio_message_get_data(self), common_hal_canio_message_get_length(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_message_data_get_obj, canio_message_data_get); + +STATIC mp_obj_t canio_message_data_set(const mp_obj_t self_in, const mp_obj_t data_in) { + canio_message_obj_t *self = self_in; + mp_buffer_info_t data; + mp_get_buffer_raise(data_in, &data, MP_BUFFER_READ); + if (data.len > 8) { + mp_raise_ValueError(translate("Messages limited to 8 bytes")); + } + common_hal_canio_message_set_data(self, data.buf, data.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_message_data_set_obj, canio_message_data_set); + + +MP_PROPERTY_GETSET(canio_message_data_obj, + (mp_obj_t)&canio_message_data_get_obj, + (mp_obj_t)&canio_message_data_set_obj); + + +//| extended: bool +//| """True if the message's id is an extended id""" +//| +STATIC mp_obj_t canio_message_extended_get(const mp_obj_t self_in) { + canio_message_obj_t *self = self_in; + return mp_obj_new_bool(common_hal_canio_message_get_extended(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_message_extended_get_obj, canio_message_extended_get); + +STATIC mp_obj_t canio_message_extended_set(const mp_obj_t self_in, const mp_obj_t extended) { + canio_message_obj_t *self = self_in; + common_hal_canio_message_set_extended(self, mp_obj_is_true(extended)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_message_extended_set_obj, canio_message_extended_set); + + +MP_PROPERTY_GETSET(canio_message_extended_obj, + (mp_obj_t)&canio_message_extended_get_obj, + (mp_obj_t)&canio_message_extended_set_obj); + +STATIC const mp_rom_map_elem_t canio_message_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&canio_message_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_data), MP_ROM_PTR(&canio_message_data_obj) }, + { MP_ROM_QSTR(MP_QSTR_extended), MP_ROM_PTR(&canio_message_extended_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_message_locals_dict, canio_message_locals_dict_table); + +const mp_obj_type_t canio_message_type = { + { &mp_type_type }, + .name = MP_QSTR_Message, + .make_new = canio_message_make_new, + .locals_dict = (mp_obj_t)&canio_message_locals_dict, +}; diff --git a/circuitpython/shared-bindings/canio/Message.h b/circuitpython/shared-bindings/canio/Message.h new file mode 100644 index 0000000..b765359 --- /dev/null +++ b/circuitpython/shared-bindings/canio/Message.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-module/canio/Message.h" + +extern const mp_obj_type_t canio_message_type; +extern const mp_obj_type_t canio_remote_transmission_request_type; + +void common_hal_canio_message_construct(canio_message_obj_t *self, int id, void *data, size_t size, bool extended); +const void *common_hal_canio_message_get_data(const canio_message_obj_t *self); +void common_hal_canio_message_set_data(canio_message_obj_t *self, const void *data, size_t size); +bool common_hal_canio_message_get_extended(const canio_message_obj_t *self); +void common_hal_canio_message_set_extended(canio_message_obj_t *self, bool extended); +int common_hal_canio_message_get_id(const canio_message_obj_t *self); +void common_hal_canio_message_set_id(canio_message_obj_t *self, int id); +size_t common_hal_canio_message_get_length(const canio_message_obj_t *self); diff --git a/circuitpython/shared-bindings/canio/RemoteTransmissionRequest.c b/circuitpython/shared-bindings/canio/RemoteTransmissionRequest.c new file mode 100644 index 0000000..58ac3aa --- /dev/null +++ b/circuitpython/shared-bindings/canio/RemoteTransmissionRequest.c @@ -0,0 +1,147 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "shared-bindings/canio/RemoteTransmissionRequest.h" + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +//| class RemoteTransmissionRequest: +//| def __init__(self, id: int, length: int, *, extended: bool = False) -> None: +//| """Construct a RemoteTransmissionRequest to send on a CAN bus. +//| +//| :param int id: The numeric ID of the requested message +//| :param int length: The length of the requested message +//| :param bool extended: True if the message has an extended identifier, False if it has a standard identifier +//| +//| In CAN, messages can have a length from 0 to 8 bytes. +//| """ +//| ... +//| +STATIC mp_obj_t canio_remote_transmission_request_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_length, ARG_extended, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_length, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_extended, MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int length = args[ARG_length].u_int; + if (length < 0 || length > 8) { + mp_raise_ValueError(translate("RemoteTransmissionRequests limited to 8 bytes")); + } + + canio_remote_transmission_request_obj_t *self = m_new_obj(canio_remote_transmission_request_obj_t); + self->base.type = &canio_remote_transmission_request_type; + common_hal_canio_remote_transmission_request_construct(self, args[ARG_id].u_int, length, args[ARG_extended].u_bool); + return self; +} + + +//| id: int +//| """The numeric ID of the message""" +//| +STATIC mp_obj_t canio_remote_transmission_request_id_get(const mp_obj_t self_in) { + canio_remote_transmission_request_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_remote_transmission_request_get_id(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_remote_transmission_request_id_get_obj, canio_remote_transmission_request_id_get); + +STATIC mp_obj_t canio_remote_transmission_request_id_set(const mp_obj_t self_in, const mp_obj_t id) { + canio_remote_transmission_request_obj_t *self = self_in; + common_hal_canio_remote_transmission_request_set_id(self, mp_obj_get_int(id)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_remote_transmission_request_id_set_obj, canio_remote_transmission_request_id_set); + +MP_PROPERTY_GETSET(canio_remote_transmission_request_id_obj, + (mp_obj_t)&canio_remote_transmission_request_id_get_obj, + (mp_obj_t)&canio_remote_transmission_request_id_set_obj); + +//| extended: bool +//| """True if the message's id is an extended id""" +//| +STATIC mp_obj_t canio_remote_transmission_request_extended_get(const mp_obj_t self_in) { + canio_remote_transmission_request_obj_t *self = self_in; + return mp_obj_new_bool(common_hal_canio_remote_transmission_request_get_extended(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_remote_transmission_request_extended_get_obj, canio_remote_transmission_request_extended_get); + +STATIC mp_obj_t canio_remote_transmission_request_extended_set(const mp_obj_t self_in, const mp_obj_t extended) { + canio_remote_transmission_request_obj_t *self = self_in; + common_hal_canio_remote_transmission_request_set_extended(self, mp_obj_is_true(extended)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_remote_transmission_request_extended_set_obj, canio_remote_transmission_request_extended_set); + + +MP_PROPERTY_GETSET(canio_remote_transmission_request_extended_obj, + (mp_obj_t)&canio_remote_transmission_request_extended_get_obj, + (mp_obj_t)&canio_remote_transmission_request_extended_set_obj); + +//| length: int +//| """The length of the requested message.""" +//| +STATIC mp_obj_t canio_remote_transmission_request_length_get(const mp_obj_t self_in) { + canio_remote_transmission_request_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(common_hal_canio_remote_transmission_request_get_length(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(canio_remote_transmission_request_length_get_obj, canio_remote_transmission_request_length_get); + +STATIC mp_obj_t canio_remote_transmission_request_length_set(const mp_obj_t self_in, const mp_obj_t length_in) { + canio_remote_transmission_request_obj_t *self = self_in; + int length = mp_obj_get_int(length_in); + if (length < 0 || length > 8) { + mp_raise_ValueError(translate("RemoteTransmissionRequests limited to 8 bytes")); + } + common_hal_canio_remote_transmission_request_set_length(self, length); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(canio_remote_transmission_request_length_set_obj, canio_remote_transmission_request_length_set); + + +MP_PROPERTY_GETSET(canio_remote_transmission_request_length_obj, + (mp_obj_t)&canio_remote_transmission_request_length_get_obj, + (mp_obj_t)&canio_remote_transmission_request_length_set_obj); + +STATIC const mp_rom_map_elem_t canio_remote_transmission_request_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&canio_remote_transmission_request_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_length), MP_ROM_PTR(&canio_remote_transmission_request_length_obj) }, + { MP_ROM_QSTR(MP_QSTR_extended), MP_ROM_PTR(&canio_remote_transmission_request_extended_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(canio_remote_transmission_request_locals_dict, canio_remote_transmission_request_locals_dict_table); + +const mp_obj_type_t canio_remote_transmission_request_type = { + { &mp_type_type }, + .name = MP_QSTR_RemoteTransmissionRequest, + .make_new = canio_remote_transmission_request_make_new, + .locals_dict = (mp_obj_t)&canio_remote_transmission_request_locals_dict, +}; diff --git a/circuitpython/shared-bindings/canio/RemoteTransmissionRequest.h b/circuitpython/shared-bindings/canio/RemoteTransmissionRequest.h new file mode 100644 index 0000000..8956587 --- /dev/null +++ b/circuitpython/shared-bindings/canio/RemoteTransmissionRequest.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-module/canio/RemoteTransmissionRequest.h" + +extern const mp_obj_type_t canio_remote_transmission_request_type; + +void common_hal_canio_remote_transmission_request_construct(canio_remote_transmission_request_obj_t *self, int id, size_t size, bool extended); +const void *common_hal_canio_remote_transmission_request_get_data(const canio_remote_transmission_request_obj_t *self); +void common_hal_canio_remote_transmission_request_set_data(canio_remote_transmission_request_obj_t *self, const void *data, size_t size); +bool common_hal_canio_remote_transmission_request_get_extended(const canio_remote_transmission_request_obj_t *self); +void common_hal_canio_remote_transmission_request_set_extended(canio_remote_transmission_request_obj_t *self, bool extended); +int common_hal_canio_remote_transmission_request_get_id(const canio_remote_transmission_request_obj_t *self); +void common_hal_canio_remote_transmission_request_set_id(canio_remote_transmission_request_obj_t *self, int id); +size_t common_hal_canio_remote_transmission_request_get_length(const canio_remote_transmission_request_obj_t *self); +void common_hal_canio_remote_transmission_request_set_length(canio_remote_transmission_request_obj_t *self, size_t length); diff --git a/circuitpython/shared-bindings/canio/__init__.c b/circuitpython/shared-bindings/canio/__init__.c new file mode 100644 index 0000000..ef1b90d --- /dev/null +++ b/circuitpython/shared-bindings/canio/__init__.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +//| """CAN bus access +//| +//| The `canio` module contains low level classes to support the CAN bus +//| protocol. +//| +//| CAN and Listener 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 canio +//| from board import * +//| +//| can = canio.CAN(board.CAN_RX, board.CAN_TX, baudrate=1000000) +//| message = canio.Message(id=0x0408, data=b"adafruit") +//| can.send(message) +//| can.deinit() +//| +//| This example will write the data 'adafruit' onto the CAN bus to any +//| device listening for message id 0x0408. +//| +//| A CAN bus involves a transceiver, which is often a separate chip with a "standby" pin. +//| If your board has a CAN_STANDBY pin, ensure to set it to an output with the value False +//| to enable the transceiver. +//| +//| Other implementations of the CAN device may exist (for instance, attached +//| via an SPI bus). If so their constructor arguments may differ, but +//| otherwise we encourage implementors to follow the API that the core uses. +//| """ +//| + +#include "py/obj.h" +#include "py/enum.h" + +#include "shared-bindings/canio/__init__.h" +#include "shared-bindings/canio/CAN.h" +#include "shared-bindings/canio/Match.h" +#include "shared-bindings/canio/Message.h" +#include "shared-bindings/canio/Listener.h" + +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, ERROR_ACTIVE, BUS_STATE_ERROR_ACTIVE); +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, ERROR_PASSIVE, BUS_STATE_ERROR_PASSIVE); +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, ERROR_WARNING, BUS_STATE_ERROR_WARNING); +MAKE_ENUM_VALUE(canio_bus_state_type, bus_state, BUS_OFF, BUS_STATE_OFF); + +//| class BusState: +//| """The state of the CAN bus""" +//| +//| ERROR_ACTIVE: object +//| """The bus is in the normal (active) state""" +//| +//| ERROR_WARNING: object +//| """The bus is in the normal (active) state, but a moderate number of errors have occurred recently. +//| +//| .. note:: Not all implementations may use ``ERROR_WARNING``. Do not rely on seeing ``ERROR_WARNING`` before ``ERROR_PASSIVE``.""" +//| +//| ERROR_PASSIVE: object +//| """The bus is in the passive state due to the number of errors that have occurred recently. +//| +//| This device will acknowledge packets it receives, but cannot transmit messages. +//| If additional errors occur, this device may progress to BUS_OFF. +//| If it successfully acknowledges other packets on the bus, it can return to ERROR_WARNING or ERROR_ACTIVE and transmit packets. +//| """ +//| +//| BUS_OFF: object +//| """The bus has turned off due to the number of errors that have +//| occurred recently. It must be restarted before it will send or receive +//| packets. This device will neither send or acknowledge packets on the bus.""" +//| +MAKE_ENUM_MAP(canio_bus_state) { + MAKE_ENUM_MAP_ENTRY(bus_state, ERROR_ACTIVE), + MAKE_ENUM_MAP_ENTRY(bus_state, ERROR_PASSIVE), + MAKE_ENUM_MAP_ENTRY(bus_state, ERROR_WARNING), + MAKE_ENUM_MAP_ENTRY(bus_state, BUS_OFF), +}; +STATIC MP_DEFINE_CONST_DICT(canio_bus_state_locals_dict, canio_bus_state_locals_table); + +MAKE_PRINTER(canio, canio_bus_state); + +MAKE_ENUM_TYPE(canio, BusState, canio_bus_state); + +STATIC const mp_rom_map_elem_t canio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_canio) }, + { MP_ROM_QSTR(MP_QSTR_BusState), MP_ROM_PTR(&canio_bus_state_type) }, + { MP_ROM_QSTR(MP_QSTR_CAN), MP_ROM_PTR(&canio_can_type) }, + { MP_ROM_QSTR(MP_QSTR_Listener), MP_ROM_PTR(&canio_listener_type) }, + { MP_ROM_QSTR(MP_QSTR_Match), MP_ROM_PTR(&canio_match_type) }, + { MP_ROM_QSTR(MP_QSTR_Message), MP_ROM_PTR(&canio_message_type) }, + { MP_ROM_QSTR(MP_QSTR_RemoteTransmissionRequest), MP_ROM_PTR(&canio_remote_transmission_request_type) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__canio) }, +}; + +STATIC MP_DEFINE_CONST_DICT(canio_module_globals, canio_module_globals_table); + +const mp_obj_module_t canio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&canio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_canio, canio_module, CIRCUITPY_CANIO); diff --git a/circuitpython/shared-bindings/canio/__init__.h b/circuitpython/shared-bindings/canio/__init__.h new file mode 100644 index 0000000..e24eba9 --- /dev/null +++ b/circuitpython/shared-bindings/canio/__init__.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +typedef enum { + BUS_STATE_ERROR_ACTIVE, BUS_STATE_ERROR_PASSIVE, BUS_STATE_ERROR_WARNING, BUS_STATE_OFF +} canio_bus_state_t; + +extern const mp_obj_type_t canio_bus_state_type; diff --git a/circuitpython/shared-bindings/countio/Counter.c b/circuitpython/shared-bindings/countio/Counter.c new file mode 100644 index 0000000..1914124 --- /dev/null +++ b/circuitpython/shared-bindings/countio/Counter.c @@ -0,0 +1,149 @@ + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/countio/Counter.h" +#include "shared-bindings/countio/Edge.h" +#include "shared-bindings/util.h" + +//| class Counter: +//| """Count the number of rising- and/or falling-edge transitions on a given pin. +//| """ +//| +//| def __init__(self, pin: microcontroller.Pin, *, edge: Edge = Edge.FALL, pull: Optional[digitalio.Pull] = None) -> None: +//| """Create a Counter object associated with the given pin that counts +//| rising- and/or falling-edge transitions. At least one of ``rise`` and ``fall`` must be True. +//| The default is to count only falling edges, and is for historical backward compatibility. +//| +//| :param ~microcontroller.Pin pin: pin to monitor +//| :param Edge edge: which edge transitions to count +//| :param Optional[digitalio.Pull] pull: enable a pull-up or pull-down if not None +//| +//| +//| For example:: +//| +//| import board +//| import countio +//| +//| # Count rising edges only. +//| pin_counter = countio.Counter(board.D1, edge=Edge.RISE) +//| # Reset the count after 100 counts. +//| while True: +//| if pin_counter.count >= 100: +//| pin_counter.reset() +//| print(pin_counter.count) +//| """ +//| ... +//| +STATIC mp_obj_t countio_counter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pin, ARG_edge, ARG_pull }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_edge, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_FROM_PTR(&edge_FALL_obj) } }, + { MP_QSTR_pull, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + 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 *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + const countio_edge_t edge = validate_edge(args[ARG_edge].u_obj, MP_QSTR_edge); + const digitalio_pull_t pull = validate_pull(args[ARG_pull].u_obj, MP_QSTR_pull); + // Make long-lived because some implementations use a pointer to the object as interrupt-handler data. + countio_counter_obj_t *self = m_new_ll_obj(countio_counter_obj_t); + self->base.type = &countio_counter_type; + + common_hal_countio_counter_construct(self, pin, edge, pull); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitializes the Counter and releases any hardware resources for reuse.""" +//| +STATIC mp_obj_t countio_counter_deinit(mp_obj_t self_in) { + countio_counter_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_countio_counter_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(countio_counter_deinit_obj, countio_counter_deinit); + +STATIC void check_for_deinit(countio_counter_obj_t *self) { + if (common_hal_countio_counter_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> Counter: +//| """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 countio_counter_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_countio_counter_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(countio_counter___exit___obj, 4, 4, countio_counter_obj___exit__); + + +//| count: int +//| """The current count in terms of pulses.""" +//| +STATIC mp_obj_t countio_counter_obj_get_count(mp_obj_t self_in) { + countio_counter_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return mp_obj_new_int(common_hal_countio_counter_get_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(countio_counter_get_count_obj, countio_counter_obj_get_count); + +STATIC mp_obj_t countio_counter_obj_set_count(mp_obj_t self_in, mp_obj_t new_count) { + countio_counter_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_countio_counter_set_count(self, mp_obj_get_int(new_count)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(countio_counter_set_count_obj, countio_counter_obj_set_count); + +MP_PROPERTY_GETSET(countio_counter_count_obj, + (mp_obj_t)&countio_counter_get_count_obj, + (mp_obj_t)&countio_counter_set_count_obj); + +//| def reset(self) -> None: +//| """Resets the count back to 0.""" +//| +STATIC mp_obj_t countio_counter_reset(mp_obj_t self_in) { + countio_counter_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_countio_counter_set_count(self, 0); + return mp_const_none; +} + + +MP_DEFINE_CONST_FUN_OBJ_1(countio_counter_reset_obj, countio_counter_reset); + +STATIC const mp_rom_map_elem_t countio_counter_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&countio_counter_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&countio_counter___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&countio_counter_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&countio_counter_reset_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(countio_counter_locals_dict, countio_counter_locals_dict_table); + +const mp_obj_type_t countio_counter_type = { + { &mp_type_type }, + .name = MP_QSTR_Counter, + .make_new = countio_counter_make_new, + .locals_dict = (mp_obj_dict_t *)&countio_counter_locals_dict, +}; diff --git a/circuitpython/shared-bindings/countio/Counter.h b/circuitpython/shared-bindings/countio/Counter.h new file mode 100644 index 0000000..d660cc8 --- /dev/null +++ b/circuitpython/shared-bindings/countio/Counter.h @@ -0,0 +1,19 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO_COUNTER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO_COUNTER_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/countio/Counter.h" +#include "shared-bindings/countio/Edge.h" +#include "shared-bindings/digitalio/Pull.h" + +extern const mp_obj_type_t countio_counter_type; + +extern void common_hal_countio_counter_construct(countio_counter_obj_t *self, + const mcu_pin_obj_t *pin, countio_edge_t edge, digitalio_pull_t pull); +extern void common_hal_countio_counter_deinit(countio_counter_obj_t *self); +extern bool common_hal_countio_counter_deinited(countio_counter_obj_t *self); +extern mp_int_t common_hal_countio_counter_get_count(countio_counter_obj_t *self); +extern void common_hal_countio_counter_set_count(countio_counter_obj_t *self, + mp_int_t new_count); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO_COUNTER_H diff --git a/circuitpython/shared-bindings/countio/Edge.c b/circuitpython/shared-bindings/countio/Edge.c new file mode 100644 index 0000000..980b3a3 --- /dev/null +++ b/circuitpython/shared-bindings/countio/Edge.c @@ -0,0 +1,67 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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 "py/obj.h" +#include "py/enum.h" +#include "py/runtime.h" + +#include "shared-bindings/countio/Edge.h" + +MAKE_ENUM_VALUE(countio_edge_type, edge, RISE, EDGE_RISE); +MAKE_ENUM_VALUE(countio_edge_type, edge, FALL, EDGE_FALL); +MAKE_ENUM_VALUE(countio_edge_type, edge, RISE_AND_FALL, EDGE_RISE_AND_FALL); + +//| class Edge: +//| """Enumerates which signal transitions can be counted.""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define which signal transitions to count.""" +//| ... +//| +//| RISE: Edge +//| """Count the rising edges.""" +//| +//| FALL: Edge +//| """Count the falling edges.""" +//| +//| RISE_AND_FALL: Edge +//| """Count the rising and falling edges.""" +//| +MAKE_ENUM_MAP(countio_edge) { + MAKE_ENUM_MAP_ENTRY(edge, RISE), + MAKE_ENUM_MAP_ENTRY(edge, FALL), + MAKE_ENUM_MAP_ENTRY(edge, RISE_AND_FALL), +}; + +STATIC MP_DEFINE_CONST_DICT(countio_edge_locals_dict, countio_edge_locals_table); + +MAKE_PRINTER(countio, countio_edge); + +MAKE_ENUM_TYPE(countio, Edge, countio_edge); + +countio_edge_t validate_edge(mp_obj_t obj, qstr arg_name) { + return cp_enum_value(&countio_edge_type, mp_arg_validate_type(obj, &countio_edge_type, arg_name)); +} diff --git a/circuitpython/shared-bindings/countio/Edge.h b/circuitpython/shared-bindings/countio/Edge.h new file mode 100644 index 0000000..ca4cddb --- /dev/null +++ b/circuitpython/shared-bindings/countio/Edge.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 an Halbertfor 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_COUNTIO_EDGE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO_EDGE_H + +#include "py/enum.h" +#include "py/obj.h" + +typedef enum _countio_edge_t { + EDGE_RISE, + EDGE_FALL, + EDGE_RISE_AND_FALL, +} countio_edge_t; + +extern const mp_obj_type_t countio_edge_type; +extern const cp_enum_obj_t edge_FALL_obj; + +countio_edge_t validate_edge(mp_obj_t obj, qstr arg_name); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO_EDGE_H diff --git a/circuitpython/shared-bindings/countio/__init__.c b/circuitpython/shared-bindings/countio/__init__.c new file mode 100644 index 0000000..d72ed9f --- /dev/null +++ b/circuitpython/shared-bindings/countio/__init__.c @@ -0,0 +1,35 @@ + +#include <stdint.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/countio/__init__.h" +#include "shared-bindings/countio/Counter.h" +#include "shared-bindings/countio/Edge.h" + +//| """Support for edge counting +//| +//| The `countio` module contains logic to read and count edge transistions +//| +//| 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.""" +//| + +STATIC const mp_rom_map_elem_t countio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_countio) }, + { MP_ROM_QSTR(MP_QSTR_Counter), MP_ROM_PTR(&countio_counter_type) }, + { MP_ROM_QSTR(MP_QSTR_Edge), MP_ROM_PTR(&countio_edge_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(countio_module_globals, countio_module_globals_table); + +const mp_obj_module_t countio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&countio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_countio, countio_module, CIRCUITPY_COUNTIO); diff --git a/circuitpython/shared-bindings/countio/__init__.h b/circuitpython/shared-bindings/countio/__init__.h new file mode 100644 index 0000000..35ae9f0 --- /dev/null +++ b/circuitpython/shared-bindings/countio/__init__.h @@ -0,0 +1,9 @@ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_COUNTIO___INIT___H diff --git a/circuitpython/shared-bindings/digitalio/DigitalInOut.c b/circuitpython/shared-bindings/digitalio/DigitalInOut.c new file mode 100644 index 0000000..37093ad --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/DigitalInOut.c @@ -0,0 +1,363 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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 <string.h> + +#include "shared/runtime/context_manager_helpers.h" + +#include "py/nlr.h" +#include "py/objtype.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/mphal.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class DigitalInOut: +//| """Digital input and output +//| +//| A DigitalInOut is used to digitally control I/O pins. For analog control of +//| a pin, see the :py:class:`analogio.AnalogIn` and +//| :py:class:`analogio.AnalogOut` classes.""" +//| +//| def __init__(self, pin: microcontroller.Pin) -> None: +//| """Create a new DigitalInOut object associated with the pin. Defaults to input +//| with no pull. Use :py:meth:`switch_to_input` and +//| :py:meth:`switch_to_output` to change the direction. +//| +//| :param ~microcontroller.Pin pin: The pin to control""" +//| ... +//| +STATIC mp_obj_t digitalio_digitalinout_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + digitalio_digitalinout_obj_t *self = m_new_obj(digitalio_digitalinout_obj_t); + self->base.type = &digitalio_digitalinout_type; + + const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[0]); + common_hal_digitalio_digitalinout_construct(self, pin); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Turn off the DigitalInOut and release the pin for other use.""" +//| ... +//| +STATIC mp_obj_t digitalio_digitalinout_obj_deinit(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_digitalio_digitalinout_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_deinit_obj, digitalio_digitalinout_obj_deinit); + +//| def __enter__(self) -> DigitalInOut: +//| """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 digitalio_digitalinout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_digitalio_digitalinout_deinit(MP_OBJ_TO_PTR(args[0])); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(digitalio_digitalinout_obj___exit___obj, 4, 4, digitalio_digitalinout_obj___exit__); + +STATIC void check_for_deinit(digitalio_digitalinout_obj_t *self) { + if (common_hal_digitalio_digitalinout_deinited(self)) { + raise_deinited_error(); + } +} + +//| def switch_to_output(self, value: bool = False, drive_mode: DriveMode = DriveMode.PUSH_PULL) -> None: +//| """Set the drive mode and value and then switch to writing out digital +//| values. +//| +//| :param bool value: default value to set upon switching +//| :param ~digitalio.DriveMode drive_mode: drive mode for the output +//| """ +//| ... +//| +STATIC mp_obj_t digitalio_digitalinout_switch_to_output(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_value, ARG_drive_mode }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_value, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_drive_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&digitalio_drive_mode_push_pull_obj)} }, + }; + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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); + + digitalio_drive_mode_t drive_mode = DRIVE_MODE_PUSH_PULL; + if (args[ARG_drive_mode].u_rom_obj == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + drive_mode = DRIVE_MODE_OPEN_DRAIN; + } + // do the transfer + digitalinout_result_t result = common_hal_digitalio_digitalinout_switch_to_output(self, args[ARG_value].u_bool, drive_mode); + if (result == DIGITALINOUT_INPUT_ONLY) { + mp_raise_NotImplementedError(translate("Pin is input only")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(digitalio_digitalinout_switch_to_output_obj, 1, digitalio_digitalinout_switch_to_output); + +//| def switch_to_input(self, pull: Optional[Pull] = None) -> None: +//| """Set the pull and then switch to read in digital values. +//| +//| :param Pull pull: pull configuration for the input +//| +//| Example usage:: +//| +//| import digitalio +//| import board +//| +//| switch = digitalio.DigitalInOut(board.SLIDE_SWITCH) +//| switch.switch_to_input(pull=digitalio.Pull.UP) +//| # Or, after switch_to_input +//| switch.pull = digitalio.Pull.UP +//| print(switch.value)""" +//| ... +//| +STATIC mp_obj_t digitalio_digitalinout_switch_to_input(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_pull }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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); + + common_hal_digitalio_digitalinout_switch_to_input(self, validate_pull(args[ARG_pull].u_rom_obj, MP_QSTR_pull)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(digitalio_digitalinout_switch_to_input_obj, 1, digitalio_digitalinout_switch_to_input); + +//| direction: Direction +//| """The direction of the pin. +//| +//| Setting this will use the defaults from the corresponding +//| :py:meth:`switch_to_input` or :py:meth:`switch_to_output` method. If +//| you want to set pull, value or drive mode prior to switching, then use +//| those methods instead.""" +//| +typedef struct { + mp_obj_base_t base; +} digitalio_digitalio_direction_obj_t; +extern const digitalio_digitalio_direction_obj_t digitalio_digitalio_direction_in_obj; +extern const digitalio_digitalio_direction_obj_t digitalio_digitalio_direction_out_obj; + +STATIC mp_obj_t digitalio_digitalinout_obj_get_direction(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + digitalio_direction_t direction = common_hal_digitalio_digitalinout_get_direction(self); + if (direction == DIRECTION_INPUT) { + return (mp_obj_t)&digitalio_direction_input_obj; + } + return (mp_obj_t)&digitalio_direction_output_obj; +} +MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_direction_obj, digitalio_digitalinout_obj_get_direction); + +STATIC mp_obj_t digitalio_digitalinout_obj_set_direction(mp_obj_t self_in, mp_obj_t value) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (value == MP_ROM_PTR(&digitalio_direction_input_obj)) { + common_hal_digitalio_digitalinout_switch_to_input(self, PULL_NONE); + } else if (value == MP_ROM_PTR(&digitalio_direction_output_obj)) { + digitalinout_result_t result = common_hal_digitalio_digitalinout_switch_to_output(self, false, DRIVE_MODE_PUSH_PULL); + if (result == DIGITALINOUT_INPUT_ONLY) { + mp_raise_NotImplementedError(translate("Pin is input only")); + } + } else { + mp_raise_ValueError(translate("Invalid direction.")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(digitalio_digitalinout_set_direction_obj, digitalio_digitalinout_obj_set_direction); + +MP_PROPERTY_GETSET(digitalio_digitalio_direction_obj, + (mp_obj_t)&digitalio_digitalinout_get_direction_obj, + (mp_obj_t)&digitalio_digitalinout_set_direction_obj); + +//| value: bool +//| """The digital logic level of the pin.""" +//| +STATIC mp_obj_t digitalio_digitalinout_obj_get_value(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + bool value = common_hal_digitalio_digitalinout_get_value(self); + return mp_obj_new_bool(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_value_obj, digitalio_digitalinout_obj_get_value); + +STATIC mp_obj_t digitalio_digitalinout_obj_set_value(mp_obj_t self_in, mp_obj_t value) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_digitalio_digitalinout_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(translate("Cannot set value when direction is input.")); + return mp_const_none; + } + common_hal_digitalio_digitalinout_set_value(self, mp_obj_is_true(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(digitalio_digitalinout_set_value_obj, digitalio_digitalinout_obj_set_value); + +MP_PROPERTY_GETSET(digitalio_digitalinout_value_obj, + (mp_obj_t)&digitalio_digitalinout_get_value_obj, + (mp_obj_t)&digitalio_digitalinout_set_value_obj); + +//| drive_mode: DriveMode +//| """The pin drive mode. One of: +//| +//| - `digitalio.DriveMode.PUSH_PULL` +//| - `digitalio.DriveMode.OPEN_DRAIN`""" +//| +STATIC mp_obj_t digitalio_digitalinout_obj_get_drive_mode(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_digitalio_digitalinout_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(translate("Drive mode not used when direction is input.")); + return mp_const_none; + } + digitalio_drive_mode_t drive_mode = common_hal_digitalio_digitalinout_get_drive_mode(self); + if (drive_mode == DRIVE_MODE_PUSH_PULL) { + return (mp_obj_t)&digitalio_drive_mode_push_pull_obj; + } + return (mp_obj_t)&digitalio_drive_mode_open_drain_obj; +} +MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_drive_mode_obj, digitalio_digitalinout_obj_get_drive_mode); + +STATIC mp_obj_t digitalio_digitalinout_obj_set_drive_mode(mp_obj_t self_in, mp_obj_t drive_mode) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_digitalio_digitalinout_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(translate("Drive mode not used when direction is input.")); + return mp_const_none; + } + digitalio_drive_mode_t c_drive_mode = DRIVE_MODE_PUSH_PULL; + if (drive_mode == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + c_drive_mode = DRIVE_MODE_OPEN_DRAIN; + } + common_hal_digitalio_digitalinout_set_drive_mode(self, c_drive_mode); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(digitalio_digitalinout_set_drive_mode_obj, digitalio_digitalinout_obj_set_drive_mode); + +MP_PROPERTY_GETSET(digitalio_digitalio_drive_mode_obj, + (mp_obj_t)&digitalio_digitalinout_get_drive_mode_obj, + (mp_obj_t)&digitalio_digitalinout_set_drive_mode_obj); + +//| pull: Optional[Pull] +//| """The pin pull direction. One of: +//| +//| - `digitalio.Pull.UP` +//| - `digitalio.Pull.DOWN` +//| - `None` +//| +//| :raises AttributeError: if `direction` is :py:data:`~digitalio.Direction.OUTPUT`.""" +//| +STATIC mp_obj_t digitalio_digitalinout_obj_get_pull(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_digitalio_digitalinout_get_direction(self) == DIRECTION_OUTPUT) { + mp_raise_AttributeError(translate("Pull not used when direction is output.")); + return mp_const_none; + } + digitalio_pull_t pull = common_hal_digitalio_digitalinout_get_pull(self); + if (pull == PULL_UP) { + return MP_OBJ_FROM_PTR(&digitalio_pull_up_obj); + } else if (pull == PULL_DOWN) { + return MP_OBJ_FROM_PTR(&digitalio_pull_down_obj); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_pull_obj, digitalio_digitalinout_obj_get_pull); + +STATIC mp_obj_t digitalio_digitalinout_obj_set_pull(mp_obj_t self_in, mp_obj_t pull_obj) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_digitalio_digitalinout_get_direction(self) == DIRECTION_OUTPUT) { + mp_raise_AttributeError(translate("Pull not used when direction is output.")); + return mp_const_none; + } + + common_hal_digitalio_digitalinout_set_pull(self, validate_pull(pull_obj, MP_QSTR_pull)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(digitalio_digitalinout_set_pull_obj, digitalio_digitalinout_obj_set_pull); + +MP_PROPERTY_GETSET(digitalio_digitalio_pull_obj, + (mp_obj_t)&digitalio_digitalinout_get_pull_obj, + (mp_obj_t)&digitalio_digitalinout_set_pull_obj); + +STATIC const mp_rom_map_elem_t digitalio_digitalinout_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&digitalio_digitalinout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&digitalio_digitalinout_obj___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_switch_to_output), MP_ROM_PTR(&digitalio_digitalinout_switch_to_output_obj) }, + { MP_ROM_QSTR(MP_QSTR_switch_to_input), MP_ROM_PTR(&digitalio_digitalinout_switch_to_input_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_direction), MP_ROM_PTR(&digitalio_digitalio_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&digitalio_digitalinout_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_drive_mode), MP_ROM_PTR(&digitalio_digitalio_drive_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_pull), MP_ROM_PTR(&digitalio_digitalio_pull_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(digitalio_digitalinout_locals_dict, digitalio_digitalinout_locals_dict_table); + +const mp_obj_type_t digitalio_digitalinout_type = { + { &mp_type_type }, + .name = MP_QSTR_DigitalInOut, + .make_new = digitalio_digitalinout_make_new, + .locals_dict = (mp_obj_dict_t *)&digitalio_digitalinout_locals_dict, +}; + +// Helper for validating digitalio.DigitalInOut arguments +digitalio_digitalinout_obj_t *assert_digitalinout(mp_obj_t obj) { + if (!mp_obj_is_type(obj, &digitalio_digitalinout_type)) { + mp_raise_TypeError(translate("argument num/types mismatch")); + } + digitalio_digitalinout_obj_t *pin = MP_OBJ_TO_PTR(obj); + check_for_deinit(pin); + return pin; +} diff --git a/circuitpython/shared-bindings/digitalio/DigitalInOut.h b/circuitpython/shared-bindings/digitalio/DigitalInOut.h new file mode 100644 index 0000000..b6edbc6 --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/DigitalInOut.h @@ -0,0 +1,70 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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_DIGITALIO_DIGITALINOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_DIGITALINOUT_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" + +extern const mp_obj_type_t digitalio_digitalinout_type; + +typedef enum { + DIGITALINOUT_OK, + DIGITALINOUT_PIN_BUSY, + DIGITALINOUT_INPUT_ONLY +} digitalinout_result_t; + +typedef enum { + DIGITALINOUT_REG_READ, + DIGITALINOUT_REG_WRITE, + DIGITALINOUT_REG_SET, + DIGITALINOUT_REG_RESET, + DIGITALINOUT_REG_TOGGLE, +} digitalinout_reg_op_t; + +digitalinout_result_t common_hal_digitalio_digitalinout_construct(digitalio_digitalinout_obj_t *self, const mcu_pin_obj_t *pin); +void common_hal_digitalio_digitalinout_deinit(digitalio_digitalinout_obj_t *self); +bool common_hal_digitalio_digitalinout_deinited(digitalio_digitalinout_obj_t *self); +void common_hal_digitalio_digitalinout_switch_to_input(digitalio_digitalinout_obj_t *self, digitalio_pull_t pull); +digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output(digitalio_digitalinout_obj_t *self, bool value, digitalio_drive_mode_t drive_mode); +digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(digitalio_digitalinout_obj_t *self); +void common_hal_digitalio_digitalinout_set_value(digitalio_digitalinout_obj_t *self, bool value); +bool common_hal_digitalio_digitalinout_get_value(digitalio_digitalinout_obj_t *self); +digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode(digitalio_digitalinout_obj_t *self, digitalio_drive_mode_t drive_mode); +digitalio_drive_mode_t common_hal_digitalio_digitalinout_get_drive_mode(digitalio_digitalinout_obj_t *self); +void common_hal_digitalio_digitalinout_set_pull(digitalio_digitalinout_obj_t *self, digitalio_pull_t pull); +digitalio_pull_t common_hal_digitalio_digitalinout_get_pull(digitalio_digitalinout_obj_t *self); +void common_hal_digitalio_digitalinout_never_reset(digitalio_digitalinout_obj_t *self); +digitalio_digitalinout_obj_t *assert_digitalinout(mp_obj_t obj); + +volatile uint32_t *common_hal_digitalio_digitalinout_get_reg(digitalio_digitalinout_obj_t *self, digitalinout_reg_op_t op, uint32_t *mask); +bool common_hal_digitalio_has_reg_op(digitalinout_reg_op_t op); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_DIGITALINOUT_H diff --git a/circuitpython/shared-bindings/digitalio/Direction.c b/circuitpython/shared-bindings/digitalio/Direction.c new file mode 100644 index 0000000..0ed18f8 --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/Direction.c @@ -0,0 +1,84 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 <string.h> + +#include "shared/runtime/context_manager_helpers.h" + +#include "py/nlr.h" +#include "py/objtype.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/mphal.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + +//| class Direction: +//| """Defines the direction of a digital pin""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define which direction the digital values are +//| going.""" +//| ... +//| +//| INPUT: Direction +//| """Read digital data in""" +//| +//| OUTPUT: Direction +//| """Write digital data out""" +//| +const mp_obj_type_t digitalio_direction_type; + +const digitalio_direction_obj_t digitalio_direction_input_obj = { + { &digitalio_direction_type }, +}; + +const digitalio_direction_obj_t digitalio_direction_output_obj = { + { &digitalio_direction_type }, +}; + +STATIC const mp_rom_map_elem_t digitalio_direction_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_INPUT), MP_ROM_PTR(&digitalio_direction_input_obj) }, + { MP_ROM_QSTR(MP_QSTR_OUTPUT), MP_ROM_PTR(&digitalio_direction_output_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(digitalio_direction_locals_dict, digitalio_direction_locals_dict_table); + +STATIC void digitalio_direction_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr direction = MP_QSTR_INPUT; + if (self_in == MP_ROM_PTR(&digitalio_direction_output_obj)) { + direction = MP_QSTR_OUTPUT; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_digitalio, MP_QSTR_Direction, direction); +} + +const mp_obj_type_t digitalio_direction_type = { + { &mp_type_type }, + .name = MP_QSTR_Direction, + .print = digitalio_direction_print, + .locals_dict = (mp_obj_dict_t *)&digitalio_direction_locals_dict, +}; diff --git a/circuitpython/shared-bindings/digitalio/Direction.h b/circuitpython/shared-bindings/digitalio/Direction.h new file mode 100644 index 0000000..17e1eda --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/Direction.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_DIGITALIO_DIRECTION_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_DIRECTION_H + +#include "py/obj.h" + +typedef enum { + DIRECTION_INPUT, + DIRECTION_OUTPUT +} digitalio_direction_t; +typedef struct { + mp_obj_base_t base; +} digitalio_direction_obj_t; + +extern const mp_obj_type_t digitalio_direction_type; + +extern const digitalio_direction_obj_t digitalio_direction_input_obj; +extern const digitalio_direction_obj_t digitalio_direction_output_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_DIRECTION_H diff --git a/circuitpython/shared-bindings/digitalio/DriveMode.c b/circuitpython/shared-bindings/digitalio/DriveMode.c new file mode 100644 index 0000000..c7c3400 --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/DriveMode.c @@ -0,0 +1,73 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "shared-bindings/digitalio/DriveMode.h" + +//| class DriveMode: +//| """Defines the drive mode of a digital pin""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define the drive mode used when outputting +//| digital values.""" +//| ... +//| +//| PUSH_PULL: DriveMode +//| """Output both high and low digital values""" +//| +//| OPEN_DRAIN: DriveMode +//| """Output low digital values but go into high z for digital high. This is +//| useful for i2c and other protocols that share a digital line.""" +//| +const mp_obj_type_t digitalio_drive_mode_type; + +const digitalio_drive_mode_obj_t digitalio_drive_mode_push_pull_obj = { + { &digitalio_drive_mode_type }, +}; + +const digitalio_drive_mode_obj_t digitalio_drive_mode_open_drain_obj = { + { &digitalio_drive_mode_type }, +}; + +STATIC const mp_rom_map_elem_t digitalio_drive_mode_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_PUSH_PULL), MP_ROM_PTR(&digitalio_drive_mode_push_pull_obj) }, + { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(digitalio_drive_mode_locals_dict, digitalio_drive_mode_locals_dict_table); + +STATIC void digitalio_drive_mode_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr drive_mode = MP_QSTR_PUSH_PULL; + if (self_in == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + drive_mode = MP_QSTR_OPEN_DRAIN; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_digitalio, MP_QSTR_DriveMode, drive_mode); +} + +const mp_obj_type_t digitalio_drive_mode_type = { + { &mp_type_type }, + .name = MP_QSTR_DriveMode, + .print = digitalio_drive_mode_print, + .locals_dict = (mp_obj_dict_t *)&digitalio_drive_mode_locals_dict, +}; diff --git a/circuitpython/shared-bindings/digitalio/DriveMode.h b/circuitpython/shared-bindings/digitalio/DriveMode.h new file mode 100644 index 0000000..01ecaa4 --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/DriveMode.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_DIGITALIO_DRIVEMODE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_DRIVEMODE_H + +#include "py/obj.h" + +typedef enum { + DRIVE_MODE_PUSH_PULL, + DRIVE_MODE_OPEN_DRAIN +} digitalio_drive_mode_t; + +typedef struct { + mp_obj_base_t base; +} digitalio_drive_mode_obj_t; + +extern const mp_obj_type_t digitalio_drive_mode_type; + +extern const digitalio_drive_mode_obj_t digitalio_drive_mode_push_pull_obj; +extern const digitalio_drive_mode_obj_t digitalio_drive_mode_open_drain_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_DRIVEMODE_H diff --git a/circuitpython/shared-bindings/digitalio/Pull.c b/circuitpython/shared-bindings/digitalio/Pull.c new file mode 100644 index 0000000..4db68dd --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/Pull.c @@ -0,0 +1,87 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "py/runtime.h" +#include "shared-bindings/digitalio/Pull.h" + +//| class Pull: +//| """Defines the pull of a digital input pin""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define the pull value, if any, used while reading +//| digital values in.""" +//| ... +//| +//| UP: Pull +//| """When the input line isn't being driven the pull up can pull the state +//| of the line high so it reads as true.""" +//| +//| DOWN: Pull +//| """When the input line isn't being driven the pull down can pull the +//| state of the line low so it reads as false.""" +//| +const mp_obj_type_t digitalio_pull_type; + +const digitalio_pull_obj_t digitalio_pull_up_obj = { + { &digitalio_pull_type }, +}; + +const digitalio_pull_obj_t digitalio_pull_down_obj = { + { &digitalio_pull_type }, +}; + +STATIC const mp_rom_map_elem_t digitalio_pull_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_UP), MP_ROM_PTR(&digitalio_pull_up_obj) }, + { MP_ROM_QSTR(MP_QSTR_DOWN), MP_ROM_PTR(&digitalio_pull_down_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(digitalio_pull_locals_dict, digitalio_pull_locals_dict_table); + +STATIC void digitalio_pull_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr pull = MP_QSTR_UP; + if (self_in == MP_ROM_PTR(&digitalio_pull_down_obj)) { + pull = MP_QSTR_DOWN; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_digitalio, MP_QSTR_Pull, pull); +} + +const mp_obj_type_t digitalio_pull_type = { + { &mp_type_type }, + .name = MP_QSTR_Pull, + .print = digitalio_pull_print, + .locals_dict = (mp_obj_dict_t *)&digitalio_pull_locals_dict, +}; + +digitalio_pull_t validate_pull(mp_rom_obj_t obj, qstr arg_name) { + if (obj == MP_ROM_PTR(&digitalio_pull_up_obj)) { + return PULL_UP; + } else if (obj == MP_ROM_PTR(&digitalio_pull_down_obj)) { + return PULL_DOWN; + } + if (obj == MP_ROM_NONE) { + return PULL_NONE; + } + mp_raise_TypeError_varg(translate("%q must be of type %q or None"), arg_name, MP_QSTR_Pull); +} diff --git a/circuitpython/shared-bindings/digitalio/Pull.h b/circuitpython/shared-bindings/digitalio/Pull.h new file mode 100644 index 0000000..f4f6c42 --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/Pull.h @@ -0,0 +1,48 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_DIGITALIO_PULL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_PULL_H + +#include "py/obj.h" + +typedef enum _digitalio_pull_t { + PULL_NONE, + PULL_UP, + PULL_DOWN +} digitalio_pull_t; + +extern const mp_obj_type_t digitalio_pull_type; + +typedef struct { + mp_obj_base_t base; +} digitalio_pull_obj_t; +extern const digitalio_pull_obj_t digitalio_pull_up_obj; +extern const digitalio_pull_obj_t digitalio_pull_down_obj; + +digitalio_pull_t validate_pull(mp_rom_obj_t obj, qstr arg_name); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO_PULL_H diff --git a/circuitpython/shared-bindings/digitalio/__init__.c b/circuitpython/shared-bindings/digitalio/__init__.c new file mode 100644 index 0000000..9a9db69 --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/__init__.c @@ -0,0 +1,100 @@ +/* + * 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/digitalio/__init__.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" + +#include "py/runtime.h" + +//| """Basic digital pin support +//| +//| The `digitalio` module contains classes to provide access to basic digital IO. +//| +//| 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 digitalio +//| import board +//| +//| pin = digitalio.DigitalInOut(board.LED) +//| print(pin.value) +//| +//| This example will initialize the the device, read +//| :py:data:`~digitalio.DigitalInOut.value` and then +//| :py:meth:`~digitalio.DigitalInOut.deinit` the hardware. +//| +//| Here is blinky:: +//| +//| import time +//| import digitalio +//| import board +//| +//| led = digitalio.DigitalInOut(board.LED) +//| led.direction = digitalio.Direction.OUTPUT +//| while True: +//| led.value = True +//| time.sleep(0.1) +//| led.value = False +//| time.sleep(0.1) +//| +//| For the essentials of `digitalio`, see the `CircuitPython Essentials +//| Learn guide <https://learn.adafruit.com/circuitpython-essentials/circuitpython-digital-in-out>`_ +//| +//| For more information on using `digitalio`, see `this additional Learn guide +//| <https://learn.adafruit.com/circuitpython-digital-inputs-and-outputs>`_ +//| """ + +STATIC const mp_rom_map_elem_t digitalio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_digitalio) }, + { MP_ROM_QSTR(MP_QSTR_DigitalInOut), MP_ROM_PTR(&digitalio_digitalinout_type) }, + + // Enum-like Classes. + { MP_ROM_QSTR(MP_QSTR_Direction), MP_ROM_PTR(&digitalio_direction_type) }, + { MP_ROM_QSTR(MP_QSTR_DriveMode), MP_ROM_PTR(&digitalio_drive_mode_type) }, + { MP_ROM_QSTR(MP_QSTR_Pull), MP_ROM_PTR(&digitalio_pull_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(digitalio_module_globals, digitalio_module_globals_table); + +const mp_obj_module_t digitalio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&digitalio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_digitalio, digitalio_module, CIRCUITPY_DIGITALIO); diff --git a/circuitpython/shared-bindings/digitalio/__init__.h b/circuitpython/shared-bindings/digitalio/__init__.h new file mode 100644 index 0000000..8ab7f78 --- /dev/null +++ b/circuitpython/shared-bindings/digitalio/__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_DIGITALIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DIGITALIO___INIT___H diff --git a/circuitpython/shared-bindings/displayio/Bitmap.c b/circuitpython/shared-bindings/displayio/Bitmap.c new file mode 100644 index 0000000..3af152f --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Bitmap.c @@ -0,0 +1,370 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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 "shared-bindings/displayio/Bitmap.h" +#include "shared-module/displayio/Bitmap.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Bitmap: +//| """Stores values of a certain size in a 2D array +//| +//| Bitmaps can be treated as read-only buffers. If the number of bits in a pixel is 8, 16, or 32; and the number of bytes +//| per row is a multiple of 4, then the resulting memoryview will correspond directly with the bitmap's contents. Otherwise, +//| the bitmap data is packed into the memoryview with unspecified padding. +//| +//| A Bitmap can be treated as a buffer, allowing its content to be +//| viewed and modified using e.g., with ``ulab.numpy.frombuffer``, +//| but the `displayio.Bitmap.dirty` method must be used to inform +//| displayio when a bitmap was modified through the buffer interface. +//| +//| `bitmaptools.arrayblit` can also be useful to move data efficiently +//| into a Bitmap. +//| """ +//| +//| def __init__(self, width: int, height: int, value_count: int) -> None: +//| """Create a Bitmap object with the given fixed size. Each pixel stores a value that is used to +//| index into a corresponding palette. This enables differently colored sprites to share the +//| underlying Bitmap. value_count is used to minimize the memory used to store the Bitmap. +//| +//| :param int width: The number of values wide +//| :param int height: The number of values high +//| :param int value_count: The number of possible pixel values.""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 3, 3, false); + uint32_t width = mp_obj_get_int(all_args[0]); + uint32_t height = mp_obj_get_int(all_args[1]); + uint32_t value_count = mp_obj_get_int(all_args[2]); + uint32_t bits = 1; + + if (value_count == 0) { + mp_raise_ValueError(translate("value_count must be > 0")); + } + while ((value_count - 1) >> bits) { + if (bits < 8) { + bits <<= 1; + } else { + bits += 8; + } + } + + displayio_bitmap_t *self = m_new_obj(displayio_bitmap_t); + self->base.type = &displayio_bitmap_type; + common_hal_displayio_bitmap_construct(self, width, height, bits); + + return MP_OBJ_FROM_PTR(self); +} +//| width: int +//| """Width of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_bitmap_obj_get_width(mp_obj_t self_in) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_bitmap_get_width(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_bitmap_get_width_obj, displayio_bitmap_obj_get_width); + +MP_PROPERTY_GETTER(displayio_bitmap_width_obj, + (mp_obj_t)&displayio_bitmap_get_width_obj); + +//| height: int +//| """Height of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_bitmap_obj_get_height(mp_obj_t self_in) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_bitmap_get_height(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_bitmap_get_height_obj, displayio_bitmap_obj_get_height); + +MP_PROPERTY_GETTER(displayio_bitmap_height_obj, + (mp_obj_t)&displayio_bitmap_get_height_obj); + +//| def __getitem__(self, index: Union[Tuple[int, int], int]) -> int: +//| """Returns the value at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| print(bitmap[0,1])""" +//| ... +//| +//| def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None: +//| """Sets the value at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| bitmap[0,1] = 3""" +//| ... +//| +STATIC mp_obj_t bitmap_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value_obj) { + if (value_obj == mp_const_none) { + // delete item + mp_raise_AttributeError(translate("Cannot delete values")); + return mp_const_none; + } + + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + // TODO(tannewt): Implement subscr after slices support start, stop and step tuples. + mp_raise_NotImplementedError(translate("Slices not supported")); + return mp_const_none; + } + + uint16_t x = 0; + uint16_t y = 0; + if (mp_obj_is_small_int(index_obj)) { + mp_int_t i = MP_OBJ_SMALL_INT_VALUE(index_obj); + uint16_t width = common_hal_displayio_bitmap_get_width(self); + x = i % width; + y = i / width; + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(index_obj, 2, &items); + x = mp_obj_get_int(items[0]); + y = mp_obj_get_int(items[1]); + if (x >= common_hal_displayio_bitmap_get_width(self) || y >= common_hal_displayio_bitmap_get_height(self)) { + mp_raise_IndexError(translate("pixel coordinates out of bounds")); + } + } + + if (value_obj == MP_OBJ_SENTINEL) { + // load + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_bitmap_get_pixel(self, x, y)); + } else { + mp_uint_t value = (mp_uint_t)mp_obj_get_int(value_obj); + if ((value >> common_hal_displayio_bitmap_get_bits_per_value(self)) != 0) { + mp_raise_ValueError(translate("pixel value requires too many bits")); + } + common_hal_displayio_bitmap_set_pixel(self, x, y, value); + } + return mp_const_none; +} + +//| def blit(self, x: int, y: int, source_bitmap: Bitmap, *, x1: int, y1: int, x2: int, y2: int, skip_index: int) -> None: +//| """Inserts the source_bitmap region defined by rectangular boundaries +//| (x1,y1) and (x2,y2) into the bitmap at the specified (x,y) location. +//| +//| :param int x: Horizontal pixel location in bitmap where source_bitmap upper-left +//| corner will be placed +//| :param int y: Vertical pixel location in bitmap where source_bitmap upper-left +//| corner will be placed +//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied +//| :param int x1: Minimum x-value for rectangular bounding box to be copied from the source bitmap +//| :param int y1: Minimum y-value for rectangular bounding box to be copied from the source bitmap +//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be copied from the source bitmap +//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be copied from the source bitmap +//| :param int skip_index: bitmap palette index in the source that will not be copied, +//| set to None to copy all pixels""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_obj_blit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_x, ARG_y, ARG_source, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_index}; + static const mp_arg_t allowed_args[] = { + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_x1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + {MP_QSTR_y1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + {MP_QSTR_x2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->width + {MP_QSTR_y2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->height + {MP_QSTR_skip_index, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + }; + 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); + + displayio_bitmap_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + + displayio_bitmap_t *source = mp_arg_validate_type(args[ARG_source].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap); + + + // ensure that the target bitmap (self) has at least as many `bits_per_value` as the source + if (self->bits_per_value < source->bits_per_value) { + mp_raise_ValueError(translate("source palette too large")); + } + + int16_t x1 = args[ARG_x1].u_int; + int16_t y1 = args[ARG_y1].u_int; + int16_t x2, y2; + // if x2 or y2 is None, then set as the maximum size of the source bitmap + if (args[ARG_x2].u_obj == mp_const_none) { + x2 = source->width; + } else { + x2 = mp_obj_get_int(args[ARG_x2].u_obj); + } + // int16_t y2; + if (args[ARG_y2].u_obj == mp_const_none) { + y2 = source->height; + } else { + y2 = mp_obj_get_int(args[ARG_y2].u_obj); + } + + // Check x,y are within self (target) bitmap boundary + if ((x < 0) || (y < 0) || (x > self->width) || (y > self->height)) { + mp_raise_ValueError(translate("out of range of target")); + } + // Check x1,y1,x2,y2 are within source bitmap boundary + if ((x1 < 0) || (x1 > source->width) || + (y1 < 0) || (y1 > source->height) || + (x2 < 0) || (x2 > source->width) || + (y2 < 0) || (y2 > source->height)) { + mp_raise_ValueError(translate("out of range of source")); + } + + // Ensure x1 < x2 and y1 < y2 + if (x1 > x2) { + int16_t temp = x2; + x2 = x1; + x1 = temp; + } + if (y1 > y2) { + int16_t temp = y2; + y2 = y1; + y1 = temp; + } + + uint32_t skip_index; + bool skip_index_none; // flag whether skip_value was None + + if (args[ARG_skip_index].u_obj == mp_const_none) { + skip_index = 0; + skip_index_none = true; + } else { + skip_index = mp_obj_get_int(args[ARG_skip_index].u_obj); + skip_index_none = false; + } + + common_hal_displayio_bitmap_blit(self, x, y, source, x1, y1, x2, y2, skip_index, skip_index_none); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_bitmap_blit_obj, 1, displayio_bitmap_obj_blit); + +//| def fill(self, value: int) -> None: +//| """Fills the bitmap with the supplied palette index value.""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_obj_fill(mp_obj_t self_in, mp_obj_t value_obj) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + mp_uint_t value = (mp_uint_t)mp_obj_get_int(value_obj); + if ((value >> common_hal_displayio_bitmap_get_bits_per_value(self)) != 0) { + mp_raise_ValueError(translate("pixel value requires too many bits")); + } + common_hal_displayio_bitmap_fill(self, value); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_bitmap_fill_obj, displayio_bitmap_obj_fill); + +//| def dirty(self, x1: int=0, y1: int=0, x2: int=-1, y2:int = -1) -> None: +//| """Inform displayio of bitmap updates done via the buffer +//| protocol. +//| +//| :param int x1: Minimum x-value for rectangular bounding box to be considered as modified +//| :param int y1: Minimum y-value for rectangular bounding box to be considered as modified +//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be considered as modified +//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be considered as modified +//| +//| If x1 or y1 are not specified, they are taken as 0. If x2 or y2 +//| are not specified, or are given as -1, they are taken as the width +//| and height of the image. Thus, calling dirty() with the +//| default arguments treats the whole bitmap as modified. +//| +//| When a bitmap is modified through the buffer protocol, the +//| display will not be properly updated unless the bitmap is +//| notified of the "dirty rectangle" that encloses all modified +//| pixels.""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_obj_dirty(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(pos_args[0]); + enum { ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_x1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_x2, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_y2, MP_ARG_INT, {.u_int = -1} }, + }; + 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); + + displayio_area_t dirty_area = { + .x1 = args[ARG_x1].u_int, + .y1 = args[ARG_y1].u_int, + .x2 = args[ARG_x2].u_int == -1 ? self->width : args[ARG_x2].u_int, + .y2 = args[ARG_y2].u_int == -1 ? self->height : args[ARG_y2].u_int, + }; + + displayio_bitmap_set_dirty_area(self, &dirty_area); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_bitmap_dirty_obj, 0, displayio_bitmap_obj_dirty); + +STATIC const mp_rom_map_elem_t displayio_bitmap_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_bitmap_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_bitmap_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&displayio_bitmap_blit_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&displayio_bitmap_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_dirty), MP_ROM_PTR(&displayio_bitmap_dirty_obj) }, + +}; +STATIC MP_DEFINE_CONST_DICT(displayio_bitmap_locals_dict, displayio_bitmap_locals_dict_table); + +// (the get_buffer protocol returns 0 for success, 1 for failure) +STATIC mp_int_t bitmap_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_displayio_bitmap_get_buffer(self, bufinfo, flags); +} + +const mp_obj_type_t displayio_bitmap_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Bitmap, + .make_new = displayio_bitmap_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_bitmap_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = bitmap_subscr, + .buffer_p = { .get_buffer = bitmap_get_buffer }, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/Bitmap.h b/circuitpython/shared-bindings/displayio/Bitmap.h new file mode 100644 index 0000000..4580475 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Bitmap.h @@ -0,0 +1,50 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_DISPLAYIO_BITMAP_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_BITMAP_H + +#include "shared-module/displayio/Bitmap.h" + +extern const mp_obj_type_t displayio_bitmap_type; + +void common_hal_displayio_bitmap_construct(displayio_bitmap_t *self, uint32_t width, + uint32_t height, uint32_t bits_per_value); + +void common_hal_displayio_bitmap_load_row(displayio_bitmap_t *self, uint16_t y, uint8_t *data, + uint16_t len); +uint16_t common_hal_displayio_bitmap_get_height(displayio_bitmap_t *self); +uint16_t common_hal_displayio_bitmap_get_width(displayio_bitmap_t *self); +uint32_t common_hal_displayio_bitmap_get_bits_per_value(displayio_bitmap_t *self); +void common_hal_displayio_bitmap_set_pixel(displayio_bitmap_t *bitmap, int16_t x, int16_t y, uint32_t value); +void common_hal_displayio_bitmap_blit(displayio_bitmap_t *self, int16_t x, int16_t y, displayio_bitmap_t *source, + int16_t x1, int16_t y1, int16_t x2, int16_t y2, + uint32_t skip_index, bool skip_index_none); +uint32_t common_hal_displayio_bitmap_get_pixel(displayio_bitmap_t *bitmap, int16_t x, int16_t y); +void common_hal_displayio_bitmap_fill(displayio_bitmap_t *bitmap, uint32_t value); +int common_hal_displayio_bitmap_get_buffer(displayio_bitmap_t *self, mp_buffer_info_t *bufinfo, mp_uint_t flags); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_BITMAP_H diff --git a/circuitpython/shared-bindings/displayio/ColorConverter.c b/circuitpython/shared-bindings/displayio/ColorConverter.c new file mode 100644 index 0000000..18b8866 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/ColorConverter.c @@ -0,0 +1,152 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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 "shared-bindings/displayio/ColorConverter.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/enum.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class ColorConverter: +//| """Converts one color format to another.""" +//| +//| def __init__(self, *, input_colorspace: Colorspace=Colorspace.RGB888, dither: bool = False) -> None: +//| """Create a ColorConverter object to convert color formats. +//| +//| :param Colorspace colorspace: The source colorspace, one of the Colorspace constants +//| :param bool dither: Adds random noise to dither the output image""" +//| ... +//| + +STATIC mp_obj_t displayio_colorconverter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_dither, ARG_input_colorspace }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_dither, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_input_colorspace, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = (void *)&displayio_colorspace_RGB888_obj} }, + }; + 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); + + displayio_colorconverter_t *self = m_new_obj(displayio_colorconverter_t); + self->base.type = &displayio_colorconverter_type; + + common_hal_displayio_colorconverter_construct(self, args[ARG_dither].u_bool, (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_input_colorspace].u_obj)); + + return MP_OBJ_FROM_PTR(self); +} + +//| def convert(self, color: int) -> int: +//| """Converts the given color to RGB565 according to the Colorspace""" +//| ... +//| +STATIC mp_obj_t displayio_colorconverter_obj_convert(mp_obj_t self_in, mp_obj_t color_obj) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t color; + if (!mp_obj_get_int_maybe(color_obj, &color)) { + mp_raise_ValueError(translate("color should be an int")); + } + _displayio_colorspace_t colorspace; + colorspace.depth = 16; + uint32_t output_color; + common_hal_displayio_colorconverter_convert(self, &colorspace, color, &output_color); + return MP_OBJ_NEW_SMALL_INT(output_color); +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_convert_obj, displayio_colorconverter_obj_convert); + +//| dither: bool +//| """When `True` the ColorConverter dithers the output by adding random noise when +//| truncating to display bitdepth""" +//| +STATIC mp_obj_t displayio_colorconverter_obj_get_dither(mp_obj_t self_in) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_displayio_colorconverter_get_dither(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_colorconverter_get_dither_obj, displayio_colorconverter_obj_get_dither); + +STATIC mp_obj_t displayio_colorconverter_obj_set_dither(mp_obj_t self_in, mp_obj_t dither) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_displayio_colorconverter_set_dither(self, mp_obj_is_true(dither)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_set_dither_obj, displayio_colorconverter_obj_set_dither); + +MP_PROPERTY_GETSET(displayio_colorconverter_dither_obj, + (mp_obj_t)&displayio_colorconverter_get_dither_obj, + (mp_obj_t)&displayio_colorconverter_set_dither_obj); + +//| def make_transparent(self, color: int) -> None: +//| """Set the transparent color or index for the ColorConverter. This will +//| raise an Exception if there is already a selected transparent index. +//| +//| :param int color: The color to be transparent""" +//| +STATIC mp_obj_t displayio_colorconverter_make_transparent(mp_obj_t self_in, mp_obj_t transparent_color_obj) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t transparent_color = mp_obj_get_int(transparent_color_obj); + common_hal_displayio_colorconverter_make_transparent(self, transparent_color); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_make_transparent_obj, displayio_colorconverter_make_transparent); + +//| def make_opaque(self, color: int) -> None: +//| """Make the ColorConverter be opaque and have no transparent pixels. +//| +//| :param int color: [IGNORED] Use any value""" +//| +STATIC mp_obj_t displayio_colorconverter_make_opaque(mp_obj_t self_in, mp_obj_t transparent_color_obj) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t transparent_color = mp_obj_get_int(transparent_color_obj); + common_hal_displayio_colorconverter_make_opaque(self, transparent_color); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_make_opaque_obj, displayio_colorconverter_make_opaque); + +STATIC const mp_rom_map_elem_t displayio_colorconverter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_convert), MP_ROM_PTR(&displayio_colorconverter_convert_obj) }, + { MP_ROM_QSTR(MP_QSTR_dither), MP_ROM_PTR(&displayio_colorconverter_dither_obj) }, + { MP_ROM_QSTR(MP_QSTR_make_transparent), MP_ROM_PTR(&displayio_colorconverter_make_transparent_obj) }, + { MP_ROM_QSTR(MP_QSTR_make_opaque), MP_ROM_PTR(&displayio_colorconverter_make_opaque_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_colorconverter_locals_dict, displayio_colorconverter_locals_dict_table); + +const mp_obj_type_t displayio_colorconverter_type = { + { &mp_type_type }, + .name = MP_QSTR_ColorConverter, + .make_new = displayio_colorconverter_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_colorconverter_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/ColorConverter.h b/circuitpython/shared-bindings/displayio/ColorConverter.h new file mode 100644 index 0000000..12fa8da --- /dev/null +++ b/circuitpython/shared-bindings/displayio/ColorConverter.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_DISPLAYIO_COLORCONVERTER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_COLORCONVERTER_H + +#include "shared-bindings/displayio/__init__.h" +#include "shared-module/displayio/ColorConverter.h" + +#include "shared-module/displayio/Palette.h" + +extern const mp_obj_type_t displayio_colorconverter_type; + +void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither, displayio_colorspace_t input_colorspace); +void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *colorconverter, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color); +uint32_t displayio_colorconverter_convert_pixel(displayio_colorspace_t colorspace, uint32_t pixel); + +void common_hal_displayio_colorconverter_set_dither(displayio_colorconverter_t *self, bool dither); +bool common_hal_displayio_colorconverter_get_dither(displayio_colorconverter_t *self); + +void common_hal_displayio_colorconverter_make_transparent(displayio_colorconverter_t *self, uint32_t transparent_color); +void common_hal_displayio_colorconverter_make_opaque(displayio_colorconverter_t *self, uint32_t transparent_color); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_COLORCONVERTER_H diff --git a/circuitpython/shared-bindings/displayio/Colorspace.c b/circuitpython/shared-bindings/displayio/Colorspace.c new file mode 100644 index 0000000..3692dc2 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Colorspace.c @@ -0,0 +1,77 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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 "py/enum.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/displayio/__init__.h" + +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB888, DISPLAYIO_COLORSPACE_RGB888); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565, DISPLAYIO_COLORSPACE_RGB565); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565_SWAPPED, DISPLAYIO_COLORSPACE_RGB565_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555, DISPLAYIO_COLORSPACE_RGB555); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555_SWAPPED, DISPLAYIO_COLORSPACE_RGB555_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565, DISPLAYIO_COLORSPACE_BGR565); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565_SWAPPED, DISPLAYIO_COLORSPACE_BGR565_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555, DISPLAYIO_COLORSPACE_BGR555); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555_SWAPPED, DISPLAYIO_COLORSPACE_BGR555_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, L8, DISPLAYIO_COLORSPACE_L8); + +//| class Colorspace: +//| """The colorspace for a `ColorConverter` to operate in""" +//| +//| RGB888: Colorspace +//| """The standard 24-bit colorspace. Bits 0-7 are blue, 8-15 are green, and 16-24 are red. (0xRRGGBB)""" +//| +//| RGB565: Colorspace +//| """The standard 16-bit colorspace. Bits 0-4 are blue, bits 5-10 are green, and 11-15 are red (0bRRRRRGGGGGGBBBBB)""" +//| +//| RGB565_SWAPPED: Colorspace +//| """The swapped 16-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB565""" +//| +//| RGB555: Colorspace +//| """The standard 15-bit colorspace. Bits 0-4 are blue, bits 5-9 are green, and 11-14 are red. The top bit is ignored. (0bxRRRRRGGGGGBBBBB)""" +//| +//| RGB555_SWAPPED: Colorspace +//| """The swapped 15-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB555""" +//| +MAKE_ENUM_MAP(displayio_colorspace) { + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB888), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, L8), +}; +STATIC MP_DEFINE_CONST_DICT(displayio_colorspace_locals_dict, displayio_colorspace_locals_table); + +MAKE_PRINTER(displayio, displayio_colorspace); +MAKE_ENUM_TYPE(displayio, ColorSpace, displayio_colorspace); diff --git a/circuitpython/shared-bindings/displayio/Display.c b/circuitpython/shared-bindings/displayio/Display.c new file mode 100644 index 0000000..b19c0ba --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Display.c @@ -0,0 +1,524 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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 "shared-bindings/displayio/Display.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| _DisplayBus = Union['FourWire', 'paralleldisplay.ParallelBus', 'I2CDisplay'] +//| """:py:class:`FourWire`, :py:class:`paralleldisplay.ParallelBus` or :py:class:`I2CDisplay`""" +//| + +//| +//| class Display: +//| """Manage updating a display over a display bus +//| +//| This initializes a display and connects it into CircuitPython. Unlike other +//| objects in CircuitPython, Display objects live until `displayio.release_displays()` +//| is called. This is done so that CircuitPython can use the display itself. +//| +//| Most people should not use this class directly. Use a specific display driver instead that will +//| contain the initialization sequence at minimum.""" +//| +//| def __init__(self, display_bus: _DisplayBus, init_sequence: ReadableBuffer, *, width: int, height: int, colstart: int = 0, rowstart: int = 0, rotation: int = 0, color_depth: int = 16, grayscale: bool = False, pixels_in_byte_share_row: bool = True, bytes_per_cell: int = 1, reverse_pixels_in_byte: bool = False, set_column_command: int = 0x2a, set_row_command: int = 0x2b, write_ram_command: int = 0x2c, backlight_pin: Optional[microcontroller.Pin] = None, brightness_command: Optional[int] = None, brightness: float = 1.0, auto_brightness: bool = False, single_byte_bounds: bool = False, data_as_commands: bool = False, auto_refresh: bool = True, native_frames_per_second: int = 60, backlight_on_high: bool = True, SH1107_addressing: bool = False) -> None: +//| r"""Create a Display object on the given display bus (`FourWire`, `ParallelBus` or `I2CDisplay`). +//| +//| The ``init_sequence`` is bitpacked to minimize the ram impact. Every command begins with a +//| command byte followed by a byte to determine the parameter count and delay. When the top bit +//| of the second byte is 1 (0x80), a delay will occur after the command parameters are sent. +//| The remaining 7 bits are the parameter count excluding any delay byte. The bytes following +//| are the parameters. When the delay bit is set, a single byte after the parameters specifies +//| the delay duration in milliseconds. The value 0xff will lead to an extra long 500 ms delay +//| instead of 255 ms. The next byte will begin a new command definition. +//| Here is an example: +//| +//| .. code-block:: python +//| +//| init_sequence = (b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma +//| b"\x11\x80\x78"# Exit Sleep then delay 0x78 (120ms) +//| b"\x29\x81\xaa\x78"# Display on then delay 0x78 (120ms) +//| ) +//| display = displayio.Display(display_bus, init_sequence, width=320, height=240) +//| +//| The first command is 0xe1 with 15 (0xf) parameters following. The second is 0x11 with 0 +//| parameters and a 120ms (0x78) delay. The third command is 0x29 with one parameter 0xaa and a +//| 120ms delay (0x78). Multiple byte literals (b"") are merged together on load. The parens +//| are needed to allow byte literals on subsequent lines. +//| +//| The initialization sequence should always leave the display memory access inline with the scan +//| of the display to minimize tearing artifacts. +//| +//| :param display_bus: The bus that the display is connected to +//| :type _DisplayBus: FourWire, ParallelBus or I2CDisplay +//| :param ~circuitpython_typing.ReadableBuffer init_sequence: Byte-packed initialization sequence. +//| :param int width: Width in pixels +//| :param int height: Height in pixels +//| :param int colstart: The index if the first visible column +//| :param int rowstart: The index if the first visible row +//| :param int rotation: The rotation of the display in degrees clockwise. Must be in 90 degree increments (0, 90, 180, 270) +//| :param int color_depth: The number of bits of color per pixel transmitted. (Some displays +//| support 18 bit but 16 is easier to transmit. The last bit is extrapolated.) +//| :param bool grayscale: True if the display only shows a single color. +//| :param bool pixels_in_byte_share_row: True when pixels are less than a byte and a byte includes pixels from the same row of the display. When False, pixels share a column. +//| :param int bytes_per_cell: Number of bytes per addressable memory location when color_depth < 8. When greater than one, bytes share a row or column according to pixels_in_byte_share_row. +//| :param bool reverse_pixels_in_byte: Reverses the pixel order within each byte when color_depth < 8. Does not apply across multiple bytes even if there is more than one byte per cell (bytes_per_cell.) +//| :param bool reverse_bytes_in_word: Reverses the order of bytes within a word when color_depth == 16 +//| :param int set_column_command: Command used to set the start and end columns to update +//| :param int set_row_command: Command used so set the start and end rows to update +//| :param int write_ram_command: Command used to write pixels values into the update region. Ignored if data_as_commands is set. +//| :param microcontroller.Pin backlight_pin: Pin connected to the display's backlight +//| :param int brightness_command: Command to set display brightness. Usually available in OLED controllers. +//| :param float brightness: Initial display brightness. This value is ignored if auto_brightness is True. +//| :param bool auto_brightness: If True, brightness is controlled via an ambient light sensor or other mechanism. +//| :param bool single_byte_bounds: Display column and row commands use single bytes +//| :param bool data_as_commands: Treat all init and boundary data as SPI commands. Certain displays require this. +//| :param bool auto_refresh: Automatically refresh the screen +//| :param int native_frames_per_second: Number of display refreshes per second that occur with the given init_sequence. +//| :param bool backlight_on_high: If True, pulling the backlight pin high turns the backlight on. +//| :param bool SH1107_addressing: Special quirk for SH1107, use upper/lower column set and page set +//| :param int set_vertical_scroll: This parameter is accepted but ignored for backwards compatibility. It will be removed in a future release. +//| """ +//| ... +//| +STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_display_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, + ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, + ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_reverse_bytes_in_word, + ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, + ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, + ARG_brightness, ARG_auto_brightness, ARG_single_byte_bounds, ARG_data_as_commands, + ARG_auto_refresh, ARG_native_frames_per_second, ARG_backlight_on_high, + ARG_SH1107_addressing }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_init_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_colstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rowstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rotation, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_color_depth, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_grayscale, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_pixels_in_byte_share_row, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_bytes_per_cell, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_reverse_pixels_in_byte, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_reverse_bytes_in_word, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_set_column_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2a} }, + { MP_QSTR_set_row_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2b} }, + { MP_QSTR_write_ram_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2c} }, + { MP_QSTR_set_vertical_scroll, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x0} }, + { MP_QSTR_backlight_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_brightness_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_BRIGHTNESS_COMMAND} }, + { MP_QSTR_brightness, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + { MP_QSTR_auto_brightness, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_single_byte_bounds, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_data_as_commands, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_auto_refresh, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_native_frames_per_second, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60} }, + { MP_QSTR_backlight_on_high, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_SH1107_addressing, MP_ARG_BOOL | 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); + + mp_obj_t display_bus = args[ARG_display_bus].u_obj; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_init_sequence].u_obj, &bufinfo, MP_BUFFER_READ); + + const mcu_pin_obj_t *backlight_pin = validate_obj_is_free_pin_or_none(args[ARG_backlight_pin].u_obj); + + mp_float_t brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + + mp_int_t rotation = args[ARG_rotation].u_int; + if (rotation % 90 != 0) { + mp_raise_ValueError(translate("Display rotation must be in 90 degree increments")); + } + + const bool sh1107_addressing = args[ARG_SH1107_addressing].u_bool; + const mp_int_t color_depth = args[ARG_color_depth].u_int; + if (sh1107_addressing && color_depth != 1) { + mp_raise_ValueError_varg(translate("%q must be 1 when %q is True"), MP_QSTR_color_depth, MP_QSTR_SH1107_addressing); + } + + primary_display_t *disp = allocate_display_or_raise(); + displayio_display_obj_t *self = &disp->display; + + self->base.type = &displayio_display_type; + common_hal_displayio_display_construct( + self, + display_bus, args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, + color_depth, args[ARG_grayscale].u_bool, + args[ARG_pixels_in_byte_share_row].u_bool, + args[ARG_bytes_per_cell].u_bool, + args[ARG_reverse_pixels_in_byte].u_bool, + args[ARG_reverse_bytes_in_word].u_bool, + args[ARG_set_column_command].u_int, args[ARG_set_row_command].u_int, + args[ARG_write_ram_command].u_int, + bufinfo.buf, bufinfo.len, + MP_OBJ_TO_PTR(backlight_pin), + args[ARG_brightness_command].u_int, + brightness, + args[ARG_auto_brightness].u_bool, + args[ARG_single_byte_bounds].u_bool, + args[ARG_data_as_commands].u_bool, + args[ARG_auto_refresh].u_bool, + args[ARG_native_frames_per_second].u_int, + args[ARG_backlight_on_high].u_bool, + sh1107_addressing + ); + + return self; +} + +// Helper to ensure we have the native super class instead of a subclass. +static displayio_display_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native_display = mp_obj_cast_to_native_base(display_obj, &displayio_display_type); + mp_obj_assert_native_inited(native_display); + return MP_OBJ_TO_PTR(native_display); +} + +//| def show(self, group: Group) -> None: +//| """Switches to displaying the given group of layers. When group is None, the default +//| CircuitPython terminal will be shown. +//| +//| :param Group group: The group to show.""" +//| ... +//| +STATIC mp_obj_t displayio_display_obj_show(mp_obj_t self_in, mp_obj_t group_in) { + displayio_display_obj_t *self = native_display(self_in); + displayio_group_t *group = NULL; + if (group_in != mp_const_none) { + group = MP_OBJ_TO_PTR(native_group(group_in)); + } + + bool ok = common_hal_displayio_display_show(self, group); + if (!ok) { + mp_raise_ValueError(translate("Group already used")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_show_obj, displayio_display_obj_show); + +//| def refresh(self, *, target_frames_per_second: Optional[int] = None, minimum_frames_per_second: int = 0) -> bool: +//| """When auto_refresh is off, and :py:attr:`target_frames_per_second` is not `None` this waits +//| for the target frame rate and then refreshes the display, +//| returning `True`. If the call has taken too long since the last refresh call for the given +//| target frame rate, then the refresh returns `False` immediately without updating the screen to +//| hopefully help getting caught up. +//| +//| If the time since the last successful refresh is below the minimum frame rate, then an +//| exception will be raised. The default :py:attr:`minimum_frames_per_second` of 0 disables this behavior. +//| +//| When auto_refresh is off, and :py:attr:`target_frames_per_second` is `None` this +//| will update the display immediately. +//| +//| When auto_refresh is on, updates the display immediately. (The display will also update +//| without calls to this.) +//| +//| :param Optional[int] target_frames_per_second: The target frame rate that :py:func:`refresh` should try to +//| achieve. Set to `None` for immediate refresh. +//| :param int minimum_frames_per_second: The minimum number of times the screen should be updated per second.""" +//| ... +//| +STATIC mp_obj_t displayio_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_target_frames_per_second, ARG_minimum_frames_per_second }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_target_frames_per_second, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + 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); + + displayio_display_obj_t *self = native_display(pos_args[0]); + uint32_t maximum_ms_per_real_frame = 0xffffffff; + mp_int_t minimum_frames_per_second = args[ARG_minimum_frames_per_second].u_int; + if (minimum_frames_per_second > 0) { + maximum_ms_per_real_frame = 1000 / minimum_frames_per_second; + } + + uint32_t target_ms_per_frame; + if (args[ARG_target_frames_per_second].u_obj == mp_const_none) { + target_ms_per_frame = 0xffffffff; + } else { + target_ms_per_frame = 1000 / mp_obj_get_int(args[ARG_target_frames_per_second].u_obj); + } + + return mp_obj_new_bool(common_hal_displayio_display_refresh(self, target_ms_per_frame, maximum_ms_per_real_frame)); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_refresh_obj, 1, displayio_display_obj_refresh); + +//| auto_refresh: bool +//| """True when the display is refreshed automatically.""" +//| +STATIC mp_obj_t displayio_display_obj_get_auto_refresh(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_displayio_display_get_auto_refresh(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_auto_refresh_obj, displayio_display_obj_get_auto_refresh); + +STATIC mp_obj_t displayio_display_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) { + displayio_display_obj_t *self = native_display(self_in); + + common_hal_displayio_display_set_auto_refresh(self, mp_obj_is_true(auto_refresh)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_auto_refresh_obj, displayio_display_obj_set_auto_refresh); + +MP_PROPERTY_GETSET(displayio_display_auto_refresh_obj, + (mp_obj_t)&displayio_display_get_auto_refresh_obj, + (mp_obj_t)&displayio_display_set_auto_refresh_obj); + +//| brightness: float +//| """The brightness of the display as a float. 0.0 is off and 1.0 is full brightness. When +//| `auto_brightness` is True, the value of `brightness` will change automatically. +//| If `brightness` is set, `auto_brightness` will be disabled and will be set to False.""" +//| +STATIC mp_obj_t displayio_display_obj_get_brightness(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + mp_float_t brightness = common_hal_displayio_display_get_brightness(self); + if (brightness < 0) { + mp_raise_RuntimeError(translate("Brightness not adjustable")); + } + return mp_obj_new_float(brightness); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_brightness_obj, displayio_display_obj_get_brightness); + +STATIC mp_obj_t displayio_display_obj_set_brightness(mp_obj_t self_in, mp_obj_t brightness_obj) { + displayio_display_obj_t *self = native_display(self_in); + common_hal_displayio_display_set_auto_brightness(self, false); + mp_float_t brightness = mp_obj_get_float(brightness_obj); + if (brightness < 0 || brightness > 1.0) { + mp_raise_ValueError(translate("Brightness must be 0-1.0")); + } + bool ok = common_hal_displayio_display_set_brightness(self, brightness); + if (!ok) { + mp_raise_RuntimeError(translate("Brightness not adjustable")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_brightness_obj, displayio_display_obj_set_brightness); + +MP_PROPERTY_GETSET(displayio_display_brightness_obj, + (mp_obj_t)&displayio_display_get_brightness_obj, + (mp_obj_t)&displayio_display_set_brightness_obj); + +//| auto_brightness: bool +//| """True when the display brightness is adjusted automatically, based on an ambient +//| light sensor or other method. Note that some displays may have this set to True by default, +//| but not actually implement automatic brightness adjustment. `auto_brightness` is set to False +//| if `brightness` is set manually.""" +//| +STATIC mp_obj_t displayio_display_obj_get_auto_brightness(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_displayio_display_get_auto_brightness(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_auto_brightness_obj, displayio_display_obj_get_auto_brightness); + +STATIC mp_obj_t displayio_display_obj_set_auto_brightness(mp_obj_t self_in, mp_obj_t auto_brightness) { + displayio_display_obj_t *self = native_display(self_in); + + common_hal_displayio_display_set_auto_brightness(self, mp_obj_is_true(auto_brightness)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_auto_brightness_obj, displayio_display_obj_set_auto_brightness); + +MP_PROPERTY_GETSET(displayio_display_auto_brightness_obj, + (mp_obj_t)&displayio_display_get_auto_brightness_obj, + (mp_obj_t)&displayio_display_set_auto_brightness_obj); + + + + +//| width: int +//| """Gets the width of the board""" +//| +STATIC mp_obj_t displayio_display_obj_get_width(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_width_obj, displayio_display_obj_get_width); + +MP_PROPERTY_GETTER(displayio_display_width_obj, + (mp_obj_t)&displayio_display_get_width_obj); + +//| height: int +//| """Gets the height of the board""" +//| +STATIC mp_obj_t displayio_display_obj_get_height(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_height_obj, displayio_display_obj_get_height); + +MP_PROPERTY_GETTER(displayio_display_height_obj, + (mp_obj_t)&displayio_display_get_height_obj); + +//| rotation: int +//| """The rotation of the display as an int in degrees.""" +//| +STATIC mp_obj_t displayio_display_obj_get_rotation(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_get_rotation(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_rotation_obj, displayio_display_obj_get_rotation); +STATIC mp_obj_t displayio_display_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) { + displayio_display_obj_t *self = native_display(self_in); + common_hal_displayio_display_set_rotation(self, mp_obj_get_int(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_rotation_obj, displayio_display_obj_set_rotation); + + +MP_PROPERTY_GETSET(displayio_display_rotation_obj, + (mp_obj_t)&displayio_display_get_rotation_obj, + (mp_obj_t)&displayio_display_set_rotation_obj); + +//| bus: _DisplayBus +//| """The bus being used by the display""" +//| +//| +STATIC mp_obj_t displayio_display_obj_get_bus(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return common_hal_displayio_display_get_bus(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_bus_obj, displayio_display_obj_get_bus); + +MP_PROPERTY_GETTER(displayio_display_bus_obj, + (mp_obj_t)&displayio_display_get_bus_obj); + +//| root_group: Group +//| """The root group on the display.""" +//| +//| +STATIC mp_obj_t displayio_display_obj_get_root_group(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return common_hal_displayio_display_get_root_group(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_root_group_obj, displayio_display_obj_get_root_group); + +MP_PROPERTY_GETTER(displayio_display_root_group_obj, + (mp_obj_t)&displayio_display_get_root_group_obj); + + +//| def fill_row(self, y: int, buffer: WriteableBuffer) -> WriteableBuffer: +//| """Extract the pixels from a single row +//| +//| :param int y: The top edge of the area +//| :param ~circuitpython_typing.WriteableBuffer buffer: The buffer in which to place the pixel data""" +//| ... +//| +STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_y, ARG_buffer }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_y, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = -1} }, + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + }; + 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); + displayio_display_obj_t *self = native_display(pos_args[0]); + mp_int_t y = args[ARG_y].u_int; + mp_obj_t *result = args[ARG_buffer].u_obj; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(result, &bufinfo, MP_BUFFER_WRITE); + + if (self->core.colorspace.depth != 16) { + mp_raise_ValueError(translate("Display must have a 16 bit colorspace.")); + } + + displayio_area_t area = { + .x1 = 0, + .y1 = y, + .x2 = self->core.width, + .y2 = y + 1 + }; + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; + uint16_t buffer_size = self->core.width / pixels_per_word; + uint16_t pixels_per_buffer = displayio_area_size(&area); + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + + uint32_t *result_buffer = bufinfo.buf; + size_t result_buffer_size = bufinfo.len; + + if (result_buffer_size >= (buffer_size * 4)) { + volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask[mask_length]; + + for (uint16_t k = 0; k < mask_length; k++) { + mask[k] = 0x00000000; + } + + displayio_display_core_fill_area(&self->core, &area, mask, result_buffer); + return result; + } else { + mp_raise_ValueError(translate("Buffer is too small")); + } +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_fill_row_obj, 1, displayio_display_obj_fill_row); + +STATIC const mp_rom_map_elem_t displayio_display_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_display_show_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&displayio_display_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_row), MP_ROM_PTR(&displayio_display_fill_row_obj) }, + + { MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&displayio_display_auto_refresh_obj) }, + + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&displayio_display_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_auto_brightness), MP_ROM_PTR(&displayio_display_auto_brightness_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_display_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_display_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&displayio_display_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_bus), MP_ROM_PTR(&displayio_display_bus_obj) }, + { MP_ROM_QSTR(MP_QSTR_root_group), MP_ROM_PTR(&displayio_display_root_group_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_display_locals_dict, displayio_display_locals_dict_table); + +const mp_obj_type_t displayio_display_type = { + { &mp_type_type }, + .name = MP_QSTR_Display, + .make_new = displayio_display_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_display_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/Display.h b/circuitpython/shared-bindings/displayio/Display.h new file mode 100644 index 0000000..f193e61 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Display.h @@ -0,0 +1,74 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 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_DISPLAYIO_DISPLAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_DISPLAY_H + +#include "common-hal/microcontroller/Pin.h" + +#include "shared-module/displayio/Display.h" +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_display_type; + +#define NO_BRIGHTNESS_COMMAND 0x100 + +void common_hal_displayio_display_construct(displayio_display_obj_t *self, + mp_obj_t bus, uint16_t width, uint16_t height, + int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t color_depth, bool grayscale, + bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word, + uint8_t set_column_command, uint8_t set_row_command, uint8_t write_ram_command, + uint8_t *init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t *backlight_pin, uint16_t brightness_command, + mp_float_t brightness, bool auto_brightness, + bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second, + bool backlight_on_high, bool SH1107_addressing); + +bool common_hal_displayio_display_show(displayio_display_obj_t *self, + displayio_group_t *root_group); + +bool common_hal_displayio_display_refresh(displayio_display_obj_t *self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame); + +bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t *self); +void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t *self, bool auto_refresh); + +uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t *self); +uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t *self); +uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t *self); +void common_hal_displayio_display_set_rotation(displayio_display_obj_t *self, int rotation); + +bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t *self); +void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t *self, bool auto_brightness); + +bool common_hal_displayio_display_get_dither(displayio_display_obj_t *self); +void common_hal_displayio_display_set_dither(displayio_display_obj_t *self, bool dither); + +mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t *self); +bool common_hal_displayio_display_set_brightness(displayio_display_obj_t *self, mp_float_t brightness); + +mp_obj_t common_hal_displayio_display_get_bus(displayio_display_obj_t *self); +mp_obj_t common_hal_displayio_display_get_root_group(displayio_display_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_DISPLAY_H diff --git a/circuitpython/shared-bindings/displayio/EPaperDisplay.c b/circuitpython/shared-bindings/displayio/EPaperDisplay.c new file mode 100644 index 0000000..94d2978 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/EPaperDisplay.c @@ -0,0 +1,363 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "shared-bindings/displayio/EPaperDisplay.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class EPaperDisplay: +//| """Manage updating an epaper display over a display bus +//| +//| This initializes an epaper display and connects it into CircuitPython. Unlike other +//| objects in CircuitPython, EPaperDisplay objects live until `displayio.release_displays()` +//| is called. This is done so that CircuitPython can use the display itself. +//| +//| Most people should not use this class directly. Use a specific display driver instead that will +//| contain the startup and shutdown sequences at minimum.""" +//| +//| def __init__(self, display_bus: _DisplayBus, +//| start_sequence: ReadableBuffer, stop_sequence: ReadableBuffer, *, +//| width: int, height: int, ram_width: int, ram_height: int, +//| colstart: int = 0, rowstart: int = 0, rotation: int = 0, +//| set_column_window_command: Optional[int] = None, +//| set_row_window_command: Optional[int] = None, +//| set_current_column_command: Optional[int] = None, +//| set_current_row_command: Optional[int] = None, +//| write_black_ram_command: int, black_bits_inverted: bool = False, +//| write_color_ram_command: Optional[int] = None, +//| color_bits_inverted: bool = False, highlight_color: int = 0x000000, +//| refresh_display_command: int, refresh_time: float = 40, +//| busy_pin: Optional[microcontroller.Pin] = None, busy_state: bool = True, +//| seconds_per_frame: float = 180, always_toggle_chip_select: bool = False, +//| grayscale: bool = False, two_byte_sequence_length: bool = False) -> None: +//| """Create a EPaperDisplay object on the given display bus (`displayio.FourWire` or `paralleldisplay.ParallelBus`). +//| +//| The ``start_sequence`` and ``stop_sequence`` are bitpacked to minimize the ram impact. Every +//| command begins with a command byte followed by a byte to determine the parameter count and +//| delay. When the top bit of the second byte is 1 (0x80), a delay will occur after the command +//| parameters are sent. The remaining 7 bits are the parameter count excluding any delay +//| byte. The bytes following are the parameters. When the delay bit is set, a single byte after +//| the parameters specifies the delay duration in milliseconds. The value 0xff will lead to an +//| extra long 500 ms delay instead of 255 ms. The next byte will begin a new command definition. +//| +//| :param display_bus: The bus that the display is connected to +//| :type _DisplayBus: displayio.FourWire or paralleldisplay.ParallelBus +//| :param ~circuitpython_typing.ReadableBuffer start_sequence: Byte-packed initialization sequence. +//| :param ~circuitpython_typing.ReadableBuffer stop_sequence: Byte-packed initialization sequence. +//| :param int width: Width in pixels +//| :param int height: Height in pixels +//| :param int ram_width: RAM width in pixels +//| :param int ram_height: RAM height in pixels +//| :param int colstart: The index if the first visible column +//| :param int rowstart: The index if the first visible row +//| :param int rotation: The rotation of the display in degrees clockwise. Must be in 90 degree increments (0, 90, 180, 270) +//| :param int set_column_window_command: Command used to set the start and end columns to update +//| :param int set_row_window_command: Command used so set the start and end rows to update +//| :param int set_current_column_command: Command used to set the current column location +//| :param int set_current_row_command: Command used to set the current row location +//| :param int write_black_ram_command: Command used to write pixels values into the update region +//| :param bool black_bits_inverted: True if 0 bits are used to show black pixels. Otherwise, 1 means to show black. +//| :param int write_color_ram_command: Command used to write pixels values into the update region +//| :param bool color_bits_inverted: True if 0 bits are used to show the color. Otherwise, 1 means to show color. +//| :param int highlight_color: RGB888 of source color to highlight with third ePaper color. +//| :param int refresh_display_command: Command used to start a display refresh +//| :param float refresh_time: Time it takes to refresh the display before the stop_sequence should be sent. Ignored when busy_pin is provided. +//| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy +//| :param bool busy_state: State of the busy pin when the display is busy +//| :param float seconds_per_frame: Minimum number of seconds between screen refreshes +//| :param bool always_toggle_chip_select: When True, chip select is toggled every byte +//| :param bool grayscale: When true, the color ram is the low bit of 2-bit grayscale +//| :param bool two_byte_sequence_length: When true, use two bytes to define sequence length""" +//| ... +//| +STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, + ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, + ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, + ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, + ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, + ARG_refresh_display_command, ARG_refresh_time, ARG_busy_pin, ARG_busy_state, + ARG_seconds_per_frame, ARG_always_toggle_chip_select, ARG_grayscale, ARG_two_byte_sequence_length }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_start_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_stop_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_ram_width, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_ram_height, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_colstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rowstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rotation, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_set_column_window_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_set_row_window_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_set_current_column_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_set_current_row_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_write_black_ram_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_black_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_write_color_ram_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_color_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, + { MP_QSTR_refresh_display_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_refresh_time, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(40)} }, + { MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_busy_state, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_seconds_per_frame, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(180)} }, + { MP_QSTR_always_toggle_chip_select, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_grayscale, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_two_byte_sequence_length, MP_ARG_BOOL | 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); + + mp_obj_t display_bus = args[ARG_display_bus].u_obj; + + mp_buffer_info_t start_bufinfo; + mp_get_buffer_raise(args[ARG_start_sequence].u_obj, &start_bufinfo, MP_BUFFER_READ); + mp_buffer_info_t stop_bufinfo; + mp_get_buffer_raise(args[ARG_stop_sequence].u_obj, &stop_bufinfo, MP_BUFFER_READ); + + + const mcu_pin_obj_t *busy_pin = validate_obj_is_free_pin_or_none(args[ARG_busy_pin].u_obj); + + mp_int_t rotation = args[ARG_rotation].u_int; + if (rotation % 90 != 0) { + mp_raise_ValueError(translate("Display rotation must be in 90 degree increments")); + } + + primary_display_t *disp = allocate_display_or_raise(); + displayio_epaperdisplay_obj_t *self = &disp->epaper_display; + ; + + mp_float_t refresh_time = mp_obj_get_float(args[ARG_refresh_time].u_obj); + mp_float_t seconds_per_frame = mp_obj_get_float(args[ARG_seconds_per_frame].u_obj); + + mp_int_t write_color_ram_command = NO_COMMAND; + mp_int_t highlight_color = args[ARG_highlight_color].u_int; + if (args[ARG_write_color_ram_command].u_obj != mp_const_none) { + write_color_ram_command = mp_obj_get_int(args[ARG_write_color_ram_command].u_obj); + } + + self->base.type = &displayio_epaperdisplay_type; + common_hal_displayio_epaperdisplay_construct( + self, + display_bus, + start_bufinfo.buf, start_bufinfo.len, stop_bufinfo.buf, stop_bufinfo.len, + args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_ram_width].u_int, args[ARG_ram_height].u_int, + args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, + args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int, + args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int, + args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, + args[ARG_color_bits_inverted].u_bool, highlight_color, args[ARG_refresh_display_command].u_int, refresh_time, + busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, + args[ARG_always_toggle_chip_select].u_bool, args[ARG_grayscale].u_bool, args[ARG_two_byte_sequence_length].u_bool + ); + + return self; +} + +// Helper to ensure we have the native super class instead of a subclass. +static displayio_epaperdisplay_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native_display = mp_obj_cast_to_native_base(display_obj, &displayio_epaperdisplay_type); + mp_obj_assert_native_inited(native_display); + return MP_OBJ_TO_PTR(native_display); +} + +//| def show(self, group: Group) -> None: +//| """Switches to displaying the given group of layers. When group is None, the default +//| CircuitPython terminal will be shown. +//| +//| :param Group group: The group to show.""" +//| ... +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_show(mp_obj_t self_in, mp_obj_t group_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + displayio_group_t *group = NULL; + if (group_in != mp_const_none) { + group = MP_OBJ_TO_PTR(native_group(group_in)); + } + + bool ok = common_hal_displayio_epaperdisplay_show(self, group); + if (!ok) { + mp_raise_ValueError(translate("Group already used")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_show_obj, displayio_epaperdisplay_obj_show); + +//| def update_refresh_mode(self, start_sequence: ReadableBuffer, seconds_per_frame: float = 180) -> None: +//| """Updates the ``start_sequence`` and ``seconds_per_frame`` parameters to enable +//| varying the refresh mode of the display.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_update_refresh_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_start_sequence, ARG_seconds_per_frame }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_start_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_seconds_per_frame, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(180)} }, + }; + displayio_epaperdisplay_obj_t *self = native_display(pos_args[0]); + 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 parameters + mp_buffer_info_t start_sequence; + mp_get_buffer_raise(args[ARG_start_sequence].u_obj, &start_sequence, MP_BUFFER_READ); + float seconds_per_frame = mp_obj_get_float(args[ARG_seconds_per_frame].u_obj); + + // Update parameters + displayio_epaperdisplay_change_refresh_mode_parameters(self, &start_sequence, seconds_per_frame); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_epaperdisplay_update_refresh_mode_obj, 1, displayio_epaperdisplay_update_refresh_mode); + +//| def refresh(self) -> None: +//| """Refreshes the display immediately or raises an exception if too soon. Use +//| ``time.sleep(display.time_to_refresh)`` to sleep until a refresh can occur.""" +//| ... +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_refresh(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + bool ok = common_hal_displayio_epaperdisplay_refresh(self); + if (!ok) { + mp_raise_RuntimeError(translate("Refresh too soon")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_obj, displayio_epaperdisplay_obj_refresh); + +//| time_to_refresh: float +//| """Time, in fractional seconds, until the ePaper display can be refreshed.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_time_to_refresh(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return mp_obj_new_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self) / 1000.0); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_time_to_refresh_obj, displayio_epaperdisplay_obj_get_time_to_refresh); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_time_to_refresh_obj, + (mp_obj_t)&displayio_epaperdisplay_get_time_to_refresh_obj); + +//| busy: bool +//| """True when the display is refreshing. This uses the ``busy_pin`` when available or the +//| ``refresh_time`` otherwise.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_busy(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_displayio_epaperdisplay_get_busy(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_busy_obj, displayio_epaperdisplay_obj_get_busy); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_busy_obj, + (mp_obj_t)&displayio_epaperdisplay_get_busy_obj); + +//| width: int +//| """Gets the width of the display in pixels""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_width(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_width_obj, displayio_epaperdisplay_obj_get_width); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_width_obj, + (mp_obj_t)&displayio_epaperdisplay_get_width_obj); + +//| height: int +//| """Gets the height of the display in pixels""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_height(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_height_obj, displayio_epaperdisplay_obj_get_height); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_height_obj, + (mp_obj_t)&displayio_epaperdisplay_get_height_obj); + +//| rotation: int +//| """The rotation of the display as an int in degrees.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_rotation(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_get_rotation(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_rotation_obj, displayio_epaperdisplay_obj_get_rotation); +STATIC mp_obj_t displayio_epaperdisplay_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + common_hal_displayio_epaperdisplay_set_rotation(self, mp_obj_get_int(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_set_rotation_obj, displayio_epaperdisplay_obj_set_rotation); + + +MP_PROPERTY_GETSET(displayio_epaperdisplay_rotation_obj, + (mp_obj_t)&displayio_epaperdisplay_get_rotation_obj, + (mp_obj_t)&displayio_epaperdisplay_set_rotation_obj); + +//| bus: _DisplayBus +//| """The bus being used by the display""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_bus(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return common_hal_displayio_epaperdisplay_get_bus(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_bus_obj, displayio_epaperdisplay_obj_get_bus); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_bus_obj, + (mp_obj_t)&displayio_epaperdisplay_get_bus_obj); + + +STATIC const mp_rom_map_elem_t displayio_epaperdisplay_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_epaperdisplay_show_obj) }, + { MP_ROM_QSTR(MP_QSTR_update_refresh_mode), MP_ROM_PTR(&displayio_epaperdisplay_update_refresh_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&displayio_epaperdisplay_refresh_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_epaperdisplay_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_epaperdisplay_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&displayio_epaperdisplay_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_bus), MP_ROM_PTR(&displayio_epaperdisplay_bus_obj) }, + { MP_ROM_QSTR(MP_QSTR_busy), MP_ROM_PTR(&displayio_epaperdisplay_busy_obj) }, + { MP_ROM_QSTR(MP_QSTR_time_to_refresh), MP_ROM_PTR(&displayio_epaperdisplay_time_to_refresh_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_epaperdisplay_locals_dict, displayio_epaperdisplay_locals_dict_table); + +const mp_obj_type_t displayio_epaperdisplay_type = { + { &mp_type_type }, + .name = MP_QSTR_EPaperDisplay, + .make_new = displayio_epaperdisplay_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_epaperdisplay_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/EPaperDisplay.h b/circuitpython/shared-bindings/displayio/EPaperDisplay.h new file mode 100644 index 0000000..ba6dffc --- /dev/null +++ b/circuitpython/shared-bindings/displayio/EPaperDisplay.h @@ -0,0 +1,62 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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_DISPLAYIO_EPAPERDISPLAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H + +#include "common-hal/microcontroller/Pin.h" + +#include "shared-module/displayio/EPaperDisplay.h" +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_epaperdisplay_type; + +#define NO_COMMAND 0x100 + +void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t *self, + mp_obj_t bus, const uint8_t *start_sequence, uint16_t start_sequence_len, const uint8_t *stop_sequence, uint16_t stop_sequence_len, + uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation, + uint16_t set_column_window_command, uint16_t set_row_window_command, + uint16_t set_current_column_command, uint16_t set_current_row_command, + uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, mp_float_t refresh_time, + const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select, bool grayscale, bool two_byte_sequence_length); + +bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t *self); + +bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t *self, displayio_group_t *root_group); + +// Returns time in milliseconds. +uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t *self); +bool common_hal_displayio_epaperdisplay_get_busy(displayio_epaperdisplay_obj_t *self); + +uint16_t common_hal_displayio_epaperdisplay_get_width(displayio_epaperdisplay_obj_t *self); +uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_obj_t *self); +uint16_t common_hal_displayio_epaperdisplay_get_rotation(displayio_epaperdisplay_obj_t *self); +void common_hal_displayio_epaperdisplay_set_rotation(displayio_epaperdisplay_obj_t *self, int rotation); + +mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H diff --git a/circuitpython/shared-bindings/displayio/FourWire.c b/circuitpython/shared-bindings/displayio/FourWire.c new file mode 100644 index 0000000..90aa5c6 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/FourWire.c @@ -0,0 +1,172 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 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 "shared-bindings/displayio/FourWire.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class FourWire: +//| """Manage updating a display over SPI four wire protocol in the background while Python code runs. +//| It doesn't handle display initialization.""" +//| +//| def __init__(self, spi_bus: busio.SPI, *, command: Optional[microcontroller.Pin], chip_select: microcontroller.Pin, reset: Optional[microcontroller.Pin] = None, baudrate: int = 24000000, polarity: int = 0, phase: int = 0) -> None: +//| """Create a FourWire object associated with the given pins. +//| +//| The SPI bus and pins are then in use by the display until `displayio.release_displays()` is +//| called even after a reload. (It does this so CircuitPython can use the display after your code +//| is done.) So, the first time you initialize a display bus in code.py you should call +//| :py:func:`displayio.release_displays` first, otherwise it will error after the first code.py run. +//| +//| If the ``command`` pin is not specified, a 9-bit SPI mode will be simulated by adding a +//| data/command bit to every bit being transmitted, and splitting the resulting data back +//| into 8-bit bytes for transmission. The extra bits that this creates at the end are ignored +//| by the receiving device. +//| +//| :param busio.SPI spi_bus: The SPI bus that make up the clock and data lines +//| :param microcontroller.Pin command: Data or command pin. When None, 9-bit SPI is simulated. +//| :param microcontroller.Pin chip_select: Chip select pin +//| :param microcontroller.Pin reset: Reset pin. When None only software reset can be used +//| :param int baudrate: Maximum baudrate in Hz for the display on the bus +//| :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.""" +//| ... +//| +STATIC mp_obj_t displayio_fourwire_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_spi_bus, ARG_command, ARG_chip_select, ARG_reset, ARG_baudrate, ARG_polarity, ARG_phase }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_spi_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_chip_select, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_reset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_baudrate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 24000000} }, + { 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_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 *command = validate_obj_is_free_pin_or_none(args[ARG_command].u_obj); + const mcu_pin_obj_t *chip_select = validate_obj_is_free_pin(args[ARG_chip_select].u_obj); + const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj); + + mp_obj_t spi = mp_arg_validate_type(args[ARG_spi_bus].u_obj, &busio_spi_type, MP_QSTR_spi_bus); + + displayio_fourwire_obj_t *self = &allocate_display_bus_or_raise()->fourwire_bus; + self->base.type = &displayio_fourwire_type; + + 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")); + } + + common_hal_displayio_fourwire_construct(self, + MP_OBJ_TO_PTR(spi), command, chip_select, reset, args[ARG_baudrate].u_int, polarity, phase); + return self; +} + +//| def reset(self) -> None: +//| """Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available.""" +//| ... +//| +STATIC mp_obj_t displayio_fourwire_obj_reset(mp_obj_t self_in) { + displayio_fourwire_obj_t *self = self_in; + + if (!common_hal_displayio_fourwire_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_fourwire_reset_obj, displayio_fourwire_obj_reset); + +//| def send(self, command: int, data: ReadableBuffer, *, toggle_every_byte: bool = False) -> None: +//| """Sends the given command value followed by the full set of data. Display state, such as +//| vertical scroll, set via ``send`` may or may not be reset once the code is done.""" +//| ... +//| +STATIC mp_obj_t displayio_fourwire_obj_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_command, ARG_data, ARG_toggle_every_byte }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_toggle_every_byte, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + 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_int_t command_int = args[ARG_command].u_int; + if (command_int > 255 || command_int < 0) { + mp_raise_ValueError(translate("Command must be an int between 0 and 255")); + } + displayio_fourwire_obj_t *self = pos_args[0]; + uint8_t command = command_int; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + + // Wait for display bus to be available. + while (!common_hal_displayio_fourwire_begin_transaction(self)) { + RUN_BACKGROUND_TASKS; + } + display_chip_select_behavior_t chip_select = CHIP_SELECT_UNTOUCHED; + if (args[ARG_toggle_every_byte].u_bool) { + chip_select = CHIP_SELECT_TOGGLE_EVERY_BYTE; + } + common_hal_displayio_fourwire_send(self, DISPLAY_COMMAND, chip_select, &command, 1); + common_hal_displayio_fourwire_send(self, DISPLAY_DATA, chip_select, ((uint8_t *)bufinfo.buf), bufinfo.len); + common_hal_displayio_fourwire_end_transaction(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_fourwire_send_obj, 1, displayio_fourwire_obj_send); + +STATIC const mp_rom_map_elem_t displayio_fourwire_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&displayio_fourwire_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_fourwire_send_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_fourwire_locals_dict, displayio_fourwire_locals_dict_table); + +const mp_obj_type_t displayio_fourwire_type = { + { &mp_type_type }, + .name = MP_QSTR_FourWire, + .make_new = displayio_fourwire_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_fourwire_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/FourWire.h b/circuitpython/shared-bindings/displayio/FourWire.h new file mode 100644 index 0000000..300327f --- /dev/null +++ b/circuitpython/shared-bindings/displayio/FourWire.h @@ -0,0 +1,56 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 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_DISPLAYBUSIO_FOURWIRE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_FOURWIRE_H + +#include "shared-module/displayio/FourWire.h" + +#include "shared-bindings/displayio/__init__.h" +#include "common-hal/microcontroller/Pin.h" + +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_fourwire_type; + +void common_hal_displayio_fourwire_construct(displayio_fourwire_obj_t *self, + busio_spi_obj_t *spi, const mcu_pin_obj_t *command, + const mcu_pin_obj_t *chip_select, const mcu_pin_obj_t *reset, uint32_t baudrate, + uint8_t polarity, uint8_t phase); + +void common_hal_displayio_fourwire_deinit(displayio_fourwire_obj_t *self); + +bool common_hal_displayio_fourwire_reset(mp_obj_t self); +bool common_hal_displayio_fourwire_bus_free(mp_obj_t self); + +bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t self); + +void common_hal_displayio_fourwire_send(mp_obj_t self, display_byte_type_t byte_type, + display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); + +void common_hal_displayio_fourwire_end_transaction(mp_obj_t self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_FOURWIRE_H diff --git a/circuitpython/shared-bindings/displayio/Group.c b/circuitpython/shared-bindings/displayio/Group.c new file mode 100644 index 0000000..188331f --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Group.c @@ -0,0 +1,355 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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 "shared-bindings/displayio/Group.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +//| class Group: +//| """Manage a group of sprites and groups and how they are inter-related.""" +//| +//| def __init__(self, *, scale: int = 1, x: int = 0, y: int = 0) -> None: +//| """Create a Group of a given size and scale. Scale is in one dimension. For example, scale=2 +//| leads to a layer's pixel being 2x2 pixels when in the group. +//| +//| :param int scale: Scale of layer pixels in one dimension. +//| :param int x: Initial x position within the parent. +//| :param int y: Initial y position within the parent.""" +//| ... +//| +STATIC mp_obj_t displayio_group_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_scale, ARG_x, ARG_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_scale, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + 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); + + mp_int_t scale = args[ARG_scale].u_int; + if (scale < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_scale); + } + + displayio_group_t *self = m_new_obj(displayio_group_t); + self->base.type = &displayio_group_type; + common_hal_displayio_group_construct(self, scale, args[ARG_x].u_int, args[ARG_y].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +// Helper to ensure we have the native super class instead of a subclass. +displayio_group_t *native_group(mp_obj_t group_obj) { + mp_obj_t native_group = mp_obj_cast_to_native_base(group_obj, &displayio_group_type); + if (native_group == MP_OBJ_NULL) { + mp_raise_ValueError_varg(translate("Must be a %q subclass."), MP_QSTR_Group); + } + mp_obj_assert_native_inited(native_group); + return MP_OBJ_TO_PTR(native_group); +} + +//| hidden: bool +//| """True when the Group and all of it's layers are not visible. When False, the Group's layers +//| are visible if they haven't been hidden.""" +//| +STATIC mp_obj_t displayio_group_obj_get_hidden(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return mp_obj_new_bool(common_hal_displayio_group_get_hidden(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_hidden_obj, displayio_group_obj_get_hidden); + +STATIC mp_obj_t displayio_group_obj_set_hidden(mp_obj_t self_in, mp_obj_t hidden_obj) { + displayio_group_t *self = native_group(self_in); + + common_hal_displayio_group_set_hidden(self, mp_obj_is_true(hidden_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_hidden_obj, displayio_group_obj_set_hidden); + +MP_PROPERTY_GETSET(displayio_group_hidden_obj, + (mp_obj_t)&displayio_group_get_hidden_obj, + (mp_obj_t)&displayio_group_set_hidden_obj); + +//| scale: int +//| """Scales each pixel within the Group in both directions. For example, when scale=2 each pixel +//| will be represented by 2x2 pixels.""" +//| +STATIC mp_obj_t displayio_group_obj_get_scale(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_group_get_scale(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_scale_obj, displayio_group_obj_get_scale); + +STATIC mp_obj_t displayio_group_obj_set_scale(mp_obj_t self_in, mp_obj_t scale_obj) { + displayio_group_t *self = native_group(self_in); + + mp_int_t scale = mp_obj_get_int(scale_obj); + if (scale < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_scale); + } + common_hal_displayio_group_set_scale(self, scale); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_scale_obj, displayio_group_obj_set_scale); + +MP_PROPERTY_GETSET(displayio_group_scale_obj, + (mp_obj_t)&displayio_group_get_scale_obj, + (mp_obj_t)&displayio_group_set_scale_obj); + +//| x: int +//| """X position of the Group in the parent.""" +//| +STATIC mp_obj_t displayio_group_obj_get_x(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_group_get_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_x_obj, displayio_group_obj_get_x); + +STATIC mp_obj_t displayio_group_obj_set_x(mp_obj_t self_in, mp_obj_t x_obj) { + displayio_group_t *self = native_group(self_in); + + mp_int_t x = mp_obj_get_int(x_obj); + common_hal_displayio_group_set_x(self, x); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_x_obj, displayio_group_obj_set_x); + +MP_PROPERTY_GETSET(displayio_group_x_obj, + (mp_obj_t)&displayio_group_get_x_obj, + (mp_obj_t)&displayio_group_set_x_obj); + +//| y: int +//| """Y position of the Group in the parent.""" +//| +STATIC mp_obj_t displayio_group_obj_get_y(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_group_get_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_y_obj, displayio_group_obj_get_y); + +STATIC mp_obj_t displayio_group_obj_set_y(mp_obj_t self_in, mp_obj_t y_obj) { + displayio_group_t *self = native_group(self_in); + + mp_int_t y = mp_obj_get_int(y_obj); + common_hal_displayio_group_set_y(self, y); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_y_obj, displayio_group_obj_set_y); + +MP_PROPERTY_GETSET(displayio_group_y_obj, + (mp_obj_t)&displayio_group_get_y_obj, + (mp_obj_t)&displayio_group_set_y_obj); + +//| def append(self, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: +//| """Append a layer to the group. It will be drawn above other layers.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_append(mp_obj_t self_in, mp_obj_t layer) { + displayio_group_t *self = native_group(self_in); + common_hal_displayio_group_insert(self, common_hal_displayio_group_get_len(self), layer); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_append_obj, displayio_group_obj_append); + +//| def insert(self, index: int, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: +//| """Insert a layer into the group.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_insert(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t layer) { + displayio_group_t *self = native_group(self_in); + if ((size_t)MP_OBJ_SMALL_INT_VALUE(index_obj) == common_hal_displayio_group_get_len(self)) { + return displayio_group_obj_append(self_in, layer); + } + size_t index = mp_get_index(&displayio_group_type, common_hal_displayio_group_get_len(self), index_obj, false); + common_hal_displayio_group_insert(self, index, layer); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(displayio_group_insert_obj, displayio_group_obj_insert); + + +//| def index(self, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> int: +//| """Returns the index of the first copy of layer. Raises ValueError if not found.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_index(mp_obj_t self_in, mp_obj_t layer) { + displayio_group_t *self = native_group(self_in); + mp_int_t index = common_hal_displayio_group_index(self, layer); + if (index < 0) { + mp_raise_ValueError(translate("object not in sequence")); + } + return MP_OBJ_NEW_SMALL_INT(index); +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_index_obj, displayio_group_obj_index); + +//| def pop(self, i: int = -1) -> Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]: +//| """Remove the ith item and return it.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_pop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_i }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i, MP_ARG_INT, {.u_int = -1} }, + }; + 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); + + displayio_group_t *self = native_group(pos_args[0]); + + size_t index = mp_get_index(&displayio_group_type, + common_hal_displayio_group_get_len(self), + MP_OBJ_NEW_SMALL_INT(args[ARG_i].u_int), + false); + return common_hal_displayio_group_pop(self, index); +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_group_pop_obj, 1, displayio_group_obj_pop); + + +//| def remove(self, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: +//| """Remove the first copy of layer. Raises ValueError if it is not present.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_remove(mp_obj_t self_in, mp_obj_t layer) { + mp_obj_t index = displayio_group_obj_index(self_in, layer); + displayio_group_t *self = native_group(self_in); + + common_hal_displayio_group_pop(self, MP_OBJ_SMALL_INT_VALUE(index)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_remove_obj, displayio_group_obj_remove); + +//| def __bool__(self) -> bool: +//| ... +//| +//| def __len__(self) -> int: +//| """Returns the number of layers in a Group""" +//| ... +//| +STATIC mp_obj_t group_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + uint16_t len = common_hal_displayio_group_get_len(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __getitem__(self, index: int) -> Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]: +//| """Returns the value at the given index. +//| +//| This allows you to:: +//| +//| print(group[0])""" +//| ... +//| +//| def __setitem__(self, index: int, value: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: +//| """Sets the value at the given index. +//| +//| This allows you to:: +//| +//| group[0] = sprite""" +//| ... +//| +//| def __delitem__(self, index: int) -> None: +//| """Deletes the value at the given index. +//| +//| This allows you to:: +//| +//| del group[0]""" +//| ... +//| +STATIC mp_obj_t group_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value) { + displayio_group_t *self = native_group(self_in); + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + mp_raise_NotImplementedError(translate("Slices not supported")); + } else { + size_t index = mp_get_index(&displayio_group_type, common_hal_displayio_group_get_len(self), index_obj, false); + + if (value == MP_OBJ_SENTINEL) { + // load + return common_hal_displayio_group_get(self, index); + } else if (value == MP_OBJ_NULL) { + common_hal_displayio_group_pop(self, index); + } else { + common_hal_displayio_group_set(self, index, value); + } + } + return mp_const_none; +} + +//| def sort(self, key: function, reverse: bool) -> None: +//| """Sort the members of the group.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + displayio_group_t *self = native_group(pos_args[0]); + mp_obj_t *args = m_new(mp_obj_t, n_args); + for (size_t i = 1; i < n_args; ++i) { + args[i] = pos_args[i]; + } + args[0] = MP_OBJ_FROM_PTR(self->members); + return mp_obj_list_sort(n_args, args, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_group_sort_obj, 1, displayio_group_obj_sort); + +STATIC const mp_rom_map_elem_t displayio_group_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_hidden), MP_ROM_PTR(&displayio_group_hidden_obj) }, + { MP_ROM_QSTR(MP_QSTR_scale), MP_ROM_PTR(&displayio_group_scale_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&displayio_group_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&displayio_group_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&displayio_group_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_insert), MP_ROM_PTR(&displayio_group_insert_obj) }, + { MP_ROM_QSTR(MP_QSTR_index), MP_ROM_PTR(&displayio_group_index_obj) }, + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&displayio_group_pop_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&displayio_group_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&displayio_group_sort_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_group_locals_dict, displayio_group_locals_dict_table); + +const mp_obj_type_t displayio_group_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Group, + .make_new = displayio_group_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_group_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = group_subscr, + .unary_op = group_unary_op, + .getiter = mp_obj_new_generic_iterator, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/Group.h b/circuitpython/shared-bindings/displayio/Group.h new file mode 100644 index 0000000..266fce9 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Group.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_DISPLAYIO_GROUP_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_GROUP_H + +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_group_type; + +displayio_group_t *native_group(mp_obj_t group_obj); + +void common_hal_displayio_group_construct(displayio_group_t *self, uint32_t scale, mp_int_t x, mp_int_t y); +uint32_t common_hal_displayio_group_get_scale(displayio_group_t *self); +void common_hal_displayio_group_set_scale(displayio_group_t *self, uint32_t scale); +bool common_hal_displayio_group_get_hidden(displayio_group_t *self); +void common_hal_displayio_group_set_hidden(displayio_group_t *self, bool hidden); +mp_int_t common_hal_displayio_group_get_x(displayio_group_t *self); +void common_hal_displayio_group_set_x(displayio_group_t *self, mp_int_t x); +mp_int_t common_hal_displayio_group_get_y(displayio_group_t *self); +void common_hal_displayio_group_set_y(displayio_group_t *self, mp_int_t y); +void common_hal_displayio_group_append(displayio_group_t *self, mp_obj_t layer); +void common_hal_displayio_group_insert(displayio_group_t *self, size_t index, mp_obj_t layer); +size_t common_hal_displayio_group_get_len(displayio_group_t *self); +mp_obj_t common_hal_displayio_group_pop(displayio_group_t *self, size_t index); +mp_int_t common_hal_displayio_group_index(displayio_group_t *self, mp_obj_t layer); +mp_obj_t common_hal_displayio_group_get(displayio_group_t *self, size_t index); +void common_hal_displayio_group_set(displayio_group_t *self, size_t index, mp_obj_t layer); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_GROUP_H diff --git a/circuitpython/shared-bindings/displayio/I2CDisplay.c b/circuitpython/shared-bindings/displayio/I2CDisplay.c new file mode 100644 index 0000000..fbfbd04 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/I2CDisplay.c @@ -0,0 +1,134 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "shared-bindings/displayio/I2CDisplay.h" + +#include <stdint.h> +#include <string.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class I2CDisplay: +//| """Manage updating a display over I2C in the background while Python code runs. +//| It doesn't handle display initialization.""" +//| +//| def __init__(self, i2c_bus: busio.I2C, *, device_address: int, reset: Optional[microcontroller.Pin] = None) -> None: +//| """Create a I2CDisplay object associated with the given I2C bus and reset pin. +//| +//| The I2C bus and pins are then in use by the display until `displayio.release_displays()` is +//| called even after a reload. (It does this so CircuitPython can use the display after your code +//| is done.) So, the first time you initialize a display bus in code.py you should call +//| :py:func:`displayio.release_displays` first, otherwise it will error after the first code.py run. +//| +//| :param busio.I2C i2c_bus: The I2C bus that make up the clock and data lines +//| :param int device_address: The I2C address of the device +//| :param microcontroller.Pin reset: Reset pin. When None only software reset can be used""" +//| ... +//| +STATIC mp_obj_t displayio_i2cdisplay_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_i2c_bus, ARG_device_address, ARG_reset }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_device_address, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_reset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + }; + 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 *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj); + + mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c_bus].u_obj, &busio_i2c_type, MP_QSTR_i2c_bus); + displayio_i2cdisplay_obj_t *self = &allocate_display_bus_or_raise()->i2cdisplay_bus; + self->base.type = &displayio_i2cdisplay_type; + + common_hal_displayio_i2cdisplay_construct(self, + MP_OBJ_TO_PTR(i2c), args[ARG_device_address].u_int, reset); + return self; +} + +//| def reset(self) -> None: +//| """Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available.""" +//| ... +//| +STATIC mp_obj_t displayio_i2cdisplay_obj_reset(mp_obj_t self_in) { + displayio_i2cdisplay_obj_t *self = self_in; + + if (!common_hal_displayio_i2cdisplay_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_i2cdisplay_reset_obj, displayio_i2cdisplay_obj_reset); + +//| def send(self, command: int, data: ReadableBuffer) -> None: +//| """Sends the given command value followed by the full set of data. Display state, such as +//| vertical scroll, set via ``send`` may or may not be reset once the code is done.""" +//| ... +//| +STATIC mp_obj_t displayio_i2cdisplay_obj_send(mp_obj_t self, mp_obj_t command_obj, mp_obj_t data_obj) { + mp_int_t command_int = MP_OBJ_SMALL_INT_VALUE(command_obj); + if (!mp_obj_is_small_int(command_obj) || command_int > 255 || command_int < 0) { + mp_raise_ValueError(translate("Command must be an int between 0 and 255")); + } + uint8_t command = command_int; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); + + // Wait for display bus to be available. + while (!common_hal_displayio_i2cdisplay_begin_transaction(self)) { + RUN_BACKGROUND_TASKS; + } + uint8_t full_command[bufinfo.len + 1]; + full_command[0] = command; + memcpy(full_command + 1, ((uint8_t *)bufinfo.buf), bufinfo.len); + common_hal_displayio_i2cdisplay_send(self, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, full_command, bufinfo.len + 1); + common_hal_displayio_i2cdisplay_end_transaction(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(displayio_i2cdisplay_send_obj, displayio_i2cdisplay_obj_send); + +STATIC const mp_rom_map_elem_t displayio_i2cdisplay_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&displayio_i2cdisplay_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_i2cdisplay_send_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_i2cdisplay_locals_dict, displayio_i2cdisplay_locals_dict_table); + +const mp_obj_type_t displayio_i2cdisplay_type = { + { &mp_type_type }, + .name = MP_QSTR_I2CDisplay, + .make_new = displayio_i2cdisplay_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_i2cdisplay_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/I2CDisplay.h b/circuitpython/shared-bindings/displayio/I2CDisplay.h new file mode 100644 index 0000000..d40cd19 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/I2CDisplay.h @@ -0,0 +1,52 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 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_DISPLAYBUSIO_I2CDISPLAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_I2CDISPLAY_H + +#include "shared-module/displayio/I2CDisplay.h" + +#include "shared-bindings/displayio/__init__.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t displayio_i2cdisplay_type; + +void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t *self, + busio_i2c_obj_t *i2c, uint16_t device_address, const mcu_pin_obj_t *reset); + +void common_hal_displayio_i2cdisplay_deinit(displayio_i2cdisplay_obj_t *self); + +bool common_hal_displayio_i2cdisplay_reset(mp_obj_t self); +bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t self); + +bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t self); + +void common_hal_displayio_i2cdisplay_send(mp_obj_t self, display_byte_type_t byte_type, + display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); + +void common_hal_displayio_i2cdisplay_end_transaction(mp_obj_t self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_I2CDISPLAY_H diff --git a/circuitpython/shared-bindings/displayio/OnDiskBitmap.c b/circuitpython/shared-bindings/displayio/OnDiskBitmap.c new file mode 100644 index 0000000..aa749bf --- /dev/null +++ b/circuitpython/shared-bindings/displayio/OnDiskBitmap.c @@ -0,0 +1,161 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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 "shared-bindings/displayio/OnDiskBitmap.h" + +#include <stdint.h> + +#include "py/runtime.h" +#include "py/objproperty.h" +#include "supervisor/shared/translate.h" +#include "shared-bindings/displayio/OnDiskBitmap.h" + +//| class OnDiskBitmap: +//| """Loads values straight from disk. This minimizes memory use but can lead to +//| much slower pixel load times. These load times may result in frame tearing where only part of +//| the image is visible. +//| +//| It's easiest to use on a board with a built in display such as the `Hallowing M0 Express +//| <https://www.adafruit.com/product/3900>`_. +//| +//| .. code-block:: Python +//| +//| import board +//| import displayio +//| import time +//| import pulseio +//| +//| board.DISPLAY.auto_brightness = False +//| board.DISPLAY.brightness = 0 +//| splash = displayio.Group() +//| board.DISPLAY.show(splash) +//| +//| odb = displayio.OnDiskBitmap('/sample.bmp') +//| face = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader) +//| splash.append(face) +//| # Wait for the image to load. +//| board.DISPLAY.refresh(target_frames_per_second=60) +//| +//| # Fade up the backlight +//| for i in range(100): +//| board.DISPLAY.brightness = 0.01 * i +//| time.sleep(0.05) +//| +//| # Wait forever +//| while True: +//| pass""" +//| +//| def __init__(self, file: Union[str,typing.BinaryIO]) -> None: +//| """Create an OnDiskBitmap object with the given file. +//| +//| :param file file: The name of the bitmap file. For backwards compatibility, a file opened in binary mode may also be passed. +//| +//| Older versions of CircuitPython required a file opened in binary +//| mode. CircuitPython 7.0 modified OnDiskBitmap so that it takes a +//| filename instead, and opens the file internally. A future version +//| of CircuitPython will remove the ability to pass in an opened file. +//| """ +//| ... +//| +STATIC mp_obj_t displayio_ondiskbitmap_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_obj_t arg = all_args[0]; + + if (mp_obj_is_str(arg)) { + arg = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), arg, MP_ROM_QSTR(MP_QSTR_rb)); + } + if (!mp_obj_is_type(arg, &mp_type_fileio)) { + mp_raise_TypeError(translate("file must be a file opened in byte mode")); + } + + displayio_ondiskbitmap_t *self = m_new_obj(displayio_ondiskbitmap_t); + self->base.type = &displayio_ondiskbitmap_type; + common_hal_displayio_ondiskbitmap_construct(self, MP_OBJ_TO_PTR(arg)); + + return MP_OBJ_FROM_PTR(self); +} + +//| width: int +//| """Width of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_ondiskbitmap_obj_get_width(mp_obj_t self_in) { + displayio_ondiskbitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_ondiskbitmap_get_width(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_ondiskbitmap_get_width_obj, displayio_ondiskbitmap_obj_get_width); + +MP_PROPERTY_GETTER(displayio_ondiskbitmap_width_obj, + (mp_obj_t)&displayio_ondiskbitmap_get_width_obj); + +//| height: int +//| """Height of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_ondiskbitmap_obj_get_height(mp_obj_t self_in) { + displayio_ondiskbitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_ondiskbitmap_get_height(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_ondiskbitmap_get_height_obj, displayio_ondiskbitmap_obj_get_height); + +MP_PROPERTY_GETTER(displayio_ondiskbitmap_height_obj, + (mp_obj_t)&displayio_ondiskbitmap_get_height_obj); + +//| pixel_shader: Union[ColorConverter, Palette] +//| """The image's pixel_shader. The type depends on the underlying +//| bitmap's structure. The pixel shader can be modified (e.g., to set the +//| transparent pixel or, for palette shaded images, to update the palette.)""" +//| +STATIC mp_obj_t displayio_ondiskbitmap_obj_get_pixel_shader(mp_obj_t self_in) { + displayio_ondiskbitmap_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_displayio_ondiskbitmap_get_pixel_shader(self); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_ondiskbitmap_get_pixel_shader_obj, displayio_ondiskbitmap_obj_get_pixel_shader); + +const mp_obj_property_t displayio_ondiskbitmap_pixel_shader_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&displayio_ondiskbitmap_get_pixel_shader_obj, + (mp_obj_t)MP_ROM_NONE, + (mp_obj_t)MP_ROM_NONE}, +}; + + +STATIC const mp_rom_map_elem_t displayio_ondiskbitmap_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_ondiskbitmap_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&displayio_ondiskbitmap_pixel_shader_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_ondiskbitmap_width_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_ondiskbitmap_locals_dict, displayio_ondiskbitmap_locals_dict_table); + +const mp_obj_type_t displayio_ondiskbitmap_type = { + { &mp_type_type }, + .name = MP_QSTR_OnDiskBitmap, + .make_new = displayio_ondiskbitmap_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_ondiskbitmap_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/OnDiskBitmap.h b/circuitpython/shared-bindings/displayio/OnDiskBitmap.h new file mode 100644 index 0000000..6d534ec --- /dev/null +++ b/circuitpython/shared-bindings/displayio/OnDiskBitmap.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_DISPLAYIO_ONDISKBITMAP_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKBITMAP_H + +#include "shared-module/displayio/OnDiskBitmap.h" +#include "extmod/vfs_fat.h" + +extern const mp_obj_type_t displayio_ondiskbitmap_type; + +void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self, pyb_file_obj_t *file); + +uint32_t common_hal_displayio_ondiskbitmap_get_pixel(displayio_ondiskbitmap_t *bitmap, + int16_t x, int16_t y); + +uint16_t common_hal_displayio_ondiskbitmap_get_height(displayio_ondiskbitmap_t *self); +mp_obj_t common_hal_displayio_ondiskbitmap_get_pixel_shader(displayio_ondiskbitmap_t *self); +uint16_t common_hal_displayio_ondiskbitmap_get_width(displayio_ondiskbitmap_t *self); +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKBITMAP_H diff --git a/circuitpython/shared-bindings/displayio/Palette.c b/circuitpython/shared-bindings/displayio/Palette.c new file mode 100644 index 0000000..ad6d7c3 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Palette.c @@ -0,0 +1,216 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "shared-bindings/displayio/Palette.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Palette: +//| """Map a pixel palette_index to a full color. Colors are transformed to the display's format internally to +//| save memory.""" +//| +//| def __init__(self, color_count: int) -> None: +//| """Create a Palette object to store a set number of colors. +//| +//| :param int color_count: The number of colors in the Palette""" +//| ... +//| +// TODO(tannewt): Add support for other color formats. +// TODO(tannewt): Add support for 8-bit alpha blending. +//| +STATIC mp_obj_t displayio_palette_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_color_count }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_color_count, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + 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); + + displayio_palette_t *self = m_new_obj(displayio_palette_t); + self->base.type = &displayio_palette_type; + common_hal_displayio_palette_construct(self, args[ARG_color_count].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| def __bool__(self) -> bool: +//| ... +//| +//| def __len__(self) -> int: +//| """Returns the number of colors in a Palette""" +//| ... +//| +STATIC mp_obj_t group_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_const_true; + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_palette_get_len(self)); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __getitem__(self, index: int) -> Optional[int]: +//| r"""Return the pixel color at the given index as an integer.""" +//| ... +//| +//| def __setitem__(self, index: int, value: Union[int, ReadableBuffer, Tuple[int, int, int]]) -> None: +//| r"""Sets the pixel color at the given index. The index should be an integer in the range 0 to color_count-1. +//| +//| The value argument represents a color, and can be from 0x000000 to 0xFFFFFF (to represent an RGB value). +//| Value can be an int, bytes (3 bytes (RGB) or 4 bytes (RGB + pad byte)), bytearray, +//| or a tuple or list of 3 integers. +//| +//| This allows you to:: +//| +//| palette[0] = 0xFFFFFF # set using an integer +//| palette[1] = b'\xff\xff\x00' # set using 3 bytes +//| palette[2] = b'\xff\xff\x00\x00' # set using 4 bytes +//| palette[3] = bytearray(b'\x00\x00\xFF') # set using a bytearay of 3 or 4 bytes +//| palette[4] = (10, 20, 30) # set using a tuple of 3 integers""" +//| ... +//| +STATIC mp_obj_t palette_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete item + return MP_OBJ_NULL; // op not supported + } + // Slicing not supported. Use a duplicate Palette to swap multiple colors atomically. + if (mp_obj_is_type(index_in, &mp_type_slice)) { + return MP_OBJ_NULL; + } + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + size_t index = mp_get_index(&displayio_palette_type, self->color_count, index_in, false); + // index read + if (value == MP_OBJ_SENTINEL) { + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_palette_get_color(self, index)); + } + + // Convert a tuple or list to a bytearray. + if (mp_obj_is_type(value, &mp_type_tuple) || + mp_obj_is_type(value, &mp_type_list)) { + value = mp_type_bytes.make_new(&mp_type_bytes, 1, 0, &value); + } + + uint32_t color; + mp_int_t int_value; + mp_buffer_info_t bufinfo; + if (mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) { + if (bufinfo.typecode != 'b' && bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) { + mp_raise_ValueError(translate("color buffer must be a bytearray or array of type 'b' or 'B'")); + } + uint8_t *buf = bufinfo.buf; + if (bufinfo.len == 3 || bufinfo.len == 4) { + color = buf[0] << 16 | buf[1] << 8 | buf[2]; + } else { + mp_raise_ValueError(translate("color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)")); + } + } else if (mp_obj_get_int_maybe(value, &int_value)) { + if (int_value < 0 || int_value > 0xffffff) { + mp_raise_TypeError(translate("color must be between 0x000000 and 0xffffff")); + } + color = int_value; + } else { + mp_raise_TypeError(translate("color buffer must be a buffer, tuple, list, or int")); + } + common_hal_displayio_palette_set_color(self, index, color); + return mp_const_none; +} + +//| def make_transparent(self, palette_index: int) -> None: +//| ... +//| +STATIC mp_obj_t displayio_palette_obj_make_transparent(mp_obj_t self_in, mp_obj_t palette_index_obj) { + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t palette_index; + if (!mp_obj_get_int_maybe(palette_index_obj, &palette_index)) { + mp_raise_ValueError(translate("palette_index should be an int")); + } + common_hal_displayio_palette_make_transparent(self, palette_index); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_palette_make_transparent_obj, displayio_palette_obj_make_transparent); + +//| def make_opaque(self, palette_index: int) -> None: +//| ... +//| +STATIC mp_obj_t displayio_palette_obj_make_opaque(mp_obj_t self_in, mp_obj_t palette_index_obj) { + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t palette_index; + if (!mp_obj_get_int_maybe(palette_index_obj, &palette_index)) { + mp_raise_ValueError(translate("palette_index should be an int")); + } + common_hal_displayio_palette_make_opaque(self, palette_index); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_palette_make_opaque_obj, displayio_palette_obj_make_opaque); + +//| def is_transparent(self, palette_index: int) -> bool: +//| """Returns `True` if the palette index is transparent. Returns `False` if opaque.""" +//| ... +//| +STATIC mp_obj_t displayio_palette_obj_is_transparent(mp_obj_t self_in, mp_obj_t palette_index_obj) { + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t palette_index; + if (!mp_obj_get_int_maybe(palette_index_obj, &palette_index)) { + mp_raise_ValueError(translate("palette_index should be an int")); + } + return mp_obj_new_bool(common_hal_displayio_palette_is_transparent(self, palette_index)); +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_palette_is_transparent_obj, displayio_palette_obj_is_transparent); + +STATIC const mp_rom_map_elem_t displayio_palette_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_make_transparent), MP_ROM_PTR(&displayio_palette_make_transparent_obj) }, + { MP_ROM_QSTR(MP_QSTR_make_opaque), MP_ROM_PTR(&displayio_palette_make_opaque_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_transparent), MP_ROM_PTR(&displayio_palette_is_transparent_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_palette_locals_dict, displayio_palette_locals_dict_table); + +const mp_obj_type_t displayio_palette_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Palette, + .make_new = displayio_palette_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_palette_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = palette_subscr, + .unary_op = group_unary_op, + .getiter = mp_obj_new_generic_iterator, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/Palette.h b/circuitpython/shared-bindings/displayio/Palette.h new file mode 100644 index 0000000..d9a7980 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Palette.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_DISPLAYIO_PALETTE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_PALETTE_H + +#include "shared-module/displayio/Palette.h" + +extern const mp_obj_type_t displayio_palette_type; + +void common_hal_displayio_palette_construct(displayio_palette_t *self, uint16_t color_count); +void common_hal_displayio_palette_set_color(displayio_palette_t *self, uint32_t palette_index, uint32_t color); +uint32_t common_hal_displayio_palette_get_color(displayio_palette_t *self, uint32_t palette_index); +uint32_t common_hal_displayio_palette_get_len(displayio_palette_t *self); + +void common_hal_displayio_palette_make_opaque(displayio_palette_t *self, uint32_t palette_index); +void common_hal_displayio_palette_make_transparent(displayio_palette_t *self, uint32_t palette_index); +bool common_hal_displayio_palette_is_transparent(displayio_palette_t *self, uint32_t palette_index); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_PALETTE_H diff --git a/circuitpython/shared-bindings/displayio/Shape.c b/circuitpython/shared-bindings/displayio/Shape.c new file mode 100644 index 0000000..f39e782 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Shape.c @@ -0,0 +1,117 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "shared-bindings/displayio/Shape.h" + +#include <stdint.h> + +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Shape: +//| """Represents a shape made by defining boundaries that may be mirrored.""" +//| +//| def __init__(self, width: int, height: int, *, mirror_x: bool = False, mirror_y: bool = False) -> None: +//| """Create a Shape object with the given fixed size. Each pixel is one bit and is stored by the +//| column boundaries of the shape on each row. Each row's boundary defaults to the full row. +//| +//| :param int width: The number of pixels wide +//| :param int height: The number of pixels high +//| :param bool mirror_x: When true the left boundary is mirrored to the right. +//| :param bool mirror_y: When true the top boundary is mirrored to the bottom.""" +//| ... +//| +STATIC mp_obj_t displayio_shape_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_width, ARG_height, ARG_mirror_x, ARG_mirror_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_mirror_x, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_mirror_y, MP_ARG_BOOL | 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); + + mp_int_t width = args[ARG_width].u_int; + if (width < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_width); + } + mp_int_t height = args[ARG_height].u_int; + if (height < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_height); + } + + displayio_shape_t *self = m_new_obj(displayio_shape_t); + self->base.type = &displayio_shape_type; + common_hal_displayio_shape_construct(self, + width, + height, + args[ARG_mirror_x].u_bool, + args[ARG_mirror_y].u_bool); + + return MP_OBJ_FROM_PTR(self); +} + + +//| def set_boundary(self, y: int, start_x: int, end_x: int) -> None: +//| """Loads pre-packed data into the given row.""" +//| ... +//| +STATIC mp_obj_t displayio_shape_obj_set_boundary(size_t n_args, const mp_obj_t *args) { + (void)n_args; + displayio_shape_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t y; + if (!mp_obj_get_int_maybe(args[1], &y)) { + mp_raise_ValueError(translate("y should be an int")); + } + mp_int_t start_x; + if (!mp_obj_get_int_maybe(args[2], &start_x)) { + mp_raise_ValueError(translate("start_x should be an int")); + } + mp_int_t end_x; + if (!mp_obj_get_int_maybe(args[3], &end_x)) { + mp_raise_ValueError(translate("end_x should be an int")); + } + common_hal_displayio_shape_set_boundary(self, y, start_x, end_x); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(displayio_shape_set_boundary_obj, 4, 4, displayio_shape_obj_set_boundary); + +STATIC const mp_rom_map_elem_t displayio_shape_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_set_boundary), MP_ROM_PTR(&displayio_shape_set_boundary_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_shape_locals_dict, displayio_shape_locals_dict_table); + +const mp_obj_type_t displayio_shape_type = { + { &mp_type_type }, + .name = MP_QSTR_Shape, + .make_new = displayio_shape_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_shape_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/Shape.h b/circuitpython/shared-bindings/displayio/Shape.h new file mode 100644 index 0000000..961b57c --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Shape.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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_DISPLAYIO_SHAPE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_SHAPE_H + +#include "shared-module/displayio/Shape.h" + +extern const mp_obj_type_t displayio_shape_type; + +void common_hal_displayio_shape_construct(displayio_shape_t *self, uint32_t width, + uint32_t height, bool mirror_x, bool mirror_y); + +void common_hal_displayio_shape_set_boundary(displayio_shape_t *self, uint16_t y, uint16_t start_x, + uint16_t end_x); +uint32_t common_hal_displayio_shape_get_pixel(void *shape, int16_t x, int16_t y); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_SHAPE_H diff --git a/circuitpython/shared-bindings/displayio/TileGrid.c b/circuitpython/shared-bindings/displayio/TileGrid.c new file mode 100644 index 0000000..113721b --- /dev/null +++ b/circuitpython/shared-bindings/displayio/TileGrid.c @@ -0,0 +1,502 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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 "shared-bindings/displayio/TileGrid.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Bitmap.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/OnDiskBitmap.h" +#include "shared-bindings/displayio/Palette.h" +#include "shared-bindings/displayio/Shape.h" +#include "supervisor/shared/translate.h" + +//| class TileGrid: +//| """A grid of tiles sourced out of one bitmap +//| +//| Position a grid of tiles sourced from a bitmap and pixel_shader combination. Multiple grids +//| can share bitmaps and pixel shaders. +//| +//| A single tile grid is also known as a Sprite.""" +//| +//| def __init__(self, bitmap: Union[Bitmap, OnDiskBitmap, Shape], *, pixel_shader: Union[ColorConverter, Palette], width: int = 1, height: int = 1, tile_width: Optional[int] = None, tile_height: Optional[int] = None, default_tile: int = 0, x: int = 0, y: int = 0) -> None: +//| """Create a TileGrid object. The bitmap is source for 2d pixels. The pixel_shader is used to +//| convert the value and its location to a display native pixel color. This may be a simple color +//| palette lookup, a gradient, a pattern or a color transformer. +//| +//| To save RAM usage, tile values are only allowed in the range from 0 to 255 inclusive (single byte values). +//| +//| tile_width and tile_height match the height of the bitmap by default. +//| +//| :param Bitmap,OnDiskBitmap,Shape bitmap: The bitmap storing one or more tiles. +//| :param ColorConverter,Palette pixel_shader: The pixel shader that produces colors from values +//| :param int width: Width of the grid in tiles. +//| :param int height: Height of the grid in tiles. +//| :param int tile_width: Width of a single tile in pixels. Defaults to the full Bitmap and must evenly divide into the Bitmap's dimensions. +//| :param int tile_height: Height of a single tile in pixels. Defaults to the full Bitmap and must evenly divide into the Bitmap's dimensions. +//| :param int default_tile: Default tile index to show. +//| :param int x: Initial x position of the left edge within the parent. +//| :param int y: Initial y position of the top edge within the parent.""" +//| +STATIC mp_obj_t displayio_tilegrid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_bitmap, ARG_pixel_shader, ARG_width, ARG_height, ARG_tile_width, ARG_tile_height, ARG_default_tile, ARG_x, ARG_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_tile_width, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_tile_height, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_default_tile, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + 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); + + mp_obj_t bitmap = args[ARG_bitmap].u_obj; + + uint16_t bitmap_width; + uint16_t bitmap_height; + mp_obj_t native = mp_obj_cast_to_native_base(bitmap, &displayio_shape_type); + if (native != MP_OBJ_NULL) { + displayio_shape_t *bmp = MP_OBJ_TO_PTR(native); + bitmap_width = bmp->width; + bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_bitmap_type)) { + displayio_bitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + bitmap_width = bmp->width; + bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_ondiskbitmap_type)) { + displayio_ondiskbitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + bitmap_width = bmp->width; + bitmap_height = bmp->height; + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_bitmap); + } + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + if (!mp_obj_is_type(pixel_shader, &displayio_colorconverter_type) && + !mp_obj_is_type(pixel_shader, &displayio_palette_type)) { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_pixel_shader); + } + uint16_t tile_width = args[ARG_tile_width].u_int; + if (tile_width == 0) { + tile_width = bitmap_width; + } + uint16_t tile_height = args[ARG_tile_height].u_int; + if (tile_height == 0) { + tile_height = bitmap_height; + } + if (bitmap_width % tile_width != 0) { + mp_raise_ValueError(translate("Tile width must exactly divide bitmap width")); + } + if (bitmap_height % tile_height != 0) { + mp_raise_ValueError(translate("Tile height must exactly divide bitmap height")); + } + + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + + displayio_tilegrid_t *self = m_new_obj(displayio_tilegrid_t); + self->base.type = &displayio_tilegrid_type; + common_hal_displayio_tilegrid_construct(self, native, + bitmap_width / tile_width, bitmap_height / tile_height, + pixel_shader, args[ARG_width].u_int, args[ARG_height].u_int, + tile_width, tile_height, x, y, args[ARG_default_tile].u_int); + return MP_OBJ_FROM_PTR(self); +} + +// Helper to ensure we have the native super class instead of a subclass. +static displayio_tilegrid_t *native_tilegrid(mp_obj_t tilegrid_obj) { + mp_obj_t native_tilegrid = mp_obj_cast_to_native_base(tilegrid_obj, &displayio_tilegrid_type); + mp_obj_assert_native_inited(native_tilegrid); + return MP_OBJ_TO_PTR(native_tilegrid); +} + +//| hidden: bool +//| """True when the TileGrid is hidden. This may be False even when a part of a hidden Group.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_hidden(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_hidden(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_hidden_obj, displayio_tilegrid_obj_get_hidden); + +STATIC mp_obj_t displayio_tilegrid_obj_set_hidden(mp_obj_t self_in, mp_obj_t hidden_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_hidden(self, mp_obj_is_true(hidden_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_hidden_obj, displayio_tilegrid_obj_set_hidden); + +MP_PROPERTY_GETSET(displayio_tilegrid_hidden_obj, + (mp_obj_t)&displayio_tilegrid_get_hidden_obj, + (mp_obj_t)&displayio_tilegrid_set_hidden_obj); + +//| x: int +//| """X position of the left edge in the parent.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_x(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_x_obj, displayio_tilegrid_obj_get_x); + +STATIC mp_obj_t displayio_tilegrid_obj_set_x(mp_obj_t self_in, mp_obj_t x_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + mp_int_t x = mp_obj_get_int(x_obj); + common_hal_displayio_tilegrid_set_x(self, x); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_x_obj, displayio_tilegrid_obj_set_x); + +MP_PROPERTY_GETSET(displayio_tilegrid_x_obj, + (mp_obj_t)&displayio_tilegrid_get_x_obj, + (mp_obj_t)&displayio_tilegrid_set_x_obj); + +//| y: int +//| """Y position of the top edge in the parent.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_y(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_y_obj, displayio_tilegrid_obj_get_y); + +STATIC mp_obj_t displayio_tilegrid_obj_set_y(mp_obj_t self_in, mp_obj_t y_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + mp_int_t y = mp_obj_get_int(y_obj); + common_hal_displayio_tilegrid_set_y(self, y); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_y_obj, displayio_tilegrid_obj_set_y); + +MP_PROPERTY_GETSET(displayio_tilegrid_y_obj, + (mp_obj_t)&displayio_tilegrid_get_y_obj, + (mp_obj_t)&displayio_tilegrid_set_y_obj); + +//| width: int +//| """Width of the tilegrid in tiles.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_width(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_width_obj, displayio_tilegrid_obj_get_width); + +MP_PROPERTY_GETTER(displayio_tilegrid_width_obj, + (mp_obj_t)&displayio_tilegrid_get_width_obj); + +//| height: int +//| """Height of the tilegrid in tiles.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_height(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_height_obj, displayio_tilegrid_obj_get_height); + +MP_PROPERTY_GETTER(displayio_tilegrid_height_obj, + (mp_obj_t)&displayio_tilegrid_get_height_obj); + +//| tile_width: int +//| """Width of a single tile in pixels.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_tile_width(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_tile_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_tile_width_obj, displayio_tilegrid_obj_get_tile_width); + +MP_PROPERTY_GETTER(displayio_tilegrid_tile_width_obj, + (mp_obj_t)&displayio_tilegrid_get_tile_width_obj); + +//| tile_height: int +//| """Height of a single tile in pixels.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_tile_height(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_tile_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_tile_height_obj, displayio_tilegrid_obj_get_tile_height); + +MP_PROPERTY_GETTER(displayio_tilegrid_tile_height_obj, + (mp_obj_t)&displayio_tilegrid_get_tile_height_obj); + +//| flip_x: bool +//| """If true, the left edge rendered will be the right edge of the right-most tile.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_flip_x(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_flip_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_flip_x_obj, displayio_tilegrid_obj_get_flip_x); + +STATIC mp_obj_t displayio_tilegrid_obj_set_flip_x(mp_obj_t self_in, mp_obj_t flip_x_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_flip_x(self, mp_obj_is_true(flip_x_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_flip_x_obj, displayio_tilegrid_obj_set_flip_x); + +MP_PROPERTY_GETSET(displayio_tilegrid_flip_x_obj, + (mp_obj_t)&displayio_tilegrid_get_flip_x_obj, + (mp_obj_t)&displayio_tilegrid_set_flip_x_obj); + +//| flip_y: bool +//| """If true, the top edge rendered will be the bottom edge of the bottom-most tile.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_flip_y(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_flip_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_flip_y_obj, displayio_tilegrid_obj_get_flip_y); + +STATIC mp_obj_t displayio_tilegrid_obj_set_flip_y(mp_obj_t self_in, mp_obj_t flip_y_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_flip_y(self, mp_obj_is_true(flip_y_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_flip_y_obj, displayio_tilegrid_obj_set_flip_y); + +MP_PROPERTY_GETSET(displayio_tilegrid_flip_y_obj, + (mp_obj_t)&displayio_tilegrid_get_flip_y_obj, + (mp_obj_t)&displayio_tilegrid_set_flip_y_obj); + + +//| transpose_xy: bool +//| """If true, the TileGrid's axis will be swapped. When combined with mirroring, any 90 degree +//| rotation can be achieved along with the corresponding mirrored version.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_transpose_xy(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_transpose_xy(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_transpose_xy_obj, displayio_tilegrid_obj_get_transpose_xy); + +STATIC mp_obj_t displayio_tilegrid_obj_set_transpose_xy(mp_obj_t self_in, mp_obj_t transpose_xy_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_transpose_xy(self, mp_obj_is_true(transpose_xy_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_transpose_xy_obj, displayio_tilegrid_obj_set_transpose_xy); + +MP_PROPERTY_GETSET(displayio_tilegrid_transpose_xy_obj, + (mp_obj_t)&displayio_tilegrid_get_transpose_xy_obj, + (mp_obj_t)&displayio_tilegrid_set_transpose_xy_obj); + +//| pixel_shader: Union[ColorConverter, Palette] +//| """The pixel shader of the tilegrid.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_pixel_shader(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return common_hal_displayio_tilegrid_get_pixel_shader(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_pixel_shader_obj, displayio_tilegrid_obj_get_pixel_shader); + +STATIC mp_obj_t displayio_tilegrid_obj_set_pixel_shader(mp_obj_t self_in, mp_obj_t pixel_shader) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + if (!mp_obj_is_type(pixel_shader, &displayio_palette_type) && !mp_obj_is_type(pixel_shader, &displayio_colorconverter_type)) { + mp_raise_TypeError(translate("pixel_shader must be displayio.Palette or displayio.ColorConverter")); + } + + common_hal_displayio_tilegrid_set_pixel_shader(self, pixel_shader); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_pixel_shader_obj, displayio_tilegrid_obj_set_pixel_shader); + +MP_PROPERTY_GETSET(displayio_tilegrid_pixel_shader_obj, + (mp_obj_t)&displayio_tilegrid_get_pixel_shader_obj, + (mp_obj_t)&displayio_tilegrid_set_pixel_shader_obj); + +//| bitmap: Union[Bitmap,OnDiskBitmap,Shape] +//| """The bitmap of the tilegrid.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_bitmap(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return common_hal_displayio_tilegrid_get_bitmap(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_bitmap_obj, displayio_tilegrid_obj_get_bitmap); + +STATIC mp_obj_t displayio_tilegrid_obj_set_bitmap(mp_obj_t self_in, mp_obj_t bitmap) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + uint16_t new_bitmap_width; + uint16_t new_bitmap_height; + mp_obj_t native = mp_obj_cast_to_native_base(bitmap, &displayio_shape_type); + if (native != MP_OBJ_NULL) { + displayio_shape_t *bmp = MP_OBJ_TO_PTR(native); + new_bitmap_width = bmp->width; + new_bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_bitmap_type)) { + displayio_bitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + new_bitmap_width = bmp->width; + new_bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_ondiskbitmap_type)) { + displayio_ondiskbitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + new_bitmap_width = bmp->width; + new_bitmap_height = bmp->height; + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_bitmap); + } + + mp_obj_t old_native = mp_obj_cast_to_native_base(self->bitmap, &displayio_shape_type); + if (old_native != MP_OBJ_NULL) { + displayio_shape_t *old_bmp = MP_OBJ_TO_PTR(old_native); + if (old_bmp->width != new_bitmap_width || old_bmp->height != new_bitmap_height) { + mp_raise_ValueError(translate("New bitmap must be same size as old bitmap")); + } + } else if (mp_obj_is_type(self->bitmap, &displayio_bitmap_type)) { + displayio_bitmap_t *old_bmp = MP_OBJ_TO_PTR(self->bitmap); + old_native = self->bitmap; + if (old_bmp->width != new_bitmap_width || old_bmp->height != new_bitmap_height) { + mp_raise_ValueError(translate("New bitmap must be same size as old bitmap")); + } + } else if (mp_obj_is_type(self->bitmap, &displayio_ondiskbitmap_type)) { + displayio_ondiskbitmap_t *old_bmp = MP_OBJ_TO_PTR(self->bitmap); + old_native = self->bitmap; + if (old_bmp->width != new_bitmap_width || old_bmp->height != new_bitmap_height) { + mp_raise_ValueError(translate("New bitmap must be same size as old bitmap")); + } + } + + common_hal_displayio_tilegrid_set_bitmap(self, bitmap); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_bitmap_obj, displayio_tilegrid_obj_set_bitmap); + +MP_PROPERTY_GETSET(displayio_tilegrid_bitmap_obj, + (mp_obj_t)&displayio_tilegrid_get_bitmap_obj, + (mp_obj_t)&displayio_tilegrid_set_bitmap_obj); + +//| def __getitem__(self, index: Union[Tuple[int, int], int]) -> int: +//| """Returns the tile index at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| print(grid[0])""" +//| ... +//| +//| def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None: +//| """Sets the tile index at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| grid[0] = 10 +//| +//| or:: +//| +//| grid[0,0] = 10""" +//| ... +//| +STATIC mp_obj_t tilegrid_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + mp_raise_NotImplementedError(translate("Slices not supported")); + } else { + uint16_t x = 0; + uint16_t y = 0; + if (mp_obj_is_small_int(index_obj)) { + mp_int_t i = MP_OBJ_SMALL_INT_VALUE(index_obj); + uint16_t width = common_hal_displayio_tilegrid_get_width(self); + x = i % width; + y = i / width; + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(index_obj, 2, &items); + x = mp_obj_get_int(items[0]); + y = mp_obj_get_int(items[1]); + } + if (x >= common_hal_displayio_tilegrid_get_width(self) || + y >= common_hal_displayio_tilegrid_get_height(self)) { + mp_raise_IndexError(translate("Tile index out of bounds")); + } + + if (value_obj == MP_OBJ_SENTINEL) { + // load + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_tile(self, x, y)); + } else if (value_obj == mp_const_none) { + return MP_OBJ_NULL; // op not supported + } else { + mp_int_t value = mp_obj_get_int(value_obj); + if (value < 0 || value > 255) { + mp_raise_ValueError(translate("Tile value out of bounds")); + } + common_hal_displayio_tilegrid_set_tile(self, x, y, value); + } + } + return mp_const_none; +} + +STATIC const mp_rom_map_elem_t displayio_tilegrid_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_hidden), MP_ROM_PTR(&displayio_tilegrid_hidden_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&displayio_tilegrid_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&displayio_tilegrid_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_tilegrid_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_tilegrid_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_tile_width), MP_ROM_PTR(&displayio_tilegrid_tile_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_tile_height), MP_ROM_PTR(&displayio_tilegrid_tile_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_flip_x), MP_ROM_PTR(&displayio_tilegrid_flip_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_flip_y), MP_ROM_PTR(&displayio_tilegrid_flip_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_transpose_xy), MP_ROM_PTR(&displayio_tilegrid_transpose_xy_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&displayio_tilegrid_pixel_shader_obj) }, + { MP_ROM_QSTR(MP_QSTR_bitmap), MP_ROM_PTR(&displayio_tilegrid_bitmap_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_tilegrid_locals_dict, displayio_tilegrid_locals_dict_table); + +const mp_obj_type_t displayio_tilegrid_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_TileGrid, + .make_new = displayio_tilegrid_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_tilegrid_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = tilegrid_subscr, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/TileGrid.h b/circuitpython/shared-bindings/displayio/TileGrid.h new file mode 100644 index 0000000..2c79412 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/TileGrid.h @@ -0,0 +1,71 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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_DISPLAYIO_TILEGRID_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H + +#include "shared-module/displayio/TileGrid.h" + +extern const mp_obj_type_t displayio_tilegrid_type; + +void common_hal_displayio_tilegrid_construct(displayio_tilegrid_t *self, mp_obj_t bitmap, + uint16_t bitmap_width_in_tiles, uint16_t bitmap_height_in_tiles, + mp_obj_t pixel_shader, uint16_t width, uint16_t height, + uint16_t tile_width, uint16_t tile_height, uint16_t x, uint16_t y, uint8_t default_tile); + +bool common_hal_displayio_tilegrid_get_hidden(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_hidden(displayio_tilegrid_t *self, bool hidden); +mp_int_t common_hal_displayio_tilegrid_get_x(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_x(displayio_tilegrid_t *self, mp_int_t x); +mp_int_t common_hal_displayio_tilegrid_get_y(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_y(displayio_tilegrid_t *self, mp_int_t y); +mp_obj_t common_hal_displayio_tilegrid_get_pixel_shader(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_pixel_shader(displayio_tilegrid_t *self, mp_obj_t pixel_shader); + +mp_obj_t common_hal_displayio_tilegrid_get_bitmap(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_bitmap(displayio_tilegrid_t *self, mp_obj_t bitmap); + + +bool common_hal_displayio_tilegrid_get_flip_x(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_flip_x(displayio_tilegrid_t *self, bool flip_x); +bool common_hal_displayio_tilegrid_get_flip_y(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_flip_y(displayio_tilegrid_t *self, bool flip_y); +bool common_hal_displayio_tilegrid_get_transpose_xy(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_transpose_xy(displayio_tilegrid_t *self, bool transpose_xy); + +uint16_t common_hal_displayio_tilegrid_get_width(displayio_tilegrid_t *self); +uint16_t common_hal_displayio_tilegrid_get_height(displayio_tilegrid_t *self); + +uint16_t common_hal_displayio_tilegrid_get_tile_width(displayio_tilegrid_t *self); +uint16_t common_hal_displayio_tilegrid_get_tile_height(displayio_tilegrid_t *self); + +uint8_t common_hal_displayio_tilegrid_get_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y); +void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y, uint8_t tile_index); + +// Private API for scrolling the TileGrid. +void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint16_t x, uint16_t y); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H diff --git a/circuitpython/shared-bindings/displayio/__init__.c b/circuitpython/shared-bindings/displayio/__init__.c new file mode 100644 index 0000000..2e52f12 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/__init__.c @@ -0,0 +1,106 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/enum.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/displayio/__init__.h" +#include "shared-bindings/displayio/Bitmap.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/Display.h" +#include "shared-bindings/displayio/EPaperDisplay.h" +#include "shared-bindings/displayio/FourWire.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/displayio/I2CDisplay.h" +#include "shared-bindings/displayio/OnDiskBitmap.h" +#include "shared-bindings/displayio/Palette.h" +#if CIRCUITPY_PARALLELDISPLAY +#include "shared-bindings/paralleldisplay/ParallelBus.h" +#endif +#include "shared-bindings/displayio/Shape.h" +#include "shared-bindings/displayio/TileGrid.h" + +//| """Native helpers for driving displays +//| +//| The `displayio` module contains classes to manage display output +//| including synchronizing with refresh rates and partial updating. +//| +//| For more a more thorough explanation and guide for using `displayio`, please +//| refer to `this Learn guide +//| <https://learn.adafruit.com/circuitpython-display-support-using-displayio>`_. +//| """ +//| + +//| import paralleldisplay + +//| def release_displays() -> None: +//| """Releases any actively used displays so their busses and pins can be used again. This will also +//| release the builtin display on boards that have one. You will need to reinitialize it yourself +//| afterwards. This may take seconds to complete if an active EPaperDisplay is refreshing. +//| +//| Use this once in your code.py if you initialize a display. Place it right before the +//| initialization so the display is active as long as possible.""" +//| ... +//| +STATIC mp_obj_t displayio_release_displays(void) { + common_hal_displayio_release_displays(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(displayio_release_displays_obj, displayio_release_displays); + + +STATIC const mp_rom_map_elem_t displayio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_displayio) }, + { MP_ROM_QSTR(MP_QSTR_Bitmap), MP_ROM_PTR(&displayio_bitmap_type) }, + { MP_ROM_QSTR(MP_QSTR_ColorConverter), MP_ROM_PTR(&displayio_colorconverter_type) }, + { MP_ROM_QSTR(MP_QSTR_Colorspace), MP_ROM_PTR(&displayio_colorspace_type) }, + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&displayio_display_type) }, + { MP_ROM_QSTR(MP_QSTR_EPaperDisplay), MP_ROM_PTR(&displayio_epaperdisplay_type) }, + { MP_ROM_QSTR(MP_QSTR_Group), MP_ROM_PTR(&displayio_group_type) }, + { MP_ROM_QSTR(MP_QSTR_OnDiskBitmap), MP_ROM_PTR(&displayio_ondiskbitmap_type) }, + { MP_ROM_QSTR(MP_QSTR_Palette), MP_ROM_PTR(&displayio_palette_type) }, + { MP_ROM_QSTR(MP_QSTR_Shape), MP_ROM_PTR(&displayio_shape_type) }, + { MP_ROM_QSTR(MP_QSTR_TileGrid), MP_ROM_PTR(&displayio_tilegrid_type) }, + + { MP_ROM_QSTR(MP_QSTR_FourWire), MP_ROM_PTR(&displayio_fourwire_type) }, + { MP_ROM_QSTR(MP_QSTR_I2CDisplay), MP_ROM_PTR(&displayio_i2cdisplay_type) }, + #if CIRCUITPY_PARALLELDISPLAY + { MP_ROM_QSTR(MP_QSTR_ParallelBus), MP_ROM_PTR(¶lleldisplay_parallelbus_type) }, + #endif + + { MP_ROM_QSTR(MP_QSTR_release_displays), MP_ROM_PTR(&displayio_release_displays_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_module_globals, displayio_module_globals_table); + +const mp_obj_module_t displayio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&displayio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_displayio, displayio_module, CIRCUITPY_DISPLAYIO); diff --git a/circuitpython/shared-bindings/displayio/__init__.h b/circuitpython/shared-bindings/displayio/__init__.h new file mode 100644 index 0000000..b297e4d --- /dev/null +++ b/circuitpython/shared-bindings/displayio/__init__.h @@ -0,0 +1,68 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_DISPLAYIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H + +#include "py/enum.h" +#include "py/obj.h" + +typedef enum { + DISPLAY_COMMAND, + DISPLAY_DATA +} display_byte_type_t; + +typedef enum { + CHIP_SELECT_UNTOUCHED, + CHIP_SELECT_TOGGLE_EVERY_BYTE +} display_chip_select_behavior_t; + +typedef enum displayio_colorspace { + DISPLAYIO_COLORSPACE_RGB888, + DISPLAYIO_COLORSPACE_RGB565, + DISPLAYIO_COLORSPACE_RGB555, + DISPLAYIO_COLORSPACE_RGB565_SWAPPED, + DISPLAYIO_COLORSPACE_RGB555_SWAPPED, + DISPLAYIO_COLORSPACE_BGR565, + DISPLAYIO_COLORSPACE_BGR555, + DISPLAYIO_COLORSPACE_BGR565_SWAPPED, + DISPLAYIO_COLORSPACE_BGR555_SWAPPED, + DISPLAYIO_COLORSPACE_L8, +} displayio_colorspace_t; + +typedef bool (*display_bus_bus_reset)(mp_obj_t bus); +typedef bool (*display_bus_bus_free)(mp_obj_t bus); +typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); +typedef void (*display_bus_send)(mp_obj_t bus, display_byte_type_t byte_type, + display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); +typedef void (*display_bus_end_transaction)(mp_obj_t bus); + +void common_hal_displayio_release_displays(void); + +extern const mp_obj_type_t displayio_colorspace_type; +extern const cp_enum_obj_t displayio_colorspace_RGB888_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H diff --git a/circuitpython/shared-bindings/displayio/area.c b/circuitpython/shared-bindings/displayio/area.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/area.c diff --git a/circuitpython/shared-bindings/dualbank/__init__.c b/circuitpython/shared-bindings/dualbank/__init__.c new file mode 100644 index 0000000..f907c91 --- /dev/null +++ b/circuitpython/shared-bindings/dualbank/__init__.c @@ -0,0 +1,117 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 microDev + * + * 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 "shared-bindings/dualbank/__init__.h" + +//| """DUALBANK Module +//| +//| The `dualbank` module adds ability to update and switch +//| between the two app partitions. +//| +//| There are two identical partitions, these contain different +//| firmware versions. +//| Having two partitions enables rollback functionality. +//| +//| The two partitions are defined as boot partition and +//| next-update partition. Calling `dualbank.flash()` writes +//| the next-update partition. +//| +//| After the next-update partition is written a validation +//| check is performed and on a successful validation this +//| partition is set as the boot partition. On next reset, +//| firmware will be loaded from this partition. +//| +//| Here is the sequence of commands to follow: +//| +//| .. code-block:: python +//| +//| import dualbank +//| +//| dualbank.flash(buffer, offset) +//| dualbank.switch() +//| """ +//| ... +//| + +//| def flash(*buffer: ReadableBuffer, offset: int=0) -> None: +//| """Writes one of two app partitions at the given offset. +//| +//| This can be called multiple times when flashing the firmware +//| in small chunks. +//| """ +//| ... +//| +STATIC mp_obj_t dualbank_flash(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_offset }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_offset, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (args[ARG_offset].u_int < 0) { + mp_raise_ValueError(translate("offset must be >= 0")); + } + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + + common_hal_dualbank_flash(bufinfo.buf, bufinfo.len, args[ARG_offset].u_int); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(dualbank_flash_obj, 0, dualbank_flash); + +//| def switch() -> None: +//| """Switches the boot partition. +//| +//| On next reset, firmware will be loaded from the partition +//| just switched over to. +//| """ +//| ... +//| +STATIC mp_obj_t dualbank_switch(void) { + common_hal_dualbank_switch(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(dualbank_switch_obj, dualbank_switch); + +STATIC const mp_rom_map_elem_t dualbank_module_globals_table[] = { + // module name + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_dualbank) }, + // module functions + { MP_ROM_QSTR(MP_QSTR_flash), MP_ROM_PTR(&dualbank_flash_obj) }, + { MP_ROM_QSTR(MP_QSTR_switch), MP_ROM_PTR(&dualbank_switch_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(dualbank_module_globals, dualbank_module_globals_table); + +const mp_obj_module_t dualbank_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&dualbank_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_dualbank, dualbank_module, CIRCUITPY_DUALBANK); diff --git a/circuitpython/shared-bindings/dualbank/__init__.h b/circuitpython/shared-bindings/dualbank/__init__.h new file mode 100644 index 0000000..7edbc6d --- /dev/null +++ b/circuitpython/shared-bindings/dualbank/__init__.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 microDev + * + * 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_DUALBANK___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DUALBANK___INIT___H + +#include "py/runtime.h" + +extern void common_hal_dualbank_switch(void); +extern void common_hal_dualbank_flash(const void *buf, const size_t len, const size_t offset); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DUALBANK___INIT___H diff --git a/circuitpython/shared-bindings/floppyio/__init__.c b/circuitpython/shared-bindings/floppyio/__init__.c new file mode 100644 index 0000000..3ef58b2 --- /dev/null +++ b/circuitpython/shared-bindings/floppyio/__init__.c @@ -0,0 +1,128 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Jeff Epler 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 "shared-bindings/floppyio/__init__.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "common-hal/floppyio/__init__.h" + +#include <stdint.h> + +#include "py/binary.h" +#include "py/enum.h" +#include "py/obj.h" +#include "py/runtime.h" + +//| def flux_readinto(buffer: WriteableBuffer, data: digitalio.DigitalInOut, index: digitalio.DigitalInOut) -> int: +//| """Read flux transition information into the buffer. +//| +//| The function returns when the buffer has filled, or when the index input +//| indicates that one full revolution of data has been recorded. Due to +//| technical limitations, this process may not be interruptible by +//| KeyboardInterrupt. +//| +//| :param buffer: Read data into this buffer. Each element represents the time between successive zero-to-one transitions. +//| :param data: Pin on which the flux data appears +//| :param index: Pin on which the index pulse appears +//| :return: The actual number of bytes of read +//| """ +//| ... +//| +STATIC mp_obj_t floppyio_flux_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_data, ARG_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, 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); + digitalio_digitalinout_obj_t *data = assert_digitalinout(args[ARG_data].u_obj); + digitalio_digitalinout_obj_t *index = assert_digitalinout(args[ARG_index].u_obj); + + return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_flux_readinto(bufinfo.buf, bufinfo.len, data, index)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(floppyio_flux_readinto_obj, 0, floppyio_flux_readinto); + +//| def mfm_readinto(buffer: WriteableBuffer, data: digitalio.DigitalInOut, index: digitalio.DigitalInOut) -> int: +//| """Read mfm blocks into the buffer. +//| +//| The track is assumed to consist of 512-byte sectors. +//| +//| The function returns when all sectors have been successfully read, or +//| a number of index pulses have occurred. Due to technical limitations, this +//| process may not be interruptible by KeyboardInterrupt. +//| +//| :param buffer: Read data into this buffer. Must be a multiple of 512. +//| :param data: Pin on which the mfm data appears +//| :param index: Pin on which the index pulse appears +//| :return: The actual number of sectors read +//| """ +//| ... +//| +STATIC mp_obj_t floppyio_mfm_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_data, ARG_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, 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); + digitalio_digitalinout_obj_t *data = assert_digitalinout(args[ARG_data].u_obj); + digitalio_digitalinout_obj_t *index = assert_digitalinout(args[ARG_index].u_obj); + + if (bufinfo.len % 512 != 0) { + mp_raise_ValueError(translate("Buffer must be a multiple of 512 bytes")); + } + size_t n_sectors = bufinfo.len / 512; + return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_mfm_readinto(bufinfo.buf, n_sectors, data, index)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(floppyio_mfm_readinto_obj, 0, floppyio_mfm_readinto); + +//| samplerate: int +//| """The approximate sample rate in Hz used by flux_readinto.""" +//| + +STATIC const mp_rom_map_elem_t floppyio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_floppyio) }, + { MP_ROM_QSTR(MP_QSTR_flux_readinto), MP_ROM_PTR(&floppyio_flux_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_mfm_readinto), MP_ROM_PTR(&floppyio_mfm_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_samplerate), MP_ROM_INT(FLOPPYIO_SAMPLERATE) }, +}; +STATIC MP_DEFINE_CONST_DICT(floppyio_module_globals, floppyio_module_globals_table); + +const mp_obj_module_t floppyio_module = { + .base = {&mp_type_module }, + .globals = (mp_obj_dict_t *)&floppyio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_floppyio, floppyio_module, CIRCUITPY_FLOPPYIO); diff --git a/circuitpython/shared-bindings/floppyio/__init__.h b/circuitpython/shared-bindings/floppyio/__init__.h new file mode 100644 index 0000000..322bbe7 --- /dev/null +++ b/circuitpython/shared-bindings/floppyio/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Jeff Epler 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. + */ + +#pragma once + +#include "common-hal/digitalio/DigitalInOut.h" + +int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index); +int common_hal_floppyio_mfm_readinto(void *buf, size_t n_sector, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index); diff --git a/circuitpython/shared-bindings/fontio/BuiltinFont.c b/circuitpython/shared-bindings/fontio/BuiltinFont.c new file mode 100644 index 0000000..9c97d9a --- /dev/null +++ b/circuitpython/shared-bindings/fontio/BuiltinFont.c @@ -0,0 +1,120 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "shared-bindings/fontio/BuiltinFont.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| from typing_extensions import Protocol # for compat with python < 3.8 +//| +//| class FontProtocol(Protocol): +//| """A protocol shared by `BuiltinFont` and classes in ``adafruit_bitmap_font``""" +//| def get_bounding_box(self) -> Union[Tuple[int, int], Tuple[int, int, int, int]]: +//| """Retrieve the maximum bounding box of any glyph in the font. +//| +//| The four element version is ``(width, height, x_offset, y_offset)``. +//| The two element version is ``(width, height)``, in which +//| ``x_offset`` and ``y_offset`` are assumed to be zero.""" +//| pass +//| +//| def get_glyph(self, codepoint: int) -> Optional[Glyph]: +//| """Retrieve the Glyph for a given code point +//| +//| If the code point is not present in the font, `None` is returned.""" +//| pass +//| + +//| class BuiltinFont: +//| """A font built into CircuitPython""" +//| +//| def __init__(self) -> None: +//| """Creation not supported. Available fonts are defined when CircuitPython is built. See the +//| `Adafruit_CircuitPython_Bitmap_Font <https://github.com/adafruit/Adafruit_CircuitPython_Bitmap_Font>`_ +//| library for dynamically loaded fonts.""" +//| ... +//| + +//| bitmap: displayio.Bitmap +//| """Bitmap containing all font glyphs starting with ASCII and followed by unicode. Use +//| `get_glyph` in most cases. This is useful for use with `displayio.TileGrid` and +//| `terminalio.Terminal`.""" +//| +STATIC mp_obj_t fontio_builtinfont_obj_get_bitmap(mp_obj_t self_in) { + fontio_builtinfont_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_fontio_builtinfont_get_bitmap(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(fontio_builtinfont_get_bitmap_obj, fontio_builtinfont_obj_get_bitmap); + +MP_PROPERTY_GETTER(fontio_builtinfont_bitmap_obj, + (mp_obj_t)&fontio_builtinfont_get_bitmap_obj); + +//| def get_bounding_box(self) -> Tuple[int, int]: +//| """Returns the maximum bounds of all glyphs in the font in a tuple of two values: width, height.""" +//| ... +//| +STATIC mp_obj_t fontio_builtinfont_obj_get_bounding_box(mp_obj_t self_in) { + fontio_builtinfont_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_fontio_builtinfont_get_bounding_box(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(fontio_builtinfont_get_bounding_box_obj, fontio_builtinfont_obj_get_bounding_box); + + +//| def get_glyph(self, codepoint: int) -> Glyph: +//| """Returns a `fontio.Glyph` for the given codepoint or None if no glyph is available.""" +//| ... +//| +STATIC mp_obj_t fontio_builtinfont_obj_get_glyph(mp_obj_t self_in, mp_obj_t codepoint_obj) { + fontio_builtinfont_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t codepoint; + if (!mp_obj_get_int_maybe(codepoint_obj, &codepoint)) { + mp_raise_ValueError_varg(translate("%q should be an int"), MP_QSTR_codepoint); + } + return common_hal_fontio_builtinfont_get_glyph(self, codepoint); +} +MP_DEFINE_CONST_FUN_OBJ_2(fontio_builtinfont_get_glyph_obj, fontio_builtinfont_obj_get_glyph); + +STATIC const mp_rom_map_elem_t fontio_builtinfont_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_bitmap), MP_ROM_PTR(&fontio_builtinfont_bitmap_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_bounding_box), MP_ROM_PTR(&fontio_builtinfont_get_bounding_box_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_glyph), MP_ROM_PTR(&fontio_builtinfont_get_glyph_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(fontio_builtinfont_locals_dict, fontio_builtinfont_locals_dict_table); + +const mp_obj_type_t fontio_builtinfont_type = { + { &mp_type_type }, + .name = MP_QSTR_BuiltinFont, + .locals_dict = (mp_obj_dict_t *)&fontio_builtinfont_locals_dict, +}; diff --git a/circuitpython/shared-bindings/fontio/BuiltinFont.h b/circuitpython/shared-bindings/fontio/BuiltinFont.h new file mode 100644 index 0000000..e87445e --- /dev/null +++ b/circuitpython/shared-bindings/fontio/BuiltinFont.h @@ -0,0 +1,38 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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_FONTIO_BUILTINFONT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO_BUILTINFONT_H + +#include "shared-module/fontio/BuiltinFont.h" + +extern const mp_obj_type_t fontio_builtinfont_type; + +mp_obj_t common_hal_fontio_builtinfont_get_bitmap(const fontio_builtinfont_t *self); +mp_obj_t common_hal_fontio_builtinfont_get_bounding_box(const fontio_builtinfont_t *self); +mp_obj_t common_hal_fontio_builtinfont_get_glyph(const fontio_builtinfont_t *self, mp_uint_t codepoint); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO_BUILTINFONT_H diff --git a/circuitpython/shared-bindings/fontio/Glyph.c b/circuitpython/shared-bindings/fontio/Glyph.c new file mode 100644 index 0000000..92be6da --- /dev/null +++ b/circuitpython/shared-bindings/fontio/Glyph.c @@ -0,0 +1,84 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "shared-bindings/fontio/Glyph.h" + +#include <stdint.h> + +//| class Glyph: +//| """Storage of glyph info""" +//| +//| def __init__(self, +//| bitmap: displayio.Bitmap, +//| tile_index: int, +//| width: int, +//| height: int, +//| dx: int, +//| dy: int, +//| shift_x: int, +//| shift_y: int) -> None: +//| """Named tuple used to capture a single glyph and its attributes. +//| +//| :param bitmap: the bitmap including the glyph +//| :param tile_index: the tile index within the bitmap +//| :param width: the width of the glyph's bitmap +//| :param height: the height of the glyph's bitmap +//| :param dx: x adjustment to the bitmap's position +//| :param dy: y adjustment to the bitmap's position +//| :param shift_x: the x difference to the next glyph +//| :param shift_y: the y difference to the next glyph""" +//| ... +//| +const mp_obj_namedtuple_type_t fontio_glyph_type = { + .base = { + .base = { + .type = &mp_type_type + }, + .name = MP_QSTR_Glyph, + .flags = MP_TYPE_FLAG_EXTENDED, + .print = namedtuple_print, + .make_new = namedtuple_make_new, + .parent = &mp_type_tuple, + .attr = namedtuple_attr, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_obj_tuple_unary_op, + .binary_op = mp_obj_tuple_binary_op, + .subscr = mp_obj_tuple_subscr, + .getiter = mp_obj_tuple_getiter, + ), + }, + .n_fields = 8, + .fields = { + MP_QSTR_bitmap, + MP_QSTR_tile_index, + MP_QSTR_width, + MP_QSTR_height, + MP_QSTR_dx, + MP_QSTR_dy, + MP_QSTR_shift_x, + MP_QSTR_shift_y + }, +}; diff --git a/circuitpython/shared-bindings/fontio/Glyph.h b/circuitpython/shared-bindings/fontio/Glyph.h new file mode 100644 index 0000000..c58d812 --- /dev/null +++ b/circuitpython/shared-bindings/fontio/Glyph.h @@ -0,0 +1,34 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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_FONTIO_GLYPH_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO_GLYPH_H + +#include "py/objnamedtuple.h" + +extern const mp_obj_namedtuple_type_t fontio_glyph_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO_GLYPH_H diff --git a/circuitpython/shared-bindings/fontio/__init__.c b/circuitpython/shared-bindings/fontio/__init__.c new file mode 100644 index 0000000..ea1b8a6 --- /dev/null +++ b/circuitpython/shared-bindings/fontio/__init__.c @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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/fontio/__init__.h" +#include "shared-bindings/fontio/BuiltinFont.h" +#include "shared-bindings/fontio/Glyph.h" + +//| """Core font related data structures +//| +//| .. note:: This module is intended only for low-level usage. For working with +//| fonts in CircuitPython see the `adafruit_bitmap_font library +//| <https://github.com/adafruit/Adafruit_CircuitPython_Bitmap_Font>`_. +//| For information on creating custom fonts for use in CircuitPython, see +//| `this Learn guide <https://learn.adafruit.com/custom-fonts-for-pyportal-circuitpython-display>`_ +//| +//| """ +//| + +STATIC const mp_rom_map_elem_t fontio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_fontio) }, + { MP_ROM_QSTR(MP_QSTR_BuiltinFont), MP_ROM_PTR(&fontio_builtinfont_type) }, + { MP_ROM_QSTR(MP_QSTR_Glyph), MP_ROM_PTR(&fontio_glyph_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(fontio_module_globals, fontio_module_globals_table); + +const mp_obj_module_t fontio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&fontio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_fontio, fontio_module, CIRCUITPY_DISPLAYIO && CIRCUITPY_TERMINALIO); diff --git a/circuitpython/shared-bindings/fontio/__init__.h b/circuitpython/shared-bindings/fontio/__init__.h new file mode 100644 index 0000000..2099197 --- /dev/null +++ b/circuitpython/shared-bindings/fontio/__init__.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_FONTIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO___INIT___H + + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO___INIT___H diff --git a/circuitpython/shared-bindings/framebufferio/FramebufferDisplay.c b/circuitpython/shared-bindings/framebufferio/FramebufferDisplay.c new file mode 100644 index 0000000..daff217 --- /dev/null +++ b/circuitpython/shared-bindings/framebufferio/FramebufferDisplay.c @@ -0,0 +1,378 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Jeff Epler 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 "shared-bindings/framebufferio/FramebufferDisplay.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class FramebufferDisplay: +//| """Manage updating a display with framebuffer in RAM +//| +//| This initializes a display and connects it into CircuitPython. Unlike other +//| objects in CircuitPython, Display objects live until `displayio.release_displays()` +//| is called. This is done so that CircuitPython can use the display itself.""" +//| +//| def __init__(self, framebuffer: circuitpython_typing.FrameBuffer, *, rotation: int = 0, auto_refresh: bool = True) -> None: +//| """Create a Display object with the given framebuffer (a buffer, array, ulab.array, etc) +//| +//| :param ~circuitpython_typing.FrameBuffer framebuffer: The framebuffer that the display is connected to +//| :param bool auto_refresh: Automatically refresh the screen +//| :param int rotation: The rotation of the display in degrees clockwise. Must be in 90 degree increments (0, 90, 180, 270)""" +//| ... +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_framebuffer, ARG_rotation, ARG_auto_refresh, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_framebuffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rotation, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_auto_refresh, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + }; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + 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); + + mp_obj_t framebuffer = args[ARG_framebuffer].u_obj; + + mp_int_t rotation = args[ARG_rotation].u_int; + if (rotation % 90 != 0) { + mp_raise_ValueError(translate("Display rotation must be in 90 degree increments")); + } + + primary_display_t *disp = allocate_display_or_raise(); + framebufferio_framebufferdisplay_obj_t *self = &disp->framebuffer_display; + self->base.type = &framebufferio_framebufferdisplay_type; + common_hal_framebufferio_framebufferdisplay_construct( + self, + framebuffer, + rotation, + args[ARG_auto_refresh].u_bool + ); + + return self; +} + +// Helper to ensure we have the native super class instead of a subclass. +static framebufferio_framebufferdisplay_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native_display = mp_obj_cast_to_native_base(display_obj, &framebufferio_framebufferdisplay_type); + mp_obj_assert_native_inited(native_display); + return MP_OBJ_TO_PTR(native_display); +} + +//| def show(self, group: displayio.Group) -> None: +//| """Switches to displaying the given group of layers. When group is None, the default +//| CircuitPython terminal will be shown. +//| +//| :param Group group: The group to show.""" +//| ... +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_show(mp_obj_t self_in, mp_obj_t group_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + displayio_group_t *group = NULL; + if (group_in != mp_const_none) { + group = MP_OBJ_TO_PTR(native_group(group_in)); + } + + bool ok = common_hal_framebufferio_framebufferdisplay_show(self, group); + if (!ok) { + mp_raise_ValueError(translate("Group already used")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(framebufferio_framebufferdisplay_show_obj, framebufferio_framebufferdisplay_obj_show); + +//| def refresh(self, *, target_frames_per_second: int = 60, minimum_frames_per_second: int = 1) -> bool: +//| """When auto refresh is off, waits for the target frame rate and then refreshes the display, +//| returning True. If the call has taken too long since the last refresh call for the given +//| target frame rate, then the refresh returns False immediately without updating the screen to +//| hopefully help getting caught up. +//| +//| If the time since the last successful refresh is below the minimum frame rate, then an +//| exception will be raised. Set minimum_frames_per_second to 0 to disable. +//| +//| When auto refresh is on, updates the display immediately. (The display will also update +//| without calls to this.) +//| +//| :param int target_frames_per_second: How many times a second `refresh` should be called and the screen updated. +//| :param int minimum_frames_per_second: The minimum number of times the screen should be updated per second.""" +//| ... +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_target_frames_per_second, ARG_minimum_frames_per_second }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_target_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 60} }, + { MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + }; + 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); + + framebufferio_framebufferdisplay_obj_t *self = native_display(pos_args[0]); + uint32_t maximum_ms_per_real_frame = 0xffffffff; + mp_int_t minimum_frames_per_second = args[ARG_minimum_frames_per_second].u_int; + if (minimum_frames_per_second > 0) { + maximum_ms_per_real_frame = 1000 / minimum_frames_per_second; + } + return mp_obj_new_bool(common_hal_framebufferio_framebufferdisplay_refresh(self, 1000 / args[ARG_target_frames_per_second].u_int, maximum_ms_per_real_frame)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(framebufferio_framebufferdisplay_refresh_obj, 1, framebufferio_framebufferdisplay_obj_refresh); + +//| auto_refresh: bool +//| """True when the display is refreshed automatically.""" +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_get_auto_refresh(mp_obj_t self_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_framebufferio_framebufferdisplay_get_auto_refresh(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(framebufferio_framebufferdisplay_get_auto_refresh_obj, framebufferio_framebufferdisplay_obj_get_auto_refresh); + +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + + common_hal_framebufferio_framebufferdisplay_set_auto_refresh(self, mp_obj_is_true(auto_refresh)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(framebufferio_framebufferdisplay_set_auto_refresh_obj, framebufferio_framebufferdisplay_obj_set_auto_refresh); + +MP_PROPERTY_GETSET(framebufferio_framebufferdisplay_auto_refresh_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_get_auto_refresh_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_set_auto_refresh_obj); + +//| brightness: float +//| """The brightness of the display as a float. 0.0 is off and 1.0 is full brightness. When +//| `auto_brightness` is True, the value of `brightness` will change automatically. +//| If `brightness` is set, `auto_brightness` will be disabled and will be set to False.""" +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_get_brightness(mp_obj_t self_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + mp_float_t brightness = common_hal_framebufferio_framebufferdisplay_get_brightness(self); + if (brightness < 0) { + mp_raise_RuntimeError(translate("Brightness not adjustable")); + } + return mp_obj_new_float(brightness); +} +MP_DEFINE_CONST_FUN_OBJ_1(framebufferio_framebufferdisplay_get_brightness_obj, framebufferio_framebufferdisplay_obj_get_brightness); + +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_set_brightness(mp_obj_t self_in, mp_obj_t brightness_obj) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + common_hal_framebufferio_framebufferdisplay_set_auto_brightness(self, false); + mp_float_t brightness = mp_obj_get_float(brightness_obj); + if (brightness < 0.0f || brightness > 1.0f) { + mp_raise_ValueError(translate("Brightness must be 0-1.0")); + } + bool ok = common_hal_framebufferio_framebufferdisplay_set_brightness(self, brightness); + if (!ok) { + mp_raise_RuntimeError(translate("Brightness not adjustable")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(framebufferio_framebufferdisplay_set_brightness_obj, framebufferio_framebufferdisplay_obj_set_brightness); + +MP_PROPERTY_GETSET(framebufferio_framebufferdisplay_brightness_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_get_brightness_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_set_brightness_obj); + +//| auto_brightness: bool +//| """True when the display brightness is adjusted automatically, based on an ambient +//| light sensor or other method. Note that some displays may have this set to True by default, +//| but not actually implement automatic brightness adjustment. `auto_brightness` is set to False +//| if `brightness` is set manually.""" +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_get_auto_brightness(mp_obj_t self_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_framebufferio_framebufferdisplay_get_auto_brightness(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(framebufferio_framebufferdisplay_get_auto_brightness_obj, framebufferio_framebufferdisplay_obj_get_auto_brightness); + +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_set_auto_brightness(mp_obj_t self_in, mp_obj_t auto_brightness) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + + bool ok = common_hal_framebufferio_framebufferdisplay_set_auto_brightness(self, mp_obj_is_true(auto_brightness)); + if (!ok) { + mp_raise_RuntimeError(translate("Brightness not adjustable")); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(framebufferio_framebufferdisplay_set_auto_brightness_obj, framebufferio_framebufferdisplay_obj_set_auto_brightness); + +MP_PROPERTY_GETSET(framebufferio_framebufferdisplay_auto_brightness_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_get_auto_brightness_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_set_auto_brightness_obj); + +//| width: int +//| """Gets the width of the framebuffer""" +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_get_width(mp_obj_t self_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_framebufferio_framebufferdisplay_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(framebufferio_framebufferdisplay_get_width_obj, framebufferio_framebufferdisplay_obj_get_width); + +MP_PROPERTY_GETTER(framebufferio_framebufferdisplay_width_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_get_width_obj); + +//| height: int +//| """Gets the height of the framebuffer""" +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_get_height(mp_obj_t self_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_framebufferio_framebufferdisplay_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(framebufferio_framebufferdisplay_get_height_obj, framebufferio_framebufferdisplay_obj_get_height); + +MP_PROPERTY_GETTER(framebufferio_framebufferdisplay_height_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_get_height_obj); + +//| rotation: int +//| """The rotation of the display as an int in degrees.""" +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_get_rotation(mp_obj_t self_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_framebufferio_framebufferdisplay_get_rotation(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(framebufferio_framebufferdisplay_get_rotation_obj, framebufferio_framebufferdisplay_obj_get_rotation); +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + common_hal_framebufferio_framebufferdisplay_set_rotation(self, mp_obj_get_int(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(framebufferio_framebufferdisplay_set_rotation_obj, framebufferio_framebufferdisplay_obj_set_rotation); + + +MP_PROPERTY_GETSET(framebufferio_framebufferdisplay_rotation_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_get_rotation_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_set_rotation_obj); + +//| framebuffer: circuitpython_typing.FrameBuffer +//| """The framebuffer being used by the display""" +//| +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_get_framebuffer(mp_obj_t self_in) { + framebufferio_framebufferdisplay_obj_t *self = native_display(self_in); + return common_hal_framebufferio_framebufferdisplay_get_framebuffer(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(framebufferio_framebufferdisplay_get_framebuffer_obj, framebufferio_framebufferdisplay_obj_get_framebuffer); + +MP_PROPERTY_GETTER(framebufferio_framebufferframebuffer_obj, + (mp_obj_t)&framebufferio_framebufferdisplay_get_framebuffer_obj); + + +//| def fill_row(self, y: int, buffer: WriteableBuffer) -> WriteableBuffer: +//| """Extract the pixels from a single row +//| +//| :param int y: The top edge of the area +//| :param ~circuitpython_typing.WriteableBuffer buffer: The buffer in which to place the pixel data""" +//| ... +//| +STATIC mp_obj_t framebufferio_framebufferdisplay_obj_fill_row(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_y, ARG_buffer }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_y, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = -1} }, + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + }; + 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); + framebufferio_framebufferdisplay_obj_t *self = native_display(pos_args[0]); + mp_int_t y = args[ARG_y].u_int; + mp_obj_t *result = args[ARG_buffer].u_obj; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(result, &bufinfo, MP_BUFFER_WRITE); + + if (bufinfo.typecode != BYTEARRAY_TYPECODE) { + mp_raise_ValueError(translate("Buffer is not a bytearray.")); + } + if (self->core.colorspace.depth != 16) { + mp_raise_ValueError(translate("Display must have a 16 bit colorspace.")); + } + + displayio_area_t area = { + .x1 = 0, + .y1 = y, + .x2 = self->core.width, + .y2 = y + 1 + }; + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; + uint16_t buffer_size = self->core.width / pixels_per_word; + uint16_t pixels_per_buffer = displayio_area_size(&area); + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + + uint32_t *result_buffer = bufinfo.buf; + size_t result_buffer_size = bufinfo.len; + + if (result_buffer_size >= (buffer_size * 4)) { + volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask[mask_length]; + + for (uint16_t k = 0; k < mask_length; k++) { + mask[k] = 0x00000000; + } + + displayio_display_core_fill_area(&self->core, &area, mask, result_buffer); + return result; + } else { + mp_raise_ValueError(translate("Buffer is too small")); + } +} +MP_DEFINE_CONST_FUN_OBJ_KW(framebufferio_framebufferdisplay_fill_row_obj, 1, framebufferio_framebufferdisplay_obj_fill_row); + +STATIC const mp_rom_map_elem_t framebufferio_framebufferdisplay_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&framebufferio_framebufferdisplay_show_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&framebufferio_framebufferdisplay_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_row), MP_ROM_PTR(&framebufferio_framebufferdisplay_fill_row_obj) }, + + { MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&framebufferio_framebufferdisplay_auto_refresh_obj) }, + + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&framebufferio_framebufferdisplay_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_auto_brightness), MP_ROM_PTR(&framebufferio_framebufferdisplay_auto_brightness_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&framebufferio_framebufferdisplay_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&framebufferio_framebufferdisplay_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&framebufferio_framebufferdisplay_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_framebuffer), MP_ROM_PTR(&framebufferio_framebufferframebuffer_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(framebufferio_framebufferdisplay_locals_dict, framebufferio_framebufferdisplay_locals_dict_table); + +const mp_obj_type_t framebufferio_framebufferdisplay_type = { + { &mp_type_type }, + .name = MP_QSTR_FramebufferDisplay, + .make_new = framebufferio_framebufferdisplay_make_new, + .locals_dict = (mp_obj_dict_t *)&framebufferio_framebufferdisplay_locals_dict, +}; diff --git a/circuitpython/shared-bindings/framebufferio/FramebufferDisplay.h b/circuitpython/shared-bindings/framebufferio/FramebufferDisplay.h new file mode 100644 index 0000000..f96b47e --- /dev/null +++ b/circuitpython/shared-bindings/framebufferio/FramebufferDisplay.h @@ -0,0 +1,67 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Jeff Epler 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_DISPLAYIO_FRAMEBUFFERDISPLAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_FRAMEBUFFERDISPLAY_H + +#include "common-hal/microcontroller/Pin.h" + +#include "shared-module/framebufferio/FramebufferDisplay.h" +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t framebufferio_framebufferdisplay_type; + +#define NO_BRIGHTNESS_COMMAND 0x100 + +void common_hal_framebufferio_framebufferdisplay_construct(framebufferio_framebufferdisplay_obj_t *self, + mp_obj_t framebuffer, + uint16_t rotation, + bool auto_refresh); + +bool common_hal_framebufferio_framebufferdisplay_show(framebufferio_framebufferdisplay_obj_t *self, + displayio_group_t *root_group); + +bool common_hal_framebufferio_framebufferdisplay_refresh(framebufferio_framebufferdisplay_obj_t *self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame); + +bool common_hal_framebufferio_framebufferdisplay_get_auto_refresh(framebufferio_framebufferdisplay_obj_t *self); +void common_hal_framebufferio_framebufferdisplay_set_auto_refresh(framebufferio_framebufferdisplay_obj_t *self, bool auto_refresh); + +uint16_t common_hal_framebufferio_framebufferdisplay_get_width(framebufferio_framebufferdisplay_obj_t *self); +uint16_t common_hal_framebufferio_framebufferdisplay_get_height(framebufferio_framebufferdisplay_obj_t *self); +uint16_t common_hal_framebufferio_framebufferdisplay_get_rotation(framebufferio_framebufferdisplay_obj_t *self); +void common_hal_framebufferio_framebufferdisplay_set_rotation(framebufferio_framebufferdisplay_obj_t *self, int rotation); + +bool common_hal_framebufferio_framebufferdisplay_get_auto_brightness(framebufferio_framebufferdisplay_obj_t *self); +bool common_hal_framebufferio_framebufferdisplay_set_auto_brightness(framebufferio_framebufferdisplay_obj_t *self, bool auto_brightness); + +mp_float_t common_hal_framebufferio_framebufferdisplay_get_brightness(framebufferio_framebufferdisplay_obj_t *self); +bool common_hal_framebufferio_framebufferdisplay_set_brightness(framebufferio_framebufferdisplay_obj_t *self, mp_float_t brightness); + +mp_obj_t common_hal_framebufferio_framebufferdisplay_framebuffer(framebufferio_framebufferdisplay_obj_t *self); + + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_FRAMEBUFFERDISPLAY_H diff --git a/circuitpython/shared-bindings/framebufferio/__init__.c b/circuitpython/shared-bindings/framebufferio/__init__.c new file mode 100644 index 0000000..5d95ef4 --- /dev/null +++ b/circuitpython/shared-bindings/framebufferio/__init__.c @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "py/obj.h" +#include "shared-bindings/framebufferio/__init__.h" +#include "shared-bindings/framebufferio/FramebufferDisplay.h" + +//| """Native framebuffer display driving +//| +//| The `framebufferio` module contains classes to manage display output +//| including synchronizing with refresh rates and partial updating. +//| It is used in conjunction with classes from `displayio` to actually +//| place items on the display; and classes like `RGBMatrix` to actually +//| drive the display.""" +//| + +#if CIRCUITPY_FRAMEBUFFERIO +static const mp_rom_map_elem_t framebufferio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebufferio) }, + { MP_ROM_QSTR(MP_QSTR_FramebufferDisplay), MP_ROM_PTR(&framebufferio_framebufferdisplay_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(framebufferio_module_globals, framebufferio_module_globals_table); + +const mp_obj_module_t framebufferio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&framebufferio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_framebufferio, framebufferio_module, CIRCUITPY_FRAMEBUFFERIO); +#endif diff --git a/circuitpython/shared-bindings/framebufferio/__init__.h b/circuitpython/shared-bindings/framebufferio/__init__.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/shared-bindings/framebufferio/__init__.h diff --git a/circuitpython/shared-bindings/frequencyio/FrequencyIn.c b/circuitpython/shared-bindings/frequencyio/FrequencyIn.c new file mode 100644 index 0000000..2f6ebd0 --- /dev/null +++ b/circuitpython/shared-bindings/frequencyio/FrequencyIn.c @@ -0,0 +1,234 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Michael Schroeder + * + * 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/frequencyio/FrequencyIn.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class FrequencyIn: +//| """Read a frequency signal +//| +//| FrequencyIn is used to measure the frequency, in hertz, of a digital signal +//| on an incoming pin. Accuracy has shown to be within 10%, if not better. It +//| is recommended to utilize an average of multiple samples to smooth out readings. +//| +//| Frequencies below 1KHz are not currently detectable. +//| +//| FrequencyIn will not determine pulse width (use ``PulseIn``).""" +//| +//| def __init__(self, pin: microcontroller.Pin, capture_period: int = 10) -> None: +//| """Create a FrequencyIn object associated with the given pin. +//| +//| :param ~microcontroller.Pin pin: Pin to read frequency from. +//| :param int capture_period: Keyword argument to set the measurement period, in +//| milliseconds. Default is 10ms; range is 1ms - 500ms. +//| +//| Read the incoming frequency from a pin:: +//| +//| import frequencyio +//| import board +//| +//| frequency = frequencyio.FrequencyIn(board.D11) +//| +//| # Loop while printing the detected frequency +//| while True: +//| print(frequency.value) +//| +//| # Optional clear() will reset the value +//| # to zero. Without this, if the incoming +//| # signal stops, the last reading will remain +//| # as the value. +//| frequency.clear()""" +//| ... +//| +STATIC mp_obj_t frequencyio_frequencyin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, true); + + frequencyio_frequencyin_obj_t *self = m_new_obj(frequencyio_frequencyin_obj_t); + self->base.type = &frequencyio_frequencyin_type; + enum { ARG_pin, ARG_capture_period }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_capture_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 10} }, + }; + 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 *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + + const uint16_t capture_period = args[ARG_capture_period].u_int; + + common_hal_frequencyio_frequencyin_construct(self, pin, capture_period); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the FrequencyIn and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t frequencyio_frequencyin_deinit(mp_obj_t self_in) { + frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_frequencyio_frequencyin_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_deinit_obj, frequencyio_frequencyin_deinit); + +STATIC void check_for_deinit(frequencyio_frequencyin_obj_t *self) { + if (common_hal_frequencyio_frequencyin_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> FrequencyIn: +//| """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 frequencyio_frequencyin_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_frequencyio_frequencyin_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(frequencyio_frequencyin___exit___obj, 4, 4, frequencyio_frequencyin_obj___exit__); + +//| def pause(self) -> None: +//| """Pause frequency capture.""" +//| ... +//| +STATIC mp_obj_t frequencyio_frequencyin_obj_pause(mp_obj_t self_in) { + frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_frequencyio_frequencyin_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_pause_obj, frequencyio_frequencyin_obj_pause); + +//| def resume(self) -> None: +//| """Resumes frequency capture.""" +//| ... +//| +STATIC mp_obj_t frequencyio_frequencyin_obj_resume(mp_obj_t self_in) { + frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_frequencyio_frequencyin_resume(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_resume_obj, frequencyio_frequencyin_obj_resume); + +//| def clear(self) -> None: +//| """Clears the last detected frequency capture value.""" +//| ... +//| + +STATIC mp_obj_t frequencyio_frequencyin_obj_clear(mp_obj_t self_in) { + frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_frequencyio_frequencyin_clear(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_clear_obj, frequencyio_frequencyin_obj_clear); + +//| capture_period: int +//| """The capture measurement period. Lower incoming frequencies will be measured +//| more accurately with longer capture periods. Higher frequencies are more +//| accurate with shorter capture periods. +//| +//| .. note:: When setting a new ``capture_period``, all previous capture information is +//| cleared with a call to ``clear()``.""" +//| +STATIC mp_obj_t frequencyio_frequencyin_obj_get_capture_period(mp_obj_t self_in) { + frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_frequencyio_frequencyin_get_capture_period(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequency_get_capture_period_obj, frequencyio_frequencyin_obj_get_capture_period); + +STATIC mp_obj_t frequencyio_frequencyin_obj_set_capture_period(mp_obj_t self_in, mp_obj_t capture_period) { + frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_frequencyio_frequencyin_set_capture_period(self, mp_obj_get_int(capture_period)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(frequencyio_frequency_set_capture_period_obj, frequencyio_frequencyin_obj_set_capture_period); + +MP_PROPERTY_GETSET(frequencyio_frequencyin_capture_period_obj, + (mp_obj_t)&frequencyio_frequency_get_capture_period_obj, + (mp_obj_t)&frequencyio_frequency_set_capture_period_obj); + +//| def __get__(self, index: int) -> int: +//| """Returns the value of the last frequency captured.""" +//| ... +//| +STATIC mp_obj_t frequencyio_frequencyin_obj_get_value(mp_obj_t self_in) { + frequencyio_frequencyin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + // return MP_OBJ_NEW_SMALL_INT(common_hal_frequencyio_frequencyin_get_item(self)); + return mp_obj_new_int_from_float(common_hal_frequencyio_frequencyin_get_item(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(frequencyio_frequencyin_get_value_obj, frequencyio_frequencyin_obj_get_value); + +MP_PROPERTY_GETTER(frequencyio_frequencyin_value_obj, + (mp_obj_t)&frequencyio_frequencyin_get_value_obj); + +STATIC const mp_rom_map_elem_t frequencyio_frequencyin_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&frequencyio_frequencyin_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&frequencyio_frequencyin___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&frequencyio_frequencyin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&frequencyio_frequencyin_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&frequencyio_frequencyin_resume_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&frequencyio_frequencyin_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_capture_period), MP_ROM_PTR(&frequencyio_frequencyin_capture_period_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(frequencyio_frequencyin_locals_dict, frequencyio_frequencyin_locals_dict_table); + +const mp_obj_type_t frequencyio_frequencyin_type = { + { &mp_type_type }, + .name = MP_QSTR_frequencyin, + .make_new = frequencyio_frequencyin_make_new, + .locals_dict = (mp_obj_dict_t *)&frequencyio_frequencyin_locals_dict, +}; diff --git a/circuitpython/shared-bindings/frequencyio/FrequencyIn.h b/circuitpython/shared-bindings/frequencyio/FrequencyIn.h new file mode 100644 index 0000000..dba6c3a --- /dev/null +++ b/circuitpython/shared-bindings/frequencyio/FrequencyIn.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Michael Schroeder + * + * 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_FREQUENCYIO_FREQUENCYIN_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_FREQUENCYIO_FREQUENCYIN_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/frequencyio/FrequencyIn.h" + +extern const mp_obj_type_t frequencyio_frequencyin_type; + +extern void common_hal_frequencyio_frequencyin_construct(frequencyio_frequencyin_obj_t *self, + const mcu_pin_obj_t *pin, uint16_t capture_period); +extern void common_hal_frequencyio_frequencyin_deinit(frequencyio_frequencyin_obj_t *self); +extern bool common_hal_frequencyio_frequencyin_deinited(frequencyio_frequencyin_obj_t *self); +extern void common_hal_frequencyio_frequencyin_pause(frequencyio_frequencyin_obj_t *self); +extern void common_hal_frequencyio_frequencyin_resume(frequencyio_frequencyin_obj_t *self); +extern void common_hal_frequencyio_frequencyin_clear(frequencyio_frequencyin_obj_t *self); +extern uint32_t common_hal_frequencyio_frequencyin_get_item(frequencyio_frequencyin_obj_t *self); +extern uint16_t common_hal_frequencyio_frequencyin_get_capture_period(frequencyio_frequencyin_obj_t *self); +extern void common_hal_frequencyio_frequencyin_set_capture_period(frequencyio_frequencyin_obj_t *self, uint16_t capture_period); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_FREQUENCYIO_FREQUENCYIN_H diff --git a/circuitpython/shared-bindings/frequencyio/__init__.c b/circuitpython/shared-bindings/frequencyio/__init__.c new file mode 100644 index 0000000..ffbb7af --- /dev/null +++ b/circuitpython/shared-bindings/frequencyio/__init__.c @@ -0,0 +1,76 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Michael Schroeder + * + * 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/frequencyio/__init__.h" +#include "shared-bindings/frequencyio/FrequencyIn.h" + +//| """Support for frequency based protocols +//| +//| .. warning:: This module is not available in SAMD21 builds. See the +//| :ref:`module-support-matrix` for more info. +//| + +//| 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 time +//| import frequencyio +//| import board +//| +//| frequency = frequencyio.FrequencyIn(board.D11) +//| frequency.capture_period = 15 +//| time.sleep(0.1) +//| +//| This example will initialize the the device, set +//| :py:data:`~frequencyio.FrequencyIn.capture_period`, and then sleep 0.1 seconds. +//| CircuitPython will automatically turn off FrequencyIn capture when it resets all +//| hardware after program completion. Use ``deinit()`` or a ``with`` statement +//| to do it yourself.""" +//| + +STATIC const mp_rom_map_elem_t frequencyio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_frequencyio) }, + { MP_ROM_QSTR(MP_QSTR_FrequencyIn), MP_ROM_PTR(&frequencyio_frequencyin_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(frequencyio_module_globals, frequencyio_module_globals_table); + +const mp_obj_module_t frequencyio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&frequencyio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_frequencyio, frequencyio_module, CIRCUITPY_FREQUENCYIO); diff --git a/circuitpython/shared-bindings/frequencyio/__init__.h b/circuitpython/shared-bindings/frequencyio/__init__.h new file mode 100644 index 0000000..3915765 --- /dev/null +++ b/circuitpython/shared-bindings/frequencyio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Michael Schroeder + * + * 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_FREQUENCYIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_FREQUENCYIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_FREQUENCYIO___INIT___H diff --git a/circuitpython/shared-bindings/gamepadshift/GamePadShift.c b/circuitpython/shared-bindings/gamepadshift/GamePadShift.c new file mode 100644 index 0000000..1c43eea --- /dev/null +++ b/circuitpython/shared-bindings/gamepadshift/GamePadShift.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Radomir Dopieralski 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 "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/gc.h" +#include "py/mpstate.h" +#include "shared-bindings/gamepadshift/GamePadShift.h" +#include "shared-bindings/gamepadshift/__init__.h" +#include "supervisor/shared/translate.h" +#include "supervisor/shared/tick.h" + +//| class GamePadShift: +//| """Scan buttons for presses through a shift register""" +//| +//| def __init__(self, clock: digitalio.DigitalInOut, data: digitalio.DigitalInOut, latch: digitalio.DigitalInOut) -> None: +//| """Initializes button scanning routines. +//| +//| The ``clock``, ``data`` and ``latch`` parameters are ``DigitalInOut`` +//| objects connected to the shift register controlling the buttons. +//| +//| The button presses are accumulated, until the ``get_pressed`` method +//| is called, at which point the button state is cleared, and the new +//| button presses start to be recorded. +//| +//| Only one `gamepadshift.GamePadShift` may be used at a time.""" +//| ... +//| +STATIC mp_obj_t gamepadshift_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + + enum { ARG_clock, ARG_data, ARG_latch }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_OBJ}, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_latch, MP_ARG_REQUIRED | MP_ARG_OBJ}, + }; + 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); + + digitalio_digitalinout_obj_t *clock_pin = assert_digitalinout(args[ARG_clock].u_obj); + digitalio_digitalinout_obj_t *data_pin = assert_digitalinout(args[ARG_data].u_obj); + digitalio_digitalinout_obj_t *latch_pin = assert_digitalinout(args[ARG_latch].u_obj); + + gamepadshift_obj_t *gamepad_singleton = MP_STATE_VM(gamepad_singleton); + if (!gamepad_singleton || + !mp_obj_is_type(MP_OBJ_FROM_PTR(gamepad_singleton), + &gamepadshift_type)) { + gamepad_singleton = m_new_ll_obj(gamepadshift_obj_t); + gamepad_singleton->base.type = &gamepadshift_type; + if (!MP_STATE_VM(gamepad_singleton)) { + supervisor_enable_tick(); + } + MP_STATE_VM(gamepad_singleton) = gamepad_singleton; + } + common_hal_gamepadshift_gamepadshift_init(gamepad_singleton, clock_pin, data_pin, latch_pin); + return MP_OBJ_FROM_PTR(gamepad_singleton); +} + +//| def get_pressed(self) -> int: +//| """Get the status of buttons pressed since the last call and clear it. +//| +//| Returns an 8-bit number, with bits that correspond to buttons, +//| which have been pressed (or held down) since the last call to this +//| function set to 1, and the remaining bits set to 0. Then it clears +//| the button state, so that new button presses (or buttons that are +//| held down) can be recorded for the next call.""" +//| ... +//| +STATIC mp_obj_t gamepadshift_get_pressed(mp_obj_t self_in) { + gamepadshift_obj_t *gamepad_singleton = MP_STATE_VM(gamepad_singleton); + mp_obj_t pressed = MP_OBJ_NEW_SMALL_INT(gamepad_singleton->pressed); + gamepad_singleton->pressed = gamepad_singleton->last; + return pressed; +} +MP_DEFINE_CONST_FUN_OBJ_1(gamepadshift_get_pressed_obj, gamepadshift_get_pressed); + +//| def deinit(self) -> None: +//| """Disable button scanning.""" +//| ... +//| +STATIC mp_obj_t gamepadshift_deinit(mp_obj_t self_in) { + common_hal_gamepadshift_gamepadshift_deinit(self_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(gamepadshift_deinit_obj, gamepadshift_deinit); + + +STATIC const mp_rom_map_elem_t gamepadshift_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_get_pressed), MP_ROM_PTR(&gamepadshift_get_pressed_obj)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&gamepadshift_deinit_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(gamepadshift_locals_dict, gamepadshift_locals_dict_table); +const mp_obj_type_t gamepadshift_type = { + { &mp_type_type }, + .name = MP_QSTR_GamePadShift, + .make_new = gamepadshift_make_new, + .locals_dict = (mp_obj_dict_t *)&gamepadshift_locals_dict, +}; diff --git a/circuitpython/shared-bindings/gamepadshift/GamePadShift.h b/circuitpython/shared-bindings/gamepadshift/GamePadShift.h new file mode 100644 index 0000000..8856e13 --- /dev/null +++ b/circuitpython/shared-bindings/gamepadshift/GamePadShift.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Radomir Dopieralski 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_GAMEPADSHIFT_GAMEPADSHIFT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_GAMEPADSHIFT_GAMEPADSHIFT_H + +#include "shared-module/gamepadshift/GamePadShift.h" + +extern const mp_obj_type_t gamepadshift_type; + +void common_hal_gamepadshift_gamepadshift_init(gamepadshift_obj_t *gamepadshift, + digitalio_digitalinout_obj_t *clock_pin, + digitalio_digitalinout_obj_t *data_pin, + digitalio_digitalinout_obj_t *latch_pin); + +void common_hal_gamepadshift_gamepadshift_deinit(gamepadshift_obj_t *gamepadshift); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GAMEPADSHIFT_GAMEPADSHIFT_H diff --git a/circuitpython/shared-bindings/gamepadshift/__init__.c b/circuitpython/shared-bindings/gamepadshift/__init__.c new file mode 100644 index 0000000..816fd8d --- /dev/null +++ b/circuitpython/shared-bindings/gamepadshift/__init__.c @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Radomir Dopieralski 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 "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "shared-bindings/gamepadshift/GamePadShift.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/util.h" + +//| """Tracks button presses read through a shift register. +//| +//| .. note:: `gamepadshift` is deprecated in CircuitPython 7.0.0 and will be removed in 8.0.0. +//| Use `keypad` instead. +//| """ +//| +STATIC const mp_rom_map_elem_t gamepadshift_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gamepadshift) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GamePadShift), MP_ROM_PTR(&gamepadshift_type)}, +}; + +STATIC MP_DEFINE_CONST_DICT(gamepadshift_module_globals, gamepadshift_module_globals_table); + +const mp_obj_module_t gamepadshift_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&gamepadshift_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_gamepadshift, gamepadshift_module, CIRCUITPY_GAMEPADSHIFT); diff --git a/circuitpython/shared-bindings/gamepadshift/__init__.h b/circuitpython/shared-bindings/gamepadshift/__init__.h new file mode 100644 index 0000000..4b4be75 --- /dev/null +++ b/circuitpython/shared-bindings/gamepadshift/__init__.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Radomir Dopieralski 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_GAMEPADSHIFT___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_GAMEPADSHIFT___INIT___H + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GAMEPADSHIFT___INIT___H diff --git a/circuitpython/shared-bindings/getpass/__init__.c b/circuitpython/shared-bindings/getpass/__init__.c new file mode 100644 index 0000000..7c2de89 --- /dev/null +++ b/circuitpython/shared-bindings/getpass/__init__.c @@ -0,0 +1,88 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "py/stream.h" +#include "shared-module/getpass/__init__.h" + +//| """Getpass Module +//| +//| This module provides a way to get input from user without echoing it. +//| +//| """ +//| ... +//| + +//| def getpass(prompt: Optional[str] = 'Password: ', stream: Optional[io.FileIO] = None) -> str: +//| +//| """Prompt the user without echoing. +//| +//| :param str prompt: The user is prompted using the string ``prompt``, which defaults to ``'Password: '``. +//| :param io.FileIO stream: The ``prompt`` is written to the file-like object ``stream`` if provided. +//| +//| """ +//| ... +//| +STATIC mp_obj_t getpass_getpass(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_prompt, ARG_stream }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_prompt, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_stream, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const char *prompt = (args[ARG_prompt].u_obj == mp_const_none) ? "Password: " : mp_obj_str_get_str(args[ARG_prompt].u_obj); + + mp_print_t print = {.data = NULL}; + if (args[ARG_stream].u_obj != mp_const_none) { + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + mp_get_stream_raise(args[ARG_stream].u_obj, MP_STREAM_OP_WRITE); + print.data = MP_OBJ_TO_PTR(args[ARG_stream].u_obj); + print.print_strn = mp_stream_write_adaptor; + #else + mp_raise_NotImplementedError(translate("stream operation not supported")); + #endif + } + + return shared_module_getpass_getpass(prompt, ((print.data) ? &print : NULL)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(getpass_getpass_obj, 0, getpass_getpass); + +STATIC const mp_rom_map_elem_t getpass_module_globals_table[] = { + // module name + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_getpass) }, + // module functions + { MP_ROM_QSTR(MP_QSTR_getpass), MP_ROM_PTR(&getpass_getpass_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(getpass_module_globals, getpass_module_globals_table); + +const mp_obj_module_t getpass_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&getpass_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_getpass, getpass_module, CIRCUITPY_GETPASS); diff --git a/circuitpython/shared-bindings/gifio/GifWriter.c b/circuitpython/shared-bindings/gifio/GifWriter.c new file mode 100644 index 0000000..11dd43b --- /dev/null +++ b/circuitpython/shared-bindings/gifio/GifWriter.c @@ -0,0 +1,161 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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 "py/obj.h" +#if MICROPY_VFS +#include "extmod/vfs.h" +#endif +#include "py/runtime.h" +#include "shared-bindings/gifio/GifWriter.h" +#include "shared-module/gifio/GifWriter.h" +#include "shared/runtime/context_manager_helpers.h" + +//| class GifWriter: +//| def __init__(self, file: Union[typing.BinaryIO, str], width:int, height:int, colorspace: displayio.Colorspace, loop:bool=True, dither:bool=False) -> None: +//| """Construct a GifWriter object +//| +//| :param file: Either a file open in bytes mode, or the name of a file to open in bytes mode. +//| :param width: The width of the image. All frames must have the same width. +//| :param height: The height of the image. All frames must have the same height. +//| :param colorspace: The colorspace of the image. All frames must have the same colorspace. The supported colorspaces are ``RGB565``, ``BGR565``, ``RGB565_SWAPPED``, ``BGR565_SWAPPED``, and ``L8`` (greyscale) +//| :param loop: If True, the GIF is marked for looping playback +//| :param dither: If True, and the image is in color, a simple ordered dither is applied. +//| """ +//| ... +//| +static mp_obj_t gifio_gifwriter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_file, ARG_width, ARG_height, ARG_colorspace, ARG_loop, ARG_dither }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, + { MP_QSTR_colorspace, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} }, + { MP_QSTR_loop, MP_ARG_BOOL, { .u_bool = true } }, + { MP_QSTR_dither, 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); + + mp_obj_t file = args[ARG_file].u_obj; + bool own_file = false; + if (mp_obj_is_str(file)) { + file = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), file, MP_OBJ_NEW_QSTR(MP_QSTR_wb)); + own_file = true; + } + + gifio_gifwriter_t *self = m_new_obj(gifio_gifwriter_t); + self->base.type = &gifio_gifwriter_type; + shared_module_gifio_gifwriter_construct( + self, + file, + args[ARG_width].u_int, + args[ARG_height].u_int, + (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_colorspace].u_obj), + args[ARG_loop].u_bool, + args[ARG_dither].u_bool, + own_file); + + return self; +} + + +//| def __enter__(self) -> GifWriter: +//| """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 gifio_gifwriter___exit__(size_t n_args, const mp_obj_t *args) { + gifio_gifwriter_t *self = MP_OBJ_TO_PTR(args[0]); + shared_module_gifio_gifwriter_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gifio_gifwriter___exit___obj, 4, 4, gifio_gifwriter___exit__); + +//| def deinit(self) -> None: +//| """Close the underlying file.""" +//| ... +//| +static mp_obj_t gifio_gifwriter_deinit(mp_obj_t self_in) { + gifio_gifwriter_t *self = MP_OBJ_TO_PTR(self_in); + shared_module_gifio_gifwriter_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(gifio_gifwriter_deinit_obj, gifio_gifwriter_deinit); + +//| def add_frame(self, bitmap: ReadableBuffer, delay: float = 0.1) -> None: +//| """Add a frame to the GIF. +//| +//| :param bitmap: The frame data +//| :param delay: The frame delay in seconds. The GIF format rounds this to the nearest 1/100 second, and the largest permitted value is 655 seconds. +//| """ +//| ... +static mp_obj_t gifio_gifwriter_add_frame(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_bitmap, ARG_delay }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bitmap, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} }, + { MP_QSTR_delay, MP_ARG_OBJ, {.u_obj = NULL} }, + }; + 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); + + gifio_gifwriter_t *self = MP_OBJ_TO_PTR(pos_args[0]); + shared_module_gifio_gifwriter_check_for_deinit(self); + + + mp_float_t delay = mp_arg_validate_obj_float_non_negative(args[ARG_delay].u_obj, MICROPY_FLOAT_CONST(0.1), MP_QSTR_delay); + if (delay > MICROPY_FLOAT_CONST(655.)) { + mp_raise_ValueError_varg(translate("%q must be <= %d"), MP_QSTR_delay, 655); + } + + int delay_centiseconds = (int)(delay * 100); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_bitmap].u_obj, &bufinfo, MP_BUFFER_READ); + shared_module_gifio_gifwriter_add_frame(self, &bufinfo, delay_centiseconds); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(gifio_gifwriter_add_frame_obj, 1, gifio_gifwriter_add_frame); + +STATIC const mp_rom_map_elem_t gifio_gifwriter_locals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_GifWriter) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&gifio_gifwriter___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&gifio_gifwriter_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_add_frame), MP_ROM_PTR(&gifio_gifwriter_add_frame_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(gifio_gifwriter_locals, gifio_gifwriter_locals_table); + +const mp_obj_type_t gifio_gifwriter_type = { + { &mp_type_type }, + .name = MP_QSTR_GifWriter, + .make_new = gifio_gifwriter_make_new, + .locals_dict = (mp_obj_dict_t *)&gifio_gifwriter_locals, +}; diff --git a/circuitpython/shared-bindings/gifio/GifWriter.h b/circuitpython/shared-bindings/gifio/GifWriter.h new file mode 100644 index 0000000..601bd78 --- /dev/null +++ b/circuitpython/shared-bindings/gifio/GifWriter.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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 "py/obj.h" + +#pragma once + +typedef struct gifio_gifwriter gifio_gifwriter_t; +typedef enum displayio_colorspace displayio_colorspace_t; + +extern const mp_obj_type_t gifio_gifwriter_type; + +void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool dither, bool own_file); +void shared_module_gifio_gifwriter_check_for_deinit(gifio_gifwriter_t *self); +bool shared_module_gifio_gifwriter_deinited(gifio_gifwriter_t *self); +void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self); +void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_buffer_info_t *buf, int16_t delay); +void shared_module_gifio_gifwriter_close(gifio_gifwriter_t *self); diff --git a/circuitpython/shared-bindings/gifio/__init__.c b/circuitpython/shared-bindings/gifio/__init__.c new file mode 100644 index 0000000..317eebc --- /dev/null +++ b/circuitpython/shared-bindings/gifio/__init__.c @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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 "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "shared-bindings/gifio/GifWriter.h" +#include "shared-bindings/util.h" + +//| """Access GIF-format images +//| """ +//| +STATIC const mp_rom_map_elem_t gifio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gifio) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GifWriter), MP_ROM_PTR(&gifio_gifwriter_type)}, +}; + +STATIC MP_DEFINE_CONST_DICT(gifio_module_globals, gifio_module_globals_table); + +const mp_obj_module_t gifio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&gifio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_gifio, gifio_module, CIRCUITPY_GIFIO); diff --git a/circuitpython/shared-bindings/gifio/__init__.h b/circuitpython/shared-bindings/gifio/__init__.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/shared-bindings/gifio/__init__.h diff --git a/circuitpython/shared-bindings/gnss/GNSS.c b/circuitpython/shared-bindings/gnss/GNSS.c new file mode 100644 index 0000000..0bd800b --- /dev/null +++ b/circuitpython/shared-bindings/gnss/GNSS.c @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: Sony Semiconductor Solutions Corporation +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/gnss/GNSS.h" +#include "shared-bindings/time/__init__.h" +#include "shared-bindings/util.h" + +#include "py/objproperty.h" +#include "py/runtime.h" + +//| class GNSS: +//| """Get updated positioning information from Global Navigation Satellite System (GNSS) +//| +//| Usage:: +//| +//| import gnss +//| import time +//| +//| nav = gnss.GNSS([gnss.SatelliteSystem.GPS, gnss.SatelliteSystem.GLONASS]) +//| last_print = time.monotonic() +//| while True: +//| nav.update() +//| current = time.monotonic() +//| if current - last_print >= 1.0: +//| last_print = current +//| if nav.fix is gnss.PositionFix.INVALID: +//| print("Waiting for fix...") +//| continue +//| print("Latitude: {0:.6f} degrees".format(nav.latitude)) +//| print("Longitude: {0:.6f} degrees".format(nav.longitude))""" +//| + +//| def __init__(self, system: Union[SatelliteSystem, List[SatelliteSystem]]) -> None: +//| """Turn on the GNSS. +//| +//| :param system: satellite system to use""" +//| ... +//| +STATIC mp_obj_t gnss_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + gnss_obj_t *self = m_new_obj(gnss_obj_t); + self->base.type = &gnss_type; + enum { ARG_system }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_system, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + 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); + + unsigned long selection = 0; + if (mp_obj_is_type(args[ARG_system].u_obj, &gnss_satellitesystem_type)) { + selection |= gnss_satellitesystem_obj_to_type(args[ARG_system].u_obj); + } else if (mp_obj_is_type(args[ARG_system].u_obj, &mp_type_list)) { + size_t systems_size = 0; + mp_obj_t *systems; + mp_obj_list_get(args[ARG_system].u_obj, &systems_size, &systems); + for (size_t i = 0; i < systems_size; ++i) { + if (!mp_obj_is_type(systems[i], &gnss_satellitesystem_type)) { + mp_raise_TypeError(translate("System entry must be gnss.SatelliteSystem")); + } + selection |= gnss_satellitesystem_obj_to_type(systems[i]); + } + } else { + mp_raise_TypeError(translate("System entry must be gnss.SatelliteSystem")); + } + + common_hal_gnss_construct(self, selection); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Turn off the GNSS.""" +//| ... +//| +STATIC mp_obj_t gnss_obj_deinit(mp_obj_t self_in) { + gnss_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_gnss_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(gnss_deinit_obj, gnss_obj_deinit); + +STATIC void check_for_deinit(gnss_obj_t *self) { + if (common_hal_gnss_deinited(self)) { + raise_deinited_error(); + } +} + +//| def update(self) -> None: +//| """Update GNSS positioning information.""" +//| ... +//| +STATIC mp_obj_t gnss_obj_update(mp_obj_t self_in) { + gnss_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_gnss_update(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(gnss_update_obj, gnss_obj_update); + +//| latitude: float +//| """Latitude of current position in degrees (float).""" +//| +STATIC mp_obj_t gnss_obj_get_latitude(mp_obj_t self_in) { + gnss_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_float(common_hal_gnss_get_latitude(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(gnss_get_latitude_obj, gnss_obj_get_latitude); + +MP_PROPERTY_GETTER(gnss_latitude_obj, + (mp_obj_t)&gnss_get_latitude_obj); + +//| longitude: float +//| """Longitude of current position in degrees (float).""" +//| +STATIC mp_obj_t gnss_obj_get_longitude(mp_obj_t self_in) { + gnss_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_float(common_hal_gnss_get_longitude(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(gnss_get_longitude_obj, gnss_obj_get_longitude); + +MP_PROPERTY_GETTER(gnss_longitude_obj, + (mp_obj_t)&gnss_get_longitude_obj); + +//| altitude: float +//| """Altitude of current position in meters (float).""" +//| +STATIC mp_obj_t gnss_obj_get_altitude(mp_obj_t self_in) { + gnss_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_float(common_hal_gnss_get_altitude(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(gnss_get_altitude_obj, gnss_obj_get_altitude); + +MP_PROPERTY_GETTER(gnss_altitude_obj, + (mp_obj_t)&gnss_get_altitude_obj); + +//| timestamp: time.struct_time +//| """Time when the position data was updated.""" +//| +STATIC mp_obj_t gnss_obj_get_timestamp(mp_obj_t self_in) { + gnss_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + timeutils_struct_time_t tm; + common_hal_gnss_get_timestamp(self, &tm); + return struct_time_from_tm(&tm); +} +MP_DEFINE_CONST_FUN_OBJ_1(gnss_get_timestamp_obj, gnss_obj_get_timestamp); + +MP_PROPERTY_GETTER(gnss_timestamp_obj, + (mp_obj_t)&gnss_get_timestamp_obj); + +//| fix: PositionFix +//| """Fix mode.""" +//| +STATIC mp_obj_t gnss_obj_get_fix(mp_obj_t self_in) { + gnss_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return gnss_positionfix_type_to_obj(common_hal_gnss_get_fix(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(gnss_get_fix_obj, gnss_obj_get_fix); + +MP_PROPERTY_GETTER(gnss_fix_obj, + (mp_obj_t)&gnss_get_fix_obj); + +STATIC const mp_rom_map_elem_t gnss_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&gnss_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&gnss_update_obj) }, + + { MP_ROM_QSTR(MP_QSTR_latitude), MP_ROM_PTR(&gnss_latitude_obj) }, + { MP_ROM_QSTR(MP_QSTR_longitude), MP_ROM_PTR(&gnss_longitude_obj) }, + { MP_ROM_QSTR(MP_QSTR_altitude), MP_ROM_PTR(&gnss_altitude_obj) }, + { MP_ROM_QSTR(MP_QSTR_timestamp), MP_ROM_PTR(&gnss_timestamp_obj) }, + { MP_ROM_QSTR(MP_QSTR_fix), MP_ROM_PTR(&gnss_fix_obj) } +}; +STATIC MP_DEFINE_CONST_DICT(gnss_locals_dict, gnss_locals_dict_table); + +const mp_obj_type_t gnss_type = { + { &mp_type_type }, + .name = MP_QSTR_GNSS, + .make_new = gnss_make_new, + .locals_dict = (mp_obj_dict_t *)&gnss_locals_dict, +}; diff --git a/circuitpython/shared-bindings/gnss/GNSS.h b/circuitpython/shared-bindings/gnss/GNSS.h new file mode 100644 index 0000000..dca640b --- /dev/null +++ b/circuitpython/shared-bindings/gnss/GNSS.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Sony Semiconductor Solutions Corporation +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_GNSS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_GNSS_H + +#include "common-hal/gnss/GNSS.h" +#include "shared-bindings/gnss/SatelliteSystem.h" +#include "shared-bindings/gnss/PositionFix.h" + +#include "shared/timeutils/timeutils.h" + +extern const mp_obj_type_t gnss_type; + +void common_hal_gnss_construct(gnss_obj_t *self, unsigned long selection); +void common_hal_gnss_deinit(gnss_obj_t *self); +bool common_hal_gnss_deinited(gnss_obj_t *self); +void common_hal_gnss_update(gnss_obj_t *self); + +mp_float_t common_hal_gnss_get_latitude(gnss_obj_t *self); +mp_float_t common_hal_gnss_get_longitude(gnss_obj_t *self); +mp_float_t common_hal_gnss_get_altitude(gnss_obj_t *self); +void common_hal_gnss_get_timestamp(gnss_obj_t *self, timeutils_struct_time_t *tm); +gnss_positionfix_t common_hal_gnss_get_fix(gnss_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_GNSS_H diff --git a/circuitpython/shared-bindings/gnss/PositionFix.c b/circuitpython/shared-bindings/gnss/PositionFix.c new file mode 100644 index 0000000..e60611d --- /dev/null +++ b/circuitpython/shared-bindings/gnss/PositionFix.c @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Sony Semiconductor Solutions Corporation +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/gnss/PositionFix.h" + +//| class PositionFix: +//| """Position fix mode""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define the position fix mode.""" +//| +//| INVALID: PositionFix +//| """No measurement.""" +//| +//| FIX_2D: PositionFix +//| """2D fix.""" +//| +//| FIX_3D: PositionFix +//| """3D fix.""" +//| +const mp_obj_type_t gnss_positionfix_type; + +const gnss_positionfix_obj_t gnss_positionfix_invalid_obj = { + { &gnss_positionfix_type }, +}; + +const gnss_positionfix_obj_t gnss_positionfix_fix2d_obj = { + { &gnss_positionfix_type }, +}; + +const gnss_positionfix_obj_t gnss_positionfix_fix3d_obj = { + { &gnss_positionfix_type }, +}; + +gnss_positionfix_t gnss_positionfix_obj_to_type(mp_obj_t obj) { + gnss_positionfix_t posfix = POSITIONFIX_INVALID; + if (obj == MP_ROM_PTR(&gnss_positionfix_fix2d_obj)) { + posfix = POSITIONFIX_2D; + } else if (obj == MP_ROM_PTR(&gnss_positionfix_fix3d_obj)) { + posfix = POSITIONFIX_3D; + } + return posfix; +} + +mp_obj_t gnss_positionfix_type_to_obj(gnss_positionfix_t posfix) { + switch (posfix) { + case POSITIONFIX_2D: + return (mp_obj_t)MP_ROM_PTR(&gnss_positionfix_fix2d_obj); + case POSITIONFIX_3D: + return (mp_obj_t)MP_ROM_PTR(&gnss_positionfix_fix3d_obj); + case POSITIONFIX_INVALID: + default: + return (mp_obj_t)MP_ROM_PTR(&gnss_positionfix_invalid_obj); + } +} + +STATIC const mp_rom_map_elem_t gnss_positionfix_locals_dict_table[] = { + {MP_ROM_QSTR(MP_QSTR_INVALID), MP_ROM_PTR(&gnss_positionfix_invalid_obj)}, + {MP_ROM_QSTR(MP_QSTR_FIX_2D), MP_ROM_PTR(&gnss_positionfix_fix2d_obj)}, + {MP_ROM_QSTR(MP_QSTR_FIX_3D), MP_ROM_PTR(&gnss_positionfix_fix3d_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(gnss_positionfix_locals_dict, gnss_positionfix_locals_dict_table); + +STATIC void gnss_positionfix_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr posfix = MP_QSTR_INVALID; + if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&gnss_positionfix_fix2d_obj)) { + posfix = MP_QSTR_FIX_2D; + } else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&gnss_positionfix_fix3d_obj)) { + posfix = MP_QSTR_FIX_3D; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_gnss, MP_QSTR_PositionFix, posfix); +} + +const mp_obj_type_t gnss_positionfix_type = { + { &mp_type_type }, + .name = MP_QSTR_PositionFix, + .print = gnss_positionfix_print, + .locals_dict = (mp_obj_t)&gnss_positionfix_locals_dict, +}; diff --git a/circuitpython/shared-bindings/gnss/PositionFix.h b/circuitpython/shared-bindings/gnss/PositionFix.h new file mode 100644 index 0000000..0fd595f --- /dev/null +++ b/circuitpython/shared-bindings/gnss/PositionFix.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Sony Semiconductor Solutions Corporation +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_POSITIONFIX_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_POSITIONFIX_H + +#include "py/obj.h" + +typedef enum { + POSITIONFIX_INVALID, + POSITIONFIX_2D, + POSITIONFIX_3D, +} gnss_positionfix_t; + +extern const mp_obj_type_t gnss_positionfix_type; + +gnss_positionfix_t gnss_positionfix_obj_to_type(mp_obj_t obj); +mp_obj_t gnss_positionfix_type_to_obj(gnss_positionfix_t mode); + +typedef struct { + mp_obj_base_t base; +} gnss_positionfix_obj_t; +extern const gnss_positionfix_obj_t gnss_positionfix_invalid_obj; +extern const gnss_positionfix_obj_t gnss_positionfix_fix2d_obj; +extern const gnss_positionfix_obj_t gnss_positionfix_fix3d_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_POSITIONFIX_H diff --git a/circuitpython/shared-bindings/gnss/SatelliteSystem.c b/circuitpython/shared-bindings/gnss/SatelliteSystem.c new file mode 100644 index 0000000..edac03d --- /dev/null +++ b/circuitpython/shared-bindings/gnss/SatelliteSystem.c @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Sony Semiconductor Solutions Corporation +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/gnss/SatelliteSystem.h" + +//| class SatelliteSystem: +//| """Satellite system type""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define the satellite system type.""" +//| +//| GPS: SatelliteSystem +//| """Global Positioning System.""" +//| +//| GLONASS: SatelliteSystem +//| """GLObal NAvigation Satellite System.""" +//| +//| SBAS: SatelliteSystem +//| """Satellite Based Augmentation System.""" +//| +//| QZSS_L1CA: SatelliteSystem +//| """Quasi-Zenith Satellite System L1C/A.""" +//| +//| QZSS_L1S: SatelliteSystem +//| """Quasi-Zenith Satellite System L1S.""" +//| +const mp_obj_type_t gnss_satellitesystem_type; + +const gnss_satellitesystem_obj_t gnss_satellitesystem_gps_obj = { + { &gnss_satellitesystem_type }, +}; + +const gnss_satellitesystem_obj_t gnss_satellitesystem_glonass_obj = { + { &gnss_satellitesystem_type }, +}; + +const gnss_satellitesystem_obj_t gnss_satellitesystem_sbas_obj = { + { &gnss_satellitesystem_type }, +}; + +const gnss_satellitesystem_obj_t gnss_satellitesystem_qzss_l1ca_obj = { + { &gnss_satellitesystem_type }, +}; + +const gnss_satellitesystem_obj_t gnss_satellitesystem_qzss_l1s_obj = { + { &gnss_satellitesystem_type }, +}; + +gnss_satellitesystem_t gnss_satellitesystem_obj_to_type(mp_obj_t obj) { + if (obj == MP_ROM_PTR(&gnss_satellitesystem_gps_obj)) { + return SATELLITESYSTEM_GPS; + } else if (obj == MP_ROM_PTR(&gnss_satellitesystem_glonass_obj)) { + return SATELLITESYSTEM_GLONASS; + } else if (obj == MP_ROM_PTR(&gnss_satellitesystem_sbas_obj)) { + return SATELLITESYSTEM_SBAS; + } else if (obj == MP_ROM_PTR(&gnss_satellitesystem_qzss_l1ca_obj)) { + return SATELLITESYSTEM_QZSS_L1CA; + } else if (obj == MP_ROM_PTR(&gnss_satellitesystem_qzss_l1s_obj)) { + return SATELLITESYSTEM_QZSS_L1S; + } + return SATELLITESYSTEM_NONE; +} + +mp_obj_t gnss_satellitesystem_type_to_obj(gnss_satellitesystem_t system) { + switch (system) { + case SATELLITESYSTEM_GPS: + return (mp_obj_t)MP_ROM_PTR(&gnss_satellitesystem_gps_obj); + case SATELLITESYSTEM_GLONASS: + return (mp_obj_t)MP_ROM_PTR(&gnss_satellitesystem_glonass_obj); + case SATELLITESYSTEM_SBAS: + return (mp_obj_t)MP_ROM_PTR(&gnss_satellitesystem_sbas_obj); + case SATELLITESYSTEM_QZSS_L1CA: + return (mp_obj_t)MP_ROM_PTR(&gnss_satellitesystem_qzss_l1ca_obj); + case SATELLITESYSTEM_QZSS_L1S: + return (mp_obj_t)MP_ROM_PTR(&gnss_satellitesystem_qzss_l1s_obj); + case SATELLITESYSTEM_NONE: + default: + return MP_ROM_NONE; + } +} + +STATIC const mp_rom_map_elem_t gnss_satellitesystem_locals_dict_table[] = { + {MP_ROM_QSTR(MP_QSTR_GPS), MP_ROM_PTR(&gnss_satellitesystem_gps_obj)}, + {MP_ROM_QSTR(MP_QSTR_GLONASS), MP_ROM_PTR(&gnss_satellitesystem_glonass_obj)}, + {MP_ROM_QSTR(MP_QSTR_SBAS), MP_ROM_PTR(&gnss_satellitesystem_sbas_obj)}, + {MP_ROM_QSTR(MP_QSTR_QZSS_L1CA), MP_ROM_PTR(&gnss_satellitesystem_qzss_l1ca_obj)}, + {MP_ROM_QSTR(MP_QSTR_QZSS_L1S), MP_ROM_PTR(&gnss_satellitesystem_qzss_l1s_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(gnss_satellitesystem_locals_dict, gnss_satellitesystem_locals_dict_table); + +STATIC void gnss_satellitesystem_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr system = MP_QSTR_None; + if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&gnss_satellitesystem_gps_obj)) { + system = MP_QSTR_GPS; + } else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&gnss_satellitesystem_glonass_obj)) { + system = MP_QSTR_GLONASS; + } else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&gnss_satellitesystem_sbas_obj)) { + system = MP_QSTR_SBAS; + } else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&gnss_satellitesystem_qzss_l1ca_obj)) { + system = MP_QSTR_QZSS_L1CA; + } else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&gnss_satellitesystem_qzss_l1s_obj)) { + system = MP_QSTR_QZSS_L1S; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_gnss, MP_QSTR_SatelliteSystem, system); +} + +const mp_obj_type_t gnss_satellitesystem_type = { + { &mp_type_type }, + .name = MP_QSTR_SatelliteSystem, + .print = gnss_satellitesystem_print, + .locals_dict = (mp_obj_t)&gnss_satellitesystem_locals_dict, +}; diff --git a/circuitpython/shared-bindings/gnss/SatelliteSystem.h b/circuitpython/shared-bindings/gnss/SatelliteSystem.h new file mode 100644 index 0000000..02cd17d --- /dev/null +++ b/circuitpython/shared-bindings/gnss/SatelliteSystem.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Sony Semiconductor Solutions Corporation +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_SATELLITESYSTEM_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_SATELLITESYSTEM_H + +#include "py/obj.h" + +typedef enum { + SATELLITESYSTEM_NONE = 0, + SATELLITESYSTEM_GPS = (1U << 0), + SATELLITESYSTEM_GLONASS = (1U << 1), + SATELLITESYSTEM_SBAS = (1U << 2), + SATELLITESYSTEM_QZSS_L1CA = (1U << 3), + SATELLITESYSTEM_QZSS_L1S = (1U << 4), +} gnss_satellitesystem_t; + +extern const mp_obj_type_t gnss_satellitesystem_type; + +gnss_satellitesystem_t gnss_satellitesystem_obj_to_type(mp_obj_t obj); +mp_obj_t gnss_satellitesystem_type_to_obj(gnss_satellitesystem_t mode); + +typedef struct { + mp_obj_base_t base; +} gnss_satellitesystem_obj_t; +extern const gnss_satellitesystem_obj_t gnss_satellitesystem_gps_obj; +extern const gnss_satellitesystem_obj_t gnss_satellitesystem_glonass_obj; +extern const gnss_satellitesystem_obj_t gnss_satellitesystem_sbas_obj; +extern const gnss_satellitesystem_obj_t gnss_satellitesystem_qzss_l1ca_obj; +extern const gnss_satellitesystem_obj_t gnss_satellitesystem_qzss_l1s_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_SATELLITESYSTEM_H diff --git a/circuitpython/shared-bindings/gnss/__init__.c b/circuitpython/shared-bindings/gnss/__init__.c new file mode 100644 index 0000000..ab6cbf5 --- /dev/null +++ b/circuitpython/shared-bindings/gnss/__init__.c @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Sony Semiconductor Solutions Corporation +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "shared-bindings/gnss/GNSS.h" +#include "shared-bindings/gnss/SatelliteSystem.h" +#include "shared-bindings/gnss/PositionFix.h" +#include "shared-bindings/util.h" + +//| """Global Navigation Satellite System +//| +//| The `gnss` module contains classes to control the GNSS and acquire positioning information.""" +//| +STATIC const mp_rom_map_elem_t gnss_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gnss) }, + { MP_ROM_QSTR(MP_QSTR_GNSS), MP_ROM_PTR(&gnss_type) }, + + // Enum-like Classes. + { MP_ROM_QSTR(MP_QSTR_SatelliteSystem), MP_ROM_PTR(&gnss_satellitesystem_type) }, + { MP_ROM_QSTR(MP_QSTR_PositionFix), MP_ROM_PTR(&gnss_positionfix_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(gnss_module_globals, gnss_module_globals_table); + +const mp_obj_module_t gnss_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&gnss_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_gnss, gnss_module, CIRCUITPY_GNSS); diff --git a/circuitpython/shared-bindings/help.rst b/circuitpython/shared-bindings/help.rst new file mode 100644 index 0000000..ccc3790 --- /dev/null +++ b/circuitpython/shared-bindings/help.rst @@ -0,0 +1,31 @@ +.. 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. + +:func:`help` -- Built-in method to provide helpful information +============================================================== + +.. function:: help(object=None) + + Prints a help method about the given object. When ``object`` is none, + prints general port information. diff --git a/circuitpython/shared-bindings/i2cperipheral/I2CPeripheral.c b/circuitpython/shared-bindings/i2cperipheral/I2CPeripheral.c new file mode 100644 index 0000000..b63e8a8 --- /dev/null +++ b/circuitpython/shared-bindings/i2cperipheral/I2CPeripheral.c @@ -0,0 +1,436 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * 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 "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/i2cperipheral/I2CPeripheral.h" +#include "shared-bindings/time/__init__.h" +#include "shared-bindings/util.h" + +#include "shared/runtime/buffer_helper.h" +#include "shared/runtime/context_manager_helpers.h" +#include "shared/runtime/interrupt_char.h" + +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +STATIC mp_obj_t mp_obj_new_i2cperipheral_i2c_peripheral_request(i2cperipheral_i2c_peripheral_obj_t *peripheral, uint8_t address, bool is_read, bool is_restart) { + i2cperipheral_i2c_peripheral_request_obj_t *self = m_new_obj(i2cperipheral_i2c_peripheral_request_obj_t); + self->base.type = &i2cperipheral_i2c_peripheral_request_type; + self->peripheral = peripheral; + self->address = address; + self->is_read = is_read; + self->is_restart = is_restart; + return (mp_obj_t)self; +} + +//| class I2CPeripheral: +//| """Two wire serial protocol peripheral""" +//| +//| def __init__(self, scl: microcontroller.Pin, sda: microcontroller.Pin, addresses: Sequence[int], smbus: bool = False) -> None: +//| """I2C is a two-wire protocol for communicating between devices. +//| This implements the peripheral (sensor, secondary) side. +//| +//| :param ~microcontroller.Pin scl: The clock pin +//| :param ~microcontroller.Pin sda: The data pin +//| :param addresses: The I2C addresses to respond to (how many is hw dependent). +//| :type addresses: list[int] +//| :param bool smbus: Use SMBUS timings if the hardware supports it""" +//| ... +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + i2cperipheral_i2c_peripheral_obj_t *self = m_new_obj(i2cperipheral_i2c_peripheral_obj_t); + self->base.type = &i2cperipheral_i2c_peripheral_type; + enum { ARG_scl, ARG_sda, ARG_addresses, ARG_smbus }; + 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_addresses, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_smbus, 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 *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); + + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(args[ARG_addresses].u_obj, &iter_buf); + mp_obj_t item; + uint8_t *addresses = NULL; + unsigned int i = 0; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + mp_int_t value; + if (!mp_obj_get_int_maybe(item, &value)) { + mp_raise_TypeError_varg(translate("can't convert %q to %q"), MP_QSTR_address, MP_QSTR_int); + } + if (value < 0x00 || value > 0x7f) { + mp_raise_ValueError(translate("address out of bounds")); + } + addresses = m_renew(uint8_t, addresses, i, i + 1); + addresses[i++] = value; + } + if (i == 0) { + mp_raise_ValueError(translate("addresses is empty")); + } + + common_hal_i2cperipheral_i2c_peripheral_construct(self, scl, sda, addresses, i, args[ARG_smbus].u_bool); + 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 i2cperipheral_i2c_peripheral_obj_deinit(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &i2cperipheral_i2c_peripheral_type)); + i2cperipheral_i2c_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_i2cperipheral_i2c_peripheral_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cperipheral_i2c_peripheral_deinit_obj, i2cperipheral_i2c_peripheral_obj_deinit); + +//| def __enter__(self) -> I2CPeripheral: +//| """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 i2cperipheral_i2c_peripheral_obj___exit__(size_t n_args, const mp_obj_t *args) { + mp_check_self(mp_obj_is_type(args[0], &i2cperipheral_i2c_peripheral_type)); + i2cperipheral_i2c_peripheral_obj_t *self = MP_OBJ_TO_PTR(args[0]); + common_hal_i2cperipheral_i2c_peripheral_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(i2cperipheral_i2c_peripheral___exit___obj, 4, 4, i2cperipheral_i2c_peripheral_obj___exit__); + +//| def request(self, timeout: float = -1) -> I2CPeripheralRequest: +//| """Wait for an I2C request. +//| +//| :param float timeout: Timeout in seconds. Zero means wait forever, a negative value means check once +//| :return: I2C Slave Request or None if timeout=-1 and there's no request +//| :rtype: ~i2cperipheral.I2CPeripheralRequest""" +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_check_self(mp_obj_is_type(pos_args[0], &i2cperipheral_i2c_peripheral_type)); + i2cperipheral_i2c_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + if (common_hal_i2cperipheral_i2c_peripheral_deinited(self)) { + raise_deinited_error(); + } + enum { ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1)} }, + }; + 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); + + #if MICROPY_PY_BUILTINS_FLOAT + float f = mp_obj_get_float(args[ARG_timeout].u_obj) * 1000; + int timeout_ms = (int)f; + #else + int timeout_ms = mp_obj_get_int(args[ARG_timeout].u_obj) * 1000; + #endif + + bool forever = false; + uint64_t timeout_end = 0; + if (timeout_ms == 0) { + forever = true; + } else if (timeout_ms > 0) { + timeout_end = common_hal_time_monotonic_ms() + timeout_ms; + } + + int last_error = 0; + + do { + uint8_t address; + bool is_read; + bool is_restart; + + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + return mp_const_none; + } + + int status = common_hal_i2cperipheral_i2c_peripheral_is_addressed(self, &address, &is_read, &is_restart); + if (status < 0) { + // On error try one more time before bailing out + if (last_error) { + mp_raise_OSError(last_error); + } + last_error = -status; + mp_hal_delay_ms(10); + continue; + } + + last_error = 0; + + if (status == 0) { + mp_hal_delay_us(10); + continue; + } + + return mp_obj_new_i2cperipheral_i2c_peripheral_request(self, address, is_read, is_restart); + } while (forever || common_hal_time_monotonic_ms() < timeout_end); + + if (timeout_ms > 0) { + mp_raise_OSError(MP_ETIMEDOUT); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(i2cperipheral_i2c_peripheral_request_obj, 1, i2cperipheral_i2c_peripheral_request); + +STATIC const mp_rom_map_elem_t i2cperipheral_i2c_peripheral_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&i2cperipheral_i2c_peripheral___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_request), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_obj) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(i2cperipheral_i2c_peripheral_locals_dict, i2cperipheral_i2c_peripheral_locals_dict_table); + +const mp_obj_type_t i2cperipheral_i2c_peripheral_type = { + { &mp_type_type }, + .name = MP_QSTR_I2CPeripheral, + .make_new = i2cperipheral_i2c_peripheral_make_new, + .locals_dict = (mp_obj_dict_t *)&i2cperipheral_i2c_peripheral_locals_dict, +}; + +//| class I2CPeripheralRequest: +//| +//| def __init__(self, peripheral: i2cperipheral.I2CPeripheral, address: int, is_read: bool, is_restart: bool) -> None: +//| """Information about an I2C transfer request +//| This cannot be instantiated directly, but is returned by :py:meth:`I2CPeripheral.request`. +//| +//| :param peripheral: The I2CPeripheral object receiving this request +//| :param address: I2C address +//| :param is_read: True if the main peripheral is requesting data +//| :param is_restart: Repeated Start Condition""" +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 4, 4, false); + return mp_obj_new_i2cperipheral_i2c_peripheral_request(args[0], mp_obj_get_int(args[1]), mp_obj_is_true(args[2]), mp_obj_is_true(args[3])); +} + +//| def __enter__(self) -> I2CPeripheralRequest: +//| """No-op used in Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Close the request.""" +//| ... +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_obj___exit__(size_t n_args, const mp_obj_t *args) { + mp_check_self(mp_obj_is_type(args[0], &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(args[0]); + common_hal_i2cperipheral_i2c_peripheral_close(self->peripheral); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(i2cperipheral_i2c_peripheral_request___exit___obj, 4, 4, i2cperipheral_i2c_peripheral_request_obj___exit__); + +//| address: int +//| """The I2C address of the request.""" +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_get_address(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(self->address); +} +MP_DEFINE_CONST_PROP_GET(i2cperipheral_i2c_peripheral_request_address_obj, i2cperipheral_i2c_peripheral_request_get_address); + +//| is_read: bool +//| """The I2C main controller is reading from this peripheral.""" +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_get_is_read(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->is_read); +} +MP_DEFINE_CONST_PROP_GET(i2cperipheral_i2c_peripheral_request_is_read_obj, i2cperipheral_i2c_peripheral_request_get_is_read); + +//| is_restart: bool +//| """Is Repeated Start Condition.""" +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_get_is_restart(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->is_restart); +} +MP_DEFINE_CONST_PROP_GET(i2cperipheral_i2c_peripheral_request_is_restart_obj, i2cperipheral_i2c_peripheral_request_get_is_restart); + +//| def read(self, n: int = -1, ack: bool = True) -> bytearray: +//| """Read data. +//| If ack=False, the caller is responsible for calling :py:meth:`I2CPeripheralRequest.ack`. +//| +//| :param n: Number of bytes to read (negative means all) +//| :param ack: Whether or not to send an ACK after the n'th byte +//| :return: Bytes read""" +//| ... +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_check_self(mp_obj_is_type(pos_args[0], &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + enum { ARG_n, ARG_ack }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_n, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_ack, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + }; + 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); + + if (self->is_read) { + mp_raise_OSError(MP_EACCES); + } + + int n = args[ARG_n].u_int; + if (n == 0) { + return mp_obj_new_bytearray(0, NULL); + } + bool ack = args[ARG_ack].u_bool; + + int i = 0; + uint8_t *buffer = NULL; + uint64_t timeout_end = common_hal_time_monotonic_ms() + 10 * 1000; + while (common_hal_time_monotonic_ms() < timeout_end) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } + + uint8_t data; + int num = common_hal_i2cperipheral_i2c_peripheral_read_byte(self->peripheral, &data); + if (num == 0) { + break; + } + + buffer = m_renew(uint8_t, buffer, i, i + 1); + buffer[i++] = data; + if (i == n) { + if (ack) { + common_hal_i2cperipheral_i2c_peripheral_ack(self->peripheral, true); + } + break; + } + common_hal_i2cperipheral_i2c_peripheral_ack(self->peripheral, true); + } + + return mp_obj_new_bytearray(i, buffer); +} +MP_DEFINE_CONST_FUN_OBJ_KW(i2cperipheral_i2c_peripheral_request_read_obj, 1, i2cperipheral_i2c_peripheral_request_read); + +//| def write(self, buffer: ReadableBuffer) -> int: +//| """Write the data contained in buffer. +//| +//| :param ~circuitpython_typing.ReadableBuffer buffer: Write out the data in this buffer +//| :return: Number of bytes written""" +//| ... +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_write(mp_obj_t self_in, mp_obj_t buf_in) { + mp_check_self(mp_obj_is_type(self_in, &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (!self->is_read) { + mp_raise_OSError(MP_EACCES); + } + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + + for (size_t i = 0; i < bufinfo.len; i++) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } + + int num = common_hal_i2cperipheral_i2c_peripheral_write_byte(self->peripheral, ((uint8_t *)(bufinfo.buf))[i]); + if (num == 0) { + return mp_obj_new_int(i); + } + } + + return mp_obj_new_int(bufinfo.len); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(i2cperipheral_i2c_peripheral_request_write_obj, i2cperipheral_i2c_peripheral_request_write); + +//| def ack(self, ack: bool = True) -> None: +//| """Acknowledge or Not Acknowledge last byte received. +//| Use together with :py:meth:`I2CPeripheralRequest.read` ack=False. +//| +//| :param ack: Whether to send an ACK or NACK""" +//| ... +//| +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_ack(uint n_args, const mp_obj_t *args) { + mp_check_self(mp_obj_is_type(args[0], &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(args[0]); + bool ack = (n_args == 1) ? true : mp_obj_is_true(args[1]); + + if (self->is_read) { + mp_raise_OSError(MP_EACCES); + } + + common_hal_i2cperipheral_i2c_peripheral_ack(self->peripheral, ack); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(i2cperipheral_i2c_peripheral_request_ack_obj, 1, 2, i2cperipheral_i2c_peripheral_request_ack); + +STATIC mp_obj_t i2cperipheral_i2c_peripheral_request_close(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &i2cperipheral_i2c_peripheral_request_type)); + i2cperipheral_i2c_peripheral_request_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_i2cperipheral_i2c_peripheral_close(self->peripheral); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(i2cperipheral_i2c_peripheral_request_close_obj, i2cperipheral_i2c_peripheral_request_close); + +STATIC const mp_rom_map_elem_t i2cperipheral_i2c_peripheral_request_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_read), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_is_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_restart), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_is_restart_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_ack), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_ack_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_request_close_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(i2cperipheral_i2c_peripheral_request_locals_dict, i2cperipheral_i2c_peripheral_request_locals_dict_table); + +const mp_obj_type_t i2cperipheral_i2c_peripheral_request_type = { + { &mp_type_type }, + .name = MP_QSTR_I2CPeripheralRequest, + .make_new = i2cperipheral_i2c_peripheral_request_make_new, + .locals_dict = (mp_obj_dict_t *)&i2cperipheral_i2c_peripheral_request_locals_dict, +}; diff --git a/circuitpython/shared-bindings/i2cperipheral/I2CPeripheral.h b/circuitpython/shared-bindings/i2cperipheral/I2CPeripheral.h new file mode 100644 index 0000000..d3db1b9 --- /dev/null +++ b/circuitpython/shared-bindings/i2cperipheral/I2CPeripheral.h @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * 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_I2C_SLAVE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_I2C_SLAVE_H + +#include "py/obj.h" + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/i2cperipheral/I2CPeripheral.h" + +typedef struct { + mp_obj_base_t base; + i2cperipheral_i2c_peripheral_obj_t *peripheral; + uint16_t address; + bool is_read; + bool is_restart; +} i2cperipheral_i2c_peripheral_request_obj_t; + +extern const mp_obj_type_t i2cperipheral_i2c_peripheral_request_type; + +extern const mp_obj_type_t i2cperipheral_i2c_peripheral_type; + +extern void common_hal_i2cperipheral_i2c_peripheral_construct(i2cperipheral_i2c_peripheral_obj_t *self, + const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, + uint8_t *addresses, unsigned int num_addresses, bool smbus); +extern void common_hal_i2cperipheral_i2c_peripheral_deinit(i2cperipheral_i2c_peripheral_obj_t *self); +extern bool common_hal_i2cperipheral_i2c_peripheral_deinited(i2cperipheral_i2c_peripheral_obj_t *self); + +extern int common_hal_i2cperipheral_i2c_peripheral_is_addressed(i2cperipheral_i2c_peripheral_obj_t *self, + uint8_t *address, bool *is_read, bool *is_restart); +extern int common_hal_i2cperipheral_i2c_peripheral_read_byte(i2cperipheral_i2c_peripheral_obj_t *self, uint8_t *data); +extern int common_hal_i2cperipheral_i2c_peripheral_write_byte(i2cperipheral_i2c_peripheral_obj_t *self, uint8_t data); +extern void common_hal_i2cperipheral_i2c_peripheral_ack(i2cperipheral_i2c_peripheral_obj_t *self, bool ack); +extern void common_hal_i2cperipheral_i2c_peripheral_close(i2cperipheral_i2c_peripheral_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_I2C_SLAVE_H diff --git a/circuitpython/shared-bindings/i2cperipheral/__init__.c b/circuitpython/shared-bindings/i2cperipheral/__init__.c new file mode 100644 index 0000000..fde7002 --- /dev/null +++ b/circuitpython/shared-bindings/i2cperipheral/__init__.c @@ -0,0 +1,108 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * 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/i2cperipheral/__init__.h" +#include "shared-bindings/i2cperipheral/I2CPeripheral.h" + +#include "py/runtime.h" + +//| """Two wire serial protocol peripheral +//| +//| The `i2cperipheral` module contains classes to support an I2C peripheral. +//| +//| Example emulating a peripheral with 2 addresses (read and write):: +//| +//| import board +//| from i2cperipheral import I2CPeripheral +//| +//| regs = [0] * 16 +//| index = 0 +//| +//| with I2CPeripheral(board.SCL, board.SDA, (0x40, 0x41)) as device: +//| while True: +//| r = device.request() +//| if not r: +//| # Maybe do some housekeeping +//| continue +//| with r: # Closes the transfer if necessary by sending a NACK or feeding dummy bytes +//| if r.address == 0x40: +//| if not r.is_read: # Main write which is Selected read +//| b = r.read(1) +//| if not b or b[0] > 15: +//| break +//| index = b[0] +//| b = r.read(1) +//| if b: +//| regs[index] = b[0] +//| elif r.is_restart: # Combined transfer: This is the Main read message +//| n = r.write(bytes([regs[index]])) +//| #else: +//| # A read transfer is not supported in this example +//| # If the microcontroller tries, it will get 0xff byte(s) by the ctx manager (r.close()) +//| elif r.address == 0x41: +//| if not r.is_read: +//| b = r.read(1) +//| if b and b[0] == 0xde: +//| # do something +//| pass +//| +//| This example sets up an I2C device that can be accessed from Linux like this:: +//| +//| $ i2cget -y 1 0x40 0x01 +//| 0x00 +//| $ i2cset -y 1 0x40 0x01 0xaa +//| $ i2cget -y 1 0x40 0x01 +//| 0xaa +//| +//| .. warning:: +//| I2CPeripheral makes use of clock stretching in order to slow down +//| the host. +//| Make sure the I2C host supports this. +//| +//| Raspberry Pi in particular does not support this with its I2C hw block. +//| This can be worked around by using the ``i2c-gpio`` bit banging driver. +//| Since the RPi firmware uses the hw i2c, it's not possible to emulate a HAT eeprom.""" +//| + +STATIC const mp_rom_map_elem_t i2cperipheral_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_i2cperipheral) }, + { MP_ROM_QSTR(MP_QSTR_I2CPeripheral), MP_ROM_PTR(&i2cperipheral_i2c_peripheral_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(i2cperipheral_module_globals, i2cperipheral_module_globals_table); + +const mp_obj_module_t i2cperipheral_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&i2cperipheral_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_i2cperipheral, i2cperipheral_module, CIRCUITPY_I2CPERIPHERAL); diff --git a/circuitpython/shared-bindings/imagecapture/ParallelImageCapture.c b/circuitpython/shared-bindings/imagecapture/ParallelImageCapture.c new file mode 100644 index 0000000..7f90f09 --- /dev/null +++ b/circuitpython/shared-bindings/imagecapture/ParallelImageCapture.c @@ -0,0 +1,200 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared/runtime/context_manager_helpers.h" + +#include "shared-bindings/imagecapture/ParallelImageCapture.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "common-hal/imagecapture/ParallelImageCapture.h" + +//| class ParallelImageCapture: +//| """Capture image frames from a camera with parallel data interface""" +//| +//| def __init__( +//| self, +//| *, +//| data_pins: List[microcontroller.Pin], +//| clock: microcontroller.Pin, +//| vsync: Optional[microcontroller.Pin], +//| href: Optional[microcontroller.Pin], +//| ) -> None: +//| """Create a parallel image capture object +//| +//| :param List[microcontroller.Pin] data_pins: The data pins. +//| :param microcontroller.Pin clock: The pixel clock input. +//| :param microcontroller.Pin vsync: The vertical sync input, which has a negative-going pulse at the beginning of each frame. +//| :param microcontroller.Pin href: The horizontal reference input, which is high whenever the camera is transmitting valid pixel information. +//| """ +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_data_pins, ARG_clock, ARG_vsync, ARG_href, + NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data_pins, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = MP_ROM_NONE } }, + { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_vsync, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_href, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint8_t pins[32]; + uint8_t pin_count; + validate_pins(MP_QSTR_data, pins, MP_ARRAY_SIZE(pins), args[ARG_data_pins].u_obj, &pin_count); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj); + const mcu_pin_obj_t *vsync = validate_obj_is_free_pin_or_none(args[ARG_vsync].u_obj); + const mcu_pin_obj_t *href = validate_obj_is_free_pin_or_none(args[ARG_href].u_obj); + + imagecapture_parallelimagecapture_obj_t *self = m_new_obj(imagecapture_parallelimagecapture_obj_t); + self->base.type = &imagecapture_parallelimagecapture_type; + + common_hal_imagecapture_parallelimagecapture_construct(self, pins, pin_count, clock, vsync, href); + + return self; +} + +//| def capture(self, buffer: WriteableBuffer) -> WriteableBuffer: +//| """Capture a single frame into the given buffer. +//| +//| This will stop a continuous-mode capture, if one is in progress.""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_capture(mp_obj_t self_in, mp_obj_t buffer) { + imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in; + common_hal_imagecapture_parallelimagecapture_singleshot_capture(self, buffer); + + return buffer; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(imagecapture_parallelimagecapture_capture_obj, imagecapture_parallelimagecapture_capture); + +//| def continuous_capture_start(self, buffer1: WriteableBuffer, buffer2: WriteableBuffer, /) -> None: +//| """Begin capturing into the given buffers in the background. +//| +//| Call `continuous_capture_get_frame` to get the next available +//| frame, and `continuous_capture_stop` to stop capturing. +//| +//| Until `continuous_capture_stop` (or `deinit`) is called, the +//| `ParallelImageCapture` object keeps references to ``buffer1`` and +//| ``buffer2``, so the objects will not be garbage collected.""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_continuous_capture_start(mp_obj_t self_in, mp_obj_t buffer1, mp_obj_t buffer2) { + imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in; + common_hal_imagecapture_parallelimagecapture_continuous_capture_start(self, buffer1, buffer2); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(imagecapture_parallelimagecapture_continuous_capture_start_obj, imagecapture_parallelimagecapture_continuous_capture_start); + +//| def continuous_capture_get_frame(self) -> WriteableBuffer: +//| """Return the next available frame, one of the two buffers passed to `continuous_capture_start`""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_continuous_capture_get_frame(mp_obj_t self_in) { + imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in; + return common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(imagecapture_parallelimagecapture_continuous_capture_get_frame_obj, imagecapture_parallelimagecapture_continuous_capture_get_frame); + + + +//| def continuous_capture_stop(self) -> None: +//| """Stop continuous capture. +//| +//| Calling this method also causes the object to release its +//| references to the buffers passed to `continuous_capture_start`, +//| potentially allowing the objects to be garbage collected.""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_continuous_capture_stop(mp_obj_t self_in) { + imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in; + common_hal_imagecapture_parallelimagecapture_continuous_capture_stop(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(imagecapture_parallelimagecapture_continuous_capture_stop_obj, imagecapture_parallelimagecapture_continuous_capture_stop); + + + + +//| def deinit(self) -> None: +//| """Deinitialize this instance""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_deinit(mp_obj_t self_in) { + imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in; + common_hal_imagecapture_parallelimagecapture_deinit(self); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(imagecapture_parallelimagecapture_deinit_obj, imagecapture_parallelimagecapture_deinit); + +//| def __enter__(self) -> ParallelImageCapture: +//| """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 imagecapture_parallelimagecapture___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_imagecapture_parallelimagecapture_deinit(args[0]); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(imagecapture_parallelimagecapture___exit___obj, 4, 4, imagecapture_parallelimagecapture___exit__); + + +STATIC const mp_rom_map_elem_t imagecapture_parallelimagecapture_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&imagecapture_parallelimagecapture_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&imagecapture_parallelimagecapture___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_capture), MP_ROM_PTR(&imagecapture_parallelimagecapture_capture_obj) }, + { MP_ROM_QSTR(MP_QSTR_continuous_capture_start), MP_ROM_PTR(&imagecapture_parallelimagecapture_continuous_capture_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_continuous_capture_stop), MP_ROM_PTR(&imagecapture_parallelimagecapture_continuous_capture_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_continuous_capture_get_frame), MP_ROM_PTR(&imagecapture_parallelimagecapture_continuous_capture_get_frame_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(imagecapture_parallelimagecapture_locals_dict, imagecapture_parallelimagecapture_locals_dict_table); + +const mp_obj_type_t imagecapture_parallelimagecapture_type = { + { &mp_type_type }, + .name = MP_QSTR_ParallelImageCapture, + .make_new = imagecapture_parallelimagecapture_make_new, + .locals_dict = (mp_obj_dict_t *)&imagecapture_parallelimagecapture_locals_dict, +}; diff --git a/circuitpython/shared-bindings/imagecapture/ParallelImageCapture.h b/circuitpython/shared-bindings/imagecapture/ParallelImageCapture.h new file mode 100644 index 0000000..72ddc23 --- /dev/null +++ b/circuitpython/shared-bindings/imagecapture/ParallelImageCapture.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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. + */ + +#pragma once + +#include "common-hal/microcontroller/Pin.h" + +typedef struct imagecapture_parallelimagecapture_obj imagecapture_parallelimagecapture_obj_t; +extern const mp_obj_type_t imagecapture_parallelimagecapture_type; + +// if only the first element of data_pins is non-NULL, the pins are sequential in microcontroller pin numbering. +void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_parallelimagecapture_obj_t *self, + const uint8_t data_pins[], + uint8_t data_count, + const mcu_pin_obj_t *data_clock, + const mcu_pin_obj_t *vertical_sync, + const mcu_pin_obj_t *horizontal_sync); +void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self); +bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self); +void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer); +void common_hal_imagecapture_parallelimagecapture_continuous_capture_start(imagecapture_parallelimagecapture_obj_t *self, mp_obj_t buffer1, mp_obj_t buffer2); +void common_hal_imagecapture_parallelimagecapture_continuous_capture_stop(imagecapture_parallelimagecapture_obj_t *self); +mp_obj_t common_hal_imagecapture_parallelimagecapture_continuous_capture_get_frame(imagecapture_parallelimagecapture_obj_t *self); diff --git a/circuitpython/shared-bindings/imagecapture/__init__.c b/circuitpython/shared-bindings/imagecapture/__init__.c new file mode 100644 index 0000000..0e5092a --- /dev/null +++ b/circuitpython/shared-bindings/imagecapture/__init__.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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/imagecapture/ParallelImageCapture.h" + +//| """Support for "Parallel capture" interfaces""" +//| + +STATIC const mp_rom_map_elem_t imagecapture_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_imagecapture) }, + { MP_ROM_QSTR(MP_QSTR_ParallelImageCapture), MP_ROM_PTR(&imagecapture_parallelimagecapture_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(imagecapture_module_globals, imagecapture_module_globals_table); + +const mp_obj_module_t imagecapture_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&imagecapture_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_imagecapture, imagecapture_module, CIRCUITPY_IMAGECAPTURE); diff --git a/circuitpython/shared-bindings/imagecapture/__init__.h b/circuitpython/shared-bindings/imagecapture/__init__.h new file mode 100644 index 0000000..1e170aa --- /dev/null +++ b/circuitpython/shared-bindings/imagecapture/__init__.h @@ -0,0 +1,27 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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. + */ + +#pragma once diff --git a/circuitpython/shared-bindings/index.rst b/circuitpython/shared-bindings/index.rst new file mode 100644 index 0000000..bf04ae1 --- /dev/null +++ b/circuitpython/shared-bindings/index.rst @@ -0,0 +1,23 @@ +Core Modules +======================================== + +These core modules are intended on being consistent across ports and boards. +A module may not exist on a port/board if no underlying hardware support is +present or if flash space is limited. For example, a microcontroller without +analog features will not have `analogio`. See the `support_matrix` page for +a list of modules supported on each board. + +.. toctree:: + :hidden: + + support_matrix + +Modules +--------- + +.. toctree:: + :glob: + :maxdepth: 2 + + ../shared-bindings/*/index + help diff --git a/circuitpython/shared-bindings/ipaddress/IPv4Address.c b/circuitpython/shared-bindings/ipaddress/IPv4Address.c new file mode 100644 index 0000000..2320432 --- /dev/null +++ b/circuitpython/shared-bindings/ipaddress/IPv4Address.c @@ -0,0 +1,194 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 "shared-bindings/ipaddress/IPv4Address.h" + +#include <string.h> +#include <stdio.h> + +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/ipaddress/__init__.h" + +//| class IPv4Address: +//| """Encapsulates an IPv4 address.""" +//| + +//| def __init__(self, address: Union[int, str, bytes]) -> None: +//| """Create a new IPv4Address object encapsulating the address value. +//| +//| The value itself can either be bytes or a string formatted address.""" +//| ... +//| +STATIC mp_obj_t ipaddress_ipv4address_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_address }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + + 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 mp_obj_t address = args[ARG_address].u_obj; + + uint32_t value; + uint8_t *buf = NULL; + if (mp_obj_get_int_maybe(address, (mp_int_t *)&value)) { + // We're done. + buf = (uint8_t *)&value; + } else if (mp_obj_is_str(address)) { + GET_STR_DATA_LEN(address, str_data, str_len); + if (!ipaddress_parse_ipv4address((const char *)str_data, str_len, &value)) { + mp_raise_ValueError(translate("Not a valid IP string")); + } + buf = (uint8_t *)&value; + } else { + mp_buffer_info_t buf_info; + if (mp_get_buffer(address, &buf_info, MP_BUFFER_READ)) { + if (buf_info.len != 4) { + mp_raise_ValueError_varg(translate("Address must be %d bytes long"), 4); + } + buf = buf_info.buf; + } + } + + + ipaddress_ipv4address_obj_t *self = m_new_obj(ipaddress_ipv4address_obj_t); + self->base.type = &ipaddress_ipv4address_type; + + common_hal_ipaddress_ipv4address_construct(self, buf, 4); + + return MP_OBJ_FROM_PTR(self); +} + +//| packed: bytes +//| """The bytes that make up the address (read-only).""" +//| +STATIC mp_obj_t ipaddress_ipv4address_get_packed(mp_obj_t self_in) { + ipaddress_ipv4address_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_ipaddress_ipv4address_get_packed(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(ipaddress_ipv4address_get_packed_obj, ipaddress_ipv4address_get_packed); + +MP_PROPERTY_GETTER(ipaddress_ipv4address_packed_obj, + (mp_obj_t)&ipaddress_ipv4address_get_packed_obj); + +//| version: int +//| """4 for IPv4, 6 for IPv6""" +//| +STATIC mp_obj_t ipaddress_ipv4address_get_version(mp_obj_t self_in) { + ipaddress_ipv4address_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t buf_info; + mp_obj_t address_bytes = common_hal_ipaddress_ipv4address_get_packed(self); + mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ); + mp_int_t version = 6; + if (buf_info.len == 4) { + version = 4; + } + + return MP_OBJ_NEW_SMALL_INT(version); +} +MP_DEFINE_CONST_FUN_OBJ_1(ipaddress_ipv4address_get_version_obj, ipaddress_ipv4address_get_version); + +MP_PROPERTY_GETTER(ipaddress_ipv4address_version_obj, + (mp_obj_t)&ipaddress_ipv4address_get_version_obj); + +//| def __eq__(self, other: object) -> bool: +//| """Two Address objects are equal if their addresses and address types are equal.""" +//| ... +//| +STATIC mp_obj_t ipaddress_ipv4address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + // Two Addresses are equal if their address bytes and address_type are equal + case MP_BINARY_OP_EQUAL: + if (mp_obj_is_type(rhs_in, &ipaddress_ipv4address_type)) { + ipaddress_ipv4address_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); + ipaddress_ipv4address_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool( + mp_obj_equal(common_hal_ipaddress_ipv4address_get_packed(lhs), + common_hal_ipaddress_ipv4address_get_packed(rhs))); + + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __hash__(self) -> int: +//| """Returns a hash for the IPv4Address data.""" +//| ... +//| +STATIC mp_obj_t ipaddress_ipv4address_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + switch (op) { + // Two Addresses are equal if their address bytes and address_type are equal + case MP_UNARY_OP_HASH: { + mp_obj_t bytes = common_hal_ipaddress_ipv4address_get_packed(MP_OBJ_TO_PTR(self_in)); + GET_STR_HASH(bytes, h); + if (h == 0) { + GET_STR_DATA_LEN(bytes, data, len); + h = qstr_compute_hash(data, len); + } + return MP_OBJ_NEW_SMALL_INT(h); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC void ipaddress_ipv4address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + ipaddress_ipv4address_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t buf_info; + mp_obj_t address_bytes = common_hal_ipaddress_ipv4address_get_packed(self); + mp_get_buffer_raise(address_bytes, &buf_info, MP_BUFFER_READ); + + const uint8_t *buf = (uint8_t *)buf_info.buf; + mp_printf(print, "%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]); +} + +STATIC const mp_rom_map_elem_t ipaddress_ipv4address_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_packed), MP_ROM_PTR(&ipaddress_ipv4address_packed_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ipaddress_ipv4address_locals_dict, ipaddress_ipv4address_locals_dict_table); + +const mp_obj_type_t ipaddress_ipv4address_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Address, + .make_new = ipaddress_ipv4address_make_new, + .locals_dict = (mp_obj_dict_t *)&ipaddress_ipv4address_locals_dict, + .print = ipaddress_ipv4address_print, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = ipaddress_ipv4address_unary_op, + .binary_op = ipaddress_ipv4address_binary_op, + ) +}; diff --git a/circuitpython/shared-bindings/ipaddress/IPv4Address.h b/circuitpython/shared-bindings/ipaddress/IPv4Address.h new file mode 100644 index 0000000..46a52a0 --- /dev/null +++ b/circuitpython/shared-bindings/ipaddress/IPv4Address.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_IPADDRESS_IPV4ADDRESS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_IPADDRESS_IPV4ADDRESS_H + +#include "shared-module/ipaddress/IPv4Address.h" + +extern const mp_obj_type_t ipaddress_ipv4address_type; + +mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value); +void common_hal_ipaddress_ipv4address_construct(ipaddress_ipv4address_obj_t *self, uint8_t *buf, size_t len); +mp_obj_t common_hal_ipaddress_ipv4address_get_packed(ipaddress_ipv4address_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_IPADDRESS_IPV4ADDRESS_H diff --git a/circuitpython/shared-bindings/ipaddress/__init__.c b/circuitpython/shared-bindings/ipaddress/__init__.c new file mode 100644 index 0000000..6da5a52 --- /dev/null +++ b/circuitpython/shared-bindings/ipaddress/__init__.c @@ -0,0 +1,115 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "py/objexcept.h" +#include "py/objstr.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "shared-bindings/ipaddress/__init__.h" +#include "shared-bindings/ipaddress/IPv4Address.h" + +//| """ +//| The `ipaddress` module provides types for IP addresses. It is a subset of CPython's ipaddress +//| module. +//| """ +//| + + +bool ipaddress_parse_ipv4address(const char *str_data, size_t str_len, uint32_t *ip_out) { + size_t period_count = 0; + size_t period_index[4] = {0, 0, 0, str_len}; + for (size_t i = 0; i < str_len; i++) { + if (str_data[i] == '.') { + if (period_count < 3) { + period_index[period_count] = i; + } + period_count++; + } + } + if (period_count > 3) { + return false; + } + + size_t last_period = 0; + if (ip_out != NULL) { + *ip_out = 0; + } + for (size_t i = 0; i < 4; i++) { + // Catch exceptions thrown by mp_parse_num_integer + nlr_buf_t nlr; + mp_obj_t octet; + if (nlr_push(&nlr) == 0) { + octet = mp_parse_num_integer((const char *)str_data + last_period, period_index[i] - last_period, 10, NULL); + nlr_pop(); + } else { + return false; + } + last_period = period_index[i] + 1; + if (ip_out != NULL) { + mp_int_t int_octet = MP_OBJ_SMALL_INT_VALUE(octet); + *ip_out |= int_octet << (i * 8); + } + } + return true; +} + +//| def ip_address(obj: Union[int, str]) -> IPv4Address: +//| """Return a corresponding IP address object or raise ValueError if not possible.""" +//| ... +//| + +STATIC mp_obj_t ipaddress_ip_address(mp_obj_t ip_in) { + uint32_t value; + if (mp_obj_get_int_maybe(ip_in, (mp_int_t *)&value)) { + // We're done. + } else if (mp_obj_is_str(ip_in)) { + GET_STR_DATA_LEN(ip_in, str_data, str_len); + if (!ipaddress_parse_ipv4address((const char *)str_data, str_len, &value)) { + mp_raise_ValueError(translate("Not a valid IP string")); + } + } else { + mp_raise_ValueError(translate("Only int or string supported for ip")); + } + + return common_hal_ipaddress_new_ipv4address(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(ipaddress_ip_address_obj, ipaddress_ip_address); + +STATIC const mp_rom_map_elem_t ipaddress_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ipaddress) }, + { MP_ROM_QSTR(MP_QSTR_ip_address), MP_ROM_PTR(&ipaddress_ip_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_IPv4Address), MP_ROM_PTR(&ipaddress_ipv4address_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ipaddress_module_globals, ipaddress_module_globals_table); + + +const mp_obj_module_t ipaddress_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&ipaddress_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ipaddress, ipaddress_module, CIRCUITPY_IPADDRESS); diff --git a/circuitpython/shared-bindings/ipaddress/__init__.h b/circuitpython/shared-bindings/ipaddress/__init__.h new file mode 100644 index 0000000..7c2e5c1 --- /dev/null +++ b/circuitpython/shared-bindings/ipaddress/__init__.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_IPADDRESS___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_IPADDRESS___INIT___H + +#include "shared-module/ipaddress/__init__.h" + +bool ipaddress_parse_ipv4address(const char *ip_str, size_t len, uint32_t *ip_out); + +mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_IPADDRESS___INIT___H diff --git a/circuitpython/shared-bindings/is31fl3741/FrameBuffer.c b/circuitpython/shared-bindings/is31fl3741/FrameBuffer.c new file mode 100644 index 0000000..e1a87e8 --- /dev/null +++ b/circuitpython/shared-bindings/is31fl3741/FrameBuffer.c @@ -0,0 +1,301 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "shared-bindings/is31fl3741/IS31FL3741.h" +#include "shared-bindings/is31fl3741/FrameBuffer.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" +#include "shared-bindings/busio/I2C.h" + +//| class IS31FL3741_FrameBuffer: +//| """Creates an in-memory framebuffer for a IS31FL3741 device.""" +//| +//| def __init__(self, is31: is31fl3741.IS31FL3741, width: int, height: int, mapping: Tuple[int, ...], *, +//| framebuffer: Optional[WriteableBuffer] = None, scale: bool = False, gamma: bool = False) -> None: +//| """Create a IS31FL3741_FrameBuffer object with the given attributes. +//| +//| The framebuffer is in "RGB888" format using 4 bytes per pixel. +//| Bits 24-31 are ignored. The format is in RGB order. +//| +//| If a framebuffer is not passed in, one is allocated and initialized +//| to all black. In any case, the framebuffer can be retrieved +//| by passing the Is31fl3741 object to memoryview(). +//| +//| A Is31fl3741 is often used in conjunction with a +//| `framebufferio.FramebufferDisplay`. +//| +//| :param is31fl3741.IS31FL3741 is31: base IS31FL3741 instance to drive the framebuffer +//| :param int width: width of the display +//| :param int height: height of the display +//| :param Tuple[int, ...] mapping: mapping of matrix locations to LEDs +//| :param Optional[WriteableBuffer] framebuffer: Optional buffer to hold the display +//| :param bool scale: if True display is scaled down by 3 when displayed +//| :param bool gamma: if True apply gamma correction to all LEDs""" +//| ... +//| +STATIC mp_obj_t is31fl3741_FrameBuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_is31, ARG_width, ARG_height, ARG_mapping, ARG_framebuffer, ARG_scale, ARG_gamma }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_is31, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_mapping, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_framebuffer, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = mp_const_none } }, + { MP_QSTR_scale, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false } }, + { MP_QSTR_gamma, MP_ARG_BOOL | 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); + + is31fl3741_FrameBuffer_obj_t *self = &allocate_display_bus_or_raise()->is31fl3741; + self->base.type = &is31fl3741_FrameBuffer_type; + + if (args[ARG_width].u_int <= 0) { + mp_raise_ValueError(translate("width must be greater than zero")); + } + + self->scale = args[ARG_scale].u_bool; + if (self->scale) { + if (((args[ARG_height].u_int % 3) != 0) || ((args[ARG_width].u_int % 3) != 0)) { + mp_raise_ValueError(translate("Scale dimensions must divide by 3")); + } + + self->scale_width = args[ARG_width].u_int / 3; + self->scale_height = args[ARG_height].u_int / 3; + } else { + self->scale_width = args[ARG_width].u_int; + self->scale_height = args[ARG_height].u_int; + } + + self->auto_gamma = args[ARG_gamma].u_bool; + + mp_obj_t framebuffer = args[ARG_framebuffer].u_obj; + if (framebuffer == mp_const_none) { + int width = args[ARG_width].u_int; + int height = args[ARG_height].u_int; + int bufsize = 4 * width * height; + framebuffer = mp_obj_new_bytearray_of_zeros(bufsize); + } + + common_hal_is31fl3741_FrameBuffer_construct(self, + args[ARG_width].u_int, + args[ARG_height].u_int, + framebuffer, + args[ARG_is31].u_obj, + args[ARG_mapping].u_obj + ); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Free the resources associated with this +//| IS31FL3741 instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +STATIC mp_obj_t is31fl3741_FrameBuffer_deinit(mp_obj_t self_in) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + common_hal_is31fl3741_FrameBuffer_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_deinit_obj, is31fl3741_FrameBuffer_deinit); + +static void check_for_deinit(is31fl3741_FrameBuffer_obj_t *self) { + if (self->framebuffer == NULL) { + raise_deinited_error(); + } +} + +//| brightness: float +//| """In the current implementation, 0.0 turns the display off entirely +//| and any other value up to 1.0 turns the display on fully.""" +//| +STATIC mp_obj_t is31fl3741_FrameBuffer_get_brightness(mp_obj_t self_in) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + check_for_deinit(self); + uint8_t current = common_hal_is31fl3741_get_current(self->is31fl3741); + + float brightness = (float)current / (float)0xFF; + return mp_obj_new_float(brightness); +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_get_brightness_obj, is31fl3741_FrameBuffer_get_brightness); + +STATIC mp_obj_t is31fl3741_FrameBuffer_set_brightness(mp_obj_t self_in, mp_obj_t value_in) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + check_for_deinit(self); + mp_float_t brightness = mp_obj_get_float(value_in); + if (brightness < 0.0f || brightness > 1.0f) { + mp_raise_ValueError(translate("Brightness must be 0-1.0")); + } + + uint8_t current = (uint8_t)(brightness * 0xFF); + common_hal_is31fl3741_set_current(self->is31fl3741, current); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(is31fl3741_FrameBuffer_set_brightness_obj, is31fl3741_FrameBuffer_set_brightness); + +MP_PROPERTY_GETSET(is31fl3741_FrameBuffer_brightness_obj, + (mp_obj_t)&is31fl3741_FrameBuffer_get_brightness_obj, + (mp_obj_t)&is31fl3741_FrameBuffer_set_brightness_obj); + +//| def refresh(self) -> None: +//| """Transmits the color data in the buffer to the pixels so that +//| they are shown.""" +//| ... +//| +STATIC mp_obj_t is31fl3741_FrameBuffer_refresh(mp_obj_t self_in) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + check_for_deinit(self); + common_hal_is31fl3741_FrameBuffer_refresh(self, 0); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_refresh_obj, is31fl3741_FrameBuffer_refresh); + +//| width: int +//| """The width of the display, in pixels""" +//| +STATIC mp_obj_t is31fl3741_FrameBuffer_get_width(mp_obj_t self_in) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_FrameBuffer_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_get_width_obj, is31fl3741_FrameBuffer_get_width); +MP_PROPERTY_GETTER(is31fl3741_FrameBuffer_width_obj, + (mp_obj_t)&is31fl3741_FrameBuffer_get_width_obj); + +//| height: int +//| """The height of the display, in pixels""" +//| +STATIC mp_obj_t is31fl3741_FrameBuffer_get_height(mp_obj_t self_in) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_FrameBuffer_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_FrameBuffer_get_height_obj, is31fl3741_FrameBuffer_get_height); +MP_PROPERTY_GETTER(is31fl3741_FrameBuffer_height_obj, + (mp_obj_t)&is31fl3741_FrameBuffer_get_height_obj); + +STATIC const mp_rom_map_elem_t is31fl3741_FrameBuffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&is31fl3741_FrameBuffer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&is31fl3741_FrameBuffer_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&is31fl3741_FrameBuffer_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&is31fl3741_FrameBuffer_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&is31fl3741_FrameBuffer_height_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(is31fl3741_FrameBuffer_locals_dict, is31fl3741_FrameBuffer_locals_dict_table); + +STATIC void is31fl3741_FrameBuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + check_for_deinit(self); + + *bufinfo = self->bufinfo; +} + +STATIC void is31fl3741_FrameBuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) { + common_hal_is31fl3741_FrameBuffer_refresh(self_in, dirty_row_bitmap); +} + +STATIC void is31fl3741_FrameBuffer_deinit_proto(mp_obj_t self_in) { + common_hal_is31fl3741_FrameBuffer_deinit(self_in); +} + +STATIC float is31fl3741_FrameBuffer_get_brightness_proto(mp_obj_t self_in) { + return common_hal_is31fl3741_FrameBuffer_get_paused(self_in) ? 0.0f : 1.0f; +} + +STATIC bool is31fl3741_FrameBuffer_set_brightness_proto(mp_obj_t self_in, mp_float_t value) { + common_hal_is31fl3741_FrameBuffer_set_paused(self_in, value <= 0); + return true; +} + +STATIC int is31fl3741_FrameBuffer_get_width_proto(mp_obj_t self_in) { + return common_hal_is31fl3741_FrameBuffer_get_width(self_in); +} + +STATIC int is31fl3741_FrameBuffer_get_height_proto(mp_obj_t self_in) { + return common_hal_is31fl3741_FrameBuffer_get_height(self_in); +} + +STATIC int is31fl3741_FrameBuffer_get_color_depth_proto(mp_obj_t self_in) { + // The way displayio works depth is used to calculate bytes + // We use an uint32_t for color already so setting to 24 causes + // more changes required + return 32; +} + +STATIC int is31fl3741_FrameBuffer_get_bytes_per_cell_proto(mp_obj_t self_in) { + return 1; +} + +STATIC int is31fl3741_FrameBuffer_get_native_frames_per_second_proto(mp_obj_t self_in) { + return 60; // This was just chosen may vary based on LEDs used? +} + +STATIC const framebuffer_p_t is31fl3741_FrameBuffer_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) + .get_bufinfo = is31fl3741_FrameBuffer_get_bufinfo, + .set_brightness = is31fl3741_FrameBuffer_set_brightness_proto, + .get_brightness = is31fl3741_FrameBuffer_get_brightness_proto, + .get_width = is31fl3741_FrameBuffer_get_width_proto, + .get_height = is31fl3741_FrameBuffer_get_height_proto, + .get_color_depth = is31fl3741_FrameBuffer_get_color_depth_proto, + .get_bytes_per_cell = is31fl3741_FrameBuffer_get_bytes_per_cell_proto, + .get_native_frames_per_second = is31fl3741_FrameBuffer_get_native_frames_per_second_proto, + .swapbuffers = is31fl3741_FrameBuffer_swapbuffers, + .deinit = is31fl3741_FrameBuffer_deinit_proto, +}; + +STATIC mp_int_t is31fl3741_FrameBuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + is31fl3741_FrameBuffer_obj_t *self = (is31fl3741_FrameBuffer_obj_t *)self_in; + // a readonly framebuffer would be unusual but not impossible + if ((flags & MP_BUFFER_WRITE) && !(self->bufinfo.typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) { + return 1; + } + *bufinfo = self->bufinfo; + bufinfo->typecode = 'H'; + return 0; +} + +const mp_obj_type_t is31fl3741_FrameBuffer_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_is31fl3741, + .locals_dict = (mp_obj_dict_t *)&is31fl3741_FrameBuffer_locals_dict, + .make_new = is31fl3741_FrameBuffer_make_new, + MP_TYPE_EXTENDED_FIELDS( + .buffer_p = { .get_buffer = is31fl3741_FrameBuffer_get_buffer, }, + .protocol = &is31fl3741_FrameBuffer_proto, + ), +}; diff --git a/circuitpython/shared-bindings/is31fl3741/FrameBuffer.h b/circuitpython/shared-bindings/is31fl3741/FrameBuffer.h new file mode 100644 index 0000000..704913b --- /dev/null +++ b/circuitpython/shared-bindings/is31fl3741/FrameBuffer.h @@ -0,0 +1,50 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * 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. + */ + +#pragma once + +#include "shared-module/is31fl3741/FrameBuffer.h" +#include "shared-module/is31fl3741/IS31FL3741.h" + +extern const mp_obj_type_t is31fl3741_FrameBuffer_type; + +void common_hal_is31fl3741_FrameBuffer_construct(is31fl3741_FrameBuffer_obj_t *self, int width, int height, mp_obj_t framebuffer, is31fl3741_IS31FL3741_obj_t *is31, mp_obj_t mapping); + +void common_hal_is31fl3741_FrameBuffer_deinit(is31fl3741_FrameBuffer_obj_t *); + +int common_hal_is31fl3741_FrameBuffer_get_width(is31fl3741_FrameBuffer_obj_t *self); +int common_hal_is31fl3741_FrameBuffer_get_height(is31fl3741_FrameBuffer_obj_t *self); + +void common_hal_is31fl3741_FrameBuffer_set_global_current(is31fl3741_FrameBuffer_obj_t *self, uint8_t current); +uint8_t common_hal_is31fl3741_FrameBuffer_get_global_current(is31fl3741_FrameBuffer_obj_t *self); + +void common_hal_is31fl3741_FrameBuffer_set_paused(is31fl3741_FrameBuffer_obj_t *self, bool paused); +bool common_hal_is31fl3741_FrameBuffer_get_paused(is31fl3741_FrameBuffer_obj_t *self); +void common_hal_is31fl3741_FrameBuffer_refresh(is31fl3741_FrameBuffer_obj_t *self, uint8_t *dirtyrows); + +void common_hal_is31fl3741_FrameBuffer_reconstruct(is31fl3741_FrameBuffer_obj_t *self, mp_obj_t framebuffer); + +void is31fl3741_FrameBuffer_collect_ptrs(is31fl3741_FrameBuffer_obj_t *self); diff --git a/circuitpython/shared-bindings/is31fl3741/IS31FL3741.c b/circuitpython/shared-bindings/is31fl3741/IS31FL3741.c new file mode 100644 index 0000000..46a7f8a --- /dev/null +++ b/circuitpython/shared-bindings/is31fl3741/IS31FL3741.c @@ -0,0 +1,180 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "shared-bindings/is31fl3741/IS31FL3741.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "shared-bindings/busio/I2C.h" + +//| class IS31FL3741: +//| """Driver for an IS31FL3741 device.""" +//| +//| def __init__(self, i2c: busio.I2C, *, addr: int = 0x30) -> None: +//| """Create a IS31FL3741 object with the given attributes. +//| +//| Designed to work low level or passed to and object such as +//| :class:`~is31fl3741.IS31FL3741_FrameBuffer`. +//| +//| :param ~busio.I2C i2c: I2C bus the IS31FL3741 is on +//| :param int addr: device address""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_i2c, ARG_addr }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_addr, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 0x30 } }, + }; + 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); + + mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c].u_obj, &busio_i2c_type, MP_QSTR_i2c_bus); + + is31fl3741_IS31FL3741_obj_t *self = m_new_obj(is31fl3741_IS31FL3741_obj_t); + self->base.type = &is31fl3741_IS31FL3741_type; + + common_hal_is31fl3741_IS31FL3741_construct(self, + MP_OBJ_TO_PTR(i2c), + args[ARG_addr].u_int + ); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Free the resources associated with this +//| IS31FL3741 instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_deinit(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + common_hal_is31fl3741_IS31FL3741_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_deinit_obj, is31fl3741_IS31FL3741_deinit); + +//| def reset(self) -> None: +//| """Resets the IS31FL3741 chip.""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_reset(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_is31fl3741_send_reset(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_reset_obj, is31fl3741_IS31FL3741_reset); + +//| def enable(self) -> None: +//| """Enables the IS31FL3741 chip.""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_enable(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_is31fl3741_send_enable(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_enable_obj, is31fl3741_IS31FL3741_enable); + +//| def set_global_current(self, current: int) -> None: +//| """Sets the global current of the IS31FL3741 chip. +//| +//| :param int current: global current value 0x00 to 0xFF""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_set_global_current(mp_obj_t self_in, mp_obj_t value) { + is31fl3741_IS31FL3741_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t current = mp_obj_get_int(value); + common_hal_is31fl3741_set_current(self, current); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(is31fl3741_IS31FL3741_set_global_current_obj, is31fl3741_IS31FL3741_set_global_current); + +//| def set_led(self, led: int, value: int, page: int) -> None: +//| """Resets the IS31FL3741 chip. +//| +//| :param int led: which LED to set +//| :param int value: value to set the LED to 0x00 to 0xFF +//| :param int page: page to write to 0 or 2. If the LED is a >= 180 +//| the routine will automatically write to page 1 or 3 (instead +//| of 0 or 2)""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_set_led(size_t n_args, const mp_obj_t *args) { + is31fl3741_IS31FL3741_obj_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t led = mp_obj_get_int(args[1]); + mp_int_t value = mp_obj_get_int(args[2]); + mp_int_t page = mp_obj_get_int(args[3]); + common_hal_is31fl3741_set_led(self, led, value, page); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(is31fl3741_IS31FL3741_set_led_obj, 4, 4, is31fl3741_IS31FL3741_set_led); + +//| def write(mapping: Tuple[int, ...], buf: ReadableBuffer) -> None: +//| """Write buf out on the I2C bus to the IS31FL3741. +//| +//| :param ~Tuple[int, ...] mapping: map the pixels in the buffer to the order addressed by the driver chip +//| :param ~_typing.ReadableBuffer buf: The bytes to clock out. No assumption is made about color order""" +//| ... +STATIC mp_obj_t is31fl3741_IS31FL3741_write(mp_obj_t self_in, mp_obj_t mapping, mp_obj_t buffer) { + is31fl3741_IS31FL3741_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!mp_obj_is_tuple_compatible(mapping)) { + mp_raise_ValueError(translate("Mapping must be a tuple")); + } + + mp_obj_t *map_items; + size_t map_len; + mp_obj_tuple_get(mapping, &map_len, &map_items); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_READ); + + common_hal_is31fl3741_write(self, map_items, (uint8_t *)bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(is31fl3741_IS31FL3741_write_obj, is31fl3741_IS31FL3741_write); + +STATIC const mp_rom_map_elem_t is31fl3741_IS31FL3741_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&is31fl3741_IS31FL3741_deinit_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&is31fl3741_IS31FL3741_write_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_reset), (mp_obj_t)&is31fl3741_IS31FL3741_reset_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_enable), (mp_obj_t)&is31fl3741_IS31FL3741_enable_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_set_global_current), (mp_obj_t)&is31fl3741_IS31FL3741_set_global_current_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_set_led), (mp_obj_t)&is31fl3741_IS31FL3741_set_led_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(is31fl3741_IS31FL3741_locals_dict, is31fl3741_IS31FL3741_locals_dict_table); + +const mp_obj_type_t is31fl3741_IS31FL3741_type = { + { &mp_type_type }, + .name = MP_QSTR_is31fl3741, + .locals_dict = (mp_obj_dict_t *)&is31fl3741_IS31FL3741_locals_dict, + .make_new = is31fl3741_IS31FL3741_make_new, +}; diff --git a/circuitpython/shared-bindings/is31fl3741/IS31FL3741.h b/circuitpython/shared-bindings/is31fl3741/IS31FL3741.h new file mode 100644 index 0000000..2b81b01 --- /dev/null +++ b/circuitpython/shared-bindings/is31fl3741/IS31FL3741.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * 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. + */ + +#pragma once + +#include "shared-module/is31fl3741/IS31FL3741.h" + +extern const mp_obj_type_t is31fl3741_IS31FL3741_type; + +void common_hal_is31fl3741_IS31FL3741_construct(is31fl3741_IS31FL3741_obj_t *self, busio_i2c_obj_t *i2c, uint8_t addr); + +void common_hal_is31fl3741_IS31FL3741_deinit(is31fl3741_IS31FL3741_obj_t *); + +void common_hal_is31fl3741_write(is31fl3741_IS31FL3741_obj_t *is31, const mp_obj_t *mapping, const uint8_t *pixels, size_t numBytes); + +void common_hal_is31fl3741_begin_transaction(is31fl3741_IS31FL3741_obj_t *self); +void common_hal_is31fl3741_end_transaction(is31fl3741_IS31FL3741_obj_t *self); + +void common_hal_is31fl3741_send_unlock(is31fl3741_IS31FL3741_obj_t *self); +void common_hal_is31fl3741_set_page(is31fl3741_IS31FL3741_obj_t *self, uint8_t p); +void common_hal_is31fl3741_send_enable(is31fl3741_IS31FL3741_obj_t *self); +void common_hal_is31fl3741_send_reset(is31fl3741_IS31FL3741_obj_t *self); +void common_hal_is31fl3741_set_current(is31fl3741_IS31FL3741_obj_t *self, uint8_t current); +uint8_t common_hal_is31fl3741_get_current(is31fl3741_IS31FL3741_obj_t *self); +void common_hal_is31fl3741_set_led(is31fl3741_IS31FL3741_obj_t *self, uint16_t led, uint8_t level, uint8_t page); +void common_hal_is31fl3741_draw_pixel(is31fl3741_IS31FL3741_obj_t *self, int16_t x, int16_t y, uint32_t color, uint16_t *mapping); diff --git a/circuitpython/shared-bindings/is31fl3741/__init__.c b/circuitpython/shared-bindings/is31fl3741/__init__.c new file mode 100644 index 0000000..6058897 --- /dev/null +++ b/circuitpython/shared-bindings/is31fl3741/__init__.c @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * 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 <stdbool.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/is31fl3741/IS31FL3741.h" +#include "shared-bindings/is31fl3741/FrameBuffer.h" + +STATIC const mp_rom_map_elem_t is31fl3741_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_is31fl3741) }, + { MP_ROM_QSTR(MP_QSTR_IS31FL3741), MP_ROM_PTR(&is31fl3741_IS31FL3741_type) }, + { MP_ROM_QSTR(MP_QSTR_IS31FL3741_FrameBuffer), MP_ROM_PTR(&is31fl3741_FrameBuffer_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(is31fl3741_module_globals, is31fl3741_module_globals_table); + +const mp_obj_module_t is31fl3741_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&is31fl3741_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_is31fl3741, is31fl3741_module, CIRCUITPY_IS31FL3741); diff --git a/circuitpython/shared-bindings/keypad/Event.c b/circuitpython/shared-bindings/keypad/Event.c new file mode 100644 index 0000000..380b6b5 --- /dev/null +++ b/circuitpython/shared-bindings/keypad/Event.c @@ -0,0 +1,197 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/supervisor/__init__.h" + +//| class Event: +//| """A key transition event.""" +//| def __init__(self, key_number: int=0, pressed: bool=True, timestamp:Optional[int]=None) -> None: +//| """Create a key transition event, which reports a key-pressed or key-released transition. +//| +//| :param int key_number: The key number. +//| :param bool pressed: ``True`` if the key was pressed; ``False`` if it was released. +//| :param int timestamp: The time in milliseconds that the keypress occurred in the `supervisor.ticks_ms` time system. If specified as None, the current value of `supervisor.ticks_ms` is used. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_event_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + keypad_event_obj_t *self = m_new_obj(keypad_event_obj_t); + self->base.type = &keypad_event_type; + enum { ARG_key_number, ARG_pressed, ARG_timestamp }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_key_number, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_pressed, MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_timestamp, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + 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 mp_uint_t key_number = + (mp_uint_t)mp_arg_validate_int_min(args[ARG_key_number].u_int, 0, MP_QSTR_key_number); + + mp_obj_t timestamp = args[ARG_timestamp].u_obj; + if (timestamp == mp_const_none) { + timestamp = supervisor_ticks_ms(); + } + + (void)mp_obj_get_int_truncated(timestamp); // ensure that timesamp is an integer + common_hal_keypad_event_construct(self, key_number, args[ARG_pressed].u_bool, timestamp); + return MP_OBJ_FROM_PTR(self); +} + +//| key_number: int +//| """The key number.""" +//| +STATIC mp_obj_t keypad_event_get_key_number(mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_event_get_key_number(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_event_get_key_number_obj, keypad_event_get_key_number); + +MP_PROPERTY_GETTER(keypad_event_key_number_obj, + (mp_obj_t)&keypad_event_get_key_number_obj); + +//| pressed: bool +//| """``True`` if the event represents a key down (pressed) transition. +//| The opposite of `released`. +//| """ +//| +STATIC mp_obj_t keypad_event_get_pressed(mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_keypad_event_get_pressed(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_event_get_pressed_obj, keypad_event_get_pressed); + +MP_PROPERTY_GETTER(keypad_event_pressed_obj, + (mp_obj_t)&keypad_event_get_pressed_obj); + +//| released: bool +//| """``True`` if the event represents a key up (released) transition. +//| The opposite of `pressed`. +//| """ +//| +STATIC mp_obj_t keypad_event_get_released(mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_keypad_event_get_released(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_event_get_released_obj, keypad_event_get_released); + +MP_PROPERTY_GETTER(keypad_event_released_obj, + (mp_obj_t)&keypad_event_get_released_obj); + +//| timestamp: int +//| """The timestamp.""" +//| +STATIC mp_obj_t keypad_event_get_timestamp(mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_keypad_event_get_timestamp(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_event_get_timestamp_obj, keypad_event_get_timestamp); + +MP_PROPERTY_GETTER(keypad_event_timestamp_obj, + (mp_obj_t)&keypad_event_get_timestamp_obj); + + +//| def __eq__(self, other: object) -> bool: +//| """Two `Event` objects are equal if their `key_number` +//| and `pressed`/`released` values are equal. +//| Note that this does not compare the event timestamps. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_event_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + switch (op) { + case MP_BINARY_OP_EQUAL: + if (mp_obj_is_type(rhs_in, &keypad_event_type)) { + keypad_event_obj_t *lhs = MP_OBJ_TO_PTR(lhs_in); + keypad_event_obj_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool( + (common_hal_keypad_event_get_key_number(lhs) == + common_hal_keypad_event_get_key_number(rhs)) && + (common_hal_keypad_event_get_pressed(lhs) == + common_hal_keypad_event_get_pressed(rhs)) + ); + } else { + return mp_const_false; + } + + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __hash__(self) -> int: +//| """Returns a hash for the `Event`, so it can be used in dictionaries, etc.. +//| +//| Note that as events with different timestamps compare equal, they also hash to the same value.""" +//| ... +//| +STATIC mp_obj_t keypad_event_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_HASH: { + const mp_int_t key_number = common_hal_keypad_event_get_key_number(self); + const bool pressed = common_hal_keypad_event_get_pressed(self); + return MP_OBJ_NEW_SMALL_INT((pressed << 15) + key_number); + } + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC void keypad_event_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + keypad_event_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "<Event: key_number %d %s>", + common_hal_keypad_event_get_key_number(self), + common_hal_keypad_event_get_pressed(self) ? "pressed" : "released"); +} + +STATIC const mp_rom_map_elem_t keypad_event_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_key_number), MP_ROM_PTR(&keypad_event_key_number_obj) }, + { MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&keypad_event_pressed_obj) }, + { MP_ROM_QSTR(MP_QSTR_released), MP_ROM_PTR(&keypad_event_released_obj) }, + { MP_ROM_QSTR(MP_QSTR_timestamp), MP_ROM_PTR(&keypad_event_timestamp_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(keypad_event_locals_dict, keypad_event_locals_dict_table); + +const mp_obj_type_t keypad_event_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Event, + .make_new = keypad_event_make_new, + .print = keypad_event_print, + .locals_dict = (mp_obj_dict_t *)&keypad_event_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = keypad_event_unary_op, + .binary_op = keypad_event_binary_op, + ), +}; diff --git a/circuitpython/shared-bindings/keypad/Event.h b/circuitpython/shared-bindings/keypad/Event.h new file mode 100644 index 0000000..44e6929 --- /dev/null +++ b/circuitpython/shared-bindings/keypad/Event.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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_KEYPAD_EVENT__H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENT__H + +#include "py/obj.h" +#include "shared-module/keypad/Event.h" + +extern const mp_obj_type_t keypad_event_type; + +void common_hal_keypad_event_construct(keypad_event_obj_t *self, mp_uint_t key_number, bool pressed, mp_obj_t timestamp); +mp_int_t common_hal_keypad_event_get_key_number(keypad_event_obj_t *self); +bool common_hal_keypad_event_get_pressed(keypad_event_obj_t *self); +bool common_hal_keypad_event_get_released(keypad_event_obj_t *self); +mp_obj_t common_hal_keypad_event_get_timestamp(keypad_event_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENT__H diff --git a/circuitpython/shared-bindings/keypad/EventQueue.c b/circuitpython/shared-bindings/keypad/EventQueue.c new file mode 100644 index 0000000..8e45f7a --- /dev/null +++ b/circuitpython/shared-bindings/keypad/EventQueue.c @@ -0,0 +1,152 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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 "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/EventQueue.h" + +//| class EventQueue: +//| """A queue of `Event` objects, filled by a `keypad` scanner such as `Keys` or `KeyMatrix`. +//| +//| You cannot create an instance of `EventQueue` directly. Each scanner creates an +//| instance when it is created. +//| """ +//| ... + +//| def get(self) -> Optional[Event]: +//| """Return the next key transition event. Return ``None`` if no events are pending. +//| +//| Note that the queue size is limited; see ``max_events`` in the constructor of +//| a scanner such as `Keys` or `KeyMatrix`. +//| If a new event arrives when the queue is full, the event is discarded, and +//| `overflowed` is set to ``True``. +//| +//| :return: The next queued key transition `Event`. +//| :rtype: Optional[Event] +//| """ +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_get(mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_keypad_eventqueue_get(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_eventqueue_get_obj, keypad_eventqueue_get); + +//| def get_into(self, event: Event) -> bool: +//| """Store the next key transition event in the supplied event, if available, +//| and return ``True``. +//| If there are no queued events, do not touch ``event`` and return ``False``. +//| +//| The advantage of this method over ``get()`` is that it does not allocate storage. +//| Instead you can reuse an existing ``Event`` object. +//| +//| Note that the queue size is limited; see ``max_events`` in the constructor of +//| a scanner such as `Keys` or `KeyMatrix`. +//| +//| :return: ``True`` if an event was available and stored, ``False`` if not. +//| :rtype: bool +//| """ +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_get_into(mp_obj_t self_in, mp_obj_t event_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + + keypad_event_obj_t *event = MP_OBJ_TO_PTR(mp_arg_validate_type(event_in, &keypad_event_type, MP_QSTR_event)); + + return mp_obj_new_bool(common_hal_keypad_eventqueue_get_into(self, event)); +} +MP_DEFINE_CONST_FUN_OBJ_2(keypad_eventqueue_get_into_obj, keypad_eventqueue_get_into); + +//| def clear(self) -> None: +//| """Clear any queued key transition events. Also sets `overflowed` to ``False``. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_clear(mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_keypad_eventqueue_clear(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_eventqueue_clear_obj, keypad_eventqueue_clear); + +//| def __bool__(self) -> bool: +//| """``True`` if `len()` is greater than zero. +//| This is an easy way to check if the queue is empty. +//| """ +//| ... +//| +//| def __len__(self) -> int: +//| """Return the number of events currently in the queue. Used to implement ``len()``.""" +//| ... +//| +STATIC mp_obj_t keypad_eventqueue_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint16_t len = common_hal_keypad_eventqueue_get_length(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| overflowed: bool +//| """``True`` if an event could not be added to the event queue because it was full. (read-only) +//| Set to ``False`` by `clear()`. +//| """ +//| +STATIC mp_obj_t keypad_eventqueue_get_overflowed(mp_obj_t self_in) { + keypad_eventqueue_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_keypad_eventqueue_get_overflowed(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_eventqueue_get_overflowed_obj, keypad_eventqueue_get_overflowed); + +MP_PROPERTY_GETTER(keypad_eventqueue_overflowed_obj, + (mp_obj_t)&keypad_eventqueue_get_overflowed_obj); + +STATIC const mp_rom_map_elem_t keypad_eventqueue_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&keypad_eventqueue_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&keypad_eventqueue_get_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_into), MP_ROM_PTR(&keypad_eventqueue_get_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_overflowed), MP_ROM_PTR(&keypad_eventqueue_overflowed_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_eventqueue_locals_dict, keypad_eventqueue_locals_dict_table); + +const mp_obj_type_t keypad_eventqueue_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_EventQueue, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = keypad_eventqueue_unary_op, + ), + .locals_dict = (mp_obj_t)&keypad_eventqueue_locals_dict, +}; diff --git a/circuitpython/shared-bindings/keypad/EventQueue.h b/circuitpython/shared-bindings/keypad/EventQueue.h new file mode 100644 index 0000000..fbbd9df --- /dev/null +++ b/circuitpython/shared-bindings/keypad/EventQueue.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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_KEYPAD_EVENTQUEUE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENTQUEUE_H + +#include "shared-module/keypad/Event.h" +#include "shared-module/keypad/EventQueue.h" + +extern const mp_obj_type_t keypad_eventqueue_type; + +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events); + +void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self); +size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self); +mp_obj_t common_hal_keypad_eventqueue_get(keypad_eventqueue_obj_t *self); +bool common_hal_keypad_eventqueue_get_into(keypad_eventqueue_obj_t *self, keypad_event_obj_t *event); + +bool common_hal_keypad_eventqueue_get_overflowed(keypad_eventqueue_obj_t *self); +void common_hal_keypad_eventqueue_set_overflowed(keypad_eventqueue_obj_t *self, bool overflowed); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENTQUEUE_H diff --git a/circuitpython/shared-bindings/keypad/KeyMatrix.c b/circuitpython/shared-bindings/keypad/KeyMatrix.c new file mode 100644 index 0000000..9e65471 --- /dev/null +++ b/circuitpython/shared-bindings/keypad/KeyMatrix.c @@ -0,0 +1,236 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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 "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/__init__.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +//| class KeyMatrix: +//| """Manage a 2D matrix of keys with row and column pins.""" +//| +//| def __init__(self, row_pins: Sequence[microcontroller.Pin], column_pins: Sequence[microcontroller.Pin], columns_to_anodes: bool = True, interval: float = 0.020, max_events: int = 64) -> None: +//| """ +//| Create a `Keys` object that will scan the key matrix attached to the given row and column pins. +//| There should not be any external pull-ups or pull-downs on the matrix: +//| ``KeyMatrix`` enables internal pull-ups or pull-downs on the pins as necessary. +//| +//| The keys are numbered sequentially from zero. A key number can be computed +//| by ``row * len(column_pins) + column``. +//| +//| An `EventQueue` is created when this object is created and is available in the `events` attribute. +//| +//| :param Sequence[microcontroller.Pin] row_pins: The pins attached to the rows. +//| :param Sequence[microcontroller.Pin] column_pins: The pins attached to the colums. +//| :param bool columns_to_anodes: Default ``True``. +//| If the matrix uses diodes, the diode anodes are typically connected to the column pins, +//| and the cathodes should be connected to the row pins. If your diodes are reversed, +//| set ``columns_to_anodes`` to ``False``. +//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing. +//| ``interval`` is in float seconds. The default is 0.020 (20 msecs). +//| :param int max_events: maximum size of `events` `EventQueue`: +//| maximum number of key transition events that are saved. +//| Must be >= 1. +//| If a new event arrives when the queue is full, the oldest event is discarded. +//| """ +//| ... + +STATIC mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + keypad_keymatrix_obj_t *self = m_new_obj(keypad_keymatrix_obj_t); + self->base.type = &keypad_keymatrix_type; + enum { ARG_row_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_interval, ARG_max_events }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_row_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_column_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_columns_to_anodes, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + 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); + + mp_obj_t row_pins = args[ARG_row_pins].u_obj; + // mp_obj_len() will be >= 0. + const size_t num_row_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(row_pins)); + + mp_obj_t column_pins = args[ARG_column_pins].u_obj; + const size_t num_column_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(column_pins)); + + const mp_float_t interval = + mp_arg_validate_obj_float_non_negative(args[ARG_interval].u_obj, 0.020f, MP_QSTR_interval); + const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); + + const mcu_pin_obj_t *row_pins_array[num_row_pins]; + const mcu_pin_obj_t *column_pins_array[num_column_pins]; + + validate_no_duplicate_pins_2(row_pins, column_pins, MP_QSTR_row_pins, MP_QSTR_column_pins); + + for (size_t row = 0; row < num_row_pins; row++) { + const mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(row_pins, MP_OBJ_NEW_SMALL_INT(row), MP_OBJ_SENTINEL)); + row_pins_array[row] = pin; + } + + for (size_t column = 0; column < num_column_pins; column++) { + const mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(column_pins, MP_OBJ_NEW_SMALL_INT(column), MP_OBJ_SENTINEL)); + column_pins_array[column] = pin; + } + + common_hal_keypad_keymatrix_construct(self, num_row_pins, row_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, interval, max_events); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Stop scanning and release the pins.""" +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_deinit(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_keypad_keymatrix_deinit(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_deinit_obj, keypad_keymatrix_deinit); + +//| def __enter__(self) -> KeyMatrix: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t keypad_keymatrix___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_keypad_keymatrix_deinit(args[0]); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(keypad_keymatrix___exit___obj, 4, 4, keypad_keymatrix___exit__); + +STATIC void check_for_deinit(keypad_keymatrix_obj_t *self) { + if (common_hal_keypad_deinited(self)) { + raise_deinited_error(); + } +} + +//| def reset(self) -> None: +//| """Reset the internal state of the scanner to assume that all keys are now released. +//| Any key that is already pressed at the time of this call will therefore immediately cause +//| a new key-pressed event to occur. +//| """ +//| ... +//| + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| + +//| def key_number_to_row_column(self, key_number: int) -> Tuple[int]: +//| """Return the row and column for the given key number. +//| The row is ``key_number // len(column_pins)``. +//| The column is ``key_number % len(column_pins)``. +//| +//| :return: ``(row, column)`` +//| :rtype: Tuple[int] +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_key_number_to_row_column(mp_obj_t self_in, mp_obj_t key_number_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + const mp_uint_t key_number = (mp_uint_t)mp_arg_validate_int_range( + mp_obj_get_int(key_number_in), + 0, (mp_int_t)common_hal_keypad_generic_get_key_count(self), + MP_QSTR_key_number); + + mp_uint_t row; + mp_uint_t column; + common_hal_keypad_keymatrix_key_number_to_row_column(self, key_number, &row, &column); + + mp_obj_t row_column[2]; + row_column[0] = MP_OBJ_NEW_SMALL_INT(row); + row_column[1] = MP_OBJ_NEW_SMALL_INT(column); + + return mp_obj_new_tuple(2, row_column); +} +MP_DEFINE_CONST_FUN_OBJ_2(keypad_keymatrix_key_number_to_row_column_obj, keypad_keymatrix_key_number_to_row_column); + +//| def row_column_to_key_number(self, row: int, column: int) -> int: +//| """Return the key number for a given row and column. +//| The key number is ``row * len(column_pins) + column``. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_row_column_to_key_number(mp_obj_t self_in, mp_obj_t row_in, mp_obj_t column_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + const mp_uint_t row = (mp_uint_t)mp_arg_validate_int_range( + mp_obj_get_int(row_in), 0, (mp_int_t)common_hal_keypad_keymatrix_get_row_count(self), MP_QSTR_row); + + const mp_int_t column = (mp_uint_t)mp_arg_validate_int_range( + mp_obj_get_int(column_in), 0, (mp_int_t)common_hal_keypad_keymatrix_get_column_count(self), MP_QSTR_column); + + return MP_OBJ_NEW_SMALL_INT( + (mp_int_t)common_hal_keypad_keymatrix_row_column_to_key_number(self, row, column)); +} +MP_DEFINE_CONST_FUN_OBJ_3(keypad_keymatrix_row_column_to_key_number_obj, keypad_keymatrix_row_column_to_key_number); + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| + +STATIC const mp_rom_map_elem_t keypad_keymatrix_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&keypad_keymatrix_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&keypad_keymatrix___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_events), MP_ROM_PTR(&keypad_generic_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_generic_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_generic_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_number_to_row_column), MP_ROM_PTR(&keypad_keymatrix_key_number_to_row_column_obj) }, + { MP_ROM_QSTR(MP_QSTR_row_column_to_key_number), MP_ROM_PTR(&keypad_keymatrix_row_column_to_key_number_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_keymatrix_locals_dict, keypad_keymatrix_locals_dict_table); + +const mp_obj_type_t keypad_keymatrix_type = { + { &mp_type_type }, + .name = MP_QSTR_KeyMatrix, + .make_new = keypad_keymatrix_make_new, + .locals_dict = (mp_obj_t)&keypad_keymatrix_locals_dict, +}; diff --git a/circuitpython/shared-bindings/keypad/KeyMatrix.h b/circuitpython/shared-bindings/keypad/KeyMatrix.h new file mode 100644 index 0000000..bdf77dd --- /dev/null +++ b/circuitpython/shared-bindings/keypad/KeyMatrix.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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_KEYPAD_KEYMATRIX_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYMATRIX_H + +#include "py/objlist.h" +#include "shared-module/keypad/KeyMatrix.h" + +extern const mp_obj_type_t keypad_keymatrix_type; + +void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint_t num_row_pins, const mcu_pin_obj_t *row_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, mp_float_t interval, size_t max_events); + +void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self); + +void common_hal_keypad_keymatrix_key_number_to_row_column(keypad_keymatrix_obj_t *self, mp_uint_t key_number, mp_uint_t *row, mp_uint_t *column); +mp_uint_t common_hal_keypad_keymatrix_row_column_to_key_number(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t column); + +size_t common_hal_keypad_keymatrix_get_column_count(keypad_keymatrix_obj_t *self); +size_t common_hal_keypad_keymatrix_get_row_count(keypad_keymatrix_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYMATRIX_H diff --git a/circuitpython/shared-bindings/keypad/Keys.c b/circuitpython/shared-bindings/keypad/Keys.c new file mode 100644 index 0000000..6ac705e --- /dev/null +++ b/circuitpython/shared-bindings/keypad/Keys.c @@ -0,0 +1,168 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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 "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/__init__.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/Keys.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +//| class Keys: +//| """Manage a set of independent keys.""" +//| +//| def __init__(self, pins: Sequence[microcontroller.Pin], *, value_when_pressed: bool, pull: bool = True, interval: float = 0.020, max_events: int = 64) -> None: +//| """ +//| Create a `Keys` object that will scan keys attached to the given sequence of pins. +//| Each key is independent and attached to its own pin. +//| +//| An `EventQueue` is created when this object is created and is available in the `events` attribute. +//| +//| :param Sequence[microcontroller.Pin] pins: The pins attached to the keys. +//| The key numbers correspond to indices into this sequence. +//| :param bool value_when_pressed: ``True`` if the pin reads high when the key is pressed. +//| ``False`` if the pin reads low (is grounded) when the key is pressed. +//| All the pins must be connected in the same way. +//| :param bool pull: ``True`` if an internal pull-up or pull-down should be +//| enabled on each pin. A pull-up will be used if ``value_when_pressed`` is ``False``; +//| a pull-down will be used if it is ``True``. +//| If an external pull is already provided for all the pins, you can set ``pull`` to ``False``. +//| However, enabling an internal pull when an external one is already present is not a problem; +//| it simply uses slightly more current. +//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing. +//| ``interval`` is in float seconds. The default is 0.020 (20 msecs). +//| :param int max_events: maximum size of `events` `EventQueue`: +//| maximum number of key transition events that are saved. +//| Must be >= 1. +//| If a new event arrives when the queue is full, the oldest event is discarded. +//| """ +//| ... + +STATIC mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + keypad_keys_obj_t *self = m_new_obj(keypad_keys_obj_t); + self->base.type = &keypad_keys_type; + enum { ARG_pins, ARG_value_when_pressed, ARG_pull, ARG_interval, ARG_max_events }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_value_when_pressed, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_BOOL }, + { MP_QSTR_pull, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } }, + { MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + 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); + + mp_obj_t pins = args[ARG_pins].u_obj; + validate_no_duplicate_pins(pins, MP_QSTR_row_pins); + // mp_obj_len() will be >= 0. + const size_t num_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(pins)); + + const bool value_when_pressed = args[ARG_value_when_pressed].u_bool; + const mp_float_t interval = + mp_arg_validate_obj_float_non_negative(args[ARG_interval].u_obj, 0.020f, MP_QSTR_interval); + const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); + + const mcu_pin_obj_t *pins_array[num_pins]; + + for (mp_uint_t i = 0; i < num_pins; i++) { + pins_array[i] = + validate_obj_is_free_pin(mp_obj_subscr(pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + } + + common_hal_keypad_keys_construct(self, num_pins, pins_array, value_when_pressed, args[ARG_pull].u_bool, interval, max_events); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Stop scanning and release the pins.""" +//| ... +//| +STATIC mp_obj_t keypad_keys_deinit(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_keypad_keys_deinit(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_deinit_obj, keypad_keys_deinit); + +//| def __enter__(self) -> Keys: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t keypad_keys___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_keypad_keys_deinit(args[0]); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(keypad_keys___exit___obj, 4, 4, keypad_keys___exit__); + + +//| def reset(self) -> None: +//| """Reset the internal state of the scanner to assume that all keys are now released. +//| Any key that is already pressed at the time of this call will therefore immediately cause +//| a new key-pressed event to occur. +//| """ +//| ... +//| + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| +STATIC const mp_rom_map_elem_t keypad_keys_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&keypad_keys_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&keypad_keys___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_events), MP_ROM_PTR(&keypad_generic_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_generic_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_generic_reset_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_keys_locals_dict, keypad_keys_locals_dict_table); + +const mp_obj_type_t keypad_keys_type = { + { &mp_type_type }, + .name = MP_QSTR_Keys, + .make_new = keypad_keys_make_new, + .locals_dict = (mp_obj_t)&keypad_keys_locals_dict, +}; diff --git a/circuitpython/shared-bindings/keypad/Keys.h b/circuitpython/shared-bindings/keypad/Keys.h new file mode 100644 index 0000000..eb833b9 --- /dev/null +++ b/circuitpython/shared-bindings/keypad/Keys.h @@ -0,0 +1,39 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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_KEYPAD_KEYS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYS_H + +#include "py/objlist.h" +#include "shared-module/keypad/Keys.h" + +extern const mp_obj_type_t keypad_keys_type; + +void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pins, const mcu_pin_obj_t *pins[], bool value_when_pressed, bool pull, mp_float_t interval, size_t max_events); + +void common_hal_keypad_keys_deinit(keypad_keys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYS_H diff --git a/circuitpython/shared-bindings/keypad/ShiftRegisterKeys.c b/circuitpython/shared-bindings/keypad/ShiftRegisterKeys.c new file mode 100644 index 0000000..14ab874 --- /dev/null +++ b/circuitpython/shared-bindings/keypad/ShiftRegisterKeys.c @@ -0,0 +1,172 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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 "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/keypad/__init__.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +//| class ShiftRegisterKeys: +//| """Manage a set of keys attached to an incoming shift register.""" +//| +//| def __init__(self, *, clock: microcontroller.Pin, data: microcontroller.Pin, latch: microcontroller.Pin, value_to_latch: bool = True, key_count: int, value_when_pressed: bool, interval: float = 0.020, max_events: int = 64) -> None: +//| """ +//| Create a `Keys` object that will scan keys attached to a parallel-in serial-out shift register +//| like the 74HC165 or CD4021. +//| Note that you may chain shift registers to load in as many values as you need. +//| +//| Key number 0 is the first (or more properly, the zero-th) bit read. In the +//| 74HC165, this bit is labeled ``Q7``. Key number 1 will be the value of ``Q6``, etc. +//| +//| An `EventQueue` is created when this object is created and is available in the `events` attribute. +//| +//| :param microcontroller.Pin clock: The shift register clock pin. +//| The shift register should clock on a low-to-high transition. +//| :param microcontroller.Pin data: the incoming shift register data pin +//| :param microcontroller.Pin latch: +//| Pin used to latch parallel data going into the shift register. +//| :param bool value_to_latch: Pin state to latch data being read. +//| ``True`` if the data is latched when ``latch`` goes high +//| ``False`` if the data is latched when ``latch`` goes low. +//| The default is ``True``, which is how the 74HC165 operates. The CD4021 latch is the opposite. +//| Once the data is latched, it will be shifted out by toggling the clock pin. +//| :param int key_count: number of data lines to clock in +//| :param bool value_when_pressed: ``True`` if the pin reads high when the key is pressed. +//| ``False`` if the pin reads low (is grounded) when the key is pressed. +//| :param float interval: Scan keys no more often than ``interval`` to allow for debouncing. +//| ``interval`` is in float seconds. The default is 0.020 (20 msecs). +//| :param int max_events: maximum size of `events` `EventQueue`: +//| maximum number of key transition events that are saved. +//| Must be >= 1. +//| If a new event arrives when the queue is full, the oldest event is discarded. +//| """ +//| ... + +STATIC mp_obj_t keypad_shiftregisterkeys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + keypad_shiftregisterkeys_obj_t *self = m_new_obj(keypad_shiftregisterkeys_obj_t); + self->base.type = &keypad_shiftregisterkeys_type; + enum { ARG_clock, ARG_data, ARG_latch, ARG_value_to_latch, ARG_key_count, ARG_value_when_pressed, ARG_interval, ARG_max_events }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_latch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_value_to_latch, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_key_count, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_value_when_pressed, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_BOOL }, + { MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_max_events, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + }; + 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 *data = validate_obj_is_free_pin(args[ARG_data].u_obj); + const mcu_pin_obj_t *latch = validate_obj_is_free_pin(args[ARG_latch].u_obj); + const bool value_to_latch = args[ARG_value_to_latch].u_bool; + + const size_t key_count = (size_t)mp_arg_validate_int_min(args[ARG_key_count].u_int, 1, MP_QSTR_key_count); + const bool value_when_pressed = args[ARG_value_when_pressed].u_bool; + const mp_float_t interval = + mp_arg_validate_obj_float_non_negative(args[ARG_interval].u_obj, 0.020f, MP_QSTR_interval); + const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); + + common_hal_keypad_shiftregisterkeys_construct( + self, clock, data, latch, value_to_latch, key_count, value_when_pressed, interval, max_events); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Stop scanning and release the pins.""" +//| ... +//| +STATIC mp_obj_t keypad_shiftregisterkeys_deinit(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_keypad_shiftregisterkeys_deinit(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_deinit_obj, keypad_shiftregisterkeys_deinit); + +//| def __enter__(self) -> Keys: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t keypad_shiftregisterkeys___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_keypad_shiftregisterkeys_deinit(args[0]); + return MP_ROM_NONE; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(keypad_shiftregisterkeys___exit___obj, 4, 4, keypad_shiftregisterkeys___exit__); + +//| def reset(self) -> None: +//| """Reset the internal state of the scanner to assume that all keys are now released. +//| Any key that is already pressed at the time of this call will therefore immediately cause +//| a new key-pressed event to occur. +//| """ +//| ... +//| + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| + +STATIC const mp_rom_map_elem_t keypad_shiftregisterkeys_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&keypad_shiftregisterkeys_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&keypad_shiftregisterkeys___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_events), MP_ROM_PTR(&keypad_generic_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_generic_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_generic_reset_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_shiftregisterkeys_locals_dict, keypad_shiftregisterkeys_locals_dict_table); + +const mp_obj_type_t keypad_shiftregisterkeys_type = { + { &mp_type_type }, + .name = MP_QSTR_ShiftRegisterKeys, + .make_new = keypad_shiftregisterkeys_make_new, + .locals_dict = (mp_obj_t)&keypad_shiftregisterkeys_locals_dict, +}; diff --git a/circuitpython/shared-bindings/keypad/ShiftRegisterKeys.h b/circuitpython/shared-bindings/keypad/ShiftRegisterKeys.h new file mode 100644 index 0000000..bc91c78 --- /dev/null +++ b/circuitpython/shared-bindings/keypad/ShiftRegisterKeys.h @@ -0,0 +1,39 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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_KEYPAD_SHIFTREGISTERKEYS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_SHIFTREGISTERKEYS_H + +#include "py/objlist.h" +#include "shared-module/keypad/ShiftRegisterKeys.h" + +extern const mp_obj_type_t keypad_shiftregisterkeys_type; + +void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_t *self, const mcu_pin_obj_t *clock_pin, const mcu_pin_obj_t *data_pin, const mcu_pin_obj_t *latch_pin, bool value_to_latch, size_t key_count, bool value_when_pressed, mp_float_t interval, size_t max_events); + +void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_SHIFTREGISTERKEYS_H diff --git a/circuitpython/shared-bindings/keypad/__init__.c b/circuitpython/shared-bindings/keypad/__init__.c new file mode 100644 index 0000000..97db750 --- /dev/null +++ b/circuitpython/shared-bindings/keypad/__init__.c @@ -0,0 +1,108 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2011 Dan Halbert 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 "py/obj.h" + +#include "shared-bindings/keypad/__init__.h" +#include "shared-bindings/keypad/Event.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/keypad/Keys.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" +#include "shared-bindings/util.h" + +STATIC void check_for_deinit(keypad_keymatrix_obj_t *self) { + if (common_hal_keypad_deinited(self)) { + raise_deinited_error(); + } +} + +STATIC mp_obj_t keypad_generic_reset(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_generic_reset(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_generic_reset_obj, keypad_generic_reset); + +STATIC mp_obj_t keypad_generic_get_key_count(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_generic_get_key_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_generic_get_key_count_obj, keypad_generic_get_key_count); + +const mp_obj_property_t keypad_generic_key_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_generic_get_key_count_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +STATIC mp_obj_t keypad_generic_get_events(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return common_hal_keypad_generic_get_events(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_generic_get_events_obj, keypad_generic_get_events); + +const mp_obj_property_t keypad_generic_events_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_generic_get_events_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + + +//| """Support for scanning keys and key matrices +//| +//| The `keypad` module provides native support to scan sets of keys or buttons, +//| connected independently to individual pins, +//| connected to a shift register, +//| or connected in a row-and-column matrix. +//| """ +//| + +STATIC mp_rom_map_elem_t keypad_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_keypad) }, + { MP_ROM_QSTR(MP_QSTR_Event), MP_OBJ_FROM_PTR(&keypad_event_type) }, + { MP_ROM_QSTR(MP_QSTR_EventQueue), MP_OBJ_FROM_PTR(&keypad_eventqueue_type) }, + { MP_ROM_QSTR(MP_QSTR_KeyMatrix), MP_OBJ_FROM_PTR(&keypad_keymatrix_type) }, + { MP_ROM_QSTR(MP_QSTR_Keys), MP_OBJ_FROM_PTR(&keypad_keys_type) }, + { MP_ROM_QSTR(MP_QSTR_ShiftRegisterKeys), MP_OBJ_FROM_PTR(&keypad_shiftregisterkeys_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(keypad_module_globals, keypad_module_globals_table); + +const mp_obj_module_t keypad_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&keypad_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_keypad, keypad_module, CIRCUITPY_KEYPAD); diff --git a/circuitpython/shared-bindings/keypad/__init__.h b/circuitpython/shared-bindings/keypad/__init__.h new file mode 100644 index 0000000..eb3b16a --- /dev/null +++ b/circuitpython/shared-bindings/keypad/__init__.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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 SHARED_BINDINGS_KEYPAD_H +#define SHARED_BINDINGS_KEYPAD_H + +#include "py/obj.h" +#include "py/objproperty.h" +#include "shared-module/keypad/__init__.h" + +bool common_hal_keypad_deinited(void *self); +void common_hal_keypad_generic_reset(void *self); +size_t common_hal_keypad_generic_get_key_count(void *self); +mp_obj_t common_hal_keypad_generic_get_events(void *self); + +MP_DECLARE_CONST_FUN_OBJ_1(keypad_generic_reset_obj); + +extern const mp_obj_property_t keypad_generic_events_obj; +extern const mp_obj_property_t keypad_generic_key_count_obj; + +#endif // SHARED_BINDINGS_KEYPAD_H diff --git a/circuitpython/shared-bindings/math/__init__.c b/circuitpython/shared-bindings/math/__init__.c new file mode 100644 index 0000000..54dbf00 --- /dev/null +++ b/circuitpython/shared-bindings/math/__init__.c @@ -0,0 +1,479 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2017 Michael McWethy + * + * 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 "py/builtin.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +#if MICROPY_PY_BUILTINS_FLOAT + +#include <math.h> + +// M_PI is not part of the math.h standard and may not be defined +// And by defining our own we can ensure it uses the correct const format. +#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846) + + +//| """mathematical functions +//| +//| The `math` module provides some basic mathematical functions for +//| working with floating-point numbers. +//| +//| |see_cpython_module| :mod:`cpython:math`. +//| """ +//| + +STATIC NORETURN void math_error(void) { + mp_raise_ValueError(translate("math domain error")); +} + +#define MATH_FUN_1(py_name, c_name) \ + STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj))); } \ + STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_2(py_name, c_name) \ + STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj, mp_obj_t y_obj) { return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj), mp_obj_get_float(y_obj))); } \ + STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_1_TO_BOOL(py_name, c_name) \ + STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_bool(c_name(mp_obj_get_float(x_obj))); } \ + STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_1_TO_INT(py_name, c_name) \ + STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_int_from_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj))); } \ + STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name); + +#define MATH_FUN_1_ERRCOND(py_name, c_name, error_condition) \ + STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { \ + mp_float_t x = mp_obj_get_float(x_obj); \ + if (error_condition) { \ + math_error(); \ + } \ + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(x)); \ + } \ + STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name); + +#ifdef MP_NEED_LOG2 +// 1.442695040888963407354163704 is 1/_M_LN2 +#define log2(x) (log(x) * 1.442695040888963407354163704) +#endif + +//| e: float +//| """base of the natural logarithm""" +//| +//| pi: float +//| """the ratio of a circle's circumference to its diameter""" +//| + +//| def acos(x: float) -> float: +//| """Return the inverse cosine of ``x``.""" +//| ... +//| +//| def asin(x: float) -> float: +//| """Return the inverse sine of ``x``.""" +//| ... +//| +//| def atan(x: float) -> float: +//| """Return the inverse tangent of ``x``.""" +//| ... +//| +//| def atan2(y: float, x: float) -> float: +//| """Return the principal value of the inverse tangent of ``y/x``.""" +//| ... +//| +//| def ceil(x: float) -> int: +//| """Return an integer, being ``x`` rounded towards positive infinity.""" +//| ... +//| +//| def copysign(x: float, y: float) -> float: +//| """Return ``x`` with the sign of ``y``.""" +//| ... +//| +//| def cos(x: float) -> float: +//| """Return the cosine of ``x``.""" +//| ... +//| +//| def degrees(x: float) -> float: +//| """Return radians ``x`` converted to degrees.""" +//| ... +//| +//| def exp(x: float) -> float: +//| """Return the exponential of ``x``.""" +//| ... +//| +//| def fabs(x: float) -> float: +//| """Return the absolute value of ``x``.""" +//| ... +//| +//| def floor(x: float) -> int: +//| """Return an integer, being ``x`` rounded towards negative infinity.""" +//| ... +//| +//| def fmod(x: float, y: float) -> int: +//| """Return the remainder of ``x/y``.""" +//| ... +//| +//| def frexp(x: float) -> Tuple[int, int]: +//| """Decomposes a floating-point number into its mantissa and exponent. +//| The returned value is the tuple ``(m, e)`` such that ``x == m * 2**e`` +//| exactly. If ``x == 0`` then the function returns ``(0.0, 0)``, otherwise +//| the relation ``0.5 <= abs(m) < 1`` holds.""" +//| ... +//| +//| def isfinite(x: float) -> bool: +//| """Return ``True`` if ``x`` is finite.""" +//| ... +//| +//| def isinf(x: float) -> bool: +//| """Return ``True`` if ``x`` is infinite.""" +//| ... +//| +//| def isnan(x: float) -> bool: +//| """Return ``True`` if ``x`` is not-a-number""" +//| ... +//| +//| def ldexp(x: float, exp: float) -> float: +//| """Return ``x * (2**exp)``.""" +//| ... +//| +//| def log(x: float, base: float = e) -> float: +//| """Return the logarithm of x to the given base. If base is not specified, +//| returns the natural logarithm (base e) of x""" +//| ... +//| +//| def modf(x: float) -> Tuple[float, float]: +//| """Return a tuple of two floats, being the fractional and integral parts of +//| ``x``. Both return values have the same sign as ``x``.""" +//| ... +//| +//| def pow(x: float, y: float) -> float: +//| """Returns ``x`` to the power of ``y``.""" +//| +//| def radians(x: float) -> float: +//| """Return degrees ``x`` converted to radians.""" +//| +//| def sin(x: float) -> float: +//| """Return the sine of ``x``.""" +//| ... +//| +//| def sqrt(x: float) -> float: +//| """Returns the square root of ``x``.""" +//| ... +//| +//| def tan(x: float) -> float: +//| """Return the tangent of ``x``.""" +//| ... +//| +//| def trunc(x: float) -> int: +//| """Return an integer, being ``x`` rounded towards 0.""" +//| ... +//| +MATH_FUN_1_ERRCOND(sqrt, sqrt, (x < (mp_float_t)0.0)) + +MATH_FUN_2(pow, pow) + +MATH_FUN_1(exp, exp) +#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS +//| def expm1(x: float) -> float: +//| """Return ``exp(x) - 1``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(expm1, expm1) + +//| def log2(x: float) -> float: +//| """Return the base-2 logarithm of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1_ERRCOND(log2, log2, (x <= (mp_float_t)0.0)) + +//| def log10(x: float) -> float: +//| """Return the base-10 logarithm of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1_ERRCOND(log10, log10, (x <= (mp_float_t)0.0)) + +//| def cosh(x: float) -> float: +//| """Return the hyperbolic cosine of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(cosh, cosh) + +//| def sinh(x: float) -> float: +//| """Return the hyperbolic sine of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(sinh, sinh) + +//| def tanh(x: float) -> float: +//| """Return the hyperbolic tangent of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(tanh, tanh) + +//| def acosh(x: float) -> float: +//| """Return the inverse hyperbolic cosine of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(acosh, acosh) + +//| def asinh(x: float) -> float: +//| """Return the inverse hyperbolic sine of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(asinh, asinh) + +//| def atanh(x: float) -> float: +//| """Return the inverse hyperbolic tangent of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(atanh, atanh) +#endif + +MATH_FUN_1(cos, cos) + +MATH_FUN_1(sin, sin) + +MATH_FUN_1(tan, tan) + +MATH_FUN_1(acos, acos) + +MATH_FUN_1(asin, asin) + +MATH_FUN_1(atan, atan) + +MATH_FUN_2(atan2, atan2) + +MATH_FUN_1_TO_INT(ceil, ceil) + +MATH_FUN_2(copysign, copysign) + +MATH_FUN_1(fabs, fabs) + +MATH_FUN_1_TO_INT(floor, floor) // TODO: delegate to x.__floor__() if x is not a float + +MATH_FUN_2(fmod, fmod) + +MATH_FUN_1_TO_BOOL(isfinite, isfinite) + +MATH_FUN_1_TO_BOOL(isinf, isinf) + +MATH_FUN_1_TO_BOOL(isnan, isnan) + +MATH_FUN_1_TO_INT(trunc, trunc) + +MATH_FUN_2(ldexp, ldexp) +#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + +//| def erf(x: float) -> float: +//| """Return the error function of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(erf, erf) + +//| def erfc(x: float) -> float: +//| """Return the complementary error function of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(erfc, erfc) + +//| def gamma(x: float) -> float: +//| """Return the gamma function of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(gamma, tgamma) + +//| def lgamma(x: float) -> float: +//| """Return the natural logarithm of the gamma function of ``x``. +//| +//| May not be available on some boards. +//| """ +//| ... +//| +MATH_FUN_1(lgamma, lgamma) +#endif +// TODO: factorial, fsum + +// Function that takes a variable number of arguments + +// log(x[, base]) +STATIC mp_obj_t mp_math_log(size_t n_args, const mp_obj_t *args) { + mp_float_t x = mp_obj_get_float(args[0]); + if (x <= (mp_float_t)0.0) { + math_error(); + } + mp_float_t l = MICROPY_FLOAT_C_FUN(log)(x); + if (n_args == 1) { + return mp_obj_new_float(l); + } else { + mp_float_t base = mp_obj_get_float(args[1]); + if (base <= (mp_float_t)0.0) { + math_error(); +// Turn off warning when comparing exactly with integral value 1.0 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" + } else if (base == (mp_float_t)1.0) { + #pragma GCC diagnostic pop + mp_raise_msg(&mp_type_ZeroDivisionError, translate("division by zero")); + } + return mp_obj_new_float(l / MICROPY_FLOAT_C_FUN(log)(base)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_math_log_obj, 1, 2, mp_math_log); + +// Functions that return a tuple + + +STATIC mp_obj_t mp_math_frexp(mp_obj_t x_obj) { + int int_exponent = 0; + mp_float_t significand = MICROPY_FLOAT_C_FUN(frexp)(mp_obj_get_float(x_obj), &int_exponent); + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_float(significand); + tuple[1] = mp_obj_new_int(int_exponent); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_frexp_obj, mp_math_frexp); + +STATIC mp_obj_t mp_math_modf(mp_obj_t x_obj) { + mp_float_t int_part = 0.0; + mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(mp_obj_get_float(x_obj), &int_part); + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_float(fractional_part); + tuple[1] = mp_obj_new_float(int_part); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_modf_obj, mp_math_modf); + +// Angular conversions + + +STATIC mp_obj_t mp_math_radians(mp_obj_t x_obj) { + return mp_obj_new_float(mp_obj_get_float(x_obj) * (MP_PI / MICROPY_FLOAT_CONST(180.0))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_radians_obj, mp_math_radians); + + +STATIC mp_obj_t mp_math_degrees(mp_obj_t x_obj) { + return mp_obj_new_float(mp_obj_get_float(x_obj) * (MICROPY_FLOAT_CONST(180.0) / MP_PI)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_degrees_obj, mp_math_degrees); + +STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_math) }, + { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e }, + { MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi }, + { MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_math_sqrt_obj) }, + { MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_math_pow_obj) }, + { MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&mp_math_exp_obj) }, + #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + { MP_ROM_QSTR(MP_QSTR_expm1), MP_ROM_PTR(&mp_math_expm1_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&mp_math_log_obj) }, + #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + { MP_ROM_QSTR(MP_QSTR_log2), MP_ROM_PTR(&mp_math_log2_obj) }, + { MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&mp_math_log10_obj) }, + { MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&mp_math_cosh_obj) }, + { MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&mp_math_sinh_obj) }, + { MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&mp_math_tanh_obj) }, + { MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&mp_math_acosh_obj) }, + { MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&mp_math_asinh_obj) }, + { MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&mp_math_atanh_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&mp_math_cos_obj) }, + { MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&mp_math_sin_obj) }, + { MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&mp_math_tan_obj) }, + { MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&mp_math_acos_obj) }, + { MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&mp_math_asin_obj) }, + { MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&mp_math_atan_obj) }, + { MP_ROM_QSTR(MP_QSTR_atan2), MP_ROM_PTR(&mp_math_atan2_obj) }, + { MP_ROM_QSTR(MP_QSTR_ceil), MP_ROM_PTR(&mp_math_ceil_obj) }, + { MP_ROM_QSTR(MP_QSTR_copysign), MP_ROM_PTR(&mp_math_copysign_obj) }, + { MP_ROM_QSTR(MP_QSTR_fabs), MP_ROM_PTR(&mp_math_fabs_obj) }, + { MP_ROM_QSTR(MP_QSTR_floor), MP_ROM_PTR(&mp_math_floor_obj) }, + { MP_ROM_QSTR(MP_QSTR_fmod), MP_ROM_PTR(&mp_math_fmod_obj) }, + { MP_ROM_QSTR(MP_QSTR_frexp), MP_ROM_PTR(&mp_math_frexp_obj) }, + { MP_ROM_QSTR(MP_QSTR_ldexp), MP_ROM_PTR(&mp_math_ldexp_obj) }, + { MP_ROM_QSTR(MP_QSTR_modf), MP_ROM_PTR(&mp_math_modf_obj) }, + { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) }, + { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) }, + { MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) }, + { MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) }, + { MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) }, + { MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) }, + #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS + { MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&mp_math_erf_obj) }, + { MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&mp_math_erfc_obj) }, + { MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&mp_math_gamma_obj) }, + { MP_ROM_QSTR(MP_QSTR_lgamma), MP_ROM_PTR(&mp_math_lgamma_obj) }, + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_math_globals, mp_module_math_globals_table); + +const mp_obj_module_t math_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_math_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_math, math_module, CIRCUITPY_MATH); + +#endif // MICROPY_PY_BUILTINS_FLOAT diff --git a/circuitpython/shared-bindings/mdns/RemoteService.c b/circuitpython/shared-bindings/mdns/RemoteService.c new file mode 100644 index 0000000..846207b --- /dev/null +++ b/circuitpython/shared-bindings/mdns/RemoteService.c @@ -0,0 +1,136 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 <string.h> + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/mdns/RemoteService.h" + +//| class RemoteService: +//| """Encapsulates information about a remote service that was found during a search. This +//| object may only be created by a `mdns.Server`. It has no user-visible constructor.""" +//| + +//| def __init__(self) -> None: +//| """Cannot be instantiated directly. Use `mdns.Server.find`.""" +//| ... +//| + +//| hostname: str +//| """The hostname of the device (read-only),.""" +//| +STATIC mp_obj_t mdns_remoteservice_get_hostname(mp_obj_t self_in) { + mdns_remoteservice_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *hostname = common_hal_mdns_remoteservice_get_hostname(self); + return mp_obj_new_str(hostname, strlen(hostname)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_get_hostname_obj, mdns_remoteservice_get_hostname); + +MP_PROPERTY_GETTER(mdns_remoteservice_hostname_obj, + (mp_obj_t)&mdns_remoteservice_get_hostname_obj); + +//| instance_name: str +//| """The human readable instance name for the service. (read-only)""" +//| +STATIC mp_obj_t remoteservice_get_instance_name(mp_obj_t self_in) { + mdns_remoteservice_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *instance_name = common_hal_mdns_remoteservice_get_instance_name(self); + return mp_obj_new_str(instance_name, strlen(instance_name)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_get_instance_name_obj, remoteservice_get_instance_name); + +MP_PROPERTY_GETTER(mdns_remoteservice_instance_name_obj, + (mp_obj_t)&mdns_remoteservice_get_instance_name_obj); + +//| service_type: str +//| """The service type string such as ``_http``. (read-only)""" +//| +STATIC mp_obj_t remoteservice_get_service_type(mp_obj_t self_in) { + mdns_remoteservice_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *service_type = common_hal_mdns_remoteservice_get_service_type(self); + return mp_obj_new_str(service_type, strlen(service_type)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_get_service_type_obj, remoteservice_get_service_type); + +MP_PROPERTY_GETTER(mdns_remoteservice_service_type_obj, + (mp_obj_t)&mdns_remoteservice_get_service_type_obj); + +//| protocol: str +//| """The protocol string such as ``_tcp``. (read-only)""" +//| +STATIC mp_obj_t remoteservice_get_protocol(mp_obj_t self_in) { + mdns_remoteservice_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *protocol = common_hal_mdns_remoteservice_get_protocol(self); + return mp_obj_new_str(protocol, strlen(protocol)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_get_protocol_obj, remoteservice_get_protocol); + +MP_PROPERTY_GETTER(mdns_remoteservice_protocol_obj, + (mp_obj_t)&mdns_remoteservice_get_protocol_obj); + +//| port: int +//| """Port number used for the service. (read-only)""" +//| +STATIC mp_obj_t remoteservice_get_port(mp_obj_t self_in) { + mdns_remoteservice_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_mdns_remoteservice_get_port(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_get_port_obj, remoteservice_get_port); + +MP_PROPERTY_GETTER(mdns_remoteservice_port_obj, + (mp_obj_t)&mdns_remoteservice_get_port_obj); + +//| def __del__(self) -> None: +//| """Deletes the RemoteService object.""" +//| ... +//| +STATIC mp_obj_t mdns_remoteservice_obj_deinit(mp_obj_t self_in) { + mdns_remoteservice_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_mdns_remoteservice_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mdns_remoteservice_deinit_obj, mdns_remoteservice_obj_deinit); + +STATIC const mp_rom_map_elem_t mdns_remoteservice_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&mdns_remoteservice_hostname_obj) }, + { MP_ROM_QSTR(MP_QSTR_instance_name), MP_ROM_PTR(&mdns_remoteservice_instance_name_obj) }, + { MP_ROM_QSTR(MP_QSTR_service_type), MP_ROM_PTR(&mdns_remoteservice_service_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_protocol), MP_ROM_PTR(&mdns_remoteservice_protocol_obj) }, + { MP_ROM_QSTR(MP_QSTR_port), MP_ROM_PTR(&mdns_remoteservice_port_obj) }, + + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mdns_remoteservice_deinit_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mdns_remoteservice_locals_dict, mdns_remoteservice_locals_dict_table); + +const mp_obj_type_t mdns_remoteservice_type = { + { &mp_type_type }, + .name = MP_QSTR_RemoteService, + .locals_dict = (mp_obj_dict_t *)&mdns_remoteservice_locals_dict +}; diff --git a/circuitpython/shared-bindings/mdns/RemoteService.h b/circuitpython/shared-bindings/mdns/RemoteService.h new file mode 100644 index 0000000..f751b68 --- /dev/null +++ b/circuitpython/shared-bindings/mdns/RemoteService.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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. + */ + +#pragma once + +#include "py/obj.h" +#include "common-hal/mdns/RemoteService.h" + +extern const mp_obj_type_t mdns_remoteservice_type; + +const char *common_hal_mdns_remoteservice_get_service_type(mdns_remoteservice_obj_t *self); +const char *common_hal_mdns_remoteservice_get_protocol(mdns_remoteservice_obj_t *self); +const char *common_hal_mdns_remoteservice_get_instance_name(mdns_remoteservice_obj_t *self); +const char *common_hal_mdns_remoteservice_get_hostname(mdns_remoteservice_obj_t *self); +mp_int_t common_hal_mdns_remoteservice_get_port(mdns_remoteservice_obj_t *self); +void common_hal_mdns_remoteservice_deinit(mdns_remoteservice_obj_t *self); diff --git a/circuitpython/shared-bindings/mdns/Server.c b/circuitpython/shared-bindings/mdns/Server.c new file mode 100644 index 0000000..b5e6c11 --- /dev/null +++ b/circuitpython/shared-bindings/mdns/Server.c @@ -0,0 +1,208 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * + * 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 <string.h> + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/mdns/__init__.h" +#include "shared-bindings/mdns/Server.h" +#include "shared-bindings/util.h" + +//| class Server: +//| """The MDNS Server responds to queries for this device's information and allows for querying +//| other devices.""" +//| + +//| def __init__(self, network_interface: wifi.Radio) -> None: +//| """ +//| Constructs or returns the mdns.Server for the given network_interface. (CircuitPython +//| may already be using it.) Only native interfaces are currently supported. +//| """ +//| ... +//| +STATIC mp_obj_t mdns_server_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_network_interface }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_network_interface, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + mdns_server_obj_t *self = m_new_obj(mdns_server_obj_t); + self->base.type = &mdns_server_type; + common_hal_mdns_server_construct(self, args[ARG_network_interface].u_obj); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Stops the server""" +//| ... +//| +STATIC mp_obj_t mdns_server_obj_deinit(mp_obj_t self_in) { + mdns_server_obj_t *self = (mdns_server_obj_t *)self_in; + common_hal_mdns_server_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mdns_server_deinit_obj, mdns_server_obj_deinit); + +STATIC void check_for_deinit(mdns_server_obj_t *self) { + if (common_hal_mdns_server_deinited(self)) { + raise_deinited_error(); + } +} + +//| +//| hostname: str +//| """Hostname resolvable as ``<hostname>.local`` in addition to ``circuitpython.local``. Make +//| sure this is unique across all devices on the network. It defaults to ``cpy-######`` +//| where ``######`` is the hex digits of the last three bytes of the mac address.""" +//| +STATIC mp_obj_t mdns_server_get_hostname(mp_obj_t self) { + check_for_deinit(self); + const char *hostname = common_hal_mdns_server_get_hostname(self); + return mp_obj_new_str(hostname, strlen(hostname)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mdns_server_get_hostname_obj, mdns_server_get_hostname); + +static mp_obj_t mdns_server_set_hostname(mp_obj_t self, mp_obj_t hostname) { + check_for_deinit(self); + common_hal_mdns_server_set_hostname(self, mp_obj_str_get_str(hostname)); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mdns_server_set_hostname_obj, mdns_server_set_hostname); + +MP_PROPERTY_GETSET(mdns_server_hostname_obj, + (mp_obj_t)&mdns_server_get_hostname_obj, + (mp_obj_t)&mdns_server_set_hostname_obj); + +//| instance_name: str +//| """Human readable name to describe the device.""" +//| +STATIC mp_obj_t mdns_server_get_instance_name(mp_obj_t self) { + check_for_deinit(self); + const char *instance_name = common_hal_mdns_server_get_instance_name(self); + return mp_obj_new_str(instance_name, strlen(instance_name)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mdns_server_get_instance_name_obj, mdns_server_get_instance_name); + +STATIC mp_obj_t mdns_server_set_instance_name(mp_obj_t self, mp_obj_t new_instance_name) { + check_for_deinit(self); + common_hal_mdns_server_set_instance_name(self, mp_obj_str_get_str(new_instance_name)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(mdns_server_set_instance_name_obj, mdns_server_set_instance_name); + +MP_PROPERTY_GETSET(mdns_server_instance_name_obj, + (mp_obj_t)&mdns_server_get_instance_name_obj, + (mp_obj_t)&mdns_server_set_instance_name_obj); + + +//| def find(self, service_type: str, protocol: str, *, timeout: float = 1) -> Tuple[RemoteService]: +//| """Find all locally available remote services with the given service type and protocol. +//| +//| This doesn't allow for direct hostname lookup. To do that, use +//| `socketpool.SocketPool.getaddrinfo()`. +//| +//| :param str service_type: The service type such as "_http" +//| :param str protocol: The service protocol such as "_tcp" +//| :param float/int timeout: Time to wait for responses""" +//| ... +//| +STATIC mp_obj_t mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mdns_server_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + enum { ARG_service_type, ARG_protocol, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service_type, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_protocol, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + }; + + 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_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + const char *service_type = mp_obj_str_get_str(args[ARG_service_type].u_obj); + const char *protocol = mp_obj_str_get_str(args[ARG_protocol].u_obj); + + return common_hal_mdns_server_find(self, service_type, protocol, timeout); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, mdns_server_find); + +//| def advertise_service(self, *, service_type: str, protocol: str, port: int) -> None: +//| """Respond to queries for the given service with the given port. +//| +//| :param str service_type: The service type such as "_http" +//| :param str protocol: The service protocol such as "_tcp" +//| :param int port: The port used by the service""" +//| ... +//| +STATIC mp_obj_t mdns_server_advertise_service(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mdns_server_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + enum { ARG_service_type, ARG_protocol, ARG_port }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service_type, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_protocol, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_port, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + 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); + + const char *service_type = mp_obj_str_get_str(args[ARG_service_type].u_obj); + const char *protocol = mp_obj_str_get_str(args[ARG_protocol].u_obj); + + common_hal_mdns_server_advertise_service(self, service_type, protocol, args[ARG_port].u_int); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_advertise_service_obj, 1, mdns_server_advertise_service); + +STATIC const mp_rom_map_elem_t mdns_server_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&mdns_server_hostname_obj) }, + { MP_ROM_QSTR(MP_QSTR_instance_name), MP_ROM_PTR(&mdns_server_instance_name_obj) }, + + { MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&mdns_server_find_obj) }, + { MP_ROM_QSTR(MP_QSTR_advertise_service), MP_ROM_PTR(&mdns_server_advertise_service_obj) }, + + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mdns_server_deinit_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mdns_server_locals_dict, mdns_server_locals_dict_table); + +const mp_obj_type_t mdns_server_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Server, + .make_new = mdns_server_make_new, + .locals_dict = (mp_obj_t)&mdns_server_locals_dict, +}; diff --git a/circuitpython/shared-bindings/mdns/Server.h b/circuitpython/shared-bindings/mdns/Server.h new file mode 100644 index 0000000..a178e9b --- /dev/null +++ b/circuitpython/shared-bindings/mdns/Server.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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. + */ + +#pragma once + +#include <stdbool.h> + +#include "common-hal/mdns/Server.h" + +extern const mp_obj_type_t mdns_server_type; + +void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_interface); +void common_hal_mdns_server_deinit(mdns_server_obj_t *self); +bool common_hal_mdns_server_deinited(mdns_server_obj_t *self); +const char *common_hal_mdns_server_get_hostname(mdns_server_obj_t *self); +void common_hal_mdns_server_set_hostname(mdns_server_obj_t *self, const char *hostname); +const char *common_hal_mdns_server_get_instance_name(mdns_server_obj_t *self); +void common_hal_mdns_server_set_instance_name(mdns_server_obj_t *self, const char *instance_name); +mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_float_t timeout); +void common_hal_mdns_server_advertise_service(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_int_t port); diff --git a/circuitpython/shared-bindings/mdns/__init__.c b/circuitpython/shared-bindings/mdns/__init__.c new file mode 100644 index 0000000..9752a3b --- /dev/null +++ b/circuitpython/shared-bindings/mdns/__init__.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Dan Halbert 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 <stdarg.h> + +#include "py/objexcept.h" +#include "py/runtime.h" +#include "shared-bindings/mdns/__init__.h" +#include "shared-bindings/mdns/Server.h" +#include "shared-bindings/mdns/RemoteService.h" + +//| """Multicast Domain Name Service +//| +//| The `mdns` module provides basic support for multicast domain name services. +//| Basic use provides hostname resolution under the .local TLD. This module +//| also supports DNS Service Discovery that allows for discovering other hosts +//| that provide a desired service.""" +//| + +STATIC const mp_rom_map_elem_t mdns_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mdns) }, + { MP_ROM_QSTR(MP_QSTR_Server), MP_ROM_PTR(&mdns_server_type) }, + { MP_ROM_QSTR(MP_QSTR_RemoteService), MP_ROM_PTR(&mdns_remoteservice_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mdns_module_globals, mdns_module_globals_table); + +const mp_obj_module_t mdns_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mdns_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mdns, mdns_module, CIRCUITPY_MDNS); diff --git a/circuitpython/shared-bindings/mdns/__init__.h b/circuitpython/shared-bindings/mdns/__init__.h new file mode 100644 index 0000000..d672285 --- /dev/null +++ b/circuitpython/shared-bindings/mdns/__init__.h @@ -0,0 +1,27 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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. + */ + +#pragma once diff --git a/circuitpython/shared-bindings/memorymonitor/AllocationAlarm.c b/circuitpython/shared-bindings/memorymonitor/AllocationAlarm.c new file mode 100644 index 0000000..b546452 --- /dev/null +++ b/circuitpython/shared-bindings/memorymonitor/AllocationAlarm.c @@ -0,0 +1,137 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/memorymonitor/AllocationAlarm.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class AllocationAlarm: +//| +//| def __init__(self, *, minimum_block_count: int = 1) -> None: +//| """Throw an exception when an allocation of ``minimum_block_count`` or more blocks +//| occurs while active. +//| +//| Track allocations:: +//| +//| import memorymonitor +//| +//| aa = memorymonitor.AllocationAlarm(minimum_block_count=2) +//| x = 2 +//| # Should not allocate any blocks. +//| with aa: +//| x = 5 +//| +//| # Should throw an exception when allocating storage for the 20 bytes. +//| with aa: +//| x = bytearray(20) +//| +//| """ +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationalarm_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *all_args, mp_map_t *kw_args) { + enum { ARG_minimum_block_count }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_minimum_block_count, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + }; + 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); + mp_int_t minimum_block_count = args[ARG_minimum_block_count].u_int; + if (minimum_block_count < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_minimum_block_count); + } + + memorymonitor_allocationalarm_obj_t *self = m_new_obj(memorymonitor_allocationalarm_obj_t); + self->base.type = &memorymonitor_allocationalarm_type; + + common_hal_memorymonitor_allocationalarm_construct(self, minimum_block_count); + + return MP_OBJ_FROM_PTR(self); +} + +//| def ignore(self, count: int) -> AllocationAlarm: +//| """Sets the number of applicable allocations to ignore before raising the exception. +//| Automatically set back to zero at context exit. +//| +//| Use it within a ``with`` block:: +//| +//| # Will not alarm because the bytearray allocation will be ignored. +//| with aa.ignore(2): +//| x = bytearray(20) +//| """ +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationalarm_obj_ignore(mp_obj_t self_in, mp_obj_t count_obj) { + mp_int_t count = mp_obj_get_int(count_obj); + if (count < 0) { + mp_raise_ValueError_varg(translate("%q must be >= 0"), MP_QSTR_count); + } + common_hal_memorymonitor_allocationalarm_set_ignore(self_in, count); + return self_in; +} +MP_DEFINE_CONST_FUN_OBJ_2(memorymonitor_allocationalarm_ignore_obj, memorymonitor_allocationalarm_obj_ignore); + +//| def __enter__(self) -> AllocationAlarm: +//| """Enables the alarm.""" +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationalarm_obj___enter__(mp_obj_t self_in) { + common_hal_memorymonitor_allocationalarm_resume(self_in); + return self_in; +} +MP_DEFINE_CONST_FUN_OBJ_1(memorymonitor_allocationalarm___enter___obj, memorymonitor_allocationalarm_obj___enter__); + +//| def __exit__(self) -> None: +//| """Automatically disables the allocation alarm when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationalarm_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_memorymonitor_allocationalarm_set_ignore(args[0], 0); + common_hal_memorymonitor_allocationalarm_pause(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(memorymonitor_allocationalarm___exit___obj, 4, 4, memorymonitor_allocationalarm_obj___exit__); + +STATIC const mp_rom_map_elem_t memorymonitor_allocationalarm_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_ignore), MP_ROM_PTR(&memorymonitor_allocationalarm_ignore_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&memorymonitor_allocationalarm___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&memorymonitor_allocationalarm___exit___obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(memorymonitor_allocationalarm_locals_dict, memorymonitor_allocationalarm_locals_dict_table); + +const mp_obj_type_t memorymonitor_allocationalarm_type = { + { &mp_type_type }, + .name = MP_QSTR_AllocationAlarm, + .make_new = memorymonitor_allocationalarm_make_new, + .locals_dict = (mp_obj_dict_t *)&memorymonitor_allocationalarm_locals_dict, +}; diff --git a/circuitpython/shared-bindings/memorymonitor/AllocationAlarm.h b/circuitpython/shared-bindings/memorymonitor/AllocationAlarm.h new file mode 100644 index 0000000..0a62971 --- /dev/null +++ b/circuitpython/shared-bindings/memorymonitor/AllocationAlarm.h @@ -0,0 +1,39 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_MEMORYMONITOR_ALLOCATIONALARM_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MEMORYMONITOR_ALLOCATIONALARM_H + +#include "shared-module/memorymonitor/AllocationAlarm.h" + +extern const mp_obj_type_t memorymonitor_allocationalarm_type; + +void common_hal_memorymonitor_allocationalarm_construct(memorymonitor_allocationalarm_obj_t *self, size_t minimum_block_count); +void common_hal_memorymonitor_allocationalarm_pause(memorymonitor_allocationalarm_obj_t *self); +void common_hal_memorymonitor_allocationalarm_resume(memorymonitor_allocationalarm_obj_t *self); +void common_hal_memorymonitor_allocationalarm_set_ignore(memorymonitor_allocationalarm_obj_t *self, mp_int_t count); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MEMORYMONITOR_ALLOCATIONALARM_H diff --git a/circuitpython/shared-bindings/memorymonitor/AllocationSize.c b/circuitpython/shared-bindings/memorymonitor/AllocationSize.c new file mode 100644 index 0000000..1c39fdc --- /dev/null +++ b/circuitpython/shared-bindings/memorymonitor/AllocationSize.c @@ -0,0 +1,182 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/memorymonitor/AllocationSize.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class AllocationSize: +//| +//| def __init__(self) -> None: +//| """Tracks the number of allocations in power of two buckets. +//| +//| It will have 16 16-bit buckets to track allocation counts. It is total allocations +//| meaning frees are ignored. Reallocated memory is counted twice, at allocation and when +//| reallocated with the larger size. +//| +//| The buckets are measured in terms of blocks which is the finest granularity of the heap. +//| This means bucket 0 will count all allocations less than or equal to the number of bytes +//| per block, typically 16. Bucket 2 will be less than or equal to 4 blocks. See +//| `bytes_per_block` to convert blocks to bytes. +//| +//| Multiple AllocationSizes can be used to track different code boundaries. +//| +//| Track allocations:: +//| +//| import memorymonitor +//| +//| mm = memorymonitor.AllocationSize() +//| with mm: +//| print("hello world" * 3) +//| +//| for bucket, count in enumerate(mm): +//| print("<", 2 ** bucket, count) +//| +//| """ +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationsize_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *all_args, mp_map_t *kw_args) { + memorymonitor_allocationsize_obj_t *self = m_new_obj(memorymonitor_allocationsize_obj_t); + self->base.type = &memorymonitor_allocationsize_type; + + common_hal_memorymonitor_allocationsize_construct(self); + + return MP_OBJ_FROM_PTR(self); +} + +//| def __enter__(self) -> AllocationSize: +//| """Clears counts and resumes tracking.""" +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationsize_obj___enter__(mp_obj_t self_in) { + common_hal_memorymonitor_allocationsize_clear(self_in); + common_hal_memorymonitor_allocationsize_resume(self_in); + return self_in; +} +MP_DEFINE_CONST_FUN_OBJ_1(memorymonitor_allocationsize___enter___obj, memorymonitor_allocationsize_obj___enter__); + +//| def __exit__(self) -> None: +//| """Automatically pauses allocation tracking when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationsize_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_memorymonitor_allocationsize_pause(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(memorymonitor_allocationsize___exit___obj, 4, 4, memorymonitor_allocationsize_obj___exit__); + +//| bytes_per_block: int +//| """Number of bytes per block""" +//| +STATIC mp_obj_t memorymonitor_allocationsize_obj_get_bytes_per_block(mp_obj_t self_in) { + memorymonitor_allocationsize_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_memorymonitor_allocationsize_get_bytes_per_block(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(memorymonitor_allocationsize_get_bytes_per_block_obj, memorymonitor_allocationsize_obj_get_bytes_per_block); + +MP_PROPERTY_GETTER(memorymonitor_allocationsize_bytes_per_block_obj, + (mp_obj_t)&memorymonitor_allocationsize_get_bytes_per_block_obj); + +//| def __len__(self) -> int: +//| """Returns the number of allocation buckets. +//| +//| This allows you to:: +//| +//| mm = memorymonitor.AllocationSize() +//| print(len(mm))""" +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationsize_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + memorymonitor_allocationsize_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint16_t len = common_hal_memorymonitor_allocationsize_get_len(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __getitem__(self, index: int) -> Optional[int]: +//| """Returns the allocation count for the given bucket. +//| +//| This allows you to:: +//| +//| mm = memorymonitor.AllocationSize() +//| print(mm[0])""" +//| ... +//| +STATIC mp_obj_t memorymonitor_allocationsize_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value) { + if (value == mp_const_none) { + // delete item + mp_raise_AttributeError(translate("Cannot delete values")); + } else { + memorymonitor_allocationsize_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + mp_raise_NotImplementedError(translate("Slices not supported")); + } else { + size_t index = mp_get_index(&memorymonitor_allocationsize_type, common_hal_memorymonitor_allocationsize_get_len(self), index_obj, false); + if (value == MP_OBJ_SENTINEL) { + // load + return MP_OBJ_NEW_SMALL_INT(common_hal_memorymonitor_allocationsize_get_item(self, index)); + } else { + mp_raise_AttributeError(translate("Read-only")); + } + } + } + return mp_const_none; +} + +STATIC const mp_rom_map_elem_t memorymonitor_allocationsize_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&memorymonitor_allocationsize___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&memorymonitor_allocationsize___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_bytes_per_block), MP_ROM_PTR(&memorymonitor_allocationsize_bytes_per_block_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(memorymonitor_allocationsize_locals_dict, memorymonitor_allocationsize_locals_dict_table); + +const mp_obj_type_t memorymonitor_allocationsize_type = { + { &mp_type_type }, + .name = MP_QSTR_AllocationSize, + .make_new = memorymonitor_allocationsize_make_new, + .subscr = memorymonitor_allocationsize_subscr, + .unary_op = memorymonitor_allocationsize_unary_op, + .getiter = mp_obj_new_generic_iterator, + .locals_dict = (mp_obj_dict_t *)&memorymonitor_allocationsize_locals_dict, +}; diff --git a/circuitpython/shared-bindings/memorymonitor/AllocationSize.h b/circuitpython/shared-bindings/memorymonitor/AllocationSize.h new file mode 100644 index 0000000..c677c1a --- /dev/null +++ b/circuitpython/shared-bindings/memorymonitor/AllocationSize.h @@ -0,0 +1,42 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_MEMORYMONITOR_ALLOCATIONSIZE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MEMORYMONITOR_ALLOCATIONSIZE_H + +#include "shared-module/memorymonitor/AllocationSize.h" + +extern const mp_obj_type_t memorymonitor_allocationsize_type; + +extern void common_hal_memorymonitor_allocationsize_construct(memorymonitor_allocationsize_obj_t *self); +extern void common_hal_memorymonitor_allocationsize_pause(memorymonitor_allocationsize_obj_t *self); +extern void common_hal_memorymonitor_allocationsize_resume(memorymonitor_allocationsize_obj_t *self); +extern void common_hal_memorymonitor_allocationsize_clear(memorymonitor_allocationsize_obj_t *self); +extern size_t common_hal_memorymonitor_allocationsize_get_bytes_per_block(memorymonitor_allocationsize_obj_t *self); +extern uint16_t common_hal_memorymonitor_allocationsize_get_len(memorymonitor_allocationsize_obj_t *self); +extern uint16_t common_hal_memorymonitor_allocationsize_get_item(memorymonitor_allocationsize_obj_t *self, int16_t index); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MEMORYMONITOR_ALLOCATIONSIZE_H diff --git a/circuitpython/shared-bindings/memorymonitor/__init__.c b/circuitpython/shared-bindings/memorymonitor/__init__.c new file mode 100644 index 0000000..64a3a6a --- /dev/null +++ b/circuitpython/shared-bindings/memorymonitor/__init__.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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. + */ + +#include <stdint.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/memorymonitor/__init__.h" +#include "shared-bindings/memorymonitor/AllocationAlarm.h" +#include "shared-bindings/memorymonitor/AllocationSize.h" + +//| """Memory monitoring helpers""" +//| + +//| class AllocationError(Exception): +//| """Catchall exception for allocation related errors.""" +//| ... +MP_DEFINE_MEMORYMONITOR_EXCEPTION(AllocationError, Exception) + +NORETURN void mp_raise_memorymonitor_AllocationError(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_memorymonitor_AllocationError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +STATIC const mp_rom_map_elem_t memorymonitor_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_memorymonitor) }, + { MP_ROM_QSTR(MP_QSTR_AllocationAlarm), MP_ROM_PTR(&memorymonitor_allocationalarm_type) }, + { MP_ROM_QSTR(MP_QSTR_AllocationSize), MP_ROM_PTR(&memorymonitor_allocationsize_type) }, + + // Errors + { MP_ROM_QSTR(MP_QSTR_AllocationError), MP_ROM_PTR(&mp_type_memorymonitor_AllocationError) }, +}; + +STATIC MP_DEFINE_CONST_DICT(memorymonitor_module_globals, memorymonitor_module_globals_table); + +void memorymonitor_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; + bool is_subclass = kind & PRINT_EXC_SUBCLASS; + if (!is_subclass && (k == PRINT_EXC)) { + mp_print_str(print, qstr_str(MP_OBJ_QSTR_VALUE(memorymonitor_module_globals_table[0].value))); + mp_print_str(print, "."); + } + mp_obj_exception_print(print, o_in, kind); +} + +const mp_obj_module_t memorymonitor_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&memorymonitor_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_memorymonitor, memorymonitor_module, CIRCUITPY_MEMORYMONITOR); diff --git a/circuitpython/shared-bindings/memorymonitor/__init__.h b/circuitpython/shared-bindings/memorymonitor/__init__.h new file mode 100644 index 0000000..5d9dfdd --- /dev/null +++ b/circuitpython/shared-bindings/memorymonitor/__init__.h @@ -0,0 +1,49 @@ +/* + * 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_MEMORYMONITOR___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MEMORYMONITOR___INIT___H + +#include "py/obj.h" + + +void memorymonitor_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); + +#define MP_DEFINE_MEMORYMONITOR_EXCEPTION(exc_name, base_name) \ + const mp_obj_type_t mp_type_memorymonitor_##exc_name = { \ + { &mp_type_type }, \ + .name = MP_QSTR_##exc_name, \ + .print = memorymonitor_exception_print, \ + .make_new = mp_obj_exception_make_new, \ + .attr = mp_obj_exception_attr, \ + .parent = &mp_type_##base_name, \ + }; + +extern const mp_obj_type_t mp_type_memorymonitor_AllocationError; + +NORETURN void mp_raise_memorymonitor_AllocationError(const compressed_string_t *msg, ...); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MEMORYMONITOR___INIT___H diff --git a/circuitpython/shared-bindings/microcontroller/Pin.c b/circuitpython/shared-bindings/microcontroller/Pin.c new file mode 100644 index 0000000..e6809d6 --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/Pin.c @@ -0,0 +1,196 @@ +/* + * 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. + */ + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +//| class Pin: +//| """Identifies an IO pin on the microcontroller.""" +//| +//| def __init__(self) -> None: +//| """Identifies an IO pin on the microcontroller. They are fixed by the +//| hardware so they cannot be constructed on demand. Instead, use +//| :mod:`board` or :mod:`microcontroller.pin` to reference the desired pin.""" +//| ... +//| + +//| def __hash__(self) -> int: +//| """Returns a hash for the Pin.""" +//| ... +//| +// Provided by mp_generic_unary_op(). + +static void get_pin_name(const mcu_pin_obj_t *self, qstr *package, qstr *module, qstr *name) { + const mp_map_t *board_map = &board_module_globals.map; + for (uint8_t i = 0; i < board_map->alloc; i++) { + if (board_map->table[i].value == MP_OBJ_FROM_PTR(self)) { + *package = 0; + *module = MP_QSTR_board; + *name = MP_OBJ_QSTR_VALUE(board_map->table[i].key); + return; + } + } + const mp_map_t *mcu_map = &mcu_pin_globals.map; + for (uint8_t i = 0; i < mcu_map->alloc; i++) { + if (mcu_map->table[i].value == MP_OBJ_FROM_PTR(self)) { + *package = MP_QSTR_microcontroller; + *module = MP_QSTR_pin; + *name = MP_OBJ_QSTR_VALUE(mcu_map->table[i].key); + return; + } + } +} + +STATIC void mcu_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mcu_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + qstr package = MP_QSTR_Pin; + qstr module; + qstr name = MP_QSTR_Pin; + + get_pin_name(self, &package, &module, &name); + if (package) { + mp_printf(print, "%q.%q.%q", package, module, name); + } else { + mp_printf(print, "%q.%q", module, name); + } +} + +const mp_obj_type_t mcu_pin_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Pin, + .print = mcu_pin_print, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_generic_unary_op, + ) +}; + +const mcu_pin_obj_t *validate_obj_is_pin(mp_obj_t obj) { + if (!mp_obj_is_type(obj, &mcu_pin_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), mcu_pin_type.name); + } + return MP_OBJ_TO_PTR(obj); +} + +// Validate that the obj is a pin or None. Return an mcu_pin_obj_t* or NULL, correspondingly. +const mcu_pin_obj_t *validate_obj_is_pin_or_none(mp_obj_t obj) { + if (obj == mp_const_none) { + return NULL; + } + return validate_obj_is_pin(obj); +} + +const mcu_pin_obj_t *validate_obj_is_free_pin(mp_obj_t obj) { + const mcu_pin_obj_t *pin = validate_obj_is_pin(obj); + assert_pin_free(pin); + return pin; +} + +// Validate every element in the list is a unique pin +void validate_no_duplicate_pins(mp_obj_t seq, qstr arg_name) { + const size_t num_pins = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(seq)); + + for (size_t pin_cnt = 0; pin_cnt < num_pins; pin_cnt++) { + mp_obj_t pin1_obj = mp_obj_subscr(seq, MP_OBJ_NEW_SMALL_INT(pin_cnt), MP_OBJ_SENTINEL); + const mcu_pin_obj_t *pin1 = validate_obj_is_pin(pin1_obj); + + for (size_t pin_cnt_2 = pin_cnt + 1; pin_cnt_2 < num_pins; pin_cnt_2++) { + mp_obj_t pin2_obj = mp_obj_subscr(seq, MP_OBJ_NEW_SMALL_INT(pin_cnt_2), MP_OBJ_SENTINEL); + const mcu_pin_obj_t *pin2 = validate_obj_is_pin(pin2_obj); + if (pin1 == pin2) { + mp_raise_TypeError_varg(translate("%q contains duplicate pins"), arg_name); + } + } + } +} + +void validate_no_duplicate_pins_2(mp_obj_t seq1, mp_obj_t seq2, qstr arg_name1, qstr arg_name2) { + const size_t num_pins_1 = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(seq1)); + const size_t num_pins_2 = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(seq2)); + + validate_no_duplicate_pins(seq1, arg_name1); + validate_no_duplicate_pins(seq2, arg_name2); + + for (size_t pin_cnt_1 = 0; pin_cnt_1 < num_pins_1; pin_cnt_1++) { + mp_obj_t pin1_obj = mp_obj_subscr(seq1, MP_OBJ_NEW_SMALL_INT(pin_cnt_1), MP_OBJ_SENTINEL); + const mcu_pin_obj_t *pin1 = validate_obj_is_pin(pin1_obj); + + for (size_t pin_cnt_2 = 0; pin_cnt_2 < num_pins_2; pin_cnt_2++) { + mp_obj_t pin2_obj = mp_obj_subscr(seq2, MP_OBJ_NEW_SMALL_INT(pin_cnt_2), MP_OBJ_SENTINEL); + const mcu_pin_obj_t *pin2 = validate_obj_is_pin(pin2_obj); + if (pin1 == pin2) { + mp_raise_TypeError_varg(translate("%q and %q contain duplicate pins"), arg_name1, arg_name2); + } + } + } +} + +// Validate every element in the list to be a free pin. +void validate_list_is_free_pins(qstr what, const mcu_pin_obj_t **pins_out, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out) { + mp_int_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(seq)); + if (len > max_pins) { + mp_raise_ValueError_varg(translate("At most %d %q may be specified (not %d)"), max_pins, what, len); + } + *count_out = len; + for (mp_int_t i = 0; i < len; i++) { + pins_out[i] = validate_obj_is_free_pin(mp_obj_subscr(seq, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + } +} + +// Validate that the obj is a free pin or None. Return an mcu_pin_obj_t* or NULL, correspondingly. +const mcu_pin_obj_t *validate_obj_is_free_pin_or_none(mp_obj_t obj) { + if (obj == mp_const_none) { + return NULL; + } + const mcu_pin_obj_t *pin = validate_obj_is_pin(obj); + assert_pin_free(pin); + return pin; +} + +void assert_pin_free(const mcu_pin_obj_t *pin) { + if (pin != NULL && pin != MP_OBJ_TO_PTR(mp_const_none) && !common_hal_mcu_pin_is_free(pin)) { + qstr package; + qstr module; + qstr name = MP_QSTR_Pin; + + get_pin_name(pin, &package, &module, &name); + mp_raise_ValueError_varg(translate("%q in use"), name); + } +} + +void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out) { + const mcu_pin_obj_t *pins[max_pins]; + validate_list_is_free_pins(what, pins, max_pins, seq, count_out); + for (mp_int_t i = 0; i < *count_out; i++) { + pin_nos[i] = common_hal_mcu_pin_number(pins[i]); + } +} diff --git a/circuitpython/shared-bindings/microcontroller/Pin.h b/circuitpython/shared-bindings/microcontroller/Pin.h new file mode 100644 index 0000000..a7378ea --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/Pin.h @@ -0,0 +1,57 @@ +/* + * 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_MICROCONTROLLER_PIN_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER_PIN_H + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_type_t mcu_pin_type; + +const mcu_pin_obj_t *validate_obj_is_pin(mp_obj_t obj); +const mcu_pin_obj_t *validate_obj_is_pin_or_none(mp_obj_t obj); +const mcu_pin_obj_t *validate_obj_is_free_pin(mp_obj_t obj); +const mcu_pin_obj_t *validate_obj_is_free_pin_or_none(mp_obj_t obj); +void validate_no_duplicate_pins(mp_obj_t seq, qstr arg_name); +void validate_no_duplicate_pins_2(mp_obj_t seq1, mp_obj_t seq2, qstr arg_name1, qstr arg_name2); +void validate_list_is_free_pins(qstr what, const mcu_pin_obj_t **pins_out, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); +void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); + +void assert_pin_free(const mcu_pin_obj_t *pin); + +bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin); +void common_hal_never_reset_pin(const mcu_pin_obj_t *pin); +void common_hal_reset_pin(const mcu_pin_obj_t *pin); +uint8_t common_hal_mcu_pin_number(const mcu_pin_obj_t *pin); +void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin); +void common_hal_mcu_pin_claim_number(uint8_t pin_no); +void common_hal_mcu_pin_reset_number(uint8_t pin_no); + +#define COMMON_HAL_MCU_NO_PIN ((uint8_t)0xff) + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER_PIN_H diff --git a/circuitpython/shared-bindings/microcontroller/Processor.c b/circuitpython/shared-bindings/microcontroller/Processor.c new file mode 100644 index 0000000..ff1b52e --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/Processor.c @@ -0,0 +1,165 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert 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 "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Processor.h" + +#include <math.h> +#include <stdint.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/objtype.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +//| class Processor: +//| """Microcontroller CPU information and control +//| +//| Usage:: +//| +//| import microcontroller +//| print(microcontroller.cpu.frequency) +//| print(microcontroller.cpu.temperature) +//| +//| Note that on chips with more than one cpu (such as the RP2040) +//| microcontroller.cpu will return the value for CPU 0. +//| To get values from other CPUs use microcontroller.cpus indexed by +//| the number of the desired cpu. i.e. +//| +//| print(microcontroller.cpus[0].temperature) +//| print(microcontroller.cpus[1].frequency)""" +//| + +//| def __init__(self) -> None: +//| """You cannot create an instance of `microcontroller.Processor`. +//| Use `microcontroller.cpu` to access the sole instance available.""" +//| ... +//| + +//| frequency: int +//| """The CPU operating frequency in Hertz. (read-only)""" +//| + +STATIC mp_obj_t mcu_processor_set_frequency(mp_obj_t self, mp_obj_t freq) { + #if CIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY + uint32_t value_of_freq = (uint32_t)mp_arg_validate_int_min(mp_obj_get_int(freq), 0, MP_QSTR_frequency); + common_hal_mcu_processor_set_frequency(self, value_of_freq); + #else + mp_raise_msg(&mp_type_NotImplementedError,translate("frequency is read-only for this board")); + #endif + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_2(mcu_processor_set_frequency_obj, mcu_processor_set_frequency); + + +STATIC mp_obj_t mcu_processor_get_frequency(mp_obj_t self) { + return mp_obj_new_int_from_uint(common_hal_mcu_processor_get_frequency()); +} + +MP_DEFINE_CONST_FUN_OBJ_1(mcu_processor_get_frequency_obj, mcu_processor_get_frequency); + +MP_PROPERTY_GETSET(mcu_processor_frequency_obj, + (mp_obj_t)&mcu_processor_get_frequency_obj, + (mp_obj_t)&mcu_processor_set_frequency_obj); + +//| reset_reason: microcontroller.ResetReason +//| """The reason the microcontroller started up from reset state.""" +//| +STATIC mp_obj_t mcu_processor_get_reset_reason(mp_obj_t self) { + return cp_enum_find(&mcu_reset_reason_type, common_hal_mcu_processor_get_reset_reason()); +} + +MP_DEFINE_CONST_FUN_OBJ_1(mcu_processor_get_reset_reason_obj, mcu_processor_get_reset_reason); + +MP_PROPERTY_GETTER(mcu_processor_reset_reason_obj, + (mp_obj_t)&mcu_processor_get_reset_reason_obj); + +//| temperature: Optional[float] +//| """The on-chip temperature, in Celsius, as a float. (read-only) +//| +//| Is `None` if the temperature is not available.""" +//| +STATIC mp_obj_t mcu_processor_get_temperature(mp_obj_t self) { + float temperature = common_hal_mcu_processor_get_temperature(); + return isnan(temperature) ? mp_const_none : mp_obj_new_float(temperature); +} + +MP_DEFINE_CONST_FUN_OBJ_1(mcu_processor_get_temperature_obj, mcu_processor_get_temperature); + +MP_PROPERTY_GETTER(mcu_processor_temperature_obj, + (mp_obj_t)&mcu_processor_get_temperature_obj); + +//| uid: bytearray +//| """The unique id (aka serial number) of the chip as a `bytearray`. (read-only)""" +//| +STATIC mp_obj_t mcu_processor_get_uid(mp_obj_t self) { + uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; + common_hal_mcu_processor_get_uid(raw_id); + return mp_obj_new_bytearray(sizeof(raw_id), raw_id); +} + +MP_DEFINE_CONST_FUN_OBJ_1(mcu_processor_get_uid_obj, mcu_processor_get_uid); + +MP_PROPERTY_GETTER(mcu_processor_uid_obj, + (mp_obj_t)&mcu_processor_get_uid_obj); + +//| voltage: Optional[float] +//| """The input voltage to the microcontroller, as a float. (read-only) +//| +//| Is `None` if the voltage is not available.""" +//| +STATIC mp_obj_t mcu_processor_get_voltage(mp_obj_t self) { + float voltage = common_hal_mcu_processor_get_voltage(); + return isnan(voltage) ? mp_const_none : mp_obj_new_float(voltage); +} + +MP_DEFINE_CONST_FUN_OBJ_1(mcu_processor_get_voltage_obj, mcu_processor_get_voltage); + +MP_PROPERTY_GETTER(mcu_processor_voltage_obj, + (mp_obj_t)&mcu_processor_get_voltage_obj); + +STATIC const mp_rom_map_elem_t mcu_processor_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&mcu_processor_frequency_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset_reason), MP_ROM_PTR(&mcu_processor_reset_reason_obj) }, + { MP_ROM_QSTR(MP_QSTR_temperature), MP_ROM_PTR(&mcu_processor_temperature_obj) }, + { MP_ROM_QSTR(MP_QSTR_uid), MP_ROM_PTR(&mcu_processor_uid_obj) }, + { MP_ROM_QSTR(MP_QSTR_voltage), MP_ROM_PTR(&mcu_processor_voltage_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mcu_processor_locals_dict, mcu_processor_locals_dict_table); + +const mp_obj_type_t mcu_processor_type = { + { &mp_type_type }, + .name = MP_QSTR_Processor, + .locals_dict = (mp_obj_dict_t *)&mcu_processor_locals_dict, +}; diff --git a/circuitpython/shared-bindings/microcontroller/Processor.h b/circuitpython/shared-bindings/microcontroller/Processor.h new file mode 100644 index 0000000..9a2f22b --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/Processor.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_MICROCONTROLLER_PROCESSOR_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER_PROCESSOR_H + +#include "py/obj.h" + +#include "common-hal/microcontroller/Processor.h" +#include "shared-bindings/microcontroller/ResetReason.h" + +extern const mp_obj_type_t mcu_processor_type; + +uint32_t common_hal_mcu_processor_get_frequency(void); +mcu_reset_reason_t common_hal_mcu_processor_get_reset_reason(void); +float common_hal_mcu_processor_get_temperature(void); +void common_hal_mcu_processor_get_uid(uint8_t raw_id[]); +float common_hal_mcu_processor_get_voltage(void); +uint32_t common_hal_mcu_processor_set_frequency(mcu_processor_obj_t *self, uint32_t frequency); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER_PROCESSOR_H diff --git a/circuitpython/shared-bindings/microcontroller/ResetReason.c b/circuitpython/shared-bindings/microcontroller/ResetReason.c new file mode 100644 index 0000000..905c19f --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/ResetReason.c @@ -0,0 +1,82 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "py/obj.h" +#include "py/enum.h" + +#include "shared-bindings/microcontroller/ResetReason.h" + +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, POWER_ON, RESET_REASON_POWER_ON); +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, BROWNOUT, RESET_REASON_BROWNOUT); +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, SOFTWARE, RESET_REASON_SOFTWARE); +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, DEEP_SLEEP_ALARM, RESET_REASON_DEEP_SLEEP_ALARM); +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, RESET_PIN, RESET_REASON_RESET_PIN); +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, WATCHDOG, RESET_REASON_WATCHDOG); +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, UNKNOWN, RESET_REASON_UNKNOWN); +MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, RESCUE_DEBUG, RESET_REASON_RESCUE_DEBUG); + +//| class ResetReason: +//| """The reason the microntroller was last reset""" +//| +//| POWER_ON: object +//| """The microntroller was started from power off.""" +//| +//| BROWNOUT: object +//| """The microntroller was reset due to too low a voltage.""" +//| +//| SOFTWARE: object +//| """The microntroller was reset from software.""" +//| +//| DEEP_SLEEP_ALARM: object +//| """The microntroller was reset for deep sleep and restarted by an alarm.""" +//| +//| RESET_PIN: object +//| """The microntroller was reset by a signal on its reset pin. The pin might be connected to a reset button.""" +//| +//| WATCHDOG: object +//| """The microcontroller was reset by its watchdog timer.""" +//| +//| UNKNOWN: object +//| """The microntroller restarted for an unknown reason.""" +//| +//| RESCUE_DEBUG: object +//| """The microntroller was reset by the rescue debug port.""" +//| +MAKE_ENUM_MAP(mcu_reset_reason) { + MAKE_ENUM_MAP_ENTRY(reset_reason, POWER_ON), + MAKE_ENUM_MAP_ENTRY(reset_reason, BROWNOUT), + MAKE_ENUM_MAP_ENTRY(reset_reason, SOFTWARE), + MAKE_ENUM_MAP_ENTRY(reset_reason, DEEP_SLEEP_ALARM), + MAKE_ENUM_MAP_ENTRY(reset_reason, RESET_PIN), + MAKE_ENUM_MAP_ENTRY(reset_reason, WATCHDOG), + MAKE_ENUM_MAP_ENTRY(reset_reason, UNKNOWN), + MAKE_ENUM_MAP_ENTRY(reset_reason, RESCUE_DEBUG), +}; +STATIC MP_DEFINE_CONST_DICT(mcu_reset_reason_locals_dict, mcu_reset_reason_locals_table); + +MAKE_PRINTER(microcontroller, mcu_reset_reason); + +MAKE_ENUM_TYPE(microcontroller, ResetReason, mcu_reset_reason); diff --git a/circuitpython/shared-bindings/microcontroller/ResetReason.h b/circuitpython/shared-bindings/microcontroller/ResetReason.h new file mode 100644 index 0000000..7abc54c --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/ResetReason.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_MCU_RESET_REASON__H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MCU_RESET_REASON__H + +#include "py/obj.h" +#include "py/enum.h" + +typedef enum { + RESET_REASON_POWER_ON, + RESET_REASON_BROWNOUT, + RESET_REASON_SOFTWARE, + RESET_REASON_DEEP_SLEEP_ALARM, + RESET_REASON_RESET_PIN, + RESET_REASON_WATCHDOG, + RESET_REASON_UNKNOWN, + RESET_REASON_RESCUE_DEBUG, +} mcu_reset_reason_t; + +extern const mp_obj_type_t mcu_reset_reason_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MCU_RESET_REASON__H diff --git a/circuitpython/shared-bindings/microcontroller/RunMode.c b/circuitpython/shared-bindings/microcontroller/RunMode.c new file mode 100644 index 0000000..477168d --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/RunMode.c @@ -0,0 +1,101 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "shared-bindings/microcontroller/RunMode.h" + +//| class RunMode: +//| """run state of the microcontroller""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define the run mode of the microcontroller and +//| CircuitPython.""" +//| +//| NORMAL: RunMode +//| """Run CircuitPython as normal. +//| +//| :type microcontroller.RunMode:""" +//| +//| SAFE_MODE: RunMode +//| """Run CircuitPython in safe mode. User code will not run and the +//| file system will be writeable over USB. +//| +//| :type microcontroller.RunMode:""" +//| +//| UF2: RunMode +//| """Run the uf2 bootloader. +//| +//| :type microcontroller.RunMode:""" +//| +//| BOOTLOADER: RunMode +//| """Run the default bootloader. +//| +//| :type microcontroller.RunMode:""" +//| +const mp_obj_type_t mcu_runmode_type; + +const mcu_runmode_obj_t mcu_runmode_uf2_obj = { + { &mcu_runmode_type }, +}; + +const mcu_runmode_obj_t mcu_runmode_normal_obj = { + { &mcu_runmode_type }, +}; + +const mcu_runmode_obj_t mcu_runmode_safe_mode_obj = { + { &mcu_runmode_type }, +}; + +const mcu_runmode_obj_t mcu_runmode_bootloader_obj = { + { &mcu_runmode_type }, +}; + +STATIC const mp_rom_map_elem_t mcu_runmode_locals_dict_table[] = { + {MP_ROM_QSTR(MP_QSTR_UF2), MP_ROM_PTR(&mcu_runmode_uf2_obj)}, + {MP_ROM_QSTR(MP_QSTR_NORMAL), MP_ROM_PTR(&mcu_runmode_normal_obj)}, + {MP_ROM_QSTR(MP_QSTR_SAFE_MODE), MP_ROM_PTR(&mcu_runmode_safe_mode_obj)}, + {MP_ROM_QSTR(MP_QSTR_BOOTLOADER), MP_ROM_PTR(&mcu_runmode_bootloader_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(mcu_runmode_locals_dict, mcu_runmode_locals_dict_table); + +STATIC void mcu_runmode_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr runmode = MP_QSTR_NORMAL; + if (self_in == MP_ROM_PTR(&mcu_runmode_uf2_obj)) { + runmode = MP_QSTR_UF2; + } else if (self_in == MP_ROM_PTR(&mcu_runmode_safe_mode_obj)) { + runmode = MP_QSTR_SAFE_MODE; + } else if (self_in == MP_ROM_PTR(&mcu_runmode_bootloader_obj)) { + runmode = MP_QSTR_BOOTLOADER; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_microcontroller, MP_QSTR_RunMode, + runmode); +} + +const mp_obj_type_t mcu_runmode_type = { + { &mp_type_type }, + .name = MP_QSTR_RunMode, + .print = mcu_runmode_print, + .locals_dict = (mp_obj_dict_t *)&mcu_runmode_locals_dict, +}; diff --git a/circuitpython/shared-bindings/microcontroller/RunMode.h b/circuitpython/shared-bindings/microcontroller/RunMode.h new file mode 100644 index 0000000..172256d --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/RunMode.h @@ -0,0 +1,50 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_MICROCONTROLLER_RUNMODE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER_RUNMODE_H + +#include "py/obj.h" + +typedef enum { + RUNMODE_UF2, + RUNMODE_NORMAL, + RUNMODE_SAFE_MODE, + RUNMODE_BOOTLOADER +} mcu_runmode_t; + +extern const mp_obj_type_t mcu_runmode_type; + +typedef struct { + mp_obj_base_t base; +} mcu_runmode_obj_t; + +extern const mcu_runmode_obj_t mcu_runmode_uf2_obj; +extern const mcu_runmode_obj_t mcu_runmode_normal_obj; +extern const mcu_runmode_obj_t mcu_runmode_safe_mode_obj; +extern const mcu_runmode_obj_t mcu_runmode_bootloader_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER_RUNMODE_H diff --git a/circuitpython/shared-bindings/microcontroller/__init__.c b/circuitpython/shared-bindings/microcontroller/__init__.c new file mode 100644 index 0000000..707080c --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/__init__.c @@ -0,0 +1,198 @@ +/* + * 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. + */ + +// Microcontroller contains pin references and microcontroller specific control +// functions. + +#include <stdint.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/microcontroller/Processor.h" + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/Processor.h" + +#include "supervisor/shared/translate.h" + +//| """Pin references and cpu functionality +//| +//| The `microcontroller` module defines the pins and other bare-metal hardware +//| from the perspective of the microcontroller. See :py:mod:`board` for +//| board-specific pin mappings.""" +//| +//| from nvm import ByteArray +//| from watchdog import WatchDogTimer +//| + +//| cpu: Processor +//| """CPU information and control, such as ``cpu.temperature`` and ``cpu.frequency`` +//| (clock frequency). +//| This object is an instance of `microcontroller.Processor`.""" +//| + +//| cpus: Processor +//| """CPU information and control, such as ``cpus[0].temperature`` and ``cpus[1].frequency`` +//| (clock frequency) on chips with more than 1 cpu. The index selects which cpu. +//| This object is an instance of `microcontroller.Processor`.""" +//| + +//| def delay_us(delay: int) -> None: +//| """Dedicated delay method used for very short delays. **Do not** do long delays +//| because this stops all other functions from completing. Think of this as an empty +//| ``while`` loop that runs for the specified ``(delay)`` time. If you have other +//| code or peripherals (e.g audio recording) that require specific timing or +//| processing while you are waiting, explore a different avenue such as using +//| `time.sleep()`.""" +//| ... +//| +STATIC mp_obj_t mcu_delay_us(mp_obj_t delay_obj) { + uint32_t delay = mp_obj_get_int(delay_obj); + + common_hal_mcu_delay_us(delay); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mcu_delay_us_obj, mcu_delay_us); + +//| def disable_interrupts() -> None: +//| """Disable all interrupts. Be very careful, this can stall everything.""" +//| ... +//| +STATIC mp_obj_t mcu_disable_interrupts(void) { + common_hal_mcu_disable_interrupts(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mcu_disable_interrupts_obj, mcu_disable_interrupts); + +//| def enable_interrupts() -> None: +//| """Enable the interrupts that were enabled at the last disable.""" +//| ... +//| +STATIC mp_obj_t mcu_enable_interrupts(void) { + common_hal_mcu_enable_interrupts(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mcu_enable_interrupts_obj, mcu_enable_interrupts); + +//| def on_next_reset(run_mode: microcontroller.RunMode) -> None: +//| """Configure the run mode used the next time the microcontroller is reset but +//| not powered down. +//| +//| :param ~microcontroller.RunMode run_mode: The next run mode""" +//| ... +//| +STATIC mp_obj_t mcu_on_next_reset(mp_obj_t run_mode_obj) { + mcu_runmode_t run_mode; + if (run_mode_obj == MP_OBJ_FROM_PTR(&mcu_runmode_uf2_obj)) { + run_mode = RUNMODE_UF2; + } else if (run_mode_obj == MP_OBJ_FROM_PTR(&mcu_runmode_normal_obj)) { + run_mode = RUNMODE_NORMAL; + } else if (run_mode_obj == MP_OBJ_FROM_PTR(&mcu_runmode_safe_mode_obj)) { + run_mode = RUNMODE_SAFE_MODE; + } else if (run_mode_obj == MP_OBJ_FROM_PTR(&mcu_runmode_bootloader_obj)) { + run_mode = RUNMODE_BOOTLOADER; + } else { + mp_raise_ValueError(translate("Invalid run mode.")); + } + common_hal_mcu_on_next_reset(run_mode); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mcu_on_next_reset_obj, mcu_on_next_reset); + +//| def reset() -> None: +//| """Reset the microcontroller. After reset, the microcontroller will enter the +//| run mode last set by `on_next_reset`. +//| +//| .. warning:: This may result in file system corruption when connected to a +//| host computer. Be very careful when calling this! Make sure the device +//| "Safely removed" on Windows or "ejected" on Mac OSX and Linux.""" +//| ... +//| +STATIC mp_obj_t mcu_reset(void) { + common_hal_mcu_reset(); + // We won't actually get here because we're resetting. + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mcu_reset_obj, mcu_reset); + +//| nvm: Optional[ByteArray] +//| """Available non-volatile memory. +//| This object is the sole instance of `nvm.ByteArray` when available or ``None`` otherwise. +//| +//| :type: nvm.ByteArray or None""" +//| + +//| watchdog: Optional[WatchDogTimer] +//| """Available watchdog timer. +//| This object is the sole instance of `watchdog.WatchDogTimer` when available or ``None`` otherwise.""" +//| + +const mp_obj_module_t mcu_pin_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mcu_pin_globals, +}; + +STATIC const mp_rom_map_elem_t mcu_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_microcontroller) }, + { MP_ROM_QSTR(MP_QSTR_cpu), MP_ROM_PTR(&common_hal_mcu_processor_obj) }, + #if CIRCUITPY_PROCESSOR_COUNT > 1 + { MP_ROM_QSTR(MP_QSTR_cpus), MP_ROM_PTR(&common_hal_multi_processor_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_delay_us), MP_ROM_PTR(&mcu_delay_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_interrupts), MP_ROM_PTR(&mcu_disable_interrupts_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_interrupts), MP_ROM_PTR(&mcu_enable_interrupts_obj) }, + { MP_ROM_QSTR(MP_QSTR_on_next_reset), MP_ROM_PTR(&mcu_on_next_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&mcu_reset_obj) }, + #if CIRCUITPY_NVM && CIRCUITPY_INTERNAL_NVM_SIZE > 0 + { MP_ROM_QSTR(MP_QSTR_nvm), MP_ROM_PTR(&common_hal_mcu_nvm_obj) }, + #else + { MP_ROM_QSTR(MP_QSTR_nvm), MP_ROM_NONE }, + #endif + #if CIRCUITPY_WATCHDOG + { MP_ROM_QSTR(MP_QSTR_watchdog), MP_ROM_PTR(&common_hal_mcu_watchdogtimer_obj) }, + #else + { MP_ROM_QSTR(MP_QSTR_watchdog), MP_ROM_NONE }, + #endif + { MP_ROM_QSTR(MP_QSTR_ResetReason), MP_ROM_PTR(&mcu_reset_reason_type) }, + { MP_ROM_QSTR(MP_QSTR_RunMode), MP_ROM_PTR(&mcu_runmode_type) }, + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&mcu_pin_type) }, + { MP_ROM_QSTR(MP_QSTR_pin), MP_ROM_PTR(&mcu_pin_module) }, + { MP_ROM_QSTR(MP_QSTR_Processor), MP_ROM_PTR(&mcu_processor_type) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(mcu_module_globals, mcu_module_globals_table); + +const mp_obj_module_t microcontroller_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mcu_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_microcontroller, microcontroller_module, CIRCUITPY_MICROCONTROLLER); diff --git a/circuitpython/shared-bindings/microcontroller/__init__.h b/circuitpython/shared-bindings/microcontroller/__init__.h new file mode 100644 index 0000000..e41cce8 --- /dev/null +++ b/circuitpython/shared-bindings/microcontroller/__init__.h @@ -0,0 +1,68 @@ +/* + * 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_MICROCONTROLLER___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER___INIT___H + +#include "py/obj.h" +#include "py/mpconfig.h" +#include "py/objtuple.h" + +#include "common-hal/microcontroller/Processor.h" +#include "shared-bindings/microcontroller/ResetReason.h" +#include "shared-bindings/microcontroller/RunMode.h" + +extern void common_hal_mcu_delay_us(uint32_t); + +extern void common_hal_mcu_disable_interrupts(void); +extern void common_hal_mcu_enable_interrupts(void); + +extern void common_hal_mcu_on_next_reset(mcu_runmode_t runmode); +extern void common_hal_mcu_reset(void); + +extern const mp_obj_dict_t mcu_pin_globals; + +#if CIRCUITPY_PROCESSOR_COUNT == 1 +extern const mcu_processor_obj_t common_hal_mcu_processor_obj; +#elif CIRCUITPY_PROCESSOR_COUNT > 1 +extern const mcu_processor_obj_t common_hal_mcu_processor_obj; +extern const mp_rom_obj_tuple_t common_hal_multi_processor_obj; +#else +#error "Invalid processor count" +#endif + + +#if CIRCUITPY_NVM && CIRCUITPY_INTERNAL_NVM_SIZE > 0 +#include "common-hal/nvm/ByteArray.h" +extern const nvm_bytearray_obj_t common_hal_mcu_nvm_obj; +#endif + +#if CIRCUITPY_WATCHDOG +#include "common-hal/watchdog/WatchDogTimer.h" +extern watchdog_watchdogtimer_obj_t common_hal_mcu_watchdogtimer_obj; +#endif + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER___INIT___H diff --git a/circuitpython/shared-bindings/msgpack/ExtType.c b/circuitpython/shared-bindings/msgpack/ExtType.c new file mode 100644 index 0000000..1719e20 --- /dev/null +++ b/circuitpython/shared-bindings/msgpack/ExtType.c @@ -0,0 +1,120 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Bernhard Boser + * + * 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 "py/runtime.h" +#include "py/smallint.h" +#include "py/objproperty.h" +#include "shared-bindings/msgpack/ExtType.h" + +//| class ExtType: +//| """ExtType represents ext type in msgpack.""" +//| def __init__(self, code: int, data: bytes) -> None: +//| """Constructor +//| :param int code: type code in range 0~127. +//| :param bytes data: representation.""" +//| +STATIC mp_obj_t mod_msgpack_exttype_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mod_msgpack_extype_obj_t *self = m_new_obj(mod_msgpack_extype_obj_t); + self->base.type = &mod_msgpack_exttype_type; + enum { ARG_code, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_code, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + 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); + + int code = args[ARG_code].u_int; + if (code < 0 || code > 127) { + mp_raise_AttributeError(translate("code outside range 0~127")); + } + self->code = code; + + mp_obj_t data = args[ARG_data].u_obj; + self->data = data; + return MP_OBJ_FROM_PTR(self); +} + + +//| code: int +//| """The type code, in range 0~127.""" +//| ... +//| +STATIC mp_obj_t mod_msgpack_exttype_get_code(mp_obj_t self_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->code); +} +MP_DEFINE_CONST_FUN_OBJ_1(mod_msgpack_exttype_get_code_obj, mod_msgpack_exttype_get_code); + +STATIC mp_obj_t mod_msgpack_exttype_set_code(mp_obj_t self_in, mp_obj_t code_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + int code = mp_obj_get_int(code_in); + if (code < 0 || code > 127) { + mp_raise_AttributeError(translate("code outside range 0~127")); + } + self->code = code; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(mod_msgpack_exttype_set_code_obj, mod_msgpack_exttype_set_code); + +MP_PROPERTY_GETSET(mod_msgpack_exttype_code_obj, + (mp_obj_t)&mod_msgpack_exttype_get_code_obj, + (mp_obj_t)&mod_msgpack_exttype_set_code_obj); + +//| data: bytes +//| """Data.""" +//| ... +//| +STATIC mp_obj_t mod_msgpack_exttype_get_data(mp_obj_t self_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + return self->data; +} +MP_DEFINE_CONST_FUN_OBJ_1(mod_msgpack_exttype_get_data_obj, mod_msgpack_exttype_get_data); + +STATIC mp_obj_t mod_msgpack_exttype_set_data(mp_obj_t self_in, mp_obj_t data_in) { + mod_msgpack_extype_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->data = data_in; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(mod_msgpack_exttype_set_data_obj, mod_msgpack_exttype_set_data); + +MP_PROPERTY_GETSET(mod_msgpack_exttype_data_obj, + (mp_obj_t)&mod_msgpack_exttype_get_data_obj, + (mp_obj_t)&mod_msgpack_exttype_set_data_obj); + +STATIC mp_rom_map_elem_t mod_msgpack_exttype_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_code), MP_ROM_PTR(&mod_msgpack_exttype_code_obj) }, + { MP_ROM_QSTR(MP_QSTR_data), MP_ROM_PTR(&mod_msgpack_exttype_data_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(mod_msgpack_exttype_locals_dict, mod_msgpack_exttype_locals_dict_table); + +const mp_obj_type_t mod_msgpack_exttype_type = { + { &mp_type_type }, + .name = MP_QSTR_ExtType, + .make_new = mod_msgpack_exttype_make_new, + .locals_dict = (mp_obj_dict_t *)&mod_msgpack_exttype_locals_dict, +}; diff --git a/circuitpython/shared-bindings/msgpack/ExtType.h b/circuitpython/shared-bindings/msgpack/ExtType.h new file mode 100644 index 0000000..64173b2 --- /dev/null +++ b/circuitpython/shared-bindings/msgpack/ExtType.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Bernhard Boser + * + * 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_MSGPACK_EXTTYPE___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK_EXTTYPE___INIT___H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + int32_t code; + mp_obj_t data; +} mod_msgpack_extype_obj_t; + +extern const mp_obj_type_t mod_msgpack_exttype_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK_EXTTYPE___INIT___H diff --git a/circuitpython/shared-bindings/msgpack/__init__.c b/circuitpython/shared-bindings/msgpack/__init__.c new file mode 100644 index 0000000..65374fb --- /dev/null +++ b/circuitpython/shared-bindings/msgpack/__init__.c @@ -0,0 +1,164 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Bernhard Boser + * + * 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 <stdio.h> +#include "py/obj.h" +#include "py/runtime.h" +#include "shared-bindings/msgpack/__init__.h" +#include "shared-module/msgpack/__init__.h" +#include "shared-bindings/msgpack/ExtType.h" + +#define MP_OBJ_IS_METH(o) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->name == MP_QSTR_bound_method)) + +//| """Pack object in msgpack format +//| +//| The msgpack format is similar to json, except that the encoded data is binary. +//| See https://msgpack.org for details. The module implements a subset of the cpython +//| module msgpack-python. +//| +//| Not implemented: 64-bit int, uint, float. +//| +//| Example 1:: +//| +//| import msgpack +//| from io import BytesIO +//| +//| b = BytesIO() +//| msgpack.pack({'list': [True, False, None, 1, 3.14], 'str': 'blah'}, b) +//| b.seek(0) +//| print(msgpack.unpack(b)) +//| +//| Example 2: handling objects:: +//| +//| from msgpack import pack, unpack, ExtType +//| from io import BytesIO +//| +//| class MyClass: +//| def __init__(self, val): +//| self.value = val +//| def __str__(self): +//| return str(self.value) +//| +//| data = MyClass(b'my_value') +//| +//| def encoder(obj): +//| if isinstance(obj, MyClass): +//| return ExtType(1, obj.value) +//| return f"no encoder for {obj}" +//| +//| def decoder(code, data): +//| if code == 1: +//| return MyClass(data) +//| return f"no decoder for type {code}" +//| +//| buffer = BytesIO() +//| pack(data, buffer, default=encoder) +//| buffer.seek(0) +//| decoded = unpack(buffer, ext_hook=decoder) +//| print(f"{data} -> {buffer.getvalue()} -> {decoded}") +//| +//| """ +//| + +//| def pack(obj: object, stream: circuitpython_typing.ByteStream, *, default: Union[Callable[[object], None], None] = None) -> None: +//| """Output object to stream in msgpack format. +//| +//| :param object obj: Object to convert to msgpack format. +//| :param ~circuitpython_typing.ByteStream stream: stream to write to +//| :param Optional[~circuitpython_typing.Callable[[object], None]] default: +//| function called for python objects that do not have +//| a representation in msgpack format. +//| """ +//| ... +//| +STATIC mp_obj_t mod_msgpack_pack(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_obj, ARG_buffer, ARG_default }; + STATIC const mp_arg_t allowed_args[] = { + { MP_QSTR_obj, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_stream, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_default, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t handler = args[ARG_default].u_obj; + if (handler != mp_const_none && !mp_obj_is_fun(handler) && !MP_OBJ_IS_METH(handler)) { + mp_raise_ValueError(translate("default is not a function")); + } + + common_hal_msgpack_pack(args[ARG_obj].u_obj, args[ARG_buffer].u_obj, handler); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mod_msgpack_pack_obj, 0, mod_msgpack_pack); + + +//| def unpack(stream: circuitpython_typing.ByteStream, *, ext_hook: Union[Callable[[int, bytes], object], None] = None, use_list: bool=True) -> object: +//| """Unpack and return one object from stream. +//| +//| :param ~circuitpython_typing.ByteStream stream: stream to read from +//| :param Optional[~circuitpython_typing.Callable[[int, bytes], object]] ext_hook: function called for objects in +//| msgpack ext format. +//| :param Optional[bool] use_list: return array as list or tuple (use_list=False). +//| +//| :return object: object read from stream. +//| """ +//| ... +//| +STATIC mp_obj_t mod_msgpack_unpack(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buffer, ARG_ext_hook, ARG_use_list }; + STATIC const mp_arg_t allowed_args[] = { + { MP_QSTR_stream, MP_ARG_REQUIRED | MP_ARG_OBJ, }, + { MP_QSTR_ext_hook, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_obj = mp_const_none } }, + { MP_QSTR_use_list, MP_ARG_KW_ONLY | MP_ARG_BOOL, { .u_bool = true } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t hook = args[ARG_ext_hook].u_obj; + if (hook != mp_const_none && !mp_obj_is_fun(hook) && !MP_OBJ_IS_METH(hook)) { + mp_raise_ValueError(translate("ext_hook is not a function")); + } + + return common_hal_msgpack_unpack(args[ARG_buffer].u_obj, hook, args[ARG_use_list].u_bool); +} +MP_DEFINE_CONST_FUN_OBJ_KW(mod_msgpack_unpack_obj, 0, mod_msgpack_unpack); + + +STATIC const mp_rom_map_elem_t msgpack_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_msgpack) }, + { MP_ROM_QSTR(MP_QSTR_ExtType), MP_ROM_PTR(&mod_msgpack_exttype_type) }, + { MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&mod_msgpack_pack_obj) }, + { MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&mod_msgpack_unpack_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(msgpack_module_globals, msgpack_module_globals_table); + +const mp_obj_module_t msgpack_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&msgpack_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_msgpack, msgpack_module, CIRCUITPY_MSGPACK); diff --git a/circuitpython/shared-bindings/msgpack/__init__.h b/circuitpython/shared-bindings/msgpack/__init__.h new file mode 100644 index 0000000..a02ead0 --- /dev/null +++ b/circuitpython/shared-bindings/msgpack/__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 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_MSGPACK___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK___INIT___H + +#include "py/obj.h" + +// nothing for now + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MSGPACK___INIT___H diff --git a/circuitpython/shared-bindings/multiterminal/__init__.c b/circuitpython/shared-bindings/multiterminal/__init__.c new file mode 100644 index 0000000..f3f8d1a --- /dev/null +++ b/circuitpython/shared-bindings/multiterminal/__init__.c @@ -0,0 +1,110 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "shared-bindings/multiterminal/__init__.h" + +#include "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +//| """Manage additional terminal sources +//| +//| The `multiterminal` module allows you to configure an additional serial +//| terminal source. Incoming characters are accepted from both the internal +//| serial connection and the optional secondary connection.""" +//| + +//| def get_secondary_terminal() -> Optional[typing.BinaryIO]: +//| """Returns the current secondary terminal.""" +//| ... +//| +STATIC mp_obj_t multiterminal_obj_get_secondary_terminal() { + return common_hal_multiterminal_get_secondary_terminal(); +} +MP_DEFINE_CONST_FUN_OBJ_0(multiterminal_get_secondary_terminal_obj, multiterminal_obj_get_secondary_terminal); + +//| def set_secondary_terminal(stream: typing.BinaryIO) -> None: +//| """Read additional input from the given stream and write out back to it. +//| This doesn't replace the core stream (usually UART or native USB) but is +//| mixed in instead. +//| +//| :param stream stream: secondary stream""" +//| ... +//| +STATIC mp_obj_t multiterminal_obj_set_secondary_terminal(mp_obj_t secondary_terminal) { + mp_obj_t write_m[3]; + mp_load_method_maybe(secondary_terminal, MP_QSTR_write, write_m); + mp_obj_t readinto_m[3]; + mp_load_method_maybe(secondary_terminal, MP_QSTR_readinto, readinto_m); + if (write_m[0] == MP_OBJ_NULL || readinto_m[0] == MP_OBJ_NULL) { + mp_raise_ValueError(translate("Stream missing readinto() or write() method.")); + return mp_const_none; + } + common_hal_multiterminal_set_secondary_terminal(secondary_terminal); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(multiterminal_set_secondary_terminal_obj, multiterminal_obj_set_secondary_terminal); + +//| def clear_secondary_terminal() -> None: +//| """Clears the secondary terminal.""" +//| ... +//| +STATIC mp_obj_t multiterminal_obj_clear_secondary_terminal() { + common_hal_multiterminal_clear_secondary_terminal(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(multiterminal_clear_secondary_terminal_obj, multiterminal_obj_clear_secondary_terminal); + +//| def schedule_secondary_terminal_read(socket: socket.socket) -> None: +//| """In cases where the underlying OS is doing task scheduling, this notifies +//| the OS when more data is available on the socket to read. This is useful +//| as a callback for lwip sockets.""" +//| ... +//| +// TODO(tannewt): This is a funny API. Replace it with a direct call into the OS +// by the lwip object. +STATIC mp_obj_t multiterminal_obj_schedule_secondary_terminal_read(mp_obj_t socket) { + common_hal_multiterminal_schedule_secondary_terminal_read(socket); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(multiterminal_schedule_secondary_terminal_read_obj, multiterminal_obj_schedule_secondary_terminal_read); + +// TODO(tannewt): Expose the internal serial connection as `primary_terminal` + +STATIC const mp_rom_map_elem_t multiterminal_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_multiterminal) }, + { MP_ROM_QSTR(MP_QSTR_get_secondary_terminal), MP_ROM_PTR(&multiterminal_get_secondary_terminal_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_secondary_terminal), MP_ROM_PTR(&multiterminal_set_secondary_terminal_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_secondary_terminal), MP_ROM_PTR(&multiterminal_clear_secondary_terminal_obj) }, + { MP_ROM_QSTR(MP_QSTR_schedule_secondary_terminal_read), MP_ROM_PTR(&multiterminal_schedule_secondary_terminal_read_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(multiterminal_module_globals, multiterminal_module_globals_table); + +const mp_obj_module_t multiterminal_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&multiterminal_module_globals, +}; diff --git a/circuitpython/shared-bindings/multiterminal/__init__.h b/circuitpython/shared-bindings/multiterminal/__init__.h new file mode 100644 index 0000000..7c28842 --- /dev/null +++ b/circuitpython/shared-bindings/multiterminal/__init__.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 SHARED_BINDINGS_MULTITERMINAL___INIT___H +#define SHARED_BINDINGS_MULTITERMINAL___INIT___H + +#include "py/obj.h" + +void common_hal_multiterminal_schedule_secondary_terminal_read(mp_obj_t socket); +mp_obj_t common_hal_multiterminal_get_secondary_terminal(); +void common_hal_multiterminal_set_secondary_terminal(mp_obj_t secondary_terminal); +void common_hal_multiterminal_clear_secondary_terminal(); + +#endif // SHARED_BINDINGS_MULTITERMINAL___INIT___H diff --git a/circuitpython/shared-bindings/neopixel_write/__init__.c b/circuitpython/shared-bindings/neopixel_write/__init__.c new file mode 100644 index 0000000..d0234fe --- /dev/null +++ b/circuitpython/shared-bindings/neopixel_write/__init__.c @@ -0,0 +1,145 @@ +/* + * 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 "shared-bindings/neopixel_write/__init__.h" + +#include "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +// RGB LED timing information: + +// From the WS2811 datasheet: high speed mode +// - T0H 0 code,high voltage time 0.25 us +-150ns +// - T1H 1 code,high voltage time 0.6 us +-150ns +// - T0L 0 code,low voltage time 1.0 us +-150ns +// - T1L 1 code,low voltage time 0.65 us +-150ns +// - RES low voltage time Above 50us + +// From the SK6812 datasheet: +// - T0H 0 code, high level time 0.3us +-0.15us +// - T1H 1 code, high level time 0.6us +-0.15us +// - T0L 0 code, low level time 0.9us +-0.15us +// - T1L 1 code, low level time 0.6us +-0.15us +// - Trst Reset code,low level time 80us + +// From the WS2812 datasheet: +// - T0H 0 code, high voltage time 0.35us +-150ns +// - T1H 1 code, high voltage time 0.7us +-150ns +// - T0L 0 code, low voltage time 0.8us +-150ns +// - T1L 1 code, low voltage time 0.6us +-150ns +// - RES low voltage time Above 50us + +// From the WS28212B datasheet: +// - T0H 0 code, high voltage time 0.4us +-150ns +// - T1H 1 code, high voltage time 0.8us +-150ns +// - T0L 0 code, low voltage time 0.85us +-150ns +// - T1L 1 code, low voltage time 0.45us +-150ns +// - RES low voltage time Above 50us + +// The timings used in various ports do not always follow the guidelines above. +// In general, a zero bit is about 300ns high, 900ns low. +// A one bit is about 700ns high, 500ns low. +// But the ports vary based on implementation considerations; the proof is in the testing. +// https://adafru.it/5225 is more sensitive to timing and should be included in testing. + +STATIC void check_for_deinit(digitalio_digitalinout_obj_t *self) { + if (common_hal_digitalio_digitalinout_deinited(self)) { + raise_deinited_error(); + } +} + +//| """Low-level neopixel implementation +//| +//| The `neopixel_write` module contains a helper method to write out bytes in +//| the 800khz neopixel protocol. +//| +//| For example, to turn off a single neopixel (like the status pixel on Express +//| boards.) +//| +//| .. code-block:: python +//| +//| import board +//| import neopixel_write +//| import digitalio +//| +//| pin = digitalio.DigitalInOut(board.NEOPIXEL) +//| pin.direction = digitalio.Direction.OUTPUT +//| pixel_off = bytearray([0, 0, 0]) +//| neopixel_write.neopixel_write(pin, pixel_off) +//| +//| .. note:: +//| +//| This module is typically not used by user level code. +//| +//| For more information on actually using NeoPixels, refer to the `CircuitPython +//| Essentials Learn guide <https://learn.adafruit.com/circuitpython-essentials/circuitpython-neopixel>`_ +//| +//| For a much more thorough guide about using NeoPixels, refer to the `Adafruit NeoPixel Überguide +//| <https://learn.adafruit.com/adafruit-neopixel-uberguide>`_. +//| +//| """ +//| +//| def neopixel_write(digitalinout: digitalio.DigitalInOut, buf: ReadableBuffer) -> None: +//| """Write buf out on the given DigitalInOut. +//| +//| :param ~digitalio.DigitalInOut digitalinout: the DigitalInOut to output with +//| :param ~circuitpython_typing.ReadableBuffer buf: The bytes to clock out. No assumption is made about color order""" +//| ... +STATIC mp_obj_t neopixel_write_neopixel_write_(mp_obj_t digitalinout_obj, mp_obj_t buf) { + if (!mp_obj_is_type(digitalinout_obj, &digitalio_digitalinout_type)) { + mp_raise_TypeError_varg(translate("Expected a %q"), digitalio_digitalinout_type.name); + } + + // Convert parameters into expected types. + const digitalio_digitalinout_obj_t *digitalinout = MP_OBJ_TO_PTR(digitalinout_obj); + + // Check to see if the NeoPixel has been deinited before writing to it. + check_for_deinit(digitalinout_obj); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); + // Call platform's neopixel write function with provided buffer and options. + common_hal_neopixel_write(digitalinout, (uint8_t *)bufinfo.buf, bufinfo.len); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(neopixel_write_neopixel_write_obj, neopixel_write_neopixel_write_); + +STATIC const mp_rom_map_elem_t neopixel_write_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write), (mp_obj_t)&neopixel_write_neopixel_write_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(neopixel_write_module_globals, neopixel_write_module_globals_table); + +const mp_obj_module_t neopixel_write_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&neopixel_write_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_neopixel_write, neopixel_write_module, CIRCUITPY_NEOPIXEL_WRITE); diff --git a/circuitpython/shared-bindings/neopixel_write/__init__.h b/circuitpython/shared-bindings/neopixel_write/__init__.h new file mode 100644 index 0000000..89b087d --- /dev/null +++ b/circuitpython/shared-bindings/neopixel_write/__init__.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_NEOPIXEL_WRITE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_NEOPIXEL_WRITE_H + +#include <stdint.h> +#include <stdbool.h> + +#include "common-hal/digitalio/DigitalInOut.h" + +extern void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *gpio, uint8_t *pixels, uint32_t numBytes); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_NEOPIXEL_WRITE_H diff --git a/circuitpython/shared-bindings/nvm/ByteArray.c b/circuitpython/shared-bindings/nvm/ByteArray.c new file mode 100644 index 0000000..6e34b76 --- /dev/null +++ b/circuitpython/shared-bindings/nvm/ByteArray.c @@ -0,0 +1,178 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/nvm/ByteArray.h" +#include "supervisor/shared/translate.h" + +//| class ByteArray: +//| r"""Presents a stretch of non-volatile memory as a bytearray. +//| +//| Non-volatile memory is available as a byte array that persists over reloads +//| and power cycles. Each assignment causes an erase and write cycle so its recommended to assign +//| all values to change at once. +//| +//| Usage:: +//| +//| import microcontroller +//| microcontroller.nvm[0:3] = b"\xcc\x10\x00" +//| """ +//| + +//| def __init__(self) -> None: +//| """Not currently dynamically supported. Access the sole instance through `microcontroller.nvm`.""" +//| ... +//| + +//| def __bool__(self) -> bool: +//| ... +//| +//| def __len__(self) -> int: +//| """Return the length. This is used by (`len`)""" +//| ... +//| +STATIC mp_obj_t nvm_bytearray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + nvm_bytearray_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint16_t len = common_hal_nvm_bytearray_get_length(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC const mp_rom_map_elem_t nvm_bytearray_locals_dict_table[] = { +}; + +STATIC MP_DEFINE_CONST_DICT(nvm_bytearray_locals_dict, nvm_bytearray_locals_dict_table); + +//| @overload +//| def __getitem__(self, index: slice) -> bytearray: ... +//| @overload +//| def __getitem__(self, index: int) -> int: +//| """Returns the value at the given index.""" +//| ... +//| +//| @overload +//| def __setitem__(self, index: slice, value: ReadableBuffer) -> None: ... +//| @overload +//| def __setitem__(self, index: int, value: int) -> None: +//| """Set the value at the given index.""" +//| ... +//| +STATIC mp_obj_t nvm_bytearray_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete item + // slice deletion + return MP_OBJ_NULL; // op not supported + } else { + nvm_bytearray_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (0) { + #if MICROPY_PY_BUILTINS_SLICE + } else if (mp_obj_is_type(index_in, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(common_hal_nvm_bytearray_get_length(self), index_in, &slice)) { + mp_raise_NotImplementedError(translate("only slices with step=1 (aka None) are supported")); + } + if (value != MP_OBJ_SENTINEL) { + #if MICROPY_PY_ARRAY_SLICE_ASSIGN + // Assign + size_t src_len = slice.stop - slice.start; + uint8_t *src_items; + if (mp_obj_is_type(value, &mp_type_array) || + mp_obj_is_type(value, &mp_type_bytearray) || + mp_obj_is_type(value, &mp_type_memoryview) || + mp_obj_is_type(value, &mp_type_bytes)) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != src_len) { + mp_raise_ValueError(translate("Slice and value different lengths.")); + } + src_len = bufinfo.len; + src_items = bufinfo.buf; + if (1 != mp_binary_get_size('@', bufinfo.typecode, NULL)) { + mp_raise_ValueError(translate("Array values should be single bytes.")); + } + } else { + mp_raise_NotImplementedError(translate("array/bytes required on right side")); + } + + if (!common_hal_nvm_bytearray_set_bytes(self, slice.start, src_items, src_len)) { + mp_raise_RuntimeError(translate("Unable to write to nvm.")); + } + return mp_const_none; + #else + return MP_OBJ_NULL; // op not supported + #endif + } else { + // Read slice. + size_t len = slice.stop - slice.start; + uint8_t *items = m_new(uint8_t, len); + common_hal_nvm_bytearray_get_bytes(self, slice.start, len, items); + return mp_obj_new_bytearray_by_ref(len, items); + } + #endif + } else { + // Single index rather than slice. + size_t index = mp_get_index(self->base.type, common_hal_nvm_bytearray_get_length(self), + index_in, false); + if (value == MP_OBJ_SENTINEL) { + // load + uint8_t value_out; + common_hal_nvm_bytearray_get_bytes(self, index, 1, &value_out); + return MP_OBJ_NEW_SMALL_INT(value_out); + } else { + // store + mp_int_t byte_value = mp_obj_get_int(value); + if (byte_value > 0xff || byte_value < 0) { + mp_raise_ValueError(translate("Bytes must be between 0 and 255.")); + } + uint8_t short_value = byte_value; + if (!common_hal_nvm_bytearray_set_bytes(self, index, &short_value, 1)) { + mp_raise_RuntimeError(translate("Unable to write to nvm.")); + } + return mp_const_none; + } + } + } +} + +const mp_obj_type_t nvm_bytearray_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_ByteArray, + .locals_dict = (mp_obj_t)&nvm_bytearray_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = nvm_bytearray_subscr, + .unary_op = nvm_bytearray_unary_op, + ), +}; diff --git a/circuitpython/shared-bindings/nvm/ByteArray.h b/circuitpython/shared-bindings/nvm/ByteArray.h new file mode 100644 index 0000000..c0446b5 --- /dev/null +++ b/circuitpython/shared-bindings/nvm/ByteArray.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_NVM_BYTEARRAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_NVM_BYTEARRAY_H + +#include "common-hal/nvm/ByteArray.h" + +extern const mp_obj_type_t nvm_bytearray_type; + +uint32_t common_hal_nvm_bytearray_get_length(const nvm_bytearray_obj_t *self); + +bool common_hal_nvm_bytearray_set_bytes(const nvm_bytearray_obj_t *self, + uint32_t start_index, uint8_t *values, uint32_t len); +// len and values are intentionally swapped to signify values is an output and +// also leverage the compiler to validate uses are expected. +void common_hal_nvm_bytearray_get_bytes(const nvm_bytearray_obj_t *self, + uint32_t start_index, uint32_t len, uint8_t *values); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_NVM_BYTEARRAY_H diff --git a/circuitpython/shared-bindings/nvm/__init__.c b/circuitpython/shared-bindings/nvm/__init__.c new file mode 100644 index 0000000..dffc4cd --- /dev/null +++ b/circuitpython/shared-bindings/nvm/__init__.c @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "shared-bindings/nvm/__init__.h" +#include "shared-bindings/nvm/ByteArray.h" + +//| """Non-volatile memory +//| +//| The `nvm` module allows you to store whatever raw bytes you wish in a +//| reserved section non-volatile memory. +//| +//| Note that this module can't be imported and used directly. The sole +//| instance of :class:`ByteArray` is available at +//| :attr:`microcontroller.nvm`.""" +//| +STATIC const mp_rom_map_elem_t nvm_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_nvm) }, + { MP_ROM_QSTR(MP_QSTR_ByteArray), MP_ROM_PTR(&nvm_bytearray_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(nvm_module_globals, nvm_module_globals_table); + +const mp_obj_module_t nvm_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&nvm_module_globals, +}; diff --git a/circuitpython/shared-bindings/nvm/__init__.h b/circuitpython/shared-bindings/nvm/__init__.h new file mode 100644 index 0000000..9e17999 --- /dev/null +++ b/circuitpython/shared-bindings/nvm/__init__.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 SHARED_BINDINGS_NVM_H +#define SHARED_BINDINGS_NVM_H + +#endif // SHARED_BINDINGS_NVM_H diff --git a/circuitpython/shared-bindings/onewireio/OneWire.c b/circuitpython/shared-bindings/onewireio/OneWire.c new file mode 100644 index 0000000..a167c86 --- /dev/null +++ b/circuitpython/shared-bindings/onewireio/OneWire.c @@ -0,0 +1,170 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/onewireio/OneWire.h" +#include "shared-bindings/util.h" + +//| class OneWire: +//| def __init__(self, pin: microcontroller.Pin) -> None: +//| """Create a OneWire object associated with the given pin. +//| +//| The object implements the lowest level timing-sensitive bits of the protocol. +//| +//| :param ~microcontroller.Pin pin: Pin connected to the OneWire bus +//| +//| .. note:: The OneWire class is available on `busio` and `bitbangio` in CircuitPython +//| 7.x for backwards compatibility but will be removed in CircuitPython 8.0.0. +//| +//| Read a short series of pulses:: +//| +//| import onewireio +//| import board +//| +//| onewire = onewireio.OneWire(board.D7) +//| onewire.reset() +//| onewire.write_bit(True) +//| onewire.write_bit(False) +//| print(onewire.read_bit())""" +//| ... +//| +STATIC mp_obj_t onewireio_onewire_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pin }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + 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 *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + + onewireio_onewire_obj_t *self = m_new_obj(onewireio_onewire_obj_t); + self->base.type = &onewireio_onewire_type; + + common_hal_onewireio_onewire_construct(self, pin); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialize the OneWire bus and release any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t onewireio_onewire_deinit(mp_obj_t self_in) { + onewireio_onewire_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_onewireio_onewire_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewireio_onewire_deinit_obj, onewireio_onewire_deinit); + +STATIC void check_for_deinit(onewireio_onewire_obj_t *self) { + if (common_hal_onewireio_onewire_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> OneWire: +//| """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 onewireio_onewire_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_onewireio_onewire_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(onewireio_onewire___exit___obj, 4, 4, onewireio_onewire_obj___exit__); + +//| def reset(self) -> bool: +//| """Reset the OneWire bus and read presence +//| +//| :returns: False when at least one device is present +//| :rtype: bool""" +//| ... +//| +STATIC mp_obj_t onewireio_onewire_obj_reset(mp_obj_t self_in) { + onewireio_onewire_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return mp_obj_new_bool(common_hal_onewireio_onewire_reset(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(onewireio_onewire_reset_obj, onewireio_onewire_obj_reset); + +//| def read_bit(self) -> bool: +//| """Read in a bit +//| +//| :returns: bit state read +//| :rtype: bool""" +//| ... +//| +STATIC mp_obj_t onewireio_onewire_obj_read_bit(mp_obj_t self_in) { + onewireio_onewire_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return mp_obj_new_bool(common_hal_onewireio_onewire_read_bit(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(onewireio_onewire_read_bit_obj, onewireio_onewire_obj_read_bit); + +//| def write_bit(self, value: bool) -> None: +//| """Write out a bit based on value.""" +//| ... +//| +STATIC mp_obj_t onewireio_onewire_obj_write_bit(mp_obj_t self_in, mp_obj_t bool_obj) { + onewireio_onewire_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_onewireio_onewire_write_bit(self, mp_obj_is_true(bool_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(onewireio_onewire_write_bit_obj, onewireio_onewire_obj_write_bit); + +STATIC const mp_rom_map_elem_t onewireio_onewire_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&onewireio_onewire_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&onewireio_onewire___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&onewireio_onewire_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_read_bit), MP_ROM_PTR(&onewireio_onewire_read_bit_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_bit), MP_ROM_PTR(&onewireio_onewire_write_bit_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(onewireio_onewire_locals_dict, onewireio_onewire_locals_dict_table); + +const mp_obj_type_t onewireio_onewire_type = { + { &mp_type_type }, + .name = MP_QSTR_OneWire, + .make_new = onewireio_onewire_make_new, + .locals_dict = (mp_obj_dict_t *)&onewireio_onewire_locals_dict, +}; diff --git a/circuitpython/shared-bindings/onewireio/OneWire.h b/circuitpython/shared-bindings/onewireio/OneWire.h new file mode 100644 index 0000000..c6d0fd6 --- /dev/null +++ b/circuitpython/shared-bindings/onewireio/OneWire.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_ONEWIREIO_ONEWIRE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ONEWIREIO_ONEWIRE_H + +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/onewireio/OneWire.h" + +extern const mp_obj_type_t onewireio_onewire_type; + +extern void common_hal_onewireio_onewire_construct(onewireio_onewire_obj_t *self, + const mcu_pin_obj_t *pin); +extern void common_hal_onewireio_onewire_deinit(onewireio_onewire_obj_t *self); +extern bool common_hal_onewireio_onewire_deinited(onewireio_onewire_obj_t *self); +extern bool common_hal_onewireio_onewire_reset(onewireio_onewire_obj_t *self); +extern bool common_hal_onewireio_onewire_read_bit(onewireio_onewire_obj_t *self); +extern void common_hal_onewireio_onewire_write_bit(onewireio_onewire_obj_t *self, bool bit); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ONEWIREIO_ONEWIRE_H diff --git a/circuitpython/shared-bindings/onewireio/__init__.c b/circuitpython/shared-bindings/onewireio/__init__.c new file mode 100644 index 0000000..45d78b2 --- /dev/null +++ b/circuitpython/shared-bindings/onewireio/__init__.c @@ -0,0 +1,55 @@ +/* + * 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/onewireio/__init__.h" +#include "shared-bindings/onewireio/OneWire.h" + +#include "py/runtime.h" + +//| """Low-level bit primitives for Maxim (formerly Dallas Semi) one-wire protocol. +//| +//| Protocol definition is here: https://www.maximintegrated.com/en/app-notes/index.mvp/id/126""" +//| + +STATIC const mp_rom_map_elem_t onewireio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_onewireio) }, + { MP_ROM_QSTR(MP_QSTR_OneWire), MP_ROM_PTR(&onewireio_onewire_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(onewireio_module_globals, onewireio_module_globals_table); + +const mp_obj_module_t onewireio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&onewireio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_onewireio, onewireio_module, CIRCUITPY_ONEWIREIO); diff --git a/circuitpython/shared-bindings/onewireio/__init__.h b/circuitpython/shared-bindings/onewireio/__init__.h new file mode 100644 index 0000000..25384f6 --- /dev/null +++ b/circuitpython/shared-bindings/onewireio/__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_ONEWIREIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ONEWIREIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ONEWIREIO___INIT___H diff --git a/circuitpython/shared-bindings/os/__init__.c b/circuitpython/shared-bindings/os/__init__.c new file mode 100644 index 0000000..2daca52 --- /dev/null +++ b/circuitpython/shared-bindings/os/__init__.c @@ -0,0 +1,250 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Josef Gajdusek + * 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 <string.h> + +#include "extmod/vfs.h" +#include "lib/oofatfs/ff.h" +#include "lib/oofatfs/diskio.h" +#include "py/mpstate.h" +#include "py/obj.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/os/__init__.h" + +//| """functions that an OS normally provides +//| +//| |see_cpython_module| :mod:`cpython:os`. +//| """ +//| +//| import typing + +//| def uname() -> _Uname: +//| """Returns a named tuple of operating specific and CircuitPython port +//| specific information.""" +//| ... +//| +//| class _Uname(typing.NamedTuple): +//| """The type of values that :py:func:`.uname()` returns""" +//| +//| sysname: str +//| nodename: str +//| release: str +//| version: str +//| machine: str +//| +STATIC mp_obj_t os_uname(void) { + return common_hal_os_uname(); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); + +//| def chdir(path: str) -> None: +//| """Change current directory.""" +//| ... +//| +STATIC mp_obj_t os_chdir(mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + common_hal_os_chdir(path); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(os_chdir_obj, os_chdir); + +//| def getcwd() -> str: +//| """Get the current directory.""" +//| ... +//| +STATIC mp_obj_t os_getcwd(void) { + return common_hal_os_getcwd(); +} +MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); + +//| def listdir(dir: str) -> str: +//| """With no argument, list the current directory. Otherwise list the given directory.""" +//| ... +//| +STATIC mp_obj_t os_listdir(size_t n_args, const mp_obj_t *args) { + const char *path; + if (n_args == 1) { + path = mp_obj_str_get_str(args[0]); + } else { + path = mp_obj_str_get_str(common_hal_os_getcwd()); + } + return common_hal_os_listdir(path); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(os_listdir_obj, 0, 1, os_listdir); + +//| def mkdir(path: str) -> None: +//| """Create a new directory.""" +//| ... +//| +STATIC mp_obj_t os_mkdir(mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + common_hal_os_mkdir(path); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(os_mkdir_obj, os_mkdir); + +//| def remove(path: str) -> None: +//| """Remove a file.""" +//| ... +//| +STATIC mp_obj_t os_remove(mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + common_hal_os_remove(path); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(os_remove_obj, os_remove); + +//| def rmdir(path: str) -> None: +//| """Remove a directory.""" +//| ... +//| +STATIC mp_obj_t os_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) { + const char *old_path = mp_obj_str_get_str(old_path_in); + const char *new_path = mp_obj_str_get_str(new_path_in); + common_hal_os_rename(old_path, new_path); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(os_rename_obj, os_rename); + +//| def rename(old_path: str, new_path: str) -> str: +//| """Rename a file.""" +//| ... +//| +STATIC mp_obj_t os_rmdir(mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + common_hal_os_rmdir(path); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(os_rmdir_obj, os_rmdir); + +//| def stat(path: str) -> Tuple[int, int, int, int, int, int, int, int, int, int]: +//| """Get the status of a file or directory. +//| +//| .. note:: On builds without long integers, the number of seconds +//| for contemporary dates will not fit in a small integer. +//| So the time fields return 946684800, +//| which is the number of seconds corresponding to 1999-12-31.""" +//| ... +//| +STATIC mp_obj_t os_stat(mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + return common_hal_os_stat(path); +} +MP_DEFINE_CONST_FUN_OBJ_1(os_stat_obj, os_stat); + +//| def statvfs(path: str) -> Tuple[int, int, int, int, int, int, int, int, int, int]: +//| """Get the status of a filesystem. +//| +//| Returns a tuple with the filesystem information in the following order: +//| +//| * ``f_bsize`` -- file system block size +//| * ``f_frsize`` -- fragment size +//| * ``f_blocks`` -- size of fs in f_frsize units +//| * ``f_bfree`` -- number of free blocks +//| * ``f_bavail`` -- number of free blocks for unprivileged users +//| * ``f_files`` -- number of inodes +//| * ``f_ffree`` -- number of free inodes +//| * ``f_favail`` -- number of free inodes for unprivileged users +//| * ``f_flag`` -- mount flags +//| * ``f_namemax`` -- maximum filename length +//| +//| Parameters related to inodes: ``f_files``, ``f_ffree``, ``f_avail`` +//| and the ``f_flags`` parameter may return ``0`` as they can be unavailable +//| in a port-specific implementation.""" +//| ... +//| +STATIC mp_obj_t os_statvfs(mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + return common_hal_os_statvfs(path); +} +MP_DEFINE_CONST_FUN_OBJ_1(os_statvfs_obj, os_statvfs); + +//| def sync() -> None: +//| """Sync all filesystems.""" +//| ... +//| +STATIC mp_obj_t os_sync(void) { + for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { + // this assumes that vfs->obj is fs_user_mount_t with block device functions + disk_ioctl(MP_OBJ_TO_PTR(vfs->obj), CTRL_SYNC, NULL); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(os_sync_obj, os_sync); + +//| def urandom(size: int) -> str: +//| """Returns a string of *size* random bytes based on a hardware True Random +//| Number Generator. When not available, it will raise a NotImplementedError.""" +//| ... +//| +STATIC mp_obj_t os_urandom(mp_obj_t size_in) { + mp_int_t size = mp_obj_get_int(size_in); + mp_obj_str_t *result = MP_OBJ_TO_PTR(mp_obj_new_bytes_of_zeros(size)); + if (!common_hal_os_urandom((uint8_t *)result->data, size)) { + mp_raise_NotImplementedError(translate("No hardware random available")); + } + return result; +} +MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); + +STATIC const mp_rom_map_elem_t os_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_os) }, + + { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, + + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&os_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&os_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&os_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&os_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&os_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&os_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&os_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&os_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&os_statvfs_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&os_remove_obj) }, // unlink aliases to remove + + { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&os_sync_obj) }, + + { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, + +//| +//| sep: str +//| """Separator used to delineate path components such as folder and file names.""" +//| + { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) }, +}; + +STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t os_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&os_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_os, os_module, CIRCUITPY_OS); diff --git a/circuitpython/shared-bindings/os/__init__.h b/circuitpython/shared-bindings/os/__init__.h new file mode 100644 index 0000000..f6f0a25 --- /dev/null +++ b/circuitpython/shared-bindings/os/__init__.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_OS___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_OS___INIT___H + +#include <stdint.h> +#include <stdbool.h> + +#include "py/objtuple.h" + +extern const mp_rom_obj_tuple_t common_hal_os_uname_info_obj; + +mp_obj_t common_hal_os_uname(void); +void common_hal_os_chdir(const char *path); +mp_obj_t common_hal_os_getcwd(void); +mp_obj_t common_hal_os_listdir(const char *path); +void common_hal_os_mkdir(const char *path); +void common_hal_os_remove(const char *path); +void common_hal_os_rename(const char *old_path, const char *new_path); +void common_hal_os_rmdir(const char *path); +mp_obj_t common_hal_os_stat(const char *path); +mp_obj_t common_hal_os_statvfs(const char *path); + +// Returns true if data was correctly sourced from a true random number generator. +bool common_hal_os_urandom(uint8_t *buffer, mp_uint_t length); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_OS___INIT___H diff --git a/circuitpython/shared-bindings/paralleldisplay/ParallelBus.c b/circuitpython/shared-bindings/paralleldisplay/ParallelBus.c new file mode 100644 index 0000000..6e6e778 --- /dev/null +++ b/circuitpython/shared-bindings/paralleldisplay/ParallelBus.c @@ -0,0 +1,160 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "shared-bindings/paralleldisplay/ParallelBus.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class ParallelBus: +//| """Manage updating a display over 8-bit parallel bus in the background while Python code runs. This +//| protocol may be refered to as 8080-I Series Parallel Interface in datasheets. It doesn't handle +//| display initialization.""" +//| +//| def __init__(self, *, data0: microcontroller.Pin, command: microcontroller.Pin, chip_select: microcontroller.Pin, write: microcontroller.Pin, read: Optional[microcontroller.Pin], reset: Optional[microcontroller.Pin] = None, frequency: int = 30_000_000) -> None: +//| """Create a ParallelBus object associated with the given pins. The bus is inferred from data0 +//| by implying the next 7 additional pins on a given GPIO port. +//| +//| The parallel bus and pins are then in use by the display until `displayio.release_displays()` +//| is called even after a reload. (It does this so CircuitPython can use the display after your +//| code is done.) So, the first time you initialize a display bus in code.py you should call +//| :py:func:`displayio.release_displays` first, otherwise it will error after the first code.py run. +//| +//| :param microcontroller.Pin data_pins: A list of data pins. Specify exactly one of ``data_pins`` or ``data0``. +//| :param microcontroller.Pin data0: The first data pin. The rest are implied +//| :param microcontroller.Pin command: Data or command pin +//| :param microcontroller.Pin chip_select: Chip select pin +//| :param microcontroller.Pin write: Write pin +//| :param microcontroller.Pin read: Read pin, optional +//| :param microcontroller.Pin reset: Reset pin, optional +//| :param int frequency: The communication frequency in Hz for the display on the bus""" +//| ... +//| +STATIC mp_obj_t paralleldisplay_parallelbus_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_data0, ARG_data_pins, ARG_command, ARG_chip_select, ARG_write, ARG_read, ARG_reset, ARG_frequency }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data0, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } }, + { MP_QSTR_data_pins, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } }, + { MP_QSTR_command, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_chip_select, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_write, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_read, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } }, + { MP_QSTR_reset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } }, + { MP_QSTR_frequency, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 30000000 } }, + }; + 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 *command = validate_obj_is_free_pin(args[ARG_command].u_obj); + const mcu_pin_obj_t *chip_select = validate_obj_is_free_pin(args[ARG_chip_select].u_obj); + const mcu_pin_obj_t *write = validate_obj_is_free_pin(args[ARG_write].u_obj); + const mcu_pin_obj_t *read = validate_obj_is_free_pin_or_none(args[ARG_read].u_obj); + const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj); + + paralleldisplay_parallelbus_obj_t *self = &allocate_display_bus_or_raise()->parallel_bus; + self->base.type = ¶lleldisplay_parallelbus_type; + + bool specified_data0 = args[ARG_data0].u_obj != mp_const_none; + bool specified_data_pins = args[ARG_data_pins].u_obj != mp_const_none; + + if (specified_data0 == specified_data_pins) { + mp_raise_ValueError(translate("Specify exactly one of data0 or data_pins")); + } + + if (specified_data0) { + const mcu_pin_obj_t *data0 = validate_obj_is_free_pin(args[ARG_data0].u_obj); + common_hal_paralleldisplay_parallelbus_construct(self, data0, command, chip_select, write, read, reset, args[ARG_frequency].u_int); + } else { + uint8_t num_pins; + const mcu_pin_obj_t *data_pins[16]; + validate_list_is_free_pins(MP_QSTR_data_pins, data_pins, (mp_int_t)MP_ARRAY_SIZE(data_pins), args[ARG_data_pins].u_obj, &num_pins); + common_hal_paralleldisplay_parallelbus_construct_nonsequential(self, num_pins, data_pins, command, chip_select, write, read, reset, args[ARG_frequency].u_int); + } + return self; +} + +//| def reset(self) -> None: +//| """Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available.""" +//| ... +//| + +STATIC mp_obj_t paralleldisplay_parallelbus_obj_reset(mp_obj_t self_in) { + paralleldisplay_parallelbus_obj_t *self = self_in; + + if (!common_hal_paralleldisplay_parallelbus_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(paralleldisplay_parallelbus_reset_obj, paralleldisplay_parallelbus_obj_reset); + +//| def send(self, command: int, data: ReadableBuffer) -> None: +//| """Sends the given command value followed by the full set of data. Display state, such as +//| vertical scroll, set via ``send`` may or may not be reset once the code is done.""" +//| ... +//| +STATIC mp_obj_t paralleldisplay_parallelbus_obj_send(mp_obj_t self, mp_obj_t command_obj, mp_obj_t data_obj) { + mp_int_t command_int = MP_OBJ_SMALL_INT_VALUE(command_obj); + if (!mp_obj_is_small_int(command_obj) || command_int > 255 || command_int < 0) { + mp_raise_ValueError(translate("Command must be an int between 0 and 255")); + } + uint8_t command = command_int; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); + + // Wait for display bus to be available. + while (!common_hal_paralleldisplay_parallelbus_begin_transaction(self)) { + RUN_BACKGROUND_TASKS; + } + common_hal_paralleldisplay_parallelbus_send(self, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, &command, 1); + common_hal_paralleldisplay_parallelbus_send(self, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ((uint8_t *)bufinfo.buf), bufinfo.len); + common_hal_paralleldisplay_parallelbus_end_transaction(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(paralleldisplay_parallelbus_send_obj, paralleldisplay_parallelbus_obj_send); + +STATIC const mp_rom_map_elem_t paralleldisplay_parallelbus_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(¶lleldisplay_parallelbus_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(¶lleldisplay_parallelbus_send_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(paralleldisplay_parallelbus_locals_dict, paralleldisplay_parallelbus_locals_dict_table); + +const mp_obj_type_t paralleldisplay_parallelbus_type = { + { &mp_type_type }, + .name = MP_QSTR_ParallelBus, + .make_new = paralleldisplay_parallelbus_make_new, + .locals_dict = (mp_obj_dict_t *)¶lleldisplay_parallelbus_locals_dict, +}; diff --git a/circuitpython/shared-bindings/paralleldisplay/ParallelBus.h b/circuitpython/shared-bindings/paralleldisplay/ParallelBus.h new file mode 100644 index 0000000..e890683 --- /dev/null +++ b/circuitpython/shared-bindings/paralleldisplay/ParallelBus.h @@ -0,0 +1,55 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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. + */ + +#pragma once + +#include "common-hal/paralleldisplay/ParallelBus.h" + +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/displayio/__init__.h" +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t paralleldisplay_parallelbus_type; + +void common_hal_paralleldisplay_parallelbus_construct(paralleldisplay_parallelbus_obj_t *self, + const mcu_pin_obj_t *data0, const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select, + const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency); + +void common_hal_paralleldisplay_parallelbus_construct_nonsequential(paralleldisplay_parallelbus_obj_t *self, + uint8_t n_pins, const mcu_pin_obj_t **data_pins, const mcu_pin_obj_t *command, const mcu_pin_obj_t *chip_select, + const mcu_pin_obj_t *write, const mcu_pin_obj_t *read, const mcu_pin_obj_t *reset, uint32_t frequency); + +void common_hal_paralleldisplay_parallelbus_deinit(paralleldisplay_parallelbus_obj_t *self); + +bool common_hal_paralleldisplay_parallelbus_reset(mp_obj_t self); +bool common_hal_paralleldisplay_parallelbus_bus_free(mp_obj_t self); + +bool common_hal_paralleldisplay_parallelbus_begin_transaction(mp_obj_t self); + +void common_hal_paralleldisplay_parallelbus_send(mp_obj_t self, display_byte_type_t byte_type, + display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); + +void common_hal_paralleldisplay_parallelbus_end_transaction(mp_obj_t self); diff --git a/circuitpython/shared-bindings/paralleldisplay/__init__.c b/circuitpython/shared-bindings/paralleldisplay/__init__.c new file mode 100644 index 0000000..e87479a --- /dev/null +++ b/circuitpython/shared-bindings/paralleldisplay/__init__.c @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/enum.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/paralleldisplay/__init__.h" +#include "shared-bindings/paralleldisplay/ParallelBus.h" + +//| """Native helpers for driving parallel displays""" + + +STATIC const mp_rom_map_elem_t paralleldisplay_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_paralleldisplay) }, + { MP_ROM_QSTR(MP_QSTR_ParallelBus), MP_ROM_PTR(¶lleldisplay_parallelbus_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(paralleldisplay_module_globals, paralleldisplay_module_globals_table); + +const mp_obj_module_t paralleldisplay_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)¶lleldisplay_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_paralleldisplay, paralleldisplay_module, CIRCUITPY_PARALLELDISPLAY); diff --git a/circuitpython/shared-bindings/paralleldisplay/__init__.h b/circuitpython/shared-bindings/paralleldisplay/__init__.h new file mode 100644 index 0000000..f7b4287 --- /dev/null +++ b/circuitpython/shared-bindings/paralleldisplay/__init__.h @@ -0,0 +1,27 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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. + */ + +#pragma once diff --git a/circuitpython/shared-bindings/ps2io/Ps2.c b/circuitpython/shared-bindings/ps2io/Ps2.c new file mode 100644 index 0000000..a0fb6d6 --- /dev/null +++ b/circuitpython/shared-bindings/ps2io/Ps2.c @@ -0,0 +1,247 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2019 Elvis Pfutzenreuter <epxx@epxx.co> + * + * 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/ps2io/Ps2.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Ps2: +//| """Communicate with a PS/2 keyboard or mouse +//| +//| Ps2 implements the PS/2 keyboard/mouse serial protocol, used in +//| legacy devices. It is similar to UART but there are only two +//| lines (Data and Clock). PS/2 devices are 5V, so bidirectional +//| level converters must be used to connect the I/O lines to pins +//| of 3.3V boards.""" +//| +//| def __init__(self, data_pin: microcontroller.Pin, clock_pin: microcontroller.Pin) -> None: +//| """Create a Ps2 object associated with the given pins. +//| +//| :param ~microcontroller.Pin data_pin: Pin tied to data wire. +//| :param ~microcontroller.Pin clock_pin: Pin tied to clock wire. +//| This pin must support interrupts. +//| +//| Read one byte from PS/2 keyboard and turn on Scroll Lock LED:: +//| +//| import ps2io +//| import board +//| +//| kbd = ps2io.Ps2(board.D10, board.D11) +//| +//| while len(kbd) == 0: +//| pass +//| +//| print(kbd.popleft()) +//| print(kbd.sendcmd(0xed)) +//| print(kbd.sendcmd(0x01))""" +//| ... +//| +STATIC mp_obj_t ps2io_ps2_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_datapin, ARG_clkpin }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_datapin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_clkpin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + 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 *clkpin = validate_obj_is_free_pin(args[ARG_clkpin].u_obj); + const mcu_pin_obj_t *datapin = validate_obj_is_free_pin(args[ARG_datapin].u_obj); + + ps2io_ps2_obj_t *self = m_new_obj(ps2io_ps2_obj_t); + self->base.type = &ps2io_ps2_type; + + common_hal_ps2io_ps2_construct(self, datapin, clkpin); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the Ps2 and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t ps2io_ps2_deinit(mp_obj_t self_in) { + ps2io_ps2_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_ps2io_ps2_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ps2io_ps2_deinit_obj, ps2io_ps2_deinit); + +STATIC void check_for_deinit(ps2io_ps2_obj_t *self) { + if (common_hal_ps2io_ps2_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> Ps2: +//| """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 ps2io_ps2_obj___exit__(size_t n_args, const mp_obj_t *args) { + mp_check_self(mp_obj_is_type(args[0], &ps2io_ps2_type)); + ps2io_ps2_obj_t *self = MP_OBJ_TO_PTR(args[0]); + common_hal_ps2io_ps2_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ps2io_ps2___exit___obj, 4, 4, ps2io_ps2_obj___exit__); + +//| def popleft(self) -> int: +//| """Removes and returns the oldest received byte. When buffer +//| is empty, raises an IndexError exception.""" +//| ... +//| +STATIC mp_obj_t ps2io_ps2_obj_popleft(mp_obj_t self_in) { + ps2io_ps2_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + int b = common_hal_ps2io_ps2_popleft(self); + if (b < 0) { + mp_raise_IndexError_varg(translate("pop from empty %q"), MP_QSTR_Ps2_space_buffer); + } + return MP_OBJ_NEW_SMALL_INT(b); +} +MP_DEFINE_CONST_FUN_OBJ_1(ps2io_ps2_popleft_obj, ps2io_ps2_obj_popleft); + +//| def sendcmd(self, byte: int) -> int: +//| """Sends a command byte to PS/2. Returns the response byte, typically +//| the general ack value (0xFA). Some commands return additional data +//| which is available through :py:func:`popleft()`. +//| +//| Raises a RuntimeError in case of failure. The root cause can be found +//| by calling :py:func:`clear_errors()`. It is advisable to call +//| :py:func:`clear_errors()` before :py:func:`sendcmd()` to flush any +//| previous errors. +//| +//| :param int byte: byte value of the command""" +//| ... +//| +STATIC mp_obj_t ps2io_ps2_obj_sendcmd(mp_obj_t self_in, mp_obj_t ob) { + ps2io_ps2_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + mp_int_t cmd = mp_obj_get_int(ob) & 0xff; + int resp = common_hal_ps2io_ps2_sendcmd(self, cmd); + if (resp < 0) { + mp_raise_RuntimeError(translate("Failed sending command.")); + } + return MP_OBJ_NEW_SMALL_INT(resp); +} +MP_DEFINE_CONST_FUN_OBJ_2(ps2io_ps2_sendcmd_obj, ps2io_ps2_obj_sendcmd); + +//| def clear_errors(self) -> None: +//| """Returns and clears a bitmap with latest recorded communication errors. +//| +//| Reception errors (arise asynchronously, as data is received): +//| +//| 0x01: start bit not 0 +//| +//| 0x02: timeout +//| +//| 0x04: parity bit error +//| +//| 0x08: stop bit not 1 +//| +//| 0x10: buffer overflow, newest data discarded +//| +//| Transmission errors (can only arise in the course of sendcmd()): +//| +//| 0x100: clock pin didn't go to LO in time +//| +//| 0x200: clock pin didn't go to HI in time +//| +//| 0x400: data pin didn't ACK +//| +//| 0x800: clock pin didn't ACK +//| +//| 0x1000: device didn't respond to RTS +//| +//| 0x2000: device didn't send a response byte in time""" +//| ... +//| +STATIC mp_obj_t ps2io_ps2_obj_clear_errors(mp_obj_t self_in) { + ps2io_ps2_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_ps2io_ps2_clear_errors(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(ps2io_ps2_clear_errors_obj, ps2io_ps2_obj_clear_errors); + +//| def __bool__(self) -> bool: ... +//| +//| def __len__(self) -> int: +//| """Returns the number of received bytes in buffer, available +//| to :py:func:`popleft()`.""" +//| ... +//| +STATIC mp_obj_t ps2_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + ps2io_ps2_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + uint16_t len = common_hal_ps2io_ps2_get_len(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC const mp_rom_map_elem_t ps2io_ps2_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&ps2io_ps2_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&ps2io_ps2___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&ps2io_ps2_popleft_obj) }, + { MP_ROM_QSTR(MP_QSTR_sendcmd), MP_ROM_PTR(&ps2io_ps2_sendcmd_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_errors), MP_ROM_PTR(&ps2io_ps2_clear_errors_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(ps2io_ps2_locals_dict, ps2io_ps2_locals_dict_table); + +const mp_obj_type_t ps2io_ps2_type = { + { &mp_type_type }, + .name = MP_QSTR_Ps2, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = ps2io_ps2_make_new, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = ps2_unary_op, + ), + .locals_dict = (mp_obj_dict_t *)&ps2io_ps2_locals_dict, +}; diff --git a/circuitpython/shared-bindings/ps2io/Ps2.h b/circuitpython/shared-bindings/ps2io/Ps2.h new file mode 100644 index 0000000..da4c6ba --- /dev/null +++ b/circuitpython/shared-bindings/ps2io/Ps2.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2019 Elvis Pfutzenreuter <epxx@epxx.co> + * + * 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_PS2IO_PS2_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PS2IO_PS2_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/ps2io/Ps2.h" + +extern const mp_obj_type_t ps2io_ps2_type; + +extern void common_hal_ps2io_ps2_construct(ps2io_ps2_obj_t *self, + const mcu_pin_obj_t *data_pin, const mcu_pin_obj_t *clk_pin); +extern void common_hal_ps2io_ps2_deinit(ps2io_ps2_obj_t *self); +extern bool common_hal_ps2io_ps2_deinited(ps2io_ps2_obj_t *self); +extern uint16_t common_hal_ps2io_ps2_get_len(ps2io_ps2_obj_t *self); +extern int16_t common_hal_ps2io_ps2_popleft(ps2io_ps2_obj_t *self); +extern int16_t common_hal_ps2io_ps2_sendcmd(ps2io_ps2_obj_t *self, uint8_t b); +extern uint16_t common_hal_ps2io_ps2_clear_errors(ps2io_ps2_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PS2IO_PS2_H diff --git a/circuitpython/shared-bindings/ps2io/__init__.c b/circuitpython/shared-bindings/ps2io/__init__.c new file mode 100644 index 0000000..517816d --- /dev/null +++ b/circuitpython/shared-bindings/ps2io/__init__.c @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2019 Elvis Pfutzenreuter <epxx@epxx.co> + * + * 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/ps2io/Ps2.h" + +//| """Support for PS/2 protocol +//| +//| The `ps2io` module contains classes to provide PS/2 communication. +//| + +//| .. warning:: This module is not available in some SAMD21 builds. See the +//| :ref:`module-support-matrix` for more info. +//| + +//| 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.""" +//| + +STATIC const mp_rom_map_elem_t ps2io_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ps2io) }, + { MP_ROM_QSTR(MP_QSTR_Ps2), MP_ROM_PTR(&ps2io_ps2_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ps2io_module_globals, ps2io_module_globals_table); + +const mp_obj_module_t ps2io_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&ps2io_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ps2io, ps2io_module, CIRCUITPY_PS2IO); diff --git a/circuitpython/shared-bindings/ps2io/__init__.h b/circuitpython/shared-bindings/ps2io/__init__.h new file mode 100644 index 0000000..1ff3d97 --- /dev/null +++ b/circuitpython/shared-bindings/ps2io/__init__.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Scott Shawcroft + * Copyright (c) 2019 Elvis Pfutzenreuter <epxx@epxx.co> + * + * 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_PS2IO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PS2IO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PS2IO___INIT___H diff --git a/circuitpython/shared-bindings/pulseio/PulseIn.c b/circuitpython/shared-bindings/pulseio/PulseIn.c new file mode 100644 index 0000000..21dfdae --- /dev/null +++ b/circuitpython/shared-bindings/pulseio/PulseIn.c @@ -0,0 +1,312 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/pulseio/PulseIn.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class PulseIn: +//| """Measure a series of active and idle pulses. This is commonly used in infrared receivers +//| and low cost temperature sensors (DHT). The pulsed signal consists of timed active and +//| idle periods. Unlike PWM, there is no set duration for active and idle pairs.""" +//| +//| def __init__(self, pin: microcontroller.Pin, maxlen: int = 2, *, idle_state: bool = False) -> None: +//| """Create a PulseIn object associated with the given pin. The object acts as +//| a read-only sequence of pulse lengths with a given max length. When it is +//| active, new pulse lengths are added to the end of the list. When there is +//| no more room (len() == `maxlen`) the oldest pulse length is removed to +//| make room. +//| +//| :param ~microcontroller.Pin pin: Pin to read pulses from. +//| :param int maxlen: Maximum number of pulse durations to store at once +//| :param bool idle_state: Idle state of the pin. At start and after `resume` +//| the first recorded pulse will the opposite state from idle. +//| +//| Read a short series of pulses:: +//| +//| import pulseio +//| import board +//| +//| pulses = pulseio.PulseIn(board.D7) +//| +//| # Wait for an active pulse +//| while len(pulses) == 0: +//| pass +//| # Pause while we do something with the pulses +//| pulses.pause() +//| +//| # Print the pulses. pulses[0] is an active pulse unless the length +//| # reached max length and idle pulses are recorded. +//| print(pulses) +//| +//| # Clear the rest +//| pulses.clear() +//| +//| # Resume with an 80 microsecond active pulse +//| pulses.resume(80)""" +//| ... +//| +STATIC mp_obj_t pulseio_pulsein_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pin, ARG_maxlen, ARG_idle_state }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_maxlen, MP_ARG_INT, {.u_int = 2} }, + { MP_QSTR_idle_state, 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 *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + + pulseio_pulsein_obj_t *self = m_new_obj(pulseio_pulsein_obj_t); + self->base.type = &pulseio_pulsein_type; + + common_hal_pulseio_pulsein_construct(self, pin, args[ARG_maxlen].u_int, + args[ARG_idle_state].u_bool); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the PulseIn and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t pulseio_pulsein_deinit(mp_obj_t self_in) { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_pulseio_pulsein_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_deinit_obj, pulseio_pulsein_deinit); + +STATIC void check_for_deinit(pulseio_pulsein_obj_t *self) { + if (common_hal_pulseio_pulsein_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> PulseIn: +//| """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 pulseio_pulsein_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_pulseio_pulsein_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pulseio_pulsein___exit___obj, 4, 4, pulseio_pulsein_obj___exit__); + +//| def pause(self) -> None: +//| """Pause pulse capture""" +//| ... +//| +STATIC mp_obj_t pulseio_pulsein_obj_pause(mp_obj_t self_in) { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_pulseio_pulsein_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_pause_obj, pulseio_pulsein_obj_pause); + +//| def resume(self, trigger_duration: int = 0) -> None: +//| """Resumes pulse capture after an optional trigger pulse. +//| +//| .. warning:: Using trigger pulse with a device that drives both high and +//| low signals risks a short. Make sure your device is open drain (only +//| drives low) when using a trigger pulse. You most likely added a +//| "pull-up" resistor to your circuit to do this. +//| +//| :param int trigger_duration: trigger pulse duration in microseconds""" +//| ... +//| +STATIC mp_obj_t pulseio_pulsein_obj_resume(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_trigger_duration }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_trigger_duration, MP_ARG_INT, {.u_int = 0} }, + }; + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(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); + + common_hal_pulseio_pulsein_resume(self, args[ARG_trigger_duration].u_int); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(pulseio_pulsein_resume_obj, 1, pulseio_pulsein_obj_resume); + +//| def clear(self) -> None: +//| """Clears all captured pulses""" +//| ... +//| +STATIC mp_obj_t pulseio_pulsein_obj_clear(mp_obj_t self_in) { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_pulseio_pulsein_clear(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_clear_obj, pulseio_pulsein_obj_clear); + +//| def popleft(self) -> int: +//| """Removes and returns the oldest read pulse.""" +//| ... +//| +STATIC mp_obj_t pulseio_pulsein_obj_popleft(mp_obj_t self_in) { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_pulseio_pulsein_popleft(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_popleft_obj, pulseio_pulsein_obj_popleft); + +//| maxlen: int +//| """The maximum length of the PulseIn. When len() is equal to maxlen, +//| it is unclear which pulses are active and which are idle.""" +//| +STATIC mp_obj_t pulseio_pulsein_obj_get_maxlen(mp_obj_t self_in) { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_pulseio_pulsein_get_maxlen(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_get_maxlen_obj, pulseio_pulsein_obj_get_maxlen); + +MP_PROPERTY_GETTER(pulseio_pulsein_maxlen_obj, + (mp_obj_t)&pulseio_pulsein_get_maxlen_obj); + +//| paused: bool +//| """True when pulse capture is paused as a result of :py:func:`pause` or an error during capture +//| such as a signal that is too fast.""" +//| +STATIC mp_obj_t pulseio_pulsein_obj_get_paused(mp_obj_t self_in) { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return mp_obj_new_bool(common_hal_pulseio_pulsein_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_get_paused_obj, pulseio_pulsein_obj_get_paused); + +MP_PROPERTY_GETTER(pulseio_pulsein_paused_obj, + (mp_obj_t)&pulseio_pulsein_get_paused_obj); + +//| def __bool__(self) -> bool: ... +//| +//| def __len__(self) -> int: +//| """Returns the number of pulse durations currently stored. +//| +//| This allows you to:: +//| +//| pulses = pulseio.PulseIn(pin) +//| print(len(pulses))""" +//| ... +//| +STATIC mp_obj_t pulsein_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + uint16_t len = common_hal_pulseio_pulsein_get_len(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __getitem__(self, index: int) -> Optional[int]: +//| """Returns the value at the given index or values in slice. +//| +//| This allows you to:: +//| +//| pulses = pulseio.PulseIn(pin) +//| print(pulses[0])""" +//| ... +//| +STATIC mp_obj_t pulsein_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value) { + if (value == mp_const_none) { + // delete item + mp_raise_AttributeError(translate("Cannot delete values")); + } else { + pulseio_pulsein_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + mp_raise_NotImplementedError(translate("Slices not supported")); + } else { + size_t index = mp_get_index(&pulseio_pulsein_type, common_hal_pulseio_pulsein_get_len(self), index_obj, false); + if (value == MP_OBJ_SENTINEL) { + // load + return MP_OBJ_NEW_SMALL_INT(common_hal_pulseio_pulsein_get_item(self, index)); + } else { + mp_raise_AttributeError(translate("Read-only")); + } + } + } + return mp_const_none; +} + +STATIC const mp_rom_map_elem_t pulseio_pulsein_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pulseio_pulsein_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&pulseio_pulsein___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&pulseio_pulsein_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&pulseio_pulsein_resume_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&pulseio_pulsein_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&pulseio_pulsein_popleft_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_maxlen), MP_ROM_PTR(&pulseio_pulsein_maxlen_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&pulseio_pulsein_paused_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(pulseio_pulsein_locals_dict, pulseio_pulsein_locals_dict_table); + +const mp_obj_type_t pulseio_pulsein_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_PulseIn, + .make_new = pulseio_pulsein_make_new, + .locals_dict = (mp_obj_dict_t *)&pulseio_pulsein_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = pulsein_subscr, + .unary_op = pulsein_unary_op, + ), +}; diff --git a/circuitpython/shared-bindings/pulseio/PulseIn.h b/circuitpython/shared-bindings/pulseio/PulseIn.h new file mode 100644 index 0000000..09d01fd --- /dev/null +++ b/circuitpython/shared-bindings/pulseio/PulseIn.h @@ -0,0 +1,48 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_PULSEIO_PULSEIN_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PULSEIO_PULSEIN_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/pulseio/PulseIn.h" + +extern const mp_obj_type_t pulseio_pulsein_type; + +extern void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self, + const mcu_pin_obj_t *pin, uint16_t maxlen, bool idle_state); +extern void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t *self); +extern bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t *self); +extern void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t *self); +extern void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t *self, uint16_t trigger_duration); +extern void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t *self); +extern uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t *self); +extern uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t *self); +extern bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t *self); +extern uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t *self); +extern uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t *self, int16_t index); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PULSEIO_PULSEIN_H diff --git a/circuitpython/shared-bindings/pulseio/PulseOut.c b/circuitpython/shared-bindings/pulseio/PulseOut.c new file mode 100644 index 0000000..ac56823 --- /dev/null +++ b/circuitpython/shared-bindings/pulseio/PulseOut.c @@ -0,0 +1,170 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/pulseio/PulseOut.h" +#include "shared-bindings/pwmio/PWMOut.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class PulseOut: +//| """Pulse PWM "carrier" output on and off. This is commonly used in infrared remotes. The +//| pulsed signal consists of timed on and off periods. Unlike PWM, there is no set duration +//| for on and off pairs.""" +//| +//| def __init__(self, pin: microcontroller.Pin, *, frequency: int = 38000, duty_cycle: int = 1 << 15) -> None: +//| """Create a PulseOut object associated with the given pin. +//| +//| :param ~microcontroller.Pin pin: Signal output pin +//| :param int frequency: Carrier signal frequency in Hertz +//| :param int duty_cycle: 16-bit duty cycle of carrier frequency (0 - 65536) +//| +//| For backwards compatibility, ``pin`` may be a PWMOut object used as the carrier. This +//| compatibility will be removed in CircuitPython 8.0.0. +//| +//| Send a short series of pulses:: +//| +//| import array +//| import pulseio +//| import pwmio +//| import board +//| +//| # 50% duty cycle at 38kHz. +//| pulse = pulseio.PulseOut(board.LED, frequency=38000, duty_cycle=32768) +//| # on off on off on +//| pulses = array.array('H', [65000, 1000, 65000, 65000, 1000]) +//| pulse.send(pulses) +//| +//| # Modify the array of pulses. +//| pulses[0] = 200 +//| pulse.send(pulses)""" +//| ... +//| +STATIC mp_obj_t pulseio_pulseout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pin, ARG_frequency, ARG_duty_cycle}; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 38000} }, + { MP_QSTR_duty_cycle, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 << 15} }, + }; + 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 *pin = args[ARG_pin].u_obj; + mp_int_t frequency = args[ARG_frequency].u_int; + mp_int_t duty_cycle = args[ARG_duty_cycle].u_int; + if (mp_obj_is_type(args[ARG_pin].u_obj, &pwmio_pwmout_type)) { + pwmio_pwmout_obj_t *pwmout = args[ARG_pin].u_obj; + duty_cycle = common_hal_pwmio_pwmout_get_duty_cycle(pwmout); + frequency = common_hal_pwmio_pwmout_get_frequency(pwmout); + pin = common_hal_pwmio_pwmout_get_pin(pwmout); + // Deinit the pin so we can use it. + common_hal_pwmio_pwmout_deinit(pwmout); + } + validate_obj_is_free_pin(MP_OBJ_FROM_PTR(pin)); + pulseio_pulseout_obj_t *self = m_new_obj(pulseio_pulseout_obj_t); + self->base.type = &pulseio_pulseout_type; + common_hal_pulseio_pulseout_construct(self, pin, frequency, duty_cycle); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the PulseOut and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t pulseio_pulseout_deinit(mp_obj_t self_in) { + pulseio_pulseout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_pulseio_pulseout_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulseout_deinit_obj, pulseio_pulseout_deinit); + +//| def __enter__(self) -> PulseOut: +//| """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 pulseio_pulseout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_pulseio_pulseout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pulseio_pulseout___exit___obj, 4, 4, pulseio_pulseout_obj___exit__); + +//| def send(self, pulses: ReadableBuffer) -> None: +//| """Pulse alternating on and off durations in microseconds starting with on. +//| ``pulses`` must be an `array.array` with data type 'H' for unsigned +//| halfword (two bytes). +//| +//| This method waits until the whole array of pulses has been sent and +//| ensures the signal is off afterwards. +//| +//| :param array.array pulses: pulse durations in microseconds""" +//| ... +//| +STATIC mp_obj_t pulseio_pulseout_obj_send(mp_obj_t self_in, mp_obj_t pulses) { + pulseio_pulseout_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (common_hal_pulseio_pulseout_deinited(self)) { + raise_deinited_error(); + } + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(pulses, &bufinfo, MP_BUFFER_READ); + if (bufinfo.typecode != 'H') { + mp_raise_TypeError(translate("Array must contain halfwords (type 'H')")); + } + common_hal_pulseio_pulseout_send(self, (uint16_t *)bufinfo.buf, bufinfo.len / 2); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pulseio_pulseout_send_obj, pulseio_pulseout_obj_send); + +STATIC const mp_rom_map_elem_t pulseio_pulseout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pulseio_pulseout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&pulseio_pulseout___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&pulseio_pulseout_send_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(pulseio_pulseout_locals_dict, pulseio_pulseout_locals_dict_table); + +const mp_obj_type_t pulseio_pulseout_type = { + { &mp_type_type }, + .name = MP_QSTR_PulseOut, + .make_new = pulseio_pulseout_make_new, + .locals_dict = (mp_obj_dict_t *)&pulseio_pulseout_locals_dict, +}; diff --git a/circuitpython/shared-bindings/pulseio/PulseOut.h b/circuitpython/shared-bindings/pulseio/PulseOut.h new file mode 100644 index 0000000..0c64c13 --- /dev/null +++ b/circuitpython/shared-bindings/pulseio/PulseOut.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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_PULSEIO_PULSEOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PULSEIO_PULSEOUT_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/pulseio/PulseOut.h" +#include "common-hal/pwmio/PWMOut.h" + +extern const mp_obj_type_t pulseio_pulseout_type; + +extern void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t *self, + const mcu_pin_obj_t *pin, + uint32_t frequency, + uint16_t duty_cycle); + +extern void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t *self); +extern bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t *self); +extern void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t *self, + uint16_t *pulses, uint16_t len); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PULSEIO_PULSEOUT_H diff --git a/circuitpython/shared-bindings/pulseio/__init__.c b/circuitpython/shared-bindings/pulseio/__init__.c new file mode 100644 index 0000000..0dba6ff --- /dev/null +++ b/circuitpython/shared-bindings/pulseio/__init__.c @@ -0,0 +1,62 @@ +/* + * 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/pulseio/__init__.h" +#include "shared-bindings/pulseio/PulseIn.h" +#include "shared-bindings/pulseio/PulseOut.h" + +//| """Support for individual pulse based protocols +//| +//| The `pulseio` module contains classes to provide access to basic pulse IO. +//| Individual pulses are commonly used in infrared remotes and in DHT +//| temperature sensors. +//| +//| 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.""" +//| + +STATIC const mp_rom_map_elem_t pulseio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pulseio) }, + { MP_ROM_QSTR(MP_QSTR_PulseIn), MP_ROM_PTR(&pulseio_pulsein_type) }, + { MP_ROM_QSTR(MP_QSTR_PulseOut), MP_ROM_PTR(&pulseio_pulseout_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pulseio_module_globals, pulseio_module_globals_table); + +const mp_obj_module_t pulseio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&pulseio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_pulseio, pulseio_module, CIRCUITPY_PULSEIO); diff --git a/circuitpython/shared-bindings/pulseio/__init__.h b/circuitpython/shared-bindings/pulseio/__init__.h new file mode 100644 index 0000000..0691ad2 --- /dev/null +++ b/circuitpython/shared-bindings/pulseio/__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_PULSEIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PULSEIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PULSEIO___INIT___H diff --git a/circuitpython/shared-bindings/pwmio/PWMOut.c b/circuitpython/shared-bindings/pwmio/PWMOut.c new file mode 100644 index 0000000..60a9dfb --- /dev/null +++ b/circuitpython/shared-bindings/pwmio/PWMOut.c @@ -0,0 +1,284 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George + * + * 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/pwmio/PWMOut.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + + +void common_hal_pwmio_pwmout_raise_error(pwmout_result_t result) { + switch (result) { + case PWMOUT_OK: + break; + case PWMOUT_INVALID_PIN: + mp_raise_ValueError(translate("Invalid pin")); + break; + case PWMOUT_INVALID_FREQUENCY: + mp_raise_ValueError(translate("Invalid PWM frequency")); + break; + case PWMOUT_INVALID_FREQUENCY_ON_PIN: + mp_raise_ValueError(translate("Frequency must match existing PWMOut using this timer")); + break; + case PWMOUT_VARIABLE_FREQUENCY_NOT_AVAILABLE: + mp_raise_ValueError(translate("Cannot vary frequency on a timer that is already in use")); + break; + case PWMOUT_ALL_TIMERS_ON_PIN_IN_USE: + mp_raise_ValueError(translate("All timers for this pin are in use")); + break; + case PWMOUT_ALL_TIMERS_IN_USE: + mp_raise_RuntimeError(translate("All timers in use")); + break; + case PWMOUT_ALL_CHANNELS_IN_USE: + mp_raise_RuntimeError(translate("All channels in use")); + break; + default: + case PWMOUT_INITIALIZATION_ERROR: + mp_raise_RuntimeError(translate("Could not start PWM")); + break; + } +} + +//| class PWMOut: +//| """Output a Pulse Width Modulated signal on a given pin.""" +//| +//| def __init__(self, pin: microcontroller.Pin, *, duty_cycle: int = 0, frequency: int = 500, variable_frequency: bool = False) -> None: +//| """Create a PWM object associated with the given pin. This allows you to +//| write PWM signals out on the given pin. Frequency is fixed after init +//| unless ``variable_frequency`` is True. +//| +//| .. note:: When ``variable_frequency`` is True, further PWM outputs may be +//| limited because it may take more internal resources to be flexible. So, +//| when outputting both fixed and flexible frequency signals construct the +//| fixed outputs first. +//| +//| :param ~microcontroller.Pin pin: The pin to output to +//| :param int duty_cycle: The fraction of each pulse which is high. 16-bit +//| :param int frequency: The target frequency in Hertz (32-bit) +//| :param bool variable_frequency: True if the frequency will change over time +//| +//| +//| Simple LED on:: +//| +//| import pwmio +//| import board +//| +//| pwm = pwmio.PWMOut(board.LED) +//| +//| while True: +//| pwm.duty_cycle = 2 ** 15 # Cycles the pin with 50% duty cycle (half of 2 ** 16) at the default 500hz +//| +//| PWM LED fade:: +//| +//| import pwmio +//| import board +//| +//| pwm = pwmio.PWMOut(board.LED) # output on LED pin with default of 500Hz +//| +//| while True: +//| for cycle in range(0, 65535): # Cycles through the full PWM range from 0 to 65535 +//| pwm.duty_cycle = cycle # Cycles the LED pin duty cycle through the range of values +//| for cycle in range(65534, 0, -1): # Cycles through the PWM range backwards from 65534 to 0 +//| pwm.duty_cycle = cycle # Cycles the LED pin duty cycle through the range of values +//| +//| PWM at specific frequency (servos and motors):: +//| +//| import pwmio +//| import board +//| +//| pwm = pwmio.PWMOut(board.D13, frequency=50) +//| pwm.duty_cycle = 2 ** 15 # Cycles the pin with 50% duty cycle (half of 2 ** 16) at 50hz +//| +//| Variable frequency (usually tones):: +//| +//| import pwmio +//| import board +//| import time +//| +//| pwm = pwmio.PWMOut(board.D13, duty_cycle=2 ** 15, frequency=440, variable_frequency=True) +//| time.sleep(0.2) +//| pwm.frequency = 880 +//| time.sleep(0.1)""" +//| ... +//| +STATIC mp_obj_t pwmio_pwmout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + enum { ARG_pin, ARG_duty_cycle, ARG_frequency, ARG_variable_frequency }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, }, + { MP_QSTR_duty_cycle, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 500} }, + { MP_QSTR_variable_frequency, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); + + const mcu_pin_obj_t *pin = validate_obj_is_free_pin(parsed_args[ARG_pin].u_obj); + + uint16_t duty_cycle = parsed_args[ARG_duty_cycle].u_int; + uint32_t frequency = parsed_args[ARG_frequency].u_int; + bool variable_frequency = parsed_args[ARG_variable_frequency].u_bool; + + // create PWM object from the given pin + pwmio_pwmout_obj_t *self = m_new_obj(pwmio_pwmout_obj_t); + self->base.type = &pwmio_pwmout_type; + pwmout_result_t result = common_hal_pwmio_pwmout_construct(self, pin, duty_cycle, frequency, variable_frequency); + common_hal_pwmio_pwmout_raise_error(result); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the PWMOut and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t pwmio_pwmout_deinit(mp_obj_t self_in) { + pwmio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_pwmio_pwmout_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pwmio_pwmout_deinit_obj, pwmio_pwmout_deinit); + +STATIC void check_for_deinit(pwmio_pwmout_obj_t *self) { + if (common_hal_pwmio_pwmout_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> PWMOut: +//| """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 pwmio_pwmout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_pwmio_pwmout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pwmio_pwmout___exit___obj, 4, 4, pwmio_pwmout_obj___exit__); + +//| duty_cycle: int +//| """16 bit value that dictates how much of one cycle is high (1) versus low +//| (0). 0xffff will always be high, 0 will always be low and 0x7fff will +//| be half high and then half low. +//| +//| Depending on how PWM is implemented on a specific board, the internal +//| representation for duty cycle might have less than 16 bits of resolution. +//| Reading this property will return the value from the internal representation, +//| so it may differ from the value set.""" +//| +STATIC mp_obj_t pwmio_pwmout_obj_get_duty_cycle(mp_obj_t self_in) { + pwmio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_pwmio_pwmout_get_duty_cycle(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pwmio_pwmout_get_duty_cycle_obj, pwmio_pwmout_obj_get_duty_cycle); + +STATIC mp_obj_t pwmio_pwmout_obj_set_duty_cycle(mp_obj_t self_in, mp_obj_t duty_cycle) { + pwmio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + mp_int_t duty = mp_obj_get_int(duty_cycle); + if (duty < 0 || duty > 0xffff) { + mp_raise_ValueError(translate("PWM duty_cycle must be between 0 and 65535 inclusive (16 bit resolution)")); + } + common_hal_pwmio_pwmout_set_duty_cycle(self, duty); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pwmio_pwmout_set_duty_cycle_obj, pwmio_pwmout_obj_set_duty_cycle); + +MP_PROPERTY_GETSET(pwmio_pwmout_duty_cycle_obj, + (mp_obj_t)&pwmio_pwmout_get_duty_cycle_obj, + (mp_obj_t)&pwmio_pwmout_set_duty_cycle_obj); + +//| frequency: int +//| """32 bit value that dictates the PWM frequency in Hertz (cycles per +//| second). Only writeable when constructed with ``variable_frequency=True``. +//| +//| Depending on how PWM is implemented on a specific board, the internal value +//| for the PWM's duty cycle may need to be recalculated when the frequency +//| changes. In these cases, the duty cycle is automatically recalculated +//| from the original duty cycle value. This should happen without any need +//| to manually re-set the duty cycle.""" +//| +STATIC mp_obj_t pwmio_pwmout_obj_get_frequency(mp_obj_t self_in) { + pwmio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_pwmio_pwmout_get_frequency(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pwmio_pwmout_get_frequency_obj, pwmio_pwmout_obj_get_frequency); + +STATIC mp_obj_t pwmio_pwmout_obj_set_frequency(mp_obj_t self_in, mp_obj_t frequency) { + pwmio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (!common_hal_pwmio_pwmout_get_variable_frequency(self)) { + mp_raise_AttributeError(translate( + "PWM frequency not writable when variable_frequency is False on " + "construction.")); + } + mp_int_t freq = mp_obj_get_int(frequency); + if (freq == 0) { + mp_raise_ValueError(translate("Invalid PWM frequency")); + } + common_hal_pwmio_pwmout_set_frequency(self, freq); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pwmio_pwmout_set_frequency_obj, pwmio_pwmout_obj_set_frequency); + +MP_PROPERTY_GETSET(pwmio_pwmout_frequency_obj, + (mp_obj_t)&pwmio_pwmout_get_frequency_obj, + (mp_obj_t)&pwmio_pwmout_set_frequency_obj); + +STATIC const mp_rom_map_elem_t pwmio_pwmout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pwmio_pwmout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&pwmio_pwmout___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_duty_cycle), MP_ROM_PTR(&pwmio_pwmout_duty_cycle_obj) }, + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&pwmio_pwmout_frequency_obj) }, + // TODO(tannewt): Add enabled to determine whether the signal is output + // without giving up the resources. Useful for IR output. +}; +STATIC MP_DEFINE_CONST_DICT(pwmio_pwmout_locals_dict, pwmio_pwmout_locals_dict_table); + +const mp_obj_type_t pwmio_pwmout_type = { + { &mp_type_type }, + .name = MP_QSTR_PWMOut, + .make_new = pwmio_pwmout_make_new, + .locals_dict = (mp_obj_dict_t *)&pwmio_pwmout_locals_dict, +}; diff --git a/circuitpython/shared-bindings/pwmio/PWMOut.h b/circuitpython/shared-bindings/pwmio/PWMOut.h new file mode 100644 index 0000000..e55205e --- /dev/null +++ b/circuitpython/shared-bindings/pwmio/PWMOut.h @@ -0,0 +1,67 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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_PWMIO_PWMOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PWMIO_PWMOUT_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/pwmio/PWMOut.h" + +extern const mp_obj_type_t pwmio_pwmout_type; + +typedef enum pwmout_result_t { + PWMOUT_OK, + PWMOUT_INVALID_PIN, + PWMOUT_INVALID_FREQUENCY, + PWMOUT_INVALID_FREQUENCY_ON_PIN, + PWMOUT_VARIABLE_FREQUENCY_NOT_AVAILABLE, + PWMOUT_ALL_TIMERS_ON_PIN_IN_USE, + PWMOUT_ALL_TIMERS_IN_USE, + PWMOUT_ALL_CHANNELS_IN_USE, + PWMOUT_INITIALIZATION_ERROR, +} pwmout_result_t; + +extern pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self, + const mcu_pin_obj_t *pin, uint16_t duty, uint32_t frequency, + bool variable_frequency); +extern void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t *self); +extern bool common_hal_pwmio_pwmout_deinited(pwmio_pwmout_obj_t *self); +extern void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t *self, uint16_t duty); +extern uint16_t common_hal_pwmio_pwmout_get_duty_cycle(pwmio_pwmout_obj_t *self); +extern void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t *self, uint32_t frequency); +extern uint32_t common_hal_pwmio_pwmout_get_frequency(pwmio_pwmout_obj_t *self); +extern bool common_hal_pwmio_pwmout_get_variable_frequency(pwmio_pwmout_obj_t *self); + +// Don't use this! It is only used internally for backwards compatibility. +extern const mcu_pin_obj_t *common_hal_pwmio_pwmout_get_pin(pwmio_pwmout_obj_t *self); + +// This is used by the supervisor to claim PWMOut devices indefinitely. +extern void common_hal_pwmio_pwmout_never_reset(pwmio_pwmout_obj_t *self); +extern void common_hal_pwmio_pwmout_reset_ok(pwmio_pwmout_obj_t *self); + +extern void common_hal_pwmio_pwmout_raise_error(pwmout_result_t result); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PWMIO_PWMOUT_H diff --git a/circuitpython/shared-bindings/pwmio/__init__.c b/circuitpython/shared-bindings/pwmio/__init__.c new file mode 100644 index 0000000..90aff45 --- /dev/null +++ b/circuitpython/shared-bindings/pwmio/__init__.c @@ -0,0 +1,79 @@ +/* + * 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/pwmio/__init__.h" +#include "shared-bindings/pwmio/PWMOut.h" + +//| """Support for PWM based protocols +//| +//| The `pwmio` module contains classes to provide access to basic pulse IO. +//| + +//| 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 time +//| import pwmio +//| import board +//| +//| pwm = pwmio.PWMOut(board.LED) +//| pwm.duty_cycle = 2 ** 15 +//| time.sleep(0.1) +//| +//| This example will initialize the the device, set +//| :py:data:`~pwmio.PWMOut.duty_cycle`, and then sleep 0.1 seconds. +//| CircuitPython will automatically turn off the PWM when it resets all +//| hardware after program completion. Use ``deinit()`` or a ``with`` statement +//| to do it yourself. +//| +//| For the essentials of `pwmio`, see the `CircuitPython Essentials +//| Learn guide <https://learn.adafruit.com/circuitpython-essentials/circuitpython-pwm>`_. +//| """ +//| + +STATIC const mp_rom_map_elem_t pwmio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pwmio) }, + { MP_ROM_QSTR(MP_QSTR_PWMOut), MP_ROM_PTR(&pwmio_pwmout_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pwmio_module_globals, pwmio_module_globals_table); + +const mp_obj_module_t pwmio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&pwmio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_pwmio, pwmio_module, CIRCUITPY_PWMIO); diff --git a/circuitpython/shared-bindings/pwmio/__init__.h b/circuitpython/shared-bindings/pwmio/__init__.h new file mode 100644 index 0000000..93c7037 --- /dev/null +++ b/circuitpython/shared-bindings/pwmio/__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_PWMIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_PWMIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_PWMIO___INIT___H diff --git a/circuitpython/shared-bindings/qrio/PixelPolicy.c b/circuitpython/shared-bindings/qrio/PixelPolicy.c new file mode 100644 index 0000000..6887081 --- /dev/null +++ b/circuitpython/shared-bindings/qrio/PixelPolicy.c @@ -0,0 +1,56 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * 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 "shared-bindings/qrio/__init__.h" +#include "shared-bindings/qrio/PixelPolicy.h" +#include "py/obj.h" +#include "py/enum.h" + +//| class PixelPolicy: +//| EVERY_BYTE: PixelPolicy +//| """The input buffer to `QRDecoder.decode` consists of greyscale values in every byte""" +//| +//| EVEN_BYTES: PixelPolicy +//| """The input buffer to `QRDecoder.decode` consists of greyscale values in positions 0, 2, …, and ignored bytes in positions 1, 3, …. This can decode directly from YUV images where the even bytes hold the Y (luminance) data.""" +//| +//| ODD_BYTES: PixelPolicy +//| """The input buffer to `QRDecoder.decode` consists of greyscale values in positions 1, 3, …, and ignored bytes in positions 0, 2, …. This can decode directly from YUV images where the odd bytes hold the Y (luminance) data""" +//| + +MAKE_ENUM_VALUE(qrio_pixel_policy_type, qrio_pixel_policy, EVERY_BYTE, QRIO_EVERY_BYTE); +MAKE_ENUM_VALUE(qrio_pixel_policy_type, qrio_pixel_policy, EVEN_BYTES, QRIO_EVEN_BYTES); +MAKE_ENUM_VALUE(qrio_pixel_policy_type, qrio_pixel_policy, ODD_BYTES, QRIO_EVEN_BYTES); + +MAKE_ENUM_MAP(qrio_pixel_policy) { + MAKE_ENUM_MAP_ENTRY(qrio_pixel_policy, EVERY_BYTE), + MAKE_ENUM_MAP_ENTRY(qrio_pixel_policy, EVEN_BYTES), + MAKE_ENUM_MAP_ENTRY(qrio_pixel_policy, ODD_BYTES), +}; +STATIC MP_DEFINE_CONST_DICT(qrio_pixel_policy_locals_dict, qrio_pixel_policy_locals_table); + +MAKE_PRINTER(qrio, qrio_pixel_policy); + +MAKE_ENUM_TYPE(qrio, PixelPolicy, qrio_pixel_policy); diff --git a/circuitpython/shared-bindings/qrio/PixelPolicy.h b/circuitpython/shared-bindings/qrio/PixelPolicy.h new file mode 100644 index 0000000..8be5dde --- /dev/null +++ b/circuitpython/shared-bindings/qrio/PixelPolicy.h @@ -0,0 +1,39 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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. + */ + +#pragma once + +#include "py/enum.h" +#include "py/obj.h" +#include "py/objnamedtuple.h" + +extern const mp_obj_type_t qrio_pixel_policy_type; + +typedef enum { + QRIO_EVERY_BYTE, QRIO_EVEN_BYTES, QRIO_ODD_BYTES +} qrio_pixel_policy_t; + +extern const cp_enum_obj_t qrio_pixel_policy_EVERY_BYTE_obj; diff --git a/circuitpython/shared-bindings/qrio/QRDecoder.c b/circuitpython/shared-bindings/qrio/QRDecoder.c new file mode 100644 index 0000000..d2d4785 --- /dev/null +++ b/circuitpython/shared-bindings/qrio/QRDecoder.c @@ -0,0 +1,149 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * 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 "shared-bindings/qrio/__init__.h" +#include "shared-bindings/qrio/QRDecoder.h" +#include "shared-module/qrio/QRDecoder.h" + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/enum.h" + +//| class QRDecoder: +//| +//| def __init__(self, width: int, height: int) -> None: +//| """Construct a QRDecoder object +//| +//| :param int width: The pixel width of the image to decode +//| :param int height: The pixel height of the image to decode +//| """ +//| ... + +STATIC mp_obj_t qrio_qrdecoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) { + enum { ARG_width, ARG_height }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, args_in, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + qrio_qrdecoder_obj_t *self = m_new_obj(qrio_qrdecoder_obj_t); + self->base.type = &qrio_qrdecoder_type_obj; + shared_module_qrio_qrdecoder_construct(self, args[ARG_width].u_int, args[ARG_height].u_int); + + return self; +} + +//| def decode(self, buffer: ReadableBuffer, pixel_policy: PixelPolicy = PixelPolicy.EVERY_BYTE) -> List[QRInfo]: +//| """Decode zero or more QR codes from the given image. The size of the buffer must be at least ``length``×``width`` bytes for `EVERY_BYTE`, and 2×``length``×``width`` bytes for `EVEN_BYTES` or `ODD_BYTES`.""" +//| +STATIC mp_obj_t qrio_qrdecoder_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + qrio_qrdecoder_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_buffer, ARG_pixel_policy }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_int = 0} }, + { MP_QSTR_pixel_policy, MP_ARG_OBJ, {.u_obj = MP_ROM_PTR((mp_obj_t *)&qrio_pixel_policy_EVERY_BYTE_obj)} }, + }; + 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); + + int width = shared_module_qrio_qrdecoder_get_width(self); + int height = shared_module_qrio_qrdecoder_get_height(self); + + // verify that the buffer is big enough + int sz = width * height; + qrio_pixel_policy_t policy = cp_enum_value(&qrio_pixel_policy_type, args[ARG_pixel_policy].u_obj); + if (policy != QRIO_EVERY_BYTE) { + sz *= 2; + } + mp_get_index(mp_obj_get_type(args[ARG_buffer].u_obj), bufinfo.len, MP_OBJ_NEW_SMALL_INT(sz - 1), false); + + return shared_module_qrio_qrdecoder_decode(self, &bufinfo, policy); +} +MP_DEFINE_CONST_FUN_OBJ_KW(qrio_qrdecoder_decode_obj, 1, qrio_qrdecoder_decode); + +//| width: int +//| """The width of image the decoder expects""" +//| +STATIC mp_obj_t qrio_qrdecoder_get_width(mp_obj_t self_in) { + qrio_qrdecoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(shared_module_qrio_qrdecoder_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(qrio_qrdecoder_get_width_obj, qrio_qrdecoder_get_width); + +STATIC mp_obj_t qrio_qrdecoder_set_width(mp_obj_t self_in, mp_obj_t width_in) { + qrio_qrdecoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + int width = mp_obj_get_int(width_in); + shared_module_qrio_qrdecoder_set_width(self, width); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(qrio_qrdecoder_set_width_obj, qrio_qrdecoder_set_width); + +MP_PROPERTY_GETSET(qrio_qrdecoder_width_obj, + (mp_obj_t)&qrio_qrdecoder_get_width_obj, + (mp_obj_t)&qrio_qrdecoder_set_width_obj); + +//| height: int +//| """The height of image the decoder expects""" +//| +STATIC mp_obj_t qrio_qrdecoder_get_height(mp_obj_t self_in) { + qrio_qrdecoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(shared_module_qrio_qrdecoder_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(qrio_qrdecoder_get_height_obj, qrio_qrdecoder_get_height); + +STATIC mp_obj_t qrio_qrdecoder_set_height(mp_obj_t self_in, mp_obj_t height_in) { + qrio_qrdecoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + int height = mp_obj_get_int(height_in); + shared_module_qrio_qrdecoder_set_height(self, height); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(qrio_qrdecoder_set_height_obj, qrio_qrdecoder_set_height); + +MP_PROPERTY_GETSET(qrio_qrdecoder_height_obj, + (mp_obj_t)&qrio_qrdecoder_get_height_obj, + (mp_obj_t)&qrio_qrdecoder_set_height_obj); + +STATIC const mp_rom_map_elem_t qrio_qrdecoder_locals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_QRDecoder) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&qrio_qrdecoder_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&qrio_qrdecoder_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&qrio_qrdecoder_decode_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(qrio_qrdecoder_locals, qrio_qrdecoder_locals_table); + +const mp_obj_type_t qrio_qrdecoder_type_obj = { + { &mp_type_type }, + .name = MP_QSTR_QRDecoder, + .make_new = qrio_qrdecoder_make_new, + .locals_dict = (mp_obj_dict_t *)&qrio_qrdecoder_locals, +}; diff --git a/circuitpython/shared-bindings/qrio/QRDecoder.h b/circuitpython/shared-bindings/qrio/QRDecoder.h new file mode 100644 index 0000000..0b01aeb --- /dev/null +++ b/circuitpython/shared-bindings/qrio/QRDecoder.h @@ -0,0 +1,37 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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. + */ + +#pragma once + +#include "py/obj.h" + +typedef struct qrio_qrdecoder_obj qrio_qrdecoder_obj_t; + +extern const mp_obj_type_t qrio_qrdecoder_type_obj; + +void common_hal_qrio_qrdecoder_construct(qrio_qrdecoder_obj_t *self); + +mp_obj_t common_hal_qrio_qrdecoder_decode(qrio_qrdecoder_obj_t *self, int width, int height, mp_buffer_info_t *buf); diff --git a/circuitpython/shared-bindings/qrio/QRInfo.c b/circuitpython/shared-bindings/qrio/QRInfo.c new file mode 100644 index 0000000..8eb0387 --- /dev/null +++ b/circuitpython/shared-bindings/qrio/QRInfo.c @@ -0,0 +1,64 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * 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 "shared-bindings/qrio/__init__.h" +#include "shared-bindings/qrio/QRInfo.h" +#include "py/obj.h" +#include "py/enum.h" + +//| class QRInfo: +//| """Information about a decoded QR code""" +//| +//| payload: bytes +//| """The content of the QR code""" +//| +//| data_type: Union[str, int] +//| """The encoding of the payload as a string (if a standard encoding) or int (if not standard)""" + +const mp_obj_namedtuple_type_t qrio_qrinfo_type_obj = { + .base = { + .base = { + .type = &mp_type_type + }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_QRInfo, + .print = namedtuple_print, + .parent = &mp_type_tuple, + .make_new = namedtuple_make_new, + .attr = namedtuple_attr, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_obj_tuple_unary_op, + .binary_op = mp_obj_tuple_binary_op, + .subscr = mp_obj_tuple_subscr, + .getiter = mp_obj_tuple_getiter, + ), + }, + .n_fields = 2, + .fields = { + MP_QSTR_payload, + MP_QSTR_data_type, + }, +}; diff --git a/circuitpython/shared-bindings/qrio/QRInfo.h b/circuitpython/shared-bindings/qrio/QRInfo.h new file mode 100644 index 0000000..956cb42 --- /dev/null +++ b/circuitpython/shared-bindings/qrio/QRInfo.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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. + */ + +#pragma once + +#include "py/objnamedtuple.h" + +extern const mp_obj_namedtuple_type_t qrio_qrinfo_type_obj; diff --git a/circuitpython/shared-bindings/qrio/__init__.c b/circuitpython/shared-bindings/qrio/__init__.c new file mode 100644 index 0000000..04c86fd --- /dev/null +++ b/circuitpython/shared-bindings/qrio/__init__.c @@ -0,0 +1,59 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * 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 "shared-bindings/qrio/__init__.h" +#include "shared-bindings/qrio/QRDecoder.h" +#include "shared-bindings/qrio/QRInfo.h" +#include "shared-bindings/qrio/PixelPolicy.h" +#include "py/obj.h" +#include "py/enum.h" + +//| """Low-level QR code decoding +//| +//| Provides the `QRDecoder` object used for decoding QR codes. For more +//| information about working with QR codes, see +//| `this Learn guide <https://learn.adafruit.com/scan-qr-codes-with-circuitpython>`_. +//| +//| .. note:: This module only handles decoding QR codes. If you are looking +//| to generate a QR code, use the +//| `adafruit_miniqr library <https://github.com/adafruit/Adafruit_CircuitPython_miniQR>`_""" +//| + +STATIC const mp_rom_map_elem_t qrio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_qrio) }, + { MP_ROM_QSTR(MP_QSTR_QRInfo), MP_ROM_PTR(&qrio_qrinfo_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_QRDecoder), MP_ROM_PTR(&qrio_qrdecoder_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_PixelPolicy), MP_ROM_PTR(&qrio_pixel_policy_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(qrio_module_globals, qrio_module_globals_table); + +const mp_obj_module_t qrio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&qrio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_qrio, qrio_module, CIRCUITPY_QRIO); diff --git a/circuitpython/shared-bindings/qrio/__init__.h b/circuitpython/shared-bindings/qrio/__init__.h new file mode 100644 index 0000000..be9b3ae --- /dev/null +++ b/circuitpython/shared-bindings/qrio/__init__.h @@ -0,0 +1,27 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler 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. + */ + +#pragma once diff --git a/circuitpython/shared-bindings/rainbowio/__init__.c b/circuitpython/shared-bindings/rainbowio/__init__.c new file mode 100644 index 0000000..0dfdcd3 --- /dev/null +++ b/circuitpython/shared-bindings/rainbowio/__init__.c @@ -0,0 +1,57 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Kattni Rembor + * + * 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 "shared-bindings/rainbowio/__init__.h" +#include "py/mpconfig.h" +#include "py/obj.h" +//| """`rainbowio` module. +//| +//| Provides the `colorwheel()` function.""" +//| +//| def colorwheel(n: float) -> int: +//| """C implementation of the common colorwheel() function found in many examples. +//| Returns the colorwheel RGB value as an integer value for n (usable in neopixel and dotstar).""" +//| ... +//| +STATIC mp_obj_t rainbowio_colorwheel(mp_obj_t n) { + mp_float_t f = mp_obj_get_float(n); + return MP_OBJ_NEW_SMALL_INT(colorwheel(f)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(rainbowio_colorwheel_obj, rainbowio_colorwheel); + +STATIC const mp_rom_map_elem_t rainbowio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rainbowio) }, + { MP_ROM_QSTR(MP_QSTR_colorwheel), MP_ROM_PTR(&rainbowio_colorwheel_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(rainbowio_module_globals, rainbowio_module_globals_table); + +const mp_obj_module_t rainbowio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&rainbowio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_rainbowio, rainbowio_module, CIRCUITPY_RAINBOWIO); diff --git a/circuitpython/shared-bindings/rainbowio/__init__.h b/circuitpython/shared-bindings/rainbowio/__init__.h new file mode 100644 index 0000000..3599e3a --- /dev/null +++ b/circuitpython/shared-bindings/rainbowio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Kattni Rembor + * + * 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 CP_SHARED_BINDINGS_RAINBOWIO_INIT_H +#define CP_SHARED_BINDINGS_RAINBOWIO_INIT_H +#include <stdint.h> +#include "py/misc.h" + +int32_t colorwheel(mp_float_t pos); + +#endif // CP_SHARED_BINDINGS_RAINBOWIO_INIT_H diff --git a/circuitpython/shared-bindings/random/__init__.c b/circuitpython/shared-bindings/random/__init__.c new file mode 100644 index 0000000..63570fe --- /dev/null +++ b/circuitpython/shared-bindings/random/__init__.c @@ -0,0 +1,191 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * 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 <assert.h> +#include <string.h> + +#include "py/obj.h" +#include "py/runtime.h" +#include "shared-bindings/random/__init__.h" +#include "supervisor/shared/translate.h" + +//| """pseudo-random numbers and choices +//| +//| |see_cpython_module| :mod:`cpython:random`. +//| +//| Like its CPython cousin, CircuitPython's random seeds itself on first use +//| with a true random from os.urandom() when available or the uptime otherwise. +//| Once seeded, it will be deterministic, which is why its bad for cryptography. +//| +//| .. warning:: Numbers from this module are not cryptographically strong! Use +//| bytes from `os.urandom` directly for true randomness.""" +//| +//| from typing import TypeVar +//| _T = TypeVar('_T') +//| + +//| def seed(seed: int) -> None: +//| """Sets the starting seed of the random number generation. Further calls to +//| `random` will return deterministic results afterwards.""" +//| ... +//| +STATIC mp_obj_t random_seed(mp_obj_t seed_in) { + mp_uint_t seed = mp_obj_get_int_truncated(seed_in); + shared_modules_random_seed(seed); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_seed_obj, random_seed); + +//| def getrandbits(k: int) -> int: +//| """Returns an integer with *k* random bits.""" +//| ... +//| +STATIC mp_obj_t random_getrandbits(mp_obj_t num_in) { + int n = mp_obj_get_int(num_in); + if (n > 32 || n == 0) { + mp_raise_ValueError(NULL); + } + return mp_obj_new_int_from_uint(shared_modules_random_getrandbits((uint8_t)n)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_getrandbits_obj, random_getrandbits); + +//| @overload +//| def randrange(stop: int) -> int: ... +//| @overload +//| def randrange(start: int, stop: int) -> int: ... +//| @overload +//| def randrange(start: int, stop: int, step: int) -> int: +//| """Returns a randomly selected integer from ``range(start[, stop[, step]])``.""" +//| ... +//| +STATIC mp_obj_t random_randrange(size_t n_args, const mp_obj_t *args) { + mp_int_t start = 0; + mp_int_t stop = mp_obj_get_int(args[0]); + mp_int_t step = 1; + if (n_args == 1) { + // range(stop) + if (stop <= 0) { + mp_raise_ValueError(translate("stop not reachable from start")); + } + } else { + start = stop; + stop = mp_obj_get_int(args[1]); + if (n_args == 2) { + // range(start, stop) + if (start >= stop) { + mp_raise_ValueError(translate("stop not reachable from start")); + } + } else { + // range(start, stop, step) + step = mp_obj_get_int(args[2]); + mp_int_t n; + if (step > 0) { + n = (stop - start + step - 1) / step; + } else if (step < 0) { + n = (stop - start + step + 1) / step; + } else { + mp_raise_ValueError(translate("step must be non-zero")); + } + if (n <= 0) { + mp_raise_ValueError(translate("invalid step")); + } + } + } + + return mp_obj_new_int(shared_modules_random_randrange(start, stop, step)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(random_randrange_obj, 1, 3, random_randrange); + +//| def randint(a: int, b: int) -> int: +//| """Returns a randomly selected integer between a and b inclusive. Equivalent +//| to ``randrange(a, b + 1, 1)``""" +//| ... +//| +STATIC mp_obj_t random_randint(mp_obj_t a_in, mp_obj_t b_in) { + mp_int_t a = mp_obj_get_int(a_in); + mp_int_t b = mp_obj_get_int(b_in); + if (a > b) { + mp_raise_ValueError(NULL); + } + return mp_obj_new_int(shared_modules_random_randrange(a, b + 1, 1)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(random_randint_obj, random_randint); + +//| def choice(seq: Sequence[_T]) -> _T: +//| """Returns a randomly selected element from the given sequence. Raises +//| IndexError when the sequence is empty.""" +//| ... +//| +STATIC mp_obj_t random_choice(mp_obj_t seq) { + mp_int_t len = mp_obj_get_int(mp_obj_len(seq)); + if (len == 0) { + mp_raise_IndexError(translate("empty sequence")); + } + return mp_obj_subscr(seq, mp_obj_new_int(shared_modules_random_randrange(0, len, 1)), MP_OBJ_SENTINEL); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_choice_obj, random_choice); + +//| def random() -> float: +//| """Returns a random float between 0 and 1.0.""" +//| ... +//| +STATIC mp_obj_t random_random(void) { + return mp_obj_new_float(shared_modules_random_random()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(random_random_obj, random_random); + +//| def uniform(a: float, b: float) -> float: +//| """Returns a random float between a and b. It may or may not be inclusive +//| depending on float rounding.""" +//| ... +//| +STATIC mp_obj_t random_uniform(mp_obj_t a_in, mp_obj_t b_in) { + mp_float_t a = mp_obj_get_float(a_in); + mp_float_t b = mp_obj_get_float(b_in); + return mp_obj_new_float(shared_modules_random_uniform(a, b)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(random_uniform_obj, random_uniform); + +STATIC const mp_rom_map_elem_t mp_module_random_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) }, + { MP_ROM_QSTR(MP_QSTR_seed), MP_ROM_PTR(&random_seed_obj) }, + { MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&random_getrandbits_obj) }, + { MP_ROM_QSTR(MP_QSTR_randrange), MP_ROM_PTR(&random_randrange_obj) }, + { MP_ROM_QSTR(MP_QSTR_randint), MP_ROM_PTR(&random_randint_obj) }, + { MP_ROM_QSTR(MP_QSTR_choice), MP_ROM_PTR(&random_choice_obj) }, + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&random_random_obj) }, + { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_random_globals, mp_module_random_globals_table); + +const mp_obj_module_t random_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_random_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_random, random_module, CIRCUITPY_RANDOM); diff --git a/circuitpython/shared-bindings/random/__init__.h b/circuitpython/shared-bindings/random/__init__.h new file mode 100644 index 0000000..9e05805 --- /dev/null +++ b/circuitpython/shared-bindings/random/__init__.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_RANDOM___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_RANDOM___INIT___H + +// This depends on shared_module because nearly all functionality is port +// agnostic. The random module only depends on the common_hal_os_urandom or +// common_hal_time_monotonic_ms to seed it initially. + +void shared_modules_random_seed(mp_uint_t seed); +mp_uint_t shared_modules_random_getrandbits(uint8_t n); +mp_int_t shared_modules_random_randrange(mp_int_t start, mp_int_t stop, mp_int_t step); +mp_float_t shared_modules_random_random(void); +mp_float_t shared_modules_random_uniform(mp_float_t a, mp_float_t b); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_RANDOM___INIT___H diff --git a/circuitpython/shared-bindings/rgbmatrix/RGBMatrix.c b/circuitpython/shared-bindings/rgbmatrix/RGBMatrix.c new file mode 100644 index 0000000..4739807 --- /dev/null +++ b/circuitpython/shared-bindings/rgbmatrix/RGBMatrix.c @@ -0,0 +1,447 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "common-hal/rgbmatrix/RGBMatrix.h" +#include "shared-bindings/rgbmatrix/RGBMatrix.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" + +//| class RGBMatrix: +//| """Displays an in-memory framebuffer to a HUB75-style RGB LED matrix.""" +//| + +extern Protomatter_core *_PM_protoPtr; + +STATIC uint8_t validate_pin(mp_obj_t obj) { + const mcu_pin_obj_t *result = validate_obj_is_free_pin(obj); + return common_hal_mcu_pin_number(result); +} + +STATIC void claim_and_never_reset_pin(mp_obj_t pin) { + common_hal_mcu_pin_claim(pin); + common_hal_never_reset_pin(pin); +} + +STATIC void claim_and_never_reset_pins(mp_obj_t seq) { + mp_int_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(seq)); + for (mp_int_t i = 0; i < len; i++) { + claim_and_never_reset_pin(mp_obj_subscr(seq, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + } +} + +STATIC void preflight_pins_or_throw(uint8_t clock_pin, uint8_t *rgb_pins, uint8_t rgb_pin_count, bool allow_inefficient) { + uint32_t port = clock_pin / 32; + uint32_t bit_mask = 1 << (clock_pin % 32); + + if (rgb_pin_count <= 0 || rgb_pin_count % 6 != 0 || rgb_pin_count > 30) { + mp_raise_ValueError_varg(translate("The length of rgb_pins must be 6, 12, 18, 24, or 30")); + } + + for (uint8_t i = 0; i < rgb_pin_count; i++) { + uint32_t pin_port = rgb_pins[i] / 32; + + if (pin_port != port) { + mp_raise_ValueError_varg( + translate("rgb_pins[%d] is not on the same port as clock"), i); + } + + uint32_t pin_mask = 1 << (rgb_pins[i] % 32); + if (pin_mask & bit_mask) { + mp_raise_ValueError_varg( + translate("rgb_pins[%d] duplicates another pin assignment"), i); + } + + bit_mask |= pin_mask; + } + + if (allow_inefficient) { + return; + } + + uint8_t byte_mask = 0; + if (bit_mask & 0x000000FF) { + byte_mask |= 0b0001; + } + if (bit_mask & 0x0000FF00) { + byte_mask |= 0b0010; + } + if (bit_mask & 0x00FF0000) { + byte_mask |= 0b0100; + } + if (bit_mask & 0xFF000000) { + byte_mask |= 0b1000; + } + + uint8_t bytes_per_element = 0xff; + uint8_t ideal_bytes_per_element = (rgb_pin_count + 7) / 8; + + switch (byte_mask) { + case 0b0001: + case 0b0010: + case 0b0100: + case 0b1000: + bytes_per_element = 1; + break; + + case 0b0011: + case 0b1100: + bytes_per_element = 2; + break; + + default: + bytes_per_element = 4; + break; + } + + if (bytes_per_element != ideal_bytes_per_element) { + mp_raise_ValueError_varg( + translate("Pinout uses %d bytes per element, which consumes more than the ideal %d bytes. If this cannot be avoided, pass allow_inefficient=True to the constructor"), + bytes_per_element, ideal_bytes_per_element); + } +} + +//| def __init__(self, *, width: int, bit_depth: int, +//| rgb_pins: Sequence[digitalio.DigitalInOut], +//| addr_pins: Sequence[digitalio.DigitalInOut], +//| clock_pin: digitalio.DigitalInOut, +//| latch_pin: digitalio.DigitalInOut, +//| output_enable_pin: digitalio.DigitalInOut, +//| doublebuffer: bool = True, +//| framebuffer: Optional[WriteableBuffer] = None, +//| height: int = 0, tile: int = 1, serpentine: bool = True) -> None: +//| """Create a RGBMatrix object with the given attributes. The height of +//| the display is determined by the number of rgb and address pins and the number of tiles: +//| ``len(rgb_pins) // 3 * 2 ** len(address_pins) * abs(tile)``. With 6 RGB pins, 4 +//| address lines, and a single matrix, the display will be 32 pixels tall. If the optional height +//| parameter is specified and is not 0, it is checked against the calculated +//| height. +//| +//| Up to 30 RGB pins and 8 address pins are supported. +//| +//| The RGB pins must be within a single "port" and performance and memory +//| usage are best when they are all within "close by" bits of the port. +//| The clock pin must also be on the same port as the RGB pins. See the +//| documentation of the underlying protomatter C library for more +//| information. Generally, Adafruit's interface boards are designed so +//| that these requirements are met when matched with the intended +//| microcontroller board. For instance, the Feather M4 Express works +//| together with the RGB Matrix Feather. +//| +//| The framebuffer is in "RGB565" format. +//| +//| "RGB565" means that it is organized as a series of 16-bit numbers +//| where the highest 5 bits are interpreted as red, the next 6 as +//| green, and the final 5 as blue. The object can be any buffer, but +//| `array.array` and ``ulab.ndarray`` objects are most often useful. +//| To update the content, modify the framebuffer and call refresh. +//| +//| If a framebuffer is not passed in, one is allocated and initialized +//| to all black. In any case, the framebuffer can be retrieved +//| by passing the RGBMatrix object to memoryview(). +//| +//| If doublebuffer is False, some memory is saved, but the display may +//| flicker during updates. +//| +//| A RGBMatrix is often used in conjunction with a +//| `framebufferio.FramebufferDisplay`.""" +//| + +STATIC mp_obj_t rgbmatrix_rgbmatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_width, ARG_bit_depth, ARG_rgb_list, ARG_addr_list, + ARG_clock_pin, ARG_latch_pin, ARG_output_enable_pin, ARG_doublebuffer, ARG_framebuffer, ARG_height, ARG_tile, ARG_serpentine }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_bit_depth, MP_ARG_INT | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_rgb_pins, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_addr_pins, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_clock_pin, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_latch_pin, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_output_enable_pin, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_doublebuffer, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = true } }, + { MP_QSTR_framebuffer, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = mp_const_none } }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 0 } }, + { MP_QSTR_tile, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 1 } }, + { MP_QSTR_serpentine, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = true } }, + }; + 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); + + rgbmatrix_rgbmatrix_obj_t *self = &allocate_display_bus_or_raise()->rgbmatrix; + self->base.type = &rgbmatrix_RGBMatrix_type; + + uint8_t rgb_count, addr_count; + uint8_t rgb_pins[MP_ARRAY_SIZE(self->rgb_pins)]; + uint8_t addr_pins[MP_ARRAY_SIZE(self->addr_pins)]; + uint8_t clock_pin = validate_pin(args[ARG_clock_pin].u_obj); + uint8_t latch_pin = validate_pin(args[ARG_latch_pin].u_obj); + uint8_t output_enable_pin = validate_pin(args[ARG_output_enable_pin].u_obj); + int bit_depth = args[ARG_bit_depth].u_int; + + if (bit_depth <= 0 || bit_depth > 6) { + mp_raise_ValueError_varg(translate("Bit depth must be from 1 to 6 inclusive, not %d"), bit_depth); + } + + validate_pins(MP_QSTR_rgb_pins, rgb_pins, MP_ARRAY_SIZE(self->rgb_pins), args[ARG_rgb_list].u_obj, &rgb_count); + validate_pins(MP_QSTR_addr_pins, addr_pins, MP_ARRAY_SIZE(self->addr_pins), args[ARG_addr_list].u_obj, &addr_count); + + if (rgb_count % 6) { + mp_raise_ValueError_varg(translate("Must use a multiple of 6 rgb pins, not %d"), rgb_count); + } + + int tile = args[ARG_tile].u_int; + + if (tile <= 0) { + mp_raise_ValueError_varg( + translate("tile must be greater than zero")); + } + + int computed_height = (rgb_count / 3) * (1 << (addr_count)) * tile; + if (args[ARG_height].u_int != 0) { + if (computed_height != args[ARG_height].u_int) { + mp_raise_ValueError_varg( + translate("%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d"), addr_count, rgb_count, tile, computed_height, args[ARG_height].u_int); + } + } + + if (args[ARG_width].u_int <= 0) { + mp_raise_ValueError(translate("width must be greater than zero")); + } + + preflight_pins_or_throw(clock_pin, rgb_pins, rgb_count, true); + + mp_obj_t framebuffer = args[ARG_framebuffer].u_obj; + if (framebuffer == mp_const_none) { + int width = args[ARG_width].u_int; + int bufsize = 2 * width * computed_height; + framebuffer = mp_obj_new_bytearray_of_zeros(bufsize); + } + + common_hal_rgbmatrix_rgbmatrix_construct(self, + args[ARG_width].u_int, + bit_depth, + rgb_count, rgb_pins, + addr_count, addr_pins, + clock_pin, latch_pin, output_enable_pin, + args[ARG_doublebuffer].u_bool, + framebuffer, tile, args[ARG_serpentine].u_bool, NULL); + + claim_and_never_reset_pins(args[ARG_rgb_list].u_obj); + claim_and_never_reset_pins(args[ARG_addr_list].u_obj); + claim_and_never_reset_pin(args[ARG_clock_pin].u_obj); + claim_and_never_reset_pin(args[ARG_output_enable_pin].u_obj); + claim_and_never_reset_pin(args[ARG_latch_pin].u_obj); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Free the resources (pins, timers, etc.) associated with this +//| rgbmatrix instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +STATIC mp_obj_t rgbmatrix_rgbmatrix_deinit(mp_obj_t self_in) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + common_hal_rgbmatrix_rgbmatrix_deinit(self); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(rgbmatrix_rgbmatrix_deinit_obj, rgbmatrix_rgbmatrix_deinit); + +static void check_for_deinit(rgbmatrix_rgbmatrix_obj_t *self) { + if (!self->protomatter.rgbPins) { + raise_deinited_error(); + } +} + +//| brightness: float +//| """In the current implementation, 0.0 turns the display off entirely +//| and any other value up to 1.0 turns the display on fully.""" +//| +STATIC mp_obj_t rgbmatrix_rgbmatrix_get_brightness(mp_obj_t self_in) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + check_for_deinit(self); + return mp_obj_new_float(common_hal_rgbmatrix_rgbmatrix_get_paused(self)? 0.0f : 1.0f); +} +MP_DEFINE_CONST_FUN_OBJ_1(rgbmatrix_rgbmatrix_get_brightness_obj, rgbmatrix_rgbmatrix_get_brightness); + +STATIC mp_obj_t rgbmatrix_rgbmatrix_set_brightness(mp_obj_t self_in, mp_obj_t value_in) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + check_for_deinit(self); + mp_float_t brightness = mp_obj_get_float(value_in); + if (brightness < 0.0f || brightness > 1.0f) { + mp_raise_ValueError(translate("Brightness must be 0-1.0")); + } + common_hal_rgbmatrix_rgbmatrix_set_paused(self, brightness <= 0); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(rgbmatrix_rgbmatrix_set_brightness_obj, rgbmatrix_rgbmatrix_set_brightness); + +MP_PROPERTY_GETSET(rgbmatrix_rgbmatrix_brightness_obj, + (mp_obj_t)&rgbmatrix_rgbmatrix_get_brightness_obj, + (mp_obj_t)&rgbmatrix_rgbmatrix_set_brightness_obj); + +//| def refresh(self) -> None: +//| """Transmits the color data in the buffer to the pixels so that +//| they are shown.""" +//| ... +//| +STATIC mp_obj_t rgbmatrix_rgbmatrix_refresh(mp_obj_t self_in) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + check_for_deinit(self); + common_hal_rgbmatrix_rgbmatrix_refresh(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(rgbmatrix_rgbmatrix_refresh_obj, rgbmatrix_rgbmatrix_refresh); + +//| width: int +//| """The width of the display, in pixels""" +//| +STATIC mp_obj_t rgbmatrix_rgbmatrix_get_width(mp_obj_t self_in) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_rgbmatrix_rgbmatrix_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(rgbmatrix_rgbmatrix_get_width_obj, rgbmatrix_rgbmatrix_get_width); +MP_PROPERTY_GETTER(rgbmatrix_rgbmatrix_width_obj, + (mp_obj_t)&rgbmatrix_rgbmatrix_get_width_obj); + +//| height: int +//| """The height of the display, in pixels""" +//| +STATIC mp_obj_t rgbmatrix_rgbmatrix_get_height(mp_obj_t self_in) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_rgbmatrix_rgbmatrix_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(rgbmatrix_rgbmatrix_get_height_obj, rgbmatrix_rgbmatrix_get_height); + +MP_PROPERTY_GETTER(rgbmatrix_rgbmatrix_height_obj, + (mp_obj_t)&rgbmatrix_rgbmatrix_get_height_obj); + +STATIC const mp_rom_map_elem_t rgbmatrix_rgbmatrix_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&rgbmatrix_rgbmatrix_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&rgbmatrix_rgbmatrix_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&rgbmatrix_rgbmatrix_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&rgbmatrix_rgbmatrix_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&rgbmatrix_rgbmatrix_height_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(rgbmatrix_rgbmatrix_locals_dict, rgbmatrix_rgbmatrix_locals_dict_table); + +STATIC void rgbmatrix_rgbmatrix_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + check_for_deinit(self); + + *bufinfo = self->bufinfo; +} + +// These version exists so that the prototype matches the protocol, +// avoiding a type cast that can hide errors +STATIC void rgbmatrix_rgbmatrix_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) { + (void)dirty_row_bitmap; + common_hal_rgbmatrix_rgbmatrix_refresh(self_in); +} + +STATIC void rgbmatrix_rgbmatrix_deinit_proto(mp_obj_t self_in) { + common_hal_rgbmatrix_rgbmatrix_deinit(self_in); +} + +STATIC float rgbmatrix_rgbmatrix_get_brightness_proto(mp_obj_t self_in) { + return common_hal_rgbmatrix_rgbmatrix_get_paused(self_in) ? 0.0f : 1.0f; +} + +STATIC bool rgbmatrix_rgbmatrix_set_brightness_proto(mp_obj_t self_in, mp_float_t value) { + common_hal_rgbmatrix_rgbmatrix_set_paused(self_in, value <= 0); + return true; +} + +STATIC int rgbmatrix_rgbmatrix_get_width_proto(mp_obj_t self_in) { + return common_hal_rgbmatrix_rgbmatrix_get_width(self_in); +} + +STATIC int rgbmatrix_rgbmatrix_get_height_proto(mp_obj_t self_in) { + return common_hal_rgbmatrix_rgbmatrix_get_height(self_in); +} + +STATIC int rgbmatrix_rgbmatrix_get_color_depth_proto(mp_obj_t self_in) { + return 16; +} + +STATIC int rgbmatrix_rgbmatrix_get_bytes_per_cell_proto(mp_obj_t self_in) { + return 1; +} + +STATIC int rgbmatrix_rgbmatrix_get_native_frames_per_second_proto(mp_obj_t self_in) { + return 250; +} + + +STATIC const framebuffer_p_t rgbmatrix_rgbmatrix_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) + .get_bufinfo = rgbmatrix_rgbmatrix_get_bufinfo, + .set_brightness = rgbmatrix_rgbmatrix_set_brightness_proto, + .get_brightness = rgbmatrix_rgbmatrix_get_brightness_proto, + .get_width = rgbmatrix_rgbmatrix_get_width_proto, + .get_height = rgbmatrix_rgbmatrix_get_height_proto, + .get_color_depth = rgbmatrix_rgbmatrix_get_color_depth_proto, + .get_bytes_per_cell = rgbmatrix_rgbmatrix_get_bytes_per_cell_proto, + .get_native_frames_per_second = rgbmatrix_rgbmatrix_get_native_frames_per_second_proto, + .swapbuffers = rgbmatrix_rgbmatrix_swapbuffers, + .deinit = rgbmatrix_rgbmatrix_deinit_proto, +}; + +STATIC mp_int_t rgbmatrix_rgbmatrix_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + rgbmatrix_rgbmatrix_obj_t *self = (rgbmatrix_rgbmatrix_obj_t *)self_in; + // a readonly framebuffer would be unusual but not impossible + if ((flags & MP_BUFFER_WRITE) && !(self->bufinfo.typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) { + return 1; + } + *bufinfo = self->bufinfo; + bufinfo->typecode = 'H'; + return 0; +} + +const mp_obj_type_t rgbmatrix_RGBMatrix_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_RGBMatrix, + .locals_dict = (mp_obj_dict_t *)&rgbmatrix_rgbmatrix_locals_dict, + .make_new = rgbmatrix_rgbmatrix_make_new, + MP_TYPE_EXTENDED_FIELDS( + .buffer_p = { .get_buffer = rgbmatrix_rgbmatrix_get_buffer, }, + .protocol = &rgbmatrix_rgbmatrix_proto, + ), +}; diff --git a/circuitpython/shared-bindings/rgbmatrix/RGBMatrix.h b/circuitpython/shared-bindings/rgbmatrix/RGBMatrix.h new file mode 100644 index 0000000..127f920 --- /dev/null +++ b/circuitpython/shared-bindings/rgbmatrix/RGBMatrix.h @@ -0,0 +1,42 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +#include "shared-module/rgbmatrix/RGBMatrix.h" +#include "lib/protomatter/src/core.h" + +extern const mp_obj_type_t rgbmatrix_RGBMatrix_type; + +void common_hal_rgbmatrix_rgbmatrix_construct(rgbmatrix_rgbmatrix_obj_t *self, int width, int bit_depth, uint8_t rgb_count, uint8_t *rgb_pins, uint8_t addr_count, uint8_t *addr_pins, uint8_t clock_pin, uint8_t latch_pin, uint8_t oe_pin, bool doublebuffer, mp_obj_t framebuffer, int8_t tile, bool serpentine, void *timer); +void common_hal_rgbmatrix_rgbmatrix_deinit(rgbmatrix_rgbmatrix_obj_t *); +void rgbmatrix_rgbmatrix_collect_ptrs(rgbmatrix_rgbmatrix_obj_t *); +void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t *self, mp_obj_t framebuffer); +void common_hal_rgbmatrix_rgbmatrix_set_paused(rgbmatrix_rgbmatrix_obj_t *self, bool paused); +bool common_hal_rgbmatrix_rgbmatrix_get_paused(rgbmatrix_rgbmatrix_obj_t *self); +void common_hal_rgbmatrix_rgbmatrix_refresh(rgbmatrix_rgbmatrix_obj_t *self); +int common_hal_rgbmatrix_rgbmatrix_get_width(rgbmatrix_rgbmatrix_obj_t *self); +int common_hal_rgbmatrix_rgbmatrix_get_height(rgbmatrix_rgbmatrix_obj_t *self); diff --git a/circuitpython/shared-bindings/rgbmatrix/__init__.c b/circuitpython/shared-bindings/rgbmatrix/__init__.c new file mode 100644 index 0000000..a7da708 --- /dev/null +++ b/circuitpython/shared-bindings/rgbmatrix/__init__.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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/rgbmatrix/RGBMatrix.h" + +//| """Low-level routines for bitbanged LED matrices""" +//| + +STATIC const mp_rom_map_elem_t rgbmatrix_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rgbmatrix) }, + { MP_ROM_QSTR(MP_QSTR_RGBMatrix), MP_ROM_PTR(&rgbmatrix_RGBMatrix_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(rgbmatrix_module_globals, rgbmatrix_module_globals_table); + +const mp_obj_module_t rgbmatrix_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&rgbmatrix_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_rgbmatrix, rgbmatrix_module, CIRCUITPY_RGBMATRIX); diff --git a/circuitpython/shared-bindings/rotaryio/IncrementalEncoder.c b/circuitpython/shared-bindings/rotaryio/IncrementalEncoder.c new file mode 100644 index 0000000..0fe22be --- /dev/null +++ b/circuitpython/shared-bindings/rotaryio/IncrementalEncoder.c @@ -0,0 +1,188 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/rotaryio/IncrementalEncoder.h" +#include "shared-bindings/util.h" + +//| class IncrementalEncoder: +//| """IncrementalEncoder determines the relative rotational position based on two series of pulses.""" +//| +//| def __init__(self, pin_a: microcontroller.Pin, pin_b: microcontroller.Pin, divisor: int = 4) -> None: +//| """Create an IncrementalEncoder object associated with the given pins. It tracks the positional +//| state of an incremental rotary encoder (also known as a quadrature encoder.) Position is +//| relative to the position when the object is contructed. +//| +//| :param ~microcontroller.Pin pin_a: First pin to read pulses from. +//| :param ~microcontroller.Pin pin_b: Second pin to read pulses from. +//| :param int divisor: The divisor of the quadrature signal. +//| +//| For example:: +//| +//| import rotaryio +//| import time +//| from board import * +//| +//| enc = rotaryio.IncrementalEncoder(D1, D2) +//| last_position = None +//| while True: +//| position = enc.position +//| if last_position == None or position != last_position: +//| print(position) +//| last_position = position""" +//| ... +//| +STATIC mp_obj_t rotaryio_incrementalencoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pin_a, ARG_pin_b, ARG_divisor }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin_a, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_pin_b, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_divisor, MP_ARG_INT, { .u_int = 4 } }, + }; + 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 *pin_a = validate_obj_is_free_pin(args[ARG_pin_a].u_obj); + const mcu_pin_obj_t *pin_b = validate_obj_is_free_pin(args[ARG_pin_b].u_obj); + + rotaryio_incrementalencoder_obj_t *self = m_new_obj(rotaryio_incrementalencoder_obj_t); + self->base.type = &rotaryio_incrementalencoder_type; + + common_hal_rotaryio_incrementalencoder_construct(self, pin_a, pin_b); + common_hal_rotaryio_incrementalencoder_set_divisor(self, args[ARG_divisor].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitializes the IncrementalEncoder and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t rotaryio_incrementalencoder_deinit(mp_obj_t self_in) { + rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_rotaryio_incrementalencoder_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(rotaryio_incrementalencoder_deinit_obj, rotaryio_incrementalencoder_deinit); + +STATIC void check_for_deinit(rotaryio_incrementalencoder_obj_t *self) { + if (common_hal_rotaryio_incrementalencoder_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> IncrementalEncoder: +//| """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 rotaryio_incrementalencoder_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_rotaryio_incrementalencoder_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rotaryio_incrementalencoder___exit___obj, 4, 4, rotaryio_incrementalencoder_obj___exit__); + + +//| divisor: int +//| """The divisor of the quadrature signal. Use 1 for encoders without +//| detents, or encoders with 4 detents per cycle. Use 2 for encoders with 2 +//| detents per cycle. Use 4 for encoders with 1 detent per cycle.""" +//| +STATIC mp_obj_t rotaryio_incrementalencoder_obj_get_divisor(mp_obj_t self_in) { + rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return mp_obj_new_int(common_hal_rotaryio_incrementalencoder_get_divisor(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(rotaryio_incrementalencoder_get_divisor_obj, rotaryio_incrementalencoder_obj_get_divisor); + +STATIC mp_obj_t rotaryio_incrementalencoder_obj_set_divisor(mp_obj_t self_in, mp_obj_t new_divisor) { + rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_rotaryio_incrementalencoder_set_divisor(self, mp_obj_get_int(new_divisor)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(rotaryio_incrementalencoder_set_divisor_obj, rotaryio_incrementalencoder_obj_set_divisor); + +MP_PROPERTY_GETSET(rotaryio_incrementalencoder_divisor_obj, + (mp_obj_t)&rotaryio_incrementalencoder_get_divisor_obj, + (mp_obj_t)&rotaryio_incrementalencoder_set_divisor_obj); + +//| position: int +//| """The current position in terms of pulses. The number of pulses per rotation is defined by the +//| specific hardware and by the divisor.""" +//| +STATIC mp_obj_t rotaryio_incrementalencoder_obj_get_position(mp_obj_t self_in) { + rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return mp_obj_new_int(common_hal_rotaryio_incrementalencoder_get_position(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(rotaryio_incrementalencoder_get_position_obj, rotaryio_incrementalencoder_obj_get_position); + +STATIC mp_obj_t rotaryio_incrementalencoder_obj_set_position(mp_obj_t self_in, mp_obj_t new_position) { + rotaryio_incrementalencoder_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_rotaryio_incrementalencoder_set_position(self, mp_obj_get_int(new_position)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(rotaryio_incrementalencoder_set_position_obj, rotaryio_incrementalencoder_obj_set_position); + +MP_PROPERTY_GETSET(rotaryio_incrementalencoder_position_obj, + (mp_obj_t)&rotaryio_incrementalencoder_get_position_obj, + (mp_obj_t)&rotaryio_incrementalencoder_set_position_obj); + +STATIC const mp_rom_map_elem_t rotaryio_incrementalencoder_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&rotaryio_incrementalencoder_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&rotaryio_incrementalencoder___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_position), MP_ROM_PTR(&rotaryio_incrementalencoder_position_obj) }, + { MP_ROM_QSTR(MP_QSTR_divisor), MP_ROM_PTR(&rotaryio_incrementalencoder_divisor_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(rotaryio_incrementalencoder_locals_dict, rotaryio_incrementalencoder_locals_dict_table); + +const mp_obj_type_t rotaryio_incrementalencoder_type = { + { &mp_type_type }, + .name = MP_QSTR_IncrementalEncoder, + .make_new = rotaryio_incrementalencoder_make_new, + .locals_dict = (mp_obj_dict_t *)&rotaryio_incrementalencoder_locals_dict, +}; diff --git a/circuitpython/shared-bindings/rotaryio/IncrementalEncoder.h b/circuitpython/shared-bindings/rotaryio/IncrementalEncoder.h new file mode 100644 index 0000000..45551c3 --- /dev/null +++ b/circuitpython/shared-bindings/rotaryio/IncrementalEncoder.h @@ -0,0 +1,46 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_ROTARYIO_INCREMENTALENCODER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ROTARYIO_INCREMENTALENCODER_H + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/rotaryio/IncrementalEncoder.h" + +extern const mp_obj_type_t rotaryio_incrementalencoder_type; + +extern void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, + const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b); +extern void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self); +extern bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self); +extern mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self); +extern void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, + mp_int_t new_position); +extern mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self); +extern void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self, + mp_int_t new_divisor); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ROTARYIO_INCREMENTALENCODER_H diff --git a/circuitpython/shared-bindings/rotaryio/__init__.c b/circuitpython/shared-bindings/rotaryio/__init__.c new file mode 100644 index 0000000..43a884e --- /dev/null +++ b/circuitpython/shared-bindings/rotaryio/__init__.c @@ -0,0 +1,60 @@ +/* + * 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/rotaryio/__init__.h" +#include "shared-bindings/rotaryio/IncrementalEncoder.h" + +//| """Support for reading rotation sensors +//| +//| The `rotaryio` module contains classes to read different rotation encoding schemes. See +//| `Wikipedia's Rotary Encoder page <https://en.wikipedia.org/wiki/Rotary_encoder>`_ for more +//| background. +//| +//| 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.""" +//| + +STATIC const mp_rom_map_elem_t rotaryio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rotaryio) }, + { MP_ROM_QSTR(MP_QSTR_IncrementalEncoder), MP_ROM_PTR(&rotaryio_incrementalencoder_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(rotaryio_module_globals, rotaryio_module_globals_table); + +const mp_obj_module_t rotaryio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&rotaryio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_rotaryio, rotaryio_module, CIRCUITPY_ROTARYIO); diff --git a/circuitpython/shared-bindings/rotaryio/__init__.h b/circuitpython/shared-bindings/rotaryio/__init__.h new file mode 100644 index 0000000..5d051d5 --- /dev/null +++ b/circuitpython/shared-bindings/rotaryio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_ROTARYIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ROTARYIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ROTARYIO___INIT___H diff --git a/circuitpython/shared-bindings/rtc/RTC.c b/circuitpython/shared-bindings/rtc/RTC.c new file mode 100644 index 0000000..b07f90b --- /dev/null +++ b/circuitpython/shared-bindings/rtc/RTC.c @@ -0,0 +1,133 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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 <string.h> + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared/timeutils/timeutils.h" +#include "shared-bindings/rtc/__init__.h" +#include "shared-bindings/rtc/RTC.h" +#include "shared-bindings/time/__init__.h" +#include "supervisor/shared/translate.h" + +const rtc_rtc_obj_t rtc_rtc_obj = {{&rtc_rtc_type}}; + +//| class RTC: +//| """Real Time Clock""" +//| +//| def __init__(self) -> None: +//| """This class represents the onboard Real Time Clock. It is a singleton and will always return the same instance.""" +//| ... +//| +STATIC mp_obj_t rtc_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // No arguments + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // return constant object + return (mp_obj_t)&rtc_rtc_obj; +} + +//| datetime: time.struct_time +//| """The current date and time of the RTC as a `time.struct_time`. +//| +//| This must be set to the current date and time whenever the board loses power:: +//| +//| import rtc +//| import time +//| +//| r = rtc.RTC() +//| r.datetime = time.struct_time((2019, 5, 29, 15, 14, 15, 0, -1, -1)) +//| +//| +//| Once set, the RTC will automatically update this value as time passes. You can read this +//| property to get a snapshot of the current time:: +//| +//| current_time = r.datetime +//| print(current_time) +//| # struct_time(tm_year=2019, tm_month=5, ...)""" +//| +STATIC mp_obj_t rtc_rtc_obj_get_datetime(mp_obj_t self_in) { + timeutils_struct_time_t tm; + common_hal_rtc_get_time(&tm); + return struct_time_from_tm(&tm); +} +MP_DEFINE_CONST_FUN_OBJ_1(rtc_rtc_get_datetime_obj, rtc_rtc_obj_get_datetime); + +STATIC mp_obj_t rtc_rtc_obj_set_datetime(mp_obj_t self_in, mp_obj_t datetime) { + timeutils_struct_time_t tm; + struct_time_to_tm(datetime, &tm); + common_hal_rtc_set_time(&tm); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(rtc_rtc_set_datetime_obj, rtc_rtc_obj_set_datetime); + +MP_PROPERTY_GETSET(rtc_rtc_datetime_obj, + (mp_obj_t)&rtc_rtc_get_datetime_obj, + (mp_obj_t)&rtc_rtc_set_datetime_obj); + +//| calibration: int +//| """The RTC calibration value as an `int`. +//| +//| A positive value speeds up the clock and a negative value slows it down. +//| Range and value is hardware specific, but one step is often approximately 1 ppm:: +//| +//| import rtc +//| import time +//| +//| r = rtc.RTC() +//| r.calibration = 1""" +//| +STATIC mp_obj_t rtc_rtc_obj_get_calibration(mp_obj_t self_in) { + int calibration = common_hal_rtc_get_calibration(); + return mp_obj_new_int(calibration); +} +MP_DEFINE_CONST_FUN_OBJ_1(rtc_rtc_get_calibration_obj, rtc_rtc_obj_get_calibration); + +STATIC mp_obj_t rtc_rtc_obj_set_calibration(mp_obj_t self_in, mp_obj_t calibration) { + common_hal_rtc_set_calibration(mp_obj_get_int(calibration)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(rtc_rtc_set_calibration_obj, rtc_rtc_obj_set_calibration); + +MP_PROPERTY_GETSET(rtc_rtc_calibration_obj, + (mp_obj_t)&rtc_rtc_get_calibration_obj, + (mp_obj_t)&rtc_rtc_set_calibration_obj); + +STATIC const mp_rom_map_elem_t rtc_rtc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&rtc_rtc_datetime_obj) }, + { MP_ROM_QSTR(MP_QSTR_calibration), MP_ROM_PTR(&rtc_rtc_calibration_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(rtc_rtc_locals_dict, rtc_rtc_locals_dict_table); + +const mp_obj_type_t rtc_rtc_type = { + { &mp_type_type }, + .name = MP_QSTR_RTC, + .make_new = rtc_rtc_make_new, + .locals_dict = (mp_obj_dict_t *)&rtc_rtc_locals_dict, +}; diff --git a/circuitpython/shared-bindings/rtc/RTC.h b/circuitpython/shared-bindings/rtc/RTC.h new file mode 100644 index 0000000..ede824f --- /dev/null +++ b/circuitpython/shared-bindings/rtc/RTC.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * 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_RTC_RTC_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_RTC_RTC_H + +#include <stdint.h> +#include <stdbool.h> + +#include "py/obj.h" +#include "shared/timeutils/timeutils.h" + +extern void common_hal_rtc_get_time(timeutils_struct_time_t *tm); +extern void common_hal_rtc_set_time(timeutils_struct_time_t *tm); + +extern int common_hal_rtc_get_calibration(void); +extern void common_hal_rtc_set_calibration(int calibration); + +extern const mp_obj_type_t rtc_rtc_type; + +typedef struct _rtc_rtc_obj_t { + mp_obj_base_t base; +} rtc_rtc_obj_t; + +extern const rtc_rtc_obj_t rtc_rtc_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_RTC_RTC_H diff --git a/circuitpython/shared-bindings/rtc/__init__.c b/circuitpython/shared-bindings/rtc/__init__.c new file mode 100644 index 0000000..0220745 --- /dev/null +++ b/circuitpython/shared-bindings/rtc/__init__.c @@ -0,0 +1,90 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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 "py/obj.h" +#include "py/runtime.h" +#include "shared-bindings/rtc/__init__.h" +#include "shared-bindings/rtc/RTC.h" +#include "shared-bindings/time/__init__.h" + +//| """Real Time Clock +//| +//| The `rtc` module provides support for a Real Time Clock. You can access and manage the +//| RTC using :class:`rtc.RTC`. It also backs the :func:`time.time` and :func:`time.localtime` +//| functions using the onboard RTC if present.""" +//| + +void rtc_reset(void) { + MP_STATE_VM(rtc_time_source) = (mp_obj_t)&rtc_rtc_obj; +} + +mp_obj_t rtc_get_time_source_time(void) { + mp_obj_t datetime = mp_load_attr(MP_STATE_VM(rtc_time_source), MP_QSTR_datetime); + timeutils_struct_time_t tm; + struct_time_to_tm(datetime, &tm); + // This sets tm_wday and tm_yday + return struct_time_from_tm(&tm); +} + +//| def set_time_source(rtc: RTC) -> None: +//| """Sets the RTC time source used by :func:`time.localtime`. +//| The default is :class:`rtc.RTC`, but it's useful to use this to override the +//| time source for testing purposes. For example:: +//| +//| import rtc +//| import time +//| +//| class RTC(object): +//| @property +//| def datetime(self): +//| return time.struct_time((2018, 3, 17, 21, 1, 47, 0, 0, 0)) +//| +//| r = RTC() +//| rtc.set_time_source(r)""" +//| ... +//| +STATIC mp_obj_t rtc_set_time_source(mp_obj_t time_source) { + MP_STATE_VM(rtc_time_source) = time_source; + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(rtc_set_time_source_obj, rtc_set_time_source); + +STATIC const mp_rom_map_elem_t rtc_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rtc) }, + { MP_ROM_QSTR(MP_QSTR_set_time_source), MP_ROM_PTR(&rtc_set_time_source_obj) }, + { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&rtc_rtc_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(rtc_module_globals, rtc_module_globals_table); + +const mp_obj_module_t rtc_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&rtc_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_rtc, rtc_module, CIRCUITPY_RTC); diff --git a/circuitpython/shared-bindings/rtc/__init__.h b/circuitpython/shared-bindings/rtc/__init__.h new file mode 100644 index 0000000..04f8f8b --- /dev/null +++ b/circuitpython/shared-bindings/rtc/__init__.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * 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_RTC___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_RTC___INIT___H + +#include "py/obj.h" + +extern void rtc_reset(void); +extern mp_obj_t rtc_get_time_source_time(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_RTC___INIT___H diff --git a/circuitpython/shared-bindings/sdcardio/SDCard.c b/circuitpython/shared-bindings/sdcardio/SDCard.c new file mode 100644 index 0000000..5afca79 --- /dev/null +++ b/circuitpython/shared-bindings/sdcardio/SDCard.c @@ -0,0 +1,206 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "shared-bindings/sdcardio/SDCard.h" +#include "shared-module/sdcardio/SDCard.h" +#include "common-hal/busio/SPI.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "supervisor/flash.h" + +//| class SDCard: +//| """SD Card Block Interface +//| +//| Controls an SD card over SPI. This built-in module has higher read +//| performance than the library adafruit_sdcard, but it is only compatible with +//| `busio.SPI`, not `bitbangio.SPI`. Usually an SDCard object is used +//| with ``storage.VfsFat`` to allow file I/O to an SD card.""" +//| +//| def __init__(self, bus: busio.SPI, cs: microcontroller.Pin, baudrate: int = 8000000) -> None: +//| """Construct an SPI SD Card object with the given properties +//| +//| :param busio.SPI spi: The SPI bus +//| :param microcontroller.Pin cs: The chip select connected to the card +//| :param int baudrate: The SPI data rate to use after card setup +//| +//| Note that during detection and configuration, a hard-coded low baudrate is used. +//| Data transfers use the specified baurate (rounded down to one that is supported by +//| the microcontroller) +//| +//| .. important:: +//| If the same SPI bus is shared with other peripherals, it is important that +//| the SD card be initialized before accessing any other peripheral on the bus. +//| Failure to do so can prevent the SD card from being recognized until it is +//| powered off or re-inserted. +//| +//| Example usage: +//| +//| .. code-block:: python +//| +//| import os +//| +//| import board +//| import sdcardio +//| import storage +//| +//| sd = sdcardio.SDCard(board.SPI(), board.SD_CS) +//| vfs = storage.VfsFat(sd) +//| storage.mount(vfs, '/sd') +//| os.listdir('/sd')""" + +STATIC mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_spi, ARG_cs, ARG_baudrate, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_spi, MP_ARG_OBJ, {.u_obj = mp_const_none } }, + { MP_QSTR_cs, MP_ARG_OBJ, {.u_obj = mp_const_none } }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 8000000} }, + }; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + 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); + + busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi].u_obj); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj); + + sdcardio_sdcard_obj_t *self = m_new_obj(sdcardio_sdcard_obj_t); + self->base.type = &sdcardio_SDCard_type; + + common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int); + + return self; +} + + +//| def count(self) -> int: +//| """Returns the total number of sectors +//| +//| Due to technical limitations, this is a function and not a property. +//| +//| :return: The number of 512-byte blocks, as a number""" +//| +STATIC mp_obj_t sdcardio_sdcard_count(mp_obj_t self_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + return mp_obj_new_int_from_ull(common_hal_sdcardio_sdcard_get_blockcount(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_count_obj, sdcardio_sdcard_count); + +//| def deinit(self) -> None: +//| """Disable permanently. +//| +//| :return: None""" +//| +STATIC mp_obj_t sdcardio_sdcard_deinit(mp_obj_t self_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + common_hal_sdcardio_sdcard_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit); + + +//| def readblocks(self, start_block: int, buf: WriteableBuffer) -> None: +//| +//| """Read one or more blocks from the card +//| +//| :param int start_block: The block to start reading from +//| :param ~circuitpython_typing.WriteableBuffer buf: The buffer to write into. Length must be multiple of 512. +//| +//| :return: None""" +//| + +STATIC mp_obj_t sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + uint32_t start_block = mp_obj_get_int(start_block_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + int result = common_hal_sdcardio_sdcard_readblocks(self, start_block, &bufinfo); + if (result < 0) { + mp_raise_OSError(-result); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, sdcardio_sdcard_readblocks); + +//| def sync(self) -> None: +//| """Ensure all blocks written are actually committed to the SD card +//| +//| :return: None""" +//| ... +STATIC mp_obj_t sdcardio_sdcard_sync(mp_obj_t self_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + int result = common_hal_sdcardio_sdcard_sync(self); + if (result < 0) { + mp_raise_OSError(-result); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_sync_obj, sdcardio_sdcard_sync); + + +//| def writeblocks(self, start_block: int, buf: ReadableBuffer) -> None: +//| +//| """Write one or more blocks to the card +//| +//| :param int start_block: The block to start writing from +//| :param ~circuitpython_typing.ReadableBuffer buf: The buffer to read from. Length must be multiple of 512. +//| +//| :return: None""" +//| + +STATIC mp_obj_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + uint32_t start_block = mp_obj_get_int(start_block_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo); + if (result < 0) { + mp_raise_OSError(-result); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, sdcardio_sdcard_writeblocks); + +STATIC const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdcardio_sdcard_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&sdcardio_sdcard_sync_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdcardio_sdcard_writeblocks_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(sdcardio_sdcard_locals_dict, sdcardio_sdcard_locals_dict_table); + +const mp_obj_type_t sdcardio_SDCard_type = { + { &mp_type_type }, + .name = MP_QSTR_SDCard, + .make_new = sdcardio_sdcard_make_new, + .locals_dict = (mp_obj_dict_t *)&sdcardio_sdcard_locals_dict, +}; diff --git a/circuitpython/shared-bindings/sdcardio/SDCard.h b/circuitpython/shared-bindings/sdcardio/SDCard.h new file mode 100644 index 0000000..5986d5b --- /dev/null +++ b/circuitpython/shared-bindings/sdcardio/SDCard.h @@ -0,0 +1,30 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +extern const mp_obj_type_t sdcardio_SDCard_type; diff --git a/circuitpython/shared-bindings/sdcardio/__init__.c b/circuitpython/shared-bindings/sdcardio/__init__.c new file mode 100644 index 0000000..c64a8a3 --- /dev/null +++ b/circuitpython/shared-bindings/sdcardio/__init__.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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/sdcardio/SDCard.h" + +//| """Interface to an SD card via the SPI bus""" + +STATIC const mp_rom_map_elem_t sdcardio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sdcardio) }, + { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&sdcardio_SDCard_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(sdcardio_module_globals, sdcardio_module_globals_table); + +const mp_obj_module_t sdcardio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&sdcardio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_sdcardio, sdcardio_module, CIRCUITPY_SDCARDIO); diff --git a/circuitpython/shared-bindings/sdcardio/__init__.h b/circuitpython/shared-bindings/sdcardio/__init__.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/shared-bindings/sdcardio/__init__.h diff --git a/circuitpython/shared-bindings/sdioio/SDCard.c b/circuitpython/shared-bindings/sdioio/SDCard.c new file mode 100644 index 0000000..f6407ff --- /dev/null +++ b/circuitpython/shared-bindings/sdioio/SDCard.c @@ -0,0 +1,288 @@ +/* + * 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 +// sdioio.SDCard class. + +#include <string.h> + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/sdioio/SDCard.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 SDCard: +//| """SD Card Block Interface with SDIO +//| +//| Controls an SD card over SDIO. SDIO is a parallel protocol designed +//| for SD cards. It uses a clock pin, a command pin, and 1 or 4 +//| data pins. It can be operated at a high frequency such as +//| 25MHz. Usually an SDCard object is used with ``storage.VfsFat`` +//| to allow file I/O to an SD card.""" +//| +//| def __init__(self, clock: microcontroller.Pin, command: microcontroller.Pin, data: Sequence[microcontroller.Pin], frequency: int) -> None: +//| """Construct an SDIO SD Card object with the given properties +//| +//| :param ~microcontroller.Pin clock: the pin to use for the clock. +//| :param ~microcontroller.Pin command: the pin to use for the command. +//| :param data: A sequence of pins to use for data. +//| :param frequency: The frequency of the bus in Hz +//| +//| Example usage: +//| +//| .. code-block:: python +//| +//| import os +//| +//| import board +//| import sdioio +//| import storage +//| +//| sd = sdioio.SDCard( +//| clock=board.SDIO_CLOCK, +//| command=board.SDIO_COMMAND, +//| data=board.SDIO_DATA, +//| frequency=25000000) +//| vfs = storage.VfsFat(sd) +//| storage.mount(vfs, '/sd') +//| os.listdir('/sd')""" +//| ... +//| + +STATIC mp_obj_t sdioio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + sdioio_sdcard_obj_t *self = m_new_obj(sdioio_sdcard_obj_t); + self->base.type = &sdioio_SDCard_type; + enum { ARG_clock, ARG_command, ARG_data, ARG_frequency, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ }, + { MP_QSTR_command, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ }, + { MP_QSTR_frequency, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT }, + }; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + 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 *command = validate_obj_is_free_pin(args[ARG_command].u_obj); + const mcu_pin_obj_t *data_pins[4]; + uint8_t num_data; + validate_list_is_free_pins(MP_QSTR_data, data_pins, MP_ARRAY_SIZE(data_pins), args[ARG_data].u_obj, &num_data); + + common_hal_sdioio_sdcard_construct(self, clock, command, num_data, data_pins, args[ARG_frequency].u_int); + return MP_OBJ_FROM_PTR(self); +} + +STATIC void check_for_deinit(sdioio_sdcard_obj_t *self) { + if (common_hal_sdioio_sdcard_deinited(self)) { + raise_deinited_error(); + } +} + +//| def configure(self, frequency: int = 0, width: int = 0) -> None: +//| """Configures the SDIO bus. +//| +//| :param int frequency: 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 width: the number of data lines to use. Must be 1 or 4 and must also not exceed the number of data lines at construction +//| +//| .. note:: Leaving a value unspecified or 0 means the current setting is kept""" +//| +STATIC mp_obj_t sdioio_sdcard_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_frequency, ARG_width, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t frequency = args[ARG_frequency].u_int; + if (frequency < 0) { + mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_baudrate); + } + + uint8_t width = args[ARG_width].u_int; + if (width != 0 && width != 1 && width != 4) { + mp_raise_ValueError_varg(translate("Invalid %q"), MP_QSTR_width); + } + + if (!common_hal_sdioio_sdcard_configure(self, frequency, width)) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(sdioio_sdcard_configure_obj, 1, sdioio_sdcard_configure); + +//| def count(self) -> int: +//| """Returns the total number of sectors +//| +//| Due to technical limitations, this is a function and not a property. +//| +//| :return: The number of 512-byte blocks, as a number""" +//| +STATIC mp_obj_t sdioio_sdcard_count(mp_obj_t self_in) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_sdioio_sdcard_get_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_count_obj, sdioio_sdcard_count); + +//| def readblocks(self, start_block: int, buf: WriteableBuffer) -> None: +//| +//| """Read one or more blocks from the card +//| +//| :param int start_block: The block to start reading from +//| :param ~circuitpython_typing.WriteableBuffer buf: The buffer to write into. Length must be multiple of 512. +//| +//| :return: None""" +STATIC mp_obj_t sdioio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + uint32_t start_block = mp_obj_get_int(start_block_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + sdioio_sdcard_obj_t *self = (sdioio_sdcard_obj_t *)self_in; + int result = common_hal_sdioio_sdcard_readblocks(self, start_block, &bufinfo); + if (result < 0) { + mp_raise_OSError(-result); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_readblocks_obj, sdioio_sdcard_readblocks); + +//| def writeblocks(self, start_block: int, buf: ReadableBuffer) -> None: +//| +//| """Write one or more blocks to the card +//| +//| :param int start_block: The block to start writing from +//| :param ~circuitpython_typing.ReadableBuffer buf: The buffer to read from. Length must be multiple of 512. +//| +//| :return: None""" +//| +STATIC mp_obj_t sdioio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + uint32_t start_block = mp_obj_get_int(start_block_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + sdioio_sdcard_obj_t *self = (sdioio_sdcard_obj_t *)self_in; + int result = common_hal_sdioio_sdcard_writeblocks(self, start_block, &bufinfo); + if (result < 0) { + mp_raise_OSError(-result); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_writeblocks_obj, sdioio_sdcard_writeblocks); + +//| @property +//| def frequency(self) -> int: +//| """The actual SDIO bus frequency. This may not match the frequency +//| requested due to internal limitations.""" +//| ... +//| +STATIC mp_obj_t sdioio_sdcard_obj_get_frequency(mp_obj_t self_in) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_sdioio_sdcard_get_frequency(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_get_frequency_obj, sdioio_sdcard_obj_get_frequency); + +MP_PROPERTY_GETTER(sdioio_sdcard_frequency_obj, + (mp_obj_t)&sdioio_sdcard_get_frequency_obj); + +//| @property +//| def width(self) -> int: +//| """The actual SDIO bus width, in bits""" +//| ... +//| +STATIC mp_obj_t sdioio_sdcard_obj_get_width(mp_obj_t self_in) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_sdioio_sdcard_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_get_width_obj, sdioio_sdcard_obj_get_width); + +MP_PROPERTY_GETTER(sdioio_sdcard_width_obj, + (mp_obj_t)&sdioio_sdcard_get_width_obj); + +//| def deinit(self) -> None: +//| """Disable permanently. +//| +//| :return: None""" +STATIC mp_obj_t sdioio_sdcard_obj_deinit(mp_obj_t self_in) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_sdioio_sdcard_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_deinit_obj, sdioio_sdcard_obj_deinit); + +//| def __enter__(self) -> SDCard: +//| """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 sdioio_sdcard_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_sdioio_sdcard_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(sdioio_sdcard_obj___exit___obj, 4, 4, sdioio_sdcard_obj___exit__); + +STATIC const mp_rom_map_elem_t sdioio_sdcard_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdioio_sdcard_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&sdioio_sdcard_obj___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&sdioio_sdcard_configure_obj) }, + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&sdioio_sdcard_frequency_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&sdioio_sdcard_width_obj) }, + + { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdioio_sdcard_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdioio_sdcard_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdioio_sdcard_writeblocks_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(sdioio_sdcard_locals_dict, sdioio_sdcard_locals_dict_table); + +const mp_obj_type_t sdioio_SDCard_type = { + { &mp_type_type }, + .name = MP_QSTR_SDCard, + .make_new = sdioio_sdcard_make_new, + .locals_dict = (mp_obj_dict_t *)&sdioio_sdcard_locals_dict, +}; diff --git a/circuitpython/shared-bindings/sdioio/SDCard.h b/circuitpython/shared-bindings/sdioio/SDCard.h new file mode 100644 index 0000000..cf15762 --- /dev/null +++ b/circuitpython/shared-bindings/sdioio/SDCard.h @@ -0,0 +1,66 @@ +/* + * 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_SDIO_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SDIO_H + +#include "py/obj.h" + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/sdioio/SDCard.h" + +// Type object used in Python. Should be shared between ports. +extern const mp_obj_type_t sdioio_SDCard_type; + +// Construct an underlying SDIO object. +extern void common_hal_sdioio_sdcard_construct(sdioio_sdcard_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *command, + uint8_t num_data, const mcu_pin_obj_t **data, uint32_t frequency); + +extern void common_hal_sdioio_sdcard_deinit(sdioio_sdcard_obj_t *self); +extern bool common_hal_sdioio_sdcard_deinited(sdioio_sdcard_obj_t *self); + +extern bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t baudrate, uint8_t width); + +extern void common_hal_sdioio_sdcard_unlock(sdioio_sdcard_obj_t *self); + +// Return actual SDIO bus frequency. +uint32_t common_hal_sdioio_sdcard_get_frequency(sdioio_sdcard_obj_t *self); + +// Return SDIO bus width. +uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t *self); + +// Return number of device blocks +uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t *self); + +// Read or write blocks +int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); +int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); + +// This is used by the supervisor to claim SDIO devices indefinitely. +extern void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SDIO_H diff --git a/circuitpython/shared-bindings/sdioio/__init__.c b/circuitpython/shared-bindings/sdioio/__init__.c new file mode 100644 index 0000000..5204da2 --- /dev/null +++ b/circuitpython/shared-bindings/sdioio/__init__.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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/sdioio/SDCard.h" + +//| """Interface to an SD card via the SDIO bus""" + +STATIC const mp_rom_map_elem_t sdioio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sdio) }, + { MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&sdioio_SDCard_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(sdioio_module_globals, sdioio_module_globals_table); + +const mp_obj_module_t sdioio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&sdioio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_sdioio, sdioio_module, CIRCUITPY_SDIOIO); diff --git a/circuitpython/shared-bindings/sdioio/__init__.h b/circuitpython/shared-bindings/sdioio/__init__.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/shared-bindings/sdioio/__init__.h diff --git a/circuitpython/shared-bindings/sharpdisplay/SharpMemoryFramebuffer.c b/circuitpython/shared-bindings/sharpdisplay/SharpMemoryFramebuffer.c new file mode 100644 index 0000000..35e06b7 --- /dev/null +++ b/circuitpython/shared-bindings/sharpdisplay/SharpMemoryFramebuffer.c @@ -0,0 +1,95 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 "py/objarray.h" +#include "py/runtime.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" + +STATIC mp_obj_t sharpdisplay_framebuffer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_spi_bus, ARG_chip_select, ARG_width, ARG_height, ARG_baudrate, NUM_ARGS }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_spi_bus, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_chip_select, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 2000000} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_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 *chip_select = validate_obj_is_free_pin(args[ARG_chip_select].u_obj); + busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi_bus].u_obj); + + sharpdisplay_framebuffer_obj_t *self = &allocate_display_bus_or_raise()->sharpdisplay; + self->base.type = &sharpdisplay_framebuffer_type; + + common_hal_sharpdisplay_framebuffer_construct(self, spi, chip_select, args[ARG_baudrate].u_int, args[ARG_width].u_int, args[ARG_height].u_int); + + return MP_OBJ_FROM_PTR(self); +} + + +STATIC mp_int_t sharpdisplay_framebuffer_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + sharpdisplay_framebuffer_obj_t *self = (sharpdisplay_framebuffer_obj_t *)self_in; + // a readonly framebuffer would be unusual but not impossible + if ((flags & MP_BUFFER_WRITE) && !(self->bufinfo.typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) { + return 1; + } + *bufinfo = self->bufinfo; + return 0; +} + +STATIC mp_obj_t sharpdisplay_framebuffer_deinit(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = (sharpdisplay_framebuffer_obj_t *)self_in; + common_hal_sharpdisplay_framebuffer_deinit(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(sharpdisplay_framebuffer_deinit_obj, sharpdisplay_framebuffer_deinit); + +STATIC const mp_rom_map_elem_t sharpdisplay_framebuffer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sharpdisplay_framebuffer_deinit_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(sharpdisplay_framebuffer_locals_dict, sharpdisplay_framebuffer_locals_dict_table); + +const mp_obj_type_t sharpdisplay_framebuffer_type = { + { &mp_type_type }, + .name = MP_QSTR_SharpMemoryFramebuffer, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = sharpdisplay_framebuffer_make_new, + .locals_dict = (mp_obj_dict_t *)&sharpdisplay_framebuffer_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .buffer_p = { .get_buffer = sharpdisplay_framebuffer_get_buffer, }, + .protocol = &sharpdisplay_framebuffer_proto, + ), +}; diff --git a/circuitpython/shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h b/circuitpython/shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h new file mode 100644 index 0000000..30f1b86 --- /dev/null +++ b/circuitpython/shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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. + */ + +#pragma once + +// #include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" +// #include "shared-module/framebufferio/FramebufferDisplay.h" + +#include "py/objtype.h" + +extern const mp_obj_type_t sharpdisplay_framebuffer_type; diff --git a/circuitpython/shared-bindings/sharpdisplay/__init__.c b/circuitpython/shared-bindings/sharpdisplay/__init__.c new file mode 100644 index 0000000..8c01c8c --- /dev/null +++ b/circuitpython/shared-bindings/sharpdisplay/__init__.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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/sharpdisplay/SharpMemoryFramebuffer.h" + +//| """Support for Sharp Memory Display framebuffers""" +//| + +STATIC const mp_rom_map_elem_t sharpdisplay_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sharpdisplay) }, + { MP_ROM_QSTR(MP_QSTR_SharpMemoryFramebuffer), MP_ROM_PTR(&sharpdisplay_framebuffer_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(sharpdisplay_module_globals, sharpdisplay_module_globals_table); + +const mp_obj_module_t sharpdisplay_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&sharpdisplay_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_sharpdisplay, sharpdisplay_module, CIRCUITPY_SHARPDISPLAY); diff --git a/circuitpython/shared-bindings/sharpdisplay/__init__.h b/circuitpython/shared-bindings/sharpdisplay/__init__.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/shared-bindings/sharpdisplay/__init__.h diff --git a/circuitpython/shared-bindings/socketpool/Socket.c b/circuitpython/shared-bindings/socketpool/Socket.c new file mode 100644 index 0000000..bea3cd1 --- /dev/null +++ b/circuitpython/shared-bindings/socketpool/Socket.c @@ -0,0 +1,404 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2021 Lucian Copeland 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 "shared-bindings/socketpool/Socket.h" + +#include <stdio.h> +#include <string.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/objtuple.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include "py/mperrno.h" + +#include "shared/netutils/netutils.h" + +//| class Socket: +//| """TCP, UDP and RAW socket. Cannot be created directly. Instead, call +//| `SocketPool.socket()`. +//| +//| Provides a subset of CPython's `socket.socket` API. It only implements the versions of +//| recv that do not allocate bytes objects.""" +//| + +//| def __hash__(self) -> int: +//| """Returns a hash for the Socket.""" +//| ... +//| +// Provided by mp_generic_unary_op(). + +//| def __enter__(self) -> Socket: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically closes the Socket when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t socketpool_socket___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_socketpool_socket_close(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket___exit___obj, 4, 4, socketpool_socket___exit__); + +//| def accept(self) -> Tuple[Socket, Tuple[str, int]]: +//| """Accept a connection on a listening socket of type SOCK_STREAM, +//| creating a new socket of type SOCK_STREAM. +//| Returns a tuple of (new_socket, remote_address)""" +//| +STATIC mp_obj_t socketpool_socket_accept(mp_obj_t self_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t ip[4]; + uint32_t port; + + socketpool_socket_obj_t *sock = common_hal_socketpool_socket_accept(self, ip, &port); + + mp_obj_t tuple_contents[2]; + tuple_contents[0] = MP_OBJ_FROM_PTR(sock); + tuple_contents[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + return mp_obj_new_tuple(2, tuple_contents); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_accept_obj, socketpool_socket_accept); + +//| def bind(self, address: Tuple[str, int]) -> None: +//| """Bind a socket to an address +//| +//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + + size_t hostlen; + const char *host = mp_obj_str_get_data(addr_items[0], &hostlen); + mp_int_t port = mp_obj_get_int(addr_items[1]); + if (port < 0) { + mp_raise_ValueError(translate("port must be >= 0")); + } + + bool ok = common_hal_socketpool_socket_bind(self, host, hostlen, (uint32_t)port); + if (!ok) { + mp_raise_ValueError(translate("Error: Failure to bind")); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_bind_obj, socketpool_socket_bind); + +//| def close(self) -> None: +//| """Closes this Socket and makes its resources available to its SocketPool.""" +//| +STATIC mp_obj_t socketpool_socket_close(mp_obj_t self_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_socketpool_socket_close(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_close_obj, socketpool_socket_close); + +//| def connect(self, address: Tuple[str, int]) -> None: +//| """Connect a socket to a remote address +//| +//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + + size_t hostlen; + const char *host = mp_obj_str_get_data(addr_items[0], &hostlen); + mp_int_t port = mp_obj_get_int(addr_items[1]); + if (port < 0) { + mp_raise_ValueError(translate("port must be >= 0")); + } + + common_hal_socketpool_socket_connect(self, host, hostlen, (uint32_t)port); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_connect_obj, socketpool_socket_connect); + +//| def listen(self, backlog: int) -> None: +//| """Set socket to listen for incoming connections +//| +//| :param ~int backlog: length of backlog queue for waiting connetions""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_listen(mp_obj_t self_in, mp_obj_t backlog_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int backlog = mp_obj_get_int(backlog_in); + + common_hal_socketpool_socket_listen(self, backlog); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_listen_obj, socketpool_socket_listen); + +//| def recvfrom_into(self, buffer: WriteableBuffer) -> Tuple[int, Tuple[str, int]]: +//| """Reads some bytes from a remote address. +//| +//| Returns a tuple containing +//| * the number of bytes received into the given buffer +//| * a remote_address, which is a tuple of ip address and port number +//| +//| :param object buffer: buffer to read into""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_recvfrom_into(mp_obj_t self_in, mp_obj_t data_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_WRITE); + + byte ip[4]; + mp_uint_t port; + mp_int_t ret = common_hal_socketpool_socket_recvfrom_into(self, + (byte *)bufinfo.buf, bufinfo.len, ip, &port); + mp_obj_t tuple_contents[2]; + tuple_contents[0] = mp_obj_new_int_from_uint(ret); + tuple_contents[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + return mp_obj_new_tuple(2, tuple_contents); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_recvfrom_into_obj, socketpool_socket_recvfrom_into); + +//| def recv_into(self, buffer: WriteableBuffer, bufsize: int) -> int: +//| """Reads some bytes from the connected remote address, writing +//| into the provided buffer. If bufsize <= len(buffer) is given, +//| a maximum of bufsize bytes will be read into the buffer. If no +//| valid value is given for bufsize, the default is the length of +//| the given buffer. +//| +//| Suits sockets of type SOCK_STREAM +//| Returns an int of number of bytes read. +//| +//| :param bytearray buffer: buffer to receive into +//| :param int bufsize: optionally, a maximum number of bytes to read.""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_recv_into(size_t n_args, const mp_obj_t *args) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (common_hal_socketpool_socket_get_closed(self)) { + // Bad file number. + mp_raise_OSError(MP_EBADF); + } + // if (!common_hal_socketpool_socket_get_connected(self)) { + // // not connected + // mp_raise_OSError(MP_ENOTCONN); + // } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + mp_int_t len = bufinfo.len; + if (n_args == 3) { + mp_int_t given_len = mp_obj_get_int(args[2]); + if (given_len > len) { + mp_raise_ValueError(translate("buffer too small for requested bytes")); + } + if (given_len > 0 && given_len < len) { + len = given_len; + } + } + + if (len == 0) { + return MP_OBJ_NEW_SMALL_INT(0); + } + + mp_int_t ret = common_hal_socketpool_socket_recv_into(self, (byte *)bufinfo.buf, len); + return mp_obj_new_int_from_uint(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket_recv_into_obj, 2, 3, socketpool_socket_recv_into); + +//| def send(self, bytes: ReadableBuffer) -> int: +//| """Send some bytes to the connected remote address. +//| Suits sockets of type SOCK_STREAM +//| +//| :param ~bytes bytes: some bytes to send""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_send(mp_obj_t self_in, mp_obj_t buf_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (common_hal_socketpool_socket_get_closed(self)) { + // Bad file number. + mp_raise_OSError(MP_EBADF); + } + if (!common_hal_socketpool_socket_get_connected(self)) { + mp_raise_BrokenPipeError(); + } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + mp_int_t ret = common_hal_socketpool_socket_send(self, bufinfo.buf, bufinfo.len); + if (ret == -1) { + mp_raise_BrokenPipeError(); + } + return mp_obj_new_int_from_uint(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_send_obj, socketpool_socket_send); + +//| def sendto(self, bytes: ReadableBuffer, address: Tuple[str, int]) -> int: +//| """Send some bytes to a specific address. +//| Suits sockets of type SOCK_DGRAM +//| +//| :param ~bytes bytes: some bytes to send +//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // get the data + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); + + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + + size_t hostlen; + const char *host = mp_obj_str_get_data(addr_items[0], &hostlen); + mp_int_t port = mp_obj_get_int(addr_items[1]); + if (port < 0) { + mp_raise_ValueError(translate("port must be >= 0")); + } + + mp_int_t ret = common_hal_socketpool_socket_sendto(self, host, hostlen, (uint32_t)port, bufinfo.buf, bufinfo.len); + + return mp_obj_new_int_from_uint(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(socketpool_socket_sendto_obj, socketpool_socket_sendto); + +//| def setblocking(self, flag: bool) -> Optional[int]: +//| """Set the blocking behaviour of this socket. +//| +//| :param ~bool flag: False means non-blocking, True means block indefinitely.""" +//| ... +//| +// method socket.setblocking(flag) +STATIC mp_obj_t socketpool_socket_setblocking(mp_obj_t self_in, mp_obj_t blocking) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (mp_obj_is_true(blocking)) { + common_hal_socketpool_socket_settimeout(self, -1); + } else { + common_hal_socketpool_socket_settimeout(self, 0); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_setblocking_obj, socketpool_socket_setblocking); + +// //| def setsockopt(self, level: int, optname: int, value: int) -> None: +// //| """Sets socket options""" +// //| ... +// //| +// STATIC mp_obj_t socketpool_socket_setsockopt(size_t n_args, const mp_obj_t *args) { +// // mod_network_socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + +// // mp_int_t level = mp_obj_get_int(args[1]); +// // mp_int_t opt = mp_obj_get_int(args[2]); + +// // const void *optval; +// // mp_uint_t optlen; +// // mp_int_t val; +// // if (mp_obj_is_integer(args[3])) { +// // val = mp_obj_get_int_truncated(args[3]); +// // optval = &val; +// // optlen = sizeof(val); +// // } else { +// // mp_buffer_info_t bufinfo; +// // mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); +// // optval = bufinfo.buf; +// // optlen = bufinfo.len; +// // } + +// // int _errno; +// // if (self->nic_type->setsockopt(self, level, opt, optval, optlen, &_errno) != 0) { +// // mp_raise_OSError(_errno); +// // } + +// return mp_const_none; +// } +// STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket_setsockopt_obj, 4, 4, socketpool_socket_setsockopt); + + +//| def settimeout(self, value: int) -> None: +//| """Set the timeout value for this socket. +//| +//| :param ~int value: timeout in seconds. 0 means non-blocking. None means block indefinitely.""" +//| ... +//| +STATIC mp_obj_t socketpool_socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) { + socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t timeout_ms; + if (timeout_in == mp_const_none) { + timeout_ms = -1; + } else { + #if MICROPY_PY_BUILTINS_FLOAT + timeout_ms = 1000 * mp_obj_get_float(timeout_in); + #else + timeout_ms = 1000 * mp_obj_get_int(timeout_in); + #endif + } + common_hal_socketpool_socket_settimeout(self, timeout_ms); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_settimeout_obj, socketpool_socket_settimeout); + +STATIC const mp_rom_map_elem_t socketpool_socket_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&socketpool_socket___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socketpool_socket_close_obj) }, + + { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&socketpool_socket_accept_obj) }, + { MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&socketpool_socket_bind_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&socketpool_socket_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&socketpool_socket_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&socketpool_socket_listen_obj) }, + { MP_ROM_QSTR(MP_QSTR_recvfrom_into), MP_ROM_PTR(&socketpool_socket_recvfrom_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_recv_into), MP_ROM_PTR(&socketpool_socket_recv_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&socketpool_socket_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_sendto), MP_ROM_PTR(&socketpool_socket_sendto_obj) }, + { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socketpool_socket_setblocking_obj) }, + // { MP_ROM_QSTR(MP_QSTR_setsockopt), MP_ROM_PTR(&socketpool_socket_setsockopt_obj) }, + { MP_ROM_QSTR(MP_QSTR_settimeout), MP_ROM_PTR(&socketpool_socket_settimeout_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(socketpool_socket_locals_dict, socketpool_socket_locals_dict_table); + +const mp_obj_type_t socketpool_socket_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Socket, + .locals_dict = (mp_obj_dict_t *)&socketpool_socket_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_generic_unary_op, + ) +}; diff --git a/circuitpython/shared-bindings/socketpool/Socket.h b/circuitpython/shared-bindings/socketpool/Socket.h new file mode 100644 index 0000000..1f4ab6f --- /dev/null +++ b/circuitpython/shared-bindings/socketpool/Socket.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_SOCKETPOOL_SOCKET_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL_SOCKET_H + +#include "common-hal/socketpool/Socket.h" + +extern const mp_obj_type_t socketpool_socket_type; + +socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self, uint8_t *ip, uint32_t *port); +bool common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port); +void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self); +void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port); +bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t *self); +bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self); +mp_uint_t common_hal_socketpool_socket_get_timeout(socketpool_socket_obj_t *self); +bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog); +mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self, + uint8_t *buf, uint32_t len, uint8_t *ip, uint32_t *port); +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len); +mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len); +mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t *self, + const char *host, size_t hostlen, uint32_t port, const uint8_t *buf, uint32_t len); +void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t *self, uint32_t timeout_ms); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL_SOCKET_H diff --git a/circuitpython/shared-bindings/socketpool/SocketPool.c b/circuitpython/shared-bindings/socketpool/SocketPool.c new file mode 100644 index 0000000..447d2d7 --- /dev/null +++ b/circuitpython/shared-bindings/socketpool/SocketPool.c @@ -0,0 +1,166 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 <stdio.h> +#include <string.h> + +#include "py/objtuple.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include "py/mperrno.h" + +#include "shared-bindings/ipaddress/__init__.h" +#include "shared-bindings/socketpool/Socket.h" +#include "shared-bindings/socketpool/SocketPool.h" + +//| class SocketPool: +//| """A pool of socket resources available for the given radio. Only one +//| SocketPool can be created for each radio. +//| +//| SocketPool should be used in place of CPython's socket which provides +//| a pool of sockets provided by the underlying OS.""" +//| + +STATIC mp_obj_t socketpool_socketpool_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + socketpool_socketpool_obj_t *s = m_new_obj_with_finaliser(socketpool_socketpool_obj_t); + s->base.type = &socketpool_socketpool_type; + mp_obj_t radio = args[0]; + + common_hal_socketpool_socketpool_construct(s, radio); + + return MP_OBJ_FROM_PTR(s); +} + +//| AF_INET: int +//| AF_INET6: int +//| SOCK_STREAM: int +//| SOCK_DGRAM: int +//| SOCK_RAW: int +//| +//| def socket(self, family: int = AF_INET, type: int = SOCK_STREAM) -> socketpool.Socket: +//| """Create a new socket +//| +//| :param ~int family: AF_INET or AF_INET6 +//| :param ~int type: SOCK_STREAM, SOCK_DGRAM or SOCK_RAW +//| +//| The ``proto`` (protocol) and ``fileno`` arguments available in ``socket.socket()`` +//| in CPython are not supported. +//| """ +//| ... +//| +STATIC mp_obj_t socketpool_socketpool_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_family, ARG_type }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_family, MP_ARG_INT, {.u_int = SOCKETPOOL_AF_INET} }, + { MP_QSTR_type, MP_ARG_INT, {.u_int = SOCKETPOOL_SOCK_STREAM} }, + }; + socketpool_socketpool_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + 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); + + socketpool_socketpool_addressfamily_t family = args[ARG_family].u_int; + socketpool_socketpool_sock_t type = args[ARG_type].u_int; + + return common_hal_socketpool_socket(self, family, type); +} +MP_DEFINE_CONST_FUN_OBJ_KW(socketpool_socketpool_socket_obj, 1, socketpool_socketpool_socket); + +//| def getaddrinfo(self, host: str, port: int, family: int = 0, type: int = 0, proto: int = 0, flags: int = 0) -> Tuple[int, int, int, str, Tuple[str, int]]: +//| """Gets the address information for a hostname and port +//| +//| Returns the appropriate family, socket type, socket protocol and +//| address information to call socket.socket() and socket.connect() with, +//| as a tuple.""" +//| ... +//| +STATIC mp_obj_t socketpool_socketpool_getaddrinfo(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_host, ARG_port, ARG_family, ARG_type, ARG_proto, ARG_flags }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_host, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_port, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_family, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_type, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_port, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_flags, MP_ARG_INT, {.u_int = 0} }, + }; + socketpool_socketpool_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + 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); + + const char *host = mp_obj_str_get_str(args[ARG_host].u_obj); + mp_int_t port = args[ARG_port].u_int; + mp_obj_t ip_str = mp_const_none; + + if (strlen(host) > 0 && ipaddress_parse_ipv4address(host, strlen(host), NULL)) { + ip_str = args[ARG_host].u_obj; + } + + if (ip_str == mp_const_none) { + ip_str = common_hal_socketpool_socketpool_gethostbyname(self, host); + } + + if (ip_str == mp_const_none) { + mp_raise_OSError(-2); // socket.EAI_NONAME from CPython + } + + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); + tuple->items[0] = MP_OBJ_NEW_SMALL_INT(SOCKETPOOL_AF_INET); + tuple->items[1] = MP_OBJ_NEW_SMALL_INT(SOCKETPOOL_SOCK_STREAM); + tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); + tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_); + mp_obj_tuple_t *sockaddr = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + sockaddr->items[0] = ip_str; + sockaddr->items[1] = MP_OBJ_NEW_SMALL_INT(port); + tuple->items[4] = MP_OBJ_FROM_PTR(sockaddr); + return mp_obj_new_list(1, (mp_obj_t *)&tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(socketpool_socketpool_getaddrinfo_obj, 1, socketpool_socketpool_getaddrinfo); + +STATIC const mp_rom_map_elem_t socketpool_socketpool_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&socketpool_socketpool_socket_obj) }, + { MP_ROM_QSTR(MP_QSTR_getaddrinfo), MP_ROM_PTR(&socketpool_socketpool_getaddrinfo_obj) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_AF_INET), MP_ROM_INT(SOCKETPOOL_AF_INET) }, + { MP_ROM_QSTR(MP_QSTR_AF_INET6), MP_ROM_INT(SOCKETPOOL_AF_INET6) }, + + { MP_ROM_QSTR(MP_QSTR_SOCK_STREAM), MP_ROM_INT(SOCKETPOOL_SOCK_STREAM) }, + { MP_ROM_QSTR(MP_QSTR_SOCK_DGRAM), MP_ROM_INT(SOCKETPOOL_SOCK_DGRAM) }, + { MP_ROM_QSTR(MP_QSTR_SOCK_RAW), MP_ROM_INT(SOCKETPOOL_SOCK_RAW) }, +}; + +STATIC MP_DEFINE_CONST_DICT(socketpool_socketpool_locals_dict, socketpool_socketpool_locals_dict_table); + +const mp_obj_type_t socketpool_socketpool_type = { + { &mp_type_type }, + .name = MP_QSTR_SocketPool, + .make_new = socketpool_socketpool_make_new, + .locals_dict = (mp_obj_dict_t *)&socketpool_socketpool_locals_dict, +}; diff --git a/circuitpython/shared-bindings/socketpool/SocketPool.h b/circuitpython/shared-bindings/socketpool/SocketPool.h new file mode 100644 index 0000000..10a943d --- /dev/null +++ b/circuitpython/shared-bindings/socketpool/SocketPool.h @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_SOCKETPOOL_SOCKETPOOL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL_SOCKETPOOL_H + +#include "common-hal/socketpool/SocketPool.h" + +#include "shared-bindings/socketpool/Socket.h" + +extern const mp_obj_type_t socketpool_socketpool_type; + +typedef enum { + SOCKETPOOL_SOCK_STREAM, + SOCKETPOOL_SOCK_DGRAM, + SOCKETPOOL_SOCK_RAW +} socketpool_socketpool_sock_t; + +typedef enum { + SOCKETPOOL_AF_INET, + SOCKETPOOL_AF_INET6 +} socketpool_socketpool_addressfamily_t; + +void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *self, mp_obj_t radio); + +socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_t *self, + socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type); + +mp_obj_t common_hal_socketpool_socketpool_gethostbyname(socketpool_socketpool_obj_t *self, + const char *host); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL_SOCKETPOOL_H diff --git a/circuitpython/shared-bindings/socketpool/__init__.c b/circuitpython/shared-bindings/socketpool/__init__.c new file mode 100644 index 0000000..fee81f2 --- /dev/null +++ b/circuitpython/shared-bindings/socketpool/__init__.c @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "py/objexcept.h" +#include "py/objstr.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "shared-bindings/socketpool/__init__.h" +#include "shared-bindings/socketpool/Socket.h" +#include "shared-bindings/socketpool/SocketPool.h" + +//| """ +//| The `socketpool` module provides sockets through a pool. The pools themselves +//| act like CPython's `socket` module. +//| +//| For more information about the `socket` module, see the CPython documentation: +//| https://docs.python.org/3/library/socket.html +//| """ +//| + +STATIC const mp_rom_map_elem_t socketpool_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_socketpool) }, + + { MP_ROM_QSTR(MP_QSTR_SocketPool), MP_ROM_PTR(&socketpool_socketpool_type) }, + { MP_ROM_QSTR(MP_QSTR_Socket), MP_ROM_PTR(&socketpool_socket_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(socketpool_globals, socketpool_globals_table); + +const mp_obj_module_t socketpool_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&socketpool_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_socketpool, socketpool_module, CIRCUITPY_SOCKETPOOL); diff --git a/circuitpython/shared-bindings/socketpool/__init__.h b/circuitpython/shared-bindings/socketpool/__init__.h new file mode 100644 index 0000000..a017e96 --- /dev/null +++ b/circuitpython/shared-bindings/socketpool/__init__.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_SOCKETPOOL___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL___INIT___H diff --git a/circuitpython/shared-bindings/ssl/SSLContext.c b/circuitpython/shared-bindings/ssl/SSLContext.c new file mode 100644 index 0000000..5dfa5e5 --- /dev/null +++ b/circuitpython/shared-bindings/ssl/SSLContext.c @@ -0,0 +1,158 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2020 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 <stdio.h> +#include <string.h> + +#include "py/objtuple.h" +#include "py/objlist.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/mperrno.h" + +#include "shared-bindings/ssl/SSLContext.h" + +//| class SSLContext: +//| """Settings related to SSL that can be applied to a socket by wrapping it. +//| This is useful to provide SSL certificates to specific connections +//| rather than all of them.""" +//| + +STATIC mp_obj_t ssl_sslcontext_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + ssl_sslcontext_obj_t *s = m_new_obj(ssl_sslcontext_obj_t); + s->base.type = &ssl_sslcontext_type; + + common_hal_ssl_sslcontext_construct(s); + + return MP_OBJ_FROM_PTR(s); +} + +//| def load_verify_locations(self, cadata: Optional[str] = None) -> None: +//| """Load a set of certification authority (CA) certificates used to validate +//| other peers' certificates.""" +//| + +STATIC mp_obj_t ssl_sslcontext_load_verify_locations(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_cadata }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_cadata, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + ssl_sslcontext_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + 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); + + const char *cadata = mp_obj_str_get_str(args[ARG_cadata].u_obj); + + common_hal_ssl_sslcontext_load_verify_locations(self, cadata); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ssl_sslcontext_load_verify_locations_obj, 1, ssl_sslcontext_load_verify_locations); + +//| def set_default_verify_paths(self) -> None: +//| """Load a set of default certification authority (CA) certificates.""" +//| + +STATIC mp_obj_t ssl_sslcontext_set_default_verify_paths(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + ssl_sslcontext_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + common_hal_ssl_sslcontext_set_default_verify_paths(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ssl_sslcontext_set_default_verify_paths_obj, 1, ssl_sslcontext_set_default_verify_paths); + +//| check_hostname: bool +//| """Whether to match the peer certificate's hostname.""" +//| + +STATIC mp_obj_t ssl_sslcontext_get_check_hostname(mp_obj_t self_in) { + ssl_sslcontext_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return mp_obj_new_bool(common_hal_ssl_sslcontext_get_check_hostname(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ssl_sslcontext_get_check_hostname_obj, ssl_sslcontext_get_check_hostname); + +STATIC mp_obj_t ssl_sslcontext_set_check_hostname(mp_obj_t self_in, mp_obj_t value) { + ssl_sslcontext_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_ssl_sslcontext_set_check_hostname(self, mp_obj_is_true(value)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslcontext_set_check_hostname_obj, ssl_sslcontext_set_check_hostname); + +MP_PROPERTY_GETSET(ssl_sslcontext_check_hostname_obj, + (mp_obj_t)&ssl_sslcontext_get_check_hostname_obj, + (mp_obj_t)&ssl_sslcontext_set_check_hostname_obj); + +//| def wrap_socket(self, sock: socketpool.Socket, *, server_side: bool = False, server_hostname: Optional[str] = None) -> ssl.SSLSocket: +//| """Wraps the socket into a socket-compatible class that handles SSL negotiation. +//| The socket must be of type SOCK_STREAM.""" +//| + +STATIC mp_obj_t ssl_sslcontext_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sock, ARG_server_side, ARG_server_hostname }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sock, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + ssl_sslcontext_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + 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); + + const char *server_hostname = NULL; + if (args[ARG_server_hostname].u_obj != mp_const_none) { + server_hostname = mp_obj_str_get_str(args[ARG_server_hostname].u_obj); + } + bool server_side = args[ARG_server_side].u_bool; + if (server_side && server_hostname != NULL) { + mp_raise_ValueError(translate("Server side context cannot have hostname")); + } + + socketpool_socket_obj_t *sock = args[ARG_sock].u_obj; + + return common_hal_ssl_sslcontext_wrap_socket(self, sock, server_side, server_hostname); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(ssl_sslcontext_wrap_socket_obj, 1, ssl_sslcontext_wrap_socket); + +STATIC const mp_rom_map_elem_t ssl_sslcontext_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_wrap_socket), MP_ROM_PTR(&ssl_sslcontext_wrap_socket_obj) }, + { MP_ROM_QSTR(MP_QSTR_load_verify_locations), MP_ROM_PTR(&ssl_sslcontext_load_verify_locations_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_default_verify_paths), MP_ROM_PTR(&ssl_sslcontext_set_default_verify_paths_obj) }, + { MP_ROM_QSTR(MP_QSTR_check_hostname), MP_ROM_PTR(&ssl_sslcontext_check_hostname_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ssl_sslcontext_locals_dict, ssl_sslcontext_locals_dict_table); + +const mp_obj_type_t ssl_sslcontext_type = { + { &mp_type_type }, + .name = MP_QSTR_SSLContext, + .make_new = ssl_sslcontext_make_new, + .locals_dict = (mp_obj_dict_t *)&ssl_sslcontext_locals_dict, +}; diff --git a/circuitpython/shared-bindings/ssl/SSLContext.h b/circuitpython/shared-bindings/ssl/SSLContext.h new file mode 100644 index 0000000..ef04f25 --- /dev/null +++ b/circuitpython/shared-bindings/ssl/SSLContext.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_SSL_SSLCONTEXT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SSL_SSLCONTEXT_H + +#include "common-hal/ssl/SSLContext.h" + +#include "shared-bindings/socketpool/Socket.h" +#include "shared-bindings/ssl/SSLSocket.h" + +extern const mp_obj_type_t ssl_sslcontext_type; + +void common_hal_ssl_sslcontext_construct(ssl_sslcontext_obj_t *self); + +ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t *self, + socketpool_socket_obj_t *sock, bool server_side, const char *server_hostname); + +void common_hal_ssl_sslcontext_load_verify_locations(ssl_sslcontext_obj_t *self, + const char *cadata); + +void common_hal_ssl_sslcontext_set_default_verify_paths(ssl_sslcontext_obj_t *self); + +bool common_hal_ssl_sslcontext_get_check_hostname(ssl_sslcontext_obj_t *self); +void common_hal_ssl_sslcontext_set_check_hostname(ssl_sslcontext_obj_t *self, bool value); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SSL_SSLCONTEXT_H diff --git a/circuitpython/shared-bindings/ssl/SSLSocket.c b/circuitpython/shared-bindings/ssl/SSLSocket.c new file mode 100644 index 0000000..630ab28 --- /dev/null +++ b/circuitpython/shared-bindings/ssl/SSLSocket.c @@ -0,0 +1,318 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland 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 "shared-bindings/ssl/SSLSocket.h" + +#include <stdio.h> +#include <string.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/objtuple.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include "py/mperrno.h" + +#include "shared/netutils/netutils.h" + +//| class SSLSocket: +//| """Implements TLS security on a subset of `socketpool.Socket` functions. Cannot be created +//| directly. Instead, call `wrap_socket` on an existing socket object. +//| +//| Provides a subset of CPython's `ssl.SSLSocket` API. It only implements the versions of +//| recv that do not allocate bytes objects.""" +//| + +//| def __hash__(self) -> int: +//| """Returns a hash for the Socket.""" +//| ... +//| +// Provided by mp_generic_unary_op(). + +//| def __enter__(self) -> SSLSocket: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically closes the Socket when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t ssl_sslsocket___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_ssl_sslsocket_close(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ssl_sslsocket___exit___obj, 4, 4, ssl_sslsocket___exit__); + +//| def accept(self) -> Tuple[SSLSocket, Tuple[str, int]]: +//| """Accept a connection on a listening socket of type SOCK_STREAM, +//| creating a new socket of type SOCK_STREAM. +//| Returns a tuple of (new_socket, remote_address)""" +//| +STATIC mp_obj_t ssl_sslsocket_accept(mp_obj_t self_in) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t ip[4]; + uint32_t port; + + ssl_sslsocket_obj_t *sslsock = common_hal_ssl_sslsocket_accept(self, ip, &port); + + mp_obj_t tuple_contents[2]; + tuple_contents[0] = MP_OBJ_FROM_PTR(sslsock); + tuple_contents[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + return mp_obj_new_tuple(2, tuple_contents); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ssl_sslsocket_accept_obj, ssl_sslsocket_accept); + +//| def bind(self, address: Tuple[str, int]) -> None: +//| """Bind a socket to an address +//| +//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| ... +//| +STATIC mp_obj_t ssl_sslsocket_bind(mp_obj_t self_in, mp_obj_t addr_in) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + + size_t hostlen; + const char *host = mp_obj_str_get_data(addr_items[0], &hostlen); + mp_int_t port = mp_obj_get_int(addr_items[1]); + if (port < 0) { + mp_raise_ValueError(translate("port must be >= 0")); + } + + bool ok = common_hal_ssl_sslsocket_bind(self, host, hostlen, (uint32_t)port); + if (!ok) { + mp_raise_ValueError(translate("Error: Failure to bind")); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_bind_obj, ssl_sslsocket_bind); + +//| def close(self) -> None: +//| """Closes this Socket""" +//| +STATIC mp_obj_t ssl_sslsocket_close(mp_obj_t self_in) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_ssl_sslsocket_close(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ssl_sslsocket_close_obj, ssl_sslsocket_close); + +//| def connect(self, address: Tuple[str, int]) -> None: +//| """Connect a socket to a remote address +//| +//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| ... +//| +STATIC mp_obj_t ssl_sslsocket_connect(mp_obj_t self_in, mp_obj_t addr_in) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + + size_t hostlen; + const char *host = mp_obj_str_get_data(addr_items[0], &hostlen); + mp_int_t port = mp_obj_get_int(addr_items[1]); + if (port < 0) { + mp_raise_ValueError(translate("port must be >= 0")); + } + + common_hal_ssl_sslsocket_connect(self, host, hostlen, (uint32_t)port); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_connect_obj, ssl_sslsocket_connect); + +//| def listen(self, backlog: int) -> None: +//| """Set socket to listen for incoming connections +//| +//| :param ~int backlog: length of backlog queue for waiting connetions""" +//| ... +//| +STATIC mp_obj_t ssl_sslsocket_listen(mp_obj_t self_in, mp_obj_t backlog_in) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int backlog = mp_obj_get_int(backlog_in); + + common_hal_ssl_sslsocket_listen(self, backlog); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_listen_obj, ssl_sslsocket_listen); + +//| def recv_into(self, buffer: WriteableBuffer, bufsize: int) -> int: +//| """Reads some bytes from the connected remote address, writing +//| into the provided buffer. If bufsize <= len(buffer) is given, +//| a maximum of bufsize bytes will be read into the buffer. If no +//| valid value is given for bufsize, the default is the length of +//| the given buffer. +//| +//| Suits sockets of type SOCK_STREAM +//| Returns an int of number of bytes read. +//| +//| :param bytearray buffer: buffer to receive into +//| :param int bufsize: optionally, a maximum number of bytes to read.""" +//| ... +//| +STATIC mp_obj_t ssl_sslsocket_recv_into(size_t n_args, const mp_obj_t *args) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (common_hal_ssl_sslsocket_get_closed(self)) { + // Bad file number. + mp_raise_OSError(MP_EBADF); + } + // if (!common_hal_ssl_sslsocket_get_connected(self)) { + // // not connected + // mp_raise_OSError(MP_ENOTCONN); + // } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + mp_int_t len = bufinfo.len; + if (n_args == 3) { + mp_int_t given_len = mp_obj_get_int(args[2]); + if (given_len > len) { + mp_raise_ValueError(translate("buffer too small for requested bytes")); + } + if (given_len > 0 && given_len < len) { + len = given_len; + } + } + + if (len == 0) { + return MP_OBJ_NEW_SMALL_INT(0); + } + + mp_int_t ret = common_hal_ssl_sslsocket_recv_into(self, (byte *)bufinfo.buf, len); + return mp_obj_new_int_from_uint(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ssl_sslsocket_recv_into_obj, 2, 3, ssl_sslsocket_recv_into); + +//| def send(self, bytes: ReadableBuffer) -> int: +//| """Send some bytes to the connected remote address. +//| Suits sockets of type SOCK_STREAM +//| +//| :param ~bytes bytes: some bytes to send""" +//| ... +//| +STATIC mp_obj_t ssl_sslsocket_send(mp_obj_t self_in, mp_obj_t buf_in) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (common_hal_ssl_sslsocket_get_closed(self)) { + // Bad file number. + mp_raise_OSError(MP_EBADF); + } + if (!common_hal_ssl_sslsocket_get_connected(self)) { + mp_raise_BrokenPipeError(); + } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + mp_int_t ret = common_hal_ssl_sslsocket_send(self, bufinfo.buf, bufinfo.len); + if (ret == -1) { + mp_raise_BrokenPipeError(); + } + return mp_obj_new_int_from_uint(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_send_obj, ssl_sslsocket_send); + +// //| def setsockopt(self, level: int, optname: int, value: int) -> None: +// //| """Sets socket options""" +// //| ... +// //| +// STATIC mp_obj_t ssl_sslsocket_setsockopt(size_t n_args, const mp_obj_t *args) { +// } +// STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ssl_sslsocket_setsockopt_obj, 4, 4, ssl_sslsocket_setsockopt); + +//| def settimeout(self, value: int) -> None: +//| """Set the timeout value for this socket. +//| +//| :param ~int value: timeout in seconds. 0 means non-blocking. None means block indefinitely.""" +//| ... +//| +STATIC mp_obj_t ssl_sslsocket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t timeout_ms; + if (timeout_in == mp_const_none) { + timeout_ms = -1; + } else { + #if MICROPY_PY_BUILTINS_FLOAT + timeout_ms = 1000 * mp_obj_get_float(timeout_in); + #else + timeout_ms = 1000 * mp_obj_get_int(timeout_in); + #endif + } + common_hal_ssl_sslsocket_settimeout(self, timeout_ms); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_settimeout_obj, ssl_sslsocket_settimeout); + +//| def setblocking(self, flag: bool) -> Optional[int]: +//| """Set the blocking behaviour of this socket. +//| +//| :param ~bool flag: False means non-blocking, True means block indefinitely.""" +//| ... +//| +// method socket.setblocking(flag) +STATIC mp_obj_t ssl_sslsocket_setblocking(mp_obj_t self_in, mp_obj_t blocking) { + ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (mp_obj_is_true(blocking)) { + common_hal_ssl_sslsocket_settimeout(self, -1); + } else { + common_hal_ssl_sslsocket_settimeout(self, 0); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_setblocking_obj, ssl_sslsocket_setblocking); + +STATIC const mp_rom_map_elem_t ssl_sslsocket_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&ssl_sslsocket___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ssl_sslsocket_close_obj) }, + + { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&ssl_sslsocket_accept_obj) }, + { MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&ssl_sslsocket_bind_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&ssl_sslsocket_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ssl_sslsocket_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&ssl_sslsocket_listen_obj) }, + { MP_ROM_QSTR(MP_QSTR_recv_into), MP_ROM_PTR(&ssl_sslsocket_recv_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&ssl_sslsocket_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&ssl_sslsocket_setblocking_obj) }, + // { MP_ROM_QSTR(MP_QSTR_setsockopt), MP_ROM_PTR(&ssl_sslsocket_setsockopt_obj) }, + { MP_ROM_QSTR(MP_QSTR_settimeout), MP_ROM_PTR(&ssl_sslsocket_settimeout_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ssl_sslsocket_locals_dict, ssl_sslsocket_locals_dict_table); + +const mp_obj_type_t ssl_sslsocket_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_SSLSocket, + .locals_dict = (mp_obj_dict_t *)&ssl_sslsocket_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_generic_unary_op, + ) +}; diff --git a/circuitpython/shared-bindings/ssl/SSLSocket.h b/circuitpython/shared-bindings/ssl/SSLSocket.h new file mode 100644 index 0000000..d651934 --- /dev/null +++ b/circuitpython/shared-bindings/ssl/SSLSocket.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Lucian Copeland 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_SSL_SSLSOCKET_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SSL_SSLSOCKET_H + +#include "common-hal/ssl/SSLSocket.h" + +extern const mp_obj_type_t ssl_sslsocket_type; + +ssl_sslsocket_obj_t *common_hal_ssl_sslsocket_accept(ssl_sslsocket_obj_t *self, uint8_t *ip, uint32_t *port); +bool common_hal_ssl_sslsocket_bind(ssl_sslsocket_obj_t *self, const char *host, size_t hostlen, uint32_t port); +void common_hal_ssl_sslsocket_close(ssl_sslsocket_obj_t *self); +void common_hal_ssl_sslsocket_connect(ssl_sslsocket_obj_t *self, const char *host, size_t hostlen, uint32_t port); +bool common_hal_ssl_sslsocket_get_closed(ssl_sslsocket_obj_t *self); +bool common_hal_ssl_sslsocket_get_connected(ssl_sslsocket_obj_t *self); +bool common_hal_ssl_sslsocket_listen(ssl_sslsocket_obj_t *self, int backlog); +mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, const uint8_t *buf, uint32_t len); +mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, uint32_t len); +void common_hal_ssl_sslsocket_settimeout(ssl_sslsocket_obj_t *self, uint32_t timeout_ms); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SSL_SSLSOCKET_H diff --git a/circuitpython/shared-bindings/ssl/__init__.c b/circuitpython/shared-bindings/ssl/__init__.c new file mode 100644 index 0000000..6967860 --- /dev/null +++ b/circuitpython/shared-bindings/ssl/__init__.c @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "py/objexcept.h" +#include "py/objstr.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "shared-bindings/ssl/__init__.h" +#include "shared-bindings/ssl/SSLContext.h" + +//| """ +//| The `ssl` module provides SSL contexts to wrap sockets in. +//| +//| |see_cpython_module| :mod:`cpython:ssl`. +//| """ +//| + +//| def create_default_context() -> ssl.SSLContext: +//| """Return the default SSLContext.""" +//| ... +//| + +STATIC mp_obj_t ssl_create_default_context(void) { + ssl_sslcontext_obj_t *s = m_new_obj(ssl_sslcontext_obj_t); + s->base.type = &ssl_sslcontext_type; + + common_hal_ssl_create_default_context(s); + return s; +} +MP_DEFINE_CONST_FUN_OBJ_0(ssl_create_default_context_obj, ssl_create_default_context); + +STATIC const mp_rom_map_elem_t ssl_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ssl) }, + + { MP_ROM_QSTR(MP_QSTR_create_default_context), MP_ROM_PTR(&ssl_create_default_context_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SSLContext), MP_ROM_PTR(&ssl_sslcontext_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ssl_globals, ssl_globals_table); + +const mp_obj_module_t ssl_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&ssl_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ssl, ssl_module, CIRCUITPY_SSL); diff --git a/circuitpython/shared-bindings/ssl/__init__.h b/circuitpython/shared-bindings/ssl/__init__.h new file mode 100644 index 0000000..64f69c3 --- /dev/null +++ b/circuitpython/shared-bindings/ssl/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_SSL___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SSL___INIT___H + +#include "common-hal/ssl/SSLContext.h" + +void common_hal_ssl_create_default_context(ssl_sslcontext_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SSL___INIT___H diff --git a/circuitpython/shared-bindings/storage/__init__.c b/circuitpython/shared-bindings/storage/__init__.c new file mode 100644 index 0000000..277c834 --- /dev/null +++ b/circuitpython/shared-bindings/storage/__init__.c @@ -0,0 +1,283 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Josef Gajdusek + * 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 <string.h> + +#include "extmod/vfs_fat.h" +#include "py/obj.h" +#include "py/objnamedtuple.h" +#include "py/runtime.h" +#include "shared-bindings/storage/__init__.h" +#include "supervisor/shared/translate.h" + +//| """Storage management +//| +//| The `storage` provides storage management functionality such as mounting and +//| unmounting which is typically handled by the operating system hosting Python. +//| CircuitPython does not have an OS, so this module provides this functionality +//| directly. + +//| For more information regarding using the `storage` module, refer to the `CircuitPython +//| Essentials Learn guide +//| <https://learn.adafruit.com/circuitpython-essentials/circuitpython-storage>`_. +//| """ +//| + +//| def mount(filesystem: VfsFat, mount_path: str, *, readonly: bool = False) -> None: +//| """Mounts the given filesystem object at the given path. +//| +//| This is the CircuitPython analog to the UNIX ``mount`` command. +//| +//| :param VfsFat filesystem: The filesystem to mount. +//| :param str mount_path: Where to mount the filesystem. +//| :param bool readonly: True when the filesystem should be readonly to CircuitPython. +//| """ +//| ... +//| +STATIC mp_obj_t storage_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_filesystem, ARG_mount_path, ARG_readonly }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_filesystem, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_mount_path, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get the mount point + const char *mnt_str = mp_obj_str_get_str(args[ARG_mount_path].u_obj); + + // Make sure we're given an object we can mount. + // TODO(tannewt): Make sure we have all the methods we need to operating it + // as a file system. + mp_obj_t vfs_obj = args[ARG_filesystem].u_obj; + mp_obj_t dest[2]; + mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest); + if (dest[0] == MP_OBJ_NULL) { + mp_raise_ValueError(translate("filesystem must provide mount method")); + } + + common_hal_storage_mount(vfs_obj, mnt_str, args[ARG_readonly].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(storage_mount_obj, 0, storage_mount); + +//| def umount(mount: Union[str, VfsFat]) -> None: +//| """Unmounts the given filesystem object or if *mount* is a path, then unmount +//| the filesystem mounted at that location. +//| +//| This is the CircuitPython analog to the UNIX ``umount`` command.""" +//| ... +//| +STATIC mp_obj_t storage_umount(mp_obj_t mnt_in) { + if (mp_obj_is_str(mnt_in)) { + common_hal_storage_umount_path(mp_obj_str_get_str(mnt_in)); + } else { + common_hal_storage_umount_object(mnt_in); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(storage_umount_obj, storage_umount); + +//| def remount(mount_path: str, readonly: bool = False, *, disable_concurrent_write_protection: bool = False) -> None: +//| """Remounts the given path with new parameters. +//| +//| :param str mount_path: The path to remount. +//| :param bool readonly: True when the filesystem should be readonly to CircuitPython. +//| :param bool disable_concurrent_write_protection: When True, the check that makes sure the +//| underlying filesystem data is written by one computer is disabled. Disabling the protection +//| allows CircuitPython and a host to write to the same filesystem with the risk that the +//| filesystem will be corrupted.""" +//| ... +//| +STATIC mp_obj_t storage_remount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_mount_path, ARG_readonly, ARG_disable_concurrent_write_protection }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mount_path, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_readonly, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_disable_concurrent_write_protection, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const char *mnt_str = mp_obj_str_get_str(args[ARG_mount_path].u_obj); + + common_hal_storage_remount(mnt_str, args[ARG_readonly].u_bool, args[ARG_disable_concurrent_write_protection].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(storage_remount_obj, 0, storage_remount); + +//| def getmount(mount_path: str) -> VfsFat: +//| """Retrieves the mount object associated with the mount path""" +//| ... +//| +STATIC mp_obj_t storage_getmount(const mp_obj_t mnt_in) { + return common_hal_storage_getmount(mp_obj_str_get_str(mnt_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(storage_getmount_obj, storage_getmount); + +//| def erase_filesystem() -> None: +//| """Erase and re-create the ``CIRCUITPY`` filesystem. +//| +//| On boards that present USB-visible ``CIRCUITPY`` drive (e.g., SAMD21 and SAMD51), +//| then call `microcontroller.reset()` to restart CircuitPython and have the +//| host computer remount CIRCUITPY. +//| +//| This function can be called from the REPL when ``CIRCUITPY`` +//| has become corrupted. +//| +//| .. warning:: All the data on ``CIRCUITPY`` will be lost, and +//| CircuitPython will restart on certain boards.""" +//| ... +//| + +STATIC mp_obj_t storage_erase_filesystem(void) { + common_hal_storage_erase_filesystem(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(storage_erase_filesystem_obj, storage_erase_filesystem); + +//| def disable_usb_drive() -> None: +//| """Disable presenting ``CIRCUITPY`` as a USB mass storage device. +//| By default, the device is enabled and ``CIRCUITPY`` is visible. +//| Can be called in ``boot.py``, before USB is connected.""" +//| ... +//| +STATIC mp_obj_t storage_disable_usb_drive(void) { + #if CIRCUITPY_USB_MSC + if (!common_hal_storage_disable_usb_drive()) { + #else + if (true) { + #endif + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(storage_disable_usb_drive_obj, storage_disable_usb_drive); + +//| def enable_usb_drive() -> None: +//| """Enabled presenting ``CIRCUITPY`` as a USB mass storage device. +//| By default, the device is enabled and ``CIRCUITPY`` is visible, +//| so you do not normally need to call this function. +//| Can be called in ``boot.py``, before USB is connected. +//| +//| If you enable too many devices at once, you will run out of USB endpoints. +//| The number of available endpoints varies by microcontroller. +//| CircuitPython will go into safe mode after running boot.py to inform you if +//| not enough endpoints are available. +//| """ +//| ... +//| +STATIC mp_obj_t storage_enable_usb_drive(void) { + #if CIRCUITPY_USB_MSC + if (!common_hal_storage_enable_usb_drive()) { + #else + if (true) { + #endif + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(storage_enable_usb_drive_obj, storage_enable_usb_drive); + +STATIC const mp_rom_map_elem_t storage_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_storage) }, + + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&storage_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&storage_umount_obj) }, + { MP_ROM_QSTR(MP_QSTR_remount), MP_ROM_PTR(&storage_remount_obj) }, + { MP_ROM_QSTR(MP_QSTR_getmount), MP_ROM_PTR(&storage_getmount_obj) }, + { MP_ROM_QSTR(MP_QSTR_erase_filesystem), MP_ROM_PTR(&storage_erase_filesystem_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_usb_drive), MP_ROM_PTR(&storage_disable_usb_drive_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_usb_drive), MP_ROM_PTR(&storage_enable_usb_drive_obj) }, + +//| class VfsFat: +//| def __init__(self, block_device: str) -> None: +//| """Create a new VfsFat filesystem around the given block device. +//| +//| :param block_device: Block device the the filesystem lives on""" +//| +//| label: str +//| """The filesystem label, up to 11 case-insensitive bytes. Note that +//| this property can only be set when the device is writable by the +//| microcontroller.""" +//| ... +//| +//| def mkfs(self) -> None: +//| """Format the block device, deleting any data that may have been there""" +//| ... +//| +//| def open(self, path: str, mode: str) -> None: +//| """Like builtin ``open()``""" +//| ... +//| +//| def ilistdir(self, path: str) -> Iterator[Union[Tuple[AnyStr, int, int, int], Tuple[AnyStr, int, int]]]: +//| """Return an iterator whose values describe files and folders within +//| ``path``""" +//| ... +//| +//| def mkdir(self, path: str) -> None: +//| """Like `os.mkdir`""" +//| ... +//| +//| def rmdir(self, path: str) -> None: +//| """Like `os.rmdir`""" +//| ... +//| +//| def stat(self, path: str) -> Tuple[int, int, int, int, int, int, int, int, int, int]: +//| """Like `os.stat`""" +//| ... +//| +//| def statvfs(self, path: int) -> Tuple[int, int, int, int, int, int, int, int, int, int]: +//| """Like `os.statvfs`""" +//| ... +//| +//| def mount(self, readonly: bool, mkfs: VfsFat) -> None: +//| """Don't call this directly, call `storage.mount`.""" +//| ... +//| +//| def umount(self) -> None: +//| """Don't call this directly, call `storage.umount`.""" +//| ... +//| + { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(storage_module_globals, storage_module_globals_table); + +const mp_obj_module_t storage_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&storage_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_storage, storage_module, CIRCUITPY_STORAGE); diff --git a/circuitpython/shared-bindings/storage/__init__.h b/circuitpython/shared-bindings/storage/__init__.h new file mode 100644 index 0000000..fbf492e --- /dev/null +++ b/circuitpython/shared-bindings/storage/__init__.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_STORAGE___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_STORAGE___INIT___H + +#include <stdint.h> +#include <stdbool.h> + +#include "shared-module/storage/__init__.h" + +void common_hal_storage_mount(mp_obj_t vfs_obj, const char *path, bool readonly); +void common_hal_storage_umount_path(const char *path); +void common_hal_storage_umount_object(mp_obj_t vfs_obj); +void common_hal_storage_remount(const char *path, bool readonly, bool disable_concurrent_write_protection); +mp_obj_t common_hal_storage_getmount(const char *path); +void common_hal_storage_erase_filesystem(void); + +bool common_hal_storage_disable_usb_drive(void); +bool common_hal_storage_enable_usb_drive(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_STORAGE___INIT___H diff --git a/circuitpython/shared-bindings/struct/__init__.c b/circuitpython/shared-bindings/struct/__init__.c new file mode 100644 index 0000000..c29c33c --- /dev/null +++ b/circuitpython/shared-bindings/struct/__init__.c @@ -0,0 +1,181 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2017 Michael McWethy + * + * 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 <assert.h> +#include <string.h> + +#include "py/runtime.h" +#include "py/builtin.h" +#include "py/objtuple.h" +#include "py/binary.h" +#include "py/parsenum.h" +#include "shared-bindings/struct/__init__.h" +#include "shared-module/struct/__init__.h" +#include "supervisor/shared/translate.h" + +//| """Manipulation of c-style data +//| +//| |see_cpython_module| :mod:`cpython:struct`. +//| +//| Supported size/byte order prefixes: *@*, *<*, *>*, *!*. +//| +//| Supported format codes: *b*, *B*, *x*, *h*, *H*, *i*, *I*, *l*, *L*, *q*, *Q*, +//| *s*, *P*, *f*, *d* (the latter 2 depending on the floating-point support).""" +//| + + +//| def calcsize(fmt: str) -> int: +//| """Return the number of bytes needed to store the given fmt.""" +//| ... +//| + +STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) { + + return MP_OBJ_NEW_SMALL_INT(shared_modules_struct_calcsize(fmt_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(struct_calcsize_obj, struct_calcsize); + +//| def pack(fmt: str, *values: Any) -> bytes: +//| """Pack the values according to the format string fmt. +//| The return value is a bytes object encoding the values.""" +//| ... +//| + +STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) { + mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0])); + vstr_t vstr; + vstr_init_len(&vstr, size); + byte *p = (byte *)vstr.buf; + memset(p, 0, size); + byte *end_p = &p[size]; + shared_modules_struct_pack_into(args[0], p, end_p, n_args - 1, &args[1]); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack); + +//| def pack_into(fmt: str, buffer: WriteableBuffer, offset: int, *values: Any) -> None: +//| """Pack the values according to the format string fmt into a buffer +//| starting at offset. offset may be negative to count from the end of buffer.""" +//| ... +//| + +STATIC mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + mp_int_t offset = mp_obj_get_int(args[2]); + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = (mp_int_t)bufinfo.len + offset; + if (offset < 0) { + mp_raise_RuntimeError(translate("buffer too small")); + } + } + byte *p = (byte *)bufinfo.buf; + byte *end_p = &p[bufinfo.len]; + p += offset; + + shared_modules_struct_pack_into(args[0], p, end_p, n_args - 3, &args[3]); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_into_obj, 3, MP_OBJ_FUN_ARGS_MAX, struct_pack_into); + +//| def unpack(fmt: str, data: ReadableBuffer) -> Tuple[Any, ...]: +//| """Unpack from the data according to the format string fmt. The return value +//| is a tuple of the unpacked values. The buffer size must match the size +//| required by the format.""" +//| ... +//| + +STATIC mp_obj_t struct_unpack(size_t n_args, const mp_obj_t *args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + byte *p = bufinfo.buf; + byte *end_p = &p[bufinfo.len]; + + // true means check the size must be exactly right. + return MP_OBJ_FROM_PTR(shared_modules_struct_unpack_from(args[0], p, end_p, true)); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_unpack_obj, 2, 3, struct_unpack); + +//| def unpack_from(fmt: str, data: ReadableBuffer, offset: int = 0) -> Tuple[Any, ...]: +//| """Unpack from the data starting at offset according to the format string fmt. +//| offset may be negative to count from the end of buffer. The return value is +//| a tuple of the unpacked values. The buffer size must be at least as big +//| as the size required by the form.""" +//| ... +//| + +STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_format, ARG_buffer, ARG_offset }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_format, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_offset, MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, 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); + byte *p = bufinfo.buf; + byte *end_p = &p[bufinfo.len]; + + mp_int_t offset = args[ARG_offset].u_int; + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = bufinfo.len + offset; + if (offset < 0) { + mp_raise_RuntimeError(translate("buffer too small")); + } + } + p += offset; + + // false means the size doesn't have to be exact. struct.unpack_from() only requires + // that be buffer be big enough. + return MP_OBJ_FROM_PTR(shared_modules_struct_unpack_from(args[ARG_format].u_obj, p, end_p, false)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(struct_unpack_from_obj, 0, struct_unpack_from); + +STATIC const mp_rom_map_elem_t mp_module_struct_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_struct) }, + { MP_ROM_QSTR(MP_QSTR_calcsize), MP_ROM_PTR(&struct_calcsize_obj) }, + { MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&struct_pack_obj) }, + { MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&struct_pack_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&struct_unpack_obj) }, + { MP_ROM_QSTR(MP_QSTR_unpack_from), MP_ROM_PTR(&struct_unpack_from_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_struct_globals, mp_module_struct_globals_table); + +const mp_obj_module_t struct_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_struct_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_struct, struct_module, CIRCUITPY_STRUCT); diff --git a/circuitpython/shared-bindings/struct/__init__.h b/circuitpython/shared-bindings/struct/__init__.h new file mode 100644 index 0000000..4c7c65d --- /dev/null +++ b/circuitpython/shared-bindings/struct/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_STRUCT___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_STRUCT___INIT___H + +void shared_modules_struct_pack_into(mp_obj_t fmt_in, byte *p, byte *end_p, size_t n_args, const mp_obj_t *args); +mp_uint_t shared_modules_struct_calcsize(mp_obj_t fmt_in); +mp_obj_tuple_t *shared_modules_struct_unpack_from(mp_obj_t fmt_in, byte *p, byte *end_p, bool exact_size); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_RANDOM___INIT___H diff --git a/circuitpython/shared-bindings/supervisor/RunReason.c b/circuitpython/shared-bindings/supervisor/RunReason.c new file mode 100644 index 0000000..a2a5fe1 --- /dev/null +++ b/circuitpython/shared-bindings/supervisor/RunReason.c @@ -0,0 +1,62 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "py/enum.h" + +#include "shared-bindings/supervisor/RunReason.h" + +MAKE_ENUM_VALUE(supervisor_run_reason_type, run_reason, STARTUP, RUN_REASON_STARTUP); +MAKE_ENUM_VALUE(supervisor_run_reason_type, run_reason, AUTO_RELOAD, RUN_REASON_AUTO_RELOAD); +MAKE_ENUM_VALUE(supervisor_run_reason_type, run_reason, SUPERVISOR_RELOAD, RUN_REASON_SUPERVISOR_RELOAD); +MAKE_ENUM_VALUE(supervisor_run_reason_type, run_reason, REPL_RELOAD, RUN_REASON_REPL_RELOAD); + +//| class RunReason: +//| """The reason that CircuitPython started running.""" +//| +//| STARTUP: object +//| """CircuitPython started the microcontroller started up. See `microcontroller.Processor.reset_reason` +//| for more detail on why the microcontroller was started.""" +//| +//| AUTO_RELOAD: object +//| """CircuitPython restarted due to an external write to the filesystem.""" +//| +//| SUPERVISOR_RELOAD: object +//| """CircuitPython restarted due to a call to `supervisor.reload()`.""" +//| +//| REPL_RELOAD: object +//| """CircuitPython started due to the user typing CTRL-D in the REPL.""" +//| +MAKE_ENUM_MAP(supervisor_run_reason) { + MAKE_ENUM_MAP_ENTRY(run_reason, STARTUP), + MAKE_ENUM_MAP_ENTRY(run_reason, AUTO_RELOAD), + MAKE_ENUM_MAP_ENTRY(run_reason, SUPERVISOR_RELOAD), + MAKE_ENUM_MAP_ENTRY(run_reason, REPL_RELOAD), +}; +STATIC MP_DEFINE_CONST_DICT(supervisor_run_reason_locals_dict, supervisor_run_reason_locals_table); + +MAKE_PRINTER(supervisor, supervisor_run_reason); + +MAKE_ENUM_TYPE(supervisor, RunReason, supervisor_run_reason); diff --git a/circuitpython/shared-bindings/supervisor/RunReason.h b/circuitpython/shared-bindings/supervisor/RunReason.h new file mode 100644 index 0000000..391e6d3 --- /dev/null +++ b/circuitpython/shared-bindings/supervisor/RunReason.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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. + */ + +#pragma once + +typedef enum { + RUN_REASON_STARTUP, + RUN_REASON_AUTO_RELOAD, + RUN_REASON_SUPERVISOR_RELOAD, + RUN_REASON_REPL_RELOAD, +} supervisor_run_reason_t; + +extern const mp_obj_type_t supervisor_run_reason_type; diff --git a/circuitpython/shared-bindings/supervisor/Runtime.c b/circuitpython/shared-bindings/supervisor/Runtime.c new file mode 100644 index 0000000..55217ec --- /dev/null +++ b/circuitpython/shared-bindings/supervisor/Runtime.c @@ -0,0 +1,131 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Michael Schroeder + * + * 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 <stdbool.h> +#include "py/obj.h" +#include "py/enum.h" +#include "py/runtime.h" +#include "py/objproperty.h" + +#include "shared-bindings/supervisor/RunReason.h" +#include "shared-bindings/supervisor/Runtime.h" + +#if (CIRCUITPY_USB) +#include "tusb.h" +#endif + +STATIC supervisor_run_reason_t _run_reason; + +// TODO: add REPL to description once it is operational + +//| class Runtime: +//| """Current status of runtime objects. +//| +//| Usage:: +//| +//| import supervisor +//| if supervisor.runtime.serial_connected: +//| print("Hello World!")""" +//| + +//| def __init__(self) -> None: +//| """You cannot create an instance of `supervisor.Runtime`. +//| Use `supervisor.runtime` to access the sole instance available.""" +//| ... +//| + +//| usb_connected: bool +//| """Returns the USB enumeration status (read-only).""" +//| +STATIC mp_obj_t supervisor_runtime_get_usb_connected(mp_obj_t self) { + #if CIRCUITPY_USB + return mp_obj_new_bool(tud_ready()); + #else + return mp_const_false; + #endif +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_usb_connected_obj, supervisor_runtime_get_usb_connected); + +MP_PROPERTY_GETTER(supervisor_runtime_usb_connected_obj, + (mp_obj_t)&supervisor_runtime_get_usb_connected_obj); + +//| serial_connected: bool +//| """Returns the USB serial communication status (read-only).""" +//| +STATIC mp_obj_t supervisor_runtime_get_serial_connected(mp_obj_t self) { + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial_connected()); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial_connected_obj, supervisor_runtime_get_serial_connected); + +MP_PROPERTY_GETTER(supervisor_runtime_serial_connected_obj, + (mp_obj_t)&supervisor_runtime_get_serial_connected_obj); + +//| serial_bytes_available: int +//| """Returns the whether any bytes are available to read +//| on the USB serial input. Allows for polling to see whether +//| to call the built-in input() or wait. (read-only)""" +//| +STATIC mp_obj_t supervisor_runtime_get_serial_bytes_available(mp_obj_t self) { + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial_bytes_available()); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial_bytes_available_obj, supervisor_runtime_get_serial_bytes_available); + +MP_PROPERTY_GETTER(supervisor_runtime_serial_bytes_available_obj, + (mp_obj_t)&supervisor_runtime_get_serial_bytes_available_obj); + +supervisor_run_reason_t supervisor_get_run_reason(void) { + return _run_reason; +} + +void supervisor_set_run_reason(supervisor_run_reason_t run_reason) { + _run_reason = run_reason; +} + +//| run_reason: RunReason +//| """Returns why CircuitPython started running this particular time.""" +//| +STATIC mp_obj_t supervisor_runtime_get_run_reason(mp_obj_t self) { + return cp_enum_find(&supervisor_run_reason_type, _run_reason); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_run_reason_obj, supervisor_runtime_get_run_reason); + +MP_PROPERTY_GETTER(supervisor_runtime_run_reason_obj, + (mp_obj_t)&supervisor_runtime_get_run_reason_obj); + +STATIC const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_usb_connected), MP_ROM_PTR(&supervisor_runtime_usb_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_runtime_serial_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial_bytes_available), MP_ROM_PTR(&supervisor_runtime_serial_bytes_available_obj) }, + { MP_ROM_QSTR(MP_QSTR_run_reason), MP_ROM_PTR(&supervisor_runtime_run_reason_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(supervisor_runtime_locals_dict, supervisor_runtime_locals_dict_table); + +const mp_obj_type_t supervisor_runtime_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Runtime, + .locals_dict = (mp_obj_dict_t *)&supervisor_runtime_locals_dict, +}; diff --git a/circuitpython/shared-bindings/supervisor/Runtime.h b/circuitpython/shared-bindings/supervisor/Runtime.h new file mode 100644 index 0000000..debc5ec --- /dev/null +++ b/circuitpython/shared-bindings/supervisor/Runtime.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Michael Schroeder + * + * 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_RUNTIME_STATUS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_RUNTIME_STATUS_H + +#include <stdbool.h> +#include "py/obj.h" + +#include "shared-bindings/supervisor/RunReason.h" + +extern const mp_obj_type_t supervisor_runtime_type; + +supervisor_run_reason_t supervisor_get_run_reason(void); +void supervisor_set_run_reason(supervisor_run_reason_t run_reason); + +bool common_hal_supervisor_runtime_get_serial_connected(void); + +bool common_hal_supervisor_runtime_get_serial_bytes_available(void); + +// TODO: placeholders for future functions +// bool common_hal_get_supervisor_runtime_repl_active(void); +// bool common_hal_get_supervisor_runtime_usb_enumerated(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SUPERVISOR_RUNTIME_H diff --git a/circuitpython/shared-bindings/supervisor/__init__.c b/circuitpython/shared-bindings/supervisor/__init__.c new file mode 100644 index 0000000..0b86b7b --- /dev/null +++ b/circuitpython/shared-bindings/supervisor/__init__.c @@ -0,0 +1,337 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 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 <string.h> + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/objstr.h" + +#include "shared/runtime/interrupt_char.h" +#include "supervisor/shared/bluetooth/bluetooth.h" +#include "supervisor/shared/display.h" +#include "supervisor/shared/status_leds.h" +#include "supervisor/shared/reload.h" +#include "supervisor/shared/stack.h" +#include "supervisor/shared/traceback.h" +#include "supervisor/shared/translate.h" +#include "supervisor/shared/workflow.h" + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/supervisor/__init__.h" +#include "shared-bindings/time/__init__.h" +#include "shared-bindings/supervisor/Runtime.h" + +//| """Supervisor settings""" +//| + +//| runtime: Runtime +//| """Runtime information, such as ``runtime.serial_connected`` +//| (USB serial connection status). +//| This object is the sole instance of `supervisor.Runtime`.""" +//| + +//| def enable_autoreload() -> None: +//| """Enable autoreload based on USB file write activity.""" +//| ... +//| +STATIC mp_obj_t supervisor_enable_autoreload(void) { + autoreload_enable(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(supervisor_enable_autoreload_obj, supervisor_enable_autoreload); + +//| def disable_autoreload() -> None: +//| """Disable autoreload based on USB file write activity until +//| `enable_autoreload` is called.""" +//| ... +//| +STATIC mp_obj_t supervisor_disable_autoreload(void) { + autoreload_disable(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(supervisor_disable_autoreload_obj, supervisor_disable_autoreload); + +//| def set_rgb_status_brightness(brightness: int) -> None: +//| """Set brightness of status RGB LED from 0-255. This will take effect +//| after the current code finishes and the status LED is used to show +//| the finish state.""" +//| ... +//| +STATIC mp_obj_t supervisor_set_rgb_status_brightness(mp_obj_t lvl) { + // This must be int. If cast to uint8_t first, will never raise a ValueError. + int brightness_int = mp_obj_get_int(lvl); + mp_arg_validate_int_range(brightness_int, 0, 255, MP_QSTR_brightness); + set_status_brightness((uint8_t)brightness_int); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_set_rgb_status_brightness_obj, supervisor_set_rgb_status_brightness); + +//| def reload() -> None: +//| """Reload the main Python code and run it (equivalent to hitting Ctrl-D at the REPL).""" +//| ... +//| +STATIC mp_obj_t supervisor_reload(void) { + reload_initiate(RUN_REASON_SUPERVISOR_RELOAD); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(supervisor_reload_obj, supervisor_reload); + +//| def set_next_stack_limit(size: int) -> None: +//| """Set the size of the stack for the next vm run. If its too large, the default will be used.""" +//| ... +//| +STATIC mp_obj_t supervisor_set_next_stack_limit(mp_obj_t size_obj) { + mp_int_t size = mp_obj_get_int(size_obj); + + if (size < 256) { + mp_raise_ValueError(translate("Stack size must be at least 256")); + } + set_next_stack_size(size); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_set_next_stack_limit_obj, supervisor_set_next_stack_limit); + +//| def set_next_code_file(filename: Optional[str], *, reload_on_success : bool = False, reload_on_error: bool = False, sticky_on_success: bool = False, sticky_on_error: bool = False, sticky_on_reload: bool = False) -> None: +//| """Set what file to run on the next vm run. +//| +//| When not ``None``, the given ``filename`` is inserted at the front of the usual ['code.py', +//| 'main.py'] search sequence. +//| +//| The optional keyword arguments specify what happens after the specified file has run: +//| +//| ``sticky_on_…`` determine whether the newly set filename and options stay in effect: If +//| True, further runs will continue to run that file (unless it says otherwise by calling +//| ``set_next_code_filename()`` itself). If False, the settings will only affect one run and +//| revert to the standard code.py/main.py afterwards. +//| +//| ``reload_on_…`` determine how to continue: If False, wait in the usual "Code done running. +//| Waiting for reload. / Press any key to enter the REPL. Use CTRL-D to reload." state. If +//| True, reload immediately as if CTRL-D was pressed. +//| +//| ``…_on_success`` take effect when the program runs to completion or calls ``sys.exit()``. +//| +//| ``…_on_error`` take effect when the program exits with an exception, including the +//| KeyboardInterrupt caused by CTRL-C. +//| +//| ``…_on_reload`` take effect when the program is interrupted by files being written to the USB +//| drive (auto-reload) or when it calls ``supervisor.reload()``. +//| +//| These settings are stored in RAM, not in persistent memory, and will therefore only affect +//| soft reloads. Powering off or resetting the device will always revert to standard settings. +//| +//| When called multiple times in the same run, only the last call takes effect, replacing any +//| settings made by previous ones. This is the main use of passing ``None`` as a filename: to +//| reset to the standard search sequence.""" +//| ... +//| +STATIC mp_obj_t supervisor_set_next_code_file(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_filename, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_reload_on_success, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_reload_on_error, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_sticky_on_success, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_sticky_on_error, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_sticky_on_reload, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + struct { + mp_arg_val_t filename; + mp_arg_val_t reload_on_success; + mp_arg_val_t reload_on_error; + mp_arg_val_t sticky_on_success; + mp_arg_val_t sticky_on_error; + mp_arg_val_t sticky_on_reload; + } args; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); + if (!mp_obj_is_str_or_bytes(args.filename.u_obj) && args.filename.u_obj != mp_const_none) { + mp_raise_TypeError(translate("argument has wrong type")); + } + if (args.filename.u_obj == mp_const_none) { + args.filename.u_obj = mp_const_empty_bytes; + } + uint8_t options = 0; + if (args.reload_on_success.u_bool) { + options |= SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS; + } + if (args.reload_on_error.u_bool) { + options |= SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR; + } + if (args.sticky_on_success.u_bool) { + options |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS; + } + if (args.sticky_on_error.u_bool) { + options |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_ERROR; + } + if (args.sticky_on_reload.u_bool) { + options |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD; + } + size_t len; + const char *filename = mp_obj_str_get_data(args.filename.u_obj, &len); + free_memory(next_code_allocation); + if (options != 0 || len != 0) { + next_code_allocation = allocate_memory(align32_size(sizeof(next_code_info_t) + len + 1), false, true); + if (next_code_allocation == NULL) { + m_malloc_fail(sizeof(next_code_info_t) + len + 1); + } + next_code_info_t *next_code = (next_code_info_t *)next_code_allocation->ptr; + next_code->options = options | SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET; + memcpy(&next_code->filename, filename, len); + next_code->filename[len] = '\0'; + } else { + next_code_allocation = NULL; + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(supervisor_set_next_code_file_obj, 0, supervisor_set_next_code_file); + +//| def ticks_ms() -> int: +//| """Return the time in milliseconds since an unspecified reference point, wrapping after 2**29ms. +//| +//| The value is initialized so that the first overflow occurs about 65 +//| seconds after power-on, making it feasible to check that your program +//| works properly around an overflow. +//| +//| The wrap value was chosen so that it is always possible to add +//| or subtract two `ticks_ms` values without overflow on a board without +//| long ints (or without allocating any long integer objects, on boards with +//| long ints). +//| +//| This ticks value comes from a low-accuracy clock internal to the +//| microcontroller, just like `time.monotonic`. Due to its low accuracy +//| and the fact that it "wraps around" every few days, it is intended +//| for working with short term events like advancing an LED animation, +//| not for long term events like counting down the time until a holiday. +//| +//| Addition, subtraction, and comparison of ticks values can be done +//| with routines like the following:: +//| +//| _TICKS_PERIOD = const(1<<29) +//| _TICKS_MAX = const(_TICKS_PERIOD-1) +//| _TICKS_HALFPERIOD = const(_TICKS_PERIOD//2) +//| +//| def ticks_add(ticks, delta): +//| "Add a delta to a base number of ticks, performing wraparound at 2**29ms." +//| return (a + b) % _TICKS_PERIOD +//| +//| def ticks_diff(ticks1, ticks2): +//| "Compute the signed difference between two ticks values, assuming that they are within 2**28 ticks" +//| diff = (ticks1 - ticks2) & _TICKS_MAX +//| diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD +//| return diff +//| +//| def ticks_less(ticks1, ticks2): +//| "Return true iff ticks1 is less than ticks2, assuming that they are within 2**28 ticks" +//| return ticks_diff(ticks1, ticks2) < 0 +//| +//| """ +//| ... +mp_obj_t supervisor_ticks_ms(void) { + uint64_t ticks_ms = common_hal_time_monotonic_ms(); + return mp_obj_new_int((ticks_ms + 0x1fff0000) % (1 << 29)); +} +MP_DEFINE_CONST_FUN_OBJ_0(supervisor_ticks_ms_obj, supervisor_ticks_ms); + +//| def get_previous_traceback() -> Optional[str]: +//| """If the last vm run ended with an exception (including the KeyboardInterrupt caused by +//| CTRL-C), returns the traceback as a string. +//| Otherwise, returns ``None``. +//| +//| An exception traceback is only preserved over a soft reload, a hard reset clears it. +//| +//| Only code (main or boot) runs are considered, not REPL runs.""" +//| ... +//| +STATIC mp_obj_t supervisor_get_previous_traceback(void) { + if (prev_traceback_allocation) { + size_t len = strlen((const char *)prev_traceback_allocation->ptr); + if (len > 0) { + mp_obj_str_t *o = m_new_obj(mp_obj_str_t); + o->base.type = &mp_type_str; + o->len = len; + // callers probably aren't going to compare this string, so skip computing the hash + o->hash = 0; + o->data = (const byte *)prev_traceback_allocation->ptr; + return MP_OBJ_FROM_PTR(o); + } + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(supervisor_get_previous_traceback_obj, supervisor_get_previous_traceback); + +//| def disable_ble_workflow() -> None: +//| """Disable ble workflow until a reset. This prevents BLE advertising outside of the VM and +//| the services used for it.""" +//| ... +//| +STATIC mp_obj_t supervisor_disable_ble_workflow(void) { + #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE + mp_raise_NotImplementedError(NULL); + #else + supervisor_bluetooth_disable_workflow(); + #endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(supervisor_disable_ble_workflow_obj, supervisor_disable_ble_workflow); + +//| def reset_terminal(x_pixels: int, y_pixels: int) -> None: +//| """Reset the CircuitPython serial terminal with new dimensions.""" +//| ... +//| +STATIC mp_obj_t supervisor_reset_terminal(mp_obj_t x_pixels, mp_obj_t y_pixels) { + #if CIRCUITPY_DISPLAYIO + supervisor_stop_terminal(); + supervisor_start_terminal(mp_obj_get_int(x_pixels), mp_obj_get_int(y_pixels)); + #else + mp_raise_NotImplementedError(NULL); + #endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(supervisor_reset_terminal_obj, supervisor_reset_terminal); + +STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) }, + { MP_ROM_QSTR(MP_QSTR_enable_autoreload), MP_ROM_PTR(&supervisor_enable_autoreload_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_autoreload), MP_ROM_PTR(&supervisor_disable_autoreload_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_rgb_status_brightness), MP_ROM_PTR(&supervisor_set_rgb_status_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_runtime), MP_ROM_PTR(&common_hal_supervisor_runtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_reload), MP_ROM_PTR(&supervisor_reload_obj) }, + { MP_ROM_QSTR(MP_QSTR_RunReason), MP_ROM_PTR(&supervisor_run_reason_type) }, + { MP_ROM_QSTR(MP_QSTR_set_next_stack_limit), MP_ROM_PTR(&supervisor_set_next_stack_limit_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_next_code_file), MP_ROM_PTR(&supervisor_set_next_code_file_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&supervisor_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_ble_workflow), MP_ROM_PTR(&supervisor_disable_ble_workflow_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset_terminal), MP_ROM_PTR(&supervisor_reset_terminal_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table); + +const mp_obj_module_t supervisor_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&supervisor_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_supervisor, supervisor_module, CIRCUITPY_SUPERVISOR); diff --git a/circuitpython/shared-bindings/supervisor/__init__.h b/circuitpython/shared-bindings/supervisor/__init__.h new file mode 100644 index 0000000..40a1e73 --- /dev/null +++ b/circuitpython/shared-bindings/supervisor/__init__.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Michael Schroeder + * + * 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_SUPERVISOR___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SUPERVISOR___INIT___H + +// #include "py/mpconfig.h" +#include "py/obj.h" + +#include "common-hal/supervisor/Runtime.h" + +extern const super_runtime_obj_t common_hal_supervisor_runtime_obj; +extern mp_obj_t supervisor_ticks_ms(void); + + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SUPERVISOR___INIT___H diff --git a/circuitpython/shared-bindings/support_matrix.rst b/circuitpython/shared-bindings/support_matrix.rst new file mode 100644 index 0000000..e007e27 --- /dev/null +++ b/circuitpython/shared-bindings/support_matrix.rst @@ -0,0 +1,34 @@ +.. _module-support-matrix: + +Module Support Matrix - Which Modules Are Available on Which Boards +=================================================================== + +The following table lists the available built-in modules for each CircuitPython +capable board, as well as each :term:`frozen module` included on it. + +.. raw:: html + + <p id="support-matrix-filter-block"><input placeholder="Filter the boards by available modules" id="support-matrix-filter" type="text"/><span id="support-matrix-filter-num">(all)</span></p> + +.. rst-class:: support-matrix-table +.. list-table:: + :header-rows: 1 + :widths: 7, 50 + + * - Board + - Modules Available + + {% for key, value in support_matrix|dictsort %} + {{ '.. _' ~ key|replace(" ", "-") ~ ':' }} + * - {{ key }} + - {{ ':py:mod:`' ~ value[0]|join("`, :py:mod:`") ~ '`' }} + + {% for module in value[1] %}\ + {% if loop.index == 1 %}**Frozen Modules:** {% endif %}\ + {% if loop.index > 1 %}, {% endif %}\ + {% if module[1] %}{{ '`' ~ module[0] ~ ' <' ~ module[1] ~ '>`__' }}\ + {% else %}{{ '`' ~ module[0] ~ ' <#>`__' }}\ + {% endif %}\ + {% endfor %} + + {% endfor %} diff --git a/circuitpython/shared-bindings/synthio/MidiTrack.c b/circuitpython/shared-bindings/synthio/MidiTrack.c new file mode 100644 index 0000000..7805c1a --- /dev/null +++ b/circuitpython/shared-bindings/synthio/MidiTrack.c @@ -0,0 +1,168 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Artyom Skrobov + * + * 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/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "shared-bindings/synthio/MidiTrack.h" +#include "supervisor/shared/translate.h" + +//| class MidiTrack: +//| """Simple square-wave MIDI synth""" +//| +//| def __init__(self, buffer: ReadableBuffer, tempo: int, *, sample_rate: int = 11025) -> None: +//| """Create a MidiTrack from the given stream of MIDI events. Only "Note On" and "Note Off" events +//| are supported; channel numbers and key velocities are ignored. Up to two notes may be on at the +//| same time. +//| +//| :param ~circuitpython_typing.ReadableBuffer buffer: Stream of MIDI events, as stored in a MIDI file track chunk +//| :param int tempo: Tempo of the streamed events, in MIDI ticks per second +//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory +//| +//| Simple melody:: +//| +//| import audioio +//| import board +//| import synthio +//| +//| dac = audioio.AudioOut(board.SPEAKER) +//| melody = synthio.MidiTrack(b"\\0\\x90H\\0*\\x80H\\0\\6\\x90J\\0*\\x80J\\0\\6\\x90L\\0*\\x80L\\0\\6\\x90J\\0" + +//| b"*\\x80J\\0\\6\\x90H\\0*\\x80H\\0\\6\\x90J\\0*\\x80J\\0\\6\\x90L\\0T\\x80L\\0" + +//| b"\\x0c\\x90H\\0T\\x80H\\0\\x0c\\x90H\\0T\\x80H\\0", tempo=640) +//| dac.play(melody) +//| print("playing") +//| while dac.playing: +//| pass +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_buffer, ARG_tempo, ARG_sample_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_tempo, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} }, + }; + 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); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + + synthio_miditrack_obj_t *self = m_new_obj(synthio_miditrack_obj_t); + self->base.type = &synthio_miditrack_type; + + common_hal_synthio_miditrack_construct(self, + (uint8_t *)bufinfo.buf, bufinfo.len, + args[ARG_tempo].u_int, + args[ARG_sample_rate].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the MidiTrack and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t synthio_miditrack_deinit(mp_obj_t self_in) { + synthio_miditrack_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_synthio_miditrack_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(synthio_miditrack_deinit_obj, synthio_miditrack_deinit); + +STATIC void check_for_deinit(synthio_miditrack_obj_t *self) { + if (common_hal_synthio_miditrack_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> MidiTrack: +//| """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 synthio_miditrack_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_synthio_miditrack_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(synthio_miditrack___exit___obj, 4, 4, synthio_miditrack_obj___exit__); + +//| sample_rate: Optional[int] +//| """32 bit value that tells how quickly samples are played in Hertz (cycles per second).""" +//| +STATIC mp_obj_t synthio_miditrack_obj_get_sample_rate(mp_obj_t self_in) { + synthio_miditrack_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_synthio_miditrack_get_sample_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_miditrack_get_sample_rate_obj, synthio_miditrack_obj_get_sample_rate); + +MP_PROPERTY_GETTER(synthio_miditrack_sample_rate_obj, + (mp_obj_t)&synthio_miditrack_get_sample_rate_obj); + +STATIC const mp_rom_map_elem_t synthio_miditrack_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&synthio_miditrack_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&synthio_miditrack___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&synthio_miditrack_sample_rate_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(synthio_miditrack_locals_dict, synthio_miditrack_locals_dict_table); + +STATIC const audiosample_p_t synthio_miditrack_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .sample_rate = (audiosample_sample_rate_fun)common_hal_synthio_miditrack_get_sample_rate, + .bits_per_sample = (audiosample_bits_per_sample_fun)common_hal_synthio_miditrack_get_bits_per_sample, + .channel_count = (audiosample_channel_count_fun)common_hal_synthio_miditrack_get_channel_count, + .reset_buffer = (audiosample_reset_buffer_fun)synthio_miditrack_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)synthio_miditrack_get_buffer, + .get_buffer_structure = (audiosample_get_buffer_structure_fun)synthio_miditrack_get_buffer_structure, +}; + +const mp_obj_type_t synthio_miditrack_type = { + { &mp_type_type }, + .name = MP_QSTR_MidiTrack, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = synthio_miditrack_make_new, + .locals_dict = (mp_obj_dict_t *)&synthio_miditrack_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &synthio_miditrack_proto, + ), +}; diff --git a/circuitpython/shared-bindings/synthio/MidiTrack.h b/circuitpython/shared-bindings/synthio/MidiTrack.h new file mode 100644 index 0000000..d44d4c3 --- /dev/null +++ b/circuitpython/shared-bindings/synthio/MidiTrack.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Artyom Skrobov + * + * 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_SYNTHIO_MIDITRACK_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SYNTHIO_MIDITRACK_H + +#include "shared-module/synthio/MidiTrack.h" + +extern const mp_obj_type_t synthio_miditrack_type; + +void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self, + const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate); + +void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self); +bool common_hal_synthio_miditrack_deinited(synthio_miditrack_obj_t *self); +uint32_t common_hal_synthio_miditrack_get_sample_rate(synthio_miditrack_obj_t *self); +uint8_t common_hal_synthio_miditrack_get_bits_per_sample(synthio_miditrack_obj_t *self); +uint8_t common_hal_synthio_miditrack_get_channel_count(synthio_miditrack_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SYNTHIO_MIDITRACK_H diff --git a/circuitpython/shared-bindings/synthio/__init__.c b/circuitpython/shared-bindings/synthio/__init__.c new file mode 100644 index 0000000..72fe2eb --- /dev/null +++ b/circuitpython/shared-bindings/synthio/__init__.c @@ -0,0 +1,138 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Artyom Skrobov + * + * 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 <string.h> + +#include "py/mperrno.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "extmod/vfs_fat.h" + +#include "shared-bindings/synthio/__init__.h" +#include "shared-bindings/synthio/MidiTrack.h" + +//| """Support for MIDI synthesis""" +//| +//| def from_file(file: typing.BinaryIO, *, sample_rate: int = 11025) -> MidiTrack: +//| """Create an AudioSample from an already opened MIDI file. +//| Currently, only single-track MIDI (type 0) is supported. +//| +//| :param typing.BinaryIO file: Already opened MIDI file +//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory +//| +//| +//| Playing a MIDI file from flash:: +//| +//| import audioio +//| import board +//| import synthio +//| +//| data = open("single-track.midi", "rb") +//| midi = synthio.from_file(data) +//| a = audioio.AudioOut(board.A0) +//| +//| print("playing") +//| a.play(midi) +//| while a.playing: +//| pass +//| print("stopped")""" +//| ... +//| +STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_file, ARG_sample_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + if (!mp_obj_is_type(args[ARG_file].u_obj, &mp_type_fileio)) { + mp_raise_TypeError(translate("file must be a file opened in byte mode")); + } + pyb_file_obj_t *file = MP_OBJ_TO_PTR(args[ARG_file].u_obj); + + uint8_t chunk_header[14]; + f_rewind(&file->fp); + UINT bytes_read; + if (f_read(&file->fp, chunk_header, sizeof(chunk_header), &bytes_read) != FR_OK) { + mp_raise_OSError(MP_EIO); + } + if (bytes_read != sizeof(chunk_header) || + memcmp(chunk_header, "MThd\0\0\0\6\0\0\0\1", 12)) { + mp_raise_ValueError(translate("Invalid MIDI file")); + // TODO: for a multi-track MIDI (type 1), return an AudioMixer + } + + uint16_t tempo; + if (chunk_header[12] & 0x80) { + tempo = -(int8_t)chunk_header[12] * chunk_header[13]; + } else { + tempo = 2 * ((chunk_header[12] << 8) | chunk_header[13]); + } + + if (f_read(&file->fp, chunk_header, 8, &bytes_read) != FR_OK) { + mp_raise_OSError(MP_EIO); + } + if (bytes_read != 8 || memcmp(chunk_header, "MTrk", 4)) { + mp_raise_ValueError(translate("Invalid MIDI file")); + } + uint32_t track_size = (chunk_header[4] << 24) | + (chunk_header[5] << 16) | (chunk_header[6] << 8) | chunk_header[7]; + uint8_t *buffer = m_malloc(track_size, false); + if (f_read(&file->fp, buffer, track_size, &bytes_read) != FR_OK) { + mp_raise_OSError(MP_EIO); + } + if (bytes_read != track_size) { + mp_raise_ValueError(translate("Invalid MIDI file")); + } + + synthio_miditrack_obj_t *result = m_new_obj(synthio_miditrack_obj_t); + result->base.type = &synthio_miditrack_type; + + common_hal_synthio_miditrack_construct(result, buffer, track_size, + tempo, args[ARG_sample_rate].u_int); + + m_free(buffer); + + return MP_OBJ_FROM_PTR(result); +} +MP_DEFINE_CONST_FUN_OBJ_KW(synthio_from_file_obj, 1, synthio_from_file); + + +STATIC const mp_rom_map_elem_t synthio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_synthio) }, + { MP_ROM_QSTR(MP_QSTR_MidiTrack), MP_ROM_PTR(&synthio_miditrack_type) }, + { MP_ROM_QSTR(MP_QSTR_from_file), MP_ROM_PTR(&synthio_from_file_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(synthio_module_globals, synthio_module_globals_table); + +const mp_obj_module_t synthio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&synthio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_synthio, synthio_module, CIRCUITPY_SYNTHIO); diff --git a/circuitpython/shared-bindings/synthio/__init__.h b/circuitpython/shared-bindings/synthio/__init__.h new file mode 100644 index 0000000..14af1a5 --- /dev/null +++ b/circuitpython/shared-bindings/synthio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Artyom Skrobov + * + * 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_SYNTHIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_SYNTHIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SYNTHIO___INIT___H diff --git a/circuitpython/shared-bindings/terminalio/Terminal.c b/circuitpython/shared-bindings/terminalio/Terminal.c new file mode 100644 index 0000000..cdeca59 --- /dev/null +++ b/circuitpython/shared-bindings/terminalio/Terminal.c @@ -0,0 +1,126 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/terminalio/Terminal.h" +#include "shared-bindings/util.h" + +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "shared-bindings/fontio/BuiltinFont.h" +#include "supervisor/shared/translate.h" + +//| class Terminal: +//| """Display a character stream with a TileGrid""" +//| +//| def __init__(self, tilegrid: displayio.TileGrid, font: fontio.BuiltinFont) -> None: +//| """Terminal manages tile indices and cursor position based on VT100 commands. The font should be +//| a `fontio.BuiltinFont` and the TileGrid's bitmap should match the font's bitmap.""" +//| ... +//| + +STATIC mp_obj_t terminalio_terminal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_tilegrid, ARG_font }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_tilegrid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_font, MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + 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); + + displayio_tilegrid_t *tilegrid = mp_arg_validate_type(args[ARG_tilegrid].u_obj, &displayio_tilegrid_type, MP_QSTR_tilegrid); + + fontio_builtinfont_t *font = mp_arg_validate_type(args[ARG_font].u_obj, &fontio_builtinfont_type, MP_QSTR_font); + + terminalio_terminal_obj_t *self = m_new_obj(terminalio_terminal_obj_t); + self->base.type = &terminalio_terminal_type; + + common_hal_terminalio_terminal_construct(self, tilegrid, font); + return MP_OBJ_FROM_PTR(self); +} + +// These are standard stream methods. Code is in py/stream.c. +// +//| def write(self, buf: ReadableBuffer) -> Optional[int]: +//| """Write the buffer of bytes to the bus. +//| +//| :return: the number of bytes written +//| :rtype: int or None""" +//| ... +//| +STATIC mp_uint_t terminalio_terminal_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + terminalio_terminal_obj_t *self = MP_OBJ_TO_PTR(self_in); + const byte *buf = buf_in; + + return common_hal_terminalio_terminal_write(self, buf, size, errcode); +} + +STATIC mp_uint_t terminalio_terminal_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + terminalio_terminal_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_WR) && common_hal_terminalio_terminal_ready_to_tx(self)) { + ret |= MP_IOCTL_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_rom_map_elem_t terminalio_terminal_locals_dict_table[] = { + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(terminalio_terminal_locals_dict, terminalio_terminal_locals_dict_table); + +STATIC const mp_stream_p_t terminalio_terminal_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = NULL, + .write = terminalio_terminal_write, + .ioctl = terminalio_terminal_ioctl, + .is_text = true, +}; + +const mp_obj_type_t terminalio_terminal_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Terminal, + .make_new = terminalio_terminal_make_new, + .locals_dict = (mp_obj_dict_t *)&terminalio_terminal_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &terminalio_terminal_stream_p, + ), +}; diff --git a/circuitpython/shared-bindings/terminalio/Terminal.h b/circuitpython/shared-bindings/terminalio/Terminal.h new file mode 100644 index 0000000..f884edd --- /dev/null +++ b/circuitpython/shared-bindings/terminalio/Terminal.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_TERMINALIO_TERMINAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_TERMINALIO_TERMINAL_H + +#include "shared-module/terminalio/Terminal.h" + +#include "shared-bindings/displayio/TileGrid.h" + +extern const mp_obj_type_t terminalio_terminal_type; + +extern void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self, + displayio_tilegrid_t *tilegrid, const fontio_builtinfont_t *font); + +// Write characters. len is in characters NOT bytes! +extern size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, + const uint8_t *data, size_t len, int *errcode); + +extern bool common_hal_terminalio_terminal_ready_to_tx(terminalio_terminal_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_TERMINALIO_TERMINAL_H diff --git a/circuitpython/shared-bindings/terminalio/__init__.c b/circuitpython/shared-bindings/terminalio/__init__.c new file mode 100644 index 0000000..5fd1a27 --- /dev/null +++ b/circuitpython/shared-bindings/terminalio/__init__.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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/terminalio/__init__.h" +#include "shared-bindings/terminalio/Terminal.h" +#include "supervisor/shared/display.h" + +#include "py/runtime.h" + +//| """Displays text in a TileGrid +//| +//| The `terminalio` module contains classes to display a character stream on a display. The built +//| in font is available as ``terminalio.FONT``. +//| +//| .. note:: This module does not give access to the +//| `REPL <https://learn.adafruit.com/welcome-to-circuitpython/interacting-with-the-serial-console>`_. +//| +//| """ +//| +//| FONT: fontio.BuiltinFont +//| """The built in font""" +//| +STATIC const mp_rom_map_elem_t terminalio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_terminalio) }, + { MP_ROM_QSTR(MP_QSTR_Terminal), MP_OBJ_FROM_PTR(&terminalio_terminal_type) }, + { MP_ROM_QSTR(MP_QSTR_FONT), MP_ROM_PTR(&supervisor_terminal_font) }, +}; + + +STATIC MP_DEFINE_CONST_DICT(terminalio_module_globals, terminalio_module_globals_table); + +const mp_obj_module_t terminalio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&terminalio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_terminalio, terminalio_module, CIRCUITPY_DISPLAYIO && CIRCUITPY_TERMINALIO); diff --git a/circuitpython/shared-bindings/terminalio/__init__.h b/circuitpython/shared-bindings/terminalio/__init__.h new file mode 100644 index 0000000..4be14df --- /dev/null +++ b/circuitpython/shared-bindings/terminalio/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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_TERMINALIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_TERMINALIO___INIT___H + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_TERMINALIO___INIT___H diff --git a/circuitpython/shared-bindings/time/__init__.c b/circuitpython/shared-bindings/time/__init__.c new file mode 100644 index 0000000..5b16ded --- /dev/null +++ b/circuitpython/shared-bindings/time/__init__.c @@ -0,0 +1,348 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Josef Gajdusek + * 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 <string.h> + +#include "py/obj.h" +#include "py/objnamedtuple.h" +#include "py/runtime.h" +#include "shared/timeutils/timeutils.h" +#include "shared-bindings/rtc/__init__.h" +#include "shared-bindings/time/__init__.h" +#include "supervisor/shared/translate.h" + +//| """time and timing related functions +//| +//| |see_cpython_module| :mod:`cpython:time`. +//| """ +//| +//| def monotonic() -> float: +//| """Returns an always increasing value of time with an unknown reference +//| point. Only use it to compare against other values from `time.monotonic()`. +//| +//| On most boards, `time.monotonic()` converts a 64-bit millisecond tick counter +//| to a float. Floats on most boards are encoded in 30 bits internally, with +//| effectively 22 bits of precision. The float returned by `time.monotonic()` will +//| accurately represent time to millisecond precision only up to 2**22 milliseconds +//| (about 1.165 hours). +//| At that point it will start losing precision, and its value will change only +//| every second millisecond. At 2**23 milliseconds it will change every fourth +//| millisecond, and so forth. +//| +//| If you need more consistent precision, use `time.monotonic_ns()`, or `supervisor.ticks_ms()`. +//| `time.monotonic_ns()` is not available on boards without long integer support. +//| `supervisor.ticks_ms()` uses intervals of a millisecond, but wraps around, and is not +//| CPython-compatible. +//| +//| :return: the current monotonic time +//| :rtype: float""" +//| ... +//| +STATIC mp_obj_t time_monotonic(void) { + uint64_t ticks_ms = common_hal_time_monotonic_ms(); + return mp_obj_new_float(uint64_to_float(ticks_ms) / 1000.0f); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_obj, time_monotonic); + +//| def sleep(seconds: float) -> None: +//| """Sleep for a given number of seconds. +//| +//| :param float seconds: the time to sleep in fractional seconds""" +//| ... +//| +STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) { + #if MICROPY_PY_BUILTINS_FLOAT + mp_float_t seconds = mp_obj_get_float(seconds_o); + mp_float_t msecs = 1000.0f * seconds + 0.5f; + #else + mp_int_t seconds = mp_obj_get_int(seconds_o); + mp_int_t msecs = 1000 * seconds; + #endif + if (seconds < 0) { + mp_raise_ValueError(translate("sleep length must be non-negative")); + } + common_hal_time_delay_ms(msecs); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_obj, time_sleep); + +#if MICROPY_PY_COLLECTIONS +STATIC mp_obj_t struct_time_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + size_t len; + mp_obj_t *items; + mp_obj_get_array(args[0], &len, &items); + if (len != 9) { + mp_raise_TypeError(translate("time.struct_time() takes a 9-sequence")); + } + return namedtuple_make_new(type, len, 0, items); +} + +//| class struct_time: +//| def __init__(self, time_tuple: Sequence[int]) -> None: +//| """Structure used to capture a date and time. Can be constructed from a `struct_time`, `tuple`, `list`, or `namedtuple` with 9 elements. +//| +//| :param Sequence time_tuple: Sequence of time info: ``(tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)`` +//| +//| * ``tm_year``: the year, 2017 for example +//| * ``tm_mon``: the month, range [1, 12] +//| * ``tm_mday``: the day of the month, range [1, 31] +//| * ``tm_hour``: the hour, range [0, 23] +//| * ``tm_min``: the minute, range [0, 59] +//| * ``tm_sec``: the second, range [0, 61] +//| * ``tm_wday``: the day of the week, range [0, 6], Monday is 0 +//| * ``tm_yday``: the day of the year, range [1, 366], -1 indicates not known +//| * ``tm_isdst``: 1 when in daylight savings, 0 when not, -1 if unknown.""" +//| ... +//| +const mp_obj_namedtuple_type_t struct_time_type_obj = { + .base = { + .base = { + .type = &mp_type_type + }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_struct_time, + .print = namedtuple_print, + .parent = &mp_type_tuple, + .make_new = struct_time_make_new, + .attr = namedtuple_attr, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_obj_tuple_unary_op, + .binary_op = mp_obj_tuple_binary_op, + .subscr = mp_obj_tuple_subscr, + .getiter = mp_obj_tuple_getiter, + ), + }, + .n_fields = 9, + .fields = { + MP_QSTR_tm_year, + MP_QSTR_tm_mon, + MP_QSTR_tm_mday, + MP_QSTR_tm_hour, + MP_QSTR_tm_min, + MP_QSTR_tm_sec, + MP_QSTR_tm_wday, + MP_QSTR_tm_yday, + MP_QSTR_tm_isdst + }, +}; + +mp_obj_t struct_time_from_tm(timeutils_struct_time_t *tm) { + timeutils_struct_time_t tmp; + mp_uint_t secs = timeutils_seconds_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + timeutils_seconds_since_epoch_to_struct_time(secs, &tmp); + tm->tm_wday = tmp.tm_wday; + tm->tm_yday = tmp.tm_yday; + + mp_obj_t elems[9] = { + mp_obj_new_int(tm->tm_year), + mp_obj_new_int(tm->tm_mon), + mp_obj_new_int(tm->tm_mday), + mp_obj_new_int(tm->tm_hour), + mp_obj_new_int(tm->tm_min), + mp_obj_new_int(tm->tm_sec), + mp_obj_new_int(tm->tm_wday), + mp_obj_new_int(tm->tm_yday), + mp_obj_new_int(-1), // tm_isdst is not supported + }; + + return namedtuple_make_new((const mp_obj_type_t *)&struct_time_type_obj, 9, 0, elems); +}; + +void struct_time_to_tm(mp_obj_t t, timeutils_struct_time_t *tm) { + mp_obj_t *elems; + size_t len; + + if (!mp_obj_is_type(t, &mp_type_tuple) && !mp_obj_is_type(t, &struct_time_type_obj.base)) { + mp_raise_TypeError(translate("Tuple or struct_time argument required")); + } + + mp_obj_tuple_get(t, &len, &elems); + if (len != 9) { + mp_raise_TypeError(translate("function takes exactly 9 arguments")); + } + + tm->tm_year = mp_obj_get_int(elems[0]); + tm->tm_mon = mp_obj_get_int(elems[1]); + tm->tm_mday = mp_obj_get_int(elems[2]); + tm->tm_hour = mp_obj_get_int(elems[3]); + tm->tm_min = mp_obj_get_int(elems[4]); + tm->tm_sec = mp_obj_get_int(elems[5]); + tm->tm_wday = mp_obj_get_int(elems[6]); + tm->tm_yday = mp_obj_get_int(elems[7]); + // elems[8] tm_isdst is not supported +} +#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE +// Function to return a NotImplementedError on platforms that don't +// support long integers +STATIC mp_obj_t time_not_implemented(void) { + mp_raise_NotImplementedError(translate("No long integer support")); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_not_implemented_obj, time_not_implemented); +#endif + +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE +mp_obj_t MP_WEAK rtc_get_time_source_time(void) { + mp_raise_RuntimeError(translate("RTC is not supported on this board")); +} + +//| def time() -> int: +//| """Return the current time in seconds since since Jan 1, 1970. +//| +//| :return: the current time +//| :rtype: int""" +//| ... +//| +STATIC mp_obj_t time_time(void) { + timeutils_struct_time_t tm; + struct_time_to_tm(rtc_get_time_source_time(), &tm); + mp_uint_t secs = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return mp_obj_new_int_from_uint(secs); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); + +//| def monotonic_ns() -> int: +//| """Return the time of the monotonic clock, which cannot go backward, in nanoseconds. +//| Not available on boards without long integer support. +//| +//| :return: the current time +//| :rtype: int""" +//| ... +//| +STATIC mp_obj_t time_monotonic_ns(void) { + uint64_t time64 = common_hal_time_monotonic_ns(); + return mp_obj_new_int_from_ll((long long)time64); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_ns_obj, time_monotonic_ns); + +//| def localtime(secs: int) -> struct_time: +//| """Convert a time expressed in seconds since Jan 1, 1970 to a struct_time in +//| local time. If secs is not provided or None, the current time as returned +//| by time() is used. +//| The earliest date for which it can generate a time is Jan 1, 2000. +//| +//| :return: the current time +//| :rtype: time.struct_time""" +//| ... +//| +STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + if (n_args == 0 || args[0] == mp_const_none) { + return rtc_get_time_source_time(); + } + + mp_obj_t arg = args[0]; + if (mp_obj_is_float(arg)) { + arg = mp_obj_new_int_from_float(mp_obj_get_float(arg)); + } + + mp_int_t secs = mp_obj_get_int(arg); + + #if MICROPY_EPOCH_IS_1970 + if (secs < 0 || (mp_uint_t)secs < TIMEUTILS_SECONDS_1970_TO_2000) { + #else + if (secs < 0) { + #endif + mp_raise_msg(&mp_type_OverflowError, translate("timestamp out of range for platform time_t")); + } + + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(secs, &tm); + + return struct_time_from_tm(&tm); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime); + +//| def mktime(t: struct_time) -> int: +//| """This is the inverse function of localtime(). Its argument is the +//| struct_time or full 9-tuple (since the dst flag is needed; use -1 as the +//| dst flag if it is unknown) which expresses the time in local time, not UTC. +//| The earliest date for which it can generate a time is Jan 1, 2000. +//| +//| :return: seconds +//| :rtype: int""" +//| ... +//| +STATIC mp_obj_t time_mktime(mp_obj_t t) { + mp_obj_t *elem; + size_t len; + + if (!mp_obj_is_type(t, &mp_type_tuple) && !mp_obj_is_type(t, &struct_time_type_obj.base)) { + mp_raise_TypeError(translate("Tuple or struct_time argument required")); + } + + mp_obj_tuple_get(t, &len, &elem); + if (len != 9) { + mp_raise_TypeError_varg(translate("function takes %d positional arguments but %d were given"), 9, len); + } + + if (mp_obj_get_int(elem[0]) < 2000) { + mp_raise_msg(&mp_type_OverflowError, translate("timestamp out of range for platform time_t")); + } + + mp_uint_t secs = timeutils_mktime(mp_obj_get_int(elem[0]), mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), + mp_obj_get_int(elem[3]), mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5])); + return mp_obj_new_int_from_uint(secs); +} +MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); +#endif // MICROPY_LONGINT_IMPL +#endif // MICROPY_PY_COLLECTIONS + +STATIC const mp_rom_map_elem_t time_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_time) }, + + { MP_ROM_QSTR(MP_QSTR_monotonic), MP_ROM_PTR(&time_monotonic_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&time_sleep_obj) }, + #if MICROPY_PY_COLLECTIONS + { MP_ROM_QSTR(MP_QSTR_struct_time), MP_ROM_PTR(&struct_time_type_obj) }, + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&time_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&time_mktime_obj) }, + #endif // MICROPY_LONGINT_IMPL + #endif // MICROPY_PY_COLLECTIONS + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) }, + { MP_ROM_QSTR(MP_QSTR_monotonic_ns), MP_ROM_PTR(&time_monotonic_ns_obj) }, + #endif + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE + { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&time_not_implemented_obj) }, + { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&time_not_implemented_obj) }, + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_not_implemented_obj) }, + { MP_ROM_QSTR(MP_QSTR_monotonic_ns), MP_ROM_PTR(&time_not_implemented_obj) }, + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t time_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&time_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_time, time_module, CIRCUITPY_TIME); diff --git a/circuitpython/shared-bindings/time/__init__.h b/circuitpython/shared-bindings/time/__init__.h new file mode 100644 index 0000000..dba8229 --- /dev/null +++ b/circuitpython/shared-bindings/time/__init__.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_TIME___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_TIME___INIT___H + +#include <stdint.h> +#include <stdbool.h> + +#include "shared/timeutils/timeutils.h" + +extern mp_obj_t struct_time_from_tm(timeutils_struct_time_t *tm); +extern void struct_time_to_tm(mp_obj_t t, timeutils_struct_time_t *tm); + +extern uint64_t common_hal_time_monotonic_ms(void); +extern uint64_t common_hal_time_monotonic_ns(void); +extern void common_hal_time_delay_ms(uint32_t); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_TIME___INIT___H diff --git a/circuitpython/shared-bindings/touchio/TouchIn.c b/circuitpython/shared-bindings/touchio/TouchIn.c new file mode 100644 index 0000000..23b3525 --- /dev/null +++ b/circuitpython/shared-bindings/touchio/TouchIn.c @@ -0,0 +1,200 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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 <string.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/mphal.h" +#include "py/nlr.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/touchio/TouchIn.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class TouchIn: +//| """Read the state of a capacitive touch sensor +//| +//| Usage:: +//| +//| import touchio +//| from board import * +//| +//| touch = touchio.TouchIn(A1) +//| while True: +//| if touch.value: +//| print("touched!")""" +//| + +//| def __init__(self, pin: microcontroller.Pin) -> None: +//| """Use the TouchIn on the given pin. +//| +//| :param ~microcontroller.Pin pin: the pin to read from""" +//| ... +//| +STATIC mp_obj_t touchio_touchin_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + // 1st argument is the pin + const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[0]); + + touchio_touchin_obj_t *self = m_new_obj(touchio_touchin_obj_t); + self->base.type = &touchio_touchin_type; + common_hal_touchio_touchin_construct(self, pin); + + return (mp_obj_t)self; +} + +//| def deinit(self) -> None: +//| """Deinitialises the TouchIn and releases any hardware resources for reuse.""" +//| ... +//| +STATIC mp_obj_t touchio_touchin_deinit(mp_obj_t self_in) { + touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_touchio_touchin_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(touchio_touchin_deinit_obj, touchio_touchin_deinit); + +STATIC void check_for_deinit(touchio_touchin_obj_t *self) { + if (common_hal_touchio_touchin_deinited(self)) { + raise_deinited_error(); + } +} + +//| def __enter__(self) -> TouchIn: +//| """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 touchio_touchin_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_touchio_touchin_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(touchio_touchin___exit___obj, 4, 4, touchio_touchin_obj___exit__); + +//| value: bool +//| """Whether the touch pad is being touched or not. (read-only) +//| +//| True when `raw_value` > `threshold`.""" +//| +STATIC mp_obj_t touchio_touchin_obj_get_value(mp_obj_t self_in) { + touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_touchio_touchin_get_value(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(touchio_touchin_get_value_obj, touchio_touchin_obj_get_value); + +MP_PROPERTY_GETTER(touchio_touchin_value_obj, + (mp_obj_t)&touchio_touchin_get_value_obj); + + +//| raw_value: int +//| """The raw touch measurement as an `int`. (read-only)""" +//| +STATIC mp_obj_t touchio_touchin_obj_get_raw_value(mp_obj_t self_in) { + touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_touchio_touchin_get_raw_value(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(touchio_touchin_get_raw_value_obj, touchio_touchin_obj_get_raw_value); + +MP_PROPERTY_GETTER(touchio_touchin_raw_value_obj, + (mp_obj_t)&touchio_touchin_get_raw_value_obj); + + +//| threshold: Optional[int] +//| """Minimum `raw_value` needed to detect a touch (and for `value` to be `True`). +//| +//| When the **TouchIn** object is created, an initial `raw_value` is read from the pin, +//| and then `threshold` is set to be 100 + that value. +//| +//| You can adjust `threshold` to make the pin more or less sensitive:: +//| +//| import board +//| import touchio +//| +//| touch = touchio.TouchIn(board.A1) +//| touch.threshold = 7300""" +//| +STATIC mp_obj_t touchio_touchin_obj_get_threshold(mp_obj_t self_in) { + touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_touchio_touchin_get_threshold(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(touchio_touchin_get_threshold_obj, touchio_touchin_obj_get_threshold); + +STATIC mp_obj_t touchio_touchin_obj_set_threshold(mp_obj_t self_in, mp_obj_t threshold_obj) { + touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + uint32_t new_threshold = mp_obj_get_int(threshold_obj); + if (new_threshold < 0 || new_threshold > UINT16_MAX) { + // I would use MP_STRINGIFY(UINT16_MAX), but that prints "0xffff" instead of 65536. + mp_raise_ValueError(translate("threshold must be in the range 0-65536")); + } + common_hal_touchio_touchin_set_threshold(self, new_threshold); + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_2(touchio_touchin_set_threshold_obj, touchio_touchin_obj_set_threshold); + +MP_PROPERTY_GETSET(touchio_touchin_threshold_obj, + (mp_obj_t)&touchio_touchin_get_threshold_obj, + (mp_obj_t)&touchio_touchin_set_threshold_obj); + + +STATIC const mp_rom_map_elem_t touchio_touchin_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&touchio_touchin___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&touchio_touchin_deinit_obj) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_value), MP_ROM_PTR(&touchio_touchin_value_obj)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_raw_value), MP_ROM_PTR(&touchio_touchin_raw_value_obj)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_threshold), MP_ROM_PTR(&touchio_touchin_threshold_obj)}, +}; + +STATIC MP_DEFINE_CONST_DICT(touchio_touchin_locals_dict, touchio_touchin_locals_dict_table); + +const mp_obj_type_t touchio_touchin_type = { + { &mp_type_type }, + .name = MP_QSTR_TouchIn, + .make_new = touchio_touchin_make_new, + .locals_dict = (mp_obj_t)&touchio_touchin_locals_dict, +}; diff --git a/circuitpython/shared-bindings/touchio/TouchIn.h b/circuitpython/shared-bindings/touchio/TouchIn.h new file mode 100644 index 0000000..1da2cf1 --- /dev/null +++ b/circuitpython/shared-bindings/touchio/TouchIn.h @@ -0,0 +1,48 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * + * 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_TOUCHIO_TOUCHIN_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_TOUCHIO_TOUCHIN_H + +#include "common-hal/microcontroller/Pin.h" + +#if CIRCUITPY_TOUCHIO_USE_NATIVE +#include "common-hal/touchio/TouchIn.h" +#else +#include "shared-module/touchio/TouchIn.h" +#endif + +extern const mp_obj_type_t touchio_touchin_type; + +void common_hal_touchio_touchin_construct(touchio_touchin_obj_t *self, const mcu_pin_obj_t *pin); +void common_hal_touchio_touchin_deinit(touchio_touchin_obj_t *self); +bool common_hal_touchio_touchin_deinited(touchio_touchin_obj_t *self); +bool common_hal_touchio_touchin_get_value(touchio_touchin_obj_t *self); +uint16_t common_hal_touchio_touchin_get_raw_value(touchio_touchin_obj_t *self); +uint16_t common_hal_touchio_touchin_get_threshold(touchio_touchin_obj_t *self); +void common_hal_touchio_touchin_set_threshold(touchio_touchin_obj_t *self, uint16_t new_threshold); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_TOUCHIO_TOUCHIN_H diff --git a/circuitpython/shared-bindings/touchio/__init__.c b/circuitpython/shared-bindings/touchio/__init__.c new file mode 100644 index 0000000..e2a6ad9 --- /dev/null +++ b/circuitpython/shared-bindings/touchio/__init__.c @@ -0,0 +1,72 @@ +/* + * 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/touchio/__init__.h" +#include "shared-bindings/touchio/TouchIn.h" + +#include "py/runtime.h" + +//| """Touch related IO +//| +//| The `touchio` module contains classes to provide access to touch IO typically +//| accelerated by hardware on the onboard microcontroller. +//| +//| 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 touchio +//| from board import * +//| +//| touch_pin = touchio.TouchIn(D6) +//| print(touch_pin.value) +//| +//| This example will initialize the the device, and print the +//| :py:data:`~touchio.TouchIn.value`.""" +//| + +STATIC const mp_rom_map_elem_t touchio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_touchio) }, + { MP_ROM_QSTR(MP_QSTR_TouchIn), MP_ROM_PTR(&touchio_touchin_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(touchio_module_globals, touchio_module_globals_table); + +const mp_obj_module_t touchio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&touchio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_touchio, touchio_module, CIRCUITPY_TOUCHIO); diff --git a/circuitpython/shared-bindings/touchio/__init__.h b/circuitpython/shared-bindings/touchio/__init__.h new file mode 100644 index 0000000..f1d3501 --- /dev/null +++ b/circuitpython/shared-bindings/touchio/__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_TOUCHIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_TOUCHIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_TOUCHIO___INIT___H diff --git a/circuitpython/shared-bindings/traceback/__init__.c b/circuitpython/shared-bindings/traceback/__init__.c new file mode 100644 index 0000000..880fe08 --- /dev/null +++ b/circuitpython/shared-bindings/traceback/__init__.c @@ -0,0 +1,175 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "py/stream.h" +#include "py/runtime.h" + +#include "shared-module/traceback/__init__.h" + +//| """Traceback Module +//| +//| This module provides a standard interface to print stack traces of programs. +//| This is useful when you want to print stack traces under program control. +//| +//| |see_cpython_module| :mod:`cpython:traceback`. +//| """ +//| ... +//| + +STATIC void traceback_exception_common(mp_print_t *print, mp_obj_t value, mp_obj_t tb_obj, mp_obj_t limit_obj) { + if (!mp_obj_is_exception_instance(value)) { + mp_raise_TypeError(translate("invalid exception")); + } + + mp_int_t limit = 0; + bool print_tb = true; + if (limit_obj != mp_const_none) { + limit = mp_obj_get_int(limit_obj); + print_tb = (limit != 0); + } + + mp_obj_exception_t *exc = mp_obj_exception_get_native(value); + mp_obj_traceback_t *trace_backup = exc->traceback; + + if (tb_obj != mp_const_none && print_tb) { + exc->traceback = mp_arg_validate_type(tb_obj, &mp_type_traceback, MP_QSTR_tb); + } else { + exc->traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj; + } + + shared_module_traceback_print_exception(MP_OBJ_TO_PTR(value), print, limit); + exc->traceback = trace_backup; +} + +//| def format_exception(etype: Type[BaseException], value: BaseException, tb: TracebackType, +//| limit: Optional[int] = None, chain: Optional[bool] = True) -> None: +//| """Format a stack trace and the exception information. +//| +//| The arguments have the same meaning as the corresponding arguments +//| to print_exception(). The return value is a list of strings, each +//| ending in a newline and some containing internal newlines. When +//| these lines are concatenated and printed, exactly the same text is +//| printed as does print_exception(). +//| +//| .. note:: Setting ``chain`` will have no effect as chained exceptions are not yet implemented. +//| +//| :param Type[BaseException] etype: This is ignored and inferred from the type of ``value``. +//| :param BaseException value: The exception. Must be an instance of `BaseException`. +//| :param TracebackType tb: The traceback object. If `None`, the traceback will not be printed. +//| :param int limit: Print up to limit stack trace entries (starting from the caller’s frame) if limit is positive. +//| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed. +//| :param bool chain: If `True` then chained exceptions will be printed (note: not yet implemented). +//| +//| """ +//| ... +//| +STATIC mp_obj_t traceback_format_exception(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_etype, ARG_value, ARG_tb, ARG_limit, ARG_chain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_etype, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_value, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_tb, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_limit, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_chain, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_print_t print; + vstr_t vstr; + vstr_init_print(&vstr, 0, &print); + traceback_exception_common(&print, args[ARG_value].u_obj, args[ARG_tb].u_obj, args[ARG_limit].u_obj); + return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(traceback_format_exception_obj, 0, traceback_format_exception); + +//| def print_exception(etype: Type[BaseException], value: BaseException, tb: TracebackType, +//| limit: Optional[int] = None, file: Optional[io.FileIO] = None, chain: Optional[bool] = True) -> None: +//| +//| """Prints exception information and stack trace entries. +//| +//| .. note:: Setting ``chain`` will have no effect as chained exceptions are not yet implemented. +//| +//| :param Type[BaseException] etype: This is ignored and inferred from the type of ``value``. +//| :param BaseException value: The exception. Must be an instance of `BaseException`. +//| :param TracebackType tb: The traceback object. If `None`, the traceback will not be printed. +//| :param int limit: Print up to limit stack trace entries (starting from the caller’s frame) if limit is positive. +//| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed. +//| :param io.FileIO file: If file is omitted or `None`, the output goes to `sys.stderr`; otherwise it should be an open +//| file or file-like object to receive the output. +//| :param bool chain: If `True` then chained exceptions will be printed (note: not yet implemented). +//| +//| """ +//| ... +//| + +STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_etype, ARG_value, ARG_tb, ARG_limit, ARG_file, ARG_chain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_etype, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_value, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_tb, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_limit, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_file, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_chain, MP_ARG_BOOL, {.u_bool = true} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_print_t print = mp_plat_print; + if (args[ARG_file].u_obj != mp_const_none) { + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + mp_get_stream_raise(args[ARG_file].u_obj, MP_STREAM_OP_WRITE); + print.data = MP_OBJ_TO_PTR(args[ARG_file].u_obj); + print.print_strn = mp_stream_write_adaptor; + #else + mp_raise_NotImplementedError(translate("file write is not available")); + #endif + } + + traceback_exception_common(&print, args[ARG_value].u_obj, args[ARG_tb].u_obj, args[ARG_limit].u_obj); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(traceback_print_exception_obj, 0, traceback_print_exception); + +STATIC const mp_rom_map_elem_t traceback_module_globals_table[] = { + // module name + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_traceback) }, + // module functions + { MP_ROM_QSTR(MP_QSTR_format_exception), MP_ROM_PTR(&traceback_format_exception_obj) }, + { MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&traceback_print_exception_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(traceback_module_globals, traceback_module_globals_table); + +const mp_obj_module_t traceback_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&traceback_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_traceback, traceback_module, CIRCUITPY_TRACEBACK); diff --git a/circuitpython/shared-bindings/uheap/__init__.c b/circuitpython/shared-bindings/uheap/__init__.c new file mode 100644 index 0000000..40aa869 --- /dev/null +++ b/circuitpython/shared-bindings/uheap/__init__.c @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#include <stdint.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/uheap/__init__.h" + +//| """Heap size analysis""" +//| + +//| def info(object: object) -> int: +//| """Prints memory debugging info for the given object and returns the +//| estimated size.""" +//| ... +//| +STATIC mp_obj_t uheap_info(mp_obj_t obj) { + uint32_t size = shared_module_uheap_info(obj); + + return MP_OBJ_NEW_SMALL_INT(size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(uheap_info_obj, uheap_info); + +STATIC const mp_rom_map_elem_t uheap_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uheap) }, + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&uheap_info_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(uheap_module_globals, uheap_module_globals_table); + +const mp_obj_module_t uheap_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&uheap_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_uheap, uheap_module, CIRCUITPY_UHEAP); diff --git a/circuitpython/shared-bindings/uheap/__init__.h b/circuitpython/shared-bindings/uheap/__init__.h new file mode 100644 index 0000000..53afb41 --- /dev/null +++ b/circuitpython/shared-bindings/uheap/__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_UHEAP___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_UHEAP___INIT___H + +#include "py/obj.h" + +extern uint32_t shared_module_uheap_info(mp_obj_t obj); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_UHEAP___INIT___H diff --git a/circuitpython/shared-bindings/usb/__init__.c b/circuitpython/shared-bindings/usb/__init__.c new file mode 100644 index 0000000..bae72da --- /dev/null +++ b/circuitpython/shared-bindings/usb/__init__.c @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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 "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "shared-bindings/usb/__init__.h" +#include "shared-bindings/usb/core/__init__.h" + +//| """PyUSB-compatible USB host API +//| +//| The `usb` is a subset of PyUSB that allows you to communicate to USB devices. +//| """ +//| + +STATIC mp_rom_map_elem_t usb_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb) }, + { MP_ROM_QSTR(MP_QSTR_core), MP_OBJ_FROM_PTR(&usb_core_module) }, +}; + +STATIC MP_DEFINE_CONST_DICT(usb_module_globals, usb_module_globals_table); + +const mp_obj_module_t usb_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_usb, usb_module, CIRCUITPY_USB_HOST); diff --git a/circuitpython/shared-bindings/usb/__init__.h b/circuitpython/shared-bindings/usb/__init__.h new file mode 100644 index 0000000..d672285 --- /dev/null +++ b/circuitpython/shared-bindings/usb/__init__.h @@ -0,0 +1,27 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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. + */ + +#pragma once diff --git a/circuitpython/shared-bindings/usb/core/Device.c b/circuitpython/shared-bindings/usb/core/Device.c new file mode 100644 index 0000000..3a9e9bb --- /dev/null +++ b/circuitpython/shared-bindings/usb/core/Device.c @@ -0,0 +1,312 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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. + */ + +// This file uses method signatures and comments derived from the PyUSB code +// that has the below BSD-3 license. +/* Copyright 2009-2017 Wander Lairson Costa + * Copyright 2009-2021 PyUSB contributors + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "py/objproperty.h" +#include "shared-bindings/usb/core/Device.h" +#include "py/runtime.h" + +//| class Device: +//| +//| def __init__(self) -> None: +//| """User code cannot create Device objects. Instead, get them from +//| `usb.core.find`. +//| """ +//| ... +//| + +//| idVendor: int +//| """The USB vendor ID of the device""" +//| +STATIC mp_obj_t usb_core_device_obj_get_idVendor(mp_obj_t self_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_usb_core_device_get_idVendor(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_idVendor_obj, usb_core_device_obj_get_idVendor); + +MP_PROPERTY_GETTER(usb_core_device_idVendor_obj, + (mp_obj_t)&usb_core_device_get_idVendor_obj); + +//| idProduct: int +//| """The USB product ID of the device""" +//| +STATIC mp_obj_t usb_core_device_obj_get_idProduct(mp_obj_t self_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_usb_core_device_get_idProduct(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_idProduct_obj, usb_core_device_obj_get_idProduct); + +MP_PROPERTY_GETTER(usb_core_device_idProduct_obj, + (mp_obj_t)&usb_core_device_get_idProduct_obj); + +//| serial_number: str +//| """The USB device's serial number string.""" +//| +STATIC mp_obj_t usb_core_device_obj_get_serial_number(mp_obj_t self_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_usb_core_device_get_serial_number(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_serial_number_obj, usb_core_device_obj_get_serial_number); + +MP_PROPERTY_GETTER(usb_core_device_serial_number_obj, + (mp_obj_t)&usb_core_device_get_serial_number_obj); + +//| product: str +//| """The USB device's product string.""" +//| +STATIC mp_obj_t usb_core_device_obj_get_product(mp_obj_t self_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_usb_core_device_get_product(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_product_obj, usb_core_device_obj_get_product); + +MP_PROPERTY_GETTER(usb_core_device_product_obj, + (mp_obj_t)&usb_core_device_get_product_obj); + +//| manufacturer: str +//| """The USB device's manufacturer string.""" +//| +STATIC mp_obj_t usb_core_device_obj_get_manufacturer(mp_obj_t self_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_usb_core_device_get_manufacturer(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_core_device_get_manufacturer_obj, usb_core_device_obj_get_manufacturer); + +MP_PROPERTY_GETTER(usb_core_device_manufacturer_obj, + (mp_obj_t)&usb_core_device_get_manufacturer_obj); + +//| def write(self, endpoint: int, data: ReadableBuffer, timeout = None) -> int: +//| """Write data to a specific endpoint on the device. +//| +//| :param int endpoint: the bEndpointAddress you want to communicate with. +//| :param ReadableBuffer data: the data to send +//| :param int timeout: Time to wait specified in milliseconds. (Different from most CircuitPython!) +//| :returns: the number of bytes written +//| """ +//| ... +//| +STATIC mp_obj_t usb_core_device_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_endpoint, ARG_data, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_endpoint, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_INT, {.u_int = 0} }, + }; + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_data].u_obj, &bufinfo, MP_BUFFER_READ); + + return MP_OBJ_NEW_SMALL_INT(common_hal_usb_core_device_write(self, args[ARG_endpoint].u_int, bufinfo.buf, bufinfo.len, args[ARG_timeout].u_int)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_core_device_write_obj, 2, usb_core_device_write); + + +//| def read(self, endpoint: int, size_or_buffer: array.array, timeout = None) -> int: +//| """Read data from the endpoint. +//| +//| :param int endpoint: the bEndpointAddress you want to communicate with. +//| :param array.array size_or_buffer: the array to read data into. PyUSB also allows size but CircuitPython only support array to force deliberate memory use. +//| :param int timeout: Time to wait specified in milliseconds. (Different from most CircuitPython!) +//| :returns: the number of bytes read +//| """ +//| ... +STATIC mp_obj_t usb_core_device_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_endpoint, ARG_size_or_buffer, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_endpoint, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_size_or_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_timeout, MP_ARG_INT, {.u_int = 0} }, + }; + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_size_or_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + + return MP_OBJ_NEW_SMALL_INT(common_hal_usb_core_device_read(self, args[ARG_endpoint].u_int, bufinfo.buf, bufinfo.len, args[ARG_timeout].u_int)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_core_device_read_obj, 2, usb_core_device_read); + +//| def ctrl_transfer(self, bmRequestType, bRequest, wValue=0, wIndex=0, +//| data_or_wLength: array.array = None, timeout = None) -> int: +//| """Do a control transfer on the endpoint 0. The parameters bmRequestType, +//| bRequest, wValue and wIndex are the same of the USB Standard Control +//| Request format. +//| +//| Control requests may or may not have a data payload to write/read. +//| In cases which it has, the direction bit of the bmRequestType +//| field is used to infer the desired request direction. +//| +//| For host to device requests (OUT), data_or_wLength parameter is +//| the data payload to send, and it must be a sequence type convertible +//| to an array object. In this case, the return value is the number +//| of bytes written in the data payload. +//| +//| For device to host requests (IN), data_or_wLength is an array +//| object which the data will be read to, and the return value is the +//| number of bytes read. +//| """ +//| ... +//| +STATIC mp_obj_t usb_core_device_ctrl_transfer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_bmRequestType, ARG_bRequest, ARG_wValue, ARG_wIndex, ARG_data_or_wLength, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bmRequestType, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_bRequest, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_wValue, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_wIndex, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_data_or_wLength, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_timeout, MP_ARG_INT, {.u_int = 0} }, + }; + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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; + // check request type + if ((args[ARG_bmRequestType].u_int & 0x80) != 0) { + mp_get_buffer_raise(args[ARG_data_or_wLength].u_obj, &bufinfo, MP_BUFFER_WRITE); + } else { + mp_get_buffer_raise(args[ARG_data_or_wLength].u_obj, &bufinfo, MP_BUFFER_READ); + } + + mp_int_t result = common_hal_usb_core_device_ctrl_transfer(self, + args[ARG_bmRequestType].u_int, args[ARG_bRequest].u_int, + args[ARG_wValue].u_int, args[ARG_wIndex].u_int, + bufinfo.buf, bufinfo.len, args[ARG_timeout].u_int); + + return MP_OBJ_NEW_SMALL_INT(result); +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_core_device_ctrl_transfer_obj, 2, usb_core_device_ctrl_transfer); + +//| def is_kernel_driver_active(self, interface: int) -> bool: +//| """Determine if CircuitPython is using the interface. If it is, the +//| object will be unable to perform I/O. +//| +//| :param int interface: the device interface number to check +//| """ +//| ... +//| +STATIC mp_obj_t usb_core_device_is_kernel_driver_active(mp_obj_t self_in, mp_obj_t interface_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t interface = mp_obj_get_int(interface_in); + bool active = common_hal_usb_core_device_is_kernel_driver_active(self, interface); + return mp_obj_new_bool(active); +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_core_device_is_kernel_driver_active_obj, usb_core_device_is_kernel_driver_active); + +//| def detach_kernel_driver(self, interface: int): +//| """Stop CircuitPython from using the interface. If successful, you +//| will then be able to perform I/O. CircuitPython will automatically +//| re-start using the interface on reload. +//| +//| :param int interface: the device interface number to stop CircuitPython on +//| """ +//| ... +//| +STATIC mp_obj_t usb_core_device_detach_kernel_driver(mp_obj_t self_in, mp_obj_t interface_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t interface = mp_obj_get_int(interface_in); + common_hal_usb_core_device_detach_kernel_driver(self, interface); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_core_device_detach_kernel_driver_obj, usb_core_device_detach_kernel_driver); + +//| def attach_kernel_driver(self, interface: int): +//| """Allow CircuitPython to use the interface if it wants to. +//| +//| :param int interface: the device interface number to allow CircuitPython to use +//| """ +//| ... +//| +STATIC mp_obj_t usb_core_device_attach_kernel_driver(mp_obj_t self_in, mp_obj_t interface_in) { + usb_core_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t interface = mp_obj_get_int(interface_in); + common_hal_usb_core_device_attach_kernel_driver(self, interface); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_core_device_attach_kernel_driver_obj, usb_core_device_attach_kernel_driver); + + +STATIC const mp_rom_map_elem_t usb_core_device_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_idVendor), MP_ROM_PTR(&usb_core_device_idVendor_obj) }, + { MP_ROM_QSTR(MP_QSTR_idProduct), MP_ROM_PTR(&usb_core_device_idProduct_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial_number), MP_ROM_PTR(&usb_core_device_serial_number_obj) }, + { MP_ROM_QSTR(MP_QSTR_product), MP_ROM_PTR(&usb_core_device_product_obj) }, + { MP_ROM_QSTR(MP_QSTR_manufacturer), MP_ROM_PTR(&usb_core_device_manufacturer_obj) }, + + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&usb_core_device_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&usb_core_device_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_ctrl_transfer), MP_ROM_PTR(&usb_core_device_ctrl_transfer_obj) }, + + { MP_ROM_QSTR(MP_QSTR_is_kernel_driver_active), MP_ROM_PTR(&usb_core_device_is_kernel_driver_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_detach_kernel_driver), MP_ROM_PTR(&usb_core_device_detach_kernel_driver_obj) }, + { MP_ROM_QSTR(MP_QSTR_attach_kernel_driver), MP_ROM_PTR(&usb_core_device_attach_kernel_driver_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(usb_core_device_locals_dict, usb_core_device_locals_dict_table); + +const mp_obj_type_t usb_core_device_type = { + { &mp_type_type }, + .name = MP_QSTR_Device, + .locals_dict = (mp_obj_t)&usb_core_device_locals_dict, +}; diff --git a/circuitpython/shared-bindings/usb/core/Device.h b/circuitpython/shared-bindings/usb/core/Device.h new file mode 100644 index 0000000..c7086e9 --- /dev/null +++ b/circuitpython/shared-bindings/usb/core/Device.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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_USB_CORE_DEVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CORE_DEVICE_H + +#include "py/objarray.h" + +#include "shared-module/usb/core/Device.h" + +extern const mp_obj_type_t usb_core_device_type; + +bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_number); +uint16_t common_hal_usb_core_device_get_idVendor(usb_core_device_obj_t *self); +uint16_t common_hal_usb_core_device_get_idProduct(usb_core_device_obj_t *self); +mp_obj_t common_hal_usb_core_device_get_serial_number(usb_core_device_obj_t *self); +mp_obj_t common_hal_usb_core_device_get_product(usb_core_device_obj_t *self); +mp_obj_t common_hal_usb_core_device_get_manufacturer(usb_core_device_obj_t *self); +mp_obj_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t endpoint, const uint8_t *buffer, mp_int_t len, mp_int_t timeout); +mp_obj_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t endpoint, uint8_t *buffer, mp_int_t len, mp_int_t timeout); +mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, + mp_int_t bmRequestType, mp_int_t bRequest, + mp_int_t wValue, mp_int_t wIndex, + uint8_t *buffer, mp_int_t len, mp_int_t timeout); + +bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface); +void common_hal_usb_core_device_detach_kernel_driver(usb_core_device_obj_t *self, mp_int_t interface); +void common_hal_usb_core_device_attach_kernel_driver(usb_core_device_obj_t *self, mp_int_t interface); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CORE_DEVICE_H diff --git a/circuitpython/shared-bindings/usb/core/__init__.c b/circuitpython/shared-bindings/usb/core/__init__.c new file mode 100644 index 0000000..0e0d409 --- /dev/null +++ b/circuitpython/shared-bindings/usb/core/__init__.c @@ -0,0 +1,193 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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 <stdarg.h> +#include <string.h> + +#include "py/obj.h" +#include "py/objexcept.h" +#include "py/misc.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "shared-bindings/usb/core/__init__.h" +#include "shared-bindings/usb/core/Device.h" + +//| """USB Core +//| +//| This is a subset of the PyUSB core module. +//| """ +//| + +//| class USBError(OSError): +//| """Catchall exception for USB related errors.""" +//| ... +MP_DEFINE_USB_CORE_EXCEPTION(USBError, OSError) +NORETURN void mp_raise_usb_core_USBError(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_usb_core_USBError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +//| class USBTimeoutError(USBError): +//| """Raised when a USB transfer times out.""" +//| ... +//| +MP_DEFINE_USB_CORE_EXCEPTION(USBTimeoutError, usb_core_USBError) +NORETURN void mp_raise_usb_core_USBTimeoutError(void) { + mp_raise_type(&mp_type_usb_core_USBTimeoutError); +} + + +//| def find(find_all=False, *, idVendor=None, idProduct=None): +//| """Find the first device that matches the given requirements or, if +//| find_all is True, return a generator of all matching devices. +//| +//| Returns None if no device matches. +//| """ +//| +typedef struct { + mp_obj_base_t base; + mp_int_t vid; + mp_int_t pid; + mp_int_t next_index; +} usb_core_devices_obj_t; + +// This is an internal iterator type to use with find. +STATIC mp_obj_t _next_device(usb_core_devices_obj_t *iter) { + // Brute force check all possible device numbers for one that matches. + usb_core_device_obj_t temp_device; + for (size_t i = iter->next_index; i < 256; i++) { + if (!common_hal_usb_core_device_construct(&temp_device, i)) { + continue; + } + if (iter->vid < 0x10000 && iter->vid != common_hal_usb_core_device_get_idVendor(&temp_device)) { + continue; + } + if (iter->pid < 0x10000 && iter->pid != common_hal_usb_core_device_get_idProduct(&temp_device)) { + continue; + } + + // We passed the filters. Now make a properly allocated object to + // return to the user. + usb_core_device_obj_t *self = m_new_obj(usb_core_device_obj_t); + self->base.type = &usb_core_device_type; + + common_hal_usb_core_device_construct(self, i); + iter->next_index = i + 1; + return MP_OBJ_FROM_PTR(self); + } + // Iter is done. + iter->next_index = 256; + return mp_const_none; +} + +STATIC mp_obj_t usb_core_devices_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &usb_core_devices_type)); + usb_core_devices_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t device = _next_device(self); + if (device != mp_const_none) { + return device; + } + return MP_OBJ_STOP_ITERATION; +} + +const mp_obj_type_t usb_core_devices_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_USBDevices, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = usb_core_devices_iternext, + ), +}; + +STATIC mp_obj_t usb_core_find(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_find_all, ARG_idVendor, ARG_idProduct }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_find_all, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_idVendor, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_idProduct, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const bool find_all = args[ARG_find_all].u_bool; + usb_core_devices_obj_t temp_iter; + temp_iter.base.type = &usb_core_devices_type; + temp_iter.next_index = 1; + if (!mp_obj_get_int_maybe(args[ARG_idVendor].u_obj, &temp_iter.vid)) { + temp_iter.vid = 0x10000; + } + if (!mp_obj_get_int_maybe(args[ARG_idProduct].u_obj, &temp_iter.pid)) { + temp_iter.pid = 0x10000; + } + if (find_all) { + // Copy the temp iter contents to a heap object before we return it. + // We could do this up front but GCC falsely detects that we may return + // the stack copy. + usb_core_devices_obj_t *iter = m_new_obj(usb_core_devices_obj_t); + memcpy(iter, &temp_iter, sizeof(usb_core_devices_obj_t)); + return MP_OBJ_FROM_PTR(iter); + } + return _next_device(&temp_iter); +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_core_find_obj, 0, usb_core_find); + + +STATIC mp_rom_map_elem_t usb_core_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_dot_core) }, + // Functions + { MP_ROM_QSTR(MP_QSTR_find), MP_OBJ_FROM_PTR(&usb_core_find_obj) }, + + // Classes + { MP_ROM_QSTR(MP_QSTR_Device), MP_OBJ_FROM_PTR(&usb_core_device_type) }, + + // Errors + { MP_ROM_QSTR(MP_QSTR_USBError), MP_OBJ_FROM_PTR(&mp_type_usb_core_USBError) }, + { MP_ROM_QSTR(MP_QSTR_USBTimeoutError), MP_OBJ_FROM_PTR(&mp_type_usb_core_USBTimeoutError) }, +}; + +STATIC MP_DEFINE_CONST_DICT(usb_core_module_globals, usb_core_module_globals_table); + +void usb_core_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; + bool is_subclass = kind & PRINT_EXC_SUBCLASS; + if (!is_subclass && (k == PRINT_EXC)) { + mp_print_str(print, qstr_str(MP_OBJ_QSTR_VALUE(usb_core_module_globals_table[0].value))); + mp_print_str(print, "."); + } + mp_obj_exception_print(print, o_in, kind); +} + +const mp_obj_module_t usb_core_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_core_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_usb_dot_core, usb_core_module, CIRCUITPY_USB_HOST); diff --git a/circuitpython/shared-bindings/usb/core/__init__.h b/circuitpython/shared-bindings/usb/core/__init__.h new file mode 100644 index 0000000..2067c4c --- /dev/null +++ b/circuitpython/shared-bindings/usb/core/__init__.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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. + */ + +#pragma once + +#include <stdbool.h> + +#include "py/obj.h" + +extern const mp_obj_module_t usb_core_module; + +void usb_core_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); + +#define MP_DEFINE_USB_CORE_EXCEPTION(exc_name, base_name) \ + const mp_obj_type_t mp_type_usb_core_##exc_name = { \ + { &mp_type_type }, \ + .name = MP_QSTR_##exc_name, \ + .print = usb_core_exception_print, \ + .make_new = mp_obj_exception_make_new, \ + .attr = mp_obj_exception_attr, \ + .parent = &mp_type_##base_name, \ + }; + +extern const mp_obj_type_t mp_type_usb_core_USBError; +extern const mp_obj_type_t mp_type_usb_core_USBTimeoutError; + +NORETURN void mp_raise_usb_core_USBError(const compressed_string_t *fmt, ...); +NORETURN void mp_raise_usb_core_USBTimeoutError(void); + +// Find is all Python object oriented so we don't need a separate common-hal API +// for it. It uses the device common-hal instead. diff --git a/circuitpython/shared-bindings/usb_cdc/Serial.c b/circuitpython/shared-bindings/usb_cdc/Serial.c new file mode 100644 index 0000000..760efb6 --- /dev/null +++ b/circuitpython/shared-bindings/usb_cdc/Serial.c @@ -0,0 +1,303 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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/usb_cdc/Serial.h" +#include "shared-bindings/util.h" + +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "supervisor/shared/translate.h" + +//| class Serial: +//| """Receives cdc commands over USB""" +//| +//| def __init__(self) -> None: +//| """You cannot create an instance of `usb_cdc.Serial`. +//| The available instances are in the ``usb_cdc.serials`` tuple.""" +//| ... +//| +//| def read(self, size: int = 1) -> bytes: +//| """Read at most ``size`` bytes. If ``size`` exceeds the internal buffer size +//| only the bytes in the buffer will be read. If `timeout` is > 0 or ``None``, +//| and fewer than ``size`` bytes are available, keep waiting until the timeout +//| expires or ``size`` bytes are available. +//| +//| :return: Data read +//| :rtype: bytes""" +//| ... +//| +//| def readinto(self, buf: WriteableBuffer) -> int: +//| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most +//| that many bytes, subject to `timeout`. Otherwise, read at most ``len(buf)`` bytes. +//| +//| :return: number of bytes read and stored into ``buf`` +//| :rtype: bytes""" +//| ... +//| +//| def readline(self, size: int = -1) -> Optional[bytes]: +//| r"""Read a line ending in a newline character ("\\n"), including the newline. +//| Return everything readable if no newline is found and ``timeout`` is 0. +//| Return ``None`` in case of error. +//| +//| This is a binary stream: the newline character "\\n" cannot be changed. +//| If the host computer transmits "\\r" it will also be included as part of the line. +//| +//| :param int size: maximum number of characters to read. ``-1`` means as many as possible. +//| :return: the line read +//| :rtype: bytes or None""" +//| ... +//| +//| def readlines(self) -> List[Optional[bytes]]: +//| """Read multiple lines as a list, using `readline()`. +//| +//| .. warning:: If ``timeout`` is ``None``, +//| `readlines()` will never return, because there is no way to indicate end of stream. +//| +//| :return: a list of the line read +//| :rtype: list""" +//| ... +//| +//| def write(self, buf: ReadableBuffer) -> int: +//| """Write as many bytes as possible from the buffer of bytes. +//| +//| :return: the number of bytes written +//| :rtype: int""" +//| ... +//| +//| def flush(self) -> None: +//| """Force out any unwritten bytes, waiting until they are written.""" +//| ... +//| + +// These three methods are used by the shared stream methods. +STATIC mp_uint_t usb_cdc_serial_read_stream(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + return common_hal_usb_cdc_serial_read(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_cdc_serial_write_stream(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + const byte *buf = buf_in; + + return common_hal_usb_cdc_serial_write(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_cdc_serial_ioctl_stream(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret = 0; + switch (request) { + case MP_IOCTL_POLL: { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_cdc_serial_get_in_waiting(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } + if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_cdc_serial_get_out_waiting(self) == 0) { + ret |= MP_IOCTL_POLL_WR; + } + break; + } + + case MP_STREAM_FLUSH: + common_hal_usb_cdc_serial_flush(self); + break; + + default: + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +//| connected: bool +//| """True if this Serial is connected to a host. (read-only) +//| +//| .. note:: The host is considered to be connected if it is asserting DTR (Data Terminal Ready). +//| Most terminal programs and ``pyserial`` assert DTR when opening a serial connection. +//| However, the C# ``SerialPort`` API does not. You must set ``SerialPort.DtrEnable``. +//| """ +//| +STATIC mp_obj_t usb_cdc_serial_get_connected(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_usb_cdc_serial_get_connected(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_connected_obj, usb_cdc_serial_get_connected); + +MP_PROPERTY_GETTER(usb_cdc_serial_connected_obj, + (mp_obj_t)&usb_cdc_serial_get_connected_obj); + +//| in_waiting: int +//| """Returns the number of bytes waiting to be read on the USB serial input. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_in_waiting(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_usb_cdc_serial_get_in_waiting(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_in_waiting_obj, usb_cdc_serial_get_in_waiting); + +MP_PROPERTY_GETTER(usb_cdc_serial_in_waiting_obj, + (mp_obj_t)&usb_cdc_serial_get_in_waiting_obj); + +//| out_waiting: int +//| """Returns the number of bytes waiting to be written on the USB serial output. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_out_waiting(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_usb_cdc_serial_get_out_waiting(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_out_waiting_obj, usb_cdc_serial_get_out_waiting); + +MP_PROPERTY_GETTER(usb_cdc_serial_out_waiting_obj, + (mp_obj_t)&usb_cdc_serial_get_out_waiting_obj); + +//| def reset_input_buffer(self) -> None: +//| """Clears any unread bytes.""" +//| ... +//| +STATIC mp_obj_t usb_cdc_serial_reset_input_buffer(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_reset_input_buffer(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_input_buffer_obj, usb_cdc_serial_reset_input_buffer); + +//| def reset_output_buffer(self) -> None: +//| """Clears any unwritten bytes.""" +//| ... +//| +STATIC mp_obj_t usb_cdc_serial_reset_output_buffer(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_reset_output_buffer(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial_reset_output_buffer); + +//| timeout: Optional[float] +//| """The initial value of `timeout` is ``None``. If ``None``, wait indefinitely to satisfy +//| the conditions of a read operation. If 0, do not wait. If > 0, wait only ``timeout`` seconds.""" +//| +STATIC mp_obj_t usb_cdc_serial_get_timeout(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t timeout = common_hal_usb_cdc_serial_get_timeout(self); + return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->timeout); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_timeout_obj, usb_cdc_serial_get_timeout); + +STATIC mp_obj_t usb_cdc_serial_set_timeout(mp_obj_t self_in, mp_obj_t timeout_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_set_timeout(self, + timeout_in == mp_const_none ? -1.0f : mp_obj_get_float(timeout_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_cdc_serial_set_timeout_obj, usb_cdc_serial_set_timeout); + +MP_PROPERTY_GETSET(usb_cdc_serial_timeout_obj, + (mp_obj_t)&usb_cdc_serial_get_timeout_obj, + (mp_obj_t)&usb_cdc_serial_set_timeout_obj); + +//| write_timeout: Optional[float] +//| """The initial value of `write_timeout` is ``None``. If ``None``, wait indefinitely to finish +//| writing all the bytes passed to ``write()``.If 0, do not wait. +//| If > 0, wait only ``write_timeout`` seconds.""" +//| +STATIC mp_obj_t usb_cdc_serial_get_write_timeout(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t write_timeout = common_hal_usb_cdc_serial_get_write_timeout(self); + return (write_timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->write_timeout); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_write_timeout_obj, usb_cdc_serial_get_write_timeout); + +STATIC mp_obj_t usb_cdc_serial_set_write_timeout(mp_obj_t self_in, mp_obj_t write_timeout_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_set_write_timeout(self, + write_timeout_in == mp_const_none ? -1.0f : mp_obj_get_float(write_timeout_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_cdc_serial_set_write_timeout_obj, usb_cdc_serial_set_write_timeout); + +MP_PROPERTY_GETSET(usb_cdc_serial_write_timeout_obj, + (mp_obj_t)&usb_cdc_serial_get_write_timeout_obj, + (mp_obj_t)&usb_cdc_serial_set_write_timeout_obj); + + +STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { + // Standard stream methods. + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)}, + { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + + // Other pyserial-inspired attributes. + { MP_OBJ_NEW_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&usb_cdc_serial_in_waiting_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_out_waiting), MP_ROM_PTR(&usb_cdc_serial_out_waiting_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&usb_cdc_serial_timeout_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write_timeout), MP_ROM_PTR(&usb_cdc_serial_write_timeout_obj) }, + + // Not in pyserial protocol. + { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_connected_obj) }, + + + +}; +STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_dict_table); + +STATIC const mp_stream_p_t usb_cdc_serial_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = usb_cdc_serial_read_stream, + .write = usb_cdc_serial_write_stream, + .ioctl = usb_cdc_serial_ioctl_stream, + .is_text = false, + .pyserial_read_compatibility = true, + .pyserial_readinto_compatibility = true, + .pyserial_dont_return_none_compatibility = true, +}; + +const mp_obj_type_t usb_cdc_serial_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Serial, + .locals_dict = (mp_obj_dict_t *)&usb_cdc_serial_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &usb_cdc_serial_stream_p, + ), +}; diff --git a/circuitpython/shared-bindings/usb_cdc/Serial.h b/circuitpython/shared-bindings/usb_cdc/Serial.h new file mode 100644 index 0000000..cdf5c3a --- /dev/null +++ b/circuitpython/shared-bindings/usb_cdc/Serial.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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_USB_CDC_SERIAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H + +#include "shared-module/usb_cdc/Serial.h" + +extern const mp_obj_type_t usb_cdc_serial_type; + +extern size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode); +extern size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode); + +extern uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self); +extern uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self); + +extern void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self); +extern uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self); + +extern uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self); + +extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); + +extern mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout); + +extern mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/circuitpython/shared-bindings/usb_cdc/__init__.c b/circuitpython/shared-bindings/usb_cdc/__init__.c new file mode 100644 index 0000000..eabe26a --- /dev/null +++ b/circuitpython/shared-bindings/usb_cdc/__init__.c @@ -0,0 +1,143 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbertfor 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/usb_cdc/__init__.h" +#include "shared-bindings/usb_cdc/Serial.h" + +#include "py/runtime.h" + +//| """USB CDC Serial streams +//| +//| The `usb_cdc` module allows access to USB CDC (serial) communications. +//| +//| On Windows, each `Serial` is visible as a separate COM port. The ports will often +//| be assigned consecutively, `console` first, but this is not always true. +//| +//| On Linux, the ports are typically ``/dev/ttyACM0`` and ``/dev/ttyACM1``. +//| The `console` port will usually be first. +//| +//| On MacOS, the ports are typically ``/dev/cu.usbmodem<something>``. The something +//| varies based on the USB bus and port used. The `console` port will usually be first. +//| """ +//| +//| console: Optional[Serial] +//| """The `console` `Serial` object is used for the REPL, and for `sys.stdin` and `sys.stdout`. +//| `console` is ``None`` if disabled. +//| +//| However, note that `sys.stdin` and `sys.stdout` are text-based streams, +//| and the `console` object is a binary stream. +//| You do not normally need to write to `console` unless you want to write binary data. +//| """ +//| +//| data: Optional[Serial] +//| """A `Serial` object that can be used to send and receive binary data to and from +//| the host. +//| Note that `data` is *disabled* by default. ``data`` is ``None`` if disabled.""" + +//| def disable() -> None: +//| """Do not present any USB CDC device to the host. +//| Can be called in ``boot.py``, before USB is connected. +//| Equivalent to ``usb_cdc.enable(console=False, data=False)``.""" +//| ... +//| +STATIC mp_obj_t usb_cdc_disable(void) { + if (!common_hal_usb_cdc_disable()) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(usb_cdc_disable_obj, usb_cdc_disable); + +//| def enable(*, console: bool = True, data: bool = False) -> None: +//| """Enable or disable each CDC device. Can be called in ``boot.py``, before USB is connected. +//| +//| :param console bool: Enable or disable the `console` USB serial device. +//| True to enable; False to disable. Enabled by default. +//| :param data bool: Enable or disable the `data` USB serial device. +//| True to enable; False to disable. *Disabled* by default. +//| +//| If you enable too many devices at once, you will run out of USB endpoints. +//| The number of available endpoints varies by microcontroller. +//| CircuitPython will go into safe mode after running boot.py to inform you if +//| not enough endpoints are available. +//| """ +//| ... +//| +STATIC mp_obj_t usb_cdc_enable(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_console, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_console, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true } }, + { MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (!common_hal_usb_cdc_enable(args[ARG_console].u_bool, args[ARG_data].u_bool)) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_cdc_enable_obj, 0, usb_cdc_enable); + +// The usb_cdc module dict is mutable so that .console and .data may +// be set to a Serial or to None depending on whether they are enabled or not. +static mp_map_elem_t usb_cdc_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_cdc) }, + { MP_ROM_QSTR(MP_QSTR_Serial), MP_OBJ_FROM_PTR(&usb_cdc_serial_type) }, + { MP_ROM_QSTR(MP_QSTR_console), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_data), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_disable), MP_OBJ_FROM_PTR(&usb_cdc_disable_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable), MP_OBJ_FROM_PTR(&usb_cdc_enable_obj) }, +}; + +static MP_DEFINE_MUTABLE_DICT(usb_cdc_module_globals, usb_cdc_module_globals_table); + +const mp_obj_module_t usb_cdc_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_cdc_module_globals, +}; + +static void set_module_dict_entry(mp_obj_t key_qstr, mp_obj_t serial_obj) { + mp_map_elem_t *elem = mp_map_lookup(&usb_cdc_module_globals.map, key_qstr, MP_MAP_LOOKUP); + if (elem) { + elem->value = serial_obj; + } +} + +void usb_cdc_set_console(mp_obj_t serial_obj) { + set_module_dict_entry(MP_ROM_QSTR(MP_QSTR_console), serial_obj); +} + +void usb_cdc_set_data(mp_obj_t serial_obj) { + set_module_dict_entry(MP_ROM_QSTR(MP_QSTR_data), serial_obj); +} + +MP_REGISTER_MODULE(MP_QSTR_usb_cdc, usb_cdc_module, CIRCUITPY_USB_CDC); diff --git a/circuitpython/shared-bindings/usb_cdc/__init__.h b/circuitpython/shared-bindings/usb_cdc/__init__.h new file mode 100644 index 0000000..0a517f4 --- /dev/null +++ b/circuitpython/shared-bindings/usb_cdc/__init__.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert 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_USB_CDC___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H + +#include "shared-module/usb_cdc/__init__.h" + +// Set the module dict entries. +void usb_cdc_set_console(mp_obj_t serial_obj); +void usb_cdc_set_data(mp_obj_t serial_obj); + +extern bool common_hal_usb_cdc_disable(void); +extern bool common_hal_usb_cdc_enable(bool console, bool data); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H diff --git a/circuitpython/shared-bindings/usb_hid/Device.c b/circuitpython/shared-bindings/usb_hid/Device.c new file mode 100644 index 0000000..2741013 --- /dev/null +++ b/circuitpython/shared-bindings/usb_hid/Device.c @@ -0,0 +1,289 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "py/objproperty.h" +#include "shared-bindings/usb_hid/Device.h" +#include "py/runtime.h" + +//| class Device: +//| """HID Device specification""" +//| +//| def __init__(self, *, report_descriptor: ReadableBuffer, usage_page: int, usage: int, report_ids: Sequence[int], in_report_lengths: Sequence[int], out_report_lengths: Sequence[int]) -> None: +//| """Create a description of a USB HID device. The actual device is created when you +//| pass a `Device` to `usb_hid.enable()`. +//| +//| :param ReadableBuffer report_descriptor: The USB HID Report descriptor bytes. The descriptor is not +//| not verified for correctness; it is up to you to make sure it is not malformed. +//| :param int usage_page: The Usage Page value from the descriptor. Must match what is in the descriptor. +//| :param int usage: The Usage value from the descriptor. Must match what is in the descriptor. +//| :param Sequence[int] report_ids: Sequence of report ids used by the descriptor. +//| If the ``report_descriptor`` does not specify any report IDs, use ``(0,)``. +//| :param Sequence[int] in_report_lengths: Sequence of sizes in bytes of the HID reports sent to the host. +//| The sizes are in order of the ``report_ids``. +//| Use a size of ``0`` for a report that is not an IN report. +//| "IN" is with respect to the host. +//| :param int out_report_lengths: Sequence of sizes in bytes of the HID reports received from the host. +//| The sizes are in order of the ``report_ids``. +//| Use a size of ``0`` for a report that is not an OUT report. +//| "OUT" is with respect to the host. +//| +//| ``report_ids``, ``in_report_lengths``, and ``out_report_lengths`` must all have the +//| same number of elements. +//| +//| Here is an example of a `Device` with a descriptor that specifies two report IDs, 3 and 4. +//| Report ID 3 sends an IN report of length 5, and receives an OUT report of length 6. +//| Report ID 4 sends an IN report of length 2, and does not receive an OUT report:: +//| +//| device = usb_hid.Device( +//| descriptor=b"...", # Omitted for brevity. +//| report_ids=(3, 4), +//| in_report_lengths=(5, 2), +//| out_report_lengths=(6, 0), +//| ) +//| """ +//| ... +//| +//| KEYBOARD: Device +//| """Standard keyboard device supporting keycodes 0x00-0xDD, modifiers 0xE-0xE7, and five LED indicators. +//| Uses Report ID 1 for its IN and OUT reports. +//| """ +//| +//| MOUSE: Device +//| """Standard mouse device supporting five mouse buttons, X and Y relative movements from -127 to 127 +//| in each report, and a relative mouse wheel change from -127 to 127 in each report. +//| Uses Report ID 2 for its IN report. +//| """ +//| +//| CONSUMER_CONTROL: Device +//| """Consumer Control device supporting sent values from 1-652, with no rollover. +//| Uses Report ID 3 for its IN report.""" +//| + +STATIC mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + usb_hid_device_obj_t *self = m_new_obj(usb_hid_device_obj_t); + self->base.type = &usb_hid_device_type; + enum { ARG_report_descriptor, ARG_usage_page, ARG_usage, ARG_report_ids, ARG_in_report_lengths, ARG_out_report_lengths }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_report_descriptor, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_usage_page, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_usage, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_report_ids, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_in_report_lengths, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_out_report_lengths, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + }; + + 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); + + mp_buffer_info_t descriptor_bufinfo; + mp_get_buffer_raise(args[ARG_report_descriptor].u_obj, &descriptor_bufinfo, MP_BUFFER_READ); + mp_obj_t descriptor = mp_obj_new_bytearray(descriptor_bufinfo.len, descriptor_bufinfo.buf); + + const mp_int_t usage_page_arg = args[ARG_usage_page].u_int; + mp_arg_validate_int_range(usage_page_arg, 1, 0xFFFF, MP_QSTR_usage_page); + const uint16_t usage_page = usage_page_arg; + + const mp_int_t usage_arg = args[ARG_usage].u_int; + mp_arg_validate_int_range(usage_arg, 1, 0xFFFF, MP_QSTR_usage); + const uint16_t usage = usage_arg; + + mp_obj_t report_ids = args[ARG_report_ids].u_obj; + mp_obj_t in_report_lengths = args[ARG_in_report_lengths].u_obj; + mp_obj_t out_report_lengths = args[ARG_out_report_lengths].u_obj; + + size_t report_ids_count = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(report_ids)); + if (report_ids_count < 1) { + mp_raise_ValueError_varg(translate("%q length must be >= 1"), MP_QSTR_report_ids); + } + + if ((size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(in_report_lengths)) != report_ids_count || + (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(out_report_lengths)) != report_ids_count) { + mp_raise_ValueError_varg(translate("%q, %q, and %q must all be the same length"), + MP_QSTR_report_ids, MP_QSTR_in_report_lengths, MP_QSTR_out_report_lengths); + } + + uint8_t report_ids_array[report_ids_count]; + uint8_t in_report_lengths_array[report_ids_count]; + uint8_t out_report_lengths_array[report_ids_count]; + + // Validate the ids and lengths are all integers in range. + for (size_t i = 0; i < report_ids_count; i++) { + mp_obj_t i_obj = MP_OBJ_NEW_SMALL_INT(i); + + report_ids_array[i] = (uint8_t)mp_arg_validate_int_range( + // It's not the actual argument that's out of range, but its elements. + // But the error message is close enough. + MP_OBJ_SMALL_INT_VALUE(mp_obj_subscr(report_ids, i_obj, MP_OBJ_SENTINEL)), + 0, 255, MP_QSTR_report_ids); + + in_report_lengths_array[i] = (uint8_t)mp_arg_validate_int_range( + MP_OBJ_SMALL_INT_VALUE(mp_obj_subscr(in_report_lengths, i_obj, MP_OBJ_SENTINEL)), + 0, 255, MP_QSTR_in_report_lengths); + + out_report_lengths_array[i] = (uint8_t)mp_arg_validate_int_range( + MP_OBJ_SMALL_INT_VALUE(mp_obj_subscr(out_report_lengths, i_obj, MP_OBJ_SENTINEL)), + 0, 255, MP_QSTR_out_report_lengths); + } + + if (report_ids_array[0] == 0 && report_ids_count > 1) { + mp_raise_ValueError_varg(translate("%q with a report ID of 0 must be of length 1"), MP_QSTR_report_ids); + } + + common_hal_usb_hid_device_construct( + self, descriptor, usage_page, usage, report_ids_count, report_ids_array, in_report_lengths_array, out_report_lengths_array); + return (mp_obj_t)self; +} + + +//| def send_report(self, buf: ReadableBuffer, report_id: Optional[int] = None) -> None: +//| """Send an HID report. If the device descriptor specifies zero or one report id's, +//| you can supply `None` (the default) as the value of ``report_id``. +//| Otherwise you must specify which report id to use when sending the report. +//| """ +//| ... +//| +STATIC mp_obj_t usb_hid_device_send_report(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_buf, ARG_report_id }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_report_id, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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_buf].u_obj, &bufinfo, MP_BUFFER_READ); + + // -1 asks common_hal to determine the report id if possible. + mp_int_t report_id_arg = -1; + if (args[ARG_report_id].u_obj != mp_const_none) { + report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj); + } + const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, report_id_arg); + + common_hal_usb_hid_device_send_report(self, ((uint8_t *)bufinfo.buf), bufinfo.len, report_id); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_send_report_obj, 1, usb_hid_device_send_report); + +//| def get_last_received_report(self, report_id: Optional[int] = None) -> bytes: +//| """Get the last received HID OUT or feature report for the given report ID. +//| The report ID may be omitted if there is no report ID, or only one report ID. +//| Return `None` if nothing received. +//| """ +//| ... +//| +STATIC mp_obj_t usb_hid_device_get_last_received_report(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + enum { ARG_report_id }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_report_id, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + 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_int_t report_id_arg = -1; + if (args[ARG_report_id].u_obj != mp_const_none) { + report_id_arg = mp_obj_int_get_checked(args[ARG_report_id].u_obj); + } + const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, report_id_arg); + + return common_hal_usb_hid_device_get_last_received_report(self, report_id); +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_device_get_last_received_report_obj, 1, usb_hid_device_get_last_received_report); + +//| last_received_report: bytes +//| """The HID OUT report as a `bytes` (read-only). `None` if nothing received. +//| Same as `get_last_received_report()` with no argument. +//| +//| Deprecated: will be removed in CircutPython 8.0.0. Use `get_last_received_report()` instead. +//| """ +//| +STATIC mp_obj_t usb_hid_device_obj_get_last_received_report_property(mp_obj_t self_in) { + usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Get the sole report_id, if there is one. + const uint8_t report_id = common_hal_usb_hid_device_validate_report_id(self, -1); + return common_hal_usb_hid_device_get_last_received_report(self, report_id); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_device_get_last_received_report_property_obj, usb_hid_device_obj_get_last_received_report_property); + +MP_PROPERTY_GETTER(usb_hid_device_last_received_report_obj, + (mp_obj_t)&usb_hid_device_get_last_received_report_property_obj); + +//| usage_page: int +//| """The device usage page identifier, which designates a category of device. (read-only)""" +//| +STATIC mp_obj_t usb_hid_device_obj_get_usage_page(mp_obj_t self_in) { + usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_usb_hid_device_get_usage_page(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_device_get_usage_page_obj, usb_hid_device_obj_get_usage_page); + +MP_PROPERTY_GETTER(usb_hid_device_usage_page_obj, + (mp_obj_t)&usb_hid_device_get_usage_page_obj); + +//| usage: int +//| """The device usage identifier, which designates a specific kind of device. (read-only) +//| +//| For example, Keyboard is 0x06 within the generic desktop usage page 0x01. +//| Mouse is 0x02 within the same usage page.""" +//| +STATIC mp_obj_t usb_hid_device_obj_get_usage(mp_obj_t self_in) { + usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_usb_hid_device_get_usage(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_device_get_usage_obj, + usb_hid_device_obj_get_usage); + +MP_PROPERTY_GETTER(usb_hid_device_usage_obj, + (mp_obj_t)&usb_hid_device_get_usage_obj); + +STATIC const mp_rom_map_elem_t usb_hid_device_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_send_report), MP_ROM_PTR(&usb_hid_device_send_report_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_last_received_report), MP_ROM_PTR(&usb_hid_device_get_last_received_report_obj) }, + { MP_ROM_QSTR(MP_QSTR_last_received_report), MP_ROM_PTR(&usb_hid_device_last_received_report_obj) }, + { MP_ROM_QSTR(MP_QSTR_usage_page), MP_ROM_PTR(&usb_hid_device_usage_page_obj) }, + { MP_ROM_QSTR(MP_QSTR_usage), MP_ROM_PTR(&usb_hid_device_usage_obj) }, + + { MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&usb_hid_device_keyboard_obj) }, + { MP_ROM_QSTR(MP_QSTR_MOUSE), MP_ROM_PTR(&usb_hid_device_mouse_obj) }, + { MP_ROM_QSTR(MP_QSTR_CONSUMER_CONTROL), MP_ROM_PTR(&usb_hid_device_consumer_control_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(usb_hid_device_locals_dict, usb_hid_device_locals_dict_table); + +const mp_obj_type_t usb_hid_device_type = { + { &mp_type_type }, + .name = MP_QSTR_Device, + .make_new = usb_hid_device_make_new, + .locals_dict = (mp_obj_t)&usb_hid_device_locals_dict, +}; diff --git a/circuitpython/shared-bindings/usb_hid/Device.h b/circuitpython/shared-bindings/usb_hid/Device.h new file mode 100644 index 0000000..dde66e2 --- /dev/null +++ b/circuitpython/shared-bindings/usb_hid/Device.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_USB_HID_DEVICE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H + +#include "py/objarray.h" + +#include "shared-module/usb_hid/Device.h" + +extern const mp_obj_type_t usb_hid_device_type; + +void common_hal_usb_hid_device_construct(usb_hid_device_obj_t *self, mp_obj_t report_descriptor, uint16_t usage_page, uint16_t usage, size_t report_ids_count,uint8_t *report_ids, uint8_t *in_report_lengths, uint8_t *out_report_lengths); +void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t *report, uint8_t len, uint8_t report_id); +mp_obj_t common_hal_usb_hid_device_get_last_received_report(usb_hid_device_obj_t *self, uint8_t report_id); +uint16_t common_hal_usb_hid_device_get_usage_page(usb_hid_device_obj_t *self); +uint16_t common_hal_usb_hid_device_get_usage(usb_hid_device_obj_t *self); +uint8_t common_hal_usb_hid_device_validate_report_id(usb_hid_device_obj_t *self, mp_int_t report_id); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_HID_DEVICE_H diff --git a/circuitpython/shared-bindings/usb_hid/__init__.c b/circuitpython/shared-bindings/usb_hid/__init__.c new file mode 100644 index 0000000..3922ded --- /dev/null +++ b/circuitpython/shared-bindings/usb_hid/__init__.c @@ -0,0 +1,186 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "shared-bindings/usb_hid/__init__.h" +#include "shared-bindings/usb_hid/Device.h" + +//| """USB Human Interface Device +//| +//| The `usb_hid` module allows you to output data as a HID device.""" +//| + +//| devices: Tuple[Device, ...] +//| """Tuple of all active HID device interfaces. +//| The default set of devices is ``Device.KEYBOARD, Device.MOUSE, Device.CONSUMER_CONTROL``, +//| On boards where `usb_hid` is disabled by default, `devices` is an empty tuple. +//| +//| If a boot device is enabled by `usb_hid.enable()`, *and* the host has requested a boot device, +//| the `devices` tuple is **replaced** when ``code.py`` starts with a single-element tuple +//| containing a `Device` that describes the boot device chosen (keyboard or mouse). +//| The request for a boot device overrides any other HID devices. +//| """ +//| + +//| def disable() -> None: +//| """Do not present any USB HID devices to the host computer. +//| Can be called in ``boot.py``, before USB is connected. +//| The HID composite device is normally enabled by default, +//| but on some boards with limited endpoints, including STM32F4, +//| it is disabled by default. You must turn off another USB device such +//| as `usb_cdc` or `storage` to free up endpoints for use by `usb_hid`. +//| """ +//| +STATIC mp_obj_t usb_hid_disable(void) { + if (!common_hal_usb_hid_disable()) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(usb_hid_disable_obj, usb_hid_disable); + +//| def enable(devices: Optional[Sequence[Device]], boot_device: int = 0) -> None: +//| """Specify which USB HID devices that will be available. +//| Can be called in ``boot.py``, before USB is connected. +//| +//| :param Sequence devices: `Device` objects. +//| If `devices` is empty, HID is disabled. The order of the ``Devices`` +//| may matter to the host. For instance, for MacOS, put the mouse device +//| before any Gamepad or Digitizer HID device or else it will not work. +//| :param int boot_device: If non-zero, inform the host that support for a +//| a boot HID device is available. +//| If ``boot_device=1``, a boot keyboard is available. +//| If ``boot_device=2``, a boot mouse is available. No other values are allowed. +//| See below. +//| +//| If you enable too many devices at once, you will run out of USB endpoints. +//| The number of available endpoints varies by microcontroller. +//| CircuitPython will go into safe mode after running ``boot.py`` to inform you if +//| not enough endpoints are available. +//| +//| **Boot Devices** +//| +//| Boot devices implement a fixed, predefined report descriptor, defined in +//| https://www.usb.org/sites/default/files/hid1_12.pdf, Appendix B. A USB host +//| can request to use the boot device if the USB device says it is available. +//| Usually only a BIOS or other kind of limited-functionality +//| host needs boot keyboard support. +//| +//| For example, to make a boot keyboard available, you can use this code:: +//| +//| usb_hid.enable((Device.KEYBOARD), boot_device=1) # 1 for a keyboard +//| +//| If the host requests the boot keyboard, the report descriptor provided by `Device.KEYBOARD` +//| will be ignored, and the predefined report descriptor will be used. +//| But if the host does not request the boot keyboard, +//| the descriptor provided by `Device.KEYBOARD` will be used. +//| +//| The HID boot device must usually be the first or only device presented by CircuitPython. +//| The HID device will be USB interface number 0. +//| To make sure it is the first device, disable other USB devices, including CDC and MSC (CIRCUITPY). +//| If you specify a non-zero ``boot_device``, and it is not the first device, CircuitPython +//| will enter safe mode to report this error. +//| """ +//| ... +//| +STATIC mp_obj_t usb_hid_enable(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_devices, ARG_boot_device }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_devices, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_boot_device, MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t devices = args[ARG_devices].u_obj; + const mp_int_t len = mp_obj_get_int(mp_obj_len(devices)); + for (mp_int_t i = 0; i < len; i++) { + mp_obj_t item = mp_obj_subscr(devices, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL); + if (!mp_obj_is_type(item, &usb_hid_device_type)) { + mp_raise_ValueError_varg(translate("non-Device in %q"), MP_QSTR_devices); + } + } + + uint8_t boot_device = + (uint8_t)mp_arg_validate_int_range(args[ARG_boot_device].u_int, 0, 2, MP_QSTR_boot_device); + + if (!common_hal_usb_hid_enable(devices, boot_device)) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(usb_hid_enable_obj, 1, usb_hid_enable); + +//| def get_boot_device() -> int: +//| """ +//| :return: the boot device requested by the host, if any. +//| Returns 0 if the host did not request a boot device, or if `usb_hid.enable()` +//| was called with ``boot_device=0``, the default, which disables boot device support. +//| If the host did request a boot device, +//| returns the value of ``boot_device`` set in `usb_hid.enable()`: +//| ``1`` for a boot keyboard, or ``2`` for boot mouse. +//| However, the standard devices provided by CircuitPython, `Device.KEYBOARD` and `Device.MOUSE`, +//| describe reports that match the boot device reports, so you don't need to check this +//| if you are using those devices. +//| :rtype int: +//| """ +//| +STATIC mp_obj_t usb_hid_get_boot_device(void) { + return MP_OBJ_NEW_SMALL_INT(common_hal_usb_hid_get_boot_device()); +} +MP_DEFINE_CONST_FUN_OBJ_0(usb_hid_get_boot_device_obj, usb_hid_get_boot_device); + +// usb_hid.devices is set once the usb devices are determined, after boot.py runs. +STATIC mp_map_elem_t usb_hid_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid) }, + { MP_ROM_QSTR(MP_QSTR_Device), MP_OBJ_FROM_PTR(&usb_hid_device_type) }, + { MP_ROM_QSTR(MP_QSTR_devices), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_disable), MP_OBJ_FROM_PTR(&usb_hid_disable_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable), MP_OBJ_FROM_PTR(&usb_hid_enable_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_boot_device), MP_OBJ_FROM_PTR(&usb_hid_get_boot_device_obj) }, +}; + +STATIC MP_DEFINE_MUTABLE_DICT(usb_hid_module_globals, usb_hid_module_globals_table); + +const mp_obj_module_t usb_hid_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_hid_module_globals, +}; + +void usb_hid_set_devices(mp_obj_t devices) { + mp_map_elem_t *elem = + mp_map_lookup(&usb_hid_module_globals.map, MP_ROM_QSTR(MP_QSTR_devices), MP_MAP_LOOKUP); + if (elem) { + elem->value = devices; + } +} + +MP_REGISTER_MODULE(MP_QSTR_usb_hid, usb_hid_module, CIRCUITPY_USB_HID); diff --git a/circuitpython/shared-bindings/usb_hid/__init__.h b/circuitpython/shared-bindings/usb_hid/__init__.h new file mode 100644 index 0000000..feaeed2 --- /dev/null +++ b/circuitpython/shared-bindings/usb_hid/__init__.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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 SHARED_BINDINGS_USB_HID_H +#define SHARED_BINDINGS_USB_HID_H + +#include "py/obj.h" +#include "py/objtuple.h" +#include "shared-module/usb_hid/__init__.h" + +extern mp_obj_tuple_t common_hal_usb_hid_devices; + +void usb_hid_set_devices(mp_obj_t devices); + +bool common_hal_usb_hid_disable(void); +bool common_hal_usb_hid_enable(const mp_obj_t devices_seq, uint8_t boot_device); +uint8_t common_hal_usb_hid_get_boot_device(void); + +#endif // SHARED_BINDINGS_USB_HID_H diff --git a/circuitpython/shared-bindings/usb_host/Port.c b/circuitpython/shared-bindings/usb_host/Port.c new file mode 100644 index 0000000..8f54246 --- /dev/null +++ b/circuitpython/shared-bindings/usb_host/Port.c @@ -0,0 +1,103 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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 "shared/runtime/context_manager_helpers.h" +#include "shared-bindings/usb_host/Port.h" +#include "shared-bindings/util.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +//| class Port: +//| """USB host port. Also known as a root hub port.""" +//| +//| def __init__(self, dp: microcontroller.Pin, dm: microcontroller.Pin) -> None: +//| """Create a USB host port on the given pins. Access attached devices +//| through the `usb` module. Keep this object referenced while +//| interacting with devices, otherwise they will be disconnected. +//| +//| :param ~microcontroller.Pin dp: The data plus pin +//| :param ~microcontroller.Pin dm: The data minus pin +//| """ +//| ... +//| +STATIC mp_obj_t usb_host_port_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + // check number of arguments + mp_arg_check_num(n_args, n_kw, 2, 2, false); + + const mcu_pin_obj_t *dp = validate_obj_is_free_pin(args[0]); + const mcu_pin_obj_t *dm = validate_obj_is_free_pin(args[1]); + + usb_host_port_obj_t *self = m_new_obj(usb_host_port_obj_t); + self->base.type = &usb_host_port_type; + common_hal_usb_host_port_construct(self, dp, dm); + + return (mp_obj_t)self; +} + +//| def deinit(self) -> None: +//| """Turn off the USB host port and release the pins for other use.""" +//| ... +//| +STATIC mp_obj_t usb_host_port_obj_deinit(mp_obj_t self_in) { + usb_host_port_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_host_port_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_host_port_deinit_obj, usb_host_port_obj_deinit); + +//| def __enter__(self) -> Port: +//| """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 usb_host_port_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_usb_host_port_deinit(MP_OBJ_TO_PTR(args[0])); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_host_port_obj___exit___obj, 4, 4, usb_host_port_obj___exit__); + +STATIC const mp_rom_map_elem_t usb_host_port_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&usb_host_port_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&usb_host_port_obj___exit___obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(usb_host_port_locals_dict, usb_host_port_locals_dict_table); + +const mp_obj_type_t usb_host_port_type = { + { &mp_type_type }, + .name = MP_QSTR_Port, + .make_new = usb_host_port_make_new, + .locals_dict = (mp_obj_t)&usb_host_port_locals_dict, +}; diff --git a/circuitpython/shared-bindings/usb_host/Port.h b/circuitpython/shared-bindings/usb_host/Port.h new file mode 100644 index 0000000..68645d1 --- /dev/null +++ b/circuitpython/shared-bindings/usb_host/Port.h @@ -0,0 +1,42 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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_USB_HOST_PORT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_HOST_PORT_H + +#include "py/objarray.h" + +#include "shared-bindings/microcontroller/Pin.h" + +#include "common-hal/usb_host/Port.h" + +extern const mp_obj_type_t usb_host_port_type; + +void common_hal_usb_host_port_construct(usb_host_port_obj_t *self, const mcu_pin_obj_t *dp, const mcu_pin_obj_t *dm); +void common_hal_usb_host_port_deinit(usb_host_port_obj_t *self); +bool common_hal_usb_host_port_deinited(usb_host_port_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_HOST_PORT_H diff --git a/circuitpython/shared-bindings/usb_host/__init__.c b/circuitpython/shared-bindings/usb_host/__init__.c new file mode 100644 index 0000000..c689a25 --- /dev/null +++ b/circuitpython/shared-bindings/usb_host/__init__.c @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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 "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "shared-bindings/usb_host/__init__.h" +#include "shared-bindings/usb_host/Port.h" + +//| """USB Host +//| +//| The `usb_host` module allows you to manage USB host ports. To communicate +//| with devices use the `usb` module that is a subset of PyUSB's API. +//| """ +//| + +STATIC mp_map_elem_t usb_host_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usb_host) }, + { MP_ROM_QSTR(MP_QSTR_Port), MP_OBJ_FROM_PTR(&usb_host_port_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(usb_host_module_globals, usb_host_module_globals_table); + +const mp_obj_module_t usb_host_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_host_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_usb_host, usb_host_module, CIRCUITPY_USB_HOST); diff --git a/circuitpython/shared-bindings/usb_host/__init__.h b/circuitpython/shared-bindings/usb_host/__init__.h new file mode 100644 index 0000000..d672285 --- /dev/null +++ b/circuitpython/shared-bindings/usb_host/__init__.h @@ -0,0 +1,27 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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. + */ + +#pragma once diff --git a/circuitpython/shared-bindings/usb_midi/PortIn.c b/circuitpython/shared-bindings/usb_midi/PortIn.c new file mode 100644 index 0000000..0056a08 --- /dev/null +++ b/circuitpython/shared-bindings/usb_midi/PortIn.c @@ -0,0 +1,124 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/usb_midi/PortIn.h" +#include "shared-bindings/util.h" + +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "supervisor/shared/translate.h" + +//| class PortIn: +//| """Receives midi commands over USB""" +//| +//| def __init__(self) -> None: +//| """You cannot create an instance of `usb_midi.PortIn`. +//| +//| PortIn objects are constructed for every corresponding entry in the USB +//| descriptor and added to the ``usb_midi.ports`` tuple.""" +//| ... +//| + +// 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, nbytes: Optional[int] = None) -> Optional[bytes]: +//| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most +//| that many bytes. Otherwise, read at most ``len(buf)`` bytes. +//| +//| :return: number of bytes read and stored into ``buf`` +//| :rtype: bytes or None""" +//| ... +//| + +// These three methods are used by the shared stream methods. +STATIC mp_uint_t usb_midi_portin_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + usb_midi_portin_obj_t *self = MP_OBJ_TO_PTR(self_in); + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + return common_hal_usb_midi_portin_read(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_midi_portin_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + usb_midi_portin_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_midi_portin_bytes_available(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_rom_map_elem_t usb_midi_portin_locals_dict_table[] = { + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(usb_midi_portin_locals_dict, usb_midi_portin_locals_dict_table); + +STATIC const mp_stream_p_t usb_midi_portin_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = usb_midi_portin_read, + .write = NULL, + .ioctl = usb_midi_portin_ioctl, + .is_text = false, +}; + +const mp_obj_type_t usb_midi_portin_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_PortIn, + .locals_dict = (mp_obj_dict_t *)&usb_midi_portin_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &usb_midi_portin_stream_p, + ), +}; diff --git a/circuitpython/shared-bindings/usb_midi/PortIn.h b/circuitpython/shared-bindings/usb_midi/PortIn.h new file mode 100644 index 0000000..58d017f --- /dev/null +++ b/circuitpython/shared-bindings/usb_midi/PortIn.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_USB_MIDI_PORTIN_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_MIDI_PORTIN_H + +#include "shared-module/usb_midi/PortIn.h" + +extern const mp_obj_type_t usb_midi_portin_type; + +// Read characters. +extern size_t common_hal_usb_midi_portin_read(usb_midi_portin_obj_t *self, + uint8_t *data, size_t len, int *errcode); + +extern uint32_t common_hal_usb_midi_portin_bytes_available(usb_midi_portin_obj_t *self); +extern void common_hal_usb_midi_portin_clear_buffer(usb_midi_portin_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_MIDI_PORTIN_H diff --git a/circuitpython/shared-bindings/usb_midi/PortOut.c b/circuitpython/shared-bindings/usb_midi/PortOut.c new file mode 100644 index 0000000..c468401 --- /dev/null +++ b/circuitpython/shared-bindings/usb_midi/PortOut.c @@ -0,0 +1,105 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/usb_midi/PortOut.h" +#include "shared-bindings/util.h" + +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "supervisor/shared/translate.h" + +//| class PortOut: +//| """Sends midi messages to a computer over USB""" +//| +//| def __init__(self) -> None: +//| """You cannot create an instance of `usb_midi.PortOut`. +//| +//| PortOut objects are constructed for every corresponding entry in the USB +//| descriptor and added to the ``usb_midi.ports`` tuple.""" +//| + +// These are standard stream methods. Code is in py/stream.c. +// +//| def write(self, buf: ReadableBuffer) -> Optional[int]: +//| """Write the buffer of bytes to the bus. +//| +//| :return: the number of bytes written +//| :rtype: int or None""" +//| ... +//| + +STATIC mp_uint_t usb_midi_portout_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + usb_midi_portout_obj_t *self = MP_OBJ_TO_PTR(self_in); + const byte *buf = buf_in; + + return common_hal_usb_midi_portout_write(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_midi_portout_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + usb_midi_portout_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_midi_portout_ready_to_tx(self)) { + ret |= MP_IOCTL_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_rom_map_elem_t usb_midi_portout_locals_dict_table[] = { + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(usb_midi_portout_locals_dict, usb_midi_portout_locals_dict_table); + +STATIC const mp_stream_p_t usb_midi_portout_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = NULL, + .write = usb_midi_portout_write, + .ioctl = usb_midi_portout_ioctl, + .is_text = false, +}; + +const mp_obj_type_t usb_midi_portout_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_PortOut, + .locals_dict = (mp_obj_dict_t *)&usb_midi_portout_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &usb_midi_portout_stream_p, + ), +}; diff --git a/circuitpython/shared-bindings/usb_midi/PortOut.h b/circuitpython/shared-bindings/usb_midi/PortOut.h new file mode 100644 index 0000000..7d8a014 --- /dev/null +++ b/circuitpython/shared-bindings/usb_midi/PortOut.h @@ -0,0 +1,40 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_USB_MIDI_PORTOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_MIDI_PORTOUT_H + +#include "shared-module/usb_midi/PortOut.h" + +extern const mp_obj_type_t usb_midi_portout_type; + +// Write characters. len is in characters NOT bytes! +extern size_t common_hal_usb_midi_portout_write(usb_midi_portout_obj_t *self, + const uint8_t *data, size_t len, int *errcode); + +extern bool common_hal_usb_midi_portout_ready_to_tx(usb_midi_portout_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_MIDI_PORTOUT_H diff --git a/circuitpython/shared-bindings/usb_midi/__init__.c b/circuitpython/shared-bindings/usb_midi/__init__.c new file mode 100644 index 0000000..ec065d1 --- /dev/null +++ b/circuitpython/shared-bindings/usb_midi/__init__.c @@ -0,0 +1,98 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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/usb_midi/__init__.h" +#include "shared-bindings/usb_midi/PortIn.h" +#include "shared-bindings/usb_midi/PortOut.h" + +#include "py/runtime.h" + +//| """MIDI over USB +//| +//| The `usb_midi` module contains classes to transmit and receive MIDI messages over USB.""" +//| +//| ports: Tuple[Union[PortIn, PortOut], ...] +//| """Tuple of all MIDI ports. Each item is ether `PortIn` or `PortOut`.""" +//| + +//| def disable() -> None: +//| """Disable presenting a USB MIDI device to the host. +//| The device is normally enabled by default, but on some boards with limited endpoints +//| including ESP32-S2 and certain STM boards, it is disabled by default. +//| Can be called in ``boot.py``, before USB is connected.""" +//| ... +//| +STATIC mp_obj_t usb_midi_disable(void) { + if (!common_hal_usb_midi_disable()) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(usb_midi_disable_obj, usb_midi_disable); + +//| def enable() -> None: +//| """Enable presenting a USB MIDI device to the host. +//| The device is enabled by default, so you do not normally need to call this function. +//| Can be called in ``boot.py``, before USB is connected. +//| +//| If you enable too many devices at once, you will run out of USB endpoints. +//| The number of available endpoints varies by microcontroller. +//| CircuitPython will go into safe mode after running boot.py to inform you if +//| not enough endpoints are available. +//| """ +//| ... +//| +STATIC mp_obj_t usb_midi_enable(void) { + if (!common_hal_usb_midi_enable()) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(usb_midi_enable_obj, usb_midi_enable); + +mp_map_elem_t usb_midi_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_midi) }, + { MP_ROM_QSTR(MP_QSTR_disable), MP_OBJ_FROM_PTR(&usb_midi_disable_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable), MP_OBJ_FROM_PTR(&usb_midi_enable_obj) }, + { MP_ROM_QSTR(MP_QSTR_ports), mp_const_empty_tuple }, + { MP_ROM_QSTR(MP_QSTR_PortIn), MP_OBJ_FROM_PTR(&usb_midi_portin_type) }, + { MP_ROM_QSTR(MP_QSTR_PortOut), MP_OBJ_FROM_PTR(&usb_midi_portout_type) }, +}; + +// This isn't const so we can set ports dynamically. +MP_DEFINE_MUTABLE_DICT(usb_midi_module_globals, usb_midi_module_globals_table); + +const mp_obj_module_t usb_midi_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&usb_midi_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_usb_midi, usb_midi_module, CIRCUITPY_USB_MIDI); diff --git a/circuitpython/shared-bindings/usb_midi/__init__.h b/circuitpython/shared-bindings/usb_midi/__init__.h new file mode 100644 index 0000000..b657cfe --- /dev/null +++ b/circuitpython/shared-bindings/usb_midi/__init__.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 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_USB_MIDI___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_MIDI___INIT___H + +#include "py/obj.h" +#include "shared-module/usb_midi/__init__.h" + +extern mp_obj_dict_t usb_midi_module_globals; + +bool common_hal_usb_midi_disable(void); +bool common_hal_usb_midi_enable(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_MIDI___INIT___H diff --git a/circuitpython/shared-bindings/ustack/__init__.c b/circuitpython/shared-bindings/ustack/__init__.c new file mode 100644 index 0000000..17bdcbb --- /dev/null +++ b/circuitpython/shared-bindings/ustack/__init__.c @@ -0,0 +1,89 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert 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/ustack/__init__.h" + + + + +//| """Stack information and analysis""" +//| + +#if MICROPY_MAX_STACK_USAGE +//| def max_stack_usage() -> int: +//| """Return the maximum excursion of the stack so far.""" +//| ... +//| +STATIC mp_obj_t max_stack_usage(void) { + return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_max_stack_usage()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(max_stack_usage_obj, max_stack_usage); + +#endif // MICROPY_MAX_STACK_USAGE + +//| def stack_size() -> int: +//| """Return the size of the entire stack. +//| Same as in micropython.mem_info(), but returns a value instead +//| of just printing it.""" +//| ... +//| +STATIC mp_obj_t stack_size(void) { + return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_stack_size()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(stack_size_obj, stack_size); + +//| def stack_usage() -> int: +//| """Return how much stack is currently in use. +//| Same as micropython.stack_use(); duplicated here for convenience.""" +//| ... +//| +STATIC mp_obj_t stack_usage(void) { + return MP_OBJ_NEW_SMALL_INT(shared_module_ustack_stack_usage()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(stack_usage_obj, stack_usage); + +STATIC const mp_rom_map_elem_t ustack_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ustack) }, + #if MICROPY_MAX_STACK_USAGE + { MP_ROM_QSTR(MP_QSTR_max_stack_usage), MP_ROM_PTR(&max_stack_usage_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&stack_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_stack_usage), MP_ROM_PTR(&stack_usage_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(ustack_module_globals, ustack_module_globals_table); + +const mp_obj_module_t ustack_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&ustack_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ustack, ustack_module, CIRCUITPY_USTACK); diff --git a/circuitpython/shared-bindings/ustack/__init__.h b/circuitpython/shared-bindings/ustack/__init__.h new file mode 100644 index 0000000..561d581 --- /dev/null +++ b/circuitpython/shared-bindings/ustack/__init__.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert 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_USTACK___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H + +#include "py/obj.h" + +#if MICROPY_MAX_STACK_USAGE +extern uint32_t shared_module_ustack_max_stack_usage(void); +#endif +extern uint32_t shared_module_ustack_stack_size(void); +extern uint32_t shared_module_ustack_stack_usage(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USTACK___INIT___H diff --git a/circuitpython/shared-bindings/util.c b/circuitpython/shared-bindings/util.c new file mode 100644 index 0000000..c1ca01e --- /dev/null +++ b/circuitpython/shared-bindings/util.c @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert 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_UTIL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_UTIL_H + +#include "py/runtime.h" + +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +// If so, deinit() has already been called on the object, so complain. +void raise_deinited_error(void) { + mp_raise_ValueError(translate("Object has been deinitialized and can no longer be used. Create a new object.")); +} + + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_UTIL_H diff --git a/circuitpython/shared-bindings/util.h b/circuitpython/shared-bindings/util.h new file mode 100644 index 0000000..33454f1 --- /dev/null +++ b/circuitpython/shared-bindings/util.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Dan Halbert 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_ATMEL_SAMD_COMMON_HAL_UTIL_H +#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H + +void raise_deinited_error(void); + + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H diff --git a/circuitpython/shared-bindings/vectorio/Circle.c b/circuitpython/shared-bindings/vectorio/Circle.c new file mode 100644 index 0000000..ded1861 --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/Circle.c @@ -0,0 +1,141 @@ +#include "shared-bindings/vectorio/__init__.h" +#include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/VectorShape.h" + + +#include <stdint.h> + +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +//| class Circle: +//| +//| def __init__(self, pixel_shader: Union[displayio.ColorConverter, displayio.Palette], radius: int, x: int, y: int) -> None: +//| """Circle is positioned on screen by its center point. +//| +//| :param Union[~displayio.ColorConverter,~displayio.Palette] pixel_shader: The pixel shader that produces colors from values +//| :param int radius: The radius of the circle in pixels +//| :param int x: Initial x position of the axis. +//| :param int y: Initial y position of the axis. +//| :param int color_index: Initial color_index to use when selecting color from the palette.""" +//| +static mp_obj_t vectorio_circle_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pixel_shader, ARG_radius, ARG_x, ARG_y, ARG_color_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_color_index, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + 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); + + mp_int_t radius = args[ARG_radius].u_int; + if (radius < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_radius); + } + + vectorio_circle_t *self = m_new_obj(vectorio_circle_t); + self->base.type = &vectorio_circle_type; + uint16_t color_index = args[ARG_color_index].u_int; + common_hal_vectorio_circle_construct(self, radius, color_index); + + // VectorShape parts + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + int32_t x = args[ARG_x].u_int; + int32_t y = args[ARG_y].u_int; + mp_obj_t vector_shape = vectorio_vector_shape_make_new(self, pixel_shader, x, y); + self->draw_protocol_instance = vector_shape; + + return MP_OBJ_FROM_PTR(self); +} + +STATIC const vectorio_draw_protocol_t circle_draw_protocol = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_draw) + .draw_get_protocol_self = (draw_get_protocol_self_fun)common_hal_vectorio_circle_get_draw_protocol, + .draw_protocol_impl = &vectorio_vector_shape_draw_protocol_impl +}; + + +//| radius : int +//| """The radius of the circle in pixels.""" +//| +STATIC mp_obj_t vectorio_circle_obj_get_radius(mp_obj_t self_in) { + vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_vectorio_circle_get_radius(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_circle_get_radius_obj, vectorio_circle_obj_get_radius); + +STATIC mp_obj_t vectorio_circle_obj_set_radius(mp_obj_t self_in, mp_obj_t radius) { + vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_vectorio_circle_set_radius(self, mp_obj_get_int(radius)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_circle_set_radius_obj, vectorio_circle_obj_set_radius); + +MP_PROPERTY_GETSET(vectorio_circle_radius_obj, + (mp_obj_t)&vectorio_circle_get_radius_obj, + (mp_obj_t)&vectorio_circle_set_radius_obj); + +//| color_index : int +//| """The color_index of the circle as 0 based index of the palette.""" +//| +STATIC mp_obj_t vectorio_circle_obj_get_color_index(mp_obj_t self_in) { + vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_vectorio_circle_get_color_index(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_circle_get_color_index_obj, vectorio_circle_obj_get_color_index); + +STATIC mp_obj_t vectorio_circle_obj_set_color_index(mp_obj_t self_in, mp_obj_t color_index) { + vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_vectorio_circle_set_color_index(self, mp_obj_get_int(color_index)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_circle_set_color_index_obj, vectorio_circle_obj_set_color_index); + +MP_PROPERTY_GETSET(vectorio_circle_color_index_obj, + (mp_obj_t)&vectorio_circle_get_color_index_obj, + (mp_obj_t)&vectorio_circle_set_color_index_obj); + + +// Documentation for properties inherited from VectorShape. + +//| x : int +//| """X position of the center point of the circle in the parent.""" +//| +//| y : int +//| """Y position of the center point of the circle in the parent.""" +//| +//| location : Tuple[int,int] +//| """(X,Y) position of the center point of the circle in the parent.""" +//| +//| pixel_shader : Union[displayio.ColorConverter,displayio.Palette] +//| """The pixel shader of the circle.""" +//| + +STATIC const mp_rom_map_elem_t vectorio_circle_locals_dict_table[] = { + // Functions + { MP_ROM_QSTR(MP_QSTR_contains), MP_ROM_PTR(&vectorio_vector_shape_contains_obj) }, + // Properties + { MP_ROM_QSTR(MP_QSTR_radius), MP_ROM_PTR(&vectorio_circle_radius_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_color_index), MP_ROM_PTR(&vectorio_circle_color_index_obj) }, + { MP_ROM_QSTR(MP_QSTR_location), MP_ROM_PTR(&vectorio_vector_shape_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_circle_locals_dict, vectorio_circle_locals_dict_table); + +const mp_obj_type_t vectorio_circle_type = { + { &mp_type_type }, + .name = MP_QSTR_Circle, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = vectorio_circle_make_new, + .locals_dict = (mp_obj_dict_t *)&vectorio_circle_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &circle_draw_protocol, + ), +}; diff --git a/circuitpython/shared-bindings/vectorio/Circle.h b/circuitpython/shared-bindings/vectorio/Circle.h new file mode 100644 index 0000000..8f16979 --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/Circle.h @@ -0,0 +1,27 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H + +#include "shared-module/vectorio/__init__.h" +#include "shared-module/vectorio/Circle.h" +#include "shared-module/displayio/area.h" + +extern const mp_obj_type_t vectorio_circle_type; + +void common_hal_vectorio_circle_construct(vectorio_circle_t *self, uint16_t radius, uint16_t color_index); + +void common_hal_vectorio_circle_set_on_dirty(vectorio_circle_t *self, vectorio_event_t notification); + +uint32_t common_hal_vectorio_circle_get_pixel(void *circle, int16_t x, int16_t y); + +void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_area); + + +int16_t common_hal_vectorio_circle_get_radius(void *circle); +void common_hal_vectorio_circle_set_radius(void *circle, int16_t radius); + +uint16_t common_hal_vectorio_circle_get_color_index(void *obj); +void common_hal_vectorio_circle_set_color_index(void *obj, uint16_t color_index); + +mp_obj_t common_hal_vectorio_circle_get_draw_protocol(void *circle); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H diff --git a/circuitpython/shared-bindings/vectorio/Polygon.c b/circuitpython/shared-bindings/vectorio/Polygon.c new file mode 100644 index 0000000..ae9d7a0 --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/Polygon.c @@ -0,0 +1,147 @@ +#include "shared-bindings/vectorio/__init__.h" +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/VectorShape.h" + +#include <stdint.h> + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +#define VECTORIO_POLYGON_DEBUG(...) (void)0 +// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +//| class Polygon: +//| def __init__(self, pixel_shader: Union[displayio.ColorConverter, displayio.Palette], points: List[Tuple[int, int]], x: int, y: int) -> None: +//| """Represents a closed shape by ordered vertices. The path will be treated as +//| 'closed', the last point will connect to the first point. +//| +//| :param Union[~displayio.ColorConverter,~displayio.Palette] pixel_shader: The pixel +//| shader that produces colors from values +//| :param List[Tuple[int,int]] points: Vertices for the polygon +//| :param int x: Initial screen x position of the 0,0 origin in the points list. +//| :param int y: Initial screen y position of the 0,0 origin in the points list. +//| :param int color_index: Initial color_index to use when selecting color from the palette.""" +//| +static mp_obj_t vectorio_polygon_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pixel_shader, ARG_points_list, ARG_x, ARG_y, ARG_color_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_points, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_color_index, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + 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); + + mp_obj_t points_list = mp_arg_validate_type(args[ARG_points_list].u_obj, &mp_type_list, MP_QSTR_points); + + vectorio_polygon_t *self = m_new_obj(vectorio_polygon_t); + self->base.type = &vectorio_polygon_type; + + uint16_t color_index = args[ARG_color_index].u_int; + common_hal_vectorio_polygon_construct(self, points_list, color_index); + + // VectorShape parts + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + int32_t x = args[ARG_x].u_int; + int32_t y = args[ARG_y].u_int; + mp_obj_t vector_shape = vectorio_vector_shape_make_new(self, pixel_shader, x, y); + self->draw_protocol_instance = vector_shape; + + return MP_OBJ_FROM_PTR(self); +} + +STATIC const vectorio_draw_protocol_t polygon_draw_protocol = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_draw) + .draw_get_protocol_self = (draw_get_protocol_self_fun)common_hal_vectorio_polygon_get_draw_protocol, + .draw_protocol_impl = &vectorio_vector_shape_draw_protocol_impl +}; + + +//| points: List[Tuple[int, int]] +//| """Vertices for the polygon.""" +//| +STATIC mp_obj_t vectorio_polygon_obj_get_points(mp_obj_t self_in) { + vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_vectorio_polygon_get_points(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_polygon_get_points_obj, vectorio_polygon_obj_get_points); + +STATIC mp_obj_t vectorio_polygon_obj_set_points(mp_obj_t self_in, mp_obj_t points) { + vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_vectorio_polygon_set_points(self, points); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_polygon_set_points_obj, vectorio_polygon_obj_set_points); + +MP_PROPERTY_GETSET(vectorio_polygon_points_obj, + (mp_obj_t)&vectorio_polygon_get_points_obj, + (mp_obj_t)&vectorio_polygon_set_points_obj); + +//| color_index : int +//| """The color_index of the polygon as 0 based index of the palette.""" +//| +STATIC mp_obj_t vectorio_polygon_obj_get_color_index(mp_obj_t self_in) { + vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_vectorio_polygon_get_color_index(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_polygon_get_color_index_obj, vectorio_polygon_obj_get_color_index); + +STATIC mp_obj_t vectorio_polygon_obj_set_color_index(mp_obj_t self_in, mp_obj_t color_index) { + vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_vectorio_polygon_set_color_index(self, mp_obj_get_int(color_index)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_polygon_set_color_index_obj, vectorio_polygon_obj_set_color_index); + +MP_PROPERTY_GETSET(vectorio_polygon_color_index_obj, + (mp_obj_t)&vectorio_polygon_get_color_index_obj, + (mp_obj_t)&vectorio_polygon_set_color_index_obj); + + +// Documentation for properties inherited from VectorShape. + +//| x : int +//| """X position of the 0,0 origin in the points list.""" +//| +//| y : int +//| """Y position of the 0,0 origin in the points list.""" +//| +//| location : Tuple[int,int] +//| """(X,Y) position of the 0,0 origin in the points list.""" +//| +//| pixel_shader : Union[displayio.ColorConverter,displayio.Palette] +//| """The pixel shader of the polygon.""" +//| + +STATIC const mp_rom_map_elem_t vectorio_polygon_locals_dict_table[] = { + // Functions + { MP_ROM_QSTR(MP_QSTR_contains), MP_ROM_PTR(&vectorio_vector_shape_contains_obj) }, + // Properties + { MP_ROM_QSTR(MP_QSTR_points), MP_ROM_PTR(&vectorio_polygon_points_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_color_index), MP_ROM_PTR(&vectorio_polygon_color_index_obj) }, + { MP_ROM_QSTR(MP_QSTR_location), MP_ROM_PTR(&vectorio_vector_shape_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_polygon_locals_dict, vectorio_polygon_locals_dict_table); + +const mp_obj_type_t vectorio_polygon_type = { + { &mp_type_type }, + .name = MP_QSTR_Polygon, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = vectorio_polygon_make_new, + .locals_dict = (mp_obj_dict_t *)&vectorio_polygon_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &polygon_draw_protocol, + ), +}; diff --git a/circuitpython/shared-bindings/vectorio/Polygon.h b/circuitpython/shared-bindings/vectorio/Polygon.h new file mode 100644 index 0000000..9d3ce2d --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/Polygon.h @@ -0,0 +1,29 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H + +#include "shared-module/vectorio/Polygon.h" +#include "shared-module/displayio/area.h" +#include "shared-module/vectorio/__init__.h" + +extern const mp_obj_type_t vectorio_polygon_type; + +void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list, uint16_t color_index); +void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio_event_t notification); + + +uint32_t common_hal_vectorio_polygon_get_pixel(void *polygon, int16_t x, int16_t y); + +void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *out_area); + + + +mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self); +void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list); + +uint16_t common_hal_vectorio_polygon_get_color_index(void *obj); +void common_hal_vectorio_polygon_set_color_index(void *obj, uint16_t color_index); + +mp_obj_t common_hal_vectorio_polygon_get_draw_protocol(void *polygon); + + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H diff --git a/circuitpython/shared-bindings/vectorio/Rectangle.c b/circuitpython/shared-bindings/vectorio/Rectangle.c new file mode 100644 index 0000000..739a1ba --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/Rectangle.c @@ -0,0 +1,173 @@ +#include "shared-bindings/vectorio/__init__.h" +#include "shared-bindings/vectorio/Rectangle.h" +#include "shared-module/vectorio/VectorShape.h" +#include "shared-bindings/vectorio/VectorShape.h" + +#include <stdint.h> + +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +//| class Rectangle: +//| def __init__(self, pixel_shader: Union[displayio.ColorConverter, displayio.Palette], width: int, height: int, x: int, y: int) -> None: +//| """Represents a rectangle by defining its bounds +//| +//| :param Union[~displayio.ColorConverter,~displayio.Palette] pixel_shader: The pixel shader that produces colors from values +//| :param int width: The number of pixels wide +//| :param int height: The number of pixels high +//| :param int x: Initial x position of the top left corner. +//| :param int y: Initial y position of the top left corner. +//| :param int color_index: Initial color_index to use when selecting color from the palette.""" +//| +static mp_obj_t vectorio_rectangle_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pixel_shader, ARG_width, ARG_height, ARG_x, ARG_y, ARG_color_index }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_color_index, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + }; + 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); + + mp_int_t width = args[ARG_width].u_int; + if (width < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_width); + } + mp_int_t height = args[ARG_height].u_int; + if (height < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_height); + } + + vectorio_rectangle_t *self = m_new_obj(vectorio_rectangle_t); + self->base.type = &vectorio_rectangle_type; + uint16_t color_index = args[ARG_color_index].u_int; + common_hal_vectorio_rectangle_construct(self, width, height, color_index); + + // VectorShape parts + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + int32_t x = args[ARG_x].u_int; + int32_t y = args[ARG_y].u_int; + mp_obj_t vector_shape = vectorio_vector_shape_make_new(self, pixel_shader, x, y); + self->draw_protocol_instance = vector_shape; + + return MP_OBJ_FROM_PTR(self); +} + +STATIC const vectorio_draw_protocol_t rectangle_draw_protocol = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_draw) + .draw_get_protocol_self = (draw_get_protocol_self_fun)common_hal_vectorio_rectangle_get_draw_protocol, + .draw_protocol_impl = &vectorio_vector_shape_draw_protocol_impl +}; + +//| width : int +//| """The width of the rectangle in pixels.""" +//| +STATIC mp_obj_t vectorio_rectangle_obj_get_width(mp_obj_t self_in) { + vectorio_rectangle_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_vectorio_rectangle_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_rectangle_get_width_obj, vectorio_rectangle_obj_get_width); + +STATIC mp_obj_t vectorio_rectangle_obj_set_width(mp_obj_t self_in, mp_obj_t width) { + vectorio_rectangle_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_vectorio_rectangle_set_width(self, mp_obj_get_int(width)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_rectangle_set_width_obj, vectorio_rectangle_obj_set_width); + +const mp_obj_property_t vectorio_rectangle_width_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_rectangle_get_width_obj, + (mp_obj_t)&vectorio_rectangle_set_width_obj, + MP_ROM_NONE}, +}; + +//| height : int +//| """The height of the rectangle in pixels.""" +//| +STATIC mp_obj_t vectorio_rectangle_obj_get_height(mp_obj_t self_in) { + vectorio_rectangle_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_vectorio_rectangle_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_rectangle_get_height_obj, vectorio_rectangle_obj_get_height); + +STATIC mp_obj_t vectorio_rectangle_obj_set_height(mp_obj_t self_in, mp_obj_t height) { + vectorio_rectangle_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_vectorio_rectangle_set_height(self, mp_obj_get_int(height)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_rectangle_set_height_obj, vectorio_rectangle_obj_set_height); + +const mp_obj_property_t vectorio_rectangle_height_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_rectangle_get_height_obj, + (mp_obj_t)&vectorio_rectangle_set_height_obj, + MP_ROM_NONE}, +}; + +//| color_index : int +//| """The color_index of the rectangle in 1 based index of the palette.""" +//| +STATIC mp_obj_t vectorio_rectangle_obj_get_color_index(mp_obj_t self_in) { + vectorio_rectangle_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_vectorio_rectangle_get_color_index(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_rectangle_get_color_index_obj, vectorio_rectangle_obj_get_color_index); + +STATIC mp_obj_t vectorio_rectangle_obj_set_color_index(mp_obj_t self_in, mp_obj_t color_index) { + vectorio_rectangle_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_vectorio_rectangle_set_color_index(self, mp_obj_get_int(color_index)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_rectangle_set_color_index_obj, vectorio_rectangle_obj_set_color_index); + +const mp_obj_property_t vectorio_rectangle_color_index_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_rectangle_get_color_index_obj, + (mp_obj_t)&vectorio_rectangle_set_color_index_obj, + MP_ROM_NONE}, +}; + +// Documentation for properties inherited from VectorShape. + +//| x : int +//| """X position of the top left corner of the rectangle in the parent.""" +//| +//| y : int +//| """Y position of the top left corner of the rectangle in the parent.""" +//| +//| location : Tuple[int,int] +//| """(X,Y) position of the top left corner of the rectangle in the parent.""" +//| +//| pixel_shader : Union[displayio.ColorConverter,displayio.Palette] +//| """The pixel shader of the rectangle.""" +//| + +STATIC const mp_rom_map_elem_t vectorio_rectangle_locals_dict_table[] = { + // Functions + { MP_ROM_QSTR(MP_QSTR_contains), MP_ROM_PTR(&vectorio_vector_shape_contains_obj) }, + // Properties + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_color_index), MP_ROM_PTR(&vectorio_rectangle_color_index_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&vectorio_rectangle_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&vectorio_rectangle_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_location), MP_ROM_PTR(&vectorio_vector_shape_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_rectangle_locals_dict, vectorio_rectangle_locals_dict_table); + +const mp_obj_type_t vectorio_rectangle_type = { + { &mp_type_type }, + .name = MP_QSTR_Rectangle, + .flags = MP_TYPE_FLAG_EXTENDED, + .make_new = vectorio_rectangle_make_new, + .locals_dict = (mp_obj_dict_t *)&vectorio_rectangle_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &rectangle_draw_protocol, + ), +}; diff --git a/circuitpython/shared-bindings/vectorio/Rectangle.h b/circuitpython/shared-bindings/vectorio/Rectangle.h new file mode 100644 index 0000000..907ae68 --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/Rectangle.h @@ -0,0 +1,28 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H + +#include "shared-module/vectorio/Rectangle.h" +#include "shared-module/displayio/area.h" +#include "shared-module/vectorio/__init__.h" + +extern const mp_obj_type_t vectorio_rectangle_type; + +void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_t width, uint32_t height, uint16_t color_index); +void common_hal_vectorio_rectangle_set_on_dirty(vectorio_rectangle_t *self, vectorio_event_t on_dirty); + +uint32_t common_hal_vectorio_rectangle_get_pixel(void *rectangle, int16_t x, int16_t y); + +void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area); + +mp_obj_t common_hal_vectorio_rectangle_get_draw_protocol(void *rectangle); + +int16_t common_hal_vectorio_rectangle_get_width(void *obj); +void common_hal_vectorio_rectangle_set_width(void *obj, int16_t width); + +uint16_t common_hal_vectorio_rectangle_get_color_index(void *obj); +void common_hal_vectorio_rectangle_set_color_index(void *obj, uint16_t color_index); + +int16_t common_hal_vectorio_rectangle_get_height(void *obj); +void common_hal_vectorio_rectangle_set_height(void *obj, int16_t height); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H diff --git a/circuitpython/shared-bindings/vectorio/VectorShape.c b/circuitpython/shared-bindings/vectorio/VectorShape.c new file mode 100644 index 0000000..fe6be55 --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/VectorShape.c @@ -0,0 +1,223 @@ + +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/VectorShape.h" +#include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/Rectangle.h" + +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/Palette.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" + +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +// shape: The shape implementation to draw. +// pixel_shader: The pixel shader that produces colors from values. The shader can be a displayio.Palette(1); it will be asked to color pixel value 0. +// x: Initial x position of the center axis of the shape within the parent. +// y: Initial y position of the center axis of the shape within the parent.""" +mp_obj_t vectorio_vector_shape_make_new(const mp_obj_t shape, const mp_obj_t pixel_shader, int32_t x, int32_t y) { + if (!mp_obj_is_type(pixel_shader, &displayio_colorconverter_type) && + !mp_obj_is_type(pixel_shader, &displayio_palette_type)) { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_pixel_shader); + } + + vectorio_ishape_t ishape; + // Wire up shape functions + if (mp_obj_is_type(shape, &vectorio_polygon_type)) { + ishape.shape = shape; + ishape.get_area = &common_hal_vectorio_polygon_get_area; + ishape.get_pixel = &common_hal_vectorio_polygon_get_pixel; + } else if (mp_obj_is_type(shape, &vectorio_rectangle_type)) { + ishape.shape = shape; + ishape.get_area = &common_hal_vectorio_rectangle_get_area; + ishape.get_pixel = &common_hal_vectorio_rectangle_get_pixel; + } else if (mp_obj_is_type(shape, &vectorio_circle_type)) { + ishape.shape = shape; + ishape.get_area = &common_hal_vectorio_circle_get_area; + ishape.get_pixel = &common_hal_vectorio_circle_get_pixel; + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_shape); + } + + vectorio_vector_shape_t *self = m_new_obj(vectorio_vector_shape_t); + self->base.type = &vectorio_vector_shape_type; + common_hal_vectorio_vector_shape_construct(self, + ishape, pixel_shader, x, y + ); + + // Wire up event callbacks + vectorio_event_t on_dirty = { + .obj = self, + .event = &common_hal_vectorio_vector_shape_set_dirty + }; + + if (mp_obj_is_type(shape, &vectorio_polygon_type)) { + common_hal_vectorio_polygon_set_on_dirty(self->ishape.shape, on_dirty); + } else if (mp_obj_is_type(shape, &vectorio_rectangle_type)) { + common_hal_vectorio_rectangle_set_on_dirty(self->ishape.shape, on_dirty); + } else if (mp_obj_is_type(shape, &vectorio_circle_type)) { + common_hal_vectorio_circle_set_on_dirty(self->ishape.shape, on_dirty); + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_shape); + } + + return MP_OBJ_FROM_PTR(self); +} + +vectorio_draw_protocol_impl_t vectorio_vector_shape_draw_protocol_impl = { + .draw_fill_area = (draw_fill_area_fun)vectorio_vector_shape_fill_area, + .draw_get_dirty_area = (draw_get_dirty_area_fun)vectorio_vector_shape_get_dirty_area, + .draw_update_transform = (draw_update_transform_fun)vectorio_vector_shape_update_transform, + .draw_finish_refresh = (draw_finish_refresh_fun)vectorio_vector_shape_finish_refresh, + .draw_get_refresh_areas = (draw_get_refresh_areas_fun)vectorio_vector_shape_get_refresh_areas, +}; + +// Stub checker does not approve of these shared properties. +// x: int +// y: int +// """true if x,y lies inside the shape.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_contains(mp_obj_t wrapper_shape, mp_obj_t x_obj, mp_obj_t y_obj) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + mp_int_t x = mp_obj_get_int(x_obj); + mp_int_t y = mp_obj_get_int(y_obj); + return mp_obj_new_bool(common_hal_vectorio_vector_shape_contains(self, x, y)); +} +MP_DEFINE_CONST_FUN_OBJ_3(vectorio_vector_shape_contains_obj, vectorio_vector_shape_obj_contains); + +// Stub checker does not approve of these shared properties. +// x: int +// """X position of the center point of the shape in the parent.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_x(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + return MP_OBJ_NEW_SMALL_INT(common_hal_vectorio_vector_shape_get_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_x_obj, vectorio_vector_shape_obj_get_x); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_x(mp_obj_t wrapper_shape, mp_obj_t x_obj) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + mp_int_t x = mp_obj_get_int(x_obj); + common_hal_vectorio_vector_shape_set_x(self, x); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_x_obj, vectorio_vector_shape_obj_set_x); + +MP_PROPERTY_GETSET(vectorio_vector_shape_x_obj, + (mp_obj_t)&vectorio_vector_shape_get_x_obj, + (mp_obj_t)&vectorio_vector_shape_set_x_obj); + + +// y: int +// """Y position of the center point of the shape in the parent.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_y(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + return MP_OBJ_NEW_SMALL_INT(common_hal_vectorio_vector_shape_get_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_y_obj, vectorio_vector_shape_obj_get_y); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_y(mp_obj_t wrapper_shape, mp_obj_t y_obj) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + mp_int_t y = mp_obj_get_int(y_obj); + common_hal_vectorio_vector_shape_set_y(self, y); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_y_obj, vectorio_vector_shape_obj_set_y); + +MP_PROPERTY_GETSET(vectorio_vector_shape_y_obj, + (mp_obj_t)&vectorio_vector_shape_get_y_obj, + (mp_obj_t)&vectorio_vector_shape_set_y_obj); + + +// location: Tuple[int, int] +// """location of the center point of the shape in the parent.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_location(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + return MP_OBJ_TO_PTR(common_hal_vectorio_vector_shape_get_location(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_location_obj, vectorio_vector_shape_obj_get_location); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_location(mp_obj_t wrapper_shape, mp_obj_t location_obj) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + common_hal_vectorio_vector_shape_set_location(self, location_obj); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_location_obj, vectorio_vector_shape_obj_set_location); + +MP_PROPERTY_GETSET(vectorio_vector_shape_location_obj, + (mp_obj_t)&vectorio_vector_shape_get_location_obj, + (mp_obj_t)&vectorio_vector_shape_set_location_obj); + + +// pixel_shader: Union[ColorConverter, Palette] +// """The pixel shader of the shape.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_pixel_shader(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + return common_hal_vectorio_vector_shape_get_pixel_shader(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_pixel_shader_obj, vectorio_vector_shape_obj_get_pixel_shader); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_pixel_shader(mp_obj_t wrapper_shape, mp_obj_t pixel_shader) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + if (!mp_obj_is_type(pixel_shader, &displayio_palette_type) && !mp_obj_is_type(pixel_shader, &displayio_colorconverter_type)) { + mp_raise_TypeError(translate("pixel_shader must be displayio.Palette or displayio.ColorConverter")); + } + + common_hal_vectorio_vector_shape_set_pixel_shader(self, pixel_shader); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_pixel_shader_obj, vectorio_vector_shape_obj_set_pixel_shader); + +MP_PROPERTY_GETSET(vectorio_vector_shape_pixel_shader_obj, + (mp_obj_t)&vectorio_vector_shape_get_pixel_shader_obj, + (mp_obj_t)&vectorio_vector_shape_set_pixel_shader_obj); + + +STATIC const mp_rom_map_elem_t vectorio_vector_shape_locals_dict_table[] = { +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_vector_shape_locals_dict, vectorio_vector_shape_locals_dict_table); + +const mp_obj_type_t vectorio_vector_shape_type = { + { &mp_type_type }, + .name = MP_QSTR_VectorShape, + .locals_dict = (mp_obj_dict_t *)&vectorio_vector_shape_locals_dict, +}; diff --git a/circuitpython/shared-bindings/vectorio/VectorShape.h b/circuitpython/shared-bindings/vectorio/VectorShape.h new file mode 100644 index 0000000..13f6292 --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/VectorShape.h @@ -0,0 +1,47 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H + +#include "py/objproperty.h" +#include "py/objtuple.h" + +#include "shared-bindings/vectorio/__init__.h" +#include "shared-module/vectorio/VectorShape.h" +#include "shared-module/displayio/area.h" + +extern const mp_obj_type_t vectorio_vector_shape_type; + +// Python shared bindings constructor +mp_obj_t vectorio_vector_shape_make_new(const mp_obj_t shape, const mp_obj_t pixel_shader, int32_t x, int32_t y); + +// C data constructor +void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, + vectorio_ishape_t ishape, + mp_obj_t pixel_shader, int32_t x, int32_t y); + +bool common_hal_vectorio_vector_shape_contains(vectorio_vector_shape_t *self, mp_int_t x, mp_int_t y); + +void common_hal_vectorio_vector_shape_set_dirty(void *self); + +mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_int_t x); + +mp_obj_tuple_t *common_hal_vectorio_vector_shape_get_location(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_location(vectorio_vector_shape_t *self, mp_obj_t xy); + +mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y); + +mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader); + +void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform); + +// Composable property definition for shapes that use VectorShape +extern vectorio_draw_protocol_impl_t vectorio_vector_shape_draw_protocol_impl; +extern const mp_obj_property_getset_t vectorio_vector_shape_x_obj; +extern const mp_obj_property_getset_t vectorio_vector_shape_y_obj; +extern const mp_obj_property_getset_t vectorio_vector_shape_location_obj; +extern const mp_obj_property_getset_t vectorio_vector_shape_pixel_shader_obj; +extern const mp_obj_fun_builtin_fixed_t vectorio_vector_shape_contains_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H diff --git a/circuitpython/shared-bindings/vectorio/__init__.c b/circuitpython/shared-bindings/vectorio/__init__.c new file mode 100644 index 0000000..6e39f26 --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/__init__.c @@ -0,0 +1,49 @@ +#include <stdint.h> + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/Rectangle.h" + +//| """Lightweight 2D shapes for displays +//| +//| The :py:attr:`vectorio` module provide simple filled drawing primitives for +//| use with `displayio`. +//| +//| .. code-block:: python +//| +//| group = displayio.Group() +//| +//| palette = displayio.Palette(1) +//| palette[0] = 0x125690 +//| +//| circle = vectorio.Circle(pixel_shader=palette, radius=25, x=70, y=40) +//| group.append(circle) +//| +//| rectangle = vectorio.Rectangle(pixel_shader=palette, width=40, height=30, x=55, y=45) +//| group.append(rectangle) +//| +//| points=[(5, 5), (100, 20), (20, 20), (20, 100)] +//| polygon = vectorio.Polygon(pixel_shader=palette, points=points, x=0, y=0) +//| group.append(polygon) +//| +//| """ +//| + +STATIC const mp_rom_map_elem_t vectorio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_vectorio) }, + { MP_ROM_QSTR(MP_QSTR_Circle), MP_ROM_PTR(&vectorio_circle_type) }, + { MP_ROM_QSTR(MP_QSTR_Polygon), MP_ROM_PTR(&vectorio_polygon_type) }, + { MP_ROM_QSTR(MP_QSTR_Rectangle), MP_ROM_PTR(&vectorio_rectangle_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(vectorio_module_globals, vectorio_module_globals_table); + +const mp_obj_module_t vectorio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&vectorio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_vectorio, vectorio_module, CIRCUITPY_VECTORIO); diff --git a/circuitpython/shared-bindings/vectorio/__init__.h b/circuitpython/shared-bindings/vectorio/__init__.h new file mode 100644 index 0000000..a34195d --- /dev/null +++ b/circuitpython/shared-bindings/vectorio/__init__.h @@ -0,0 +1,41 @@ +#ifndef SHARED_MODULE_VECTORIO__INIT__H +#define SHARED_MODULE_VECTORIO__INIT__H + +#include <stdbool.h> +#include <stdint.h> + +#include "py/obj.h" +#include "py/proto.h" + +#include "shared-module/displayio/area.h" +#include "shared-module/displayio/Palette.h" + +// Returns the object on which the rest of the draw protocol methods are invoked. +typedef mp_obj_t (*draw_get_protocol_self_fun)(mp_obj_t protocol_container); + +typedef bool (*draw_fill_area_fun)(mp_obj_t draw_protocol_self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer); +typedef bool (*draw_get_dirty_area_fun)(mp_obj_t draw_protocol_self, displayio_area_t *current_dirty_area); +typedef void (*draw_update_transform_fun)(mp_obj_t draw_protocol_self, displayio_buffer_transform_t *group_transform); +typedef void (*draw_finish_refresh_fun)(mp_obj_t draw_protocol_self); +typedef displayio_area_t *(*draw_get_refresh_areas_fun)(mp_obj_t draw_protocol_self, displayio_area_t *tail); + +typedef struct _vectorio_draw_protocol_impl_t { + draw_fill_area_fun draw_fill_area; + draw_get_dirty_area_fun draw_get_dirty_area; + draw_update_transform_fun draw_update_transform; + draw_finish_refresh_fun draw_finish_refresh; + draw_get_refresh_areas_fun draw_get_refresh_areas; +} vectorio_draw_protocol_impl_t; + +// Draw protocol +typedef struct _vectorio_draw_protocol_t { + MP_PROTOCOL_HEAD // MP_QSTR_protocol_draw + + // Instance of the draw protocol + draw_get_protocol_self_fun draw_get_protocol_self; + + // Implementation functions for the draw protocol + vectorio_draw_protocol_impl_t *draw_protocol_impl; +} vectorio_draw_protocol_t; + +#endif diff --git a/circuitpython/shared-bindings/watchdog/WatchDogMode.c b/circuitpython/shared-bindings/watchdog/WatchDogMode.c new file mode 100644 index 0000000..beea4d0 --- /dev/null +++ b/circuitpython/shared-bindings/watchdog/WatchDogMode.c @@ -0,0 +1,98 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Sean Cross 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 "shared-bindings/watchdog/WatchDogMode.h" + +//| class WatchDogMode: +//| """run state of the watchdog timer""" +//| +//| def __init__(self) -> None: +//| """Enum-like class to define the run mode of the watchdog timer.""" +//| +//| RAISE: WatchDogMode +//| """Raise an exception when the WatchDogTimer expires. +//| +//| :type WatchDogMode:""" +//| +//| RESET: WatchDogMode +//| """Reset the system if the WatchDogTimer expires. +//| +//| :type WatchDogMode:""" +//| +const mp_obj_type_t watchdog_watchdogmode_type; + +const watchdog_watchdogmode_obj_t watchdog_watchdogmode_raise_obj = { + { &watchdog_watchdogmode_type }, +}; + +const watchdog_watchdogmode_obj_t watchdog_watchdogmode_reset_obj = { + { &watchdog_watchdogmode_type }, +}; + +watchdog_watchdogmode_t watchdog_watchdogmode_obj_to_type(mp_obj_t obj) { + if (obj == MP_ROM_PTR(&watchdog_watchdogmode_raise_obj)) { + return WATCHDOGMODE_RAISE; + } else if (obj == MP_ROM_PTR(&watchdog_watchdogmode_reset_obj)) { + return WATCHDOGMODE_RESET; + } + return WATCHDOGMODE_NONE; +} + +mp_obj_t watchdog_watchdogmode_type_to_obj(watchdog_watchdogmode_t mode) { + switch (mode) { + case WATCHDOGMODE_RAISE: + return (mp_obj_t)MP_ROM_PTR(&watchdog_watchdogmode_raise_obj); + case WATCHDOGMODE_RESET: + return (mp_obj_t)MP_ROM_PTR(&watchdog_watchdogmode_reset_obj); + case WATCHDOGMODE_NONE: + default: + return MP_ROM_NONE; + } +} + +STATIC const mp_rom_map_elem_t watchdog_watchdogmode_locals_dict_table[] = { + {MP_ROM_QSTR(MP_QSTR_RAISE), MP_ROM_PTR(&watchdog_watchdogmode_raise_obj)}, + {MP_ROM_QSTR(MP_QSTR_RESET), MP_ROM_PTR(&watchdog_watchdogmode_reset_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(watchdog_watchdogmode_locals_dict, watchdog_watchdogmode_locals_dict_table); + +STATIC void watchdog_watchdogmode_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + qstr runmode = MP_QSTR_None; + if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&watchdog_watchdogmode_raise_obj)) { + runmode = MP_QSTR_RAISE; + } else if (MP_OBJ_TO_PTR(self_in) == MP_ROM_PTR(&watchdog_watchdogmode_reset_obj)) { + runmode = MP_QSTR_RESET; + } + mp_printf(print, "%q.%q.%q", MP_QSTR_watchdog, MP_QSTR_WatchDogMode, + runmode); +} + +const mp_obj_type_t watchdog_watchdogmode_type = { + { &mp_type_type }, + .name = MP_QSTR_WatchDogMode, + .print = watchdog_watchdogmode_print, + .locals_dict = (mp_obj_t)&watchdog_watchdogmode_locals_dict, +}; diff --git a/circuitpython/shared-bindings/watchdog/WatchDogMode.h b/circuitpython/shared-bindings/watchdog/WatchDogMode.h new file mode 100644 index 0000000..fb09445 --- /dev/null +++ b/circuitpython/shared-bindings/watchdog/WatchDogMode.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Sean Cross 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_WATCHDOG_WATCHDOGMODE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGMODE_H + +#include "py/obj.h" + +typedef enum { + WATCHDOGMODE_NONE, + WATCHDOGMODE_RAISE, + WATCHDOGMODE_RESET, +} watchdog_watchdogmode_t; + +extern const mp_obj_type_t watchdog_watchdogmode_type; + +watchdog_watchdogmode_t watchdog_watchdogmode_obj_to_type(mp_obj_t obj); +mp_obj_t watchdog_watchdogmode_type_to_obj(watchdog_watchdogmode_t mode); + +typedef struct { + mp_obj_base_t base; +} watchdog_watchdogmode_obj_t; +extern const watchdog_watchdogmode_obj_t watchdog_watchdogmode_raise_obj; +extern const watchdog_watchdogmode_obj_t watchdog_watchdogmode_reset_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGMODE_H diff --git a/circuitpython/shared-bindings/watchdog/WatchDogTimer.c b/circuitpython/shared-bindings/watchdog/WatchDogTimer.c new file mode 100644 index 0000000..e00b288 --- /dev/null +++ b/circuitpython/shared-bindings/watchdog/WatchDogTimer.c @@ -0,0 +1,175 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Nick Moore 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 <math.h> +#include <string.h> + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "common-hal/watchdog/WatchDogTimer.h" + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/watchdog/__init__.h" +#include "shared-bindings/watchdog/WatchDogTimer.h" + +#include "supervisor/port.h" + +//| class WatchDogTimer: +//| """Timer that is used to detect code lock ups and automatically reset the microcontroller +//| when one is detected. +//| +//| A lock up is detected when the watchdog hasn't been fed after a given duration. So, make +//| sure to call `feed` within the timeout. +//| """ +//| + +//| def __init__(self) -> None: +//| """Not currently dynamically supported. Access the sole instance through `microcontroller.watchdog`.""" +//| ... +//| + +//| def feed(self) -> None: +//| """Feed the watchdog timer. This must be called regularly, otherwise +//| the timer will expire.""" +//| ... +//| +STATIC mp_obj_t watchdog_watchdogtimer_feed(mp_obj_t self_in) { + watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in); + watchdog_watchdogmode_t current_mode = common_hal_watchdog_get_mode(self); + + if (current_mode == WATCHDOGMODE_NONE) { + mp_raise_ValueError(translate("WatchDogTimer is not currently running")); + } + + common_hal_watchdog_feed(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_feed_obj, watchdog_watchdogtimer_feed); + +//| def deinit(self) -> None: +//| """Stop the watchdog timer. This may raise an error if the watchdog +//| timer cannot be disabled on this platform.""" +//| ... +//| +STATIC mp_obj_t watchdog_watchdogtimer_deinit(mp_obj_t self_in) { + watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_watchdog_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_deinit_obj, watchdog_watchdogtimer_deinit); + +//| timeout: float +//| """The maximum number of seconds that can elapse between calls +//| to feed()""" +//| +STATIC mp_obj_t watchdog_watchdogtimer_obj_get_timeout(mp_obj_t self_in) { + watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_float(common_hal_watchdog_get_timeout(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_get_timeout_obj, watchdog_watchdogtimer_obj_get_timeout); + +STATIC mp_obj_t watchdog_watchdogtimer_obj_set_timeout(mp_obj_t self_in, mp_obj_t timeout_obj) { + watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t timeout = mp_obj_get_float(timeout_obj); + + if (timeout <= 0) { + mp_raise_ValueError(translate("watchdog timeout must be greater than 0")); + } + + common_hal_watchdog_set_timeout(self, timeout); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(watchdog_watchdogtimer_set_timeout_obj, watchdog_watchdogtimer_obj_set_timeout); + +MP_PROPERTY_GETSET(watchdog_watchdogtimer_timeout_obj, + (mp_obj_t)&watchdog_watchdogtimer_get_timeout_obj, + (mp_obj_t)&watchdog_watchdogtimer_set_timeout_obj); + +//| mode: WatchDogMode +//| """The current operating mode of the WatchDogTimer `watchdog.WatchDogMode`. +//| +//| Setting a WatchDogMode activates the WatchDog:: +//| +//| import microcontroller +//| import watchdog +//| +//| w = microcontroller.watchdog +//| w.timeout = 5 +//| w.mode = watchdog.WatchDogMode.RAISE +//| +//| +//| Once set, the WatchDogTimer will perform the specified action if the timer expires.""" +//| +STATIC mp_obj_t watchdog_watchdogtimer_obj_get_mode(mp_obj_t self_in) { + watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in); + return watchdog_watchdogmode_type_to_obj(common_hal_watchdog_get_mode(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(watchdog_watchdogtimer_get_mode_obj, watchdog_watchdogtimer_obj_get_mode); + +STATIC mp_obj_t watchdog_watchdogtimer_obj_set_mode(mp_obj_t self_in, mp_obj_t mode_obj) { + watchdog_watchdogtimer_obj_t *self = MP_OBJ_TO_PTR(self_in); + watchdog_watchdogmode_t current_mode = common_hal_watchdog_get_mode(self); + watchdog_watchdogmode_t new_mode = watchdog_watchdogmode_obj_to_type(mode_obj); + mp_float_t current_timeout = common_hal_watchdog_get_timeout(self); + + // When setting the mode, the timeout value must be greater than zero + if (new_mode == WATCHDOGMODE_RESET || new_mode == WATCHDOGMODE_RAISE) { + if (current_timeout <= 0) { + mp_raise_ValueError(translate("WatchDogTimer.timeout must be greater than 0")); + } + } + + // Don't allow changing the mode once the watchdog timer has been started + if (current_mode == WATCHDOGMODE_RESET && new_mode != WATCHDOGMODE_RESET) { + mp_raise_TypeError(translate("WatchDogTimer.mode cannot be changed once set to WatchDogMode.RESET")); + } + + common_hal_watchdog_set_mode(self, new_mode); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(watchdog_watchdogtimer_set_mode_obj, watchdog_watchdogtimer_obj_set_mode); + +MP_PROPERTY_GETSET(watchdog_watchdogtimer_mode_obj, + (mp_obj_t)&watchdog_watchdogtimer_get_mode_obj, + (mp_obj_t)&watchdog_watchdogtimer_set_mode_obj); + +STATIC const mp_rom_map_elem_t watchdog_watchdogtimer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_feed), MP_ROM_PTR(&watchdog_watchdogtimer_feed_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&watchdog_watchdogtimer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&watchdog_watchdogtimer_timeout_obj) }, + { MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&watchdog_watchdogtimer_mode_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(watchdog_watchdogtimer_locals_dict, watchdog_watchdogtimer_locals_dict_table); + +const mp_obj_type_t watchdog_watchdogtimer_type = { + { &mp_type_type }, + .name = MP_QSTR_WatchDogTimer, + // .make_new = watchdog_watchdogtimer_make_new, + .locals_dict = (mp_obj_dict_t *)&watchdog_watchdogtimer_locals_dict, +}; diff --git a/circuitpython/shared-bindings/watchdog/WatchDogTimer.h b/circuitpython/shared-bindings/watchdog/WatchDogTimer.h new file mode 100644 index 0000000..4804474 --- /dev/null +++ b/circuitpython/shared-bindings/watchdog/WatchDogTimer.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * 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_WATCHDOG_WATCHDOGTIMER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGTIMER_H + +#include <py/obj.h> +#include "shared-bindings/watchdog/WatchDogMode.h" + +typedef struct _watchdog_watchdogtimer_obj_t watchdog_watchdogtimer_obj_t; + +extern void common_hal_watchdog_feed(watchdog_watchdogtimer_obj_t *self); + +extern void common_hal_watchdog_set_mode(watchdog_watchdogtimer_obj_t *self, watchdog_watchdogmode_t); +extern watchdog_watchdogmode_t common_hal_watchdog_get_mode(watchdog_watchdogtimer_obj_t *self); + +extern void common_hal_watchdog_set_timeout(watchdog_watchdogtimer_obj_t *self, mp_float_t timeout); +extern mp_float_t common_hal_watchdog_get_timeout(watchdog_watchdogtimer_obj_t *self); + +extern void common_hal_watchdog_enable(watchdog_watchdogtimer_obj_t *self); +extern void common_hal_watchdog_deinit(watchdog_watchdogtimer_obj_t *self); + +extern const mp_obj_type_t watchdog_watchdogtimer_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG_WATCHDOGTIMER_H diff --git a/circuitpython/shared-bindings/watchdog/__init__.c b/circuitpython/shared-bindings/watchdog/__init__.c new file mode 100644 index 0000000..a0e357e --- /dev/null +++ b/circuitpython/shared-bindings/watchdog/__init__.c @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * + * 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 <string.h> + +#include "py/runtime.h" +#include "shared-bindings/watchdog/__init__.h" +#include "shared-bindings/watchdog/WatchDogMode.h" + +//| """Watchdog Timer +//| +//| The `watchdog` module provides support for a Watchdog Timer. This timer will reset the device +//| if it hasn't been fed after a specified amount of time. This is useful to ensure the board +//| has not crashed or locked up. Note that on some platforms the watchdog timer cannot be disabled +//| once it has been enabled. +//| +//| The `WatchDogTimer` is used to restart the system when the application crashes and ends +//| up into a non recoverable state. Once started it cannot be stopped or +//| reconfigured in any way. After enabling, the application must "feed" the +//| watchdog periodically to prevent it from expiring and resetting the system. +//| +//| Example usage:: +//| +//| from microcontroller import watchdog as w +//| from watchdog import WatchDogMode +//| w.timeout=2.5 # Set a timeout of 2.5 seconds +//| w.mode = WatchDogMode.RAISE +//| w.feed()""" + +//| class WatchDogTimeout(Exception): +//| """Exception raised when the watchdog timer is set to +//| ``WatchDogMode.RAISE`` and expires. +//| +//| Example:: +//| +//| import microcontroller +//| import watchdog +//| import time +//| +//| wdt = microcontroller.watchdog +//| wdt.timeout = 5 +//| +//| while True: +//| wdt.mode = watchdog.WatchDogMode.RAISE +//| print("Starting loop -- should exit after five seconds") +//| try: +//| while True: +//| time.sleep(10) # Also works with pass +//| except watchdog.WatchDogTimeout as e: +//| print("Watchdog expired") +//| except Exception as e: +//| print("Other exception") +//| +//| print("Exited loop") +//| """ + +const mp_obj_type_t mp_type_WatchDogTimeout = { + { &mp_type_type }, + .name = MP_QSTR_WatchDogTimeout, + .make_new = mp_obj_exception_make_new, + .attr = mp_obj_exception_attr, + .parent = &mp_type_Exception, +}; + +mp_obj_exception_t mp_watchdog_timeout_exception = { + .base.type = &mp_type_WatchDogTimeout, + .args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj, + .traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj, +}; + +STATIC const mp_rom_map_elem_t watchdog_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_watchdog) }, + { MP_ROM_QSTR(MP_QSTR_WatchDogMode), MP_ROM_PTR(&watchdog_watchdogmode_type) }, + { MP_ROM_QSTR(MP_QSTR_WatchDogTimeout), MP_ROM_PTR(&mp_type_WatchDogTimeout) }, +}; + +STATIC MP_DEFINE_CONST_DICT(watchdog_module_globals, watchdog_module_globals_table); + +const mp_obj_module_t watchdog_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&watchdog_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_watchdog, watchdog_module, CIRCUITPY_WATCHDOG); diff --git a/circuitpython/shared-bindings/watchdog/__init__.h b/circuitpython/shared-bindings/watchdog/__init__.h new file mode 100644 index 0000000..0921141 --- /dev/null +++ b/circuitpython/shared-bindings/watchdog/__init__.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG___INIT___H + +#include "py/obj.h" +#include "py/objexcept.h" + +extern const mp_obj_module_t watchdog_module; +extern mp_obj_exception_t mp_watchdog_timeout_exception; +extern const mp_obj_type_t mp_type_WatchDogTimeout; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WATCHDOG___INIT___H diff --git a/circuitpython/shared-bindings/wifi/AuthMode.c b/circuitpython/shared-bindings/wifi/AuthMode.c new file mode 100644 index 0000000..528fcd4 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/AuthMode.c @@ -0,0 +1,76 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "py/enum.h" + +#include "shared-bindings/wifi/AuthMode.h" + +MAKE_ENUM_VALUE(wifi_authmode_type, authmode, OPEN, AUTHMODE_OPEN); +MAKE_ENUM_VALUE(wifi_authmode_type, authmode, WEP, AUTHMODE_WEP); +MAKE_ENUM_VALUE(wifi_authmode_type, authmode, WPA, AUTHMODE_WPA); +MAKE_ENUM_VALUE(wifi_authmode_type, authmode, WPA2, AUTHMODE_WPA2); +MAKE_ENUM_VALUE(wifi_authmode_type, authmode, WPA3, AUTHMODE_WPA3); +MAKE_ENUM_VALUE(wifi_authmode_type, authmode, PSK, AUTHMODE_PSK); +MAKE_ENUM_VALUE(wifi_authmode_type, authmode, ENTERPRISE, AUTHMODE_ENTERPRISE); + +//| class AuthMode: +//| """The authentication protocols used by WiFi.""" +//| +//| OPEN: object +//| """Open network. No authentication required.""" +//| +//| WEP: object +//| """Wired Equivalent Privacy.""" +//| +//| WPA: object +//| """Wireless Protected Access.""" +//| +//| WPA2: object +//| """Wireless Protected Access 2.""" +//| +//| WPA3: object +//| """Wireless Protected Access 3.""" +//| +//| PSK: object +//| """Pre-shared Key. (password)""" +//| +//| ENTERPRISE: object +//| """Each user has a unique credential.""" +//| +MAKE_ENUM_MAP(wifi_authmode) { + MAKE_ENUM_MAP_ENTRY(authmode, OPEN), + MAKE_ENUM_MAP_ENTRY(authmode, WEP), + MAKE_ENUM_MAP_ENTRY(authmode, WPA), + MAKE_ENUM_MAP_ENTRY(authmode, WPA2), + MAKE_ENUM_MAP_ENTRY(authmode, WPA3), + MAKE_ENUM_MAP_ENTRY(authmode, PSK), + MAKE_ENUM_MAP_ENTRY(authmode, ENTERPRISE), +}; +STATIC MP_DEFINE_CONST_DICT(wifi_authmode_locals_dict, wifi_authmode_locals_table); + +MAKE_PRINTER(wifi, wifi_authmode); + +MAKE_ENUM_TYPE(wifi, AuthMode, wifi_authmode); diff --git a/circuitpython/shared-bindings/wifi/AuthMode.h b/circuitpython/shared-bindings/wifi/AuthMode.h new file mode 100644 index 0000000..a1016d6 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/AuthMode.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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_WIFI_AUTHMODE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_AUTHMODE_H + +#include "py/enum.h" + +typedef enum { + AUTHMODE_OPEN, + AUTHMODE_WEP, + AUTHMODE_WPA, + AUTHMODE_WPA2, + AUTHMODE_WPA3, + AUTHMODE_PSK, + AUTHMODE_ENTERPRISE +} wifi_authmode_t; + +extern const mp_obj_type_t wifi_authmode_type; +extern const cp_enum_obj_t authmode_OPEN_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_AUTHMODE_H diff --git a/circuitpython/shared-bindings/wifi/Monitor.c b/circuitpython/shared-bindings/wifi/Monitor.c new file mode 100644 index 0000000..a63c0a8 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Monitor.c @@ -0,0 +1,171 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "py/mpstate.h" +#include "py/runtime.h" +#include "py/objproperty.h" + +#include "shared-bindings/util.h" +#include "shared-bindings/wifi/Packet.h" +#include "shared-bindings/wifi/Monitor.h" + +//| class Monitor: +//| """For monitoring WiFi packets.""" +//| + +//| def __init__(self, channel: Optional[int] = 1, queue: Optional[int] = 128) -> None: +//| """Initialize `wifi.Monitor` singleton. +//| +//| :param int channel: The WiFi channel to scan. +//| :param int queue: The queue size for buffering the packet. +//| +//| """ +//| ... +//| +STATIC mp_obj_t wifi_monitor_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_channel, ARG_queue }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_channel, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_queue, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 128} }, + }; + + 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); + + if (args[ARG_channel].u_int < 1 || args[ARG_channel].u_int > 13) { + mp_raise_ValueError_varg(translate("%q out of bounds"), MP_QSTR_channel); + } + + if (args[ARG_queue].u_int < 0) { + mp_raise_ValueError_varg(translate("%q out of bounds"), MP_QSTR_channel); + } + + wifi_monitor_obj_t *self = MP_STATE_VM(wifi_monitor_singleton); + if (common_hal_wifi_monitor_deinited()) { + self = m_new_obj(wifi_monitor_obj_t); + self->base.type = &wifi_monitor_type; + common_hal_wifi_monitor_construct(self, args[ARG_channel].u_int, args[ARG_queue].u_int); + MP_STATE_VM(wifi_monitor_singleton) = self; + } + + return MP_OBJ_FROM_PTR(self); +} + +//| channel: int +//| """The WiFi channel to scan.""" +//| +STATIC mp_obj_t wifi_monitor_obj_get_channel(mp_obj_t self_in) { + return common_hal_wifi_monitor_get_channel(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_monitor_get_channel_obj, wifi_monitor_obj_get_channel); + +STATIC mp_obj_t wifi_monitor_obj_set_channel(mp_obj_t self_in, mp_obj_t channel) { + mp_int_t c = mp_obj_get_int(channel); + if (c < 1 || c > 13) { + mp_raise_ValueError_varg(translate("%q out of bounds"), MP_QSTR_channel); + } + common_hal_wifi_monitor_set_channel(self_in, c); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(wifi_monitor_set_channel_obj, wifi_monitor_obj_set_channel); + +MP_PROPERTY_GETSET(wifi_monitor_channel_obj, + (mp_obj_t)&wifi_monitor_get_channel_obj, + (mp_obj_t)&wifi_monitor_set_channel_obj); + +//| queue: int +//| """The queue size for buffering the packet.""" +//| +STATIC mp_obj_t wifi_monitor_obj_get_queue(mp_obj_t self_in) { + return common_hal_wifi_monitor_get_queue(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_monitor_get_queue_obj, wifi_monitor_obj_get_queue); + +MP_PROPERTY_GETTER(wifi_monitor_queue_obj, + (mp_obj_t)&wifi_monitor_get_queue_obj); + +//| def deinit(self) -> None: +//| """De-initialize `wifi.Monitor` singleton.""" +//| ... +//| +STATIC mp_obj_t wifi_monitor_obj_deinit(mp_obj_t self_in) { + common_hal_wifi_monitor_deinit(self_in); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wifi_monitor_deinit_obj, wifi_monitor_obj_deinit); + +//| def lost(self) -> int: +//| """Returns the packet loss count. The counter resets after each poll.""" +//| ... +//| +STATIC mp_obj_t wifi_monitor_obj_get_lost(mp_obj_t self_in) { + return common_hal_wifi_monitor_get_lost(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_monitor_lost_obj, wifi_monitor_obj_get_lost); + +//| def queued(self) -> int: +//| """Returns the packet queued count.""" +//| ... +//| +STATIC mp_obj_t wifi_monitor_obj_get_queued(mp_obj_t self_in) { + if (common_hal_wifi_monitor_deinited()) { + return mp_obj_new_int_from_uint(0); + } + return common_hal_wifi_monitor_get_queued(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_monitor_queued_obj, wifi_monitor_obj_get_queued); + +//| def packet(self) -> dict: +//| """Returns the monitor packet.""" +//| ... +//| +STATIC mp_obj_t wifi_monitor_obj_get_packet(mp_obj_t self_in) { + if (common_hal_wifi_monitor_deinited()) { + raise_deinited_error(); + } + return common_hal_wifi_monitor_get_packet(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_monitor_packet_obj, wifi_monitor_obj_get_packet); + +STATIC const mp_rom_map_elem_t wifi_monitor_locals_dict_table[] = { + // properties + { MP_ROM_QSTR(MP_QSTR_channel), MP_ROM_PTR(&wifi_monitor_channel_obj) }, + { MP_ROM_QSTR(MP_QSTR_queue), MP_ROM_PTR(&wifi_monitor_queue_obj) }, + + // functions + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&wifi_monitor_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_lost), MP_ROM_PTR(&wifi_monitor_lost_obj) }, + { MP_ROM_QSTR(MP_QSTR_queued), MP_ROM_PTR(&wifi_monitor_queued_obj) }, + { MP_ROM_QSTR(MP_QSTR_packet), MP_ROM_PTR(&wifi_monitor_packet_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(wifi_monitor_locals_dict, wifi_monitor_locals_dict_table); + +const mp_obj_type_t wifi_monitor_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Monitor, + .make_new = wifi_monitor_make_new, + .locals_dict = (mp_obj_t)&wifi_monitor_locals_dict, +}; diff --git a/circuitpython/shared-bindings/wifi/Monitor.h b/circuitpython/shared-bindings/wifi/Monitor.h new file mode 100644 index 0000000..38c52a0 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Monitor.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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_WIFI_MONITOR_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_MONITOR_H + +#include "common-hal/wifi/Monitor.h" + +const mp_obj_type_t wifi_monitor_type; + +void common_hal_wifi_monitor_construct(wifi_monitor_obj_t *self, + uint8_t channel, size_t queue); +void common_hal_wifi_monitor_deinit(wifi_monitor_obj_t *self); +bool common_hal_wifi_monitor_deinited(void); + +void common_hal_wifi_monitor_set_channel(wifi_monitor_obj_t *self, uint8_t channel); +mp_obj_t common_hal_wifi_monitor_get_channel(wifi_monitor_obj_t *self); + +mp_obj_t common_hal_wifi_monitor_get_queue(wifi_monitor_obj_t *self); + +mp_obj_t common_hal_wifi_monitor_get_lost(wifi_monitor_obj_t *self); + +mp_obj_t common_hal_wifi_monitor_get_queued(wifi_monitor_obj_t *self); + +mp_obj_t common_hal_wifi_monitor_get_packet(wifi_monitor_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_MONITOR_H diff --git a/circuitpython/shared-bindings/wifi/Network.c b/circuitpython/shared-bindings/wifi/Network.c new file mode 100644 index 0000000..9a457b9 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Network.c @@ -0,0 +1,134 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 <string.h> + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/wifi/Network.h" + +//| class Network: +//| """A wifi network provided by a nearby access point. +//| +//| """ +//| + +//| def __init__(self) -> None: +//| """You cannot create an instance of `wifi.Network`. They are returned by `wifi.Radio.start_scanning_networks`.""" +//| ... +//| + +//| ssid: str +//| """String id of the network""" +//| +STATIC mp_obj_t wifi_network_get_ssid(mp_obj_t self) { + return common_hal_wifi_network_get_ssid(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_network_get_ssid_obj, wifi_network_get_ssid); + +MP_PROPERTY_GETTER(wifi_network_ssid_obj, + (mp_obj_t)&wifi_network_get_ssid_obj); + + +//| bssid: bytes +//| """BSSID of the network (usually the AP's MAC address)""" +//| +STATIC mp_obj_t wifi_network_get_bssid(mp_obj_t self) { + return common_hal_wifi_network_get_bssid(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_network_get_bssid_obj, wifi_network_get_bssid); + +MP_PROPERTY_GETTER(wifi_network_bssid_obj, + (mp_obj_t)&wifi_network_get_bssid_obj); + + +//| rssi: int +//| """Signal strength of the network""" +//| +STATIC mp_obj_t wifi_network_get_rssi(mp_obj_t self) { + return common_hal_wifi_network_get_rssi(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_network_get_rssi_obj, wifi_network_get_rssi); + +MP_PROPERTY_GETTER(wifi_network_rssi_obj, + (mp_obj_t)&wifi_network_get_rssi_obj); + + +//| channel: int +//| """Channel number the network is operating on""" +//| +STATIC mp_obj_t wifi_network_get_channel(mp_obj_t self) { + return common_hal_wifi_network_get_channel(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_network_get_channel_obj, wifi_network_get_channel); + +MP_PROPERTY_GETTER(wifi_network_channel_obj, + (mp_obj_t)&wifi_network_get_channel_obj); + +//| country: str +//| """String id of the country code""" +//| +STATIC mp_obj_t wifi_network_get_country(mp_obj_t self) { + return common_hal_wifi_network_get_country(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_network_get_country_obj, wifi_network_get_country); + +MP_PROPERTY_GETTER(wifi_network_country_obj, + (mp_obj_t)&wifi_network_get_country_obj); + +//| authmode: str +//| """String id of the authmode""" +//| +STATIC mp_obj_t wifi_network_get_authmode(mp_obj_t self) { + return common_hal_wifi_network_get_authmode(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_network_get_authmode_obj, wifi_network_get_authmode); + +MP_PROPERTY_GETTER(wifi_network_authmode_obj, + (mp_obj_t)&wifi_network_get_authmode_obj); + +STATIC const mp_rom_map_elem_t wifi_network_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_ssid), MP_ROM_PTR(&wifi_network_ssid_obj) }, + { MP_ROM_QSTR(MP_QSTR_bssid), MP_ROM_PTR(&wifi_network_bssid_obj) }, + { MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&wifi_network_rssi_obj) }, + { MP_ROM_QSTR(MP_QSTR_channel), MP_ROM_PTR(&wifi_network_channel_obj) }, + { MP_ROM_QSTR(MP_QSTR_country), MP_ROM_PTR(&wifi_network_country_obj) }, + { MP_ROM_QSTR(MP_QSTR_authmode), MP_ROM_PTR(&wifi_network_authmode_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(wifi_network_locals_dict, wifi_network_locals_dict_table); + +const mp_obj_type_t wifi_network_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Network, + .locals_dict = (mp_obj_t)&wifi_network_locals_dict, +}; diff --git a/circuitpython/shared-bindings/wifi/Network.h b/circuitpython/shared-bindings/wifi/Network.h new file mode 100644 index 0000000..0f07e7b --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Network.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_WIFI_NETWORK_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_NETWORK_H + +#include <stdint.h> + +#include "common-hal/wifi/Network.h" + +#include "py/objstr.h" + +const mp_obj_type_t wifi_network_type; + +extern mp_obj_t common_hal_wifi_network_get_ssid(wifi_network_obj_t *self); +extern mp_obj_t common_hal_wifi_network_get_bssid(wifi_network_obj_t *self); +extern mp_obj_t common_hal_wifi_network_get_rssi(wifi_network_obj_t *self); +extern mp_obj_t common_hal_wifi_network_get_channel(wifi_network_obj_t *self); +extern mp_obj_t common_hal_wifi_network_get_country(wifi_network_obj_t *self); +extern mp_obj_t common_hal_wifi_network_get_authmode(wifi_network_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_NETWORK_H diff --git a/circuitpython/shared-bindings/wifi/Packet.c b/circuitpython/shared-bindings/wifi/Packet.c new file mode 100644 index 0000000..d21c8b0 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Packet.c @@ -0,0 +1,70 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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 "py/enum.h" + +#include "shared-bindings/wifi/Packet.h" + +MAKE_ENUM_VALUE(wifi_packet_type, packet, CH, PACKET_CH); +MAKE_ENUM_VALUE(wifi_packet_type, packet, LEN, PACKET_LEN); +MAKE_ENUM_VALUE(wifi_packet_type, packet, RAW, PACKET_RAW); +MAKE_ENUM_VALUE(wifi_packet_type, packet, RSSI, PACKET_RSSI); + +//| class Packet: +//| """The packet parameters.""" +//| +//| CH: object +//| """The packet's channel.""" +//| +//| LEN: object +//| """The packet's length.""" +//| +//| RAW: object +//| """The packet's payload.""" +//| +//| RSSI: object +//| """The packet's rssi.""" +//| +MAKE_ENUM_MAP(wifi_packet) { + MAKE_ENUM_MAP_ENTRY(packet, CH), + MAKE_ENUM_MAP_ENTRY(packet, LEN), + MAKE_ENUM_MAP_ENTRY(packet, RAW), + MAKE_ENUM_MAP_ENTRY(packet, RSSI), +}; +STATIC MP_DEFINE_CONST_DICT(wifi_packet_locals_dict, wifi_packet_locals_table); + +MAKE_PRINTER(wifi, wifi_packet); + +const mp_obj_type_t wifi_packet_type = { + { &mp_type_type }, + .name = MP_QSTR_Packet, + .print = wifi_packet_print, + .locals_dict = (mp_obj_t)&wifi_packet_locals_dict, + .flags = MP_TYPE_FLAG_EXTENDED, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = mp_generic_unary_op, + ), +}; diff --git a/circuitpython/shared-bindings/wifi/Packet.h b/circuitpython/shared-bindings/wifi/Packet.h new file mode 100644 index 0000000..09dfddf --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Packet.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 microDev + * + * 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_WIFI_PACKET_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_PACKET_H + +#include "py/enum.h" + +typedef enum { + PACKET_CH, + PACKET_LEN, + PACKET_RAW, + PACKET_RSSI, +} wifi_packet_t; + +extern const mp_obj_type_t wifi_packet_type; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_PACKET_H diff --git a/circuitpython/shared-bindings/wifi/Radio.c b/circuitpython/shared-bindings/wifi/Radio.c new file mode 100644 index 0000000..cf1e9df --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Radio.c @@ -0,0 +1,542 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "shared-bindings/wifi/__init__.h" +#include "shared-bindings/wifi/AuthMode.h" + +#include <regex.h> +#include <string.h> + +#include "py/runtime.h" +#include "py/objproperty.h" + +#define MAC_ADDRESS_LENGTH 6 + +//| class Radio: +//| """Native wifi radio. +//| +//| This class manages the station and access point functionality of the native +//| Wifi radio. +//| +//| """ +//| + +//| def __init__(self) -> None: +//| """You cannot create an instance of `wifi.Radio`. +//| Use `wifi.radio` to access the sole instance available.""" +//| ... +//| + +//| enabled: bool +//| """``True`` when the wifi radio is enabled. +//| If you set the value to ``False``, any open sockets will be closed. +//| """ +//| +STATIC mp_obj_t wifi_radio_get_enabled(mp_obj_t self) { + return mp_obj_new_bool(common_hal_wifi_radio_get_enabled(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_enabled_obj, wifi_radio_get_enabled); + +static mp_obj_t wifi_radio_set_enabled(mp_obj_t self, mp_obj_t value) { + const bool enabled = mp_obj_is_true(value); + + common_hal_wifi_radio_set_enabled(self, enabled); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(wifi_radio_set_enabled_obj, wifi_radio_set_enabled); + +MP_PROPERTY_GETSET(wifi_radio_enabled_obj, + (mp_obj_t)&wifi_radio_get_enabled_obj, + (mp_obj_t)&wifi_radio_set_enabled_obj); + +//| hostname: Union[str | ReadableBuffer] +//| """Hostname for wifi interface. When the hostname is altered after interface started/connected +//| the changes would only be reflected once the interface restarts/reconnects.""" +//| +STATIC mp_obj_t wifi_radio_get_hostname(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_wifi_radio_get_hostname(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_hostname_obj, wifi_radio_get_hostname); + +STATIC mp_obj_t wifi_radio_set_hostname(mp_obj_t self_in, mp_obj_t hostname_in) { + mp_buffer_info_t hostname; + mp_get_buffer_raise(hostname_in, &hostname, MP_BUFFER_READ); + + if (hostname.len < 1 || hostname.len > 253) { + mp_raise_ValueError(translate("Hostname must be between 1 and 253 characters")); + } + + #ifndef CONFIG_IDF_TARGET_ESP32C3 + regex_t regex; // validate hostname according to RFC 1123 + regcomp(®ex,"^(([a-z0-9]|[a-z0-9][a-z0-9\\-]{0,61}[a-z0-9])\\.)*([a-z0-9]|[a-z0-9][a-z0-9\\-]{0,61}[a-z0-9])$", REG_EXTENDED | REG_ICASE | REG_NOSUB); + if (regexec(®ex, hostname.buf, 0, NULL, 0)) { + mp_raise_ValueError(translate("invalid hostname")); + } + regfree(®ex); + #endif + + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_wifi_radio_set_hostname(self, hostname.buf); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(wifi_radio_set_hostname_obj, wifi_radio_set_hostname); + +MP_PROPERTY_GETSET(wifi_radio_hostname_obj, + (mp_obj_t)&wifi_radio_get_hostname_obj, + (mp_obj_t)&wifi_radio_set_hostname_obj); + +//| mac_address: ReadableBuffer +//| """MAC address for the station. When the address is altered after interface is connected +//| the changes would only be reflected once the interface reconnects.""" +//| +STATIC mp_obj_t wifi_radio_get_mac_address(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(common_hal_wifi_radio_get_mac_address(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_mac_address_obj, wifi_radio_get_mac_address); + +STATIC mp_obj_t wifi_radio_set_mac_address(mp_obj_t self_in, mp_obj_t mac_address_in) { + mp_buffer_info_t mac_address; + mp_get_buffer_raise(mac_address_in, &mac_address, MP_BUFFER_READ); + + if (mac_address.len != MAC_ADDRESS_LENGTH) { + mp_raise_ValueError(translate("Invalid MAC address")); + } + + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_wifi_radio_set_mac_address(self, mac_address.buf); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(wifi_radio_set_mac_address_obj, wifi_radio_set_mac_address); + +MP_PROPERTY_GETSET(wifi_radio_mac_address_obj, + (mp_obj_t)&wifi_radio_get_mac_address_obj, + (mp_obj_t)&wifi_radio_set_mac_address_obj); + +//| mac_address_ap: ReadableBuffer +//| """MAC address for the AP. When the address is altered after interface is started +//| the changes would only be reflected once the interface restarts.""" +//| +STATIC mp_obj_t wifi_radio_get_mac_address_ap(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(common_hal_wifi_radio_get_mac_address_ap(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_mac_address_ap_obj, wifi_radio_get_mac_address_ap); + +STATIC mp_obj_t wifi_radio_set_mac_address_ap(mp_obj_t self_in, mp_obj_t mac_address_in) { + mp_buffer_info_t mac_address; + mp_get_buffer_raise(mac_address_in, &mac_address, MP_BUFFER_READ); + + if (mac_address.len != MAC_ADDRESS_LENGTH) { + mp_raise_ValueError(translate("Invalid MAC address")); + } + + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_wifi_radio_set_mac_address_ap(self, mac_address.buf); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(wifi_radio_set_mac_address_ap_obj, wifi_radio_set_mac_address_ap); + +MP_PROPERTY_GETSET(wifi_radio_mac_address_ap_obj, + (mp_obj_t)&wifi_radio_get_mac_address_ap_obj, + (mp_obj_t)&wifi_radio_set_mac_address_ap_obj); + +//| def start_scanning_networks(self, *, start_channel: int = 1, stop_channel: int = 11) -> Iterable[Network]: +//| """Scans for available wifi networks over the given channel range. Make sure the channels are allowed in your country.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_start_scanning_networks(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return common_hal_wifi_radio_start_scanning_networks(self); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_start_scanning_networks_obj, wifi_radio_start_scanning_networks); + +//| def stop_scanning_networks(self) -> None: +//| """Stop scanning for Wifi networks and free any resources used to do it.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_stop_scanning_networks(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_wifi_radio_stop_scanning_networks(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_scanning_networks_obj, wifi_radio_stop_scanning_networks); + +//| def start_station(self) -> None: +//| """Starts a Station.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_start_station(mp_obj_t self) { + common_hal_wifi_radio_start_station(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_start_station_obj, wifi_radio_start_station); + +//| def stop_station(self) -> None: +//| """Stops the Station.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_stop_station(mp_obj_t self) { + common_hal_wifi_radio_stop_station(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); + +//| def start_ap(self, +//| ssid: Union[str | ReadableBuffer], +//| password: Union[str | ReadableBuffer] = "", +//| *, +//| channel: Optional[int] = 1, +//| authmode: Optional[AuthMode], +//| max_connections: Optional[int] = 4) -> None: +//| """Starts an Access Point with the specified ssid and password. +//| +//| If ``channel`` is given, the access point will use that channel unless +//| a station is already operating on a different channel. +//| +//| If ``authmode`` is given, the access point will use that Authentication +//| mode. If a password is given, ``authmode`` must not be ``OPEN``. +//| If ``authmode`` isn't given, ``OPEN`` will be used when password isn't provided, +//| otherwise ``WPA_WPA2_PSK``. +//| +//| If ``max_connections`` is given, the access point will allow up to +//| that number of stations to connect.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_start_ap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_ssid, ARG_password, ARG_channel, ARG_authmode, ARG_max_connections }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_password, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_authmode, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_max_connections, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 4} }, + }; + + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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 authmode = 0; + if (args[ARG_authmode].u_obj != MP_OBJ_NULL) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t item, iterable = mp_getiter(args[ARG_authmode].u_obj, &iter_buf); + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + authmode |= (1 << (wifi_authmode_t)cp_enum_value(&wifi_authmode_type, item)); + } + } + + mp_buffer_info_t ssid; + mp_get_buffer_raise(args[ARG_ssid].u_obj, &ssid, MP_BUFFER_READ); + + mp_buffer_info_t password; + password.len = 0; + if (args[ARG_password].u_obj != MP_OBJ_NULL) { + if (authmode == 1) { + mp_raise_ValueError(translate("AuthMode.OPEN is not used with password")); + } else if (authmode == 0) { + authmode = (1 << AUTHMODE_WPA) | (1 << AUTHMODE_WPA2) | (1 << AUTHMODE_PSK); + } + mp_get_buffer_raise(args[ARG_password].u_obj, &password, MP_BUFFER_READ); + if (password.len > 0 && (password.len < 8 || password.len > 63)) { + mp_raise_ValueError(translate("WiFi password must be between 8 and 63 characters")); + } + } else { + authmode = 1; + } + + common_hal_wifi_radio_start_ap(self, ssid.buf, ssid.len, password.buf, password.len, args[ARG_channel].u_int, authmode, args[ARG_max_connections].u_int); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_start_ap_obj, 1, wifi_radio_start_ap); + +//| def stop_ap(self) -> None: +//| """Stops the Access Point.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_stop_ap(mp_obj_t self) { + common_hal_wifi_radio_stop_ap(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_ap_obj, wifi_radio_stop_ap); + +//| def connect(self, +//| ssid: Union[str | ReadableBuffer], +//| password: Union[str | ReadableBuffer] = "", +//| *, +//| channel: Optional[int] = 0, +//| bssid: Optional[Union[str | ReadableBuffer]] = "", +//| timeout: Optional[float] = None) -> None: +//| """Connects to the given ssid and waits for an ip address. Reconnections are handled +//| automatically once one connection succeeds. +//| +//| By default, this will scan all channels and connect to the access point (AP) with the +//| given ``ssid`` and greatest signal strength (rssi). +//| +//| If ``channel`` is given, the scan will begin with the given channel and connect to +//| the first AP with the given ``ssid``. This can speed up the connection time +//| significantly because a full scan doesn't occur. +//| +//| If ``bssid`` is given, the scan will start at the first channel or the one given and +//| connect to the AP with the given ``bssid`` and ``ssid``.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_ssid, ARG_password, ARG_channel, ARG_bssid, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_password, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bssid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_float_t timeout = 0; + if (args[ARG_timeout].u_obj != mp_const_none) { + timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + } + + mp_buffer_info_t ssid; + ssid.len = 0; + mp_get_buffer_raise(args[ARG_ssid].u_obj, &ssid, MP_BUFFER_READ); + if (ssid.len > 32) { + mp_raise_ValueError(translate("ssid can't be more than 32 bytes")); + } + + mp_buffer_info_t password; + password.len = 0; + if (args[ARG_password].u_obj != MP_OBJ_NULL) { + mp_get_buffer_raise(args[ARG_password].u_obj, &password, MP_BUFFER_READ); + if (password.len > 0 && (password.len < 8 || password.len > 63)) { + mp_raise_ValueError(translate("WiFi password must be between 8 and 63 characters")); + } + } + + #define MAC_ADDRESS_LENGTH 6 + + mp_buffer_info_t bssid; + bssid.len = 0; + // Should probably make sure bssid is just bytes and not something else too + if (args[ARG_bssid].u_obj != MP_OBJ_NULL) { + mp_get_buffer_raise(args[ARG_bssid].u_obj, &bssid, MP_BUFFER_READ); + if (bssid.len != MAC_ADDRESS_LENGTH) { + mp_raise_ValueError(translate("Invalid BSSID")); + } + } + + wifi_radio_error_t error = common_hal_wifi_radio_connect(self, ssid.buf, ssid.len, password.buf, password.len, args[ARG_channel].u_int, timeout, bssid.buf, bssid.len); + if (error == WIFI_RADIO_ERROR_AUTH_FAIL) { + mp_raise_ConnectionError(translate("Authentication failure")); + } else if (error == WIFI_RADIO_ERROR_NO_AP_FOUND) { + mp_raise_ConnectionError(translate("No network with that ssid")); + } else if (error != WIFI_RADIO_ERROR_NONE) { + mp_raise_msg_varg(&mp_type_ConnectionError, translate("Unknown failure %d"), error); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_connect_obj, 1, wifi_radio_connect); + +//| ipv4_gateway: Optional[ipaddress.IPv4Address] +//| """IP v4 Address of the station gateway when connected to an access point. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ipv4_gateway(mp_obj_t self) { + return common_hal_wifi_radio_get_ipv4_gateway(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_gateway_obj, wifi_radio_get_ipv4_gateway); + +MP_PROPERTY_GETTER(wifi_radio_ipv4_gateway_obj, + (mp_obj_t)&wifi_radio_get_ipv4_gateway_obj); + +//| ipv4_gateway_ap: Optional[ipaddress.IPv4Address] +//| """IP v4 Address of the access point gateway, when enabled. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ipv4_gateway_ap(mp_obj_t self) { + return common_hal_wifi_radio_get_ipv4_gateway_ap(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_gateway_ap_obj, wifi_radio_get_ipv4_gateway_ap); + +MP_PROPERTY_GETTER(wifi_radio_ipv4_gateway_ap_obj, + (mp_obj_t)&wifi_radio_get_ipv4_gateway_ap_obj); + +//| ipv4_subnet: Optional[ipaddress.IPv4Address] +//| """IP v4 Address of the station subnet when connected to an access point. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ipv4_subnet(mp_obj_t self) { + return common_hal_wifi_radio_get_ipv4_subnet(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_subnet_obj, wifi_radio_get_ipv4_subnet); + +MP_PROPERTY_GETTER(wifi_radio_ipv4_subnet_obj, + (mp_obj_t)&wifi_radio_get_ipv4_subnet_obj); + +//| ipv4_subnet_ap: Optional[ipaddress.IPv4Address] +//| """IP v4 Address of the access point subnet, when enabled. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ipv4_subnet_ap(mp_obj_t self) { + return common_hal_wifi_radio_get_ipv4_subnet_ap(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_subnet_ap_obj, wifi_radio_get_ipv4_subnet_ap); + +MP_PROPERTY_GETTER(wifi_radio_ipv4_subnet_ap_obj, + (mp_obj_t)&wifi_radio_get_ipv4_subnet_ap_obj); + +//| ipv4_address: Optional[ipaddress.IPv4Address] +//| """IP v4 Address of the station when connected to an access point. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ipv4_address(mp_obj_t self) { + return common_hal_wifi_radio_get_ipv4_address(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_address_obj, wifi_radio_get_ipv4_address); + +MP_PROPERTY_GETTER(wifi_radio_ipv4_address_obj, + (mp_obj_t)&wifi_radio_get_ipv4_address_obj); + +//| ipv4_address_ap: Optional[ipaddress.IPv4Address] +//| """IP v4 Address of the access point, when enabled. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ipv4_address_ap(mp_obj_t self) { + return common_hal_wifi_radio_get_ipv4_address_ap(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_address_ap_obj, wifi_radio_get_ipv4_address_ap); + +MP_PROPERTY_GETTER(wifi_radio_ipv4_address_ap_obj, + (mp_obj_t)&wifi_radio_get_ipv4_address_ap_obj); + +//| ipv4_dns: Optional[ipaddress.IPv4Address] +//| """IP v4 Address of the DNS server in use when connected to an access point. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ipv4_dns(mp_obj_t self) { + return common_hal_wifi_radio_get_ipv4_dns(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ipv4_dns_obj, wifi_radio_get_ipv4_dns); + +MP_PROPERTY_GETTER(wifi_radio_ipv4_dns_obj, + (mp_obj_t)&wifi_radio_get_ipv4_dns_obj); + +//| ap_info: Optional[Network] +//| """Network object containing BSSID, SSID, authmode, channel, country and RSSI when connected to an access point. None otherwise.""" +//| +STATIC mp_obj_t wifi_radio_get_ap_info(mp_obj_t self) { + return common_hal_wifi_radio_get_ap_info(self); + +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ap_info_obj, wifi_radio_get_ap_info); + +MP_PROPERTY_GETTER(wifi_radio_ap_info_obj, + (mp_obj_t)&wifi_radio_get_ap_info_obj); + +//| def ping(self, ip: ipaddress.IPv4Address, *, timeout: Optional[float] = 0.5) -> Optional[float]: +//| """Ping an IP to test connectivity. Returns echo time in seconds. +//| Returns None when it times out.""" +//| ... +//| +STATIC mp_obj_t wifi_radio_ping(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_ip, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ip, MP_ARG_REQUIRED | MP_ARG_OBJ, }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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_float_t timeout = 0.5; + if (args[ARG_timeout].u_obj != mp_const_none) { + timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + } + + mp_int_t time_ms = common_hal_wifi_radio_ping(self, args[ARG_ip].u_obj, timeout); + if (time_ms == -1) { + return mp_const_none; + } + + return mp_obj_new_float(time_ms / 1000.0); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_ping_obj, 1, wifi_radio_ping); + +STATIC const mp_rom_map_elem_t wifi_radio_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&wifi_radio_enabled_obj) }, + + { MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&wifi_radio_hostname_obj) }, + + { MP_ROM_QSTR(MP_QSTR_mac_address), MP_ROM_PTR(&wifi_radio_mac_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_mac_address_ap), MP_ROM_PTR(&wifi_radio_mac_address_ap_obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_scanning_networks), MP_ROM_PTR(&wifi_radio_start_scanning_networks_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_scanning_networks), MP_ROM_PTR(&wifi_radio_stop_scanning_networks_obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_station), MP_ROM_PTR(&wifi_radio_start_station_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_station), MP_ROM_PTR(&wifi_radio_stop_station_obj) }, + + { MP_ROM_QSTR(MP_QSTR_start_ap), MP_ROM_PTR(&wifi_radio_start_ap_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop_ap), MP_ROM_PTR(&wifi_radio_stop_ap_obj) }, + + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&wifi_radio_connect_obj) }, + // { MP_ROM_QSTR(MP_QSTR_connect_to_enterprise), MP_ROM_PTR(&wifi_radio_connect_to_enterprise_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ap_info), MP_ROM_PTR(&wifi_radio_ap_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipv4_dns), MP_ROM_PTR(&wifi_radio_ipv4_dns_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipv4_gateway), MP_ROM_PTR(&wifi_radio_ipv4_gateway_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipv4_gateway_ap), MP_ROM_PTR(&wifi_radio_ipv4_gateway_ap_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipv4_subnet), MP_ROM_PTR(&wifi_radio_ipv4_subnet_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipv4_subnet_ap), MP_ROM_PTR(&wifi_radio_ipv4_subnet_ap_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipv4_address), MP_ROM_PTR(&wifi_radio_ipv4_address_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipv4_address_ap), MP_ROM_PTR(&wifi_radio_ipv4_address_ap_obj) }, + + // { MP_ROM_QSTR(MP_QSTR_access_point_active), MP_ROM_PTR(&wifi_radio_access_point_active_obj) }, + // { MP_ROM_QSTR(MP_QSTR_start_access_point), MP_ROM_PTR(&wifi_radio_start_access_point_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ping), MP_ROM_PTR(&wifi_radio_ping_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(wifi_radio_locals_dict, wifi_radio_locals_dict_table); + +const mp_obj_type_t wifi_radio_type = { + .base = { &mp_type_type }, + .name = MP_QSTR_Radio, + .locals_dict = (mp_obj_t)&wifi_radio_locals_dict, +}; diff --git a/circuitpython/shared-bindings/wifi/Radio.h b/circuitpython/shared-bindings/wifi/Radio.h new file mode 100644 index 0000000..cee9f6e --- /dev/null +++ b/circuitpython/shared-bindings/wifi/Radio.h @@ -0,0 +1,107 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_WIFI_RADIO_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_RADIO_H + +#include <stdint.h> + +#include "common-hal/wifi/Radio.h" + +#include "py/objstr.h" + +const mp_obj_type_t wifi_radio_type; + +typedef enum { + // 0 is circuitpython-specific; 1-53 are IEEE; 200+ are Espressif + WIFI_RADIO_ERROR_NONE = 0, + WIFI_RADIO_ERROR_UNSPECIFIED = 1, + WIFI_RADIO_ERROR_AUTH_EXPIRE = 2, + WIFI_RADIO_ERROR_AUTH_LEAVE = 3, + WIFI_RADIO_ERROR_ASSOC_EXPIRE = 4, + WIFI_RADIO_ERROR_ASSOC_TOOMANY = 5, + WIFI_RADIO_ERROR_NOT_AUTHED = 6, + WIFI_RADIO_ERROR_NOT_ASSOCED = 7, + WIFI_RADIO_ERROR_ASSOC_LEAVE = 8, + WIFI_RADIO_ERROR_ASSOC_NOT_AUTHED = 9, + WIFI_RADIO_ERROR_DISASSOC_PWRCAP_BAD = 10, + WIFI_RADIO_ERROR_DISASSOC_SUPCHAN_BAD = 11, + WIFI_RADIO_ERROR_IE_INVALID = 13, + WIFI_RADIO_ERROR_MIC_FAILURE = 14, + WIFI_RADIO_ERROR_4WAY_HANDSHAKE_TIMEOUT = 15, + WIFI_RADIO_ERROR_GROUP_KEY_UPDATE_TIMEOUT = 16, + WIFI_RADIO_ERROR_IE_IN_4WAY_DIFFERS = 17, + WIFI_RADIO_ERROR_GROUP_CIPHER_INVALID = 18, + WIFI_RADIO_ERROR_PAIRWISE_CIPHER_INVALID = 19, + WIFI_RADIO_ERROR_AKMP_INVALID = 20, + WIFI_RADIO_ERROR_UNSUPP_RSN_IE_VERSION = 21, + WIFI_RADIO_ERROR_INVALID_RSN_IE_CAP = 22, + WIFI_RADIO_ERROR_802_1X_AUTH_FAILED = 23, + WIFI_RADIO_ERROR_CIPHER_SUITE_REJECTED = 24, + WIFI_RADIO_ERROR_INVALID_PMKID = 53, + WIFI_RADIO_ERROR_BEACON_TIMEOUT = 200, + WIFI_RADIO_ERROR_NO_AP_FOUND = 201, + WIFI_RADIO_ERROR_AUTH_FAIL = 202, + WIFI_RADIO_ERROR_ASSOC_FAIL = 203, + WIFI_RADIO_ERROR_HANDSHAKE_TIMEOUT = 204, + WIFI_RADIO_ERROR_CONNECTION_FAIL = 205, + WIFI_RADIO_ERROR_AP_TSF_RESET = 206, +} wifi_radio_error_t; + +extern bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled); + +extern mp_obj_t common_hal_wifi_radio_get_hostname(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_set_hostname(wifi_radio_obj_t *self, const char *hostname); + +extern mp_obj_t common_hal_wifi_radio_get_mac_address(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_set_mac_address(wifi_radio_obj_t *self, const uint8_t *mac); +extern mp_obj_t common_hal_wifi_radio_get_mac_address_ap(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_set_mac_address_ap(wifi_radio_obj_t *self, const uint8_t *mac); + +extern mp_obj_t common_hal_wifi_radio_start_scanning_networks(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_stop_scanning_networks(wifi_radio_obj_t *self); + +extern void common_hal_wifi_radio_start_station(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_stop_station(wifi_radio_obj_t *self); + +extern void common_hal_wifi_radio_start_ap(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, uint8_t authmode, uint8_t max_connections); +extern void common_hal_wifi_radio_stop_ap(wifi_radio_obj_t *self); + +extern wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, mp_float_t timeout, uint8_t *bssid, size_t bssid_len); + +extern mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self); +extern mp_obj_t common_hal_wifi_radio_get_ipv4_dns(wifi_radio_obj_t *self); +extern mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self); +extern mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self); +extern mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self); +extern mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self); +extern mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self); +extern mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self); + +extern mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_RADIO_H diff --git a/circuitpython/shared-bindings/wifi/ScannedNetworks.c b/circuitpython/shared-bindings/wifi/ScannedNetworks.c new file mode 100644 index 0000000..0706d8f --- /dev/null +++ b/circuitpython/shared-bindings/wifi/ScannedNetworks.c @@ -0,0 +1,72 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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 <string.h> + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/wifi/ScannedNetworks.h" + +//| class ScannedNetworks: +//| """Iterates over all `wifi.Network` objects found while scanning. This object is always created +//| by a `wifi.Radio`: it has no user-visible constructor.""" +//| +STATIC mp_obj_t scannednetworks_iternext(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &wifi_scannednetworks_type)); + wifi_scannednetworks_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t network = common_hal_wifi_scannednetworks_next(self); + if (network != mp_const_none) { + return network; + } + + return MP_OBJ_STOP_ITERATION; +} + +//| def __init__(self) -> None: +//| """Cannot be instantiated directly. Use `wifi.Radio.start_scanning_networks`.""" +//| ... +//| +//| def __iter__(self) -> Iterator[Network]: +//| """Returns itself since it is the iterator.""" +//| ... +//| +//| def __next__(self) -> Network: +//| """Returns the next `wifi.Network`. +//| Raises `StopIteration` if scanning is finished and no other results are available.""" +//| ... +//| + +const mp_obj_type_t wifi_scannednetworks_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_ScannedNetworks, + MP_TYPE_EXTENDED_FIELDS( + .getiter = mp_identity_getiter, + .iternext = scannednetworks_iternext, + ) +}; diff --git a/circuitpython/shared-bindings/wifi/ScannedNetworks.h b/circuitpython/shared-bindings/wifi/ScannedNetworks.h new file mode 100644 index 0000000..8e0aa43 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/ScannedNetworks.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * Copyright (c) 2018 Artur Pacholec + * Copyright (c) 2017 Glenn Ruben Bakke + * + * 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_WIFI_SCANNEDNETWORKS_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_SCANNEDNETWORKS_H + +#include "py/obj.h" +#include "common-hal/wifi/ScannedNetworks.h" + +extern const mp_obj_type_t wifi_scannednetworks_type; + +mp_obj_t common_hal_wifi_scannednetworks_next(wifi_scannednetworks_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI_SCANNEDNETWORKS_H diff --git a/circuitpython/shared-bindings/wifi/__init__.c b/circuitpython/shared-bindings/wifi/__init__.c new file mode 100644 index 0000000..0f9dee8 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/__init__.c @@ -0,0 +1,74 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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 "shared-bindings/wifi/__init__.h" +#include "shared-bindings/wifi/AuthMode.h" +#include "shared-bindings/wifi/Network.h" +#include "shared-bindings/wifi/Monitor.h" +#include "shared-bindings/wifi/Packet.h" +#include "shared-bindings/wifi/Radio.h" + +//| """ +//| The `wifi` module provides necessary low-level functionality for managing +//| wifi connections. Use `socketpool` for communicating over the network.""" +//| +//| radio: Radio +//| """Wifi radio used to manage both station and AP modes. +//| This object is the sole instance of `wifi.Radio`.""" +//| + +// Called when wifi is imported. +STATIC mp_obj_t wifi___init__(void) { + common_hal_wifi_init(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(wifi___init___obj, wifi___init__); + +STATIC const mp_rom_map_elem_t wifi_module_globals_table[] = { + // Name + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_wifi) }, + + // Initialization + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&wifi___init___obj) }, + + // Classes + { MP_ROM_QSTR(MP_QSTR_AuthMode), MP_ROM_PTR(&wifi_authmode_type) }, + { MP_ROM_QSTR(MP_QSTR_Monitor), MP_ROM_PTR(&wifi_monitor_type) }, + { MP_ROM_QSTR(MP_QSTR_Network), MP_ROM_PTR(&wifi_network_type) }, + { MP_ROM_QSTR(MP_QSTR_Packet), MP_ROM_PTR(&wifi_packet_type) }, + { MP_ROM_QSTR(MP_QSTR_Radio), MP_ROM_PTR(&wifi_radio_type) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_radio), MP_ROM_PTR(&common_hal_wifi_radio_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(wifi_module_globals, wifi_module_globals_table); + +const mp_obj_module_t wifi_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&wifi_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_wifi, wifi_module, CIRCUITPY_WIFI); diff --git a/circuitpython/shared-bindings/wifi/__init__.h b/circuitpython/shared-bindings/wifi/__init__.h new file mode 100644 index 0000000..e626727 --- /dev/null +++ b/circuitpython/shared-bindings/wifi/__init__.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 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_WIFI___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_WIFI___INIT___H + +#include "shared-bindings/wifi/Radio.h" + +extern wifi_radio_obj_t common_hal_wifi_radio_obj; + +void common_hal_wifi_init(void); +void common_hal_wifi_gc_collect(void); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_WIFI___INIT___H diff --git a/circuitpython/shared-bindings/zlib/__init__.c b/circuitpython/shared-bindings/zlib/__init__.c new file mode 100644 index 0000000..65bec24 --- /dev/null +++ b/circuitpython/shared-bindings/zlib/__init__.c @@ -0,0 +1,95 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Mark Komus + * + * 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 <assert.h> +#include <string.h> + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "py/builtin.h" +#include "py/objtuple.h" +#include "py/binary.h" +#include "py/parsenum.h" + +#include "shared-bindings/zlib/__init__.h" + +#include "supervisor/shared/translate.h" + +//| """zlib decompression functionality +//| +//| The `zlib` module allows limited functionality similar to the CPython zlib library. +//| This module allows to decompress binary data compressed with DEFLATE algorithm +//| (commonly used in zlib library and gzip archiver). Compression is not yet implemented.""" +//| + +//| def zlib_decompress(data: bytes, wbits: Optional[int] = 0, bufsize: Optional[int] = 0) -> bytes: +//| """Return decompressed *data* as bytes. *wbits* is DEFLATE dictionary window +//| size used during compression (8-15, the dictionary size is power of 2 of +//| that value). Additionally, if value is positive, *data* is assumed to be +//| zlib stream (with zlib header). Otherwise, if it's negative, it's assumed +//| to be raw DEFLATE stream. +//| +//| The wbits parameter controls the size of the history buffer (or “window size”), and what header +//| and trailer format is expected. +//| +//| Common wbits values: +//| +//| * To decompress deflate format, use wbits = -15 +//| * To decompress zlib format, use wbits = 15 +//| * To decompress gzip format, use wbits = 31 +//| +//| :param bytes data: data to be decompressed +//| :param int wbits: DEFLATE dictionary window size used during compression. See above. +//| :param int bufsize: ignored for compatibility with CPython only +//| """ +//| ... +//| +STATIC mp_obj_t zlib_decompress(size_t n_args, const mp_obj_t *args) { + bool is_zlib = true; + if (n_args > 1 && MP_OBJ_SMALL_INT_VALUE(args[1]) < 0) { + is_zlib = false; + } + + return common_hal_zlib_decompress(args[0], is_zlib); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zlib_decompress_obj, 1, 3, zlib_decompress); + +STATIC const mp_rom_map_elem_t zlib_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zlib) }, + { MP_ROM_QSTR(MP_QSTR_decompress), MP_ROM_PTR(&zlib_decompress_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(zlib_globals, zlib_globals_table); + +const mp_obj_module_t zlib_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&zlib_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_zlib, zlib_module, CIRCUITPY_ZLIB); diff --git a/circuitpython/shared-bindings/zlib/__init__.h b/circuitpython/shared-bindings/zlib/__init__.h new file mode 100644 index 0000000..232d08e --- /dev/null +++ b/circuitpython/shared-bindings/zlib/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Mark Komus + * + * 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_ZLIB___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ZLIB___INIT___H + +mp_obj_t common_hal_zlib_decompress(mp_obj_t data, bool is_zlib); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ZLIB___INIT___H |