diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 83726922a5..c5039f58b5 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,6 @@ +# tools/gen-cpydiff.py: Fix formatting of doc strings for new Black. +0f78c36c5aa458a954eed39a46942209107a553e + # tests/run-tests.py: Reformat with Black. 2a38d7103672580882fb621a5b76e8d26805d593 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 65f61ebb5f..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: 'bug' -assignees: '' - ---- - - - -**Firmware** - - - -```python -Adafruit CircuitPython 6.2.0-beta.2 on 2021-03-01; Raspberry Pi Pico with rp2040 -``` - -**Code/REPL** - - - -```python -import busio, bitbangio -i2c = bitbangio.I2C(board.GP1, board.GP0) -``` - -**Behavior** - - - -```python -Traceback (most recent call last): - File "", line 1, in -TimeoutError: Clock stretch too long -``` - -**Description** - - - -- Error while using i2c... -- Only happens when... -- might be related to #4291... - -**Additional Info** - - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..7b6a53dbba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,63 @@ +name: 🐞 Bug Report +description: Create a bug report to help us improve +labels: + - bug +body: + - type: markdown + attributes: + value: >- + Thanks! for testing out CircuitPython. Now that you have encountered a + bug... you can file a report for it. + - type: textarea + id: firmware + attributes: + label: CircuitPython version + description: >- + Include the version of CircuitPython you're running. You can see it in + the `boot_out.txt` file, as well as in the `REPL`. + placeholder: Adafruit CircuitPython 6.2.0 on 2021-03-01; Raspberry Pi Pico with rp2040 + render: python + validations: + required: true + - type: textarea + id: code + attributes: + label: Code/REPL + description: This is automatically rendered as Python, so no need for backticks. + placeholder: | + import busio, bitbangio + i2c = bitbangio.I2C(board.GP1, board.GP0) + render: python + validations: + required: true + - type: textarea + id: behavior + attributes: + label: Behavior + description: What happens when you run the code above? Include error messages (if any). + placeholder: | + ```python + Traceback (most recent call last): + File "", line 1, in + TimeoutError: Clock stretch too long + ``` + On-board led pulses red. + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Optionally, describe the bug in more detail. + placeholder: | + - Error while using i2c... + - Only happens when... + - might be related to #4291... + - type: textarea + id: more-info + attributes: + label: Additional information + description: >- + Optionally, add any other information like hardware connection, scope + output etc. If you have already done some debugging, mention it here. + placeholder: Removing [this](url) line resolves the issue. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 73bf24af0c..2990e0d4b0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ contact_links: - - name: Adafruit Forum + - name: 🔗 Adafruit Forum url: https://forums.adafruit.com/ about: Official Adafruit technical support forum. Good for getting help on getting a project working. - - name: Adafruit Discord + - name: 🔗 Adafruit Discord url: https://adafru.it/discord about: Unofficial chat with many helpful folks and normally prompt replies. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 4f65b84145..87349ee00c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,5 +1,5 @@ --- -name: Feature request +name: 🚀 Feature Request about: Suggest an idea for this project title: '' labels: 'enhancement' @@ -7,5 +7,5 @@ assignees: '' --- - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 33cd4ece2e..b91f39cebf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -132,8 +132,9 @@ jobs: TWINE_USERNAME: ${{ secrets.pypi_username }} TWINE_PASSWORD: ${{ secrets.pypi_password }} run: | - echo "Uploading dev release to PyPi" - twine upload circuitpython-stubs/dist/* + # setup.py sdist was run by 'make stubs' + if [ -z "$TWINE_USERNAME" ] || echo "Uploading dev release to PyPi" + if [ -z "$TWINE_USERNAME" ] || twine upload circuitpython-stubs/dist/* mpy-cross-mac: runs-on: macos-10.15 diff --git a/.gitmodules b/.gitmodules index 55fce0eebb..382d9d92ec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -153,6 +153,7 @@ [submodule "ports/esp32s2/esp-idf"] path = ports/esp32s2/esp-idf url = https://github.com/adafruit/esp-idf.git + branch = circuitpython-v4.3 [submodule "ports/esp32s2/certificates/nina-fw"] path = ports/esp32s2/certificates/nina-fw url = https://github.com/adafruit/nina-fw.git diff --git a/conf.py b/conf.py index 063a527244..acdb2a34da 100644 --- a/conf.py +++ b/conf.py @@ -417,7 +417,6 @@ texinfo_documents = [ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"cpython": ('https://docs.python.org/3/', None), - "bus_device": ('https://circuitpython.readthedocs.io/projects/busdevice/en/latest/', None), "register": ('https://circuitpython.readthedocs.io/projects/register/en/latest/', None)} # Adapted from sphinxcontrib-redirects diff --git a/devices/ble_hci/common-hal/_bleio/Adapter.c b/devices/ble_hci/common-hal/_bleio/Adapter.c index 97468339b0..04231a4a76 100644 --- a/devices/ble_hci/common-hal/_bleio/Adapter.c +++ b/devices/ble_hci/common-hal/_bleio/Adapter.c @@ -118,7 +118,8 @@ STATIC void add_generic_services(bleio_adapter_obj_t *adapter) { SECURITY_MODE_NO_ACCESS, 248, // max length, from Bluetooth spec false, // not fixed length - &generic_name_bufinfo + &generic_name_bufinfo, + NULL ); uint16_t zero_16 = 0; @@ -140,7 +141,8 @@ STATIC void add_generic_services(bleio_adapter_obj_t *adapter) { SECURITY_MODE_NO_ACCESS, 2, // max length, from Bluetooth spec true, // fixed length - &zero_16_value + &zero_16_value, + NULL ); // Generic Attribute Service setup. @@ -176,7 +178,8 @@ STATIC void add_generic_services(bleio_adapter_obj_t *adapter) { SECURITY_MODE_NO_ACCESS, 4, // max length, from Bluetooth spec true, // fixed length - &zero_32_value + &zero_32_value, + NULL ); } @@ -645,7 +648,11 @@ STATIC void check_data_fit(size_t data_len, bool connectable) { // return true; // } -uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, bool anonymous, uint32_t timeout, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len, mp_int_t tx_power) { +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) { check_enabled(self); if (self->now_advertising) { @@ -769,7 +776,11 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, return 0; } -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) { +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) { check_enabled(self); // interval value has already been validated. @@ -803,7 +814,7 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool advertising_data_bufinfo->len, scan_response_data_bufinfo->buf, scan_response_data_bufinfo->len, - tx_power); + tx_power, directed_to); if (result) { mp_raise_bleio_BluetoothError(translate("Already advertising")); diff --git a/devices/ble_hci/common-hal/_bleio/Characteristic.c b/devices/ble_hci/common-hal/_bleio/Characteristic.c index 940fdfc755..e2f9cad0ef 100644 --- a/devices/ble_hci/common-hal/_bleio/Characteristic.c +++ b/devices/ble_hci/common-hal/_bleio/Characteristic.c @@ -41,7 +41,7 @@ #define CCCD_INDICATE 0x2 -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) { +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) { self->service = service; self->uuid = uuid; self->decl_handle = BLE_GATT_HANDLE_INVALID; @@ -66,7 +66,7 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, if (service->is_remote) { self->handle = handle; } else { - common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo); + common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo, user_description); } } diff --git a/devices/ble_hci/common-hal/_bleio/PacketBuffer.c b/devices/ble_hci/common-hal/_bleio/PacketBuffer.c index a282cdce46..2c1989b4e0 100644 --- a/devices/ble_hci/common-hal/_bleio/PacketBuffer.c +++ b/devices/ble_hci/common-hal/_bleio/PacketBuffer.c @@ -148,7 +148,8 @@ mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self return ret; } -mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len, uint8_t *header, size_t header_len) { +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) { if (self->outgoing[0] == NULL) { mp_raise_bleio_BluetoothError(translate("Writes not supported on Characteristic")); } diff --git a/devices/ble_hci/common-hal/_bleio/Service.c b/devices/ble_hci/common-hal/_bleio/Service.c index 0bf00566f2..11b7efd523 100644 --- a/devices/ble_hci/common-hal/_bleio/Service.c +++ b/devices/ble_hci/common-hal/_bleio/Service.c @@ -84,7 +84,8 @@ bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, - mp_buffer_info_t *initial_value_bufinfo) { + mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { if (self->handle != common_hal_bleio_adapter_obj.last_added_service_handle) { mp_raise_bleio_BluetoothError( diff --git a/devices/ble_hci/common-hal/_bleio/__init__.c b/devices/ble_hci/common-hal/_bleio/__init__.c index 5b592de83a..4a439e8dd2 100644 --- a/devices/ble_hci/common-hal/_bleio/__init__.c +++ b/devices/ble_hci/common-hal/_bleio/__init__.c @@ -36,7 +36,7 @@ #include "shared-bindings/_bleio/Descriptor.h" #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" -#include "supervisor/shared/bluetooth.h" +#include "supervisor/shared/bluetooth/bluetooth.h" // UUID shared by all cccd's. bleio_uuid_obj_t cccd_uuid; diff --git a/docs/library/builtins.rst b/docs/library/builtins.rst index 6cbf95d53c..c7f983010e 100644 --- a/docs/library/builtins.rst +++ b/docs/library/builtins.rst @@ -182,10 +182,6 @@ Exceptions .. exception:: OSError - |see_cpython| :py:class:`cpython:OSError`. CircuitPython doesn't implement the ``errno`` - attribute, instead use the standard way to access exception arguments: - ``exc.args[0]``. - .. exception:: RuntimeError .. exception:: ReloadException diff --git a/docs/library/uasyncio.rst b/docs/library/uasyncio.rst index 0b79b9448e..10170fee85 100644 --- a/docs/library/uasyncio.rst +++ b/docs/library/uasyncio.rst @@ -214,6 +214,14 @@ TCP stream connections This is a coroutine. +.. method:: Stream.readinto(buf) + + Read up to n bytes into *buf* with n being equal to the length of *buf*. + + Return the number of bytes read into *buf*. + + This is a coroutine, and a MicroPython extension. + .. method:: Stream.readline() Read a line and return it. diff --git a/docs/library/uctypes.rst b/docs/library/uctypes.rst index 80f88a39d8..9e15c4c178 100644 --- a/docs/library/uctypes.rst +++ b/docs/library/uctypes.rst @@ -247,7 +247,7 @@ Module contents .. data:: VOID - ``VOID`` is an alias for ``UINT8``, and is provided to conviniently define + ``VOID`` is an alias for ``UINT8``, and is provided to conveniently define C's void pointers: ``(uctypes.PTR, uctypes.VOID)``. .. data:: PTR diff --git a/docs/library/uheapq.rst b/docs/library/uheapq.rst index 67da5f7380..6ee79fcc19 100644 --- a/docs/library/uheapq.rst +++ b/docs/library/uheapq.rst @@ -8,9 +8,11 @@ |see_cpython_module| :mod:`cpython:heapq`. -This module implements the heap queue algorithm. +This module implements the +`min heap queue algorithm `_. -A heap queue is simply a list that has its elements stored in a certain way. +A heap queue is essentially a list that has its elements stored in such a way +that the first item of the list is always the smallest. Functions --------- @@ -21,8 +23,10 @@ Functions .. function:: heappop(heap) - Pop the first item from the ``heap``, and return it. Raises IndexError if - heap is empty. + Pop the first item from the ``heap``, and return it. Raise ``IndexError`` if + ``heap`` is empty. + + The returned item will be the smallest item in the ``heap``. .. function:: heapify(x) diff --git a/docs/library/uselect.rst b/docs/library/uselect.rst index a01c5b62dd..cb7818dc06 100644 --- a/docs/library/uselect.rst +++ b/docs/library/uselect.rst @@ -89,11 +89,11 @@ Methods ``callee-owned tuples``. This function provides efficient, allocation-free way to poll on streams. - If *flags* is 1, one-shot behavior for events is employed: streams for + If *flags* is 1, one-shot behaviour for events is employed: streams for which events happened will have their event masks automatically reset (equivalent to ``poll.modify(obj, 0)``), so new events for such a stream won't be processed until new mask is set with `poll.modify()`. This - behavior is useful for asynchronous I/O schedulers. + behaviour is useful for asynchronous I/O schedulers. .. admonition:: Difference to CPython :class: attention diff --git a/extmod/moduasyncio.c b/extmod/moduasyncio.c index d4b749f5ba..fcf86751a5 100644 --- a/extmod/moduasyncio.c +++ b/extmod/moduasyncio.c @@ -31,12 +31,19 @@ #if MICROPY_PY_UASYNCIO +#define TASK_STATE_RUNNING_NOT_WAITED_ON (mp_const_true) +#define TASK_STATE_DONE_NOT_WAITED_ON (mp_const_none) +#define TASK_STATE_DONE_WAS_WAITED_ON (mp_const_false) + +#define TASK_IS_DONE(task) ( \ + (task)->state == TASK_STATE_DONE_NOT_WAITED_ON \ + || (task)->state == TASK_STATE_DONE_WAS_WAITED_ON) + typedef struct _mp_obj_task_t { mp_pairheap_t pairheap; mp_obj_t coro; mp_obj_t data; - mp_obj_t waiting; - + mp_obj_t state; mp_obj_t ph_key; } mp_obj_task_t; @@ -146,9 +153,6 @@ STATIC const mp_obj_type_t task_queue_type = { /******************************************************************************/ // Task class -// For efficiency, the task object is stored to the coro entry when the task is done. -#define TASK_IS_DONE(task) ((task)->coro == MP_OBJ_FROM_PTR(task)) - // This is the core uasyncio context with cur_task, _task_queue and CancelledError. STATIC mp_obj_t uasyncio_context = MP_OBJ_NULL; @@ -159,7 +163,7 @@ STATIC mp_obj_t task_make_new(const mp_obj_type_t *type, size_t n_args, const mp mp_pairheap_init_node(task_lt, &self->pairheap); self->coro = args[0]; self->data = mp_const_none; - self->waiting = mp_const_none; + self->state = TASK_STATE_RUNNING_NOT_WAITED_ON; self->ph_key = MP_OBJ_NEW_SMALL_INT(0); if (n_args == 2) { uasyncio_context = args[1]; @@ -218,24 +222,6 @@ STATIC mp_obj_t task_cancel(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_cancel_obj, task_cancel); -STATIC mp_obj_t task_throw(mp_obj_t self_in, mp_obj_t value_in) { - // This task raised an exception which was uncaught; handle that now. - mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); - // Set the data because it was cleared by the main scheduling loop. - self->data = value_in; - if (self->waiting == mp_const_none) { - // Nothing await'ed on the task so call the exception handler. - mp_obj_t _exc_context = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR__exc_context)); - mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_exception), value_in); - mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_future), self_in); - mp_obj_t Loop = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_Loop)); - mp_obj_t call_exception_handler = mp_load_attr(Loop, MP_QSTR_call_exception_handler); - mp_call_function_1(call_exception_handler, _exc_context); - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(task_throw_obj, task_throw); - STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] == MP_OBJ_NULL) { @@ -244,32 +230,24 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = self->coro; } else if (attr == MP_QSTR_data) { dest[0] = self->data; - } else if (attr == MP_QSTR_waiting) { - if (self->waiting != mp_const_none && self->waiting != mp_const_false) { - dest[0] = self->waiting; - } + } else if (attr == MP_QSTR_state) { + dest[0] = self->state; } else if (attr == MP_QSTR_done) { dest[0] = MP_OBJ_FROM_PTR(&task_done_obj); dest[1] = self_in; } else if (attr == MP_QSTR_cancel) { dest[0] = MP_OBJ_FROM_PTR(&task_cancel_obj); dest[1] = self_in; - } else if (attr == MP_QSTR_throw) { - dest[0] = MP_OBJ_FROM_PTR(&task_throw_obj); - dest[1] = self_in; } else if (attr == MP_QSTR_ph_key) { dest[0] = self->ph_key; } } else if (dest[1] != MP_OBJ_NULL) { // Store - if (attr == MP_QSTR_coro) { - self->coro = dest[1]; - dest[0] = MP_OBJ_NULL; - } else if (attr == MP_QSTR_data) { + if (attr == MP_QSTR_data) { self->data = dest[1]; dest[0] = MP_OBJ_NULL; - } else if (attr == MP_QSTR_waiting) { - self->waiting = dest[1]; + } else if (attr == MP_QSTR_state) { + self->state = dest[1]; dest[0] = MP_OBJ_NULL; } } @@ -278,15 +256,12 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { (void)iter_buf; mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); - if (self->waiting == mp_const_none) { - // The is the first access of the "waiting" entry. - if (TASK_IS_DONE(self)) { - // Signal that the completed-task has been await'ed on. - self->waiting = mp_const_false; - } else { - // Lazily allocate the waiting queue. - self->waiting = task_queue_make_new(&task_queue_type, 0, 0, NULL); - } + if (TASK_IS_DONE(self)) { + // Signal that the completed-task has been await'ed on. + self->state = TASK_STATE_DONE_WAS_WAITED_ON; + } else if (self->state == TASK_STATE_RUNNING_NOT_WAITED_ON) { + // Allocate the waiting queue. + self->state = task_queue_make_new(&task_queue_type, 0, 0, NULL); } return self_in; } @@ -299,7 +274,7 @@ STATIC mp_obj_t task_iternext(mp_obj_t self_in) { } else { // Put calling task on waiting queue. mp_obj_t cur_task = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task)); - mp_obj_t args[2] = { self->waiting, cur_task }; + mp_obj_t args[2] = { self->state, cur_task }; task_queue_push_sorted(2, args); // Set calling task's data to this task that it waits on, to double-link it. ((mp_obj_task_t *)MP_OBJ_TO_PTR(cur_task))->data = self_in; diff --git a/extmod/moductypes.c b/extmod/moductypes.c index f78b892464..3de3a080af 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -15,38 +15,10 @@ #if MICROPY_PY_UCTYPES -/// \module uctypes - Access data structures in memory -/// -/// The module allows to define layout of raw data structure (using terms -/// of C language), and then access memory buffers using this definition. -/// The module also provides convenience functions to access memory buffers -/// contained in Python objects or wrap memory buffers in Python objects. -/// \constant UINT8_1 - uint8_t value type - -/// \class struct - C-like structure -/// -/// Encapsulalation of in-memory data structure. This class doesn't define -/// any methods, only attribute access (for structure fields) and -/// indexing (for pointer and array fields). -/// -/// Usage: -/// -/// # Define layout of a structure with 2 fields -/// # 0 and 4 are byte offsets of fields from the beginning of struct -/// # they are logically ORed with field type -/// FOO_STRUCT = {"a": 0 | uctypes.UINT32, "b": 4 | uctypes.UINT8} -/// -/// # Example memory buffer to access (contained in bytes object) -/// buf = b"\x64\0\0\0\0x14" -/// -/// # Create structure object referring to address of -/// # the data in the buffer above -/// s = uctypes.struct(FOO_STRUCT, uctypes.addressof(buf)) -/// -/// # Access fields -/// print(s.a, s.b) -/// # Result: -/// # 100, 20 +// The uctypes module allows defining the layout of a raw data structure (using +// terms of the C language), and then access memory buffers using this definition. +// The module also provides convenience functions to access memory buffers +// contained in Python objects or wrap memory buffers in Python objects. #define LAYOUT_LITTLE_ENDIAN (0) #define LAYOUT_BIG_ENDIAN (1) @@ -56,6 +28,7 @@ #define BITF_LEN_BITS 5 #define BITF_OFF_BITS 5 #define OFFSET_BITS 17 +#define LEN_BITS (OFFSET_BITS + BITF_OFF_BITS) #if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 31 #error Invalid encoding field length #endif @@ -172,7 +145,7 @@ STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_ mp_uint_t item_s; if (t->len == 2) { // Elements of array are scalar - item_s = GET_SCALAR_SIZE(val_type); + item_s = uctypes_struct_scalar_size(val_type); if (item_s > *max_field_size) { *max_field_size = item_s; } @@ -400,10 +373,8 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(deref); mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS); offset &= VALUE_MASK(VAL_TYPE_BITS); -// printf("scalar type=%d offset=%x\n", val_type, offset); if (val_type <= INT64 || val_type == FLOAT32 || val_type == FLOAT64) { -// printf("size=%d\n", GET_SCALAR_SIZE(val_type)); if (self->flags == LAYOUT_NATIVE) { if (set_val == MP_OBJ_NULL) { return get_aligned(val_type, self->addr + offset, 0); @@ -420,9 +391,9 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set } } } else if (val_type >= BFUINT8 && val_type <= BFINT32) { - uint bit_offset = (offset >> 17) & 31; - uint bit_len = (offset >> 22) & 31; - offset &= (1 << 17) - 1; + uint bit_offset = (offset >> OFFSET_BITS) & 31; + uint bit_len = (offset >> LEN_BITS) & 31; + offset &= (1 << OFFSET_BITS) - 1; mp_uint_t val; if (self->flags == LAYOUT_NATIVE) { val = get_aligned_basic(val_type & 6, self->addr + offset); @@ -470,7 +441,6 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[0]); mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS); offset &= VALUE_MASK(AGG_TYPE_BITS); -// printf("agg type=%d offset=%x\n", agg_type, offset); switch (agg_type) { case STRUCT: { @@ -495,7 +465,6 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set o->desc = MP_OBJ_FROM_PTR(sub); o->addr = self->addr + offset; o->flags = self->flags; -// printf("PTR/ARR base addr=%p\n", o->addr); return MP_OBJ_FROM_PTR(o); } } @@ -553,7 +522,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t base_in, mp_obj_t index_in, mp_ob return value; // just !MP_OBJ_NULL } } else { - byte *p = self->addr + GET_SCALAR_SIZE(val_type) * index; + byte *p = self->addr + uctypes_struct_scalar_size(val_type) * index; if (value == MP_OBJ_SENTINEL) { return get_unaligned(val_type, p, self->flags); } else { @@ -628,9 +597,8 @@ STATIC mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, return 0; } -/// \function addressof() -/// Return address of object's data (applies to object providing buffer -/// interface). +// addressof() +// Return address of object's data (applies to objects providing the buffer interface). STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); @@ -638,25 +606,20 @@ STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) { } MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_addressof_obj, uctypes_struct_addressof); -/// \function bytearray_at() -/// Capture memory at given address of given size as bytearray. Memory is -/// captured by reference (and thus memory pointed by bytearray may change -/// or become invalid at later time). Use bytes_at() to capture by value. +// bytearray_at() +// Capture memory at given address of given size as bytearray. STATIC mp_obj_t uctypes_struct_bytearray_at(mp_obj_t ptr, mp_obj_t size) { return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void *)(uintptr_t)mp_obj_int_get_truncated(ptr)); } MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytearray_at_obj, uctypes_struct_bytearray_at); -/// \function bytes_at() -/// Capture memory at given address of given size as bytes. Memory is -/// captured by value, i.e. copied. Use bytearray_at() to capture by reference -/// ("zero copy"). +// bytes_at() +// Capture memory at given address of given size as bytes. STATIC mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) { return mp_obj_new_bytes((void *)(uintptr_t)mp_obj_int_get_truncated(ptr), mp_obj_int_get_truncated(size)); } MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at); - STATIC const mp_obj_type_t uctypes_struct_type = { { &mp_type_type }, .name = MP_QSTR_struct, @@ -676,81 +639,63 @@ STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_bytes_at), MP_ROM_PTR(&uctypes_struct_bytes_at_obj) }, { MP_ROM_QSTR(MP_QSTR_bytearray_at), MP_ROM_PTR(&uctypes_struct_bytearray_at_obj) }, - /// \moduleref uctypes - - /// \constant NATIVE - Native structure layout - native endianness, - /// platform-specific field alignment { MP_ROM_QSTR(MP_QSTR_NATIVE), MP_ROM_INT(LAYOUT_NATIVE) }, - /// \constant LITTLE_ENDIAN - Little-endian structure layout, tightly packed - /// (no alignment constraints) { MP_ROM_QSTR(MP_QSTR_LITTLE_ENDIAN), MP_ROM_INT(LAYOUT_LITTLE_ENDIAN) }, - /// \constant BIG_ENDIAN - Big-endian structure layout, tightly packed - /// (no alignment constraints) { MP_ROM_QSTR(MP_QSTR_BIG_ENDIAN), MP_ROM_INT(LAYOUT_BIG_ENDIAN) }, - /// \constant VOID - void value type, may be used only as pointer target type. { MP_ROM_QSTR(MP_QSTR_VOID), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) }, - /// \constant UINT8 - uint8_t value type - { MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, 4)) }, - /// \constant INT8 - int8_t value type - { MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, 4)) }, - /// \constant UINT16 - uint16_t value type - { MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) }, - /// \constant INT16 - int16_t value type - { MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) }, - /// \constant UINT32 - uint32_t value type - { MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) }, - /// \constant INT32 - int32_t value type - { MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) }, - /// \constant UINT64 - uint64_t value type - { MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) }, - /// \constant INT64 - int64_t value type - { MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) }, + { MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, VAL_TYPE_BITS)) }, - { MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, 4)) }, - { MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, 4)) }, - { MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, 4)) }, - { MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, 4)) }, - { MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, 4)) }, - { MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, 4)) }, + { MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, VAL_TYPE_BITS)) }, - { MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(17) }, - { MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(22) }, + { MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(OFFSET_BITS) }, + { MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(LEN_BITS) }, #if MICROPY_PY_BUILTINS_FLOAT - { MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, 4)) }, - { MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) }, + { MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, VAL_TYPE_BITS)) }, #endif #if MICROPY_PY_UCTYPES_NATIVE_C_TYPES // C native type aliases. These depend on GCC-compatible predefined // preprocessor macros. #if __SIZEOF_SHORT__ == 2 - { MP_ROM_QSTR(MP_QSTR_SHORT), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) }, - { MP_ROM_QSTR(MP_QSTR_USHORT), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) }, + { MP_ROM_QSTR(MP_QSTR_SHORT), MP_ROM_INT(TYPE2SMALLINT(INT16, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_USHORT), MP_ROM_INT(TYPE2SMALLINT(UINT16, VAL_TYPE_BITS)) }, #endif #if __SIZEOF_INT__ == 4 - { MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) }, - { MP_ROM_QSTR(MP_QSTR_UINT), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) }, + { MP_ROM_QSTR(MP_QSTR_INT), MP_ROM_INT(TYPE2SMALLINT(INT32, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_UINT), MP_ROM_INT(TYPE2SMALLINT(UINT32, VAL_TYPE_BITS)) }, #endif #if __SIZEOF_LONG__ == 4 - { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) }, - { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) }, + { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT32, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT32, VAL_TYPE_BITS)) }, #elif __SIZEOF_LONG__ == 8 - { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) }, - { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) }, + { MP_ROM_QSTR(MP_QSTR_LONG), MP_ROM_INT(TYPE2SMALLINT(INT64, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_ULONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, VAL_TYPE_BITS)) }, #endif #if __SIZEOF_LONG_LONG__ == 8 - { MP_ROM_QSTR(MP_QSTR_LONGLONG), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) }, - { MP_ROM_QSTR(MP_QSTR_ULONGLONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) }, + { MP_ROM_QSTR(MP_QSTR_LONGLONG), MP_ROM_INT(TYPE2SMALLINT(INT64, VAL_TYPE_BITS)) }, + { MP_ROM_QSTR(MP_QSTR_ULONGLONG), MP_ROM_INT(TYPE2SMALLINT(UINT64, VAL_TYPE_BITS)) }, #endif #endif // MICROPY_PY_UCTYPES_NATIVE_C_TYPES { MP_ROM_QSTR(MP_QSTR_PTR), MP_ROM_INT(TYPE2SMALLINT(PTR, AGG_TYPE_BITS)) }, { MP_ROM_QSTR(MP_QSTR_ARRAY), MP_ROM_INT(TYPE2SMALLINT(ARRAY, AGG_TYPE_BITS)) }, }; - STATIC MP_DEFINE_CONST_DICT(mp_module_uctypes_globals, mp_module_uctypes_globals_table); const mp_obj_module_t mp_module_uctypes = { diff --git a/extmod/moduhashlib.c b/extmod/moduhashlib.c index 96b64f1718..0dfd2c790a 100644 --- a/extmod/moduhashlib.c +++ b/extmod/moduhashlib.c @@ -42,9 +42,16 @@ typedef struct _mp_obj_hash_t { mp_obj_base_t base; - char state[0]; + bool final; // if set, update and digest raise an exception + uintptr_t state[0]; // must be aligned to a machine word } mp_obj_hash_t; +static void uhashlib_ensure_not_final(mp_obj_hash_t *self) { + if (self->final) { + mp_raise_ValueError(MP_ERROR_TEXT("hash is final")); + } +} + #if MICROPY_PY_UHASHLIB_SHA256 STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg); @@ -60,6 +67,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg mp_arg_check_num(n_args, n_kw, 0, 1, false); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha256_context)); o->base.type = type; + o->final = false; mbedtls_sha256_init((mbedtls_sha256_context *)&o->state); mbedtls_sha256_starts_ret((mbedtls_sha256_context *)&o->state, 0); if (n_args == 1) { @@ -70,6 +78,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); mbedtls_sha256_update_ret((mbedtls_sha256_context *)&self->state, bufinfo.buf, bufinfo.len); @@ -78,6 +87,8 @@ STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); + self->final = true; vstr_t vstr; vstr_init_len(&vstr, 32); mbedtls_sha256_finish_ret((mbedtls_sha256_context *)&self->state, (unsigned char *)vstr.buf); @@ -102,6 +113,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg mp_arg_check_num(n_args, kw_args, 0, 1, false); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(CRYAL_SHA256_CTX)); o->base.type = type; + o->final = false; sha256_init((CRYAL_SHA256_CTX *)o->state); if (n_args == 1) { uhashlib_sha256_update(MP_OBJ_FROM_PTR(o), args[0]); @@ -112,6 +124,7 @@ STATIC mp_obj_t uhashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_arg STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) { check_not_unicode(arg); mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); sha256_update((CRYAL_SHA256_CTX *)self->state, bufinfo.buf, bufinfo.len); @@ -120,6 +133,8 @@ STATIC mp_obj_t uhashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); + self->final = true; vstr_t vstr; vstr_init_len(&vstr, SHA256_BLOCK_SIZE); sha256_final((CRYAL_SHA256_CTX *)self->state, (byte *)vstr.buf); @@ -153,6 +168,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_check_num(n_args, kw_args, 0, 1, false); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(SHA1_CTX)); o->base.type = type; + o->final = false; SHA1_Init((SHA1_CTX *)o->state); if (n_args == 1) { uhashlib_sha1_update(MP_OBJ_FROM_PTR(o), args[0]); @@ -163,6 +179,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args, STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) { check_not_unicode(arg); mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); SHA1_Update((SHA1_CTX *)self->state, bufinfo.buf, bufinfo.len); @@ -171,6 +188,8 @@ STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); + self->final = true; vstr_t vstr; vstr_init_len(&vstr, SHA1_SIZE); SHA1_Final((byte *)vstr.buf, (SHA1_CTX *)self->state); @@ -190,6 +209,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_check_num(n_args, n_kw, 0, 1, false); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha1_context)); o->base.type = type; + o->final = false; mbedtls_sha1_init((mbedtls_sha1_context *)o->state); mbedtls_sha1_starts_ret((mbedtls_sha1_context *)o->state); if (n_args == 1) { @@ -200,6 +220,7 @@ STATIC mp_obj_t uhashlib_sha1_make_new(const mp_obj_type_t *type, size_t n_args, STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); mbedtls_sha1_update_ret((mbedtls_sha1_context *)self->state, bufinfo.buf, bufinfo.len); @@ -208,6 +229,8 @@ STATIC mp_obj_t uhashlib_sha1_update(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); + self->final = true; vstr_t vstr; vstr_init_len(&vstr, 20); mbedtls_sha1_finish_ret((mbedtls_sha1_context *)self->state, (byte *)vstr.buf); @@ -241,6 +264,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_check_num(n_args, n_kw, 0, 1, false); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(MD5_CTX)); o->base.type = type; + o->final = false; MD5_Init((MD5_CTX *)o->state); if (n_args == 1) { uhashlib_md5_update(MP_OBJ_FROM_PTR(o), args[0]); @@ -250,6 +274,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args, STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); MD5_Update((MD5_CTX *)self->state, bufinfo.buf, bufinfo.len); @@ -258,6 +283,8 @@ STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); + self->final = true; vstr_t vstr; vstr_init_len(&vstr, MD5_SIZE); MD5_Final((byte *)vstr.buf, (MD5_CTX *)self->state); @@ -277,6 +304,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_check_num(n_args, n_kw, 0, 1, false); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_md5_context)); o->base.type = type; + o->final = false; mbedtls_md5_init((mbedtls_md5_context *)o->state); mbedtls_md5_starts_ret((mbedtls_md5_context *)o->state); if (n_args == 1) { @@ -287,6 +315,7 @@ STATIC mp_obj_t uhashlib_md5_make_new(const mp_obj_type_t *type, size_t n_args, STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); mp_buffer_info_t bufinfo; mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); mbedtls_md5_update_ret((mbedtls_md5_context *)self->state, bufinfo.buf, bufinfo.len); @@ -295,6 +324,8 @@ STATIC mp_obj_t uhashlib_md5_update(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + uhashlib_ensure_not_final(self); + self->final = true; vstr_t vstr; vstr_init_len(&vstr, 16); mbedtls_md5_finish_ret((mbedtls_md5_context *)self->state, (byte *)vstr.buf); diff --git a/extmod/modurandom.c b/extmod/modurandom.c index b33d0eb23f..e304d0b728 100644 --- a/extmod/modurandom.c +++ b/extmod/modurandom.c @@ -66,8 +66,11 @@ STATIC uint32_t yasmarang_randbelow(uint32_t n) { STATIC mp_obj_t mod_urandom_getrandbits(mp_obj_t num_in) { int n = mp_obj_get_int(num_in); - if (n > 32 || n == 0) { - mp_raise_ValueError(NULL); + if (n > 32 || n < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("bits must be 32 or less")); + } + if (n == 0) { + return MP_OBJ_NEW_SMALL_INT(0); } uint32_t mask = ~0; // Beware of C undefined behavior when shifting by >= than bit size diff --git a/extmod/moduselect.c b/extmod/moduselect.c index e14fd73536..3c66d69e32 100644 --- a/extmod/moduselect.c +++ b/extmod/moduselect.c @@ -20,10 +20,6 @@ // Flags for poll() #define FLAG_ONESHOT (1) -/// \module select - Provides select function to wait for events on a stream -/// -/// This module provides the select function. - typedef struct _poll_obj_t { mp_obj_t obj; mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); @@ -91,7 +87,7 @@ STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, size_t *rwx_num) { return n_ready; } -/// \function select(rlist, wlist, xlist[, timeout]) +// select(rlist, wlist, xlist[, timeout]) STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { // get array data from tuple/list arguments size_t rwx_len[3]; @@ -158,8 +154,6 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); -/// \class Poll - poll class - typedef struct _mp_obj_poll_t { mp_obj_base_t base; mp_map_t poll_map; @@ -170,7 +164,7 @@ typedef struct _mp_obj_poll_t { mp_obj_t ret_tuple; } mp_obj_poll_t; -/// \method register(obj[, eventmask]) +// register(obj[, eventmask]) STATIC mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]); mp_uint_t flags; @@ -184,7 +178,7 @@ STATIC mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register); -/// \method unregister(obj) +// unregister(obj) STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in); mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND); @@ -193,7 +187,7 @@ STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { } MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister); -/// \method modify(obj, eventmask) +// modify(obj, eventmask) STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in); mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP); @@ -328,7 +322,7 @@ STATIC const mp_obj_type_t mp_type_poll = { .locals_dict = (void *)&poll_locals_dict, }; -/// \function poll() +// poll() STATIC mp_obj_t select_poll(void) { mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t); poll->base.type = &mp_type_poll; diff --git a/extmod/uasyncio/core.py b/extmod/uasyncio/core.py index d74763f6a6..12833cf0cd 100644 --- a/extmod/uasyncio/core.py +++ b/extmod/uasyncio/core.py @@ -175,6 +175,10 @@ def run_until_complete(main_task=None): if not exc: t.coro.send(None) else: + # If the task is finished and on the run queue and gets here, then it + # had an exception and was not await'ed on. Throwing into it now will + # raise StopIteration and the code below will catch this and run the + # call_exception_handler function. t.data = None t.coro.throw(exc) except excs_all as er: @@ -185,22 +189,32 @@ def run_until_complete(main_task=None): if isinstance(er, StopIteration): return er.value raise er - # Schedule any other tasks waiting on the completion of this task - waiting = False - if hasattr(t, "waiting"): - while t.waiting.peek(): - _task_queue.push_head(t.waiting.pop_head()) - waiting = True - t.waiting = None # Free waiting queue head - if not waiting and not isinstance(er, excs_stop): - # An exception ended this detached task, so queue it for later - # execution to handle the uncaught exception if no other task retrieves - # the exception in the meantime (this is handled by Task.throw). - _task_queue.push_head(t) - # Indicate task is done by setting coro to the task object itself - t.coro = t - # Save return value of coro to pass up to caller - t.data = er + if t.state: + # Task was running but is now finished. + waiting = False + if t.state is True: + # "None" indicates that the task is complete and not await'ed on (yet). + t.state = None + else: + # Schedule any other tasks waiting on the completion of this task. + while t.state.peek(): + _task_queue.push_head(t.state.pop_head()) + waiting = True + # "False" indicates that the task is complete and has been await'ed on. + t.state = False + if not waiting and not isinstance(er, excs_stop): + # An exception ended this detached task, so queue it for later + # execution to handle the uncaught exception if no other task retrieves + # the exception in the meantime (this is handled by Task.throw). + _task_queue.push_head(t) + # Save return value of coro to pass up to caller. + t.data = er + elif t.state is None: + # Task is already finished and nothing await'ed on the task, + # so call the exception handler. + _exc_context["exception"] = exc + _exc_context["future"] = t + Loop.call_exception_handler(_exc_context) # Create a new task from a coroutine and run it until it finishes diff --git a/extmod/uasyncio/stream.py b/extmod/uasyncio/stream.py index b6d787e4f0..3a68881da3 100644 --- a/extmod/uasyncio/stream.py +++ b/extmod/uasyncio/stream.py @@ -30,6 +30,10 @@ class Stream: yield core._io_queue.queue_read(self.s) return self.s.read(n) + async def readinto(self, buf): + yield core._io_queue.queue_read(self.s) + return self.s.readinto(buf) + async def readexactly(self, n): r = b"" while n: @@ -82,7 +86,7 @@ async def open_connection(host, port): try: s.connect(ai[-1]) except OSError as er: - if er.args[0] != EINPROGRESS: + if er.errno != EINPROGRESS: raise er yield core._io_queue.queue_write(s) return ss, ss @@ -112,7 +116,6 @@ class Server: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(ai[-1]) s.listen(backlog) - self.task = core.cur_task # Accept incoming connections while True: try: @@ -135,7 +138,7 @@ class Server: # TODO could use an accept-callback on socket read activity instead of creating a task async def start_server(cb, host, port, backlog=5): s = Server() - core.create_task(s._serve(cb, host, port, backlog)) + s.task = core.create_task(s._serve(cb, host, port, backlog)) return s diff --git a/extmod/uasyncio/task.py b/extmod/uasyncio/task.py index 68ddf496f0..26df7b1725 100644 --- a/extmod/uasyncio/task.py +++ b/extmod/uasyncio/task.py @@ -123,6 +123,7 @@ class Task: def __init__(self, coro, globals=None): self.coro = coro # Coroutine of this Task self.data = None # General data for queue it is waiting on + self.state = True # None, False, True or a TaskQueue instance self.ph_key = 0 # Pairing heap self.ph_child = None # Paring heap self.ph_child_last = None # Paring heap @@ -130,30 +131,30 @@ class Task: self.ph_rightmost_parent = None # Paring heap def __iter__(self): - if self.coro is self: - # Signal that the completed-task has been await'ed on. - self.waiting = None - elif not hasattr(self, "waiting"): - # Lazily allocated head of linked list of Tasks waiting on completion of this task. - self.waiting = TaskQueue() + if not self.state: + # Task finished, signal that is has been await'ed on. + self.state = False + elif self.state is True: + # Allocated head of linked list of Tasks waiting on completion of this task. + self.state = TaskQueue() return self def __next__(self): - if self.coro is self: + if not self.state: # Task finished, raise return value to caller so it can continue. raise self.data else: # Put calling task on waiting queue. - self.waiting.push_head(core.cur_task) + self.state.push_head(core.cur_task) # Set calling task's data to this task that it waits on, to double-link it. core.cur_task.data = self def done(self): - return self.coro is self + return not self.state def cancel(self): # Check if task is already finished. - if self.coro is self: + if not self.state: return False # Can't cancel self (not supported yet). if self is core.cur_task: @@ -172,13 +173,3 @@ class Task: core._task_queue.push_head(self) self.data = core.CancelledError return True - - def throw(self, value): - # This task raised an exception which was uncaught; handle that now. - # Set the data because it was cleared by the main scheduling loop. - self.data = value - if not hasattr(self, "waiting"): - # Nothing await'ed on the task so call the exception handler. - core._exc_context["exception"] = value - core._exc_context["future"] = self - core.Loop.call_exception_handler(core._exc_context) diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 0090500cdc..be144ec821 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -264,7 +264,7 @@ STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_mkdir_obj, fat_vfs_mkdir); -/// Change current directory. +// Change current directory. STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *path; @@ -280,7 +280,7 @@ STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_chdir_obj, fat_vfs_chdir); -/// Get the current directory. +// Get the current directory. STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); char buf[MICROPY_ALLOC_PATH_MAX + 1]; @@ -292,8 +292,7 @@ STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd); -/// \function stat(path) -/// Get the status of a file or directory. +// Get the status of a file or directory. STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *path = mp_obj_str_get_str(path_in); diff --git a/frozen/circuitpython-stage b/frozen/circuitpython-stage index 40d8a03b45..b014aa045d 160000 --- a/frozen/circuitpython-stage +++ b/frozen/circuitpython-stage @@ -1 +1 @@ -Subproject commit 40d8a03b4569d566faa62fcb0f798178118f2954 +Subproject commit b014aa045d9014b86a4ae583f452c6b8282a61b9 diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 8bd027cf2c..b0249f9d30 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -622,8 +622,8 @@ friendly_repl_reset: // If the GC is locked at this point there is no way out except a reset, // so force the GC to be unlocked to help the user debug what went wrong. - if (MP_STATE_MEM(gc_lock_depth) != 0) { - MP_STATE_MEM(gc_lock_depth) = 0; + if (MP_STATE_THREAD(gc_lock_depth) != 0) { + MP_STATE_THREAD(gc_lock_depth) = 0; } vstr_reset(&line); diff --git a/lib/utils/pyexec.h b/lib/utils/pyexec.h index 6ec2e629f0..4924f8776a 100644 --- a/lib/utils/pyexec.h +++ b/lib/utils/pyexec.h @@ -47,9 +47,8 @@ extern pyexec_mode_kind_t pyexec_mode_kind; extern int pyexec_system_exit; #define PYEXEC_FORCED_EXIT (0x100) -#define PYEXEC_SWITCH_MODE (0x200) -#define PYEXEC_EXCEPTION (0x400) -#define PYEXEC_DEEP_SLEEP (0x800) +#define PYEXEC_EXCEPTION (0x200) +#define PYEXEC_DEEP_SLEEP (0x400) int pyexec_raw_repl(void); int pyexec_friendly_repl(void); diff --git a/lib/utils/semihosting.c b/lib/utils/semihosting.c new file mode 100644 index 0000000000..18c7f5d57a --- /dev/null +++ b/lib/utils/semihosting.c @@ -0,0 +1,132 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Ayke van Laethem + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 "semihosting.h" + +// Resources: +// http://embed.rs/articles/2016/semi-hosting-rust/ +// https://wiki.dlang.org/Minimal_semihosted_ARM_Cortex-M_%22Hello_World%22 +// https://github.com/arduino/OpenOCD/blob/master/src/target/arm_semihosting.c + +#define SYS_OPEN 0x01 +#define SYS_WRITEC 0x03 +#define SYS_WRITE 0x05 +#define SYS_READC 0x07 + +// Constants: +#define OPEN_MODE_READ (0) // mode "r" +#define OPEN_MODE_WRITE (4) // mode "w" + +#ifndef __thumb__ +#error Semihosting is only implemented for ARM microcontrollers. +#endif + +static int mp_semihosting_stdout; + +static uint32_t mp_semihosting_call(uint32_t num, const void *arg) { + // A semihosting call works as follows, similar to a SVCall: + // * the call is invoked by a special breakpoint: 0xAB + // * the command is placed in r0 + // * a pointer to the arguments is placed in r1 + // * the return value is placed in r0 + // Note that because it uses the breakpoint instruction, applications + // will hang if they're not connected to a debugger. And they'll be + // stuck in a breakpoint if semihosting is not specifically enabled in + // the debugger. + // Also note that semihosting is extremely slow (sometimes >100ms per + // call). + register uint32_t num_reg __asm__ ("r0") = num; + register const void *args_reg __asm__ ("r1") = arg; + __asm__ __volatile__ ( + "bkpt 0xAB\n" // invoke semihosting call + : "+r" (num_reg) // call number and result + : "r" (args_reg) // arguments + : "memory"); // make sure args aren't optimized away + return num_reg; // r0, which became the result +} + +static int mp_semihosting_open_console(uint32_t mode) { + struct { + char *name; + uint32_t mode; + uint32_t name_len; + } args = { + .name = ":tt", // magic path to console + .mode = mode, // e.g. "r", "w" (see OPEN_MODE_* constants) + .name_len = 3, // strlen(":tt") + }; + return mp_semihosting_call(SYS_OPEN, &args); +} + +void mp_semihosting_init() { + mp_semihosting_stdout = mp_semihosting_open_console(OPEN_MODE_WRITE); +} + +int mp_semihosting_rx_char() { + return mp_semihosting_call(SYS_READC, NULL); +} + +static void mp_semihosting_tx_char(char c) { + mp_semihosting_call(SYS_WRITEC, &c); +} + +uint32_t mp_semihosting_tx_strn(const char *str, size_t len) { + if (len == 0) { + return 0; // nothing to do + } + if (len == 1) { + mp_semihosting_tx_char(*str); // maybe faster? + return 0; + } + + struct { + uint32_t fd; + const char *str; + uint32_t len; + } args = { + .fd = mp_semihosting_stdout, + .str = str, + .len = len, + }; + return mp_semihosting_call(SYS_WRITE, &args); +} + +uint32_t mp_semihosting_tx_strn_cooked(const char *str, size_t len) { + // Write chunks of data until (excluding) the first '\n' character, + // insert a '\r' character, and then continue with the next chunk + // (starting with '\n'). + // Doing byte-by-byte writes would be easier to implement but is far + // too slow. + size_t start = 0; + for (size_t i = 0; i < len; i++) { + if (str[i] == '\n') { + mp_semihosting_tx_strn(str + start, i - start); + mp_semihosting_tx_char('\r'); + start = i; + } + } + return mp_semihosting_tx_strn(str + start, len - start); +} diff --git a/locale/ID.po b/locale/ID.po index 44622b1cc6..b04a643c49 100644 --- a/locale/ID.po +++ b/locale/ID.po @@ -54,6 +54,10 @@ msgstr " File \"%q\", baris %d" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr "output:\n" @@ -90,10 +94,22 @@ msgstr "%q indeks di luar batas" msgid "%q indices must be integers, not %s" msgstr "indeks %q harus bilangan bulat, bukan %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "daftar %q harus berupa daftar" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -102,7 +118,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q harus >= 0" @@ -118,6 +138,10 @@ msgstr "%q harus >= 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q harus berupa tuple dengan panjang 2" @@ -126,6 +150,10 @@ msgstr "%q harus berupa tuple dengan panjang 2" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -575,10 +603,6 @@ msgstr "" msgid "Buffer must be at least length 1" msgstr "Penyangga harus memiliki panjang setidaknya 1" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Buffer terlalu besar dan tidak dapat dialokasikan" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -862,6 +886,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "Potongan data harus mengikuti fmt chunk" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Data terlalu besar untuk paket advertisment" @@ -1658,7 +1686,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1704,6 +1731,10 @@ msgstr "" "Hanya format Windows, mendukung BMP tidak dikompresi: ukuran header yang " "diberikan adalah %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1727,6 +1758,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2031,6 +2063,10 @@ msgstr "Kesalahan Init SPI" msgid "SPI Re-initialization error" msgstr "Kesalahan Inisialisasi ulang SPI" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2069,6 +2105,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2213,6 +2250,7 @@ msgstr "Terlalu banyak tampilan" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2514,7 +2552,7 @@ msgstr "Argumen argsort harus berupa ndarray" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "argumen memiliki tipe yang salah" @@ -2589,6 +2627,10 @@ msgstr "typecode buruk" msgid "binary op %q not implemented" msgstr "" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3200,6 +3242,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heap harus berupa sebuah list" @@ -4412,6 +4458,9 @@ msgstr "zi harus berjenis float" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Buffer terlalu besar dan tidak dapat dialokasikan" + #~ msgid "" #~ "CircuitPython is in safe mode because you pressed the reset button during " #~ "boot. Press again to exit safe mode.\n" diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 55d3d9da8f..e421eef8ea 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -47,6 +47,10 @@ msgstr "" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr "" @@ -83,10 +87,22 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -95,7 +111,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "" @@ -111,6 +131,10 @@ msgstr "" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "" @@ -119,6 +143,10 @@ msgstr "" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -566,10 +594,6 @@ msgstr "" msgid "Buffer must be at least length 1" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -844,6 +868,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "" @@ -1638,7 +1666,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1681,6 +1708,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1702,6 +1733,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -1997,6 +2029,10 @@ msgstr "" msgid "SPI Re-initialization error" msgstr "" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2035,6 +2071,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2179,6 +2216,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2472,7 +2510,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "" @@ -2547,6 +2585,10 @@ msgstr "" msgid "binary op %q not implemented" msgstr "" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3158,6 +3200,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "" diff --git a/locale/cs.po b/locale/cs.po index e15c1db9f7..0ea37f724f 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -50,6 +50,10 @@ msgstr " Soubor \"%q\", řádek %d" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " výstup:\n" @@ -86,10 +90,22 @@ msgstr "Index %q je mimo rozsah" msgid "%q indices must be integers, not %s" msgstr "Indexy %q musí být celá čísla, nikoli %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "Seznam %q musí být seznam" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -98,7 +114,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q musí být >= 0" @@ -114,6 +134,10 @@ msgstr "%q musí být > = 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q musí být n-tice délky 2" @@ -122,6 +146,10 @@ msgstr "%q musí být n-tice délky 2" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -569,10 +597,6 @@ msgstr "" msgid "Buffer must be at least length 1" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -847,6 +871,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "" @@ -1641,7 +1669,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1684,6 +1711,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1705,6 +1736,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2000,6 +2032,10 @@ msgstr "" msgid "SPI Re-initialization error" msgstr "" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2038,6 +2074,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2182,6 +2219,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2475,7 +2513,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "" @@ -2550,6 +2588,10 @@ msgstr "" msgid "binary op %q not implemented" msgstr "" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3161,6 +3203,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "" diff --git a/locale/de_DE.po b/locale/de_DE.po index d69eb166d4..e9c7aeaaea 100644 --- a/locale/de_DE.po +++ b/locale/de_DE.po @@ -53,6 +53,10 @@ msgstr " Datei \"%q\", Zeile %d" msgid " is of type %q\n" msgstr " ist vom Type %q\n" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " Ausgabe:\n" @@ -91,10 +95,22 @@ msgstr "Der Index %q befindet sich außerhalb des Bereiches" msgid "%q indices must be integers, not %s" msgstr "%q Indizes müssen Integer sein, nicht %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "%q Liste muss eine Liste sein" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "%q muss 0-255 sein" @@ -103,7 +119,11 @@ msgstr "%q muss 0-255 sein" msgid "%q must be 1-255" msgstr "%q muss 1-255 sein" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q muss >= 0 sein" @@ -119,6 +139,10 @@ msgstr "%q muss >= 1 sein" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "%q muss None oder zwischen 1 und len(report_descriptor)-1 sein" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q muss ein Tupel der Länge 2 sein" @@ -127,6 +151,10 @@ msgstr "%q muss ein Tupel der Länge 2 sein" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -577,10 +605,6 @@ msgstr "Der Puffer muss ein vielfaches von 512 bytes sein" msgid "Buffer must be at least length 1" msgstr "Der Puffer muss eine Mindestenslänge von 1 haben" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Puffer zu groß und kann nicht reserviert werden" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -859,6 +883,10 @@ msgstr "Data 0 Pin muss Byte aligned sein." msgid "Data chunk must follow fmt chunk" msgstr "Dem fmt Block muss ein Datenblock folgen" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Zu vielen Daten für das advertisement packet" @@ -1659,7 +1687,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1706,6 +1733,10 @@ msgstr "" "Nur Windows-Format, unkomprimiertes BMP unterstützt: die gegebene Header-" "Größe ist %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1729,6 +1760,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2029,6 +2061,10 @@ msgstr "SPI-Init-Fehler" msgid "SPI Re-initialization error" msgstr "SPI-Neuinitialisierungsfehler" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2067,6 +2103,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2214,6 +2251,7 @@ msgstr "Zu viele displays" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2520,7 +2558,7 @@ msgstr "Das Argument argsort muss ein ndarray sein" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "Argument hat falschen Typ" @@ -2595,6 +2633,10 @@ msgstr "Falscher Typcode" msgid "binary op %q not implemented" msgstr "Der binäre Operator %q ist nicht implementiert" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3220,6 +3262,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "graphic muss 2048 Byte lang sein" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heap muss eine Liste sein" @@ -4448,6 +4494,9 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Puffer zu groß und kann nicht reserviert werden" + #~ msgid "wrong operand type" #~ msgstr "falscher Operandentyp" diff --git a/locale/el.po b/locale/el.po index c8d5ea37a4..f80452ea4a 100644 --- a/locale/el.po +++ b/locale/el.po @@ -47,6 +47,10 @@ msgstr "" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr "" @@ -83,10 +87,22 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -95,7 +111,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "" @@ -111,6 +131,10 @@ msgstr "" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "" @@ -119,6 +143,10 @@ msgstr "" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -566,10 +594,6 @@ msgstr "" msgid "Buffer must be at least length 1" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -844,6 +868,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "" @@ -1638,7 +1666,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1681,6 +1708,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1702,6 +1733,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -1997,6 +2029,10 @@ msgstr "" msgid "SPI Re-initialization error" msgstr "" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2035,6 +2071,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2179,6 +2216,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2472,7 +2510,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "" @@ -2547,6 +2585,10 @@ msgstr "" msgid "binary op %q not implemented" msgstr "" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3158,6 +3200,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "" diff --git a/locale/en_GB.po b/locale/en_GB.po index 970dd26a0e..8df32c33a6 100644 --- a/locale/en_GB.po +++ b/locale/en_GB.po @@ -55,6 +55,10 @@ msgstr " File \"%q\", line %d" msgid " is of type %q\n" msgstr " is of type %q\n" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " output:\n" @@ -92,10 +96,22 @@ msgstr "%q index out of range" msgid "%q indices must be integers, not %s" msgstr "" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "%q list must be a list" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -104,7 +120,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q must be >= 0" @@ -120,6 +140,10 @@ msgstr "%q must be >= 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q must be a tuple of length 2" @@ -128,6 +152,10 @@ msgstr "%q must be a tuple of length 2" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -577,10 +605,6 @@ msgstr "Buffer must be a multiple of 512 bytes" msgid "Buffer must be at least length 1" msgstr "Buffer must be at least length 1" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Buffer too large and unable to allocate" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -857,6 +881,10 @@ msgstr "Data 0 pin must be byte aligned." msgid "Data chunk must follow fmt chunk" msgstr "Data chunk must follow fmt chunk" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Data too large for advertisement packet" @@ -1653,7 +1681,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "Not a valid IP string" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1698,6 +1725,10 @@ msgid "" msgstr "" "Only Windows format, uncompressed BMP supported: given header size is %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1721,6 +1752,7 @@ msgstr "Only one TouchAlarm can be set in deep sleep." #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "Only one alarm.time alarm can be set." @@ -2025,6 +2057,10 @@ msgstr "SPI init error" msgid "SPI Re-initialization error" msgstr "SPI reinitialisation error" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "SPI peripheral in use" @@ -2063,6 +2099,7 @@ msgstr "Side set pin count must be between 1 and 5" msgid "Size not supported" msgstr "Size not supported" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2207,6 +2244,7 @@ msgstr "Too many displays" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2507,7 +2545,7 @@ msgstr "argsort argument must be an ndarray" msgid "argsort is not implemented for flattened arrays" msgstr "argsort is not implemented for flattened arrays" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "argument has wrong type" @@ -2582,6 +2620,10 @@ msgstr "bad typecode" msgid "binary op %q not implemented" msgstr "binary op %q not implemented" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "bits must be in range 5 to 9" @@ -3196,6 +3238,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "graphic must be 2048 bytes long" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heap must be a list" @@ -4410,6 +4456,9 @@ msgstr "zi must be of float type" msgid "zi must be of shape (n_section, 2)" msgstr "zi must be of shape (n_section, 2)" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Buffer too large and unable to allocate" + #~ msgid "interp is defined for 1D arrays of equal length" #~ msgstr "interp is defined for 1D arrays of equal length" diff --git a/locale/es.po b/locale/es.po index 720347396d..00e787413d 100644 --- a/locale/es.po +++ b/locale/es.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2021-06-13 04:32+0000\n" +"PO-Revision-Date: 2021-06-27 12:33+0000\n" "Last-Translator: Alvaro Figueroa \n" "Language-Team: \n" "Language: es\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.7.1-dev\n" #: main.c msgid "" @@ -56,6 +56,10 @@ msgstr " Archivo \"%q\", línea %d" msgid " is of type %q\n" msgstr " es de tipo %q\n" +#: main.c +msgid " not found.\n" +msgstr " no encontrado.\n" + #: main.c msgid " output:\n" msgstr " salida:\n" @@ -94,10 +98,22 @@ msgstr "%q indice fuera de rango" msgid "%q indices must be integers, not %s" msgstr "%q indices deben ser enteros, no %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "el tamaño de %q debe ser %q" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "%q lista debe ser una lista" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "%q debe ser <= %d" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "%q debe ser %d-%d" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "%q debe ser de 0-255" @@ -106,7 +122,11 @@ msgstr "%q debe ser de 0-255" msgid "%q must be 1-255" msgstr "%q debe estar entre 1-255" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "%q debe ser >= %d" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q debe ser >= 0" @@ -122,13 +142,21 @@ msgstr "%q debe ser >= 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "%q debe ser None o entre 1 y len(report_descriptor)-1" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "%q debe ser una cadena" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q debe ser una tupla de longitud 2" #: ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c msgid "%q must be between %d and %d" -msgstr "" +msgstr "%q debe estar entre %d y %d" + +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "%q debe ser de tipo %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c @@ -582,10 +610,6 @@ msgstr "Búfer deber ser un múltiplo de 512 bytes" msgid "Buffer must be at least length 1" msgstr "Buffer debe ser de longitud 1 como minimo" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Buffer demasiado grande e incapaz de asignar" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -866,6 +890,10 @@ msgstr "El pin de datos 0 debe ser alineado a byte." msgid "Data chunk must follow fmt chunk" msgstr "Trozo de datos debe seguir fmt chunk" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "Datos sin capacidad de anuncio dirigido" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Data es muy grande para el paquete de anuncio" @@ -1320,7 +1348,7 @@ msgstr "data_count inválido %d" #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" -msgstr "" +msgstr "Inválidos los data_pins[%d]" #: shared-bindings/digitalio/DigitalInOut.c msgid "Invalid direction." @@ -1675,7 +1703,6 @@ msgstr "El firmware del sistema Nordic no tiene memoria" msgid "Not a valid IP string" msgstr "No es una cadena de IP válida" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1722,6 +1749,10 @@ msgstr "" "Solo formato de Windows, sin comprimir BMP soportado: tamaño de encabezado " "dado es %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "Solo se puede dirigir a los anuncios conectables" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "Este hardware solo tiene capacidad para detección de borde" @@ -1745,6 +1776,7 @@ msgstr "Solamente una TouchAlarm puede ser configurada durante deep sleep." #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "Solamente una alarm.time puede ser configurada." @@ -2053,6 +2085,10 @@ msgstr "Error de inicio de SPI" msgid "SPI Re-initialization error" msgstr "Error de reinicialización de SPI" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "Configuración de SPI fallida" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "Periférico SPI en uso" @@ -2091,6 +2127,7 @@ msgstr "El conteo de pines de Side set debe estar entre 1 y 5" msgid "Size not supported" msgstr "Sin capacidades para el tamaño" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "Memoria de sueño no disponible" @@ -2245,6 +2282,7 @@ msgstr "Muchos displays" msgid "Total data to write is larger than %q" msgstr "La cantidad total de datos es mas grande que %q" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "Alarmas táctiles no disponibles" @@ -2551,7 +2589,7 @@ msgstr "El argumento para argsort debe ser un ndarray" msgid "argsort is not implemented for flattened arrays" msgstr "El argot no está implementado para arrays aplanados" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "el argumento tiene un tipo erroneo" @@ -2626,6 +2664,10 @@ msgstr "typecode erroneo" msgid "binary op %q not implemented" msgstr "operacion binaria %q no implementada" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "los bits deben ser 32 o menos" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "los bits deben estar en el rango de 5 a 9" @@ -3244,6 +3286,10 @@ msgstr "el generador genero StopIteration" msgid "graphic must be 2048 bytes long" msgstr "graphic debe ser 2048 bytes de largo" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "el hash es final" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heap debe ser una lista" @@ -4465,6 +4511,12 @@ msgstr "zi debe ser de tipo flotante" msgid "zi must be of shape (n_section, 2)" msgstr "zi debe ser una forma (n_section,2)" +#~ msgid "%q must store bytes" +#~ msgstr "%q debe almacenar bytes" + +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Buffer demasiado grande e incapaz de asignar" + #~ msgid "interp is defined for 1D arrays of equal length" #~ msgstr "interp está definido para arreglos de 1D del mismo tamaño" diff --git a/locale/fil.po b/locale/fil.po index aff73c5fe8..094f344f92 100644 --- a/locale/fil.po +++ b/locale/fil.po @@ -46,6 +46,10 @@ msgstr " File \"%q\", line %d" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " output:\n" @@ -82,10 +86,22 @@ msgstr "%q indeks wala sa sakop" msgid "%q indices must be integers, not %s" msgstr "%q indeks ay dapat integers, hindi %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -94,7 +110,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "" @@ -111,6 +131,10 @@ msgstr "aarehas na haba dapat ang buffer slices" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "" @@ -119,6 +143,10 @@ msgstr "" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -571,10 +599,6 @@ msgstr "" msgid "Buffer must be at least length 1" msgstr "Buffer dapat ay hindi baba sa 1 na haba" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -853,6 +877,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "Dapat sunurin ng Data chunk ang fmt chunk" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c #, fuzzy msgid "Data too large for advertisement packet" @@ -1653,7 +1681,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c #, fuzzy @@ -1699,6 +1726,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1720,6 +1751,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2017,6 +2049,10 @@ msgstr "" msgid "SPI Re-initialization error" msgstr "" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2055,6 +2091,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2199,6 +2236,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2500,7 +2538,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "may maling type ang argument" @@ -2575,6 +2613,10 @@ msgstr "masamang typecode" msgid "binary op %q not implemented" msgstr "binary op %q hindi implemented" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3199,6 +3241,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "graphic ay dapat 2048 bytes ang haba" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "list dapat ang heap" diff --git a/locale/fr.po b/locale/fr.po index 8374c09032..90e9c26484 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -56,6 +56,10 @@ msgstr " Fichier \"%q\", ligne %d" msgid " is of type %q\n" msgstr " est de type %q\n" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " sortie :\n" @@ -94,10 +98,22 @@ msgstr "index %q hors de portée" msgid "%q indices must be integers, not %s" msgstr "les indices %q doivent être des entiers, pas %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "La liste %q doit être une liste" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -106,7 +122,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "%q doit être compris entre 1 et 255" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q doit être >= 0" @@ -122,6 +142,10 @@ msgstr "%q doit être >= 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q doit être un tuple de longueur 2" @@ -130,6 +154,10 @@ msgstr "%q doit être un tuple de longueur 2" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -583,10 +611,6 @@ msgstr "La mémoire tampon doit être un multiple de 512" msgid "Buffer must be at least length 1" msgstr "Le tampon doit être de longueur au moins 1" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Tampon trop volumineux et impossible à allouer" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -872,6 +896,10 @@ msgstr "La broche Data 0 doit être aligné sur l'octet." msgid "Data chunk must follow fmt chunk" msgstr "Un bloc de données doit suivre un bloc fmt" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Données trop volumineuses pour un paquet d'avertissement" @@ -1683,7 +1711,6 @@ msgstr "Logiciel systême Nordic hors de mémoire" msgid "Not a valid IP string" msgstr "Chaîne IP non valide" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1730,6 +1757,10 @@ msgstr "" "Seulement le format BMP Windows, non compressé est supporté : la taille de " "l'entête fournie est %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1753,6 +1784,7 @@ msgstr "Seulement une TouchAlarm peu être réglée en someil profond." #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "Seulement une alarme alarm.time peut être réglée." @@ -2062,6 +2094,10 @@ msgstr "Erreur d'initialisation SPI" msgid "SPI Re-initialization error" msgstr "Erreur de réinitialisation SPI" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "Périphérique SPI utilisé" @@ -2100,6 +2136,7 @@ msgstr "Nombre de broches Side configurées doit être entre 1 et 5" msgid "Size not supported" msgstr "Taille n'est pas supportée" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2245,6 +2282,7 @@ msgstr "Trop d'affichages" msgid "Total data to write is larger than %q" msgstr "Quantité de données à écrire est plus que %q" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2553,7 +2591,7 @@ msgstr "Le paramêtre argsort doit être un ndarray" msgid "argsort is not implemented for flattened arrays" msgstr "argsort n'est pas mis en œuvre pour les matrices aplatis" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "l'argument est d'un mauvais type" @@ -2628,6 +2666,10 @@ msgstr "mauvais code type" msgid "binary op %q not implemented" msgstr "opération binaire '%q' non implémentée" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "les bits doivent être compris entre 5 et 9" @@ -3252,6 +3294,10 @@ msgstr "générateur (generator) à surlevé StopIteration" msgid "graphic must be 2048 bytes long" msgstr "graphic doit être long de 2048 octets" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "le tas doit être une liste" @@ -4476,6 +4522,9 @@ msgstr "zi doit être de type float" msgid "zi must be of shape (n_section, 2)" msgstr "zi doit être de forme (n_section, 2)" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Tampon trop volumineux et impossible à allouer" + #~ msgid "interp is defined for 1D arrays of equal length" #~ msgstr "interp est défini pour les matrices 1D de longueur égale" diff --git a/locale/hi.po b/locale/hi.po index 6462e14c93..67f1c611fc 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -47,6 +47,10 @@ msgstr "" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr "" @@ -83,10 +87,22 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -95,7 +111,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "" @@ -111,6 +131,10 @@ msgstr "" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "" @@ -119,6 +143,10 @@ msgstr "" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -566,10 +594,6 @@ msgstr "" msgid "Buffer must be at least length 1" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -844,6 +868,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "" @@ -1638,7 +1666,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1681,6 +1708,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1702,6 +1733,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -1997,6 +2029,10 @@ msgstr "" msgid "SPI Re-initialization error" msgstr "" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2035,6 +2071,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2179,6 +2216,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2472,7 +2510,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "" @@ -2547,6 +2585,10 @@ msgstr "" msgid "binary op %q not implemented" msgstr "" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3158,6 +3200,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "" diff --git a/locale/it_IT.po b/locale/it_IT.po index 12974df63f..bca6205810 100644 --- a/locale/it_IT.po +++ b/locale/it_IT.po @@ -55,6 +55,10 @@ msgstr " File \"%q\", riga %d" msgid " is of type %q\n" msgstr " è di tipo %q\n" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " output:\n" @@ -92,10 +96,22 @@ msgstr "indice %q fuori intervallo" msgid "%q indices must be integers, not %s" msgstr "gli indici %q devono essere interi, non %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "lista %q deve essere una lista" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -104,7 +120,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q deve essere >= 0" @@ -121,6 +141,10 @@ msgstr "slice del buffer devono essere della stessa lunghezza" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q deve essere una tupla di lunghezza 2" @@ -129,6 +153,10 @@ msgstr "%q deve essere una tupla di lunghezza 2" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -581,10 +609,6 @@ msgstr "Il buffer deve essere un multiplo di 512 bytes" msgid "Buffer must be at least length 1" msgstr "Il buffer deve essere lungo almeno 1" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Buffer troppo grande ed impossibile allocare" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -863,6 +887,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c #, fuzzy msgid "Data too large for advertisement packet" @@ -1666,7 +1694,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c #, fuzzy @@ -1713,6 +1740,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1734,6 +1765,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2036,6 +2068,10 @@ msgstr "" msgid "SPI Re-initialization error" msgstr "" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2076,6 +2112,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2220,6 +2257,7 @@ msgstr "Troppi schermi" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2515,7 +2553,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "il tipo dell'argomento è errato" @@ -2590,6 +2628,10 @@ msgstr "" msgid "binary op %q not implemented" msgstr "operazione binaria %q non implementata" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3212,6 +3254,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "graphic deve essere lunga 2048 byte" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "l'heap deve essere una lista" @@ -4441,6 +4487,9 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Buffer troppo grande ed impossibile allocare" + #~ msgid "USB Busy" #~ msgstr "USB occupata" diff --git a/locale/ja.po b/locale/ja.po index 93e76b040c..9ec4898cde 100644 --- a/locale/ja.po +++ b/locale/ja.po @@ -52,6 +52,10 @@ msgstr " ファイル \"%q\", 行 %d" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " 出力:\n" @@ -88,10 +92,22 @@ msgstr "%q インデックスは範囲外" msgid "%q indices must be integers, not %s" msgstr "" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "%q リストはリストでなければなりません" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -100,7 +116,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%qは0以上でなければなりません" @@ -116,6 +136,10 @@ msgstr "%qは1以上でなければなりません" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%qは長さ2のタプルでなければなりません" @@ -124,6 +148,10 @@ msgstr "%qは長さ2のタプルでなければなりません" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -573,10 +601,6 @@ msgstr "バッファは512の倍数でなければなりません" msgid "Buffer must be at least length 1" msgstr "バッファ長は少なくとも1以上でなければなりません" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "バッファが大きすぎて確保できません" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -853,6 +877,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "fmtチャンクの後にdataチャンクが続かなければなりません" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "データが、アドバタイズメントパケットには大きすぎます" @@ -1649,7 +1677,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "不正なIP文字列です" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1694,6 +1721,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1715,6 +1746,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2011,6 +2043,10 @@ msgstr "SPI初期化エラー" msgid "SPI Re-initialization error" msgstr "SPI再初期化エラー" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2049,6 +2085,7 @@ msgstr "" msgid "Size not supported" msgstr "サイズは対応していません" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2193,6 +2230,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2487,7 +2525,7 @@ msgstr "argsortの引数はndarrayでなければなりません" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "" @@ -2562,6 +2600,10 @@ msgstr "不正なtypecode" msgid "binary op %q not implemented" msgstr "" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3177,6 +3219,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "graphicは2048バイトでなければなりません" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heapにはリストが必要" @@ -4392,6 +4438,9 @@ msgstr "ziはfloat値でなければなりません" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "バッファが大きすぎて確保できません" + #~ msgid "" #~ "CircuitPython is in safe mode because you pressed the reset button during " #~ "boot. Press again to exit safe mode.\n" diff --git a/locale/ko.po b/locale/ko.po index 623eeb6b90..1e5abe7858 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -48,6 +48,10 @@ msgstr " 파일 \"%q\", 라인 %d" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " 산출:\n" @@ -84,10 +88,22 @@ msgstr "%q 인덱스 범위를 벗어났습니다" msgid "%q indices must be integers, not %s" msgstr "%q 인덱스는 %s 가 아닌 정수 여야합니다" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -96,7 +112,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "" @@ -112,6 +132,10 @@ msgstr "%q 는 >=1이어야합니다" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "" @@ -120,6 +144,10 @@ msgstr "" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -569,10 +597,6 @@ msgstr "" msgid "Buffer must be at least length 1" msgstr "잘못된 크기의 버퍼. >1 여야합니다" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -847,6 +871,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "광고 (브로드 캐스트) 패킷에 대한 데이터가 너무 큽니다" @@ -1641,7 +1669,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1684,6 +1711,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1705,6 +1736,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2000,6 +2032,10 @@ msgstr "" msgid "SPI Re-initialization error" msgstr "" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2038,6 +2074,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2182,6 +2219,7 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2476,7 +2514,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "" @@ -2551,6 +2589,10 @@ msgstr "" msgid "binary op %q not implemented" msgstr "" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3162,6 +3204,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "" diff --git a/locale/nl.po b/locale/nl.po index be285c0d9b..cc305dbd4c 100644 --- a/locale/nl.po +++ b/locale/nl.po @@ -50,6 +50,10 @@ msgstr " Bestand \"%q\", regel %d" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " uitvoer:\n" @@ -86,10 +90,22 @@ msgstr "%q index buiten bereik" msgid "%q indices must be integers, not %s" msgstr "%q indexen moeten integers zijn, niet %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "%q lijst moet een lijst zijn" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -98,7 +114,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q moet >= 0 zijn" @@ -114,6 +134,10 @@ msgstr "%q moet >= 1 zijn" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q moet een tuple van lengte 2 zijn" @@ -122,6 +146,10 @@ msgstr "%q moet een tuple van lengte 2 zijn" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -571,10 +599,6 @@ msgstr "Buffer moet een veelvoud van 512 bytes zijn" msgid "Buffer must be at least length 1" msgstr "Buffer moet op zijn minst lengte 1 zijn" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Buffer is te groot en niet in staat te alloceren" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -853,6 +877,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "Data chunk moet gevolgd worden door fmt chunk" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Data te groot voor advertisement pakket" @@ -1650,7 +1678,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "Geen geldige IP string" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1697,6 +1724,10 @@ msgstr "" "Alleen Windows formaat en ongecomprimeerd BMP ondersteund: gegeven header " "grootte is %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1720,6 +1751,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "Slechts één alarm.time alarm kan worden ingesteld." @@ -2027,6 +2059,10 @@ msgstr "SPI Init Fout" msgid "SPI Re-initialization error" msgstr "SPI Herinitialisatie Fout" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2065,6 +2101,7 @@ msgstr "" msgid "Size not supported" msgstr "Afmeting niet ondersteund" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2209,6 +2246,7 @@ msgstr "Teveel beeldschermen" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2513,7 +2551,7 @@ msgstr "argsort argument moet een ndarray zijn" msgid "argsort is not implemented for flattened arrays" msgstr "argsort wordt niet geïmplementeerd voor vlakke arrays" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "argument heeft onjuist type" @@ -2588,6 +2626,10 @@ msgstr "verkeerde typecode" msgid "binary op %q not implemented" msgstr "binaire op %q niet geïmplementeerd" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3204,6 +3246,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "graphic moet 2048 bytes lang zijn" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heap moet een lijst zijn" @@ -4421,6 +4467,9 @@ msgstr "zi moet van type float zijn" msgid "zi must be of shape (n_section, 2)" msgstr "zi moet vorm (n_section, 2) hebben" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Buffer is te groot en niet in staat te alloceren" + #~ msgid "interp is defined for 1D arrays of equal length" #~ msgstr "" #~ "interp is gedefinieerd voor eendimensionale arrays van gelijke lengte" diff --git a/locale/pl.po b/locale/pl.po index 195007d9c8..c9335fd036 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -52,6 +52,10 @@ msgstr " Plik \"%q\", linia %d" msgid " is of type %q\n" msgstr "" +#: main.c +msgid " not found.\n" +msgstr "" + #: main.c msgid " output:\n" msgstr " wyjście:\n" @@ -88,10 +92,22 @@ msgstr "%q poza zakresem" msgid "%q indices must be integers, not %s" msgstr "%q indeks musi być liczbą całkowitą, a nie %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "" @@ -100,7 +116,11 @@ msgstr "" msgid "%q must be 1-255" msgstr "" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q musi być >= 0" @@ -116,6 +136,10 @@ msgstr "%q musi być >= 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q musi być krotką o długości 2" @@ -124,6 +148,10 @@ msgstr "%q musi być krotką o długości 2" msgid "%q must be between %d and %d" msgstr "" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -573,10 +601,6 @@ msgstr "Bufor musi być wielokrotnością 512 bajtów" msgid "Buffer must be at least length 1" msgstr "Bufor musi mieć długość 1 lub więcej" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Bufor jest zbyt duży i nie można go przydzielić" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -853,6 +877,10 @@ msgstr "" msgid "Data chunk must follow fmt chunk" msgstr "Fragment danych musi następować po fragmencie fmt" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Zbyt dużo danych pakietu rozgłoszeniowego" @@ -1649,7 +1677,6 @@ msgstr "" msgid "Not a valid IP string" msgstr "" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1692,6 +1719,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "Wspierane są tylko nieskompresowane pliki BMP: wielkość nagłówka %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "" @@ -1713,6 +1744,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -2008,6 +2040,10 @@ msgstr "Błąd inicjowania SPI" msgid "SPI Re-initialization error" msgstr "Błąd ponownej inicjalizacji SPI" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2046,6 +2082,7 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "" @@ -2190,6 +2227,7 @@ msgstr "Zbyt wiele wyświetlaczy" msgid "Total data to write is larger than %q" msgstr "" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "" @@ -2489,7 +2527,7 @@ msgstr "" msgid "argsort is not implemented for flattened arrays" msgstr "" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "argument ma zły typ" @@ -2564,6 +2602,10 @@ msgstr "zły typecode" msgid "binary op %q not implemented" msgstr "brak dwu-argumentowego operatora %q" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "" @@ -3176,6 +3218,10 @@ msgstr "" msgid "graphic must be 2048 bytes long" msgstr "graphic musi mieć 2048 bajtów długości" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heap musi być listą" @@ -4389,6 +4435,9 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Bufor jest zbyt duży i nie można go przydzielić" + #~ msgid "wrong operand type" #~ msgstr "zły typ operandu" diff --git a/locale/pt_BR.po b/locale/pt_BR.po index 79c836bbd1..940e3e5e44 100644 --- a/locale/pt_BR.po +++ b/locale/pt_BR.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2021-06-21 22:27+0000\n" +"PO-Revision-Date: 2021-06-30 17:19+0000\n" "Last-Translator: Wellington Terumi Uemura \n" "Language-Team: \n" "Language: pt_BR\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7\n" +"X-Generator: Weblate 4.7.1-dev\n" #: main.c msgid "" @@ -54,6 +54,10 @@ msgstr " Arquivo \"%q\", linha %d" msgid " is of type %q\n" msgstr " é do tipo %q\n" +#: main.c +msgid " not found.\n" +msgstr " não encontrado.\n" + #: main.c msgid " output:\n" msgstr " saída:\n" @@ -92,10 +96,22 @@ msgstr "O índice %q está fora do intervalo" msgid "%q indices must be integers, not %s" msgstr "Os índices %q devem ser inteiros, e não %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "o comprimento %q deve ser %q" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "A lista %q deve ser uma lista" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "o %q deve ser <= %d" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "o %q deve ser %d-%d" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "%q deve ser entre 0-255" @@ -104,7 +120,11 @@ msgstr "%q deve ser entre 0-255" msgid "%q must be 1-255" msgstr "%q deve ser 1-255" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "o %q deve ser >= %d" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q deve ser >= 0" @@ -120,6 +140,10 @@ msgstr "%q deve ser >= 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "%q deve ser None ou entre 1 e len(report_descriptor)-1" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "%q deve ser uma string" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q deve ser uma tupla de comprimento 2" @@ -128,6 +152,10 @@ msgstr "%q deve ser uma tupla de comprimento 2" msgid "%q must be between %d and %d" msgstr "%q deve estar entre %d e %d" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "o %q deve ser do tipo %q" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -584,10 +612,6 @@ msgstr "O buffer deve ser um múltiplo de 512 bytes" msgid "Buffer must be at least length 1" msgstr "O comprimento do buffer deve ter pelo menos 1" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "O buffer é muito grande e incapaz de alocar" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -869,6 +893,10 @@ msgstr "O pino de dados 0 deve ser alinhado por bytes." msgid "Data chunk must follow fmt chunk" msgstr "Pedaço de dados deve seguir o pedaço de cortes" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "Os dados não são compatíveis com publicidade direcionada" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Os dados são grandes demais para o pacote de publicidade" @@ -1674,7 +1702,6 @@ msgstr "O firmware do sistema nórdico está sem memória" msgid "Not a valid IP string" msgstr "Não é uma sequência válida de IP" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1720,6 +1747,10 @@ msgstr "" "O BMP descompactado é compatível apenas no formato Windows: o tamanho do " "cabeçalho é %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "Somente anúncios conectáveis podem ser direcionados" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "Apenas a detecção de borda está disponível neste hardware" @@ -1743,6 +1774,7 @@ msgstr "Apenas um TouchAlarm pode ser colocado em deep sleep." #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "Apenas um alarme alarm.time pode ser definido." @@ -2054,6 +2086,10 @@ msgstr "Houve um erro na inicialização SPI" msgid "SPI Re-initialization error" msgstr "Houve um erro na reinicialização SPI" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "A configuração SPI falhou" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "O periférico SPI está em uso" @@ -2093,6 +2129,7 @@ msgstr "" msgid "Size not supported" msgstr "O tamanho não é suportado" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "Sleep memory não está disponível" @@ -2248,6 +2285,7 @@ msgstr "Exibições demais" msgid "Total data to write is larger than %q" msgstr "O total dos dados que serão escritos é maior do que %q" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "Alarmes de toque não estão disponíveis" @@ -2556,7 +2594,7 @@ msgstr "O argumento argsort deve ser um ndarray" msgid "argsort is not implemented for flattened arrays" msgstr "argsort não é implementado para matrizes achatadas" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "argumento tem tipo errado" @@ -2631,6 +2669,10 @@ msgstr "typecode incorreto" msgid "binary op %q not implemented" msgstr "a operação binário %q não foi implementada" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "bits deve ser 32 ou menos" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "os bits devem estar na faixa entre 5 a 9" @@ -3251,6 +3293,10 @@ msgstr "gerador StopIteration elevado" msgid "graphic must be 2048 bytes long" msgstr "o gráfico deve ter 2048 bytes de comprimento" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "o hash é final" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "a área de alocação dinâmica de variáveis (heap) deve ser uma lista" @@ -4475,6 +4521,12 @@ msgstr "zi deve ser de um tipo float" msgid "zi must be of shape (n_section, 2)" msgstr "zi deve estar na forma (n_section, 2)" +#~ msgid "%q must store bytes" +#~ msgstr "o %q deve armazenar bytes" + +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "O buffer é muito grande e incapaz de alocar" + #~ msgid "interp is defined for 1D arrays of equal length" #~ msgstr "o interp é definido para matrizes 1D de igual comprimento" diff --git a/locale/sv.po b/locale/sv.po index 1e1902a596..d1018e2f6b 100644 --- a/locale/sv.po +++ b/locale/sv.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2021-06-21 13:39+0000\n" +"PO-Revision-Date: 2021-06-27 12:33+0000\n" "Last-Translator: Jonny Bergdahl \n" "Language-Team: LANGUAGE \n" "Language: sv\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7\n" +"X-Generator: Weblate 4.7.1-dev\n" #: main.c msgid "" @@ -54,6 +54,10 @@ msgstr " Fil \"%q\", rad %d" msgid " is of type %q\n" msgstr " är av typen %q\n" +#: main.c +msgid " not found.\n" +msgstr " hittades inte.\n" + #: main.c msgid " output:\n" msgstr " utdata:\n" @@ -91,10 +95,22 @@ msgstr "Index %q ligger utanför intervallet" msgid "%q indices must be integers, not %s" msgstr "Indexet %q måste vara ett heltal, inte %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "längden på %q måste vara %q" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "%q-listan måste vara en lista" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "%q måste vara <=%d" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "%q måste vara %d-%d" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "%q måste vara 0-255" @@ -103,7 +119,11 @@ msgstr "%q måste vara 0-255" msgid "%q must be 1-255" msgstr "%q måste vara 1-255" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "%q måste vara >= %d" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q måste vara >= 0" @@ -119,6 +139,10 @@ msgstr "%q måste vara >= 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "%q måste vara None eller mellan 1 och len(report_descriptor)-1" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "%q måste vara en sträng" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q måste vara en tuple av längd 2" @@ -127,6 +151,10 @@ msgstr "%q måste vara en tuple av längd 2" msgid "%q must be between %d and %d" msgstr "%q måste vara mellan %d och %d" +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "%q måste av typen %q" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -576,10 +604,6 @@ msgstr "Bufferten måste vara en multipel av 512 byte" msgid "Buffer must be at least length 1" msgstr "Bufferten måste ha minst längd 1" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Bufferten är för stor och kan inte allokeras" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" @@ -859,6 +883,10 @@ msgstr "Datapinne 0 måste vara byte-justerad." msgid "Data chunk must follow fmt chunk" msgstr "Datasegmentet måste följa fmt-segmentet" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "Data stöds inte med riktad annonsering" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Data för stor för annonseringspaket" @@ -1658,7 +1686,6 @@ msgstr "Nordic systemfirmware fick slut på minne" msgid "Not a valid IP string" msgstr "Inte en giltig IP-sträng" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1704,6 +1731,10 @@ msgid "" msgstr "" "Endast Windows-format, okomprimerad BMP stöds: given headerstorlek är %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "Endast anslutningsbara annonseringar kan dirigeras" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "Endast kantdetektering är tillgänglig för denna hårdvara" @@ -1727,6 +1758,7 @@ msgstr "Endast ett TouchAlarm kan ställas in för djupsömn." #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "Endast ett alarm.time kan ställas in." @@ -2033,6 +2065,10 @@ msgstr "SPI Init-fel" msgid "SPI Re-initialization error" msgstr "SPI reinitialiseringsfel" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "SPI-konfigurationen misslyckades" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "SPI-enhet används redan" @@ -2071,6 +2107,7 @@ msgstr "Sido-setets antal pinnar måste vara mellan 1 och 5" msgid "Size not supported" msgstr "Storleken stöds inte" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "Sömnminne inte tillgängligt" @@ -2223,6 +2260,7 @@ msgstr "För många displayer" msgid "Total data to write is larger than %q" msgstr "Totala data att skriva är större än %q" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "Touchalarm är inte tillgängligt" @@ -2526,7 +2564,7 @@ msgstr "argumentet argsort måste vara en ndarray" msgid "argsort is not implemented for flattened arrays" msgstr "argsort är inte implementerad för tillplattade matriser" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "argumentet har fel typ" @@ -2601,6 +2639,10 @@ msgstr "Ogiltig typkod" msgid "binary op %q not implemented" msgstr "binär op %q är inte implementerad" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "bits måste vara 32 eller färre" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "bits måste mellan 5 och 9" @@ -3217,6 +3259,10 @@ msgstr "generator kastade StopIteration" msgid "graphic must be 2048 bytes long" msgstr "graphic måste vara 2048 byte lång" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "hash är slutgiltig" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "heap måste vara en lista" @@ -4434,6 +4480,12 @@ msgstr "zi måste vara av typ float" msgid "zi must be of shape (n_section, 2)" msgstr "zi måste vara i formen (n_section, 2)" +#~ msgid "%q must store bytes" +#~ msgstr "%q måste lagra bytes" + +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Bufferten är för stor och kan inte allokeras" + #~ msgid "interp is defined for 1D arrays of equal length" #~ msgstr "interp är definierad för 1D-matriser med samma längd" diff --git a/locale/zh_Latn_pinyin.po b/locale/zh_Latn_pinyin.po index 89e0c13a0d..e357c4eda5 100644 --- a/locale/zh_Latn_pinyin.po +++ b/locale/zh_Latn_pinyin.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: circuitpython-cn\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2021-06-19 20:18+0000\n" +"PO-Revision-Date: 2021-07-06 16:32+0000\n" "Last-Translator: hexthat \n" "Language-Team: Chinese Hanyu Pinyin\n" "Language: zh_Latn_pinyin\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.7\n" +"X-Generator: Weblate 4.8-dev\n" #: main.c msgid "" @@ -55,6 +55,10 @@ msgstr " Wénjiàn \"%q\", dì %d xíng" msgid " is of type %q\n" msgstr " shì %q lèi xíng\n" +#: main.c +msgid " not found.\n" +msgstr " wèi zhǎo dào.\n" + #: main.c msgid " output:\n" msgstr " shūchū:\n" @@ -93,10 +97,22 @@ msgstr "%q suǒyǐn chāochū fànwéi" msgid "%q indices must be integers, not %s" msgstr "%q suǒyǐn bìxū shì zhěngshù, ér bùshì %s" +#: py/argcheck.c +msgid "%q length must be %q" +msgstr "%q cháng dù bì xū wéi %q" + #: shared-bindings/vectorio/Polygon.c msgid "%q list must be a list" msgstr "%q lièbiǎo bìxū shì lièbiǎo" +#: py/argcheck.c +msgid "%q must <= %d" +msgstr "%q bì xū <= %d" + +#: py/argcheck.c +msgid "%q must be %d-%d" +msgstr "%q bì xū wéi %d-%d" + #: shared-bindings/usb_hid/Device.c msgid "%q must be 0-255" msgstr "%q bì xū wéi 0-255" @@ -105,7 +121,11 @@ msgstr "%q bì xū wéi 0-255" msgid "%q must be 1-255" msgstr "%q bì xū wéi 1-255" -#: shared-bindings/memorymonitor/AllocationAlarm.c +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "%q bì xū >= %d" + +#: py/argcheck.c shared-bindings/memorymonitor/AllocationAlarm.c msgid "%q must be >= 0" msgstr "%q Bìxū > = 0" @@ -121,13 +141,21 @@ msgstr "%q bìxū dàyú huò děngyú 1" msgid "%q must be None or between 1 and len(report_descriptor)-1" msgstr "%q bì xū wéi wú huò zài 1 hé len(report_descriptor)-1 zhī jiān" +#: py/argcheck.c +msgid "%q must be a string" +msgstr "%q bì xū shì yí gè zì fú chuàn" + #: shared-module/vectorio/Polygon.c msgid "%q must be a tuple of length 2" msgstr "%q bìxū shì chángdù wèi 2 de yuán zǔ" #: ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c msgid "%q must be between %d and %d" -msgstr "" +msgstr "%q bì xū zài %d hé %d zhī jiān" + +#: py/argcheck.c +msgid "%q must of type %q" +msgstr "%q bì xū lèi xíng %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c @@ -506,7 +534,7 @@ msgstr "Bǐtè shízhōng hé dānzì xuǎnzé bìxū gòngxiǎng shízhōng dā #: shared-bindings/rgbmatrix/RGBMatrix.c #, c-format msgid "Bit depth must be from 1 to 6 inclusive, not %d" -msgstr "wèi shēn dù bì xū bāo hán 1 dào 6, ér bù shì %d" +msgstr "wèi shēn dù bì xū bāo hán 1 dào 6, ér bù shì %d" #: shared-bindings/audiobusio/PDMIn.c msgid "Bit depth must be multiple of 8." @@ -578,14 +606,10 @@ msgstr "Huǎn chōng qū bì xū shì 512 zì jié de bèi shù" msgid "Buffer must be at least length 1" msgstr "Huǎnchōng qū bìxū zhìshǎo chángdù 1" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c -msgid "Buffer too large and unable to allocate" -msgstr "Huǎn chōng qū tài dà , wú fǎ fēn pèi" - #: shared-bindings/_bleio/PacketBuffer.c #, c-format msgid "Buffer too short by %d bytes" -msgstr "Huǎn chōng qū tài duǎn , àn %d zì jié" +msgstr "Huǎn chōng qū tài duǎn , àn %d zì jié" #: ports/atmel-samd/common-hal/displayio/ParallelBus.c #: ports/esp32s2/common-hal/displayio/ParallelBus.c @@ -858,6 +882,10 @@ msgstr "shù jù 0 yǐn jiǎo bì xū àn zì jié duì qí." msgid "Data chunk must follow fmt chunk" msgstr "Shùjù kuài bìxū zūnxún fmt qū kuài" +#: ports/nrf/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "bù zhī chí dìng xiàng guǎng gào de shù jù" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Data too large for advertisement packet" msgstr "Guǎnggào bāo de shùjù tài dà" @@ -1309,7 +1337,7 @@ msgstr "wú xiào data_count %d" #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" -msgstr "" +msgstr "wú xiào data_pins[%d]" #: shared-bindings/digitalio/DigitalInOut.c msgid "Invalid direction." @@ -1661,7 +1689,6 @@ msgstr "běi ōu xì tǒng gù jiàn chū nèi cún" msgid "Not a valid IP string" msgstr "Wúxiào de IP zìfú chuàn" -#: ports/nrf/common-hal/_bleio/PacketBuffer.c #: ports/nrf/common-hal/_bleio/__init__.c #: shared-bindings/_bleio/CharacteristicBuffer.c msgid "Not connected" @@ -1707,6 +1734,10 @@ msgstr "" "Jǐn zhīchí Windows géshì, zhīchí wèi yāsuō de BMP: Gěi dìng de biāo tóu " "dàxiǎo wèi %d" +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "zhǐ yǒu kě lián jiē de guǎng gào cái néng bèi yǐn dǎo" + #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" msgstr "cǐ yìng jiàn shàng jǐn tí gòng biān yuán jiǎn cè" @@ -1730,6 +1761,7 @@ msgstr "zhǐ yǒu yí gè chù mō bì kě yǐ shè zhì zài shēn dù shuì mi #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "zhǐ néng shè zhì yí gè bào jǐng." @@ -2032,6 +2064,10 @@ msgstr "SPI chūshǐhuà cuòwù" msgid "SPI Re-initialization error" msgstr "SPI chóngxīn chūshǐhuà cuòwù" +#: ports/esp32s2/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "SPI pèi zhì shī bài" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "SPI wài shè zhèng zài shǐ yòng zhōng" @@ -2070,6 +2106,7 @@ msgstr "cè miàn shè zhì yǐn jiǎo shù bì xū jiè yú 1 hé 5 zhī jiān" msgid "Size not supported" msgstr "bù zhī chí dà xiǎo" +#: ports/raspberrypi/common-hal/alarm/SleepMemory.c #: ports/stm/common-hal/alarm/SleepMemory.c msgid "Sleep Memory not available" msgstr "shuì mián jì yì bù kě yòng" @@ -2135,7 +2172,7 @@ msgid "" "Increase the stack size if you know how. If not:" msgstr "" "diàn lù dàn duī bèi sǔn huài, yīn wéi duī zhàn tài xiǎo.\n" -"rú guǒ nín zhī dào rú hé zēng jiā duī zhàn dà xiǎo. rú guǒ méi yǒu:" +"rú guǒ nín zhī dào rú hé zēng jiā duī zhàn dà xiǎo. rú guǒ méi yǒu:" #: supervisor/shared/safe_mode.c msgid "" @@ -2221,6 +2258,7 @@ msgstr "Xiǎnshì tài duō" msgid "Total data to write is larger than %q" msgstr "yào biān xiě de zǒng shù jù dà yú %q" +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c #: ports/stm/common-hal/alarm/touch/TouchAlarm.c msgid "Touch alarms not available" msgstr "bù kě yòng chù mō bào jǐng qì" @@ -2316,7 +2354,7 @@ msgstr "Wúfǎ xiě rù nvm." #: shared-bindings/alarm/SleepMemory.c msgid "Unable to write to sleep_memory." -msgstr "wú fǎ xiě rù sleep_memory。" +msgstr "wú fǎ xiě rù sleep_memory." #: ports/nrf/common-hal/_bleio/UUID.c msgid "Unexpected nrfx uuid type" @@ -2525,7 +2563,7 @@ msgstr "argsort cānshù bìxū shì ndarray" msgid "argsort is not implemented for flattened arrays" msgstr "wèi wéi pīn hé shù zǔ shí xiàn argsort" -#: py/runtime.c +#: py/runtime.c shared-bindings/supervisor/__init__.c msgid "argument has wrong type" msgstr "cānshù lèixíng cuòwù" @@ -2600,6 +2638,10 @@ msgstr "cuòwù de dàimǎ lèixíng" msgid "binary op %q not implemented" msgstr "èrjìnzhì bǎn qián bǎn %q wèi zhíxíng" +#: extmod/modurandom.c +msgid "bits must be 32 or less" +msgstr "wèi bì xū shì 32 huò gèng shǎo" + #: shared-bindings/busio/UART.c msgid "bits must be in range 5 to 9" msgstr "wèi bì xū zài fàn wéi nèi 5 zhì 9" @@ -3217,6 +3259,10 @@ msgstr "fā diàn jī tí chū tíng zhǐ" msgid "graphic must be 2048 bytes long" msgstr "túxíng bìxū wèi 2048 zì jié" +#: extmod/moduhashlib.c +msgid "hash is final" +msgstr "hā xī shì zuì zhōng de" + #: extmod/moduheapq.c msgid "heap must be a list" msgstr "duī bìxū shì yīgè lièbiǎo" @@ -3368,7 +3414,7 @@ msgstr "wúxiào de cānshù" #: shared-bindings/bitmaptools/__init__.c #, c-format msgid "invalid bits_per_pixel %d, must be, 1, 4, 8, 16, 24, or 32" -msgstr "wú xiào bits_per_pixel %d, bì xū shì, 1, 4, 8, 16, 24, huò 32" +msgstr "wú xiào bits_per_pixel %d, bì xū shì, 1, 4, 8, 16, 24, huò 32" #: shared-bindings/bitmaptools/__init__.c #, c-format @@ -3827,7 +3873,7 @@ msgstr "chāo gāo zhuǎnhuàn zhǎng zhěng shùzì shí" #: py/modstruct.c #, c-format msgid "pack expected %d items for packing (got %d)" -msgstr "bāo zhuāng yù qī de %d bāo zhuāng xiàng mù (dé dào %d)" +msgstr "bāo zhuāng yù qī de %d bāo zhuāng xiàng mù (dé dào %d)" #: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c msgid "palette must be 32 bytes long" @@ -4431,6 +4477,12 @@ msgstr "zi bìxū wèi fú diǎn xíng" msgid "zi must be of shape (n_section, 2)" msgstr "zi bìxū jùyǒu xíngzhuàng (n_section,2)" +#~ msgid "%q must store bytes" +#~ msgstr "%q bì xū cún chǔ zì jié" + +#~ msgid "Buffer too large and unable to allocate" +#~ msgstr "Huǎn chōng qū tài dà , wú fǎ fēn pèi" + #~ msgid "interp is defined for 1D arrays of equal length" #~ msgstr "interp shì wèi děng zhǎng de 1D shùzǔ dìngyì de" diff --git a/main.c b/main.c index 94d35fb8d7..395b8c3057 100755 --- a/main.c +++ b/main.c @@ -71,7 +71,7 @@ #if CIRCUITPY_BLEIO #include "shared-bindings/_bleio/__init__.h" -#include "supervisor/shared/bluetooth.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #endif #if CIRCUITPY_BOARD @@ -86,6 +86,10 @@ #include "shared-module/displayio/__init__.h" #endif +#if CIRCUITPY_KEYPAD +#include "shared-module/keypad/__init__.h" +#endif + #if CIRCUITPY_MEMORYMONITOR #include "shared-module/memorymonitor/__init__.h" #endif @@ -230,9 +234,11 @@ STATIC void cleanup_after_vm(supervisor_allocation* heap) { #if CIRCUITPY_DISPLAYIO reset_displays(); #endif + #if CIRCUITPY_MEMORYMONITOR memorymonitor_reset(); #endif + filesystem_flush(); stop_mp(); free_memory(heap); @@ -242,6 +248,10 @@ STATIC void cleanup_after_vm(supervisor_allocation* heap) { common_hal_canio_reset(); #endif + #if CIRCUITPY_KEYPAD + keypad_reset(); + #endif + // reset_board_busses() first because it may release pins from the never_reset state, so that // reset_port() can reset them. #if CIRCUITPY_BOARD @@ -280,7 +290,12 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { result.exception_type = NULL; result.exception_line = 0; + bool skip_repl; + bool skip_wait = false; bool found_main = false; + uint8_t next_code_options = 0; + // Collects stickiness bits that apply in the current situation. + uint8_t next_code_stickiness_situation = SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET; if (safe_mode == NO_SAFE_MODE) { static const char * const supported_filenames[] = STRING_LIST( @@ -302,25 +317,71 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { usb_setup_with_vm(); #endif - // This is where the user's python code is actually executed: - found_main = maybe_run_list(supported_filenames, &result); - // If that didn't work, double check the extensions - #if CIRCUITPY_FULL_BUILD - if (!found_main){ - found_main = maybe_run_list(double_extension_filenames, &result); - if (found_main) { - serial_write_compressed(translate("WARNING: Your code filename has two extensions\n")); + // Check if a different run file has been allocated + if (next_code_allocation) { + ((next_code_info_t*)next_code_allocation->ptr)->options &= ~SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET; + next_code_options = ((next_code_info_t*)next_code_allocation->ptr)->options; + if (((next_code_info_t*)next_code_allocation->ptr)->filename[0] != '\0') { + const char* next_list[] = {((next_code_info_t*)next_code_allocation->ptr)->filename, ""}; + // This is where the user's python code is actually executed: + found_main = maybe_run_list(next_list, &result); + if (!found_main) { + serial_write(((next_code_info_t*)next_code_allocation->ptr)->filename); + serial_write_compressed(translate(" not found.\n")); + } } } - #else - (void) found_main; - #endif + // Otherwise, default to the standard list of filenames + if (!found_main) { + // This is where the user's python code is actually executed: + found_main = maybe_run_list(supported_filenames, &result); + // If that didn't work, double check the extensions + #if CIRCUITPY_FULL_BUILD + if (!found_main){ + found_main = maybe_run_list(double_extension_filenames, &result); + if (found_main) { + serial_write_compressed(translate("WARNING: Your code filename has two extensions\n")); + } + } + #else + (void) found_main; + #endif + } // Finished executing python code. Cleanup includes a board reset. cleanup_after_vm(heap); + // If a new next code file was set, that is a reason to keep it (obviously). Stuff this into + // the options because it can be treated like any other reason-for-stickiness bit. The + // source is different though: it comes from the options that will apply to the next run, + // while the rest of next_code_options is what applied to this run. + if (next_code_allocation != NULL && (((next_code_info_t*)next_code_allocation->ptr)->options & SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET)) { + next_code_options |= SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET; + } + + if (reload_requested) { + next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD; + } + else if (result.return_code == 0) { + next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS; + if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS) { + skip_repl = true; + skip_wait = true; + } + } + else { + next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_ERROR; + // Deep sleep cannot be skipped + // TODO: settings in deep sleep should persist, using a new sleep memory API + if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR + && !(result.return_code & PYEXEC_DEEP_SLEEP)) { + skip_repl = true; + skip_wait = true; + } + } if (result.return_code & PYEXEC_FORCED_EXIT) { - return reload_requested; + skip_repl = reload_requested; + skip_wait = true; } if (reload_requested && result.return_code == PYEXEC_EXCEPTION) { @@ -368,12 +429,17 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { #if CIRCUITPY_ALARM bool fake_sleeping = false; #endif - bool skip_repl = false; - while (true) { + while (!skip_wait) { RUN_BACKGROUND_TASKS; // If a reload was requested by the supervisor or autoreload, return if (reload_requested) { + next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD; + // Should the STICKY_ON_SUCCESS and STICKY_ON_ERROR bits be cleared in + // next_code_stickiness_situation? I can see arguments either way, but I'm deciding + // "no" for now, mainly because it's a bit less code. At this point, we have both a + // success or error and a reload, so let's have both of the respective options take + // effect (in OR combination). reload_requested = false; skip_repl = true; break; @@ -508,6 +574,13 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { port_idle_until_interrupt(); } } + + // free code allocation if unused + if ((next_code_options & next_code_stickiness_situation) == 0) { + free_memory(next_code_allocation); + next_code_allocation = NULL; + } + // Done waiting, start the board back up. #if CIRCUITPY_STATUS_LED if (led_active) { @@ -521,6 +594,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { board_init(); } #endif + return skip_repl; } @@ -686,6 +760,11 @@ int __attribute__((used)) main(void) { stack_init(); + #if CIRCUITPY_BLEIO + // Early init so that a reset press can cause BLE public advertising. + supervisor_bluetooth_init(); + #endif + // Create a new filesystem only if we're not in a safe mode. // A power brownout here could make it appear as if there's // no SPI flash filesystem, and we might erase the existing one. diff --git a/ports/atmel-samd/audio_dma.c b/ports/atmel-samd/audio_dma.c index 7b1ca42c06..7f1260973d 100644 --- a/ports/atmel-samd/audio_dma.c +++ b/ports/atmel-samd/audio_dma.c @@ -131,7 +131,7 @@ void audio_dma_load_next_block(audio_dma_t *dma) { uint8_t *buffer; uint32_t buffer_length; audioio_get_buffer_result_t get_buffer_result = - audiosample_get_buffer(dma->sample, dma->single_channel, dma->audio_channel, + audiosample_get_buffer(dma->sample, dma->single_channel_output, dma->audio_channel, &buffer, &buffer_length); DmacDescriptor *descriptor = dma->second_descriptor; @@ -155,7 +155,7 @@ void audio_dma_load_next_block(audio_dma_t *dma) { descriptor->SRCADDR.reg = ((uint32_t)output_buffer) + output_buffer_length; if (get_buffer_result == GET_BUFFER_DONE) { if (dma->loop) { - audiosample_reset_buffer(dma->sample, dma->single_channel, dma->audio_channel); + audiosample_reset_buffer(dma->sample, dma->single_channel_output, dma->audio_channel); } else { descriptor->DESCADDR.reg = 0; } @@ -183,7 +183,7 @@ static void setup_audio_descriptor(DmacDescriptor *descriptor, uint8_t beat_size audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, mp_obj_t sample, bool loop, - bool single_channel, + bool single_channel_output, uint8_t audio_channel, bool output_signed, uint32_t output_register_address, @@ -195,7 +195,7 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, dma->sample = sample; dma->loop = loop; - dma->single_channel = single_channel; + dma->single_channel_output = single_channel_output; dma->audio_channel = audio_channel; dma->dma_channel = dma_channel; dma->signed_to_unsigned = false; @@ -203,12 +203,12 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, dma->second_descriptor = NULL; dma->spacing = 1; dma->first_descriptor_free = true; - audiosample_reset_buffer(sample, single_channel, audio_channel); + audiosample_reset_buffer(sample, single_channel_output, audio_channel); bool single_buffer; bool samples_signed; uint32_t max_buffer_length; - audiosample_get_buffer_structure(sample, single_channel, &single_buffer, &samples_signed, + audiosample_get_buffer_structure(sample, single_channel_output, &single_buffer, &samples_signed, &max_buffer_length, &dma->spacing); uint8_t output_spacing = dma->spacing; if (output_signed != samples_signed) { @@ -254,12 +254,12 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, } else { dma->beat_size = 1; dma->bytes_per_sample = 1; - if (single_channel) { + if (single_channel_output) { output_register_address += 1; } } // Transfer both channels at once. - if (!single_channel && audiosample_channel_count(sample) == 2) { + if (!single_channel_output && audiosample_channel_count(sample) == 2) { dma->beat_size *= 2; } diff --git a/ports/atmel-samd/audio_dma.h b/ports/atmel-samd/audio_dma.h index 4e7d193b1d..bc5beb3bf5 100644 --- a/ports/atmel-samd/audio_dma.h +++ b/ports/atmel-samd/audio_dma.h @@ -42,7 +42,7 @@ typedef struct { uint8_t beat_size; uint8_t spacing; bool loop; - bool single_channel; + bool single_channel_output; bool signed_to_unsigned; bool unsigned_to_signed; bool first_buffer_free; @@ -72,16 +72,16 @@ void dma_free_channel(uint8_t channel); // This sets everything up but doesn't start the timer. // Sample is the python object for the sample to play. // loop is true if we should loop the sample. -// single_channel is true if we only output a single channel. When false, all channels will be +// single_channel_output is true if we only output a single channel. When false, all channels will be // output. -// audio_channel is the index of the channel to dma. single_channel must be false in this case. +// audio_channel is the index of the channel to dma. single_channel_output must be false in this case. // output_signed is true if the dma'd data should be signed. False and it will be unsigned. // output_register_address is the address to copy data to. // dma_trigger_source is the DMA trigger source which cause another copy audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, mp_obj_t sample, bool loop, - bool single_channel, + bool single_channel_output, uint8_t audio_channel, bool output_signed, uint32_t output_register_address, diff --git a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk index 902e9594e3..fb65d7cd0a 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.mk @@ -14,6 +14,7 @@ LONGINT_IMPL = NONE CIRCUITPY_BUSDEVICE = 1 CIRCUITPY_DISPLAYIO = 0 +CIRCUITPY_KEYPAD = 0 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_CircuitPlayground diff --git a/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk index 68fb51ab00..3a97c39830 100644 --- a/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.mk @@ -11,6 +11,7 @@ EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C" # Turn off features and optimizations for displayio build to make room for additional frozen libs. LONGINT_IMPL = NONE +CIRCUITPY_KEYPAD = 0 CIRCUITPY_PIXELBUF = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 diff --git a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk index 300419fa19..d7230df4bb 100644 --- a/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/hallowing_m0_express/mpconfigboard.mk @@ -12,7 +12,6 @@ LONGINT_IMPL = NONE # To keep the build small CIRCUITPY_AUDIOBUSIO = 0 -CIRCUITPY_GAMEPAD = 0 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LIS3DH diff --git a/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk b/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk index d7b8a4bb1e..c2c583022c 100644 --- a/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk +++ b/ports/atmel-samd/boards/kicksat-sprite/mpconfigboard.mk @@ -15,6 +15,7 @@ CIRCUITPY_AUDIOMP3 = 0 CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_PS2IO = 0 CIRCUITPY_RGBMATRIX = 0 diff --git a/ports/atmel-samd/boards/meowmeow/mpconfigboard.mk b/ports/atmel-samd/boards/meowmeow/mpconfigboard.mk index 55c6bb6de3..ff96231b84 100644 --- a/ports/atmel-samd/boards/meowmeow/mpconfigboard.mk +++ b/ports/atmel-samd/boards/meowmeow/mpconfigboard.mk @@ -6,6 +6,8 @@ USB_MANUFACTURER = "Electronic Cats" CHIP_VARIANT = SAMD21G18A CHIP_FAMILY = samd21 +CIRCUITPY_PWMIO = 0 + INTERNAL_FLASH_FILESYSTEM = 1 LONGINT_IMPL = NONE CIRCUITPY_FULL_BUILD = 0 diff --git a/ports/atmel-samd/boards/pewpew_m4/mpconfigboard.mk b/ports/atmel-samd/boards/pewpew_m4/mpconfigboard.mk index d500b81112..7990f91ccc 100644 --- a/ports/atmel-samd/boards/pewpew_m4/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pewpew_m4/mpconfigboard.mk @@ -30,14 +30,17 @@ CIRCUIPTY_USB_CDC = 0 CIRCUITPY_USB_HID = 0 CIRCUITPY_USB_MIDI = 0 CIRCUITPY_VECTORIO = 0 +CIRCUITPY_GAMEPAD = 0 +CIRCUITPY_PWMIO = 0 CIRCUITPY_ANALOGIO = 1 CIRCUITPY_AUDIOMIXER = 1 CIRCUITPY_AUDIOIO = 1 +CIRCUITPY_SYNTHIO = 1 CIRCUITPY_DISPLAYIO = 1 -CIRCUITPY_GAMEPAD = 1 CIRCUITPY_MATH = 1 CIRCUITPY_STAGE = 1 +CIRCUITPY_KEYPAD = 1 FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/pewpew_m4 CIRCUITPY_DISPLAY_FONT = $(TOP)/ports/atmel-samd/boards/ugame10/brutalist-6.bdf diff --git a/ports/atmel-samd/boards/pycubed/mpconfigboard.mk b/ports/atmel-samd/boards/pycubed/mpconfigboard.mk index 1eb3cd5239..bbed46dbed 100644 --- a/ports/atmel-samd/boards/pycubed/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pycubed/mpconfigboard.mk @@ -16,8 +16,8 @@ CIRCUITPY_DRIVE_LABEL = "PYCUBED" CIRCUITPY_AUDIOBUSIO = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_DISPLAYIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_PS2IO = 0 diff --git a/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk b/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk index 0c459c309c..ccf3050d11 100644 --- a/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pycubed_mram/mpconfigboard.mk @@ -17,7 +17,7 @@ CIRCUITPY_AUDIOBUSIO = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_PS2IO = 0 diff --git a/ports/atmel-samd/boards/seeeduino_wio_terminal/pins.c b/ports/atmel-samd/boards/seeeduino_wio_terminal/pins.c index a5a8b088c3..fb043349f3 100644 --- a/ports/atmel-samd/boards/seeeduino_wio_terminal/pins.c +++ b/ports/atmel-samd/boards/seeeduino_wio_terminal/pins.c @@ -89,8 +89,8 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { // I2S { MP_OBJ_NEW_QSTR(MP_QSTR_I2S_LRCLK), MP_ROM_PTR(&pin_PA20) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_I2S_SDIN), MP_ROM_PTR(&pin_PA21) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_I2S_SDOUT), MP_ROM_PTR(&pin_PA22) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_I2S_SDOUT), MP_ROM_PTR(&pin_PA21) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_I2S_SDIN), MP_ROM_PTR(&pin_PA22) }, { MP_OBJ_NEW_QSTR(MP_QSTR_I2S_BCLK), MP_ROM_PTR(&pin_PB16) }, // RTL8720D diff --git a/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk index 53aeda3c50..a863248ca7 100644 --- a/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/stringcar_m0_express/mpconfigboard.mk @@ -11,4 +11,6 @@ SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = AT25SF161 LONGINT_IMPL = MPZ +CIRCUITPY_KEYPAD = 0 + CIRCUITPY_BITBANG_APA102 = 1 diff --git a/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk b/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk index 1c96e1d235..7364de0cfe 100644 --- a/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk +++ b/ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk @@ -22,7 +22,6 @@ CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_AUDIOBUSIO = 0 CIRCUITPY_BLEIO = 0 CIRCUITPY_DISPLAYIO = 0 -CIRCUITPY_GAMEPAD = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_TOUCHIO = 0 CIRCUITPY_RGBMATRIX = 0 diff --git a/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk b/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk index 81956a0b80..bc4fff5018 100644 --- a/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk +++ b/ports/atmel-samd/boards/winterbloom_sol/mpconfigboard.mk @@ -21,7 +21,6 @@ CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_TOUCHIO = 0 CIRCUITPY_RGBMATRIX = 0 diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index d0ef189f41..cb8fae6f07 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -50,6 +50,7 @@ CIRCUITPY_FRAMEBUFFERIO ?= 0 CIRCUITPY_FREQUENCYIO ?= 0 CIRCUITPY_I2CPERIPHERAL ?= 0 CIRCUITPY_JSON ?= 0 +CIRCUITPY_KEYPAD ?= 0 CIRCUITPY_MSGPACK ?= 0 CIRCUITPY_RE ?= 0 CIRCUITPY_SDCARDIO ?= 0 diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index 8c827d923e..90fa1ece0c 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -38,8 +38,10 @@ PORT ?= /dev/tty.SLAB_USBtoUART BUILD ?= build-$(BOARD) include ../../py/mkenv.mk + # Board-specific include boards/$(BOARD)/mpconfigboard.mk + # Port-specific include mpconfigport.mk @@ -64,51 +66,52 @@ CROSS_COMPILE = xtensa-esp32s2-elf- ####################################### INC += -I. -INC += -I../.. -INC += -I$(BUILD) -INC += -I$(BUILD)/genhdr INC += -I./boards INC += -I./boards/$(BOARD) INC += -I./peripherals +INC += -I../.. INC += -I../../lib/mp-readline INC += -I../../lib/tinyusb/src INC += -I../../supervisor/shared/usb +INC += -I$(BUILD) +INC += -I$(BUILD)/genhdr +INC += -I$(BUILD)/esp-idf/config INC += -isystem esp-idf +INC += -isystem esp-idf/components/app_update/include +INC += -isystem esp-idf/components/bootloader_support/include +INC += -isystem esp-idf/components/driver/esp32s2/include INC += -isystem esp-idf/components/driver/include -INC += -isystem esp-idf/components/freertos/include/freertos -INC += -isystem esp-idf/components/freertos/xtensa/include INC += -isystem esp-idf/components/esp32s2/include -INC += -isystem esp-idf/components/xtensa/esp32s2/include INC += -isystem esp-idf/components/esp_common/include INC += -isystem esp-idf/components/esp_event/include +INC += -isystem esp-idf/components/esp_hw_support/include INC += -isystem esp-idf/components/esp_netif/include +INC += -isystem esp-idf/components/esp_pm/include INC += -isystem esp-idf/components/esp_ringbuf/include INC += -isystem esp-idf/components/esp_rom/include -INC += -isystem esp-idf/components/esp_wifi/include -INC += -isystem esp-idf/components/xtensa/include +INC += -isystem esp-idf/components/esp_system/include INC += -isystem esp-idf/components/esp_timer/include -INC += -isystem esp-idf/components/mbedtls/mbedtls/include -INC += -isystem esp-idf/components/mbedtls/port/include/ -INC += -isystem esp-idf/components/newlib/platform_include +INC += -isystem esp-idf/components/esp_wifi/include +INC += -isystem esp-idf/components/freertos/include +INC += -isystem esp-idf/components/freertos/include/freertos +INC += -isystem esp-idf/components/freertos/port/xtensa/include +INC += -isystem esp-idf/components/hal/include +INC += -isystem esp-idf/components/hal/esp32s2/include +INC += -isystem esp-idf/components/heap/include +INC += -isystem esp-idf/components/log/include/ INC += -isystem esp-idf/components/lwip/lwip/src/include INC += -isystem esp-idf/components/lwip/port/esp32/include INC += -isystem esp-idf/components/lwip/include/apps/sntp -INC += -isystem esp-idf/components/hal/include -INC += -isystem esp-idf/components/hal/esp32s2/include -INC += -isystem esp-idf/components/log/include/ -INC += -isystem esp-idf/components/driver/esp32s2/include -INC += -isystem esp-idf/components/soc/include -INC += -isystem esp-idf/components/soc/src/esp32s2/include -INC += -isystem esp-idf/components/soc/soc/include -INC += -isystem esp-idf/components/soc/soc/esp32s2/include -INC += -isystem esp-idf/components/heap/include -INC += -isystem esp-idf/components/esp_system/include -INC += -isystem esp-idf/components/spi_flash/include +INC += -isystem esp-idf/components/mbedtls/mbedtls/include +INC += -isystem esp-idf/components/mbedtls/port/include/ +INC += -isystem esp-idf/components/newlib/platform_include INC += -isystem esp-idf/components/nvs_flash/include -INC += -isystem esp-idf/components/app_update/include -INC += -isystem esp-idf/components/bootloader_support/include -INC += -I$(BUILD)/esp-idf/config +INC += -isystem esp-idf/components/soc/include +INC += -isystem esp-idf/components/soc/esp32s2/include +INC += -isystem esp-idf/components/spi_flash/include +INC += -isystem esp-idf/components/xtensa/esp32s2/include +INC += -isystem esp-idf/components/xtensa/include CFLAGS += -DHAVE_CONFIG_H \ -DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\" \ @@ -136,28 +139,29 @@ CFLAGS += $(OPTIMIZATION_FLAGS) CFLAGS += $(INC) -Werror -Wall -mlongcalls -std=gnu11 -Wl,--gc-sections $(BASE_CFLAGS) $(C_DEFS) $(CFLAGS_MOD) $(COPT) -LDFLAGS = $(CFLAGS) -Wl,-nostdlib -Wl,-Map=$@.map -Wl,-cref -Wl,--undefined=uxTopUsedPriority +LDFLAGS = $(CFLAGS) -Wl,-nostdlib -Wl,-Map=$@.map -Wl,-cref -Wl,--undefined=uxTopUsedPriority + LDFLAGS += -L$(BUILD)/esp-idf/esp-idf/esp32s2 \ - -Tesp32s2_out.ld \ - -L$(BUILD)/esp-idf/esp-idf/esp32s2/ld \ - -Tesp32s2.project.ld \ - -Lesp-idf/components/esp32s2/ld \ - -Tesp32s2.peripherals.ld \ - -Lesp-idf/components/esp_rom/esp32s2/ld \ - -Tesp32s2.rom.ld \ - -Tesp32s2.rom.libgcc.ld \ - -Tesp32s2.rom.newlib-data.ld \ - -Tesp32s2.rom.newlib-funcs.ld \ - -Tesp32s2.rom.spiflash.ld + -L$(BUILD)/esp-idf/esp-idf/esp32s2/ld \ + -Lesp-idf/components/esp32s2/ld \ + -Lesp-idf/components/esp_rom/esp32s2/ld \ + -Tesp32s2_out.ld \ + -Tesp32s2.project.ld \ + -Tesp32s2.peripherals.ld \ + -Tesp32s2.rom.ld \ + -Tesp32s2.rom.api.ld \ + -Tesp32s2.rom.libgcc.ld \ + -Tesp32s2.rom.newlib-data.ld \ + -Tesp32s2.rom.newlib-funcs.ld \ + -Tesp32s2.rom.spiflash.ld + +LDFLAGS += -Wl,-Bstatic \ + -Wl,--no-warn-mismatch \ + -Wl,--build-id=none \ + -fno-rtti + LIBS := -lgcc -lc -lstdc++ -# - -LDFLAGS += -Wl,-Bstatic \ - -Wl,--no-warn-mismatch \ - -Wl,--build-id=none \ - -fno-rtti - # Use toolchain libm if we're not using our own. ifndef INTERNAL_LIBM LIBS += -lm @@ -272,11 +276,7 @@ menuconfig: $(BUILD)/esp-idf/config # qstr builds include headers so we need to make sure they are up to date $(HEADER_BUILD)/qstr.split: | $(BUILD)/esp-idf/config/sdkconfig.h -ESP_IDF_COMPONENTS_LINK = freertos log esp_system esp_adc_cal esp32s2 bootloader_support pthread esp_timer vfs spi_flash app_update esp_common esp32s2 heap newlib driver xtensa soc esp_ringbuf esp_wifi esp_event wpa_supplicant mbedtls efuse nvs_flash esp_netif lwip esp-tls - -ESP_IDF_COMPONENTS_INCLUDE = driver freertos log soc - -INC += $(foreach component, $(ESP_IDF_COMPONENTS_INCLUDE), -isystem esp-idf/components/$(component)/include) +ESP_IDF_COMPONENTS_LINK = app_update bootloader_support driver efuse esp32s2 esp_adc_cal esp_common esp_event esp_hw_support esp_netif esp_pm esp_ringbuf esp_rom esp_system esp_timer esp-tls esp_wifi freertos hal heap log lwip mbedtls newlib nvs_flash pthread soc spi_flash vfs wpa_supplicant xtensa ESP_IDF_COMPONENTS_EXPANDED = $(foreach component, $(ESP_IDF_COMPONENTS_LINK), $(BUILD)/esp-idf/esp-idf/$(component)/lib$(component).a) ESP_IDF_WIFI_COMPONENTS_EXPANDED = $(foreach component, $(ESP_IDF_WIFI_COMPONENTS_LINK), $(BUILD)/esp-idf/esp-idf/$(component)/lib$(component).a) @@ -284,11 +284,11 @@ ESP_IDF_WIFI_COMPONENTS_EXPANDED = $(foreach component, $(ESP_IDF_WIFI_COMPONENT MBEDTLS_COMPONENTS_LINK = crypto tls x509 MBEDTLS_COMPONENTS_LINK_EXPANDED = $(foreach component, $(MBEDTLS_COMPONENTS_LINK), $(BUILD)/esp-idf/esp-idf/mbedtls/mbedtls/library/libmbed$(component).a) -BINARY_BLOBS = esp-idf/components/xtensa/esp32s2/libhal.a +BINARY_BLOBS = esp-idf/components/xtensa/esp32s2/libxt_hal.a BINARY_WIFI_BLOBS = libcoexist.a libcore.a libespnow.a libmesh.a libnet80211.a libpp.a librtc.a libsmartconfig.a libphy.a BINARY_BLOBS += $(addprefix esp-idf/components/esp_wifi/lib/esp32s2/, $(BINARY_WIFI_BLOBS)) -ESP_IDF_COMPONENTS_EXPANDED += $(BUILD)/esp-idf/esp-idf/soc/soc/esp32s2/libsoc_esp32s2.a +ESP_IDF_COMPONENTS_EXPANDED += esp-idf/components/xtensa/esp32s2/libxt_hal.a ESP_AUTOGEN_LD = $(BUILD)/esp-idf/esp-idf/esp32s2/esp32s2_out.ld $(BUILD)/esp-idf/esp-idf/esp32s2/ld/esp32s2.project.ld FLASH_FLAGS = --flash_mode $(CIRCUITPY_ESP_FLASH_MODE) --flash_freq $(CIRCUITPY_ESP_FLASH_FREQ) --flash_size $(CIRCUITPY_ESP_FLASH_SIZE) @@ -301,24 +301,42 @@ all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 esp-idf-stamp: $(BUILD)/esp-idf/config/sdkconfig.h $(Q)ninja -C $(BUILD)/esp-idf \ bootloader/bootloader.bin \ + esp-idf/app_update/libapp_update.a \ esp-idf/bootloader_support/libbootloader_support.a \ - esp-idf/esp-tls/libesp-tls.a \ + esp-idf/driver/libdriver.a \ + esp-idf/efuse/libefuse.a \ + esp-idf/esp32s2/libesp32s2.a \ esp-idf/esp32s2/ld/esp32s2.project.ld \ + esp-idf/esp_adc_cal/libesp_adc_cal.a \ + esp-idf/esp_common/libesp_common.a \ esp-idf/esp_event/libesp_event.a \ + esp-idf/esp_hw_support/libesp_hw_support.a \ esp-idf/esp_netif/libesp_netif.a \ + esp-idf/esp_pm/libesp_pm.a \ + esp-idf/esp_ringbuf/libesp_ringbuf.a \ + esp-idf/esp_rom/libesp_rom.a \ esp-idf/esp_system/libesp_system.a \ + esp-idf/esp_timer/libesp_timer.a \ + esp-idf/esp-tls/libesp-tls.a \ esp-idf/esp_wifi/libesp_wifi.a \ - esp-idf/lwip/liblwip.a \ - esp-idf/nvs_flash/libnvs_flash.a \ - esp-idf/wpa_supplicant/libwpa_supplicant.a \ - esp-idf/mbedtls/libmbedtls.a \ esp-idf/freertos/libfreertos.a \ + esp-idf/hal/libhal.a \ + esp-idf/heap/libheap.a \ esp-idf/log/liblog.a \ + esp-idf/lwip/liblwip.a \ + esp-idf/mbedtls/libmbedtls.a \ + esp-idf/newlib/libnewlib.a \ + esp-idf/nvs_flash/libnvs_flash.a \ + esp-idf/pthread/libpthread.a \ + esp-idf/soc/libsoc.a \ + esp-idf/spi_flash/libspi_flash.a \ + esp-idf/vfs/libvfs.a \ + esp-idf/wpa_supplicant/libwpa_supplicant.a \ esp-idf/xtensa/libxtensa.a $(BUILD)/firmware.elf: $(OBJ) | esp-idf-stamp $(STEPECHO) "LINK $@" - $(Q)$(CC) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(ESP_IDF_COMPONENTS_EXPANDED) $(BINARY_BLOBS) $(MBEDTLS_COMPONENTS_LINK_EXPANDED) build-$(BOARD)/esp-idf/esp-idf/newlib/libnewlib.a -Wl,--end-group -u newlib_include_pthread_impl -Wl,--start-group $(LIBS) -Wl,--end-group build-$(BOARD)/esp-idf/esp-idf/pthread/libpthread.a -u __cxx_fatal_exception + $(Q)$(CC) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(ESP_IDF_COMPONENTS_EXPANDED) $(BINARY_BLOBS) $(MBEDTLS_COMPONENTS_LINK_EXPANDED) $(BUILD)/esp-idf/esp-idf/newlib/libnewlib.a -Wl,--end-group -u newlib_include_pthread_impl -Wl,--start-group $(LIBS) -Wl,--end-group $(BUILD)/esp-idf/esp-idf/pthread/libpthread.a -u __cxx_fatal_exception $(BUILD)/circuitpython-firmware.bin: $(BUILD)/firmware.elf | tools/build_memory_info.py $(STEPECHO) "Create $@" diff --git a/ports/esp32s2/boards/espressif_kaluga_1.3/sdkconfig b/ports/esp32s2/boards/espressif_kaluga_1.3/sdkconfig index 26c6388fe5..9d8bbde967 100644 --- a/ports/esp32s2/boards/espressif_kaluga_1.3/sdkconfig +++ b/ports/esp32s2/boards/espressif_kaluga_1.3/sdkconfig @@ -1,12 +1,3 @@ -# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE is not set -# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set -# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set -# TWAI configuration -# -# CONFIG_TWAI_ISR_IN_IRAM is not set -# end of TWAI configuration - -# CONFIG_ESP32S2_SPIRAM_SUPPORT=y # @@ -27,7 +18,6 @@ CONFIG_DEFAULT_PSRAM_CS_IO=26 # CONFIG_SPIRAM_FETCH_INSTRUCTIONS is not set # CONFIG_SPIRAM_RODATA is not set -# CONFIG_SPIRAM_USE_AHB_DBUS3 is not set # CONFIG_SPIRAM_SPEED_80M is not set CONFIG_SPIRAM_SPEED_40M=y # CONFIG_SPIRAM_SPEED_26M is not set @@ -41,16 +31,3 @@ CONFIG_SPIRAM_USE_MEMMAP=y CONFIG_SPIRAM_MEMTEST=y # CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set # end of SPI RAM config - -# CONFIG_ESP_CONSOLE_UART_NONE is not set -CONFIG_ESP_CONSOLE_UART_TX_GPIO=43 -CONFIG_ESP_CONSOLE_UART_RX_GPIO=44 -CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y -CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y -CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y -# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set -CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y -# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set -# CONFIG_CONSOLE_UART_NONE is not set -CONFIG_CONSOLE_UART_TX_GPIO=43 -CONFIG_CONSOLE_UART_RX_GPIO=44 diff --git a/ports/esp32s2/common-hal/alarm/__init__.c b/ports/esp32s2/common-hal/alarm/__init__.c index f1d4bb36f5..7a977b093c 100644 --- a/ports/esp32s2/common-hal/alarm/__init__.c +++ b/ports/esp32s2/common-hal/alarm/__init__.c @@ -44,7 +44,7 @@ #include "esp_sleep.h" -#include "components/soc/soc/esp32s2/include/soc/rtc_cntl_reg.h" +#include "components/soc/esp32s2/include/soc/rtc_cntl_reg.h" #include "components/driver/include/driver/uart.h" // Singleton instance of SleepMemory. diff --git a/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c b/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c index 948accf1fc..6c10b06cec 100644 --- a/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c +++ b/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c @@ -27,16 +27,16 @@ #include "py/runtime.h" -#include "shared-bindings/alarm/pin/PinAlarm.h" -#include "shared-bindings/microcontroller/__init__.h" -#include "shared-bindings/microcontroller/Pin.h" #include "supervisor/esp_port.h" +#include "shared-bindings/alarm/pin/PinAlarm.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/__init__.h" #include "components/driver/include/driver/rtc_io.h" -#include "components/esp32s2/include/esp_sleep.h" -#include "components/freertos/include/freertos/FreeRTOS.h" -#include "components/soc/src/esp32s2/include/hal/gpio_ll.h" +#include "components/esp_system/include/esp_sleep.h" +#include "components/hal/esp32s2/include/hal/gpio_ll.h" #include "components/xtensa/include/esp_debug_helpers.h" +#include "components/freertos/include/freertos/FreeRTOS.h" 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) { if (edge) { diff --git a/ports/esp32s2/common-hal/analogio/AnalogIn.h b/ports/esp32s2/common-hal/analogio/AnalogIn.h index 8c1f8a2ffb..470570ceb3 100644 --- a/ports/esp32s2/common-hal/analogio/AnalogIn.h +++ b/ports/esp32s2/common-hal/analogio/AnalogIn.h @@ -29,7 +29,7 @@ #include "common-hal/microcontroller/Pin.h" -#include "components/soc/include/hal/adc_types.h" +#include "components/hal/include/hal/adc_types.h" #include "FreeRTOS.h" #include "freertos/semphr.h" #include "py/obj.h" diff --git a/ports/esp32s2/common-hal/busio/I2C.c b/ports/esp32s2/common-hal/busio/I2C.c index d97ccd9f15..8052715ae9 100644 --- a/ports/esp32s2/common-hal/busio/I2C.c +++ b/ports/esp32s2/common-hal/busio/I2C.c @@ -88,8 +88,8 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, } #endif - - if (xSemaphoreCreateMutexStatic(&self->semaphore) != &self->semaphore) { + self->xSemaphore = xSemaphoreCreateMutexStatic(&self->xSemaphoreBuffer); + if (self->xSemaphore == NULL) { mp_raise_RuntimeError(translate("Unable to create lock")); } self->sda_pin = sda; @@ -168,7 +168,7 @@ bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { if (self->has_lock) { return false; } - self->has_lock = xSemaphoreTake(&self->semaphore, 0) == pdTRUE; + self->has_lock = xSemaphoreTake(self->xSemaphore, 0) == pdTRUE; return self->has_lock; } @@ -177,7 +177,7 @@ bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { } void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { - xSemaphoreGive(&self->semaphore); + xSemaphoreGive(self->xSemaphore); self->has_lock = false; } diff --git a/ports/esp32s2/common-hal/busio/I2C.h b/ports/esp32s2/common-hal/busio/I2C.h index 9629e7b18e..d699b84ef1 100644 --- a/ports/esp32s2/common-hal/busio/I2C.h +++ b/ports/esp32s2/common-hal/busio/I2C.h @@ -29,7 +29,7 @@ #include "common-hal/microcontroller/Pin.h" -#include "components/soc/include/hal/i2c_types.h" +#include "components/hal/include/hal/i2c_types.h" #include "FreeRTOS.h" #include "freertos/semphr.h" #include "py/obj.h" @@ -39,7 +39,8 @@ typedef struct { const mcu_pin_obj_t *scl_pin; const mcu_pin_obj_t *sda_pin; i2c_port_t i2c_num; - StaticSemaphore_t semaphore; + SemaphoreHandle_t xSemaphore; + StaticSemaphore_t xSemaphoreBuffer; bool has_lock; } busio_i2c_obj_t; diff --git a/ports/esp32s2/common-hal/busio/SPI.c b/ports/esp32s2/common-hal/busio/SPI.c index 0e69d9f520..fee533e755 100644 --- a/ports/esp32s2/common-hal/busio/SPI.c +++ b/ports/esp32s2/common-hal/busio/SPI.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2016 Scott Shawcroft + * 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 @@ -24,282 +24,147 @@ * THE SOFTWARE. */ -#include "shared-bindings/busio/SPI.h" -#include "py/mperrno.h" -#include "py/runtime.h" +#include -#include "shared-bindings/microcontroller/Pin.h" +#include "py/runtime.h" +#include "shared-bindings/busio/SPI.h" + +#include "driver/spi_common_internal.h" + +#define SPI_MAX_DMA_BITS (SPI_MAX_DMA_LEN * 8) static bool spi_never_reset[SOC_SPI_PERIPH_NUM]; +static spi_device_handle_t spi_handle[SOC_SPI_PERIPH_NUM]; -// Store one lock handle per device so that we can free it. -static spi_bus_lock_dev_handle_t lock_dev_handle[SOC_SPI_PERIPH_NUM]; -static intr_handle_t intr_handle[SOC_SPI_PERIPH_NUM]; +static bool spi_bus_is_free(spi_host_device_t host_id) { + return spi_bus_get_attr(host_id) == NULL; +} void spi_reset(void) { for (spi_host_device_t host_id = SPI2_HOST; host_id < SOC_SPI_PERIPH_NUM; host_id++) { if (spi_never_reset[host_id]) { continue; } - bool in_use = false; - if (lock_dev_handle[host_id] != NULL) { - spi_bus_lock_unregister_dev(lock_dev_handle[host_id]); - lock_dev_handle[host_id] = NULL; - in_use = true; - } - if (intr_handle[host_id] != NULL) { - esp_intr_free(intr_handle[host_id]); - intr_handle[host_id] = NULL; - in_use = true; - } - if (in_use) { + if (!spi_bus_is_free(host_id)) { + spi_bus_remove_device(spi_handle[host_id]); spi_bus_free(host_id); } } } -// This is copied in from the ESP-IDF because it is static. -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -static bool bus_uses_iomux_pins(spi_host_device_t host, const spi_bus_config_t *bus_config) { - if (bus_config->sclk_io_num >= 0 && - bus_config->sclk_io_num != spi_periph_signal[host].spiclk_iomux_pin) { - return false; +static void set_spi_config(busio_spi_obj_t *self, + uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { + const spi_device_interface_config_t device_config = { + .clock_speed_hz = baudrate, + .mode = phase | (polarity << 1), + .spics_io_num = -1, // No CS pin + .queue_size = 1, + .pre_cb = NULL + }; + esp_err_t result = spi_bus_add_device(self->host_id, &device_config, &spi_handle[self->host_id]); + if (result != ESP_OK) { + mp_raise_RuntimeError(translate("SPI configuration failed")); } - if (bus_config->quadwp_io_num >= 0 && - bus_config->quadwp_io_num != spi_periph_signal[host].spiwp_iomux_pin) { - return false; - } - if (bus_config->quadhd_io_num >= 0 && - bus_config->quadhd_io_num != spi_periph_signal[host].spihd_iomux_pin) { - return false; - } - if (bus_config->mosi_io_num >= 0 && - bus_config->mosi_io_num != spi_periph_signal[host].spid_iomux_pin) { - return false; - } - if (bus_config->miso_io_num >= 0 && - bus_config->miso_io_num != spi_periph_signal[host].spiq_iomux_pin) { - return false; - } - - return true; -} - -// End copied code. - -static bool spi_bus_is_free(spi_host_device_t host_id) { - return spi_bus_get_attr(host_id) == NULL; -} - -static void spi_interrupt_handler(void *arg) { - // busio_spi_obj_t *self = arg; -} - -// The interrupt may get invoked by the bus lock. -static void spi_bus_intr_enable(void *self) { - esp_intr_enable(((busio_spi_obj_t *)self)->interrupt); -} - -// The interrupt is always disabled by the ISR itself, not exposed -static void spi_bus_intr_disable(void *self) { - esp_intr_disable(((busio_spi_obj_t *)self)->interrupt); + self->baudrate = baudrate; + self->polarity = polarity; + self->phase = phase; + self->bits = bits; } 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) { - spi_bus_config_t bus_config; - bus_config.mosi_io_num = mosi != NULL ? mosi->number : -1; - bus_config.miso_io_num = miso != NULL ? miso->number : -1; - bus_config.sclk_io_num = clock != NULL ? clock->number : -1; - bus_config.quadwp_io_num = -1; - bus_config.quadhd_io_num = -1; - bus_config.max_transfer_sz = 0; // Uses the default - bus_config.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK | - (mosi != NULL ? SPICOMMON_BUSFLAG_MOSI : 0) | - (miso != NULL ? SPICOMMON_BUSFLAG_MISO : 0); - bus_config.intr_flags = 0; + const spi_bus_config_t bus_config = { + .mosi_io_num = mosi != NULL ? mosi->number : -1, + .miso_io_num = miso != NULL ? miso->number : -1, + .sclk_io_num = clock != NULL ? clock->number : -1, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; - // RAM and Flash is often on SPI1 and is unsupported by the IDF so use it as - // a flag value. - spi_host_device_t host_id = SPI1_HOST; - self->connected_through_gpio = true; - // Try and save SPI2 for pins that are on the IOMUX - if (bus_uses_iomux_pins(SPI2_HOST, &bus_config) && spi_bus_is_free(SPI2_HOST)) { - host_id = SPI2_HOST; - self->connected_through_gpio = false; - } else if (spi_bus_is_free(SPI3_HOST)) { - host_id = SPI3_HOST; - } else if (spi_bus_is_free(SPI2_HOST)) { - host_id = SPI2_HOST; + for (spi_host_device_t host_id = SPI2_HOST; host_id < SOC_SPI_PERIPH_NUM; host_id++) { + if (spi_bus_is_free(host_id)) { + self->host_id = host_id; + } } - if (host_id == SPI1_HOST) { + + if (self->host_id == 0) { mp_raise_ValueError(translate("All SPI peripherals are in use")); } - esp_err_t result = spi_bus_initialize(host_id, &bus_config, host_id /* dma channel */); + esp_err_t result = spi_bus_initialize(self->host_id, &bus_config, self->host_id /* dma channel */); if (result == ESP_ERR_NO_MEM) { mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed")); } else if (result == ESP_ERR_INVALID_ARG) { mp_raise_ValueError(translate("Invalid pins")); } - // After this point, we need to deinit to free IDF memory so fill out self's pins. - self->clock_pin = clock; - self->MOSI_pin = mosi; - self->MISO_pin = miso; - self->host_id = host_id; + set_spi_config(self, 250000, 0, 0, 8); - spi_bus_lock_dev_config_t config = { .flags = 0 }; - // The returned lock is stored in the bus lock but must be freed separately with - // spi_bus_lock_unregister_dev. - result = spi_bus_lock_register_dev(spi_bus_get_attr(host_id)->lock, - &config, - &self->lock); - if (result == ESP_ERR_NO_MEM) { - common_hal_busio_spi_deinit(self); - mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed")); + self->MOSI = mosi; + self->MISO = miso; + self->clock = clock; + + if (mosi != NULL) { + claim_pin(mosi); } - lock_dev_handle[host_id] = self->lock; - - result = esp_intr_alloc(spicommon_irqsource_for_host(host_id), - bus_config.intr_flags | ESP_INTR_FLAG_INTRDISABLED, - spi_interrupt_handler, self, &self->interrupt); - if (result == ESP_ERR_NO_MEM) { - common_hal_busio_spi_deinit(self); - mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed")); + if (miso != NULL) { + claim_pin(miso); } - intr_handle[host_id] = self->interrupt; - spi_bus_lock_set_bg_control(spi_bus_get_attr(host_id)->lock, spi_bus_intr_enable, spi_bus_intr_disable, self); - - spi_hal_context_t *hal = &self->hal_context; - - // spi_hal_init clears the given hal context so set everything after. - spi_hal_init(hal, host_id); - - // We don't use native CS. - // hal->cs_setup = 0; - // hal->cs_hold = 0; - // hal->cs_pin_id = 0; - - hal->sio = 0; - hal->half_duplex = 0; - // hal->tx_lsbfirst = 0; - // hal->rx_lsbfirst = 0; - hal->no_compensate = 1; - // Ignore CS bits - - // We don't use cmd, addr or dummy bits. - // hal->cmd = 0; - // hal->cmd_bits = 0; - // hal->addr_bits = 0; - // hal->dummy_bits = 0; - // hal->addr = 0; - - claim_pin(self->clock_pin); - if (self->MOSI_pin != NULL) { - claim_pin(self->MOSI_pin); - } - if (self->MISO_pin != NULL) { - claim_pin(self->MISO_pin); - } - - hal->io_mode = SPI_LL_IO_MODE_NORMAL; - - common_hal_busio_spi_configure(self, 250000, 0, 0, 8); + claim_pin(clock); } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { spi_never_reset[self->host_id] = true; - - common_hal_never_reset_pin(self->clock_pin); - if (self->MOSI_pin != NULL) { - common_hal_never_reset_pin(self->MOSI_pin); + common_hal_never_reset_pin(self->clock); + if (self->MOSI != NULL) { + common_hal_never_reset_pin(self->MOSI); } - if (self->MISO_pin != NULL) { - common_hal_never_reset_pin(self->MISO_pin); + if (self->MISO != NULL) { + common_hal_never_reset_pin(self->MISO); } } bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { - return self->clock_pin == NULL; + return self->clock == NULL; } void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } + spi_never_reset[self->host_id] = false; - if (self->lock != NULL) { - spi_bus_lock_unregister_dev(self->lock); - lock_dev_handle[self->host_id] = NULL; - } - if (self->interrupt != NULL) { - esp_intr_free(self->interrupt); - intr_handle[self->host_id] = NULL; - } + spi_bus_remove_device(spi_handle[self->host_id]); spi_bus_free(self->host_id); - common_hal_reset_pin(self->clock_pin); - common_hal_reset_pin(self->MOSI_pin); - common_hal_reset_pin(self->MISO_pin); - self->clock_pin = NULL; - self->MISO_pin = NULL; - self->MOSI_pin = NULL; + common_hal_reset_pin(self->MOSI); + common_hal_reset_pin(self->MISO); + common_hal_reset_pin(self->clock); + self->clock = NULL; } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { - if (baudrate == self->target_frequency && + if (baudrate == self->baudrate && polarity == self->polarity && phase == self->phase && bits == self->bits) { return true; } - self->hal_context.mode = polarity << 1 | phase; - self->polarity = polarity; - self->phase = phase; - self->bits = bits; - self->target_frequency = baudrate; - self->hal_context.timing_conf = &self->timing_conf; - esp_err_t result = spi_hal_get_clock_conf(&self->hal_context, - self->target_frequency, - 128 /* duty_cycle */, - self->connected_through_gpio, - 0 /* input_delay_ns */, - &self->real_frequency, - &self->timing_conf); - if (result != ESP_OK) { - return false; - } - - spi_hal_setup_device(&self->hal_context); + spi_bus_remove_device(spi_handle[self->host_id]); + set_spi_config(self, baudrate, polarity, phase, bits); return true; } bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { - // If our lock has already been taken then return false because someone else - // may already grabbed it in our call stack. - if (self->has_lock) { - return false; + bool grabbed_lock = false; + if (!self->has_lock) { + grabbed_lock = true; + self->has_lock = true; } - // Wait to grab the lock from another task. - esp_err_t result = spi_bus_lock_acquire_start(self->lock, portMAX_DELAY); - self->has_lock = result == ESP_OK; - return self->has_lock; + return grabbed_lock; } bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { @@ -307,13 +172,12 @@ bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { } void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { - spi_bus_lock_acquire_end(self->lock); self->has_lock = false; } bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { - if (self->MOSI_pin == NULL) { + if (self->MOSI == NULL) { mp_raise_ValueError(translate("No MOSI Pin")); } return common_hal_busio_spi_transfer(self, data, NULL, len); @@ -321,11 +185,10 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self, bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { - - if (self->MISO_pin == NULL) { + if (self->MISO == NULL) { mp_raise_ValueError(translate("No MISO Pin")); } - if (self->MOSI_pin == NULL) { + if (self->MOSI == NULL) { return common_hal_busio_spi_transfer(self, NULL, data, len); } else { memset(data, write_value, len); @@ -333,86 +196,73 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, } } -bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) { +bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, + const uint8_t *data_out, uint8_t *data_in, size_t len) { if (len == 0) { return true; } // Other than the read special case, stop transfers that don't have a pin/array match - if (!self->MOSI_pin && (data_out != data_in)) { + if (!self->MOSI && (data_out != data_in)) { mp_raise_ValueError(translate("No MOSI Pin")); } - if (!self->MISO_pin && data_in) { + if (!self->MISO && data_in) { mp_raise_ValueError(translate("No MISO Pin")); } - spi_hal_context_t *hal = &self->hal_context; - hal->send_buffer = NULL; - hal->rcv_buffer = NULL; - // Reset timing_conf in case we've moved since the last time we used it. - hal->timing_conf = &self->timing_conf; - lldesc_t tx_dma __attribute__((aligned(16))); - lldesc_t rx_dma __attribute__((aligned(16))); - hal->dmadesc_tx = &tx_dma; - hal->dmadesc_rx = &rx_dma; - hal->dmadesc_n = 1; + spi_transaction_t transaction = { 0 }; - size_t burst_length; - // If both of the incoming pointers are DMA capable then use DMA. Otherwise, do - // bursts the size of the SPI data buffer without DMA. - if ((data_out == NULL || esp_ptr_dma_capable(data_out)) && - (data_in == NULL || esp_ptr_dma_capable(data_out))) { - hal->dma_enabled = 1; - burst_length = LLDESC_MAX_NUM_PER_DESC; - } else { - hal->dma_enabled = 0; - burst_length = sizeof(hal->hw->data_buf); - // When switching to non-DMA, we need to make sure DMA is off. Otherwise, - // the S2 will transmit zeroes instead of our data. - hal->hw->dma_out_link.dma_tx_ena = 0; - hal->hw->dma_out_link.stop = 1; - } + // Round to nearest whole set of bits + int bits_to_send = len * 8 / self->bits * self->bits; - // This rounds up. - size_t burst_count = (len + burst_length - 1) / burst_length; - for (size_t i = 0; i < burst_count; i++) { - size_t offset = burst_length * i; - size_t this_length = len - offset; - if (this_length > burst_length) { - this_length = burst_length; - } - hal->tx_bitlen = this_length * self->bits; - hal->rx_bitlen = this_length * self->bits; + if (len <= 4) { if (data_out != NULL) { - hal->send_buffer = (uint8_t *)data_out + offset; - } - if (data_in != NULL) { - hal->rcv_buffer = data_in + offset; + memcpy(&transaction.tx_data, data_out, len); } - spi_hal_setup_trans(hal); - spi_hal_prepare_data(hal); - spi_hal_user_start(hal); - // TODO: Switch to waiting on a lock that is given by an interrupt. - while (!spi_hal_usr_is_done(hal)) { + transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA; + transaction.length = bits_to_send; + spi_device_transmit(spi_handle[self->host_id], &transaction); + + if (data_in != NULL) { + memcpy(data_in, &transaction.rx_data, len); + } + } else { + int offset = 0; + int bits_remaining = bits_to_send; + + while (bits_remaining && !mp_hal_is_interrupted()) { + memset(&transaction, 0, sizeof(transaction)); + + transaction.length = + bits_remaining > SPI_MAX_DMA_BITS ? SPI_MAX_DMA_BITS : bits_remaining; + + if (data_out != NULL) { + transaction.tx_buffer = data_out + offset; + } + if (data_in != NULL) { + transaction.rx_buffer = data_in + offset; + } + + spi_device_transmit(spi_handle[self->host_id], &transaction); + bits_remaining -= transaction.length; + + // doesn't need ceil(); loop ends when bits_remaining is 0 + offset += transaction.length / 8; + RUN_BACKGROUND_TASKS; } - spi_hal_fetch_result(hal); } - hal->dmadesc_tx = NULL; - hal->dmadesc_rx = NULL; - hal->dmadesc_n = 0; - return true; } uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { - return self->real_frequency; -} - -uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { - return self->phase; + return self->baudrate; } uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { return self->polarity; } + +uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { + return self->phase; +} diff --git a/ports/esp32s2/common-hal/busio/SPI.h b/ports/esp32s2/common-hal/busio/SPI.h index 488c0954ed..34ec8665c6 100644 --- a/ports/esp32s2/common-hal/busio/SPI.h +++ b/ports/esp32s2/common-hal/busio/SPI.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2016 Scott Shawcroft + * 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 @@ -27,30 +27,24 @@ #ifndef MICROPY_INCLUDED_ESP32S2_COMMON_HAL_BUSIO_SPI_H #define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_BUSIO_SPI_H -#include "common-hal/microcontroller/Pin.h" - -#include "components/driver/include/driver/spi_common_internal.h" -#include "components/soc/include/hal/spi_hal.h" -#include "components/soc/include/hal/spi_types.h" -#include "py/obj.h" +#include "driver/spi_master.h" +#include "shared-bindings/microcontroller/Pin.h" typedef struct { mp_obj_base_t base; - const mcu_pin_obj_t *clock_pin; - const mcu_pin_obj_t *MOSI_pin; - const mcu_pin_obj_t *MISO_pin; + + const mcu_pin_obj_t* MOSI; + const mcu_pin_obj_t* MISO; + const mcu_pin_obj_t* clock; + spi_host_device_t host_id; - spi_bus_lock_dev_handle_t lock; - spi_hal_context_t hal_context; - spi_hal_timing_conf_t timing_conf; - intr_handle_t interrupt; - uint32_t target_frequency; - int32_t real_frequency; - uint8_t polarity; - uint8_t phase; + uint8_t bits; + uint8_t phase; + uint8_t polarity; + uint32_t baudrate; + bool has_lock; - bool connected_through_gpio; } busio_spi_obj_t; void spi_reset(void); diff --git a/ports/esp32s2/common-hal/busio/UART.h b/ports/esp32s2/common-hal/busio/UART.h index 0d19221c8f..4c80132050 100644 --- a/ports/esp32s2/common-hal/busio/UART.h +++ b/ports/esp32s2/common-hal/busio/UART.h @@ -29,7 +29,7 @@ #include "common-hal/microcontroller/Pin.h" -#include "components/soc/include/hal/uart_types.h" +#include "components/hal/include/hal/uart_types.h" #include "py/obj.h" typedef struct { diff --git a/ports/esp32s2/common-hal/digitalio/DigitalInOut.c b/ports/esp32s2/common-hal/digitalio/DigitalInOut.c index f2c40ce9ba..24c5d5110d 100644 --- a/ports/esp32s2/common-hal/digitalio/DigitalInOut.c +++ b/ports/esp32s2/common-hal/digitalio/DigitalInOut.c @@ -30,7 +30,7 @@ #include "components/driver/include/driver/gpio.h" -#include "components/soc/include/hal/gpio_hal.h" +#include "components/hal/include/hal/gpio_hal.h" void common_hal_digitalio_digitalinout_never_reset( digitalio_digitalinout_obj_t *self) { diff --git a/ports/esp32s2/common-hal/dualbank/__init__.c b/ports/esp32s2/common-hal/dualbank/__init__.c index 04f5d6203d..91c7981dd9 100644 --- a/ports/esp32s2/common-hal/dualbank/__init__.c +++ b/ports/esp32s2/common-hal/dualbank/__init__.c @@ -38,10 +38,7 @@ static esp_ota_handle_t update_handle = 0; static const char *TAG = "dualbank"; void dualbank_reset(void) { - // should use `abort` instead of `end` - // but not in idf v4.2 - // esp_ota_abort(update_handle); - if (esp_ota_end(update_handle) == ESP_OK) { + if (esp_ota_abort(update_handle) == ESP_OK) { update_handle = 0; update_partition = NULL; } diff --git a/ports/esp32s2/common-hal/microcontroller/Pin.c b/ports/esp32s2/common-hal/microcontroller/Pin.c index 7b69c1d6ee..00c94cef6c 100644 --- a/ports/esp32s2/common-hal/microcontroller/Pin.c +++ b/ports/esp32s2/common-hal/microcontroller/Pin.c @@ -31,7 +31,7 @@ #include "py/mphal.h" #include "components/driver/include/driver/gpio.h" -#include "components/soc/include/hal/gpio_hal.h" +#include "components/hal/include/hal/gpio_hal.h" STATIC uint32_t never_reset_pins[2]; STATIC uint32_t in_use[2]; diff --git a/ports/esp32s2/common-hal/rtc/RTC.c b/ports/esp32s2/common-hal/rtc/RTC.c index 4d8713639f..a78c2fb613 100644 --- a/ports/esp32s2/common-hal/rtc/RTC.c +++ b/ports/esp32s2/common-hal/rtc/RTC.c @@ -28,7 +28,7 @@ #include "py/obj.h" #include "py/runtime.h" -#include "components/soc/soc/include/soc/rtc_periph.h" +#include "components/soc/include/soc/rtc_periph.h" #include "shared-bindings/rtc/RTC.h" void common_hal_rtc_get_time(timeutils_struct_time_t *tm) { diff --git a/ports/esp32s2/esp-idf b/ports/esp32s2/esp-idf index f30a865fd1..d97b6863ba 160000 --- a/ports/esp32s2/esp-idf +++ b/ports/esp32s2/esp-idf @@ -1 +1 @@ -Subproject commit f30a865fd1a44d880b909b84112f74741412c2ce +Subproject commit d97b6863badec4643bf8d1d1058a65d723572882 diff --git a/ports/esp32s2/mpconfigport.mk b/ports/esp32s2/mpconfigport.mk index c7f903589d..9f48e62568 100644 --- a/ports/esp32s2/mpconfigport.mk +++ b/ports/esp32s2/mpconfigport.mk @@ -18,6 +18,7 @@ CIRCUITPY_AUDIOBUSIO = 1 CIRCUITPY_AUDIOBUSIO_PDMIN = 0 CIRCUITPY_AUDIOBUSIO_I2SOUT = 1 CIRCUITPY_AUDIOIO = 0 +CIRCUITPY_AUDIOMIXER = 1 CIRCUITPY_CANIO = 1 CIRCUITPY_COUNTIO = 1 CIRCUITPY_DUALBANK = 1 diff --git a/ports/esp32s2/peripherals/pins.h b/ports/esp32s2/peripherals/pins.h index 0ffa482166..5a5867ea0c 100644 --- a/ports/esp32s2/peripherals/pins.h +++ b/ports/esp32s2/peripherals/pins.h @@ -35,9 +35,9 @@ #include "esp32s2_peripherals_config.h" #include "esp-idf/config/sdkconfig.h" -#include "components/soc/include/hal/gpio_types.h" -#include "components/soc/include/hal/adc_types.h" -#include "components/soc/include/hal/touch_sensor_types.h" +#include "components/hal/include/hal/gpio_types.h" +#include "components/hal/include/hal/adc_types.h" +#include "components/hal/include/hal/touch_sensor_types.h" typedef struct { PIN_PREFIX_FIELDS diff --git a/ports/esp32s2/peripherals/touch.c b/ports/esp32s2/peripherals/touch.c index cd895b10be..eeba49c683 100644 --- a/ports/esp32s2/peripherals/touch.c +++ b/ports/esp32s2/peripherals/touch.c @@ -24,8 +24,6 @@ * THE SOFTWARE. */ -#include "components/soc/include/hal/gpio_types.h" -// above include fixes build error in idf@v4.2 #include "peripherals/touch.h" static bool touch_inited = false; diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index 1cbe61f16b..1fca60a1c3 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -59,8 +59,8 @@ #include "components/esp_rom/include/esp32s2/rom/ets_sys.h" #include "components/heap/include/esp_heap_caps.h" #include "components/xtensa/include/esp_debug_helpers.h" -#include "components/soc/soc/esp32s2/include/soc/cache_memory.h" -#include "components/soc/soc/esp32s2/include/soc/rtc_cntl_reg.h" +#include "components/soc/esp32s2/include/soc/cache_memory.h" +#include "components/soc/esp32s2/include/soc/rtc_cntl_reg.h" #if CIRCUITPY_AUDIOBUSIO #include "common-hal/audiobusio/__init__.h" diff --git a/ports/esp32s2/supervisor/usb.c b/ports/esp32s2/supervisor/usb.c index 2289b01cc0..05e0746b00 100644 --- a/ports/esp32s2/supervisor/usb.c +++ b/ports/esp32s2/supervisor/usb.c @@ -31,11 +31,11 @@ #include "lib/utils/interrupt_char.h" #include "lib/mp-readline/readline.h" -#include "components/soc/soc/esp32s2/include/soc/usb_periph.h" -#include "components/driver/include/driver/periph_ctrl.h" #include "components/driver/include/driver/gpio.h" +#include "components/driver/include/driver/periph_ctrl.h" #include "components/esp_rom/include/esp32s2/rom/gpio.h" -#include "components/soc/src/esp32s2/include/hal/gpio_ll.h" +#include "components/hal/esp32s2/include/hal/gpio_ll.h" +#include "components/soc/esp32s2/include/soc/usb_periph.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -120,7 +120,7 @@ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { // Compare mp_interrupt_char with wanted_char and ignore if not matched if (mp_interrupt_char == wanted_char) { tud_cdc_read_flush(); // flush read fifo - mp_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); // CircuitPython's VM is run in a separate FreeRTOS task from TinyUSB. // So, we must notify the other task when a CTRL-C is received. xTaskNotifyGive(circuitpython_task); diff --git a/ports/nrf/background.c b/ports/nrf/background.c index 269bf6737f..f4d6b0f79f 100644 --- a/ports/nrf/background.c +++ b/ports/nrf/background.c @@ -41,11 +41,6 @@ #include "common-hal/audiopwmio/PWMAudioOut.h" #endif -#if CIRCUITPY_BLEIO -#include "supervisor/shared/bluetooth.h" -#include "common-hal/_bleio/bonding.h" -#endif - void port_start_background_task(void) { } void port_finish_background_task(void) { diff --git a/ports/nrf/bluetooth/ble_drv.c b/ports/nrf/bluetooth/ble_drv.c index cb923227b2..9596e45fbb 100644 --- a/ports/nrf/bluetooth/ble_drv.c +++ b/ports/nrf/bluetooth/ble_drv.c @@ -38,9 +38,6 @@ #include "py/misc.h" #include "py/mpstate.h" -#include "supervisor/shared/bluetooth.h" -#include "supervisor/bluetooth.h" - nrf_nvic_state_t nrf_nvic_state = { 0 }; // Flag indicating progress of internal flash operation. @@ -56,6 +53,14 @@ void ble_drv_reset() { } void ble_drv_add_event_handler_entry(ble_drv_evt_handler_entry_t *entry, ble_drv_evt_handler_t func, void *param) { + ble_drv_evt_handler_entry_t *it = MP_STATE_VM(ble_drv_evt_handler_entries); + while (it != NULL) { + // If event handler and its corresponding param are already on the list, don't add again. + if ((it->func == func) && (it->param == param)) { + return; + } + it = it->next; + } entry->next = MP_STATE_VM(ble_drv_evt_handler_entries); entry->param = param; entry->func = func; @@ -85,6 +90,8 @@ void ble_drv_remove_event_handler(ble_drv_evt_handler_t func, void *param) { if ((it->func == func) && (it->param == param)) { // Splice out the matching handler. *prev = it->next; + // Clear next of the removed node so it's clearly not in a list. + it->next = NULL; return; } prev = &(it->next); @@ -138,21 +145,26 @@ void SD_EVT_IRQHandler(void) { ble_evt_t *event = (ble_evt_t *)m_ble_evt_buf; #if CIRCUITPY_VERBOSE_BLE - mp_printf(&mp_plat_print, "BLE event: 0x%04x\n", event->header.evt_id); - #endif - - if (supervisor_bluetooth_hook(event)) { - continue; + size_t eid = event->header.evt_id; + if (eid != 0x1d) { + if (BLE_GAP_EVT_BASE <= eid && eid <= BLE_GAP_EVT_LAST) { + mp_printf(&mp_plat_print, "BLE GAP event: %d\n", eid - BLE_GAP_EVT_BASE); + } else { + mp_printf(&mp_plat_print, "BLE event: 0x%04x\n", event->header.evt_id); + } } + #endif ble_drv_evt_handler_entry_t *it = MP_STATE_VM(ble_drv_evt_handler_entries); bool done = false; while (it != NULL) { #if CIRCUITPY_VERBOSE_BLE - // mp_printf(&mp_plat_print, " calling handler: 0x%08lx, param: 0x%08lx\n", it->func-1, it->param); + // mp_printf(&mp_plat_print, " calling handler: 0x%08lx, param: 0x%08lx\n", it->func - 1, it->param); #endif + // Capture next before calling the function in case it removes itself from the list. + ble_drv_evt_handler_entry_t *next = it->next; done = it->func(event, it->param) || done; - it = it->next; + it = next; } #if CIRCUITPY_VERBOSE_BLE if (event->header.evt_id == BLE_GATTS_EVT_WRITE) { diff --git a/ports/nrf/bluetooth/ble_uart.c b/ports/nrf/bluetooth/ble_uart.c deleted file mode 100644 index 022b866ce6..0000000000 --- a/ports/nrf/bluetooth/ble_uart.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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 - -#include "ble.h" -#include "ble_uart.h" -#include "ringbuffer.h" -#include "py/mphal.h" -#include "py/runtime.h" -#include "lib/utils/interrupt_char.h" -#include "shared-bindings/_bleio/Adapter.h" -#include "shared-bindings/_bleio/Characteristic.h" -#include "shared-bindings/_bleio/Device.h" -#include "shared-bindings/_bleio/Service.h" -#include "shared-bindings/_bleio/UUID.h" - -#if CIRCUITPY_CONSOLE_BLE - -static const char default_name[] = "CP-REPL"; // max 8 chars or uuid won't fit in adv data -static const char NUS_UUID[] = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; - -#define NUS_RX_UUID 0x0002 -#define NUS_TX_UUID 0x0003 -#define BUFFER_SIZE 128 - -ringBuffer_typedef(uint8_t, ringbuffer_t); - -static bleio_device_obj_t m_device; -static bleio_service_obj_t *m_nus; -static bleio_characteristic_obj_t *m_tx_chara; -static bleio_characteristic_obj_t *m_rx_chara; - -static volatile bool m_cccd_enabled; - -static uint8_t m_rx_ring_buffer_data[BUFFER_SIZE]; -static ringbuffer_t m_rx_ring_buffer = { - .size = sizeof(m_rx_ring_buffer_data) + 1, - .elems = m_rx_ring_buffer_data, -}; - -STATIC void on_ble_evt(ble_evt_t *ble_evt, void *param) { - switch (ble_evt->header.evt_id) { - case BLE_GAP_EVT_DISCONNECTED: { - mp_obj_t device_obj = MP_OBJ_FROM_PTR(&m_device); - mp_call_function_0(mp_load_attr(device_obj, qstr_from_str("start_advertising"))); - break; - } - - case BLE_GATTS_EVT_WRITE: { - ble_gatts_evt_write_t *write = &ble_evt->evt.gatts_evt.params.write; - - if (write->handle == m_tx_chara->cccd_handle) { - m_cccd_enabled = true; - } else if (write->handle == m_rx_chara->handle) { - for (size_t i = 0; i < write->len; ++i) { - #if MICROPY_KBD_EXCEPTION - if (write->data[i] == mp_interrupt_char) { - mp_keyboard_interrupt(); - } else - #endif - { - bufferWrite(&m_rx_ring_buffer, write->data[i]); - } - } - } - } - } -} - -void ble_uart_init(void) { - mp_obj_t device_obj = MP_OBJ_FROM_PTR(&m_device); - m_device.base.type = &bleio_device_type; - m_device.service_list = mp_obj_new_list(0, NULL); - m_device.notif_handler = mp_const_none; - m_device.conn_handler = mp_const_none; - m_device.conn_handle = 0xFFFF; - m_device.is_peripheral = true; - m_device.name = mp_obj_new_str(default_name, strlen(default_name), false); - common_hal_bleio_adapter_get_address(&m_device.address); - - mp_obj_t nus_uuid_str = mp_obj_new_str(NUS_UUID, strlen(NUS_UUID), false); - mp_obj_t nus_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &nus_uuid_str); - mp_obj_t nus_obj = bleio_service_type.make_new(&bleio_service_type, 1, 0, &nus_uuid_obj); - m_nus = MP_OBJ_TO_PTR(nus_obj); - mp_call_function_1(mp_load_attr(device_obj, qstr_from_str("add_service")), nus_obj); - - mp_obj_t tx_uuid_int = mp_obj_new_int(NUS_TX_UUID); - mp_obj_t tx_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &tx_uuid_int); - mp_obj_t tx_obj = bleio_characteristic_type.make_new(&bleio_characteristic_type, 1, 0, &tx_uuid_obj); - m_tx_chara = MP_OBJ_TO_PTR(tx_obj); - m_tx_chara->uuid->type = UUID_TYPE_128BIT; - m_tx_chara->uuid->uuid_vs_idx = m_nus->uuid->uuid_vs_idx; - m_tx_chara->props.notify = true; - mp_call_function_1(mp_load_attr(nus_obj, qstr_from_str("add_characteristic")), tx_obj); - - mp_obj_t rx_uuid_int = mp_obj_new_int(NUS_RX_UUID); - mp_obj_t rx_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &rx_uuid_int); - mp_obj_t rx_obj = bleio_characteristic_type.make_new(&bleio_characteristic_type, 1, 0, &rx_uuid_obj); - m_rx_chara = MP_OBJ_TO_PTR(rx_obj); - m_rx_chara->uuid->type = UUID_TYPE_128BIT; - m_rx_chara->uuid->uuid_vs_idx = m_nus->uuid->uuid_vs_idx; - m_rx_chara->props.write = true; - m_rx_chara->props.write_wo_resp = true; - mp_call_function_1(mp_load_attr(nus_obj, qstr_from_str("add_characteristic")), rx_obj); - - mp_call_function_0(mp_load_attr(device_obj, qstr_from_str("start_advertising"))); - - ble_drv_add_event_handler(on_ble_evt, &m_device); - - m_cccd_enabled = false; - - while (!m_cccd_enabled) { - RUN_BACKGROUND_TASKS; - } -} - -bool ble_uart_connected(void) { - return m_device.conn_handle != BLE_CONN_HANDLE_INVALID; -} - -char ble_uart_rx_chr(void) { - while (isBufferEmpty(&m_rx_ring_buffer)) { - RUN_BACKGROUND_TASKS; - } - - uint8_t byte; - bufferRead(&m_rx_ring_buffer, byte); - return (int)byte; -} - -bool ble_uart_stdin_any(void) { - return !isBufferEmpty(&m_rx_ring_buffer); -} - -void ble_uart_stdout_tx_str(const char *text) { - mp_hal_stdout_tx_strn(text, strlen(text)); -} - -int mp_hal_stdin_rx_chr(void) { - return ble_uart_rx_chr(); -} - -void mp_hal_stdout_tx_strn(const char *str, size_t len) { - size_t send_len; - - while (len > 0) { - if (len >= BLE_GATT_ATT_MTU_DEFAULT - 3) { - send_len = (BLE_GATT_ATT_MTU_DEFAULT - 3); - } else { - send_len = len; - } - - mp_buffer_info_t bufinfo = { - .buf = (uint8_t *)str, - .len = send_len, - }; - - common_hal_bleio_characteristic_write_value(m_tx_chara, &bufinfo); - - len -= send_len; - str += send_len; - } -} - -#endif // CIRCUITPY_CONSOLE_BLE diff --git a/ports/nrf/bluetooth/ringbuffer.h b/ports/nrf/bluetooth/ringbuffer.h deleted file mode 100644 index baf2732931..0000000000 --- a/ports/nrf/bluetooth/ringbuffer.h +++ /dev/null @@ -1,99 +0,0 @@ -/* The MIT License (MIT) - * - * Copyright (c) 2013 Philip Thrasher - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * Philip Thrasher's Crazy Awesome Ring Buffer Macros! - * - * Below you will find some naughty macros for easy owning and manipulating - * generic ring buffers. Yes, they are slightly evil in readability, but they - * are really fast, and they work great. - * - * Example usage: - * - * #include - * - * // So we can use this in any method, this gives us a typedef - * // named 'intBuffer'. - * ringBuffer_typedef(int, intBuffer); - * - * int main() { - * // Declare vars. - * intBuffer myBuffer; - * - * bufferInit(myBuffer,1024,int); - * - * // We must have the pointer. All of the macros deal with the pointer. - * // (except for init.) - * intBuffer* myBuffer_ptr; - * myBuffer_ptr = &myBuffer; - * - * // Write two values. - * bufferWrite(myBuffer_ptr,37); - * bufferWrite(myBuffer_ptr,72); - * - * // Read a value into a local variable. - * int first; - * bufferRead(myBuffer_ptr,first); - * assert(first == 37); // true - * - * int second; - * bufferRead(myBuffer_ptr,second); - * assert(second == 72); // true - * - * return 0; - * } - * - */ - -#ifndef _ringbuffer_h -#define _ringbuffer_h - -#define ringBuffer_typedef(T, NAME) \ - typedef struct { \ - int size; \ - volatile int start; \ - volatile int end; \ - T *elems; \ - } NAME - -#define bufferInit(BUF, S, T) \ - BUF.size = S + 1; \ - BUF.start = 0; \ - BUF.end = 0; \ - BUF.elems = (T *)calloc(BUF.size, sizeof(T)) - - -#define bufferDestroy(BUF) free((BUF)->elems) -#define nextStartIndex(BUF) (((BUF)->start + 1) % (BUF)->size) -#define nextEndIndex(BUF) (((BUF)->end + 1) % (BUF)->size) -#define isBufferEmpty(BUF) ((BUF)->end == (BUF)->start) -#define isBufferFull(BUF) (nextEndIndex(BUF) == (BUF)->start) - -#define bufferWrite(BUF, ELEM) \ - (BUF)->elems[(BUF)->end] = ELEM; \ - (BUF)->end = ((BUF)->end + 1) % (BUF)->size; \ - if (isBufferEmpty(BUF)) { \ - (BUF)->start = nextStartIndex(BUF); \ - } - -#define bufferRead(BUF, ELEM) \ - ELEM = (BUF)->elems[(BUF)->start]; \ - (BUF)->start = nextStartIndex(BUF); - -#endif diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk index 9fae5ccc4d..6381df74f4 100644 --- a/ports/nrf/boards/pca10100/mpconfigboard.mk +++ b/ports/nrf/boards/pca10100/mpconfigboard.mk @@ -18,6 +18,7 @@ CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_JSON = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_NVM = 0 @@ -26,6 +27,7 @@ CIRCUITPY_RE = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_SDCARDIO = 0 CIRCUITPY_ULAB = 0 +CIRCUITPY_USB_MIDI = 0 MICROPY_PY_ASYNC_AWAIT = 0 diff --git a/ports/nrf/boards/simmel/mpconfigboard.mk b/ports/nrf/boards/simmel/mpconfigboard.mk index e235fd1051..4f6385260e 100644 --- a/ports/nrf/boards/simmel/mpconfigboard.mk +++ b/ports/nrf/boards/simmel/mpconfigboard.mk @@ -20,7 +20,7 @@ CIRCUITPY_COUNTIO = 0 CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_ERRNO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 -CIRCUITPY_GAMEPAD = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_NVM = 0 @@ -43,3 +43,5 @@ CIRCUITPY_WATCHDOG = 1 # Override optimization to keep binary small OPTIMIZATION_FLAGS = -Os +SUPEROPT_VM = 0 +SUPEROPT_GC = 0 diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index bfcaf461a0..bf92c7b3ba 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -40,6 +40,7 @@ #include "py/gc.h" #include "py/objstr.h" #include "py/runtime.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #include "supervisor/shared/safe_mode.h" #include "supervisor/shared/tick.h" #include "supervisor/usb.h" @@ -174,6 +175,16 @@ STATIC uint32_t ble_stack_enable(void) { return err_code; } + + // Make sure service changed characteristic is on. This lets us prompt a peer to re-discover + // portions of our attribute table. + memset(&ble_conf, 0, sizeof(ble_conf)); + ble_conf.gatts_cfg.service_changed.service_changed = 1; + err_code = sd_ble_cfg_set(BLE_GATTS_CFG_SERVICE_CHANGED, &ble_conf, sd_ram_end); + if (err_code != NRF_SUCCESS) { + return err_code; + } + // Increase the GATT Server attribute size to accomodate both the CircuitPython built-in service // and anything the user does. memset(&ble_conf, 0, sizeof(ble_conf)); @@ -231,6 +242,9 @@ STATIC bool adapter_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // For debugging. // mp_printf(&mp_plat_print, "Adapter event: 0x%04x\n", ble_evt->header.evt_id); + // Always queue a background run after a BLE event. + background_callback_add_core(&self->background_callback); + switch (ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: { // Find an empty connection. One must always be available because the SD has the same @@ -299,10 +313,6 @@ STATIC bool adapter_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { break; } - case BLE_GAP_EVT_ADV_SET_TERMINATED: - self->current_advertising_data = NULL; - break; - default: // For debugging. // mp_printf(&mp_plat_print, "Unhandled adapter event: 0x%04x\n", ble_evt->header.evt_id); @@ -333,6 +343,11 @@ STATIC void bleio_adapter_reset_name(bleio_adapter_obj_t *self) { common_hal_bleio_adapter_set_name(self, (char *)default_ble_name); } +static void bluetooth_adapter_background(void *data) { + supervisor_bluetooth_background(); + bleio_background(); +} + void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { const bool is_enabled = common_hal_bleio_adapter_get_enabled(self); @@ -341,7 +356,7 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable return; } - uint32_t err_code; + uint32_t err_code = NRF_SUCCESS; if (enabled) { // The SD takes over the POWER module and will fail if the module is already in use. // Occurs when USB is initialized previously @@ -365,11 +380,19 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable bleio_connection_internal_t *connection = &bleio_connections[i]; // Reset connection. bleio_connection_clear(connection); + ble_drv_remove_event_handler(connection_on_ble_evt, connection); connection->conn_handle = BLE_CONN_HANDLE_INVALID; } + self->background_callback.fun = bluetooth_adapter_background; + self->background_callback.data = self; bleio_adapter_reset_name(self); - ble_drv_add_event_handler_entry(&self->handler_entry, adapter_on_ble_evt, self); + ble_drv_add_event_handler_entry(&self->connection_handler_entry, adapter_on_ble_evt, self); + bluetooth_adapter_background(self); } else { + ble_drv_remove_event_handler(adapter_on_ble_evt, self); + if (self->current_advertising_data != NULL) { + common_hal_bleio_adapter_stop_advertising(self); + } ble_drv_reset(); self->scan_results = NULL; self->current_advertising_data = NULL; @@ -428,6 +451,22 @@ void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *na sd_ble_gap_device_name_set(&sec, (const uint8_t *)name, strlen(name)); } +STATIC uint32_t _update_identities(bool is_central) { + const ble_gap_id_key_t *keys[BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT]; + // TODO: Make sure we don't store more than BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT identities of + // each type. Right now, we'll silently ignore those keys. + size_t len = bonding_load_identities(is_central, keys, BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT); + uint32_t status = NRF_SUCCESS; + if (len > 0) { + status = sd_ble_gap_device_identities_set( + keys, + NULL, // Don't set local IRK because we use our device IRK for private addresses. + len + ); + } + return status; +}; + STATIC bool scan_on_ble_evt(ble_evt_t *ble_evt, void *scan_results_in) { bleio_scanresults_obj_t *scan_results = (bleio_scanresults_obj_t *)scan_results_in; @@ -476,6 +515,11 @@ mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t sd_data->len = max_packet_size; sd_data->p_data = raw_data + sizeof(ble_data_t); + + // Update the identities of peripheral peers so they can use a private + // resolvable address in their advertisements. + check_nrf_error(_update_identities(true)); + ble_drv_add_event_handler(scan_on_ble_evt, self->scan_results); uint32_t nrf_timeout = SEC_TO_UNITS(timeout, UNIT_10_MS) + 0.5f; @@ -500,9 +544,8 @@ mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t .scan_phys = BLE_GAP_PHY_1MBPS, .active = active }; - uint32_t err_code; - vm_used_ble = true; - err_code = sd_ble_gap_scan_start(&scan_params, sd_data); + + uint32_t err_code = sd_ble_gap_scan_start(&scan_params, sd_data); if (err_code != NRF_SUCCESS) { ble_drv_remove_event_handler(scan_on_ble_evt, self->scan_results); @@ -514,6 +557,9 @@ mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t } void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { + if (self->scan_results == NULL) { + return; + } sd_ble_gap_scan_stop(); shared_module_bleio_scanresults_set_done(self->scan_results, true); ble_drv_remove_event_handler(scan_on_ble_evt, self->scan_results); @@ -548,14 +594,16 @@ STATIC bool connect_on_ble_evt(ble_evt_t *ble_evt, void *info_in) { return true; } -mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { - - ble_gap_addr_t addr; - - addr.addr_type = address->type; +STATIC void _convert_address(const bleio_address_obj_t *address, ble_gap_addr_t *sd_address) { + sd_address->addr_type = address->type; mp_buffer_info_t address_buf_info; mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ); - memcpy(addr.addr, (uint8_t *)address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); + memcpy(sd_address->addr, (uint8_t *)address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES); +} + +mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { + ble_gap_addr_t addr; + _convert_address(address, &addr); ble_gap_scan_params_t scan_params = { .interval = MSEC_TO_UNITS(100, UNIT_0_625_MS), @@ -576,7 +624,6 @@ mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_addre ble_drv_add_event_handler(connect_on_ble_evt, &event_info); event_info.done = false; - vm_used_ble = true; uint32_t err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_CUSTOM); if (err_code != NRF_SUCCESS) { @@ -594,6 +641,17 @@ mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_addre if (conn_handle == BLE_CONN_HANDLE_INVALID) { mp_raise_bleio_BluetoothError(translate("Failed to connect: timeout")); } + // If we have keys, then try and encrypt the connection. + const ble_gap_enc_key_t *encryption_key = bonding_load_peer_encryption_key(true, &addr); + pair_status_t pair_status = PAIR_NOT_PAIRED; + if (encryption_key != NULL) { + err_code = sd_ble_gap_encrypt(conn_handle, &encryption_key->master_id, &encryption_key->enc_info); + pair_status = PAIR_WAITING; + + if (err_code != NRF_SUCCESS) { + pair_status = PAIR_NOT_PAIRED; + } + } // Negotiate for better PHY, larger MTU and data lengths since we are the central. These are // nice-to-haves so ignore any errors. @@ -610,6 +668,7 @@ mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_addre bleio_connection_internal_t *connection = &bleio_connections[i]; if (connection->conn_handle == conn_handle) { connection->is_central = true; + connection->pair_status = pair_status; return bleio_connection_new_from_internal(connection); } } @@ -634,9 +693,14 @@ STATIC bool advertising_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { bleio_adapter_obj_t *self = (bleio_adapter_obj_t *)self_in; switch (ble_evt->header.evt_id) { + case BLE_GAP_EVT_CONNECTED: // Connecting also stops an advertisement. + // Set the tx_power for the connection higher than the advertisement. + sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, ble_evt->evt.gap_evt.conn_handle, 0); + common_hal_bleio_adapter_stop_advertising(self); + return false; + break; case BLE_GAP_EVT_ADV_SET_TERMINATED: common_hal_bleio_adapter_stop_advertising(self); - ble_drv_remove_event_handler(advertising_on_ble_evt, self_in); break; default: @@ -648,10 +712,11 @@ STATIC bool advertising_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { return true; } -uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, - bool anonymous, uint32_t timeout, float interval, uint8_t *advertising_data, - uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len, - mp_int_t tx_power) { +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) { if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) { return NRF_ERROR_BUSY; } @@ -667,6 +732,8 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, scan_response_data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX; uint8_t adv_type; + ble_gap_addr_t *peer = NULL; + ble_gap_addr_t peer_address; if (extended) { if (connectable) { adv_type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED; @@ -676,7 +743,17 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, adv_type = BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED; } } else if (connectable) { - adv_type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; + if (directed_to == NULL) { + adv_type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; + } else if (interval <= 3.5 && timeout <= 1.3) { + adv_type = BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE; + _convert_address(directed_to, &peer_address); + peer = &peer_address; + } else { + adv_type = BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED; + _convert_address(directed_to, &peer_address); + peer = &peer_address; + } } else if (scan_response_data_len > 0) { adv_type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED; } else { @@ -714,26 +791,34 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, .duration = SEC_TO_UNITS(timeout, UNIT_10_MS), .filter_policy = BLE_GAP_ADV_FP_ANY, .primary_phy = BLE_GAP_PHY_1MBPS, + .p_peer_addr = peer, }; const ble_gap_adv_data_t ble_gap_adv_data = { - .adv_data.p_data = advertising_data, + .adv_data.p_data = (uint8_t *)advertising_data, .adv_data.len = advertising_data_len, - .scan_rsp_data.p_data = scan_response_data_len > 0 ? scan_response_data : NULL, + .scan_rsp_data.p_data = scan_response_data_len > 0 ? (uint8_t *)scan_response_data : NULL, .scan_rsp_data.len = scan_response_data_len, }; + // Update the identities of central peers so they can use a private address + // in the scan and connection initiation. + err_code = _update_identities(false); + if (err_code != NRF_SUCCESS) { + return err_code; + } + err_code = sd_ble_gap_adv_set_configure(&adv_handle, &ble_gap_adv_data, &adv_params); if (err_code != NRF_SUCCESS) { return err_code; } - ble_drv_add_event_handler(advertising_on_ble_evt, self); + ble_drv_add_event_handler_entry(&self->advertising_handler_entry, advertising_on_ble_evt, self); + err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, adv_handle, tx_power); if (err_code != NRF_SUCCESS) { return err_code; } - vm_used_ble = true; err_code = sd_ble_gap_adv_start(adv_handle, BLE_CONN_CFG_TAG_CUSTOM); if (err_code != NRF_SUCCESS) { return err_code; @@ -743,9 +828,16 @@ uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, } -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) { - if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) { +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) { + if (self->user_advertising) { mp_raise_bleio_BluetoothError(translate("Already advertising.")); + } else if (self->current_advertising_data != NULL) { + // If the user isn't advertising, then the background is. So, stop the + // background advertising so the user can. + common_hal_bleio_adapter_stop_advertising(self); } // interval value has already been validated. @@ -756,6 +848,11 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool mp_raise_bleio_BluetoothError(translate("Extended advertisements with scan response not supported.")); } + + if (advertising_data_bufinfo->len > 0 && directed_to != NULL) { + mp_raise_bleio_BluetoothError(translate("Data not supported with directed advertising")); + } + // Anonymous mode requires a timeout so that we don't continue to broadcast // the same data while cycling the MAC address -- otherwise, what's the // point of randomizing the MAC address? @@ -792,7 +889,9 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool advertising_data_bufinfo->len, self->scan_response_data, scan_response_data_bufinfo->len, - tx_power)); + tx_power, + directed_to)); + self->user_advertising = true; } void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { @@ -800,9 +899,10 @@ void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { return; } - // TODO: Don't actually stop. Switch to advertising CircuitPython if we don't already have a connection. const uint32_t err_code = sd_ble_gap_adv_stop(adv_handle); + ble_drv_remove_event_handler(advertising_on_ble_evt, self); self->current_advertising_data = NULL; + self->user_advertising = false; if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) { check_nrf_error(err_code); @@ -847,6 +947,10 @@ void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { bonding_erase_storage(); } +bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self) { + return bonding_peripheral_bond_count() > 0; +} + void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) { gc_collect_root((void **)adapter, sizeof(bleio_adapter_obj_t) / sizeof(size_t)); gc_collect_root((void **)bleio_connections, sizeof(bleio_connections) / sizeof(size_t)); diff --git a/ports/nrf/common-hal/_bleio/Adapter.h b/ports/nrf/common-hal/_bleio/Adapter.h index 68a8b48644..4d3fac20c7 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.h +++ b/ports/nrf/common-hal/_bleio/Adapter.h @@ -35,6 +35,8 @@ #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/ScanResults.h" +#include "supervisor/background_callback.h" + #ifndef BLEIO_TOTAL_CONNECTION_COUNT #define BLEIO_TOTAL_CONNECTION_COUNT 5 #endif @@ -43,13 +45,18 @@ extern bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUN typedef struct { mp_obj_base_t base; + // Pointer to buffers we maintain so that the data is long lived. uint8_t *advertising_data; uint8_t *scan_response_data; - uint8_t *current_advertising_data; + // Pointer to current data. + const uint8_t *current_advertising_data; bleio_scanresults_obj_t *scan_results; mp_obj_t name; mp_obj_tuple_t *connection_objs; - ble_drv_evt_handler_entry_t handler_entry; + ble_drv_evt_handler_entry_t connection_handler_entry; + ble_drv_evt_handler_entry_t advertising_handler_entry; + background_callback_t background_callback; + bool user_advertising; } bleio_adapter_obj_t; void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter); diff --git a/ports/nrf/common-hal/_bleio/Characteristic.c b/ports/nrf/common-hal/_bleio/Characteristic.c index f651b93b6b..df86654c70 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.c +++ b/ports/nrf/common-hal/_bleio/Characteristic.c @@ -83,15 +83,37 @@ STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_ } } -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) { +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) { self->service = service; self->uuid = uuid; self->handle = BLE_GATT_HANDLE_INVALID; self->props = props; self->read_perm = read_perm; self->write_perm = write_perm; - self->initial_value = mp_obj_new_bytes(initial_value_bufinfo->buf, initial_value_bufinfo->len); - self->descriptor_list = mp_obj_new_list(0, NULL); + self->initial_value_len = 0; + self->initial_value = NULL; + if (initial_value_bufinfo != NULL) { + // Copy the initial value if it's on the heap. Otherwise it's internal and we may not be able + // to allocate. + self->initial_value_len = initial_value_bufinfo->len; + if (gc_alloc_possible()) { + if (gc_nbytes(initial_value_bufinfo->buf) > 0) { + uint8_t *initial_value = m_malloc(self->initial_value_len, false); + memcpy(initial_value, initial_value_bufinfo->buf, self->initial_value_len); + self->initial_value = initial_value; + } else { + self->initial_value = initial_value_bufinfo->buf; + } + self->descriptor_list = mp_obj_new_list(0, NULL); + } else { + self->initial_value = initial_value_bufinfo->buf; + self->descriptor_list = NULL; + } + } const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX; if (max_length < 0 || max_length > max_length_max) { @@ -104,11 +126,14 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, if (service->is_remote) { self->handle = handle; } else { - common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo); + common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo, user_description); } } mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self) { + if (self->descriptor_list == NULL) { + return mp_const_empty_tuple; + } return mp_obj_new_tuple(self->descriptor_list->len, self->descriptor_list->items); } @@ -193,6 +218,11 @@ bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties } void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { + if (self->descriptor_list == NULL) { + // This should only happen from internal use so we just fail silently instead of raising an + // exception. + return; + } ble_uuid_t desc_uuid; bleio_uuid_convert_to_nrf_ble_uuid(descriptor->uuid, &desc_uuid); diff --git a/ports/nrf/common-hal/_bleio/Characteristic.h b/ports/nrf/common-hal/_bleio/Characteristic.h index 382fd4a81e..f7a3ec3e1a 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.h +++ b/ports/nrf/common-hal/_bleio/Characteristic.h @@ -39,9 +39,9 @@ typedef struct _bleio_characteristic_obj { // Will be MP_OBJ_NULL before being assigned to a Service. bleio_service_obj_t *service; bleio_uuid_obj_t *uuid; - mp_obj_t initial_value; + const uint8_t *initial_value; + uint16_t initial_value_len; uint16_t max_length; - bool fixed_length; uint16_t handle; bleio_characteristic_properties_t props; bleio_attribute_security_mode_t read_perm; @@ -50,6 +50,7 @@ typedef struct _bleio_characteristic_obj { uint16_t user_desc_handle; uint16_t cccd_handle; uint16_t sccd_handle; + bool fixed_length; } bleio_characteristic_obj_t; #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H diff --git a/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c index b17327da47..ca37f41c1a 100644 --- a/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c +++ b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c @@ -80,20 +80,34 @@ STATIC bool characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) { return true; } +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) { + + self->characteristic = characteristic; + self->timeout_ms = timeout * 1000; + + self->ringbuf.buf = (uint8_t *)buffer; + self->ringbuf.size = buffer_size; + self->ringbuf.iget = 0; + self->ringbuf.iput = 0; + + if (static_handler_entry != NULL) { + ble_drv_add_event_handler_entry((ble_drv_evt_handler_entry_t *)static_handler_entry, characteristic_buffer_on_ble_evt, self); + } else { + ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self); + } +} + // Assumes that timeout and buffer_size have been validated before call. 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) { - - self->characteristic = characteristic; - self->timeout_ms = timeout * 1000; - // This is a macro. - // true means long-lived, so it won't be moved. - ringbuf_alloc(&self->ringbuf, buffer_size, true); - - ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self); - + uint8_t *buffer = m_malloc(buffer_size, true); + _common_hal_bleio_characteristic_buffer_construct(self, characteristic, timeout, buffer, buffer_size, NULL); } uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) { diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index cdc6fd3d68..b94ad09e6a 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -309,6 +309,9 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { } else { if (bonding_load_cccd_info(self->is_central, self->conn_handle, self->ediv)) { // Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values. + // Indicate ATTR table change because we may have reloaded since the peer last + // connected. + sd_ble_gatts_service_changed(self->conn_handle, 0xC, 0xFFFF); } else { // No matching bonding found, so use fresh system attributes. sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0); @@ -351,6 +354,14 @@ void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { } void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { + // We may already be trying to pair if we just reconnected to a peer we're + // bonded with. + while (self->pair_status == PAIR_WAITING && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } + if (self->pair_status == PAIR_PAIRED) { + return; + } self->pair_status = PAIR_WAITING; check_nrf_error(sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params)); @@ -522,7 +533,8 @@ STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio characteristic, m_char_discovery_service, gattc_char->handle_value, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values don't matter for gattc - mp_const_empty_bytes); + mp_const_empty_bytes, + NULL); mp_obj_list_append(MP_OBJ_FROM_PTR(m_char_discovery_service->characteristic_list), MP_OBJ_FROM_PTR(characteristic)); diff --git a/ports/nrf/common-hal/_bleio/PacketBuffer.c b/ports/nrf/common-hal/_bleio/PacketBuffer.c index c3e8e43b3f..4a123a7c00 100644 --- a/ports/nrf/common-hal/_bleio/PacketBuffer.c +++ b/ports/nrf/common-hal/_bleio/PacketBuffer.c @@ -76,7 +76,7 @@ STATIC uint32_t queue_next_write(bleio_packet_buffer_obj_t *self) { ble_gattc_write_params_t write_params = { .write_op = self->write_type, .handle = self->characteristic->handle, - .p_value = self->outgoing[self->pending_index], + .p_value = (const uint8_t *)self->outgoing[self->pending_index], .len = self->pending_size, }; @@ -89,7 +89,7 @@ STATIC uint32_t queue_next_write(bleio_packet_buffer_obj_t *self) { .type = self->write_type, .offset = 0, .p_len = &hvx_len, - .p_data = self->outgoing[self->pending_index], + .p_data = (const uint8_t *)self->outgoing[self->pending_index], }; err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params); } @@ -173,6 +173,25 @@ STATIC bool packet_buffer_on_ble_server_evt(ble_evt_t *ble_evt, void *param) { } break; } + case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a + if (self->conn_handle != BLE_CONN_HANDLE_INVALID) { + break; + } + uint16_t conn_handle = ble_evt->evt.gatts_evt.conn_handle; + // Check to see if the bond restored the HVX state. + uint16_t cccd; + ble_gatts_value_t value; + value.len = sizeof(uint16_t); + value.offset = 0; + value.p_value = (uint8_t *)&cccd; + sd_ble_gatts_value_get(conn_handle, self->characteristic->cccd_handle, &value); + if (cccd & BLE_GATT_HVX_NOTIFICATION) { + self->conn_handle = conn_handle; + } + break; + } + case BLE_GAP_EVT_CONNECTED: + break; case BLE_GAP_EVT_DISCONNECTED: if (self->conn_handle == ble_evt->evt.gap_evt.conn_handle) { self->conn_handle = BLE_CONN_HANDLE_INVALID; @@ -188,12 +207,16 @@ STATIC bool packet_buffer_on_ble_server_evt(ble_evt_t *ble_evt, void *param) { return true; } -void common_hal_bleio_packet_buffer_construct( + +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) { + uint32_t *incoming_buffer, size_t incoming_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, + void *static_handler_entry) { self->characteristic = characteristic; self->client = self->characteristic->service->is_remote; + self->max_packet_size = max_packet_size; bleio_characteristic_properties_t incoming = self->characteristic->props & (CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_WRITE); bleio_characteristic_properties_t outgoing = self->characteristic->props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE); @@ -207,28 +230,25 @@ void common_hal_bleio_packet_buffer_construct( self->conn_handle = BLE_CONN_HANDLE_INVALID; } - // Cap the packet size to our implementation limits. - self->max_packet_size = MIN(max_packet_size, BLE_GATTS_VAR_ATTR_LEN_MAX - 3); - if (incoming) { - if (!ringbuf_alloc(&self->ringbuf, buffer_size * (sizeof(uint16_t) + self->max_packet_size), false)) { - mp_raise_ValueError(translate("Buffer too large and unable to allocate")); - } + self->ringbuf.buf = (uint8_t *)incoming_buffer; + self->ringbuf.size = incoming_buffer_size; + self->ringbuf.iget = 0; + self->ringbuf.iput = 0; } - if (outgoing) { - self->packet_queued = false; - self->pending_index = 0; - self->pending_size = 0; - self->outgoing[0] = m_malloc(self->max_packet_size, false); - self->outgoing[1] = m_malloc(self->max_packet_size, false); - } else { - self->outgoing[0] = NULL; - self->outgoing[1] = NULL; - } + self->packet_queued = false; + self->pending_index = 0; + self->pending_size = 0; + self->outgoing[0] = outgoing_buffer1; + self->outgoing[1] = outgoing_buffer2; if (self->client) { - ble_drv_add_event_handler(packet_buffer_on_ble_client_evt, self); + if (static_handler_entry != NULL) { + ble_drv_add_event_handler_entry((ble_drv_evt_handler_entry_t *)static_handler_entry, packet_buffer_on_ble_client_evt, self); + } else { + ble_drv_add_event_handler(packet_buffer_on_ble_client_evt, self); + } if (incoming) { // Prefer notify if both are available. if (incoming & CHAR_PROP_NOTIFY) { @@ -245,7 +265,11 @@ void common_hal_bleio_packet_buffer_construct( } } } else { - ble_drv_add_event_handler(packet_buffer_on_ble_server_evt, self); + if (static_handler_entry != NULL) { + ble_drv_add_event_handler_entry((ble_drv_evt_handler_entry_t *)static_handler_entry, packet_buffer_on_ble_server_evt, self); + } else { + ble_drv_add_event_handler(packet_buffer_on_ble_server_evt, self); + } if (outgoing) { self->write_type = BLE_GATT_HVX_INDICATION; if (outgoing & CHAR_PROP_NOTIFY) { @@ -253,12 +277,44 @@ void common_hal_bleio_packet_buffer_construct( } } } + +} + +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) { + + // Cap the packet size to our implementation limits. + max_packet_size = MIN(max_packet_size, BLE_GATTS_VAR_ATTR_LEN_MAX - 3); + + bleio_characteristic_properties_t incoming = characteristic->props & (CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_WRITE); + bleio_characteristic_properties_t outgoing = characteristic->props & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE); + if (characteristic->service->is_remote) { + // Swap if we're the client. + bleio_characteristic_properties_t temp = incoming; + incoming = outgoing; + outgoing = temp; + } + size_t incoming_buffer_size = 0; + uint32_t *incoming_buffer = NULL; + if (incoming) { + incoming_buffer_size = buffer_size * (sizeof(uint16_t) + max_packet_size); + incoming_buffer = m_malloc(incoming_buffer_size, false); + } + + uint32_t *outgoing1 = NULL; + uint32_t *outgoing2 = NULL; + if (outgoing) { + outgoing1 = m_malloc(max_packet_size, false); + outgoing2 = m_malloc(max_packet_size, false); + } + _common_hal_bleio_packet_buffer_construct(self, characteristic, + incoming_buffer, incoming_buffer_size, + outgoing1, outgoing2, max_packet_size, + NULL); } mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len) { - if (self->conn_handle == BLE_CONN_HANDLE_INVALID) { - mp_raise_ConnectionError(translate("Not connected")); - } if (ringbuf_num_filled(&self->ringbuf) < 2) { return 0; } @@ -291,7 +347,7 @@ mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self return ret; } -mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len, uint8_t *header, size_t header_len) { +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) { if (self->outgoing[0] == NULL) { mp_raise_bleio_BluetoothError(translate("Writes not supported on Characteristic")); } @@ -330,14 +386,14 @@ mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, u uint8_t is_nested_critical_region; sd_nvic_critical_region_enter(&is_nested_critical_region); - uint8_t *pending = self->outgoing[self->pending_index]; + uint32_t *pending = self->outgoing[self->pending_index]; if (self->pending_size == 0) { memcpy(pending, header, header_len); self->pending_size += header_len; num_bytes_written += header_len; } - memcpy(pending + self->pending_size, data, len); + memcpy(((uint8_t *)pending) + self->pending_size, data, len); self->pending_size += len; num_bytes_written += len; @@ -425,6 +481,15 @@ mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_ return MIN(self->characteristic->max_length, self->max_packet_size); } +void common_hal_bleio_packet_buffer_flush(bleio_packet_buffer_obj_t *self) { + while (self->pending_size != 0 && + self->packet_queued && + self->conn_handle != BLE_CONN_HANDLE_INVALID && + !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } +} + bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { return self->characteristic == NULL; } diff --git a/ports/nrf/common-hal/_bleio/PacketBuffer.h b/ports/nrf/common-hal/_bleio/PacketBuffer.h index 6f2626565b..9e679fc5ca 100644 --- a/ports/nrf/common-hal/_bleio/PacketBuffer.h +++ b/ports/nrf/common-hal/_bleio/PacketBuffer.h @@ -39,7 +39,7 @@ typedef struct { ringbuf_t ringbuf; // Two outgoing buffers to alternate between. One will be queued for transmission by the SD and // the other is waiting to be queued and can be extended. - uint8_t *outgoing[2]; + uint32_t *outgoing[2]; volatile uint16_t pending_size; // We remember the conn_handle so we can do a NOTIFY/INDICATE to a client. // We can find out the conn_handle on a Characteristic write or a CCCD write (but not a read). diff --git a/ports/nrf/common-hal/_bleio/Service.c b/ports/nrf/common-hal/_bleio/Service.c index 74e74fac9f..4980b70fe1 100644 --- a/ports/nrf/common-hal/_bleio/Service.c +++ b/ports/nrf/common-hal/_bleio/Service.c @@ -34,6 +34,19 @@ #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/Adapter.h" + +STATIC void _indicate_service_change(uint16_t start, uint16_t end) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + uint16_t conn_handle = connection->conn_handle; + if (connection->conn_handle == BLE_CONN_HANDLE_INVALID) { + continue; + } + + sd_ble_gatts_service_changed(conn_handle, start, end); + } +} + 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) { self->handle = 0xFFFF; self->uuid = uuid; @@ -50,9 +63,13 @@ uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uu service_type = BLE_GATTS_SRVC_TYPE_SECONDARY; } - vm_used_ble = true; + uint32_t result = sd_ble_gatts_service_add(service_type, &nordic_uuid, &self->handle); + // Do a service changed indication to all connected peers. + if (result == NRF_SUCCESS) { + _indicate_service_change(self->handle, self->handle); + } - return sd_ble_gatts_service_add(service_type, &nordic_uuid, &self->handle); + return result; } void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) { @@ -85,9 +102,18 @@ bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { return self->is_secondary; } +STATIC void _expand_range(uint16_t new_value, uint16_t *start, uint16_t *end) { + if (new_value == 0) { + return; + } + *start = MIN(*start, new_value); + *end = MAX(*end, new_value); +} + void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, - mp_buffer_info_t *initial_value_bufinfo) { + mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { ble_gatts_char_md_t char_md = { .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0, .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0, @@ -117,6 +143,18 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, char_md.p_cccd_md = &cccd_md; } + ble_gatts_attr_md_t user_desc_md; + if (user_description != NULL && strlen(user_description) > 0) { + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&user_desc_md.read_perm); + // If the description is on the Python heap, then have the SD copy it. If not, assume it's + // static and will live for longer than the SD. + user_desc_md.vloc = gc_nbytes(user_description) > 0 ? BLE_GATTS_VLOC_STACK : BLE_GATTS_VLOC_USER; + char_md.p_user_desc_md = &user_desc_md; + char_md.p_char_user_desc = (const uint8_t *)user_description; + char_md.char_user_desc_max_size = strlen(user_description); + char_md.char_user_desc_size = strlen(user_description); + } + bleio_attribute_gatts_set_security_mode(&char_attr_md.read_perm, characteristic->read_perm); bleio_attribute_gatts_set_security_mode(&char_attr_md.write_perm, characteristic->write_perm); #if CIRCUITPY_VERBOSE_BLE @@ -124,14 +162,11 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, char_attr_md.rd_auth = true; #endif - mp_buffer_info_t char_value_bufinfo; - mp_get_buffer_raise(characteristic->initial_value, &char_value_bufinfo, MP_BUFFER_READ); - ble_gatts_attr_t char_attr = { .p_uuid = &char_uuid, .p_attr_md = &char_attr_md, - .init_len = char_value_bufinfo.len, - .p_value = char_value_bufinfo.buf, + .init_len = characteristic->initial_value_len, + .p_value = (uint8_t *)characteristic->initial_value, .init_offs = 0, .max_len = characteristic->max_length, }; @@ -144,6 +179,15 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, characteristic->cccd_handle = char_handles.cccd_handle; characteristic->sccd_handle = char_handles.sccd_handle; characteristic->handle = char_handles.value_handle; + + // Indicate that the attribute table has changed. + uint16_t start = char_handles.value_handle; + uint16_t end = char_handles.value_handle; + _expand_range(char_handles.cccd_handle, &start, &end); + _expand_range(char_handles.sccd_handle, &start, &end); + _expand_range(char_handles.user_desc_handle, &start, &end); + _indicate_service_change(start, end); + #if CIRCUITPY_VERBOSE_BLE mp_printf(&mp_plat_print, "Char handle %x user %x cccd %x sccd %x\n", characteristic->handle, characteristic->user_desc_handle, characteristic->cccd_handle, characteristic->sccd_handle); #endif diff --git a/ports/nrf/common-hal/_bleio/UUID.c b/ports/nrf/common-hal/_bleio/UUID.c index 399bf23ed2..a55d874f22 100644 --- a/ports/nrf/common-hal/_bleio/UUID.c +++ b/ports/nrf/common-hal/_bleio/UUID.c @@ -50,7 +50,6 @@ void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, co // Register this vendor-specific UUID. Bytes 12 and 13 will be zero. check_nrf_error(sd_ble_uuid_vs_add(&vs_uuid, &self->nrf_ble_uuid.type)); - vm_used_ble = true; } } diff --git a/ports/nrf/common-hal/_bleio/__init__.c b/ports/nrf/common-hal/_bleio/__init__.c index 8c28b970c4..604f0be2e5 100644 --- a/ports/nrf/common-hal/_bleio/__init__.c +++ b/ports/nrf/common-hal/_bleio/__init__.c @@ -36,7 +36,7 @@ #include "shared-bindings/_bleio/Descriptor.h" #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" -#include "supervisor/shared/bluetooth.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #include "common-hal/_bleio/__init__.h" #include "common-hal/_bleio/bonding.h" @@ -94,30 +94,23 @@ void check_sec_status(uint8_t sec_status) { } } -bool vm_used_ble; - // Turn off BLE on a reset or reload. void bleio_reset() { + // Set this explicitly to save data. + common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { return; } + bleio_adapter_reset(&common_hal_bleio_adapter_obj); - if (!vm_used_ble) { - // No user-code BLE operations were done, so we can maintain the supervisor state. - return; - } common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); bonding_reset(); supervisor_start_bluetooth(); } // The singleton _bleio.Adapter object, bound to _bleio.adapter -// It currently only has properties and no state -bleio_adapter_obj_t common_hal_bleio_adapter_obj = { - .base = { - .type = &bleio_adapter_type, - }, -}; +// It currently only has properties and no state. Inited by bleio_reset +bleio_adapter_obj_t common_hal_bleio_adapter_obj; void common_hal_bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLE_CONN_HANDLE_INVALID) { diff --git a/ports/nrf/common-hal/_bleio/__init__.h b/ports/nrf/common-hal/_bleio/__init__.h index 97f82ece34..d94267dd3c 100644 --- a/ports/nrf/common-hal/_bleio/__init__.h +++ b/ports/nrf/common-hal/_bleio/__init__.h @@ -45,7 +45,4 @@ void check_nrf_error(uint32_t err_code); void check_gatt_status(uint16_t gatt_status); void check_sec_status(uint8_t sec_status); -// Track if the user code modified the BLE state to know if we need to undo it on reload. -extern bool vm_used_ble; - #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H diff --git a/ports/nrf/common-hal/_bleio/bonding.c b/ports/nrf/common-hal/_bleio/bonding.c index 0c39bfe423..42df817c71 100644 --- a/ports/nrf/common-hal/_bleio/bonding.c +++ b/ports/nrf/common-hal/_bleio/bonding.c @@ -139,6 +139,20 @@ STATIC bonding_block_t *find_existing_block(bool is_central, bonding_block_type_ } } +size_t bonding_peripheral_bond_count(void) { + bonding_block_t *block = NULL; + size_t count = 0; + while (1) { + block = next_block(block); + if (block == NULL) { + return count; + } + if (block->type != BLOCK_UNUSED && block->type != BLOCK_INVALID && !block->is_central) { + count++; + } + } +} + // Get an empty block large enough to store data_length data. STATIC bonding_block_t *find_unused_block(uint16_t data_length) { bonding_block_t *unused_block = find_existing_block(true, BLOCK_UNUSED, EDIV_INVALID); @@ -225,7 +239,7 @@ STATIC void write_keys_block(bleio_connection_internal_t *connection) { ? connection->bonding_keys.peer_enc.master_id.ediv : connection->bonding_keys.own_enc.master_id.ediv; - // Is there an existing keys block that matches? + // Is there an existing keys block that matches the ediv? bonding_block_t *existing_block = find_existing_block(connection->is_central, BLOCK_KEYS, ediv); if (existing_block) { if (existing_block->data_length == sizeof(bonding_keys_t) && @@ -236,6 +250,21 @@ STATIC void write_keys_block(bleio_connection_internal_t *connection) { // Data doesn't match. Invalidate block and store a new one. invalidate_block(existing_block); } + // Invalidate any existing blocks that match the peer address. + existing_block = next_block(NULL); + while (existing_block != NULL) { + if (existing_block->type == BLOCK_KEYS && connection->is_central == existing_block->is_central && + existing_block->data_length == sizeof(bonding_keys_t)) { + const ble_gap_addr_t *existing_peer = &((const bonding_keys_t *)existing_block->data)->peer_id.id_addr_info; + const ble_gap_addr_t *connecting_peer = &connection->bonding_keys.peer_id.id_addr_info; + if (memcmp(existing_peer->addr, connecting_peer->addr, 6) == 0 && + memcmp(existing_block->data, &connection->bonding_keys, sizeof(bonding_keys_t)) != 0) { + // Mismatched block found. Invalidate it. + invalidate_block(existing_block); + } + } + existing_block = next_block(existing_block); + } bonding_block_t block_header = { .is_central = connection->is_central, @@ -308,3 +337,40 @@ bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_k memcpy(bonding_keys, block->data, block->data_length); return true; } + +size_t bonding_load_identities(bool is_central, const ble_gap_id_key_t **keys, size_t max_length) { + bonding_block_t *block = NULL; + size_t len = 0; + while (len < max_length) { + block = next_block(block); + if (block == NULL) { + return len; + } + if (block->type != BLOCK_UNUSED && + block->type != BLOCK_INVALID && + block->is_central == is_central) { + if (sizeof(bonding_keys_t) != block->data_length) { + // bonding_keys_t is a fixed length, so lengths should match. + return len; + } + const bonding_keys_t *key_set = (const bonding_keys_t *)block->data; + keys[len] = &key_set->peer_id; + len++; + } + } + return len; +} + +const ble_gap_enc_key_t *bonding_load_peer_encryption_key(bool is_central, const ble_gap_addr_t *peer) { + bonding_block_t *block = next_block(NULL); + while (block != NULL) { + if (block->type == BLOCK_KEYS && block->is_central == is_central) { + const bonding_keys_t *key_set = (const bonding_keys_t *)block->data; + if (memcmp(key_set->peer_id.id_addr_info.addr, peer->addr, 6) == 0) { + return &key_set->peer_enc; + } + } + block = next_block(block); + } + return NULL; +} diff --git a/ports/nrf/common-hal/_bleio/bonding.h b/ports/nrf/common-hal/_bleio/bonding.h index c0dbe2aec0..228082ec30 100644 --- a/ports/nrf/common-hal/_bleio/bonding.h +++ b/ports/nrf/common-hal/_bleio/bonding.h @@ -81,5 +81,8 @@ void bonding_reset(void); void bonding_clear_keys(bonding_keys_t *bonding_keys); bool bonding_load_cccd_info(bool is_central, uint16_t conn_handle, uint16_t ediv); bool bonding_load_keys(bool is_central, uint16_t ediv, bonding_keys_t *bonding_keys); +const ble_gap_enc_key_t *bonding_load_peer_encryption_key(bool is_central, const ble_gap_addr_t *peer); +size_t bonding_load_identities(bool is_central, const ble_gap_id_key_t **keys, size_t max_length); +size_t bonding_peripheral_bond_count(void); #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_BONDING_H diff --git a/ports/nrf/common-hal/busio/SPI.c b/ports/nrf/common-hal/busio/SPI.c index 309e1bce18..f606fe898f 100644 --- a/ports/nrf/common-hal/busio/SPI.c +++ b/ports/nrf/common-hal/busio/SPI.c @@ -51,7 +51,7 @@ #endif // These are in order from highest available frequency to lowest (32MHz first, then 8MHz). -STATIC spim_peripheral_t spim_peripherals[] = { +STATIC const spim_peripheral_t spim_peripherals[] = { #if NRFX_CHECK(NRFX_SPIM3_ENABLED) // SPIM3 exists only on nRF52840 and supports 32MHz max. All other SPIM's are only 8MHz max. // Allocate SPIM3 first. diff --git a/ports/nrf/common-hal/busio/SPI.h b/ports/nrf/common-hal/busio/SPI.h index 796ff59f93..91c65ee0df 100644 --- a/ports/nrf/common-hal/busio/SPI.h +++ b/ports/nrf/common-hal/busio/SPI.h @@ -38,7 +38,7 @@ typedef struct { typedef struct { mp_obj_base_t base; - spim_peripheral_t *spim_peripheral; + const spim_peripheral_t *spim_peripheral; bool has_lock; uint8_t clock_pin_number; uint8_t MOSI_pin_number; diff --git a/ports/nrf/examples/ubluepy_eddystone.py b/ports/nrf/examples/ubluepy_eddystone.py deleted file mode 100644 index 96818d01c2..0000000000 --- a/ports/nrf/examples/ubluepy_eddystone.py +++ /dev/null @@ -1,68 +0,0 @@ -from ubluepy import Peripheral, constants - -BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE = const(0x02) -BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED = const(0x04) - -BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE = const( - BLE_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED -) - -EDDYSTONE_FRAME_TYPE_URL = const(0x10) -EDDYSTONE_URL_PREFIX_HTTP_WWW = const(0x00) # "http://www". -EDDYSTONE_URL_SUFFIX_DOT_COM = const(0x01) # ".com" - - -def string_to_binarray(text): - b = bytearray([]) - for c in text: - b.append(ord(c)) - return b - - -def gen_ad_type_content(ad_type, data): - b = bytearray(1) - b.append(ad_type) - b.extend(data) - b[0] = len(b) - 1 - return b - - -def generate_eddystone_adv_packet(url): - # flags - disc_mode = bytearray([BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE]) - packet_flags = gen_ad_type_content(constants.ad_types.AD_TYPE_FLAGS, disc_mode) - - # 16-bit uuid - uuid = bytearray([0xAA, 0xFE]) - packet_uuid16 = gen_ad_type_content( - constants.ad_types.AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, uuid - ) - - # eddystone data - rssi = 0xEE # -18 dB, approx signal strength at 0m. - eddystone_data = bytearray([]) - eddystone_data.append(EDDYSTONE_FRAME_TYPE_URL) - eddystone_data.append(rssi) - eddystone_data.append(EDDYSTONE_URL_PREFIX_HTTP_WWW) - eddystone_data.extend(string_to_binarray(url)) - eddystone_data.append(EDDYSTONE_URL_SUFFIX_DOT_COM) - - # service data - service_data = uuid + eddystone_data - packet_service_data = gen_ad_type_content( - constants.ad_types.AD_TYPE_SERVICE_DATA, service_data - ) - - # generate advertisement packet - packet = bytearray([]) - packet.extend(packet_flags) - packet.extend(packet_uuid16) - packet.extend(packet_service_data) - - return packet - - -def start(): - adv_packet = generate_eddystone_adv_packet("micropython") - p = Peripheral() - p.advertise(data=adv_packet, connectable=False) diff --git a/ports/nrf/examples/ubluepy_scan.py b/ports/nrf/examples/ubluepy_scan.py deleted file mode 100644 index 37daa6c69b..0000000000 --- a/ports/nrf/examples/ubluepy_scan.py +++ /dev/null @@ -1,42 +0,0 @@ -from ubluepy import Scanner, constants - - -def bytes_to_str(bytes): - string = "" - for b in bytes: - string += chr(b) - return string - - -def get_device_names(scan_entries): - dev_names = [] - for e in scan_entries: - scan = e.getScanData() - if scan: - for s in scan: - if s[0] == constants.ad_types.AD_TYPE_COMPLETE_LOCAL_NAME: - dev_names.append((e, bytes_to_str(s[2]))) - return dev_names - - -def find_device_by_name(name): - s = Scanner() - scan_res = s.scan(100) - - device_names = get_device_names(scan_res) - for dev in device_names: - if name == dev[1]: - return dev[0] - - -# >>> res = find_device_by_name("micr") -# >>> if res: -# ... print("address:", res.addr()) -# ... print("address type:", res.addr_type()) -# ... print("rssi:", res.rssi()) -# ... -# ... -# ... -# address: c2:73:61:89:24:45 -# address type: 1 -# rssi: -26 diff --git a/ports/nrf/examples/ubluepy_temp.py b/ports/nrf/examples/ubluepy_temp.py deleted file mode 100644 index c70235c4a3..0000000000 --- a/ports/nrf/examples/ubluepy_temp.py +++ /dev/null @@ -1,94 +0,0 @@ -# This file is part of the MicroPython project, http://micropython.org/ -# -# The MIT License (MIT) -# -# SPDX-FileCopyrightText: 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 - -from pyb import LED -from machine import RTC, Temp -from ubluepy import Service, Characteristic, UUID, Peripheral, constants - - -def event_handler(id, handle, data): - global rtc - global periph - global serv_env_sense - global notif_enabled - - if id == constants.EVT_GAP_CONNECTED: - # indicated 'connected' - LED(1).on() - - elif id == constants.EVT_GAP_DISCONNECTED: - # stop low power timer - rtc.stop() - # indicate 'disconnected' - LED(1).off() - # restart advertisement - periph.advertise(device_name="micr_temp", services=[serv_env_sense]) - - elif id == constants.EVT_GATTS_WRITE: - # write to this Characteristic is to CCCD - if int(data[0]) == 1: - notif_enabled = True - # start low power timer - rtc.start() - else: - notif_enabled = False - # stop low power timer - rtc.stop() - - -def send_temp(timer_id): - global notif_enabled - global char_temp - - if notif_enabled: - # measure chip temperature - temp = Temp.read() - temp = temp * 100 - char_temp.write(bytearray([temp & 0xFF, temp >> 8])) - - -# start off with LED(1) off -LED(1).off() - -# use RTC1 as RTC0 is used by bluetooth stack -# set up RTC callback every 5 second -rtc = RTC(1, period=5, mode=RTC.PERIODIC, callback=send_temp) - -notif_enabled = False - -uuid_env_sense = UUID("0x181A") # Environmental Sensing service -uuid_temp = UUID("0x2A6E") # Temperature characteristic - -serv_env_sense = Service(uuid_env_sense) - -temp_props = Characteristic.PROP_NOTIFY | Characteristic.PROP_READ -temp_attrs = Characteristic.ATTR_CCCD -char_temp = Characteristic(uuid_temp, props=temp_props, attrs=temp_attrs) - -serv_env_sense.addCharacteristic(char_temp) - -periph = Peripheral() -periph.addService(serv_env_sense) -periph.setConnectionHandler(event_handler) -periph.advertise(device_name="micr_temp", services=[serv_env_sense]) diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index b355b8f652..87d79cb0e5 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -47,6 +47,14 @@ CIRCUITPY_WATCHDOG ?= 1 # Sleep and Wakeup CIRCUITPY_ALARM ?= 1 +# Turn on the BLE file service +CIRCUITPY_BLE_FILE_SERVICE ?= 1 + +# Turn on the BLE serial service +CIRCUITPY_SERIAL_BLE ?= 1 + +CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 1 + # nRF52840-specific ifeq ($(MCU_CHIP),nrf52840) diff --git a/ports/nrf/supervisor/bluetooth.c b/ports/nrf/supervisor/bluetooth.c deleted file mode 100644 index 5ce737f6ad..0000000000 --- a/ports/nrf/supervisor/bluetooth.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 "supervisor/shared/bluetooth.h" -#include "supervisor/bluetooth.h" - -// This happens in an interrupt so we need to be quick. -bool supervisor_bluetooth_hook(ble_evt_t *ble_evt) { - #if CIRCUITPY_BLE_FILE_SERVICE - // Catch writes to filename or contents. Length is read-only. - - bool done = false; - switch (ble_evt->header.evt_id) { - case BLE_GAP_EVT_CONNECTED: - // We run our background task even if it wasn't us connected to because we may want to - // advertise if the user code stopped advertising. - run_ble_background = true; - break; - case BLE_GAP_EVT_DISCONNECTED: - run_ble_background = true; - break; - case BLE_GATTS_EVT_WRITE: { - // A client wrote to a characteristic. - - ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write; - // Event handle must match the handle for my characteristic. - if (evt_write->handle == supervisor_ble_contents_characteristic.handle) { - // Handle events - // write_to_ringbuf(self, evt_write->data, evt_write->len); - // First packet includes a uint16_t le for length at the start. - uint16_t current_length = ((uint16_t *)current_command)[0]; - memcpy(((uint8_t *)current_command) + current_offset, evt_write->data, evt_write->len); - current_offset += evt_write->len; - current_length = ((uint16_t *)current_command)[0]; - if (current_offset == current_length) { - run_ble_background = true; - done = true; - } - } else if (evt_write->handle == supervisor_ble_filename_characteristic.handle) { - new_filename = true; - run_ble_background = true; - done = true; - } else { - return done; - } - break; - } - - default: - // For debugging. - // mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id); - break; - } - return done; - #else - return false; - #endif -} diff --git a/ports/raspberrypi/audio_dma.c b/ports/raspberrypi/audio_dma.c index 7044c44edf..52b1c84b91 100644 --- a/ports/raspberrypi/audio_dma.c +++ b/ports/raspberrypi/audio_dma.c @@ -129,7 +129,7 @@ void audio_dma_load_next_block(audio_dma_t *dma) { uint8_t *buffer; uint32_t buffer_length; get_buffer_result = audiosample_get_buffer(dma->sample, - dma->single_channel, dma->audio_channel, &buffer, &buffer_length); + dma->single_channel_output, dma->audio_channel, &buffer, &buffer_length); if (get_buffer_result == GET_BUFFER_ERROR) { audio_dma_stop(dma); @@ -148,7 +148,7 @@ void audio_dma_load_next_block(audio_dma_t *dma) { dma_channel_set_read_addr(dma_channel, output_buffer, false /* trigger */); if (get_buffer_result == GET_BUFFER_DONE) { if (dma->loop) { - audiosample_reset_buffer(dma->sample, dma->single_channel, dma->audio_channel); + audiosample_reset_buffer(dma->sample, dma->single_channel_output, dma->audio_channel); } else { // Set channel trigger to ourselves so we don't keep going. dma_channel_hw_t *c = &dma_hw->ch[dma_channel]; @@ -161,26 +161,31 @@ void audio_dma_load_next_block(audio_dma_t *dma) { audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, mp_obj_t sample, bool loop, - bool single_channel, + bool single_channel_output, uint8_t audio_channel, bool output_signed, uint8_t output_resolution, uint32_t output_register_address, uint8_t dma_trigger_source) { - // Use two DMA channels to because the DMA can't wrap to itself without the + // Use two DMA channels to play because the DMA can't wrap to itself without the // buffer being power of two aligned. - dma->channel[0] = dma_claim_unused_channel(false); - dma->channel[1] = dma_claim_unused_channel(false); - if (dma->channel[0] == NUM_DMA_CHANNELS || dma->channel[1] == NUM_DMA_CHANNELS) { - if (dma->channel[0] < NUM_DMA_CHANNELS) { - dma_channel_unclaim(dma->channel[0]); - } + int dma_channel_0_maybe = dma_claim_unused_channel(false); + if (dma_channel_0_maybe < 0) { return AUDIO_DMA_DMA_BUSY; } + int dma_channel_1_maybe = dma_claim_unused_channel(false); + if (dma_channel_1_maybe < 0) { + dma_channel_unclaim((uint)dma_channel_0_maybe); + return AUDIO_DMA_DMA_BUSY; + } + + dma->channel[0] = (uint8_t)dma_channel_0_maybe; + dma->channel[1] = (uint8_t)dma_channel_1_maybe; + dma->sample = sample; dma->loop = loop; - dma->single_channel = single_channel; + dma->single_channel_output = single_channel_output; dma->audio_channel = audio_channel; dma->signed_to_unsigned = false; dma->unsigned_to_signed = false; @@ -189,12 +194,12 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, dma->first_channel_free = true; dma->output_resolution = output_resolution; dma->sample_resolution = audiosample_bits_per_sample(sample); - audiosample_reset_buffer(sample, single_channel, audio_channel); + audiosample_reset_buffer(sample, single_channel_output, audio_channel); bool single_buffer; bool samples_signed; uint32_t max_buffer_length; - audiosample_get_buffer_structure(sample, single_channel, &single_buffer, &samples_signed, + audiosample_get_buffer_structure(sample, single_channel_output, &single_buffer, &samples_signed, &max_buffer_length, &dma->sample_spacing); // Check to see if we have to scale the resolution up. @@ -227,10 +232,9 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, dma->output_size = 1; } // Transfer both channels at once. - if (!single_channel && audiosample_channel_count(sample) == 2) { + if (!single_channel_output && audiosample_channel_count(sample) == 2) { dma->output_size *= 2; } - enum dma_channel_transfer_size dma_size = DMA_SIZE_8; if (dma->output_size == 2) { dma_size = DMA_SIZE_16; @@ -324,20 +328,19 @@ void audio_dma_stop(audio_dma_t *dma) { // to hold the previous value. void audio_dma_pause(audio_dma_t *dma) { dma_hw->ch[dma->channel[0]].al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS; - dma_hw->ch[dma->channel[1]].al1_ctrl &= ~DMA_CH0_CTRL_TRIG_EN_BITS; + dma_hw->ch[dma->channel[1]].al1_ctrl &= ~DMA_CH1_CTRL_TRIG_EN_BITS; } void audio_dma_resume(audio_dma_t *dma) { // Always re-enable the non-busy channel first so it's ready to continue when the busy channel // finishes and chains to it. (An interrupt could make the time between enables long.) - size_t first = 0; - size_t second = 1; if (dma_channel_is_busy(dma->channel[0])) { - first = 1; - second = 0; + dma_hw->ch[dma->channel[1]].al1_ctrl |= DMA_CH1_CTRL_TRIG_EN_BITS; + dma_hw->ch[dma->channel[0]].al1_ctrl |= DMA_CH0_CTRL_TRIG_EN_BITS; + } else { + dma_hw->ch[dma->channel[0]].al1_ctrl |= DMA_CH0_CTRL_TRIG_EN_BITS; + dma_hw->ch[dma->channel[1]].al1_ctrl |= DMA_CH1_CTRL_TRIG_EN_BITS; } - dma_hw->ch[dma->channel[first]].al1_ctrl |= DMA_CH0_CTRL_TRIG_EN_BITS; - dma_hw->ch[dma->channel[second]].al1_ctrl |= DMA_CH0_CTRL_TRIG_EN_BITS; } bool audio_dma_get_paused(audio_dma_t *dma) { @@ -378,6 +381,8 @@ bool audio_dma_get_playing(audio_dma_t *dma) { // WARN(tannewt): DO NOT print from here, or anything it calls. Printing calls // background tasks such as this and causes a stack overflow. +// NOTE(dhalbert): I successfully printed from here while debugging. +// So it's possible, but be careful. STATIC void dma_callback_fun(void *arg) { audio_dma_t *dma = arg; if (dma == NULL) { diff --git a/ports/raspberrypi/audio_dma.h b/ports/raspberrypi/audio_dma.h index faececa8df..f84f6debeb 100644 --- a/ports/raspberrypi/audio_dma.h +++ b/ports/raspberrypi/audio_dma.h @@ -39,7 +39,7 @@ typedef struct { uint8_t output_size; uint8_t sample_spacing; bool loop; - bool single_channel; + bool single_channel_output; bool signed_to_unsigned; bool unsigned_to_signed; bool output_signed; @@ -66,16 +66,16 @@ void audio_dma_reset(void); // This sets everything up but doesn't start the timer. // Sample is the python object for the sample to play. // loop is true if we should loop the sample. -// single_channel is true if we only output a single channel. When false, all channels will be +// single_channel_output is true if we only output a single channel. When false, all channels will be // output. -// audio_channel is the index of the channel to dma. single_channel must be false in this case. +// audio_channel is the index of the channel to dma. single_channel_output must be false in this case. // output_signed is true if the dma'd data should be signed. False and it will be unsigned. // output_register_address is the address to copy data to. // dma_trigger_source is the DMA trigger source which cause another copy audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, mp_obj_t sample, bool loop, - bool single_channel, + bool single_channel_output, uint8_t audio_channel, bool output_signed, uint8_t output_resolution, diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c b/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c index c4021a83e9..eac54ce460 100644 --- a/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_feather_rp2040/board.c @@ -38,3 +38,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/board.c b/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_itsybitsy_rp2040/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c b/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c index a9f1226295..214662e331 100644 --- a/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c @@ -24,13 +24,86 @@ * THE SOFTWARE. */ -#include "supervisor/board.h" - +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/displayio/FourWire.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/busio/SPI.h" #include "shared-bindings/microcontroller/Pin.h" #include "src/rp2_common/hardware_gpio/include/hardware/gpio.h" #include "supervisor/shared/board.h" +displayio_fourwire_obj_t board_display_obj; + +#define DELAY 0x80 + +uint8_t display_init_sequence[] = { + 0xae, 0, // sleep + 0xd5, 1, 0x80, // fOsc divide by 2 + 0xa8, 1, 0x3f, // multiplex 64 + 0xd3, 1, 0x00, // offset 0 + 0x40, 1, 0x00, // start line 0 + 0xad, 1, 0x8b, // dc/dc on + 0xa1, 0, // segment remap = 0 + 0xc8, 0, // scan incr + 0xda, 1, 0x12, // com pins + 0x81, 1, 0xff, // contrast 255 + 0xd9, 1, 0x1f, // pre/dis-charge 2DCLKs/2CLKs + 0xdb, 1, 0x20, // VCOM deslect 0.770 + 0x20, 1, 0x20, + 0x33, 0, // VPP 9V + 0xa6, 0, // not inverted + 0xa4, 0, // normal + 0xaf, 0, // on +}; + void board_init(void) { + busio_spi_obj_t *spi = &displays[0].fourwire_bus.inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO26, &pin_GPIO27, NULL); + common_hal_busio_spi_never_reset(spi); + + displayio_fourwire_obj_t *bus = &displays[0].fourwire_bus; + bus->base.type = &displayio_fourwire_type; + common_hal_displayio_fourwire_construct(bus, + spi, + &pin_GPIO24, // Command or data + &pin_GPIO22, // Chip select + &pin_GPIO23, // Reset + 1000000, // Baudrate + 0, // Polarity + 0); // Phase + + displayio_display_obj_t *display = &displays[0].display; + display->base.type = &displayio_display_type; + common_hal_displayio_display_construct(display, + bus, + 128, // Width + 64, // Height + 2, // column start + 0, // row start + 0, // rotation + 1, // Color depth + true, // grayscale + false, // pixels in byte share row. Only used with depth < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + 0, // Set column command + 0, // Set row command + 0, // Write memory command + 0xd3, // set vertical scroll command + display_init_sequence, + sizeof(display_init_sequence), + NULL, + 0x81, + 1.0f, // brightness + false, // auto_brightness + true, // single_byte_bounds + true, // data as commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + true); // SH1107_addressing } bool board_requests_safe_mode(void) { @@ -41,3 +114,6 @@ void reset_board(void) { // turn off any left over LED board_reset_user_neopixels(&pin_GPIO19, 12); } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/adafruit_macropad_rp2040/pins.c b/ports/raspberrypi/boards/adafruit_macropad_rp2040/pins.c index 2e5890c1c4..f5ff3618c9 100644 --- a/ports/raspberrypi/boards/adafruit_macropad_rp2040/pins.c +++ b/ports/raspberrypi/boards/adafruit_macropad_rp2040/pins.c @@ -1,4 +1,5 @@ #include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_KEY1), MP_ROM_PTR(&pin_GPIO1) }, @@ -15,13 +16,17 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_KEY12), MP_ROM_PTR(&pin_GPIO12) }, { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_SPEAKER_SHUTDOWN), MP_ROM_PTR(&pin_GPIO14) }, { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO16) }, { MP_ROM_QSTR(MP_QSTR_ENCODER_SWITCH), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO0) }, { MP_ROM_QSTR(MP_QSTR_ENCODER_A), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_ROTA), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_ENCODER_B), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_ROTB), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO19) }, @@ -38,6 +43,8 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, - { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)} }; MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table); diff --git a/ports/raspberrypi/boards/adafruit_qt2040_trinkey/board.c b/ports/raspberrypi/boards/adafruit_qt2040_trinkey/board.c index c4021a83e9..eac54ce460 100644 --- a/ports/raspberrypi/boards/adafruit_qt2040_trinkey/board.c +++ b/ports/raspberrypi/boards/adafruit_qt2040_trinkey/board.c @@ -38,3 +38,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/adafruit_qtpy_rp2040/board.c b/ports/raspberrypi/boards/adafruit_qtpy_rp2040/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/adafruit_qtpy_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_qtpy_rp2040/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/arduino_nano_rp2040_connect/board.c b/ports/raspberrypi/boards/arduino_nano_rp2040_connect/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/arduino_nano_rp2040_connect/board.c +++ b/ports/raspberrypi/boards/arduino_nano_rp2040_connect/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/cytron_maker_pi_rp2040/board.c b/ports/raspberrypi/boards/cytron_maker_pi_rp2040/board.c index e68ca62414..b583e7bf11 100644 --- a/ports/raspberrypi/boards/cytron_maker_pi_rp2040/board.c +++ b/ports/raspberrypi/boards/cytron_maker_pi_rp2040/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/pimoroni_keybow2040/board.c b/ports/raspberrypi/boards/pimoroni_keybow2040/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/pimoroni_keybow2040/board.c +++ b/ports/raspberrypi/boards/pimoroni_keybow2040/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/pimoroni_pga2040/board.c b/ports/raspberrypi/boards/pimoroni_pga2040/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/pimoroni_pga2040/board.c +++ b/ports/raspberrypi/boards/pimoroni_pga2040/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/pimoroni_picolipo_16mb/board.c b/ports/raspberrypi/boards/pimoroni_picolipo_16mb/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/pimoroni_picolipo_16mb/board.c +++ b/ports/raspberrypi/boards/pimoroni_picolipo_16mb/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/pimoroni_picolipo_4mb/board.c b/ports/raspberrypi/boards/pimoroni_picolipo_4mb/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/pimoroni_picolipo_4mb/board.c +++ b/ports/raspberrypi/boards/pimoroni_picolipo_4mb/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/pimoroni_picosystem/board.c b/ports/raspberrypi/boards/pimoroni_picosystem/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/pimoroni_picosystem/board.c +++ b/ports/raspberrypi/boards/pimoroni_picosystem/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/pimoroni_tiny2040/board.c b/ports/raspberrypi/boards/pimoroni_tiny2040/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/pimoroni_tiny2040/board.c +++ b/ports/raspberrypi/boards/pimoroni_tiny2040/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/raspberry_pi_pico/board.c b/ports/raspberrypi/boards/raspberry_pi_pico/board.c index 67486d4c23..de6e424ed9 100644 --- a/ports/raspberrypi/boards/raspberry_pi_pico/board.c +++ b/ports/raspberrypi/boards/raspberry_pi_pico/board.c @@ -35,3 +35,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/sparkfun_micromod_rp2040/board.c b/ports/raspberrypi/boards/sparkfun_micromod_rp2040/board.c index c4021a83e9..eac54ce460 100644 --- a/ports/raspberrypi/boards/sparkfun_micromod_rp2040/board.c +++ b/ports/raspberrypi/boards/sparkfun_micromod_rp2040/board.c @@ -38,3 +38,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/sparkfun_pro_micro_rp2040/board.c b/ports/raspberrypi/boards/sparkfun_pro_micro_rp2040/board.c index c4021a83e9..eac54ce460 100644 --- a/ports/raspberrypi/boards/sparkfun_pro_micro_rp2040/board.c +++ b/ports/raspberrypi/boards/sparkfun_pro_micro_rp2040/board.c @@ -38,3 +38,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/boards/sparkfun_thing_plus_rp2040/board.c b/ports/raspberrypi/boards/sparkfun_thing_plus_rp2040/board.c index c4021a83e9..eac54ce460 100644 --- a/ports/raspberrypi/boards/sparkfun_thing_plus_rp2040/board.c +++ b/ports/raspberrypi/boards/sparkfun_thing_plus_rp2040/board.c @@ -38,3 +38,6 @@ bool board_requests_safe_mode(void) { void reset_board(void) { } + +void board_deinit(void) { +} diff --git a/ports/raspberrypi/common-hal/alarm/SleepMemory.c b/ports/raspberrypi/common-hal/alarm/SleepMemory.c new file mode 100644 index 0000000000..b8fcdfbccf --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/SleepMemory.c @@ -0,0 +1,47 @@ +/* + * 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 + +#include "py/runtime.h" +#include "common-hal/alarm/SleepMemory.h" + +void alarm_sleep_memory_reset(void) { +} + +uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) { + mp_raise_NotImplementedError(translate("Sleep Memory not available")); + return 0; +} + +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) { + mp_raise_NotImplementedError(translate("Sleep Memory not available")); + return false; +} + +void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, uint8_t *values, uint32_t len) { + mp_raise_NotImplementedError(translate("Sleep Memory not available")); +} diff --git a/ports/raspberrypi/common-hal/alarm/SleepMemory.h b/ports/raspberrypi/common-hal/alarm/SleepMemory.h new file mode 100644 index 0000000000..59d0429c3b --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/SleepMemory.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_SLEEPMEMORY_H +#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_SLEEPMEMORY_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} alarm_sleep_memory_obj_t; + +extern void alarm_sleep_memory_reset(void); + +#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_SLEEPMEMORY_H diff --git a/ports/raspberrypi/common-hal/alarm/__init__.c b/ports/raspberrypi/common-hal/alarm/__init__.c new file mode 100644 index 0000000000..7f22242e9f --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/__init__.c @@ -0,0 +1,258 @@ +/* + * 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 "py/gc.h" +#include "py/obj.h" +#include "py/objtuple.h" +#include "py/runtime.h" +#include "lib/utils/interrupt_char.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/microcontroller/__init__.h" + +#include "supervisor/port.h" +#include "supervisor/shared/workflow.h" + +#include "pico/stdlib.h" +#include "hardware/sync.h" +#include "hardware/clocks.h" +#include "hardware/xosc.h" +#include "hardware/structs/scb.h" +#include "hardware/watchdog.h" +#include "hardware/structs/watchdog.h" + +// XOSC shutdown +#include "hardware/rtc.h" +#include "hardware/pll.h" +#include "hardware/regs/io_bank0.h" + +// Watchdog scratch register +// Not used elsewhere in the SDK for now, keep an eye on it +#define RP_WKUP_SCRATCH_REG 0 + +// Light sleep turns off nonvolatile Busio and other wake-only peripherals +// TODO: this only saves about 2mA right now, expand with other non-essentials +const uint32_t RP_LIGHTSLEEP_EN0_MASK = ~( + CLOCKS_SLEEP_EN0_CLK_SYS_SPI1_BITS | + CLOCKS_SLEEP_EN0_CLK_PERI_SPI1_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_SPI0_BITS | + CLOCKS_SLEEP_EN0_CLK_PERI_SPI0_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_PWM_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_PIO1_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_PIO0_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_I2C1_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_I2C0_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_ADC_BITS | + CLOCKS_SLEEP_EN0_CLK_ADC_ADC_BITS + ); +// This bank has the USB clocks in it, leave it for now +const uint32_t RP_LIGHTSLEEP_EN1_MASK = CLOCKS_SLEEP_EN1_RESET; + +// Light sleeps used for TimeAlarm deep sleep turn off almost everything +const uint32_t RP_LIGHTSLEEP_EN0_MASK_HARSH = ( + CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS | + CLOCKS_SLEEP_EN0_CLK_SYS_PADS_BITS + ); +const uint32_t RP_LIGHTSLEEP_EN1_MASK_HARSH = 0x0; + +STATIC void prepare_for_dormant_xosc(void); + +// Singleton instance of SleepMemory. +const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = { + .base = { + .type = &alarm_sleep_memory_type, + }, +}; + +void alarm_reset(void) { + alarm_sleep_memory_reset(); + alarm_pin_pinalarm_reset(); + alarm_time_timealarm_reset(); + + // Reset the scratch source + watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] = RP_SLEEP_WAKEUP_UNDEF; +} + +STATIC uint8_t _get_wakeup_cause(void) { + // First check if the modules remember what last woke up + if (alarm_pin_pinalarm_woke_this_cycle()) { + return RP_SLEEP_WAKEUP_GPIO; + } + if (alarm_time_timealarm_woke_this_cycle()) { + return RP_SLEEP_WAKEUP_RTC; + } + // If waking from true deep sleep, modules will have lost their state, + // so check the deep wakeup cause manually + if (watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] != RP_SLEEP_WAKEUP_UNDEF) { + return watchdog_hw->scratch[RP_WKUP_SCRATCH_REG]; + } + return RP_SLEEP_WAKEUP_UNDEF; +} + +// Set up light sleep or deep sleep alarms. +STATIC void _setup_sleep_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + alarm_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms); + alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms); +} + +bool common_hal_alarm_woken_from_sleep(void) { + return _get_wakeup_cause() != RP_SLEEP_WAKEUP_UNDEF; +} + +mp_obj_t common_hal_alarm_create_wake_alarm(void) { + // If woken from deep sleep, create a copy alarm similar to what would have + // been passed in originally. Otherwise, just return none + uint8_t cause = _get_wakeup_cause(); + switch (cause) { + case RP_SLEEP_WAKEUP_RTC: { + return alarm_time_timealarm_create_wakeup_alarm(); + } + + case RP_SLEEP_WAKEUP_GPIO: { + return alarm_pin_pinalarm_create_wakeup_alarm(); + } + + case RP_SLEEP_WAKEUP_UNDEF: + default: + // Not a deep sleep reset. + break; + } + return mp_const_none; +} + +mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms) { + _setup_sleep_alarms(false, n_alarms, alarms); + + mp_obj_t wake_alarm = mp_const_none; + + while (!mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + // Detect if interrupt was alarm or ctrl-C interrupt. + if (common_hal_alarm_woken_from_sleep()) { + uint8_t cause = _get_wakeup_cause(); + switch (cause) { + case RP_SLEEP_WAKEUP_RTC: { + wake_alarm = alarm_time_timealarm_find_triggered_alarm(n_alarms,alarms); + break; + } + case RP_SLEEP_WAKEUP_GPIO: { + wake_alarm = alarm_pin_pinalarm_find_triggered_alarm(n_alarms,alarms); + break; + } + default: + // Should not reach this, if all light sleep types are covered correctly + break; + } + shared_alarm_save_wake_alarm(wake_alarm); + break; + } + + // Prune the clock for sleep + clocks_hw->sleep_en0 &= RP_LIGHTSLEEP_EN0_MASK; + clocks_hw->sleep_en1 = RP_LIGHTSLEEP_EN1_MASK; + + // Enable System Control Block (SCB) deep sleep + uint save = scb_hw->scr; + scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS; + + __wfi(); + } + + if (mp_hal_is_interrupted()) { + return mp_const_none; // Shouldn't be given to python code because exception handling should kick in. + } + + alarm_reset(); + return wake_alarm; +} + +void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { + _setup_sleep_alarms(true, n_alarms, alarms); +} + +void NORETURN common_hal_alarm_enter_deep_sleep(void) { + bool timealarm_set = alarm_time_timealarm_is_set(); + + // If there's a timealarm, just enter a very deep light sleep + if (timealarm_set) { + // Prune the clock for sleep + clocks_hw->sleep_en0 &= RP_LIGHTSLEEP_EN0_MASK_HARSH; + clocks_hw->sleep_en1 = RP_LIGHTSLEEP_EN1_MASK_HARSH; + // Enable System Control Block (SCB) deep sleep + uint save = scb_hw->scr; + scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS; + __wfi(); + } else { + prepare_for_dormant_xosc(); + xosc_dormant(); + } + // // TODO: support ROSC when available in SDK + // rosc_set_dormant(); + + // Reset uses the watchdog. Use scratch registers to store wake reason + watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] = _get_wakeup_cause(); + reset_cpu(); +} + +void common_hal_alarm_gc_collect(void) { + gc_collect_ptr(shared_alarm_get_wake_alarm()); +} + +STATIC void prepare_for_dormant_xosc(void) { + // TODO: add ROSC support with sleep_run_from_dormant_source when it's added to SDK + uint src_hz = XOSC_MHZ * MHZ; + uint clk_ref_src = CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC; + clock_configure(clk_ref, + clk_ref_src, + 0, // No aux mux + src_hz, + src_hz); + clock_configure(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, + 0, // Using glitchless mux + src_hz, + src_hz); + clock_stop(clk_usb); + clock_stop(clk_adc); + uint clk_rtc_src = CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC; + clock_configure(clk_rtc, + 0, // No GLMUX + clk_rtc_src, + src_hz, + 46875); + clock_configure(clk_peri, + 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, + src_hz, + src_hz); + pll_deinit(pll_sys); + pll_deinit(pll_usb); +} diff --git a/ports/raspberrypi/common-hal/alarm/__init__.h b/ports/raspberrypi/common-hal/alarm/__init__.h new file mode 100644 index 0000000000..3d5d86f8dc --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/__init__.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM__INIT__H +#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM__INIT__H + +#include "common-hal/alarm/SleepMemory.h" + +#include "hardware/regs/clocks.h" + +#define RP_SLEEP_WAKEUP_UNDEF 0 +#define RP_SLEEP_WAKEUP_GPIO 1 +#define RP_SLEEP_WAKEUP_RTC 2 + +extern const alarm_sleep_memory_obj_t alarm_sleep_memory_obj; + +extern void alarm_reset(void); + +#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM__INIT__H diff --git a/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c b/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c new file mode 100644 index 0000000000..276e2e409a --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c @@ -0,0 +1,164 @@ +/* + * 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 "py/runtime.h" + +#include "shared-bindings/alarm/pin/PinAlarm.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "common-hal/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "pico/stdlib.h" +#include "hardware/gpio.h" +#include "hardware/structs/iobank0.h" + +STATIC bool woke_up; +STATIC uint64_t alarm_triggered_pins; // 36 actual pins +STATIC uint64_t alarm_reserved_pins; // 36 actual pins +STATIC bool _pinalarm_set = false; + +#define GPIO_IRQ_ALL_EVENTS 0x15u + +void gpio_callback(uint gpio, uint32_t events) { + alarm_triggered_pins |= (1 << gpio); + woke_up = true; + + // does this need to be called, to prevent IRQ from constantly going off? + gpio_acknowledge_irq(gpio, events); + + // Disable IRQ automatically + gpio_set_irq_enabled(gpio, events, false); + gpio_set_dormant_irq_enabled(gpio, events, false); +} + +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) { + self->pin = pin; + self->value = value; + self->edge = edge; + self->pull = pull; +} + +const mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) { + return self->pin; +} + +bool common_hal_alarm_pin_pinalarm_get_value(alarm_pin_pinalarm_obj_t *self) { + return self->value; +} + +bool common_hal_alarm_pin_pinalarm_get_edge(alarm_pin_pinalarm_obj_t *self) { + return self->edge; +} + +bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) { + return self->pull; +} + +bool alarm_pin_pinalarm_woke_this_cycle(void) { + return woke_up; +} + +mp_obj_t alarm_pin_pinalarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms) { + for (size_t i = 0; i < n_alarms; i++) { + if (!mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { + continue; + } + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + if (alarm_triggered_pins & (1 << alarm->pin->number)) { + return alarms[i]; + } + } + return mp_const_none; +} + +mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(void) { + alarm_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t); + alarm->base.type = &alarm_pin_pinalarm_type; + // TODO: how to obtain the correct pin from memory? + return alarm; +} + +void alarm_pin_pinalarm_reset(void) { + alarm_triggered_pins = 0; + woke_up = false; + + // Clear all GPIO interrupts + for (uint8_t i = 0; i < 4; i++) { + iobank0_hw->intr[i] = 0; + } + + // Reset pins and pin IRQs + for (size_t i = 0; i < TOTAL_GPIO_COUNT; i++) { + if (alarm_reserved_pins & (1 << i)) { + gpio_set_irq_enabled(i, GPIO_IRQ_ALL_EVENTS, false); + reset_pin_number(i); + } + } + alarm_reserved_pins = 0; +} + +void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + for (size_t i = 0; i < n_alarms; i++) { + if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + + gpio_init(alarm->pin->number); + if (alarm->pull) { + // If value is high, the pullup should be off, and vice versa + gpio_set_pulls(alarm->pin->number, !alarm->value, alarm->value); + } else { + // Clear in case the pulls are already on + gpio_set_pulls(alarm->pin->number, false, false); + } + gpio_set_dir(alarm->pin->number, GPIO_IN); + // Don't reset at end of VM (instead, pinalarm_reset will reset before next VM) + common_hal_never_reset_pin(alarm->pin); + alarm_reserved_pins |= (1 << alarm->pin->number); + + uint32_t event; + if (alarm->value == true && alarm->edge == true) { + event = GPIO_IRQ_EDGE_RISE; + } else if (alarm->value == false && alarm->edge == true) { + event = GPIO_IRQ_EDGE_FALL; + } else if (alarm->value == true && alarm->edge == false) { + event = GPIO_IRQ_LEVEL_HIGH; + } else { // both false + event = GPIO_IRQ_LEVEL_LOW; + } + + gpio_set_irq_enabled_with_callback((uint)alarm->pin->number, event, true, &gpio_callback); + if (deep_sleep) { + gpio_set_dormant_irq_enabled((uint)alarm->pin->number, event, true); + } + + _pinalarm_set = true; + } + } +} + +bool alarm_pin_pinalarm_is_set(void) { + return _pinalarm_set; +} diff --git a/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h b/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h new file mode 100644 index 0000000000..d31ac6c878 --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h @@ -0,0 +1,45 @@ +/* + * 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 "py/obj.h" +#include "py/objtuple.h" + +typedef struct { + mp_obj_base_t base; + const mcu_pin_obj_t *pin; + bool value; + bool pull; + bool edge; +} alarm_pin_pinalarm_obj_t; + +mp_obj_t alarm_pin_pinalarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms); +mp_obj_t alarm_pin_pinalarm_create_wakeup_alarm(void); + +void alarm_pin_pinalarm_reset(void); +void alarm_pin_pinalarm_light_reset(void); +void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms); +bool alarm_pin_pinalarm_woke_this_cycle(void); +bool alarm_pin_pinalarm_is_set(void); diff --git a/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c b/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c new file mode 100644 index 0000000000..4302e9e8cd --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c @@ -0,0 +1,130 @@ +/* + * 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 "py/runtime.h" + +#include "shared-bindings/alarm/time/TimeAlarm.h" +#include "shared-bindings/time/__init__.h" + +#include "lib/timeutils/timeutils.h" + +#include "hardware/gpio.h" +#include "hardware/rtc.h" + +STATIC bool woke_up = false; +STATIC bool _timealarm_set = false; + +void timer_callback(void) { + woke_up = true; +} + +void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_time) { + self->monotonic_time = monotonic_time; +} + +mp_float_t common_hal_alarm_time_timealarm_get_monotonic_time(alarm_time_timealarm_obj_t *self) { + return self->monotonic_time; +} + +mp_obj_t alarm_time_timealarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms) { + for (size_t i = 0; i < n_alarms; i++) { + if (mp_obj_is_type(alarms[i], &alarm_time_timealarm_type)) { + return alarms[i]; + } + } + return mp_const_none; +} + +mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void) { + alarm_time_timealarm_obj_t *timer = m_new_obj(alarm_time_timealarm_obj_t); + timer->base.type = &alarm_time_timealarm_type; + // TODO: Set monotonic_time based on the RTC state. + timer->monotonic_time = 0.0f; + return timer; +} + +bool alarm_time_timealarm_woke_this_cycle(void) { + return woke_up; +} + +void alarm_time_timealarm_reset(void) { + rtc_disable_alarm(); + woke_up = false; +} + +void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + bool timealarm_set = false; + alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL; + + for (size_t i = 0; i < n_alarms; i++) { + if (!mp_obj_is_type(alarms[i], &alarm_time_timealarm_type)) { + continue; + } + if (timealarm_set) { + mp_raise_ValueError(translate("Only one alarm.time alarm can be set.")); + } + timealarm = MP_OBJ_TO_PTR(alarms[i]); + timealarm_set = true; + } + if (!timealarm_set) { + return; + } + if (deep_sleep) { + _timealarm_set = true; + } + + // Compute how long to actually sleep, considering the time now. + mp_float_t mono_seconds_to_date = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f; + mp_float_t wakeup_in_secs = MAX(0.0f, timealarm->monotonic_time - mono_seconds_to_date); + datetime_t t; + + rtc_get_datetime(&t); + + uint32_t rtc_seconds_to_date = timeutils_seconds_since_2000(t.year, t.month, + t.day, t.hour, t.min, t.sec); + + // The float value is always slightly under, so add 1 to compensate + uint32_t alarm_seconds = rtc_seconds_to_date + (uint32_t)wakeup_in_secs + 1; + timeutils_struct_time_t tm; + timeutils_seconds_since_2000_to_struct_time(alarm_seconds, &tm); + + // reuse t + t.hour = tm.tm_hour; + t.min = tm.tm_min; + t.sec = tm.tm_sec; + t.day = tm.tm_mday; + t.month = tm.tm_mon; + t.year = tm.tm_year; + t.dotw = (tm.tm_wday + 1) % 7; + + rtc_set_alarm(&t, &timer_callback); + + woke_up = false; +} + +bool alarm_time_timealarm_is_set(void) { + return _timealarm_set; +} diff --git a/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.h b/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.h new file mode 100644 index 0000000000..d5248551ae --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/time/TimeAlarm.h @@ -0,0 +1,41 @@ +/* + * 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 "py/obj.h" + +typedef struct { + mp_obj_base_t base; + mp_float_t monotonic_time; +} alarm_time_timealarm_obj_t; + +mp_obj_t alarm_time_timealarm_find_triggered_alarm(size_t n_alarms, const mp_obj_t *alarms); +mp_obj_t alarm_time_timealarm_create_wakeup_alarm(void); + +void alarm_time_timealarm_reset(void); +void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms); +bool alarm_time_timealarm_woke_this_cycle(void); +bool alarm_time_timealarm_is_set(void); diff --git a/ports/nrf/supervisor/bluetooth.h b/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c similarity index 76% rename from ports/nrf/supervisor/bluetooth.h rename to ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c index 425de07e4d..88c73726ee 100644 --- a/ports/nrf/supervisor/bluetooth.h +++ b/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 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 @@ -24,13 +24,9 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_NRF_SUPERVISOR_BLUETOOTH_H -#define MICROPY_INCLUDED_NRF_SUPERVISOR_BLUETOOTH_H +#include "shared-bindings/alarm/touch/TouchAlarm.h" +#include "shared-bindings/microcontroller/__init__.h" -#include - -#include "ble.h" - -bool supervisor_bluetooth_hook(ble_evt_t *ble_evt); - -#endif // MICROPY_INCLUDED_NRF_SUPERVISOR_BLUETOOTH_H +void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { + mp_raise_NotImplementedError(translate("Touch alarms not available")); +} diff --git a/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.h b/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.h new file mode 100644 index 0000000000..f631293511 --- /dev/null +++ b/ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H +#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H + +#include "py/obj.h" +#include "common-hal/microcontroller/Pin.h" + +typedef struct { + mp_obj_base_t base; + const mcu_pin_obj_t *pin; +} alarm_touch_touchalarm_obj_t; + +#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c index 0ad44c2527..0958de6883 100644 --- a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c +++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c @@ -145,7 +145,13 @@ void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) { return; } + if (common_hal_audiobusio_i2sout_get_playing(self)) { + common_hal_audiobusio_i2sout_stop(self); + } + common_hal_rp2pio_statemachine_deinit(&self->state_machine); + + audio_dma_deinit(&self->dma); } void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, @@ -153,6 +159,7 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, if (common_hal_audiobusio_i2sout_get_playing(self)) { common_hal_audiobusio_i2sout_stop(self); } + uint8_t bits_per_sample = audiosample_bits_per_sample(sample); // Make sure we transmit a minimum of 16 bits. // TODO: Maybe we need an intermediate object to upsample instead. This is diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.h b/ports/raspberrypi/common-hal/audiobusio/I2SOut.h index 851e86c8a9..52226ae49d 100644 --- a/ports/raspberrypi/common-hal/audiobusio/I2SOut.h +++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.h @@ -1,3 +1,4 @@ + /* * This file is part of the MicroPython project, http://micropython.org/ * @@ -36,10 +37,10 @@ // We don't bit pack because we'll only have two at most. Its better to save code size instead. typedef struct { mp_obj_base_t base; - bool left_justified; rp2pio_statemachine_obj_t state_machine; - bool playing; audio_dma_t dma; + bool left_justified; + bool playing; } audiobusio_i2sout_obj_t; void i2sout_reset(void); diff --git a/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c b/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c index e191606577..3a8a7ef226 100644 --- a/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +++ b/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c @@ -46,6 +46,12 @@ #define NUM_DMA_TIMERS 4 +// The PWM clock frequency is base_clock_rate / PWM_TOP, typically 125_000_000 / PWM_TOP. +// We pick BITS_PER_SAMPLE so we get a clock frequency that is above what would cause aliasing. +#define BITS_PER_SAMPLE 10 +#define SAMPLE_BITS_TO_DISCARD (16 - BITS_PER_SAMPLE) +#define PWM_TOP ((1 << BITS_PER_SAMPLE) - 1) + void audiopwmout_reset() { for (size_t i = 0; i < NUM_DMA_TIMERS; i++) { dma_hw->timer[i] = 0; @@ -55,7 +61,10 @@ void audiopwmout_reset() { // Caller validates that pins are free. 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 quiescent_value) { - if (left_channel != NULL && right_channel != NULL) { + + self->stereo = right_channel != NULL; + + if (self->stereo) { if (pwm_gpio_to_slice_num(left_channel->number) != pwm_gpio_to_slice_num(right_channel->number)) { mp_raise_ValueError(translate("Pins must share PWM slice")); } @@ -72,22 +81,20 @@ void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *s // we want. ;-) We mark ourselves variable only if we're a mono output to // prevent other PWM use on the other channel. If stereo, we say fixed // frequency so we can allocate with ourselves. - bool mono = right_channel == NULL; - // We don't actually know our frequency yet so just pick one that shouldn't - // match anyone else. (We'll only know the frequency once we play something - // back.) - uint32_t frequency = 12500; + // We don't actually know our frequency yet. It is set when + // pwmio_pwmout_set_top() is called. This value is unimportant; it just needs to be valid. + const uint32_t frequency = 12000000; // Make sure the PWMOut's are "deinited" by default. self->left_pwm.pin = NULL; self->right_pwm.pin = NULL; - pwmout_result_t result = common_hal_pwmio_pwmout_construct(&self->left_pwm, - left_channel, 0, frequency, mono); + pwmout_result_t result = + common_hal_pwmio_pwmout_construct(&self->left_pwm, left_channel, 0, frequency, !self->stereo); if (result == PWMOUT_OK && right_channel != NULL) { - result = common_hal_pwmio_pwmout_construct(&self->right_pwm, - right_channel, 0, frequency, false); + result = + common_hal_pwmio_pwmout_construct(&self->right_pwm, right_channel, 0, frequency, false); if (result != PWMOUT_OK) { common_hal_pwmio_pwmout_deinit(&self->left_pwm); } @@ -96,15 +103,16 @@ void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *s mp_raise_RuntimeError(translate("All timers in use")); } - claim_pin(left_channel); - if (right_channel != NULL) { - claim_pin(right_channel); + self->quiescent_value = quiescent_value >> SAMPLE_BITS_TO_DISCARD; + common_hal_pwmio_pwmout_set_duty_cycle(&self->left_pwm, self->quiescent_value); + pwmio_pwmout_set_top(&self->left_pwm, PWM_TOP); + if (self->stereo) { + common_hal_pwmio_pwmout_set_duty_cycle(&self->right_pwm, self->quiescent_value); + pwmio_pwmout_set_top(&self->right_pwm, PWM_TOP); } audio_dma_init(&self->dma); self->pacing_timer = NUM_DMA_TIMERS; - - self->quiescent_value = quiescent_value; } bool common_hal_audiopwmio_pwmaudioout_deinited(audiopwmio_pwmaudioout_obj_t *self) { @@ -115,6 +123,7 @@ void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t *self if (common_hal_audiopwmio_pwmaudioout_deinited(self)) { return; } + if (common_hal_audiopwmio_pwmaudioout_get_playing(self)) { common_hal_audiopwmio_pwmaudioout_stop(self); } @@ -144,13 +153,11 @@ void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self, mp_raise_RuntimeError(translate("No DMA pacing timer found")); } uint32_t tx_register = (uint32_t)&pwm_hw->slice[self->left_pwm.slice].cc; - if (common_hal_pwmio_pwmout_deinited(&self->right_pwm)) { - // Shift the destination if we are outputting to the second PWM channel. + if (self->stereo) { + // Shift the destination if we are outputting to both PWM channels. tx_register += self->left_pwm.channel * sizeof(uint16_t); } - pwmio_pwmout_set_top(&self->left_pwm, 1023); - audio_dma_result result = audio_dma_setup_playback( &self->dma, sample, @@ -158,15 +165,16 @@ void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self, false, // single channel 0, // audio channel false, // output signed - 10, - (uint32_t)tx_register, // output register + BITS_PER_SAMPLE, + (uint32_t)tx_register, // output register: PWM cc register 0x3b + pacing_timer); // data request line if (result == AUDIO_DMA_DMA_BUSY) { - // common_hal_audiobusio_i2sout_stop(self); + common_hal_audiopwmio_pwmaudioout_stop(self); mp_raise_RuntimeError(translate("No DMA channel found")); - } else if (result == AUDIO_DMA_MEMORY_ERROR) { - // common_hal_audiobusio_i2sout_stop(self); + } + if (result == AUDIO_DMA_MEMORY_ERROR) { + common_hal_audiopwmio_pwmaudioout_stop(self); mp_raise_RuntimeError(translate("Unable to allocate buffers for signed conversion")); } @@ -182,6 +190,7 @@ void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self, // are 16-bit. uint32_t sample_rate = audiosample_sample_rate(sample); + uint32_t system_clock = common_hal_mcu_processor_get_frequency(); uint32_t best_numerator = 0; uint32_t best_denominator = 0; @@ -209,18 +218,27 @@ void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self, } void common_hal_audiopwmio_pwmaudioout_stop(audiopwmio_pwmaudioout_obj_t *self) { - dma_hw->timer[self->pacing_timer] = 0; - self->pacing_timer = NUM_DMA_TIMERS; + if (self->pacing_timer < NUM_DMA_TIMERS) { + dma_hw->timer[self->pacing_timer] = 0; + self->pacing_timer = NUM_DMA_TIMERS; + } audio_dma_stop(&self->dma); + + // Set to quiescent level. + pwm_hw->slice[self->left_pwm.slice].cc = self->quiescent_value; + if (self->stereo) { + pwm_hw->slice[self->right_pwm.slice].cc = self->quiescent_value; + } } bool common_hal_audiopwmio_pwmaudioout_get_playing(audiopwmio_pwmaudioout_obj_t *self) { bool playing = audio_dma_get_playing(&self->dma); + if (!playing && self->pacing_timer < NUM_DMA_TIMERS) { - dma_hw->timer[self->pacing_timer] = 0; - self->pacing_timer = NUM_DMA_TIMERS; + common_hal_audiopwmio_pwmaudioout_stop(self); } + return playing; } diff --git a/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h b/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h index 2b43b8354d..e9f0f68d06 100644 --- a/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h +++ b/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.h @@ -38,6 +38,7 @@ typedef struct { audio_dma_t dma; uint16_t quiescent_value; uint8_t pacing_timer; + bool stereo; // if false, only using left_pwm. } audiopwmio_pwmaudioout_obj_t; void audiopwmout_reset(void); diff --git a/ports/raspberrypi/common-hal/busio/SPI.c b/ports/raspberrypi/common-hal/busio/SPI.c index 23030cbd0d..ab295c4ff1 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.c +++ b/ports/raspberrypi/common-hal/busio/SPI.c @@ -212,15 +212,6 @@ static bool _transfer(busio_spi_obj_t *self, while (dma_channel_is_busy(chan_rx) || dma_channel_is_busy(chan_tx)) { // TODO: We should idle here until we get a DMA interrupt or something else. RUN_BACKGROUND_TASKS; - if (mp_hal_is_interrupted()) { - if (dma_channel_is_busy(chan_rx)) { - dma_channel_abort(chan_rx); - } - if (dma_channel_is_busy(chan_tx)) { - dma_channel_abort(chan_tx); - } - break; - } } } @@ -233,7 +224,7 @@ static bool _transfer(busio_spi_obj_t *self, dma_channel_unclaim(chan_tx); } - if (!use_dma && !mp_hal_is_interrupted()) { + if (!use_dma) { // Use software for small transfers, or if couldn't claim two DMA channels // Never have more transfers in flight than will fit into the RX FIFO, // else FIFO will overflow if this code is heavily interrupted. @@ -241,7 +232,7 @@ static bool _transfer(busio_spi_obj_t *self, size_t rx_remaining = len; size_t tx_remaining = len; - while (!mp_hal_is_interrupted() && (rx_remaining || tx_remaining)) { + while (rx_remaining || tx_remaining) { if (tx_remaining && spi_is_writable(self->peripheral) && rx_remaining - tx_remaining < fifo_depth) { spi_get_hw(self->peripheral)->dr = (uint32_t)*data_out; // Increment only if the buffer is the transfer length. It's 1 otherwise. diff --git a/ports/raspberrypi/common-hal/pulseio/PulseOut.c b/ports/raspberrypi/common-hal/pulseio/PulseOut.c index 7a1b709b4c..bc0bc307e6 100644 --- a/ports/raspberrypi/common-hal/pulseio/PulseOut.c +++ b/ports/raspberrypi/common-hal/pulseio/PulseOut.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "shared-bindings/pulseio/PulseOut.h" #include "shared-bindings/pwmio/PWMOut.h" +#include "shared-bindings/microcontroller/__init__.h" #include "common-hal/pwmio/PWMOut.h" #include "supervisor/shared/translate.h" #include "src/rp2_common/hardware_pwm/include/hardware/pwm.h" @@ -105,5 +106,7 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t *self, uint16_t *pu // signal. RUN_BACKGROUND_TASKS; } + // Short delay to give pin time to settle before disabling PWM + common_hal_mcu_delay_us(25); pwm_set_enabled(pwmout_obj->slice,false); } diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c index 9695a8fdf7..5215f2b8df 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c @@ -163,6 +163,8 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self, self->variable_frequency = variable_frequency; self->duty_cycle = duty; + claim_pin(pin); + if (frequency == 0 || frequency > (common_hal_mcu_processor_get_frequency() / 2)) { return PWMOUT_INVALID_FREQUENCY; } diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index ab70a27fcd..887ea8e644 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -654,15 +654,6 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, (tx && dma_channel_is_busy(chan_tx))) { // TODO: We should idle here until we get a DMA interrupt or something else. RUN_BACKGROUND_TASKS; - if (mp_hal_is_interrupted()) { - if (rx && dma_channel_is_busy(chan_rx)) { - dma_channel_abort(chan_rx); - } - if (tx && dma_channel_is_busy(chan_tx)) { - dma_channel_abort(chan_tx); - } - break; - } } // Clear the stall bit so we can detect when the state machine is done transmitting. self->pio->fdebug = stall_mask; @@ -677,7 +668,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, dma_channel_unclaim(chan_tx); } - if (!use_dma && !mp_hal_is_interrupted()) { + if (!use_dma) { // Use software for small transfers, or if couldn't claim two DMA channels size_t rx_remaining = in_len / in_stride_in_bytes; size_t tx_remaining = out_len / out_stride_in_bytes; @@ -706,9 +697,6 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, --rx_remaining; } RUN_BACKGROUND_TASKS; - if (mp_hal_is_interrupted()) { - break; - } } // Clear the stall bit so we can detect when the state machine is done transmitting. self->pio->fdebug = stall_mask; @@ -719,9 +707,6 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) || (self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) { RUN_BACKGROUND_TASKS; - if (mp_hal_is_interrupted()) { - break; - } } } return true; diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 8bc70ed638..bc8a26de7b 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -17,15 +17,17 @@ endif # All raspberrypi ports have longints. LONGINT_IMPL = MPZ +CIRCUITPY_ALARM ?= 1 + CIRCUITPY_RP2PIO ?= 1 CIRCUITPY_NEOPIXEL_WRITE ?= $(CIRCUITPY_RP2PIO) -CIRCUITPY_FRAMEBUFFERIO ?= 1 +CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_DISPLAYIO) CIRCUITPY_FULL_BUILD ?= 1 CIRCUITPY_AUDIOMP3 ?= 1 CIRCUITPY_BITOPS ?= 1 CIRCUITPY_IMAGECAPTURE ?= 1 CIRCUITPY_PWMIO ?= 1 -CIRCUITPY_RGBMATRIX ?= 1 +CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_DISPLAYIO) CIRCUITPY_ROTARYIO ?= 1 CIRCUITPY_ROTARYIO_SOFTENCODER = 1 diff --git a/ports/stm/boards/espruino_pico/mpconfigboard.mk b/ports/stm/boards/espruino_pico/mpconfigboard.mk index bc415f07ec..3f3e81d553 100644 --- a/ports/stm/boards/espruino_pico/mpconfigboard.mk +++ b/ports/stm/boards/espruino_pico/mpconfigboard.mk @@ -22,6 +22,7 @@ CIRCUITPY_AUDIOPWMIO = 0 CIRCUITPY_BUSDEVICE = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_ULAB = 0 diff --git a/ports/stm/boards/meowbit_v121/mpconfigboard.mk b/ports/stm/boards/meowbit_v121/mpconfigboard.mk index dc44095a31..d846755c28 100644 --- a/ports/stm/boards/meowbit_v121/mpconfigboard.mk +++ b/ports/stm/boards/meowbit_v121/mpconfigboard.mk @@ -20,4 +20,5 @@ LD_FILE = boards/STM32F401xe_boot.ld # For debugging - also comment BOOTLOADER_OFFSET and BOARD_VTOR_DEFER # LD_FILE = boards/STM32F401xe_fs.ld +CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_ULAB = 0 diff --git a/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk b/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk index 99af948d7b..d1a627569c 100644 --- a/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk +++ b/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk @@ -16,5 +16,6 @@ LD_FILE = boards/STM32F411_fs.ld # Too big for the flash CIRCUITPY_AUDIOCORE = 0 CIRCUITPY_AUDIOPWMIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 diff --git a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk index 04c8cc2d45..c8dc80a70c 100644 --- a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk @@ -15,5 +15,6 @@ LD_FILE = boards/STM32F411_fs.ld # Too big for the flash CIRCUITPY_AUDIOCORE = 0 CIRCUITPY_AUDIOPWMIO = 0 +CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 diff --git a/ports/stm/common-hal/busio/UART.c b/ports/stm/common-hal/busio/UART.c index ecaee84d3f..621cce6cfd 100644 --- a/ports/stm/common-hal/busio/UART.c +++ b/ports/stm/common-hal/busio/UART.c @@ -355,7 +355,7 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *handle) { if (context->sigint_enabled) { if (context->rx_char == CHAR_CTRL_C) { common_hal_busio_uart_clear_rx_buffer(context); - mp_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); } } diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 8df8fd5f1e..3426fc21e5 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -270,6 +270,13 @@ STATIC mp_obj_t extra_coverage(void) { size_t len = mp_repl_autocomplete("__n", 3, &mp_plat_print, &str); mp_printf(&mp_plat_print, "%.*s\n", (int)len, str); + len = mp_repl_autocomplete("i", 1, &mp_plat_print, &str); + mp_printf(&mp_plat_print, "%.*s\n", (int)len, str); + mp_repl_autocomplete("import ", 7, &mp_plat_print, &str); + len = mp_repl_autocomplete("import ut", 9, &mp_plat_print, &str); + mp_printf(&mp_plat_print, "%.*s\n", (int)len, str); + mp_repl_autocomplete("import utime", 12, &mp_plat_print, &str); + mp_store_global(MP_QSTR_sys, mp_import_name(MP_QSTR_sys, mp_const_none, MP_OBJ_NEW_SMALL_INT(0))); mp_repl_autocomplete("sys.", 4, &mp_plat_print, &str); len = mp_repl_autocomplete("sys.impl", 8, &mp_plat_print, &str); @@ -485,7 +492,7 @@ STATIC mp_obj_t extra_coverage(void) { } // setting the keyboard interrupt and raising it during mp_handle_pending - mp_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_handle_pending(true); @@ -495,13 +502,13 @@ STATIC mp_obj_t extra_coverage(void) { } // setting the keyboard interrupt (twice) and cancelling it during mp_handle_pending - mp_keyboard_interrupt(); - mp_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); mp_handle_pending(false); // setting keyboard interrupt and a pending event (intr should be handled first) mp_sched_schedule(MP_OBJ_FROM_PTR(&mp_builtin_print_obj), MP_OBJ_NEW_SMALL_INT(10)); - mp_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); if (nlr_push(&nlr) == 0) { mp_handle_pending(true); nlr_pop(); diff --git a/ports/unix/main.c b/ports/unix/main.c index a63a377c3b..55bdc22082 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -450,7 +450,13 @@ MP_NOINLINE int main_(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); #endif - mp_stack_set_limit(40000 * (sizeof(void *) / 4)); + // Define a reasonable stack limit to detect stack overflow. + mp_uint_t stack_limit = 40000 * (sizeof(void *) / 4); + #if defined(__arm__) && !defined(__thumb2__) + // ARM (non-Thumb) architectures require more stack. + stack_limit *= 2; + #endif + mp_stack_set_limit(stack_limit); pre_process_options(argc, argv); diff --git a/ports/unix/modffi.c b/ports/unix/modffi.c index d18b82dbc6..06fb4c5718 100644 --- a/ports/unix/modffi.c +++ b/ports/unix/modffi.c @@ -35,6 +35,7 @@ #include "py/runtime.h" #include "py/binary.h" #include "py/mperrno.h" +#include "py/objint.h" #include "supervisor/shared/translate.h" @@ -58,6 +59,18 @@ * but may be later. */ +// This union is large enough to hold any supported argument/return value. +typedef union _ffi_union_t { + ffi_arg ffi; + unsigned char B; + unsigned short int H; + unsigned int I; + unsigned long int L; + unsigned long long int Q; + float flt; + double dbl; +} ffi_union_t; + typedef struct _mp_obj_opaque_t { mp_obj_base_t base; void *val; @@ -153,10 +166,10 @@ STATIC ffi_type *get_ffi_type(mp_obj_t o_in) { mp_raise_TypeError(MP_ERROR_TEXT("unknown type")); } -STATIC mp_obj_t return_ffi_value(ffi_arg val, char type) { +STATIC mp_obj_t return_ffi_value(ffi_union_t *val, char type) { switch (type) { case 's': { - const char *s = (const char *)(intptr_t)val; + const char *s = (const char *)(intptr_t)val->ffi; if (!s) { return mp_const_none; } @@ -166,20 +179,30 @@ STATIC mp_obj_t return_ffi_value(ffi_arg val, char type) { return mp_const_none; #if MICROPY_PY_BUILTINS_FLOAT case 'f': { - union { ffi_arg ffi; - float flt; - } val_union = { .ffi = val }; - return mp_obj_new_float_from_f(val_union.flt); + return mp_obj_new_float_from_f(val->flt); } case 'd': { - double *p = (double *)&val; - return mp_obj_new_float_from_d(*p); + return mp_obj_new_float_from_d(val->dbl); } #endif + case 'b': + case 'h': + case 'i': + case 'l': + return mp_obj_new_int((signed)val->ffi); + case 'B': + case 'H': + case 'I': + case 'L': + return mp_obj_new_int_from_uint(val->ffi); + case 'q': + return mp_obj_new_int_from_ll(val->Q); + case 'Q': + return mp_obj_new_int_from_ull(val->Q); case 'O': - return (mp_obj_t)(intptr_t)val; + return (mp_obj_t)(intptr_t)val->ffi; default: - return mp_obj_new_int(val); + return mp_obj_new_int(val->ffi); } } @@ -364,34 +387,74 @@ STATIC void ffifunc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_printf(print, "", self->func); } +STATIC unsigned long long ffi_get_int_value(mp_obj_t o) { + if (mp_obj_is_small_int(o)) { + return MP_OBJ_SMALL_INT_VALUE(o); + } else { + unsigned long long res; + mp_obj_int_to_bytes_impl(o, MP_ENDIANNESS_BIG, sizeof(res), (byte *)&res); + return res; + } +} + +STATIC ffi_union_t ffi_int_obj_to_ffi_union(mp_obj_t o, const char argtype) { + ffi_union_t ret; + if ((argtype | 0x20) == 'q') { + ret.Q = ffi_get_int_value(o); + return ret; + } else { + mp_uint_t val = mp_obj_int_get_truncated(o); + switch (argtype) { + case 'b': + case 'B': + ret.B = val; + break; + case 'h': + case 'H': + ret.H = val; + break; + case 'i': + case 'I': + ret.I = val; + break; + case 'l': + case 'L': + ret.L = val; + break; + default: + ret.ffi = val; + break; + } + } + return ret; +} + STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { (void)n_kw; mp_obj_ffifunc_t *self = MP_OBJ_TO_PTR(self_in); assert(n_kw == 0); assert(n_args == self->cif.nargs); - ffi_arg values[n_args]; + ffi_union_t values[n_args]; void *valueptrs[n_args]; const char *argtype = self->argtypes; for (uint i = 0; i < n_args; i++, argtype++) { mp_obj_t a = args[i]; if (*argtype == 'O') { - values[i] = (ffi_arg)(intptr_t)a; + values[i].ffi = (ffi_arg)(intptr_t)a; #if MICROPY_PY_BUILTINS_FLOAT } else if (*argtype == 'f') { - float *p = (float *)&values[i]; - *p = mp_obj_get_float_to_f(a); + values[i].flt = mp_obj_get_float_to_f(a); } else if (*argtype == 'd') { - double *p = (double *)&values[i]; - *p = mp_obj_get_float_to_d(a); + values[i].dbl = mp_obj_get_float_to_d(a); #endif } else if (a == mp_const_none) { - values[i] = 0; + values[i].ffi = 0; } else if (mp_obj_is_int(a)) { - values[i] = mp_obj_int_get_truncated(a); + values[i] = ffi_int_obj_to_ffi_union(a, *argtype); } else if (mp_obj_is_str(a)) { const char *s = mp_obj_str_get_str(a); - values[i] = (ffi_arg)(intptr_t)s; + values[i].ffi = (ffi_arg)(intptr_t)s; } else if (((mp_obj_base_t *)MP_OBJ_TO_PTR(a))->type->buffer_p.get_buffer != NULL) { mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(a); mp_buffer_info_t bufinfo; @@ -399,32 +462,19 @@ STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const if (ret != 0) { goto error; } - values[i] = (ffi_arg)(intptr_t)bufinfo.buf; + values[i].ffi = (ffi_arg)(intptr_t)bufinfo.buf; } else if (mp_obj_is_type(a, &fficallback_type)) { mp_obj_fficallback_t *p = MP_OBJ_TO_PTR(a); - values[i] = (ffi_arg)(intptr_t)p->func; + values[i].ffi = (ffi_arg)(intptr_t)p->func; } else { goto error; } valueptrs[i] = &values[i]; } - // If ffi_arg is not big enough to hold a double, then we must pass along a - // pointer to a memory location of the correct size. - // TODO check if this needs to be done for other types which don't fit into - // ffi_arg. - #if MICROPY_PY_BUILTINS_FLOAT - if (sizeof(ffi_arg) == 4 && self->rettype == 'd') { - double retval; - ffi_call(&self->cif, self->func, &retval, valueptrs); - return mp_obj_new_float_from_d(retval); - } else - #endif - { - ffi_arg retval; - ffi_call(&self->cif, self->func, &retval, valueptrs); - return return_ffi_value(retval, self->rettype); - } + ffi_union_t retval; + ffi_call(&self->cif, self->func, &retval, valueptrs); + return return_ffi_value(&retval, self->rettype); error: mp_raise_TypeError(MP_ERROR_TEXT("Don't know how to pass object to native function")); diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index e5ca793ab3..fe4030f08d 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -126,7 +126,7 @@ void mp_thread_init(void) { thread->next = NULL; #if defined(__APPLE__) - snprintf(thread_signal_done_name, sizeof(thread_signal_done_name), "micropython_sem_%d", (int)thread->id); + snprintf(thread_signal_done_name, sizeof(thread_signal_done_name), "micropython_sem_%ld", (long)thread->id); thread_signal_done_p = sem_open(thread_signal_done_name, O_CREAT | O_EXCL, 0666, 0); #else sem_init(&thread_signal_done, 0, 0); diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 89bb645bff..fceff68284 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -57,7 +57,7 @@ STATIC void sighandler(int signum) { // this is the second time we are called, so die straight away exit(1); } - mp_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); #endif } } diff --git a/py/argcheck.c b/py/argcheck.c index 8d26779b91..0d14c5eff2 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -40,7 +40,7 @@ void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig) { size_t n_args_max = (sig >> 1) & 0xffff; if (n_kw && !takes_kw) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else mp_raise_TypeError(MP_ERROR_TEXT("function doesn't take keyword arguments")); @@ -49,7 +49,7 @@ void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig) { if (n_args_min == n_args_max) { if (n_args != n_args_min) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), @@ -58,7 +58,7 @@ void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig) { } } else { if (n_args < n_args_min) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else mp_raise_TypeError_varg( @@ -66,7 +66,7 @@ void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig) { n_args_min - n_args); #endif } else if (n_args > n_args_max) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else mp_raise_TypeError_varg( @@ -106,7 +106,7 @@ void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n } if (kw == NULL) { if (allowed[i].flags & MP_ARG_REQUIRED) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' argument required"), allowed[i].qst); @@ -130,7 +130,7 @@ void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n } if (pos_found < n_pos) { extra_positional: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else // TODO better error message @@ -138,7 +138,7 @@ void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n #endif } if (kws != NULL && kws_found < kws->used) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else // TODO better error message @@ -162,3 +162,56 @@ NORETURN void mp_arg_error_unimpl_kw(void) { mp_raise_NotImplementedError(MP_ERROR_TEXT("keyword argument(s) not yet implemented - use normal args instead")); } #endif + + +mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name) { + if (i < min) { + mp_raise_ValueError_varg(translate("%q must be >= %d"), arg_name, min); + } + return i; +} + +mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t max, qstr arg_name) { + if (i > max) { + mp_raise_ValueError_varg(translate("%q must <= %d"), arg_name, max); + } + return i; +} + +mp_int_t mp_arg_validate_int_range(mp_int_t i, mp_int_t min, mp_int_t max, qstr arg_name) { + if (i < min || i > max) { + mp_raise_ValueError_varg(translate("%q must be %d-%d"), arg_name, min, max); + } + return i; +} + +mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name) { + const mp_float_t f = (float_in == MP_OBJ_NULL) + ? default_for_null + : mp_obj_get_float(float_in); + if (f <= (mp_float_t)0.0) { + mp_raise_ValueError_varg(translate("%q must be >= 0"), arg_name); + } + return f; +} + +size_t mp_arg_validate_length_with_name(mp_int_t i, size_t length, qstr arg_name, qstr length_name) { + if (i != (mp_int_t)length) { + mp_raise_ValueError_varg(translate("%q length must be %q"), MP_QSTR_pressed, MP_QSTR_num_keys); + } + return (size_t)i; +} + +mp_obj_t mp_arg_validate_type(mp_obj_t obj, const mp_obj_type_t *type, qstr arg_name) { + if (!mp_obj_is_type(obj, type)) { + mp_raise_TypeError_varg(translate("%q must of type %q"), arg_name, type->name); + } + return obj; +} + +mp_obj_t mp_arg_validate_string(mp_obj_t obj, qstr arg_name) { + if (!mp_obj_is_str(obj)) { + mp_raise_TypeError_varg(translate("%q must be a string"), arg_name); + } + return obj; +} diff --git a/py/asmarm.c b/py/asmarm.c index 30ab546ae6..3b637d34ef 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -38,25 +38,6 @@ #define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000) -void asm_arm_end_pass(asm_arm_t *as) { - if (as->base.pass == MP_ASM_PASS_EMIT) { - #if defined(__linux__) && defined(__GNUC__) - char *start = mp_asm_base_get_code(&as->base); - char *end = start + mp_asm_base_get_code_size(&as->base); - __builtin___clear_cache(start, end); - #elif defined(__arm__) - // flush I- and D-cache - asm volatile ( - "0:" - "mrc p15, 0, r15, c7, c10, 3\n" - "bne 0b\n" - "mov r0, #0\n" - "mcr p15, 0, r0, c7, c7, 0\n" - : : : "r0", "cc"); - #endif - } -} - // Insert word into instruction flow STATIC void emit(asm_arm_t *as, uint op) { uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4); diff --git a/py/asmarm.h b/py/asmarm.h index 3efb8042d3..cab4de79c2 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -72,7 +72,9 @@ typedef struct _asm_arm_t { uint stack_adjust; } asm_arm_t; -void asm_arm_end_pass(asm_arm_t *as); +static inline void asm_arm_end_pass(asm_arm_t *as) { + (void)as; +} void asm_arm_entry(asm_arm_t *as, int num_locals); void asm_arm_exit(asm_arm_t *as); diff --git a/py/asmthumb.c b/py/asmthumb.c index ae35632fac..547e5bedc1 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -35,7 +35,6 @@ #include "py/mpstate.h" #include "py/persistentcode.h" -#include "py/mphal.h" #include "py/asmthumb.h" #define UNSIGNED_FIT5(x) ((uint32_t)(x) < 32) @@ -62,20 +61,6 @@ static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) { return mp_asm_base_get_cur_to_write_bytes(&as->base, n); } -void asm_thumb_end_pass(asm_thumb_t *as) { - (void)as; - // could check labels are resolved... - - #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT == 1 - if (as->base.pass == MP_ASM_PASS_EMIT) { - // flush D-cache, so the code emitted is stored in memory - MP_HAL_CLEAN_DCACHE(as->base.code_base, as->base.code_size); - // invalidate I-cache - SCB_InvalidateICache(); - } - #endif -} - /* STATIC void asm_thumb_write_byte_1(asm_thumb_t *as, byte b1) { byte *c = asm_thumb_get_cur_to_write_bytes(as, 1); diff --git a/py/asmthumb.h b/py/asmthumb.h index acdcfed701..ea48f7ebf3 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -70,7 +70,9 @@ typedef struct _asm_thumb_t { uint32_t stack_adjust; } asm_thumb_t; -void asm_thumb_end_pass(asm_thumb_t *as); +static inline void asm_thumb_end_pass(asm_thumb_t *as) { + (void)as; +} void asm_thumb_entry(asm_thumb_t *as, int num_locals); void asm_thumb_exit(asm_thumb_t *as); diff --git a/py/asmx64.c b/py/asmx64.c index fd64eaf98b..62df5c6d4a 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -285,31 +285,28 @@ void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest } void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { - assert(src_r64 < 8); - if (dest_r64 < 8) { + if (src_r64 < 8 && dest_r64 < 8) { asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R64); } else { - asm_x64_write_byte_3(as, REX_PREFIX | REX_R, 0x0f, OPCODE_MOVZX_RM8_TO_R64); + asm_x64_write_byte_3(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 0x0f, OPCODE_MOVZX_RM8_TO_R64); } asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); } void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { - assert(src_r64 < 8); - if (dest_r64 < 8) { + if (src_r64 < 8 && dest_r64 < 8) { asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R64); } else { - asm_x64_write_byte_3(as, REX_PREFIX | REX_R, 0x0f, OPCODE_MOVZX_RM16_TO_R64); + asm_x64_write_byte_3(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 0x0f, OPCODE_MOVZX_RM16_TO_R64); } asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); } void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) { - assert(src_r64 < 8); - if (dest_r64 < 8) { + if (src_r64 < 8 && dest_r64 < 8) { asm_x64_write_byte_1(as, OPCODE_MOV_RM64_TO_R64); } else { - asm_x64_write_byte_2(as, REX_PREFIX | REX_R, OPCODE_MOV_RM64_TO_R64); + asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_MOV_RM64_TO_R64); } asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp); } diff --git a/py/bc.c b/py/bc.c index 963b3d482c..ccf503631d 100644 --- a/py/bc.c +++ b/py/bc.c @@ -77,7 +77,7 @@ const byte *mp_decode_uint_skip(const byte *ptr) { #endif STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE // generic message, used also for other argument issues (void)f; (void)expected; @@ -221,7 +221,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw } // Didn't find name match with positional args if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("unexpected keyword argument")); #else mp_raise_TypeError_varg( diff --git a/py/builtinimport.c b/py/builtinimport.c index f614b95102..d49ef2a9d5 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -332,7 +332,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { // We must have some component left over to import from if (p == this_name) { - mp_raise_ValueError(MP_ERROR_TEXT("cannot perform relative import")); + mp_raise_ImportError(MP_ERROR_TEXT("cannot perform relative import")); } uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len); @@ -421,7 +421,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { mp_module_call_init(mod_name, module_obj); } else { // couldn't find the file, so fail - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ImportError(MP_ERROR_TEXT("module not found")); #else mp_raise_msg_varg(&mp_type_ImportError, @@ -533,7 +533,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { #endif // Couldn't find the module, so fail - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found")); #else mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), module_name_qstr); diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 41f8d71abd..9756329ac5 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -178,6 +178,9 @@ endif ifeq ($(CIRCUITPY_FRAMEBUFFERIO),1) SRC_PATTERNS += framebufferio/% endif +ifeq ($(CIRCUITPY__EVE),1) +SRC_PATTERNS += _eve/% +endif ifeq ($(CIRCUITPY_FREQUENCYIO),1) SRC_PATTERNS += frequencyio/% endif @@ -196,12 +199,12 @@ endif ifeq ($(CIRCUITPY_IPADDRESS),1) SRC_PATTERNS += ipaddress/% endif +ifeq ($(CIRCUITPY_KEYPAD),1) +SRC_PATTERNS += keypad/% +endif ifeq ($(CIRCUITPY_MATH),1) SRC_PATTERNS += math/% endif -ifeq ($(CIRCUITPY__EVE),1) -SRC_PATTERNS += _eve/% -endif ifeq ($(CIRCUITPY_MEMORYMONITOR),1) SRC_PATTERNS += memorymonitor/% endif @@ -512,6 +515,12 @@ SRC_SHARED_MODULE_ALL = \ framebufferio/__init__.c \ ipaddress/IPv4Address.c \ ipaddress/__init__.c \ + keypad/__init__.c \ + keypad/Event.c \ + keypad/EventQueue.c \ + keypad/KeyMatrix.c \ + keypad/ShiftRegisterKeys.c \ + keypad/Keys.c \ sdcardio/SDCard.c \ sdcardio/__init__.c \ gamepad/GamePad.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 009b428645..fdb78d885c 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -440,6 +440,13 @@ extern const struct _mp_obj_module_t espidf_module; #define ESPIDF_MODULE #endif +#if CIRCUITPY__EVE +extern const struct _mp_obj_module_t _eve_module; +#define _EVE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__eve), (mp_obj_t)&_eve_module }, +#else +#define _EVE_MODULE +#endif + #if CIRCUITPY_FRAMEBUFFERIO extern const struct _mp_obj_module_t framebufferio_module; #define FRAMEBUFFERIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_framebufferio), (mp_obj_t)&framebufferio_module }, @@ -523,6 +530,23 @@ extern const struct _mp_obj_module_t ipaddress_module; #define JSON_MODULE #endif +#if CIRCUITPY_KEYPAD +extern const struct _mp_obj_module_t keypad_module; +#define KEYPAD_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_keypad), (mp_obj_t)&keypad_module }, +#define KEYPAD_ROOT_POINTERS mp_obj_t keypad_scanners_linked_list; +#else +#define KEYPAD_MODULE +#define KEYPAD_ROOT_POINTERS +#endif + +#if CIRCUITPY_GAMEPAD || CIRCUITPY_GAMEPADSHIFT +// Scan gamepad every 32ms +#define CIRCUITPY_GAMEPAD_TICKS 0x1f +#define GAMEPAD_ROOT_POINTERS mp_obj_t gamepad_singleton; +#else +#define GAMEPAD_ROOT_POINTERS +#endif + #if CIRCUITPY_MATH extern const struct _mp_obj_module_t math_module; #define MATH_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_math), (mp_obj_t)&math_module }, @@ -530,13 +554,6 @@ extern const struct _mp_obj_module_t math_module; #define MATH_MODULE #endif -#if CIRCUITPY__EVE -extern const struct _mp_obj_module_t _eve_module; -#define _EVE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__eve), (mp_obj_t)&_eve_module }, -#else -#define _EVE_MODULE -#endif - #if CIRCUITPY_MEMORYMONITOR extern const struct _mp_obj_module_t memorymonitor_module; #define MEMORYMONITOR_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_memorymonitor), (mp_obj_t)&memorymonitor_module }, @@ -877,6 +894,7 @@ extern const struct _mp_obj_module_t msgpack_module; VECTORIO_MODULE \ ERRNO_MODULE \ ESPIDF_MODULE \ + _EVE_MODULE \ FRAMEBUFFERIO_MODULE \ FREQUENCYIO_MODULE \ GAMEPAD_MODULE \ @@ -886,8 +904,8 @@ extern const struct _mp_obj_module_t msgpack_module; IPADDRESS_MODULE \ IMAGECAPTURE_MODULE \ JSON_MODULE \ + KEYPAD_MODULE \ MATH_MODULE \ - _EVE_MODULE \ MEMORYMONITOR_MODULE \ MICROCONTROLLER_MODULE \ MSGPACK_MODULE \ @@ -954,6 +972,7 @@ struct _supervisor_allocation_node; vstr_t *repl_line; \ mp_obj_t rtc_time_source; \ GAMEPAD_ROOT_POINTERS \ + KEYPAD_ROOT_POINTERS \ mp_obj_t pew_singleton; \ BOARD_UART_ROOT_POINTER \ FLASH_ROOT_POINTERS \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 077470d936..0e2c577c69 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -136,9 +136,6 @@ CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO) CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 0 CFLAGS += -DCIRCUITPY_COMPUTED_GOTO_SAVE_SPACE=$(CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE) -CIRCUITPY_CONSOLE_BLE ?= 0 -CFLAGS += -DCIRCUITPY_CONSOLE_BLE=$(CIRCUITPY_CONSOLE_BLE) - CIRCUITPY_CONSOLE_UART ?= 0 CFLAGS += -DCIRCUITPY_CONSOLE_UART=$(CIRCUITPY_CONSOLE_UART) @@ -178,10 +175,13 @@ CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO) CIRCUITPY_ESPIDF ?= 0 CFLAGS += -DCIRCUITPY_ESPIDF=$(CIRCUITPY_ESPIDF) +CIRCUITPY__EVE ?= 0 +CFLAGS += -DCIRCUITPY__EVE=$(CIRCUITPY__EVE) + CIRCUITPY_FREQUENCYIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_FREQUENCYIO=$(CIRCUITPY_FREQUENCYIO) -CIRCUITPY_GAMEPAD ?= $(CIRCUITPY_FULL_BUILD) +CIRCUITPY_GAMEPAD ?= 0 CFLAGS += -DCIRCUITPY_GAMEPAD=$(CIRCUITPY_GAMEPAD) CIRCUITPY_GAMEPADSHIFT ?= 0 @@ -199,12 +199,12 @@ CFLAGS += -DCIRCUITPY_IPADDRESS=$(CIRCUITPY_IPADDRESS) CIRCUITPY_JSON ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_JSON=$(CIRCUITPY_JSON) +CIRCUITPY_KEYPAD ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_KEYPAD=$(CIRCUITPY_KEYPAD) + CIRCUITPY_MATH ?= 1 CFLAGS += -DCIRCUITPY_MATH=$(CIRCUITPY_MATH) -CIRCUITPY__EVE ?= 0 -CFLAGS += -DCIRCUITPY__EVE=$(CIRCUITPY__EVE) - CIRCUITPY_MEMORYMONITOR ?= 0 CFLAGS += -DCIRCUITPY_MEMORYMONITOR=$(CIRCUITPY_MEMORYMONITOR) @@ -252,15 +252,6 @@ CFLAGS += -DCIRCUITPY_RANDOM=$(CIRCUITPY_RANDOM) CIRCUITPY_RE ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_RE=$(CIRCUITPY_RE) -CIRCUITPY_REPL_BLE ?= 0 -CFLAGS += -DCIRCUITPY_REPL_BLE=$(CIRCUITPY_REPL_BLE) - -CIRCUITPY_REPL_UART ?= 0 -CFLAGS += -DCIRCUITPY_REPL_UART=$(CIRCUITPY_REPL_UART) - -CIRCUITPY_REPL_USB ?= 1 -CFLAGS += -DCIRCUITPY_REPL_USB=$(CIRCUITPY_REPL_USB) - # Should busio.I2C() check for pullups? # Some boards in combination with certain peripherals may not want this. CIRCUITPY_REQUIRE_I2C_PULLUPS ?= 1 @@ -296,6 +287,9 @@ CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO) CIRCUITPY_SDIOIO ?= 0 CFLAGS += -DCIRCUITPY_SDIOIO=$(CIRCUITPY_SDIOIO) +CIRCUITPY_SERIAL_BLE ?= 0 +CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) + CIRCUITPY_SHARPDISPLAY ?= $(CIRCUITPY_FRAMEBUFFERIO) CFLAGS += -DCIRCUITPY_SHARPDISPLAY=$(CIRCUITPY_SHARPDISPLAY) diff --git a/py/compile.c b/py/compile.c index 911019e207..bdedc874e3 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1795,16 +1795,6 @@ STATIC void compile_yield_from(compiler_t *comp) { } #if MICROPY_PY_ASYNC_AWAIT -STATIC bool compile_require_async_context(compiler_t *comp, mp_parse_node_struct_t *pns) { - int scope_flags = comp->scope_cur->scope_flags; - if ((scope_flags & MP_SCOPE_FLAG_ASYNC) != 0) { - return true; - } - compile_syntax_error(comp, (mp_parse_node_t)pns, - MP_ERROR_TEXT("'await', 'async for' or 'async with' outside async function")); - return false; -} - STATIC void compile_await_object_method(compiler_t *comp, qstr method) { EMIT_ARG(load_method, method, false); EMIT_ARG(call_method, 0, 0, 0); @@ -1813,11 +1803,6 @@ STATIC void compile_await_object_method(compiler_t *comp, qstr method) { STATIC void compile_async_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { // comp->break_label |= MP_EMIT_BREAK_FROM_FOR; - - if (!compile_require_async_context(comp, pns)) { - return; - } - qstr context = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]); uint while_else_label = comp_next_label(comp); uint try_exception_label = comp_next_label(comp); @@ -1980,9 +1965,6 @@ STATIC void compile_async_with_stmt_helper(compiler_t *comp, size_t n, mp_parse_ } STATIC void compile_async_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { - if (!compile_require_async_context(comp, pns)) { - return; - } // get the nodes for the pre-bit of the with (the a as b, c as d, ... bit) mp_parse_node_t *nodes; size_t n = mp_parse_node_extract_list(&pns->nodes[0], PN_with_stmt_list, &nodes); @@ -2000,13 +1982,23 @@ STATIC void compile_async_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { compile_funcdef(comp, pns0); scope_t *fscope = (scope_t *)pns0->nodes[4]; fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR | MP_SCOPE_FLAG_ASYNC; - } else if (MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_for_stmt) { - // async for - compile_async_for_stmt(comp, pns0); } else { - // async with - assert(MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_with_stmt); - compile_async_with_stmt(comp, pns0); + // async for/with; first verify the scope is a generator + int scope_flags = comp->scope_cur->scope_flags; + if (!(scope_flags & MP_SCOPE_FLAG_GENERATOR)) { + compile_syntax_error(comp, (mp_parse_node_t)pns0, + MP_ERROR_TEXT("'await', 'async for' or 'async with' outside async function")); + return; + } + + if (MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_for_stmt) { + // async for + compile_async_for_stmt(comp, pns0); + } else { + // async with + assert(MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_with_stmt); + compile_async_with_stmt(comp, pns0); + } } } #endif @@ -2627,7 +2619,7 @@ STATIC void compile_atom_brace_helper(compiler_t *comp, mp_parse_node_struct_t * compile_node(comp, pn_i); if (is_dict) { if (!is_key_value) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("invalid syntax")); #else compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("expecting key:value for dict")); @@ -2637,7 +2629,7 @@ STATIC void compile_atom_brace_helper(compiler_t *comp, mp_parse_node_struct_t * EMIT(store_map); } else { if (is_key_value) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("invalid syntax")); #else compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("expecting just a value for set")); @@ -2799,7 +2791,6 @@ STATIC void compile_atom_expr_await(compiler_t *comp, mp_parse_node_struct_t *pn compile_syntax_error(comp, (mp_parse_node_t)pns, MP_ERROR_TEXT("'await' outside function")); return; } - compile_require_async_context(comp, pns); compile_atom_expr_normal(comp, pns); // If it's an awaitable thing, need to reach for the __await__ method for the coroutine. diff --git a/py/dynruntime.h b/py/dynruntime.h index 96d7d27b2f..0b0fa443bd 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -81,6 +81,7 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #define mp_type_type (*mp_fun_table.type_type) #define mp_type_str (*mp_fun_table.type_str) +#define mp_type_tuple (*((mp_obj_base_t *)mp_const_empty_tuple)->type) #define mp_type_list (*mp_fun_table.type_list) #define mp_type_EOFError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_EOFError))) #define mp_type_IndexError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_IndexError))) @@ -121,6 +122,7 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #define mp_obj_len(o) (mp_obj_len_dyn(o)) #define mp_obj_subscr(base, index, val) (mp_fun_table.obj_subscr((base), (index), (val))) +#define mp_obj_get_array(o, len, items) (mp_obj_get_array_dyn((o), (len), (items))) #define mp_obj_list_append(list, item) (mp_fun_table.list_append((list), (item))) #define mp_obj_assert_native_inited(o) (mp_fun_table.assert_native_inited((o))) @@ -264,4 +266,23 @@ static inline void mp_raise_OSError_dyn(int er) { #define mp_obj_get_float(o) (mp_obj_get_float_to_d((o))) #endif +/******************************************************************************/ +// Inline function definitions. + +// *items may point inside a GC block +static inline void mp_obj_get_array_dyn(mp_obj_t o, size_t *len, mp_obj_t **items) { + const mp_obj_type_t *type = mp_obj_get_type(o); + if (type == &mp_type_tuple) { + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(o); + *len = t->len; + *items = &t->items[0]; + } else if (type == &mp_type_list) { + mp_obj_list_t *l = MP_OBJ_TO_PTR(o); + *len = l->len; + *items = l->items; + } else { + mp_raise_TypeError("expected tuple/list"); + } +} + #endif // MICROPY_INCLUDED_PY_DYNRUNTIME_H diff --git a/py/emitglue.c b/py/emitglue.c index 5049a76869..e701d0808f 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -108,6 +108,31 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM); + // Some architectures require flushing/invalidation of the I/D caches, + // so that the generated native code which was created in data RAM will + // be available for execution from instruction RAM. + #if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB + #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT == 1 + // Flush D-cache, so the code emitted is stored in RAM. + MP_HAL_CLEAN_DCACHE(fun_data, fun_len); + // Invalidate I-cache, so the newly-created code is reloaded from RAM. + SCB_InvalidateICache(); + #endif + #elif MICROPY_EMIT_ARM + #if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7 + __builtin___clear_cache(fun_data, (uint8_t *)fun_data + fun_len); + #elif defined(__arm__) + // Flush I-cache and D-cache. + asm volatile ( + "0:" + "mrc p15, 0, r15, c7, c10, 3\n" // test and clean D-cache + "bne 0b\n" + "mov r0, #0\n" + "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache + : : : "r0", "cc"); + #endif + #endif + rc->kind = kind; rc->scope_flags = scope_flags; rc->n_pos_args = n_pos_args; diff --git a/py/emitnative.c b/py/emitnative.c index 1427bc83c9..1fa8c631e4 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1797,7 +1797,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) { int reg_index = REG_ARG_2; int reg_value = REG_ARG_3; emit_pre_pop_reg_flexible(emit, &vtype_base, ®_base, reg_index, reg_value); - #if N_X86 + #if N_X64 || N_X86 // special case: x86 needs byte stores to be from lower 4 regs (REG_ARG_3 is EDX) emit_pre_pop_reg(emit, &vtype_value, reg_value); #else @@ -1884,7 +1884,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) { EMIT_NATIVE_VIPER_TYPE_ERROR(emit, MP_ERROR_TEXT("can't store with '%q' index"), vtype_to_qstr(vtype_index)); } - #if N_X86 + #if N_X64 || N_X86 // special case: x86 needs byte stores to be from lower 4 regs (REG_ARG_3 is EDX) emit_pre_pop_reg(emit, &vtype_value, reg_value); #else diff --git a/py/gc.c b/py/gc.c index 03a10fc535..789d3036b3 100644 --- a/py/gc.c +++ b/py/gc.c @@ -167,7 +167,7 @@ void gc_init(void *start, void *end) { MP_STATE_MEM(gc_lowest_long_lived_ptr) = (void *)PTR_FROM_BLOCK(MP_STATE_MEM(gc_alloc_table_byte_len * BLOCKS_PER_ATB)); // unlock the GC - MP_STATE_MEM(gc_lock_depth) = 0; + MP_STATE_THREAD(gc_lock_depth) = 0; // allow auto collection MP_STATE_MEM(gc_auto_collect_enabled) = true; @@ -200,19 +200,20 @@ void gc_deinit(void) { } void gc_lock(void) { - GC_ENTER(); - MP_STATE_MEM(gc_lock_depth)++; - GC_EXIT(); + // This does not need to be atomic or have the GC mutex because: + // - each thread has its own gc_lock_depth so there are no races between threads; + // - a hard interrupt will only change gc_lock_depth during its execution, and + // upon return will restore the value of gc_lock_depth. + MP_STATE_THREAD(gc_lock_depth)++; } void gc_unlock(void) { - GC_ENTER(); - MP_STATE_MEM(gc_lock_depth)--; - GC_EXIT(); + // This does not need to be atomic, See comment above in gc_lock. + MP_STATE_THREAD(gc_lock_depth)--; } bool gc_is_locked(void) { - return MP_STATE_MEM(gc_lock_depth) != 0; + return MP_STATE_THREAD(gc_lock_depth) != 0; } #ifndef TRACE_MARK @@ -356,7 +357,7 @@ STATIC void gc_mark(void *ptr) { void gc_collect_start(void) { GC_ENTER(); - MP_STATE_MEM(gc_lock_depth)++; + MP_STATE_THREAD(gc_lock_depth)++; #if MICROPY_GC_ALLOC_THRESHOLD MP_STATE_MEM(gc_alloc_amount) = 0; #endif @@ -383,9 +384,19 @@ void gc_collect_ptr(void *ptr) { gc_mark(ptr); } +// Address sanitizer needs to know that the access to ptrs[i] must always be +// considered OK, even if it's a load from an address that would normally be +// prohibited (due to being undefined, in a red zone, etc). +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +__attribute__((no_sanitize_address)) +#endif +static void *gc_get_ptr(void **ptrs, int i) { + return ptrs[i]; +} + void gc_collect_root(void **ptrs, size_t len) { for (size_t i = 0; i < len; i++) { - void *ptr = ptrs[i]; + void *ptr = gc_get_ptr(ptrs, i); gc_mark(ptr); } } @@ -397,13 +408,13 @@ void gc_collect_end(void) { MP_STATE_MEM(gc_first_free_atb_index)[i] = 0; } MP_STATE_MEM(gc_last_free_atb_index) = MP_STATE_MEM(gc_alloc_table_byte_len) - 1; - MP_STATE_MEM(gc_lock_depth)--; + MP_STATE_THREAD(gc_lock_depth)--; GC_EXIT(); } void gc_sweep_all(void) { GC_ENTER(); - MP_STATE_MEM(gc_lock_depth)++; + MP_STATE_THREAD(gc_lock_depth)++; MP_STATE_MEM(gc_stack_overflow) = 0; gc_collect_end(); } @@ -488,18 +499,17 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags, bool long_lived) { return NULL; } + // check if GC is locked + if (MP_STATE_THREAD(gc_lock_depth) > 0) { + return NULL; + } + if (MP_STATE_MEM(gc_pool_start) == 0) { reset_into_safe_mode(GC_ALLOC_OUTSIDE_VM); } GC_ENTER(); - // check if GC is locked - if (MP_STATE_MEM(gc_lock_depth) > 0) { - GC_EXIT(); - return NULL; - } - size_t found_block = 0xffffffff; size_t end_block; size_t start_block; @@ -676,13 +686,13 @@ void *gc_alloc_with_finaliser(mp_uint_t n_bytes) { // force the freeing of a piece of memory // TODO: freeing here does not call finaliser void gc_free(void *ptr) { - GC_ENTER(); - if (MP_STATE_MEM(gc_lock_depth) > 0) { + if (MP_STATE_THREAD(gc_lock_depth) > 0) { // TODO how to deal with this error? - GC_EXIT(); return; } + GC_ENTER(); + DEBUG_printf("gc_free(%p)\n", ptr); if (ptr == NULL) { @@ -837,15 +847,14 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { return NULL; } + if (MP_STATE_THREAD(gc_lock_depth) > 0) { + return NULL; + } + void *ptr = ptr_in; GC_ENTER(); - if (MP_STATE_MEM(gc_lock_depth) > 0) { - GC_EXIT(); - return NULL; - } - // get the GC block number corresponding to this pointer assert(VERIFY_PTR(ptr)); size_t block = BLOCK_FROM_PTR(ptr); diff --git a/py/mkenv.mk b/py/mkenv.mk index 89628759e9..ab17a98f67 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -20,6 +20,7 @@ ifeq ("$(origin V)", "command line") BUILD_VERBOSE=$(V) endif ifndef BUILD_VERBOSE +$(info Use make V=1, make V=2 or set BUILD_VERBOSE similarly in your environment to increase build verbosity.) BUILD_VERBOSE = 0 endif ifeq ($(BUILD_VERBOSE),0) @@ -32,10 +33,6 @@ else Q = STEPECHO = @echo endif -# Since this is a new feature, advertise it -ifeq ($(BUILD_VERBOSE),0) -$(info Use make V=1, make V=2 or set BUILD_VERBOSE similarly in your environment to increase build verbosity.) -endif # default settings; can be overridden in main Makefile diff --git a/py/mkrules.cmake b/py/mkrules.cmake index f20240c62b..7589255b20 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -125,9 +125,13 @@ if(MICROPY_FROZEN_MANIFEST) MICROPY_MODULE_FROZEN_MPY=\(1\) ) + if(NOT MICROPY_LIB_DIR) + set(MICROPY_LIB_DIR ${MICROPY_DIR}/../micropython-lib) + endif() + add_custom_command( OUTPUT ${MICROPY_FROZEN_CONTENT} - COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} -v "MPY_DIR=${MICROPY_DIR}" -v "PORT_DIR=${MICROPY_PORT_DIR}" -b "${CMAKE_BINARY_DIR}" -f${MICROPY_CROSS_FLAGS} ${MICROPY_FROZEN_MANIFEST} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} -v "MPY_DIR=${MICROPY_DIR}" -v "MPY_LIB_DIR=${MICROPY_LIB_DIR}" -v "PORT_DIR=${MICROPY_PORT_DIR}" -v "BOARD_DIR=${MICROPY_BOARD_DIR}" -b "${CMAKE_BINARY_DIR}" -f${MICROPY_CROSS_FLAGS} ${MICROPY_FROZEN_MANIFEST} DEPENDS MICROPY_FORCE_BUILD ${MICROPY_QSTRDEFS_GENERATED} VERBATIM diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 0d53a07392..f5ab07c404 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -374,7 +374,7 @@ STATIC mp_obj_t mp_builtin_ord(mp_obj_t o_in) { } } - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("ord expects a character")); #else mp_raise_TypeError_varg( diff --git a/py/modmicropython.c b/py/modmicropython.c index 411cb0dc2a..8f61cd2979 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -132,13 +132,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_lock_obj, mp_micropython_he STATIC mp_obj_t mp_micropython_heap_unlock(void) { gc_unlock(); - return MP_OBJ_NEW_SMALL_INT(MP_STATE_MEM(gc_lock_depth)); + return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_unlock_obj, mp_micropython_heap_unlock); #if MICROPY_PY_MICROPYTHON_HEAP_LOCKED STATIC mp_obj_t mp_micropython_heap_locked(void) { - return MP_OBJ_NEW_SMALL_INT(MP_STATE_MEM(gc_lock_depth)); + return MP_OBJ_NEW_SMALL_INT(MP_STATE_THREAD(gc_lock_depth)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_locked_obj, mp_micropython_heap_locked); #endif diff --git a/py/modthread.c b/py/modthread.c index 5ef5930545..fd52b9dfb5 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -173,6 +173,9 @@ STATIC void *thread_entry(void *args_in) { mp_pystack_init(mini_pystack, &mini_pystack[128]); #endif + // The GC starts off unlocked on this thread. + ts.gc_lock_depth = 0; + // set locals and globals from the calling context mp_locals_set(args->dict_locals); mp_globals_set(args->dict_globals); diff --git a/py/mpconfig.h b/py/mpconfig.h index 6f6e211345..2cd2a0d356 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -570,6 +570,12 @@ #define MICROPY_VM_HOOK_RETURN #endif +// Hook for mp_sched_schedule when a function gets scheduled on sched_queue +// (this macro executes within an atomic section) +#ifndef MICROPY_SCHED_HOOK_SCHEDULED +#define MICROPY_SCHED_HOOK_SCHEDULED +#endif + // Whether to include the garbage collector #ifndef MICROPY_ENABLE_GC #define MICROPY_ENABLE_GC (0) @@ -692,6 +698,8 @@ typedef long long mp_longint_impl_t; #define MICROPY_ENABLE_DOC_STRING (0) #endif +// Exception messages are removed (requires disabling MICROPY_ROM_TEXT_COMPRESSION) +#define MICROPY_ERROR_REPORTING_NONE (0) // Exception messages are short static strings #define MICROPY_ERROR_REPORTING_TERSE (1) // Exception messages provide basic error details @@ -1518,8 +1526,12 @@ typedef double mp_float_t; /*****************************************************************************/ /* Hooks for a port to wrap functions with attributes */ -#ifndef MICROPY_WRAP_MP_KEYBOARD_INTERRUPT -#define MICROPY_WRAP_MP_KEYBOARD_INTERRUPT(f) f +#ifndef MICROPY_WRAP_MP_SCHED_EXCEPTION +#define MICROPY_WRAP_MP_SCHED_EXCEPTION(f) f +#endif + +#ifndef MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT +#define MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(f) f #endif #ifndef MICROPY_WRAP_MP_SCHED_SCHEDULE diff --git a/py/mpstate.h b/py/mpstate.h index 808e6aba0c..423463109d 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -82,7 +82,6 @@ typedef struct _mp_state_mem_t { int gc_stack_overflow; MICROPY_GC_STACK_ENTRY_TYPE gc_stack[MICROPY_ALLOC_GC_STACK_SIZE]; - uint16_t gc_lock_depth; // This variable controls auto garbage collection. If set to false then the // GC won't automatically run when gc_alloc can't find enough blocks. But @@ -253,6 +252,9 @@ typedef struct _mp_state_thread_t { uint8_t *pystack_cur; #endif + // Locking of the GC is done per thread. + uint16_t gc_lock_depth; + //////////////////////////////////////////////////////////// // START ROOT POINTER SECTION // Everything that needs GC scanning must start here, and diff --git a/py/nlraarch64.c b/py/nlraarch64.c index 37a860540e..9b00a2856a 100644 --- a/py/nlraarch64.c +++ b/py/nlraarch64.c @@ -50,7 +50,7 @@ __asm( "stp x27, x28, [x0, #96]\n" "str x29, [x0, #112]\n" #if defined(__APPLE__) && defined(__MACH__) - "b _nlr_push_tail \n" // do the rest in C + "b _nlr_push_tail \n" // do the rest in C #else "b nlr_push_tail \n" // do the rest in C #endif diff --git a/py/nlrx64.c b/py/nlrx64.c index f74fbd3bd0..f7e92608d2 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -58,7 +58,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { #else __asm volatile ( - #if defined(__APPLE__) || defined(__MACH__) + #if defined(__APPLE__) && defined(__MACH__) "pop %rbp \n" // undo function's prelude #endif "movq (%rsp), %rax \n" // load return %rip @@ -70,7 +70,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { "movq %r13, 56(%rdi) \n" // store %r13 into nlr_buf "movq %r14, 64(%rdi) \n" // store %r14 into nlr_buf "movq %r15, 72(%rdi) \n" // store %r15 into nlr_buf - #if defined(__APPLE__) || defined(__MACH__) + #if defined(__APPLE__) && defined(__MACH__) "jmp _nlr_push_tail \n" // do the rest in C #else "jmp nlr_push_tail \n" // do the rest in C diff --git a/py/obj.c b/py/obj.c index e5715d5411..d2483ed993 100644 --- a/py/obj.c +++ b/py/obj.c @@ -391,7 +391,7 @@ mp_float_t mp_obj_get_float(mp_obj_t arg) { mp_float_t val; if (!mp_obj_get_float_maybe(arg, &val)) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError_varg(MP_ERROR_TEXT("can't convert to %q"), MP_QSTR_float); #else mp_raise_TypeError_varg( @@ -431,7 +431,7 @@ bool mp_obj_get_complex_maybe(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { if (!mp_obj_get_complex_maybe(arg, real, imag)) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("can't convert to complex")); #else mp_raise_TypeError_varg( @@ -449,7 +449,7 @@ void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items) { } else if (mp_obj_is_type(o, &mp_type_list)) { mp_obj_list_get(o, len, items); } else { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("expected tuple/list")); #else mp_raise_TypeError_varg( @@ -463,7 +463,7 @@ void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items) { size_t seq_len; mp_obj_get_array(o, &seq_len, items); if (seq_len != len) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ValueError(MP_ERROR_TEXT("tuple/list has wrong length")); #else mp_raise_ValueError_varg( @@ -478,7 +478,7 @@ size_t mp_get_index(const mp_obj_type_t *type, size_t len, mp_obj_t index, bool if (mp_obj_is_small_int(index)) { i = MP_OBJ_SMALL_INT_VALUE(index); } else if (!mp_obj_get_int_maybe(index, &i)) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("indices must be integers")); #else mp_raise_TypeError_varg( @@ -498,7 +498,7 @@ size_t mp_get_index(const mp_obj_type_t *type, size_t len, mp_obj_t index, bool } } else { if (i < 0 || (mp_uint_t)i >= len) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_IndexError(MP_ERROR_TEXT("index out of range")); #else mp_raise_msg_varg(&mp_type_IndexError, @@ -533,7 +533,7 @@ mp_obj_t mp_obj_id(mp_obj_t o_in) { mp_obj_t mp_obj_len(mp_obj_t o_in) { mp_obj_t len = mp_obj_len_maybe(o_in); if (len == MP_OBJ_NULL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object has no len")); #else mp_raise_TypeError_varg( @@ -575,21 +575,21 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { } } if (value == MP_OBJ_NULL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item deletion")); #else mp_raise_TypeError_varg( MP_ERROR_TEXT("'%s' object doesn't support item deletion"), mp_obj_get_type_str(base)); #endif } else if (value == MP_OBJ_SENTINEL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object isn't subscriptable")); #else mp_raise_TypeError_varg( MP_ERROR_TEXT("'%s' object isn't subscriptable"), mp_obj_get_type_str(base)); #endif } else { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item assignment")); #else mp_raise_TypeError_varg( diff --git a/py/obj.h b/py/obj.h index 5455c81f73..da2ef08451 100644 --- a/py/obj.h +++ b/py/obj.h @@ -794,8 +794,13 @@ extern uint64_t float_to_uint64(float f); mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type); mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg); mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args); +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE +#define mp_obj_new_exception_msg(exc_type, msg) mp_obj_new_exception(exc_type) +#define mp_obj_new_exception_msg_varg(exc_type, ...) mp_obj_new_exception(exc_type) +#else mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const compressed_string_t *msg); mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const compressed_string_t *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) +#endif #ifdef va_start mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const compressed_string_t *fmt, va_list ap); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) #endif diff --git a/py/objarray.c b/py/objarray.c index 54abe4ed91..220969ee20 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -195,6 +195,9 @@ STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, // no args: construct an empty bytearray return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0)); } else if (mp_obj_is_int(args[0])) { + if (n_args > 1) { + mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments")); + } // 1 arg, an integer: construct a blank bytearray of that length mp_uint_t len = mp_obj_get_int(args[0]); mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len); @@ -211,11 +214,7 @@ STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) { mp_obj_array_t *self = m_new_obj(mp_obj_array_t); - self->base.type = &mp_type_memoryview; - self->typecode = typecode; - self->memview_offset = 0; - self->len = nitems; - self->items = items; + mp_obj_memoryview_init(self, typecode, 0, nitems, items); return MP_OBJ_FROM_PTR(self); } @@ -234,6 +233,14 @@ STATIC mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args, bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL), bufinfo.buf)); + // If the input object is a memoryview then need to point the items of the + // new memoryview to the start of the buffer so the GC can trace it. + if (mp_obj_get_type(args[0]) == &mp_type_memoryview) { + mp_obj_array_t *other = MP_OBJ_TO_PTR(args[0]); + self->memview_offset = other->memview_offset; + self->items = other->items; + } + // test if the object can be written to if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) { self->typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW; // indicate writable buffer @@ -288,6 +295,17 @@ STATIC mp_obj_t array_unary_op(mp_unary_op_t op, mp_obj_t o_in) { } } +STATIC int typecode_for_comparison(int typecode, bool *is_unsigned) { + if (typecode == BYTEARRAY_TYPECODE) { + typecode = 'B'; + } + if (typecode <= 'Z') { + typecode += 32; // to lowercase + *is_unsigned = true; + } + return typecode; +} + STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_array_t *lhs = MP_OBJ_TO_PTR(lhs_in); switch (op) { @@ -376,14 +394,33 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs return mp_const_false; } - case MP_BINARY_OP_EQUAL: { + case MP_BINARY_OP_EQUAL: + case MP_BINARY_OP_LESS: + case MP_BINARY_OP_LESS_EQUAL: + case MP_BINARY_OP_MORE: + case MP_BINARY_OP_MORE_EQUAL: { mp_buffer_info_t lhs_bufinfo; mp_buffer_info_t rhs_bufinfo; array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ); if (!mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) { return mp_const_false; } - return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len)); + // mp_seq_cmp_bytes is used so only compatible representations can be correctly compared. + // The type doesn't matter: array/bytearray/str/bytes all have the same buffer layout, so + // just check if the typecodes are compatible; for testing equality the types should have the + // same code except for signedness, and not be floating point because nan never equals nan. + // For > and < the types should be the same and unsigned. + // Note that typecode_for_comparison always returns lowercase letters to save code size. + // No need for (& TYPECODE_MASK) here: xxx_get_buffer already takes care of that. + bool is_unsigned = false; + const int lhs_code = typecode_for_comparison(lhs_bufinfo.typecode, &is_unsigned); + const int rhs_code = typecode_for_comparison(rhs_bufinfo.typecode, &is_unsigned); + if (lhs_code == rhs_code && lhs_code != 'f' && lhs_code != 'd' && (op == MP_BINARY_OP_EQUAL || is_unsigned)) { + return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len)); + } + // mp_obj_equal_not_equal treats returning MP_OBJ_NULL as 'fall back to pointer comparison' + // for MP_BINARY_OP_EQUAL but that is incompatible with CPython. + mp_raise_NotImplementedError(NULL); } default: diff --git a/py/objexcept.c b/py/objexcept.c index aaacdd5500..cf0abe6206 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -367,9 +367,11 @@ mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, return exc_type->make_new(exc_type, n_args, args, NULL); } +#if MICROPY_ERROR_REPORTING != MICROPY_ERROR_REPORTING_NONE mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const compressed_string_t *msg) { return mp_obj_new_exception_msg_varg(exc_type, msg); } +#endif // The following struct and function implement a simple printer that conservatively // allocates memory and truncates the output data if no more memory can be obtained. diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index b1f1f0ba4d..1d7e8a00ec 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -108,7 +108,7 @@ mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, const n_kw = kw_args->used; } if (n_args + n_kw != num_fields) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL mp_raise_TypeError_varg( @@ -134,7 +134,7 @@ mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, const qstr kw = mp_obj_str_get_qstr(kw_args->table[i].key); size_t id = mp_obj_namedtuple_find_field(type, kw); if (id == (size_t)-1) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else mp_raise_TypeError_varg( @@ -142,7 +142,7 @@ mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, const #endif } if (tuple->items[id] != MP_OBJ_NULL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else mp_raise_TypeError_varg( diff --git a/py/objstr.c b/py/objstr.c index 2dfb2bab29..f29a954fb2 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -233,6 +233,10 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, cons return MP_OBJ_FROM_PTR(o); } + if (n_args > 1) { + goto wrong_args; + } + if (mp_obj_is_small_int(args[0])) { mp_int_t len = MP_OBJ_SMALL_INT_VALUE(args[0]); if (len < 0) { @@ -244,10 +248,6 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, cons return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } - if (n_args > 1) { - goto wrong_args; - } - // check if __bytes__ exists, and if so delegate to it mp_obj_t dest[2]; mp_load_method_maybe(args[0], MP_QSTR___bytes__, dest); @@ -968,7 +968,7 @@ STATIC mp_obj_t arg_as_int(mp_obj_t arg) { } #endif -#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE +#if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE STATIC NORETURN void terse_str_format_value_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("bad format string")); } @@ -989,7 +989,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar vstr_add_byte(&vstr, '}'); continue; } - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError(MP_ERROR_TEXT("single '}' encountered in format string")); @@ -1028,7 +1028,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar if (str < top && (*str == 'r' || *str == 's')) { conversion = *str++; } else { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL mp_raise_ValueError(MP_ERROR_TEXT("bad conversion specifier")); @@ -1066,14 +1066,14 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } } if (str >= top) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError(MP_ERROR_TEXT("unmatched '{' in format")); #endif } if (*str != '}') { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError(MP_ERROR_TEXT("expected ':' after format specifier")); @@ -1086,7 +1086,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar int index = 0; if (MP_LIKELY(unichar_isdigit(*field_name))) { if (*arg_i > 0) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError( @@ -1116,7 +1116,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } } else { if (*arg_i < 0) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError( @@ -1209,7 +1209,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar type = *s++; } if (*s) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError(MP_ERROR_TEXT("invalid format specifier")); @@ -1230,14 +1230,14 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar if (flags & (PF_FLAG_SHOW_SIGN | PF_FLAG_SPACE_SIGN)) { if (type == 's') { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError(MP_ERROR_TEXT("sign not allowed in string format specifier")); #endif } if (type == 'c') { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError( @@ -1301,7 +1301,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar break; default: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError_varg( @@ -1373,7 +1373,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar #endif default: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError_varg( @@ -1385,7 +1385,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar // arg doesn't look like a number if (align == '=') { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError( @@ -1409,7 +1409,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } default: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError_varg( @@ -1438,7 +1438,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ mp_check_self(mp_obj_is_str_or_bytes(pattern)); GET_STR_DATA_LEN(pattern, str, len); - #if MICROPY_ERROR_REPORTING != MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_TERSE const byte *start_str = str; #endif bool is_bytes = mp_obj_is_type(pattern, &mp_type_bytes); @@ -1470,7 +1470,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ const byte *key = ++str; while (*str != ')') { if (str >= top) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError(MP_ERROR_TEXT("incomplete format key")); @@ -1534,7 +1534,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ if (str >= top) { incomplete_format: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError(MP_ERROR_TEXT("incomplete format")); @@ -1620,7 +1620,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ break; default: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else mp_raise_ValueError_varg( @@ -2165,7 +2165,7 @@ bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) { } STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("can't convert to str implicitly")); #else const qstr src_name = mp_obj_get_type_qstr(self_in); diff --git a/py/objtype.c b/py/objtype.c index 336fcc341b..cc3168a9e6 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -373,7 +373,7 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, cons m_del(mp_obj_t, args2, 2 + n_args + 2 * n_kw); } if (init_ret != mp_const_none) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("__init__() should return None")); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("__init__() should return None, not '%q'"), @@ -889,7 +889,7 @@ mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons mp_obj_t member[2] = {MP_OBJ_NULL, MP_OBJ_NULL}; mp_obj_t call = mp_obj_instance_get_call(self_in, member); if (call == MP_OBJ_NULL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not callable")); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object is not callable"), @@ -1029,7 +1029,7 @@ STATIC mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); if (self->make_new == NULL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("cannot create instance")); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("cannot create '%q' instances"), self->name); @@ -1171,7 +1171,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) mp_obj_type_t *t = MP_OBJ_TO_PTR(bases_items[i]); // TODO: Verify with CPy, tested on function type if (t->make_new == NULL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("type is not an acceptable base type")); #else mp_raise_TypeError_varg( diff --git a/py/parsenum.c b/py/parsenum.c index b3b79cad09..bd41488204 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -147,7 +147,7 @@ overflow: value_error: { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_obj_t exc = mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("invalid syntax for integer")); raise_exc(exc, lex); diff --git a/py/profile.c b/py/profile.c index 9cf8c4b7ba..e5fb35f0ed 100644 --- a/py/profile.c +++ b/py/profile.c @@ -298,9 +298,7 @@ STATIC mp_obj_t mp_prof_callback_invoke(mp_obj_t callback, prof_callback_args_t mp_prof_is_executing = false; if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; - nlr_raise(obj); + mp_handle_pending(true); } return top; } diff --git a/py/repl.c b/py/repl.c index 7ceae573d4..78367e66da 100644 --- a/py/repl.c +++ b/py/repl.c @@ -167,13 +167,13 @@ STATIC const char *find_completions(const char *s_start, size_t s_len, for (qstr q = MP_QSTR_ + 1; q < nqstr; ++q) { size_t d_len; const char *d_str = (const char *)qstr_data(q, &d_len); + // special case; filter out words that begin with underscore + // unless there's already a partial match + if (s_len == 0 && d_str[0] == '_') { + continue; + } if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) { if (test_qstr(obj, q)) { - // special case; filter out words that begin with underscore - // unless there's already a partial match - if (s_len == 0 && d_str[0] == '_') { - continue; - } if (match_str == NULL) { match_str = d_str; *match_len = d_len; @@ -297,7 +297,7 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print if (q_first == 0) { // If there're no better alternatives, and if it's first word // in the line, try to complete "import". - if (s_start == org_str && s_len > 0) { + if (s_start == org_str && s_len > 0 && s_len < sizeof(import_str) - 1) { if (memcmp(s_start, import_str, s_len) == 0) { *compl_str = import_str + s_len; return sizeof(import_str) - 1 - s_len; diff --git a/py/runtime.c b/py/runtime.c index bff6eb2ed5..b981b6aae8 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -159,9 +159,6 @@ void mp_deinit(void) { #ifdef MICROPY_PORT_DEINIT_FUNC MICROPY_PORT_DEINIT_FUNC; #endif - - // mp_obj_dict_free(&dict_main); - // mp_map_deinit(&MP_STATE_VM(mp_loaded_modules_map)); } mp_obj_t mp_load_name(qstr qst) { @@ -193,7 +190,7 @@ mp_obj_t mp_load_global(qstr qst) { #endif elem = mp_map_lookup((mp_map_t *)&mp_module_builtins_globals.map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); if (elem == NULL) { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_msg(&mp_type_NameError, MP_ERROR_TEXT("name not defined")); #else mp_raise_msg_varg(&mp_type_NameError, MP_ERROR_TEXT("name '%q' is not defined"), qst); @@ -293,20 +290,20 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { } // With MP_UNARY_OP_INT, mp_unary_op() becomes a fallback for mp_obj_get_int(). // In this case provide a more focused error message to not confuse, e.g. chr(1.0) - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - if (op == MP_UNARY_OP_INT) { - mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); - } else { - mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); - } + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + if (op == MP_UNARY_OP_INT) { + mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); } else { - if (op == MP_UNARY_OP_INT) { - mp_raise_TypeError_varg(MP_ERROR_TEXT("can't convert %q to int"), mp_obj_get_type_qstr(arg)); - } else { - mp_raise_TypeError_varg(MP_ERROR_TEXT("unsupported type for %q: '%q'"), - mp_unary_op_method_name[op], mp_obj_get_type_qstr(arg)); - } + mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); } + #else + if (op == MP_UNARY_OP_INT) { + mp_raise_TypeError_varg(MP_ERROR_TEXT("can't convert %q to int"), mp_obj_get_type_qstr(arg)); + } else { + mp_raise_TypeError_varg(MP_ERROR_TEXT("unsupported type for %q: '%q'"), + mp_unary_op_method_name[op], mp_obj_get_type_qstr(arg)); + } + #endif } } @@ -612,7 +609,7 @@ generic_binary_op: } unsupported_op: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); #else mp_raise_TypeError_varg( @@ -654,7 +651,7 @@ mp_obj_t mp_call_function_n_kw(mp_obj_t fun_in, size_t n_args, size_t n_kw, cons return type->call(fun_in, n_args, n_kw, args); } - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not callable")); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object is not callable"), mp_obj_get_type_qstr(fun_in)); @@ -882,14 +879,14 @@ void mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) { return; too_short: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); #else mp_raise_ValueError_varg(MP_ERROR_TEXT("need more than %d values to unpack"), (int)seq_len); #endif too_long: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); #else mp_raise_ValueError_varg(MP_ERROR_TEXT("too many values to unpack (expected %d)"), @@ -951,7 +948,7 @@ void mp_unpack_ex(mp_obj_t seq_in, size_t num_in, mp_obj_t *items) { return; too_short: - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); #else mp_raise_ValueError_varg(MP_ERROR_TEXT("need more than %d values to unpack"), @@ -1138,7 +1135,7 @@ void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { if (dest[0] == MP_OBJ_NULL) { // no attribute/method called attr - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_AttributeError(MP_ERROR_TEXT("no such attribute")); #else // following CPython, we give a more detailed error message for type objects @@ -1212,7 +1209,7 @@ void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) { } #endif } - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_AttributeError(MP_ERROR_TEXT("no such attribute")); #else mp_raise_msg_varg(&mp_type_AttributeError, @@ -1257,7 +1254,7 @@ mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { } // object not iterable - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not iterable")); #else mp_raise_TypeError_varg( @@ -1279,7 +1276,7 @@ mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) { // __next__ exists, call it and return its result return mp_call_method_n_kw(0, 0, dest); } else { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not an iterator")); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object is not an iterator"), @@ -1315,7 +1312,7 @@ mp_obj_t mp_iternext(mp_obj_t o_in) { } } } else { - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not an iterator")); #else mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object is not an iterator"), @@ -1565,6 +1562,26 @@ NORETURN void mp_raise_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { nlr_raise(mp_obj_new_exception_arg1(exc_type, arg)); } +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE + +NORETURN void mp_raise_type(const mp_obj_type_t *exc_type) { + nlr_raise(mp_obj_new_exception(exc_type)); +} + +NORETURN void mp_raise_ValueError_no_msg(void) { + mp_raise_type(&mp_type_ValueError); +} + +NORETURN void mp_raise_TypeError_no_msg(void) { + mp_raise_type(&mp_type_TypeError); +} + +NORETURN void mp_raise_NotImplementedError_no_msg(void) { + mp_raise_type(&mp_type_NotImplementedError); +} + +#else + NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, const compressed_string_t *msg) { if (msg == NULL) { nlr_raise(mp_obj_new_exception(exc_type)); @@ -1691,6 +1708,8 @@ NORETURN void mp_raise_MpyError(const compressed_string_t *msg) { mp_raise_msg(&mp_type_MpyError, msg); } +#endif + #if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK NORETURN void mp_raise_recursion_depth(void) { mp_raise_RuntimeError(MP_ERROR_TEXT("maximum recursion depth exceeded")); diff --git a/py/runtime.h b/py/runtime.h index a8a3120eef..fc6434ca72 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -68,7 +68,8 @@ extern const byte mp_binary_op_method_name[]; void mp_init(void); void mp_deinit(void); -void mp_keyboard_interrupt(void); +void mp_sched_exception(mp_obj_t exc); +void mp_sched_keyboard_interrupt(void); void mp_handle_pending(bool raise_exc); void mp_handle_pending_tail(mp_uint_t atomic_state); @@ -90,6 +91,14 @@ void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, NORETURN void mp_arg_error_terse_mismatch(void); NORETURN void mp_arg_error_unimpl_kw(void); +mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name); +mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t j, qstr arg_name); +mp_int_t mp_arg_validate_int_range(mp_int_t i, mp_int_t min, mp_int_t max, qstr arg_name); +mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name); +size_t mp_arg_validate_length_with_name(mp_int_t i, size_t length, qstr arg_name, qstr length_name); +mp_obj_t mp_arg_validate_type(mp_obj_t obj, const mp_obj_type_t *type, qstr arg_name); +mp_obj_t mp_arg_validate_string(mp_obj_t obj, qstr arg_name); + static inline mp_obj_dict_t *PLACE_IN_ITCM(mp_locals_get)(void) { return MP_STATE_THREAD(dict_locals); } @@ -162,6 +171,17 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level); mp_obj_t mp_import_from(mp_obj_t module, qstr name); void mp_import_all(mp_obj_t module); +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE +NORETURN void mp_raise_type(const mp_obj_type_t *exc_type); +NORETURN void mp_raise_ValueError_no_msg(void); +NORETURN void mp_raise_TypeError_no_msg(void); +NORETURN void mp_raise_NotImplementedError_no_msg(void); +#define mp_raise_msg(exc_type, msg) mp_raise_type(exc_type) +#define mp_raise_msg_varg(exc_type, ...) mp_raise_type(exc_type) +#define mp_raise_ValueError(msg) mp_raise_ValueError_no_msg() +#define mp_raise_TypeError(msg) mp_raise_TypeError_no_msg() +#define mp_raise_NotImplementedError(msg) mp_raise_NotImplementedError_no_msg() +#else #define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) #if !(defined(MICROPY_ENABLE_DYNRUNTIME) && MICROPY_ENABLE_DYNRUNTIME) NORETURN void mp_raise_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg); @@ -191,6 +211,7 @@ NORETURN void mp_raise_NotImplementedError_varg(const compressed_string_t *fmt, NORETURN void mp_raise_OverflowError_varg(const compressed_string_t *fmt, ...); NORETURN void mp_raise_MpyError(const compressed_string_t *msg); NORETURN void mp_raise_recursion_depth(void); +#endif #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG #undef mp_check_self diff --git a/py/scheduler.c b/py/scheduler.c index 8329010417..f11317dc1d 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -28,17 +28,21 @@ #include "py/runtime.h" -#if MICROPY_KBD_EXCEPTION -// This function may be called asynchronously at any time so only do the bare minimum. -void MICROPY_WRAP_MP_KEYBOARD_INTERRUPT(mp_keyboard_interrupt)(void) { - MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; - MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); +void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { + MP_STATE_VM(mp_pending_exception) = exc; #if MICROPY_ENABLE_SCHEDULER if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } #endif } + +#if MICROPY_KBD_EXCEPTION +// This function may be called asynchronously at any time so only do the bare minimum. +void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) { + MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; + mp_sched_exception(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); +} #endif #if MICROPY_ENABLE_SCHEDULER @@ -130,6 +134,7 @@ bool MICROPY_WRAP_MP_SCHED_SCHEDULE(mp_sched_schedule)(mp_obj_t function, mp_obj uint8_t iput = IDX_MASK(MP_STATE_VM(sched_idx) + MP_STATE_VM(sched_len)++); MP_STATE_VM(sched_queue)[iput].func = function; MP_STATE_VM(sched_queue)[iput].arg = arg; + MICROPY_SCHED_HOOK_SCHEDULED; ret = true; } else { // schedule queue is full diff --git a/py/stackctrl.h b/py/stackctrl.h index 6b003f6c28..a3dfadb05a 100644 --- a/py/stackctrl.h +++ b/py/stackctrl.h @@ -40,7 +40,7 @@ void mp_stack_check(void); #else -#define mp_stack_set_limit(limit) +#define mp_stack_set_limit(limit) (void)(limit) #define MP_STACK_CHECK() #endif diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index 1c16cc7d87..8262830aee 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -190,7 +190,10 @@ const mp_obj_property_t bleio_adapter_name_obj = { MP_ROM_NONE }, }; -//| 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) -> None: +//| 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. //| @@ -206,13 +209,14 @@ const mp_obj_property_t bleio_adapter_name_obj = { //| :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 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 }; + 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} }, @@ -221,6 +225,7 @@ STATIC mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t { 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_OBJ_NULL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -252,8 +257,17 @@ STATIC mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t mp_raise_bleio_BluetoothError(translate("Cannot have scan responses for extended, connectable advertisements.")); } + bleio_address_obj_t *address = MP_OBJ_TO_PTR(args[ARG_directed_to].u_obj); + if (address != NULL && !connectable) { + mp_raise_bleio_BluetoothError(translate("Only connectable advertisements can be directed")); + } + + if (address != NULL && !mp_obj_is_type(address, &bleio_address_type)) { + mp_raise_TypeError(translate("Expected an Address")); + } + common_hal_bleio_adapter_start_advertising(self, connectable, anonymous, timeout, interval, - &data_bufinfo, &scan_response_bufinfo, args[ARG_tx_power].u_int); + &data_bufinfo, &scan_response_bufinfo, args[ARG_tx_power].u_int, address); return mp_const_none; } diff --git a/shared-bindings/_bleio/Adapter.h b/shared-bindings/_bleio/Adapter.h index 06dc47311b..1dce615a40 100644 --- a/shared-bindings/_bleio/Adapter.h +++ b/shared-bindings/_bleio/Adapter.h @@ -53,9 +53,17 @@ extern bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, blei 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, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len, mp_int_t tx_power); +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); +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); @@ -66,5 +74,6 @@ extern mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *se 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/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c index 3c9e189d48..b6b6ee69d4 100644 --- a/shared-bindings/_bleio/Characteristic.c +++ b/shared-bindings/_bleio/Characteristic.c @@ -45,7 +45,11 @@ //| ... //| -//| 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) -> Characteristic: +//| 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 @@ -65,6 +69,7 @@ //| :param bool fixed_length: True if the characteristic value is of fixed length. //| :param ~_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.""" //| ... @@ -73,7 +78,7 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ // 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_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 }, @@ -83,6 +88,7 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ { 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)]; @@ -132,6 +138,11 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ 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; @@ -140,7 +151,8 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ common_hal_bleio_characteristic_construct( characteristic, MP_OBJ_TO_PTR(service_obj), 0, MP_OBJ_TO_PTR(uuid_obj), properties, read_perm, write_perm, - max_length, fixed_length, &initial_value_bufinfo); + max_length, fixed_length, &initial_value_bufinfo, + user_description); return MP_OBJ_FROM_PTR(characteristic); } diff --git a/shared-bindings/_bleio/Characteristic.h b/shared-bindings/_bleio/Characteristic.h index 878d998a2d..349dd5fdcf 100644 --- a/shared-bindings/_bleio/Characteristic.h +++ b/shared-bindings/_bleio/Characteristic.h @@ -28,6 +28,7 @@ #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" @@ -43,7 +44,7 @@ extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characte 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); +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); diff --git a/shared-bindings/_bleio/CharacteristicBuffer.h b/shared-bindings/_bleio/CharacteristicBuffer.h index e82e96ca96..1fd9dd838c 100644 --- a/shared-bindings/_bleio/CharacteristicBuffer.h +++ b/shared-bindings/_bleio/CharacteristicBuffer.h @@ -31,7 +31,15 @@ extern const mp_obj_type_t bleio_characteristic_buffer_type; -extern 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); +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); diff --git a/shared-bindings/_bleio/Connection.c b/shared-bindings/_bleio/Connection.c index b416fac579..26b5c60fe1 100644 --- a/shared-bindings/_bleio/Connection.c +++ b/shared-bindings/_bleio/Connection.c @@ -205,7 +205,7 @@ STATIC mp_obj_t bleio_connection_get_connection_interval(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_connection_interval_obj, bleio_connection_get_connection_interval); -//| attribute: int +//| max_packet_length: int //| """The maximum number of data bytes that can be sent in a single transmission, //| not including overhead bytes. //| diff --git a/shared-bindings/_bleio/PacketBuffer.h b/shared-bindings/_bleio/PacketBuffer.h index b300cb0215..adead29b46 100644 --- a/shared-bindings/_bleio/PacketBuffer.h +++ b/shared-bindings/_bleio/PacketBuffer.h @@ -31,13 +31,20 @@ extern const mp_obj_type_t bleio_packet_buffer_type; -extern void common_hal_bleio_packet_buffer_construct( +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); -mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len, uint8_t *header, size_t header_len); +// 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); diff --git a/shared-bindings/_bleio/Service.h b/shared-bindings/_bleio/Service.h index b2a35793fb..eb1d29b57e 100644 --- a/shared-bindings/_bleio/Service.h +++ b/shared-bindings/_bleio/Service.h @@ -44,6 +44,6 @@ extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t * 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); +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/shared-bindings/adafruit_bus_device/I2CDevice.c b/shared-bindings/adafruit_bus_device/I2CDevice.c index ac760a490f..12c836396d 100644 --- a/shared-bindings/adafruit_bus_device/I2CDevice.c +++ b/shared-bindings/adafruit_bus_device/I2CDevice.c @@ -46,6 +46,7 @@ //| //| """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 @@ -114,6 +115,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(adafruit_bus_device_i2cdevice___exit_ //| If ``start`` or ``end`` is provided, then the buffer will be sliced //| as if ``buf[start:end]``. This will not cause an allocation like //| ``buf[start:end]`` will so it saves memory. +//| //| :param bytearray buf: buffer to write into //| :param int start: Index to start writing at //| :param int end: Index to write up to but not include; if None, use ``len(buf)``""" @@ -157,6 +159,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(adafruit_bus_device_i2cdevice_readinto_obj, 2, //| If ``start`` or ``end`` is provided, then the buffer will be sliced //| as if ``buffer[start:end]``. This will not cause an allocation like //| ``buffer[start:end]`` will so it saves memory. +//| //| :param bytearray buf: buffer containing the bytes to write //| :param int start: Index to start writing from //| :param int end: Index to read up to but not include; if None, use ``len(buf)`` @@ -208,6 +211,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(adafruit_bus_device_i2cdevice_write_obj, 2, adafruit_ //| will be sliced as if ``in_buffer[in_start:in_end]``. This will not //| cause an allocation like ``in_buffer[in_start:in_end]`` will so //| it saves memory. +//| //| :param bytearray out_buffer: buffer containing the bytes to write //| :param bytearray in_buffer: buffer containing the bytes to read into //| :param int out_start: Index to start writing from diff --git a/shared-bindings/adafruit_bus_device/SPIDevice.c b/shared-bindings/adafruit_bus_device/SPIDevice.c index 94901e4a15..a695a168ed 100644 --- a/shared-bindings/adafruit_bus_device/SPIDevice.c +++ b/shared-bindings/adafruit_bus_device/SPIDevice.c @@ -45,6 +45,7 @@ //| //| """ //| 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 int extra_clocks: The minimum number of clock cycles to cycle the bus after CS is high. (Used for SD cards.) diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index 56a6e1b8ae..d300956d0a 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -46,7 +46,7 @@ //| lines respectively. //| //| .. seealso:: Using this class directly requires careful lock management. -//| Instead, use :class:`~adafruit_bus_device.i2c_device.I2CDevice` to +//| Instead, use :class:`~adafruit_bus_device.I2CDevice` to //| manage locks. //| //| .. seealso:: Using this class to directly read registers requires manual @@ -56,7 +56,8 @@ //| :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 bitbangio.I2C; ignored for busio.I2C) +//| :param int timeout: The maximum clock stretching timeut - (used only for +//| :class:`bitbangio.I2C`; ignored for :class:`busio.I2C`) //| //| .. note:: On the nRF52840, only one I2C object may be created, //| except on the Circuit Playground Bluefruit, which allows two, @@ -239,7 +240,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(busio_i2c_readfrom_into_obj, 3, busio_i2c_readfrom_in //| to poll for the existence of a device. //| //| :param int address: 7-bit device address -//| :param ~_typing.ReadbleBuffer buffer: buffer containing the bytes to write +//| :param ~_typing.ReadableBuffer buffer: buffer containing the bytes to write //| :param int start: Index to start writing from //| :param int end: Index to read up to but not include. Defaults to ``len(buffer)``""" //| ... @@ -286,12 +287,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(busio_i2c_writeto_obj, 1, busio_i2c_writeto); //| 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 ``start`` or ``end`` is provided, then the corresponding buffer will be sliced +//| If ``start`` or ``end`` is provided, then the corresponding 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 ~_typing.ReadbleBuffer out_buffer: buffer containing the bytes to write +//| :param ~_typing.ReadableBuffer out_buffer: buffer containing the bytes to write //| :param ~_typing.WriteableBuffer in_buffer: buffer to write into //| :param int out_start: Index to start writing from //| :param int out_end: Index to read up to but not include. Defaults to ``len(buffer)`` diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 5a82ac0b41..31f3a9021e 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -65,7 +65,7 @@ //| peripherals. //| //| .. seealso:: Using this class directly requires careful lock management. -//| Instead, use :class:`~adafruit_bus_device.spi_device.SPIDevice` to +//| Instead, use :class:`~adafruit_bus_device.SPIDevice` to //| manage locks. //| //| .. seealso:: Using this class to directly read registers requires manual diff --git a/shared-bindings/displayio/Display.c b/shared-bindings/displayio/Display.c index 2825d5466a..e12e398802 100644 --- a/shared-bindings/displayio/Display.c +++ b/shared-bindings/displayio/Display.c @@ -54,7 +54,7 @@ //| 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, set_vertical_scroll: int = 0, 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) -> None: +//| 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, set_vertical_scroll: int = 0, 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 @@ -107,10 +107,11 @@ //| :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 SH1107_addressing: Special quirk for SH1107, use upper/lower column set and page set //| :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 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 +//| """ //| ... //| STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_args, diff --git a/shared-bindings/displayio/TileGrid.c b/shared-bindings/displayio/TileGrid.c index 42e9810b54..a0c5e3d816 100644 --- a/shared-bindings/displayio/TileGrid.c +++ b/shared-bindings/displayio/TileGrid.c @@ -56,7 +56,7 @@ //| tile_width and tile_height match the height of the bitmap by default. //| //| :param Bitmap bitmap: The bitmap storing one or more tiles. -//| :param ColorConverter or Palette pixel_shader: The pixel shader that produces colors from values +//| :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. diff --git a/shared-bindings/displayio/__init__.c b/shared-bindings/displayio/__init__.c index b39e72b946..dc4971de2a 100644 --- a/shared-bindings/displayio/__init__.c +++ b/shared-bindings/displayio/__init__.c @@ -70,6 +70,10 @@ MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565, DISPLAY 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); //| class Colorspace: //| """The colorspace for a `ColorConverter` to operate in""" @@ -95,6 +99,10 @@ MAKE_ENUM_MAP(displayio_colorspace) { 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), }; STATIC MP_DEFINE_CONST_DICT(displayio_colorspace_locals_dict, displayio_colorspace_locals_table); diff --git a/shared-bindings/displayio/__init__.h b/shared-bindings/displayio/__init__.h index 31af2ff2ba..938fcf3d98 100644 --- a/shared-bindings/displayio/__init__.h +++ b/shared-bindings/displayio/__init__.h @@ -46,6 +46,10 @@ typedef enum { 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_t; typedef bool (*display_bus_bus_reset)(mp_obj_t bus); diff --git a/shared-bindings/keypad/Event.c b/shared-bindings/keypad/Event.c new file mode 100644 index 0000000000..11cd0c120c --- /dev/null +++ b/shared-bindings/keypad/Event.c @@ -0,0 +1,180 @@ +/* + * 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" + +//| class Event: +//| """A key transition event.""" +//| def __init__(self, key_number: int=0, pressed: bool=True) -> 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. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_event_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_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 }; + 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_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 mp_uint_t key_number = + (mp_uint_t)mp_arg_validate_int_min(args[ARG_key_number].u_int, 0, MP_QSTR_key_number); + + common_hal_keypad_event_construct(self, key_number, args[ARG_pressed].u_bool); + 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); + +const mp_obj_property_t keypad_event_key_number_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_event_get_key_number_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| 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); + +const mp_obj_property_t keypad_event_pressed_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_event_get_pressed_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| 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); + +const mp_obj_property_t keypad_event_released_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_event_get_released_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| def __eq__(self, other: object) -> bool: +//| """Two `Event` objects are equal if their `key_number` +//| and `pressed`/`released` values are equal. +//| """ +//| ... +//| +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..""" +//| ... +//| +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); + 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, "", + 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) }, +}; +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 }, + .name = MP_QSTR_Event, + .make_new = keypad_event_make_new, + .print = keypad_event_print, + .unary_op = keypad_event_unary_op, + .binary_op = keypad_event_binary_op, + .locals_dict = (mp_obj_dict_t *)&keypad_event_locals_dict, +}; diff --git a/shared-bindings/keypad/Event.h b/shared-bindings/keypad/Event.h new file mode 100644 index 0000000000..35cfdcdc64 --- /dev/null +++ b/shared-bindings/keypad/Event.h @@ -0,0 +1,40 @@ +/* + * 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_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); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_EVENT__H diff --git a/shared-bindings/keypad/EventQueue.c b/shared-bindings/keypad/EventQueue.c new file mode 100644 index 0000000000..e47ee0772c --- /dev/null +++ b/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); + +const mp_obj_property_t keypad_eventqueue_overflowed_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_eventqueue_get_overflowed_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +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) }, +}; + +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 }, + .name = MP_QSTR_Keys, + .unary_op = keypad_eventqueue_unary_op, + .locals_dict = (mp_obj_t)&keypad_eventqueue_locals_dict, +}; diff --git a/shared-bindings/keypad/EventQueue.h b/shared-bindings/keypad/EventQueue.h new file mode 100644 index 0000000000..fbbd9df0cb --- /dev/null +++ b/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/shared-bindings/keypad/KeyMatrix.c b/shared-bindings/keypad/KeyMatrix.c new file mode 100644 index 0000000000..0b854ef475 --- /dev/null +++ b/shared-bindings/keypad/KeyMatrix.c @@ -0,0 +1,269 @@ +/* + * 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 "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.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, const mp_obj_t *pos_args, mp_map_t *kw_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(n_args, pos_args, kw_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); + + mcu_pin_obj_t *row_pins_array[num_row_pins]; + mcu_pin_obj_t *column_pins_array[num_column_pins]; + + for (size_t row = 0; row < num_row_pins; row++) { + 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++) { + 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_keymatrix_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. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keymatrix_reset(mp_obj_t self_in) { + keypad_keymatrix_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_keymatrix_reset(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_reset_obj, keypad_keymatrix_reset); + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_keymatrix_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_keymatrix_get_key_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_get_key_count_obj, keypad_keymatrix_get_key_count); + +const mp_obj_property_t keypad_keymatrix_key_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keymatrix_get_key_count_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| def key_number_to_row_column(self, row: int, column: 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_keymatrix_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 mp_obj_t keypad_keymatrix_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_keymatrix_get_events(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keymatrix_get_events_obj, keypad_keymatrix_get_events); + +const mp_obj_property_t keypad_keymatrix_events_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keymatrix_get_events_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +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_keymatrix_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_keymatrix_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_keymatrix_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/shared-bindings/keypad/KeyMatrix.h b/shared-bindings/keypad/KeyMatrix.h new file mode 100644 index 0000000000..7ebe1af5b7 --- /dev/null +++ b/shared-bindings/keypad/KeyMatrix.h @@ -0,0 +1,50 @@ +/* + * 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, mcu_pin_obj_t *row_pins[], mp_uint_t num_column_pins, 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); +bool common_hal_keypad_keymatrix_deinited(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_key_count(keypad_keymatrix_obj_t *self); +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); + +mp_obj_t common_hal_keypad_keymatrix_get_events(keypad_keymatrix_obj_t *self); +void common_hal_keypad_keymatrix_reset(keypad_keymatrix_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYMATRIX_H diff --git a/shared-bindings/keypad/Keys.c b/shared-bindings/keypad/Keys.c new file mode 100644 index 0000000000..d616bcff84 --- /dev/null +++ b/shared-bindings/keypad/Keys.c @@ -0,0 +1,209 @@ +/* + * 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 "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.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, const mp_obj_t *pos_args, mp_map_t *kw_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(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t pins = args[ARG_pins].u_obj; + // 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); + + mcu_pin_obj_t *pins_array[num_pins]; + + for (mp_uint_t i = 0; i < num_pins; i++) { + mcu_pin_obj_t *pin = + validate_obj_is_free_pin(mp_obj_subscr(pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL)); + pins_array[i] = pin; + } + + 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__); + +STATIC void check_for_deinit(keypad_keys_obj_t *self) { + if (common_hal_keypad_keys_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. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_keys_reset(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_keys_reset(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_reset_obj, keypad_keys_reset); + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_keys_get_key_count(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_keys_get_key_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_get_key_count_obj, keypad_keys_get_key_count); + +const mp_obj_property_t keypad_keys_key_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keys_get_key_count_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_keys_get_events(mp_obj_t self_in) { + keypad_keys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return common_hal_keypad_keys_get_events(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_keys_get_events_obj, keypad_keys_get_events); + +const mp_obj_property_t keypad_keys_events_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_keys_get_events_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +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_keys_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_keys_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_keys_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/shared-bindings/keypad/Keys.h b/shared-bindings/keypad/Keys.h new file mode 100644 index 0000000000..094220e588 --- /dev/null +++ b/shared-bindings/keypad/Keys.h @@ -0,0 +1,44 @@ +/* + * 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, 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); +bool common_hal_keypad_keys_deinited(keypad_keys_obj_t *self); + +mp_obj_t common_hal_keypad_keys_get_events(keypad_keys_obj_t *self); +size_t common_hal_keypad_keys_get_key_count(keypad_keys_obj_t *self); +void common_hal_keypad_keys_reset(keypad_keys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_KEYS_H diff --git a/shared-bindings/keypad/ShiftRegisterKeys.c b/shared-bindings/keypad/ShiftRegisterKeys.c new file mode 100644 index 0000000000..c81e127601 --- /dev/null +++ b/shared-bindings/keypad/ShiftRegisterKeys.c @@ -0,0 +1,213 @@ +/* + * 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 "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.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, const mp_obj_t *pos_args, mp_map_t *kw_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(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj); + mcu_pin_obj_t *data = validate_obj_is_free_pin(args[ARG_data].u_obj); + 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__); + +STATIC void check_for_deinit(keypad_shiftregisterkeys_obj_t *self) { + if (common_hal_keypad_shiftregisterkeys_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. +//| """ +//| ... +//| +STATIC mp_obj_t keypad_shiftregisterkeys_reset(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + common_hal_keypad_shiftregisterkeys_reset(self); + return MP_ROM_NONE; +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_reset_obj, keypad_shiftregisterkeys_reset); + +//| key_count: int +//| """The number of keys that are being scanned. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_shiftregisterkeys_get_key_count(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return MP_OBJ_NEW_SMALL_INT(common_hal_keypad_shiftregisterkeys_get_key_count(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_get_key_count_obj, keypad_shiftregisterkeys_get_key_count); + +const mp_obj_property_t keypad_shiftregisterkeys_key_count_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_shiftregisterkeys_get_key_count_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| events: EventQueue +//| """The `EventQueue` associated with this `Keys` object. (read-only) +//| """ +//| +STATIC mp_obj_t keypad_shiftregisterkeys_get_events(mp_obj_t self_in) { + keypad_shiftregisterkeys_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + return common_hal_keypad_shiftregisterkeys_get_events(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(keypad_shiftregisterkeys_get_events_obj, keypad_shiftregisterkeys_get_events); + +const mp_obj_property_t keypad_shiftregisterkeys_events_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&keypad_shiftregisterkeys_get_events_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +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_shiftregisterkeys_events_obj) }, + { MP_ROM_QSTR(MP_QSTR_key_count), MP_ROM_PTR(&keypad_shiftregisterkeys_key_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&keypad_shiftregisterkeys_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/shared-bindings/keypad/ShiftRegisterKeys.h b/shared-bindings/keypad/ShiftRegisterKeys.h new file mode 100644 index 0000000000..f1432f1069 --- /dev/null +++ b/shared-bindings/keypad/ShiftRegisterKeys.h @@ -0,0 +1,44 @@ +/* + * 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, mcu_pin_obj_t *clock_pin, mcu_pin_obj_t *data_pin, 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); +bool common_hal_keypad_shiftregisterkeys_deinited(keypad_shiftregisterkeys_obj_t *self); + +mp_obj_t common_hal_keypad_shiftregisterkeys_get_events(keypad_shiftregisterkeys_obj_t *self); +size_t common_hal_keypad_shiftregisterkeys_get_key_count(keypad_shiftregisterkeys_obj_t *self); +void common_hal_keypad_shiftregisterkeys_reset(keypad_shiftregisterkeys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_KEYPAD_SHIFTREGISTERKEYS_H diff --git a/shared-bindings/keypad/__init__.c b/shared-bindings/keypad/__init__.c new file mode 100644 index 0000000000..30d3dd49f0 --- /dev/null +++ b/shared-bindings/keypad/__init__.c @@ -0,0 +1,59 @@ +/* + * 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" + +//| """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_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_MUTABLE_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, +}; diff --git a/shared-bindings/keypad/__init__.h b/shared-bindings/keypad/__init__.h new file mode 100644 index 0000000000..5e484dc5ed --- /dev/null +++ b/shared-bindings/keypad/__init__.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 SHARED_BINDINGS_KEYPAD_H +#define SHARED_BINDINGS_KEYPAD_H + +#include "py/obj.h" +#include "py/objtuple.h" +#include "shared-module/keypad/__init__.h" + +extern mp_obj_tuple_t common_hal_keypad_devices; + +void keypad_set_devices(mp_obj_t devices); + +bool common_hal_keypad_disable(void); +bool common_hal_keypad_enable(const mp_obj_t devices_seq); + +#endif // SHARED_BINDINGS_KEYPAD_H diff --git a/shared-bindings/pulseio/PulseIn.c b/shared-bindings/pulseio/PulseIn.c index ebc8d450e3..4672c1e060 100644 --- a/shared-bindings/pulseio/PulseIn.c +++ b/shared-bindings/pulseio/PulseIn.c @@ -237,7 +237,7 @@ const mp_obj_property_t pulseio_pulsein_paused_obj = { //| def __bool__(self) -> bool: ... //| //| def __len__(self) -> int: -//| """Returns the current pulse length +//| """Returns the number of pulse durations currently stored. //| //| This allows you to:: //| diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index b3b00f68d5..3b56658d87 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -23,9 +23,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include + #include "py/obj.h" #include "py/runtime.h" #include "py/reload.h" +#include "py/objstr.h" #include "lib/utils/interrupt_char.h" #include "supervisor/shared/autoreload.h" @@ -112,6 +115,98 @@ STATIC mp_obj_t supervisor_set_next_stack_limit(mp_obj_t size_obj) { } 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); + 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) }, @@ -121,7 +216,7 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { { 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) }, }; STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table); diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index 5e40d2774a..03f4fac0dc 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -80,20 +80,21 @@ MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_obj, time_sleep); #if MICROPY_PY_COLLECTIONS mp_obj_t struct_time_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { - if (n_args != 1 || (kw_args != NULL && kw_args->used > 0)) { - return namedtuple_make_new(type, n_args, args, kw_args); - } + mp_arg_check_num(n_args, kw_args, 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, items, NULL); } //| class struct_time: -//| def __init__(self, time_tuple: Tuple[int, int, int, int, int, int, int, int, int]) -> None: -//| """Structure used to capture a date and time. Note that it takes a tuple! +//| 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 tuple time_tuple: Tuple of time info: ``(tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)`` +//| :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] diff --git a/shared-module/audiocore/RawSample.c b/shared-module/audiocore/RawSample.c index 1f1db0a509..e7d765e197 100644 --- a/shared-module/audiocore/RawSample.c +++ b/shared-module/audiocore/RawSample.c @@ -67,17 +67,17 @@ uint8_t common_hal_audioio_rawsample_get_channel_count(audioio_rawsample_obj_t * } void audioio_rawsample_reset_buffer(audioio_rawsample_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel) { } audioio_get_buffer_result_t audioio_rawsample_get_buffer(audioio_rawsample_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { *buffer_length = self->len; - if (single_channel) { + if (single_channel_output) { *buffer = self->buffer + (channel % self->channel_count) * (self->bits_per_sample / 8); } else { *buffer = self->buffer; @@ -85,13 +85,13 @@ audioio_get_buffer_result_t audioio_rawsample_get_buffer(audioio_rawsample_obj_t return GET_BUFFER_DONE; } -void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t *self, bool single_channel, +void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) { *single_buffer = true; *samples_signed = self->samples_signed; *max_buffer_length = self->len; - if (single_channel) { + if (single_channel_output) { *spacing = self->channel_count; } else { *spacing = 1; diff --git a/shared-module/audiocore/RawSample.h b/shared-module/audiocore/RawSample.h index 8208b4c280..10d395b8a3 100644 --- a/shared-module/audiocore/RawSample.h +++ b/shared-module/audiocore/RawSample.h @@ -44,14 +44,14 @@ typedef struct { // These are not available from Python because it may be called in an interrupt. void audioio_rawsample_reset_buffer(audioio_rawsample_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel); audioio_get_buffer_result_t audioio_rawsample_get_buffer(audioio_rawsample_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); // length in bytes -void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t *self, bool single_channel, +void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing); diff --git a/shared-module/audiocore/WaveFile.c b/shared-module/audiocore/WaveFile.c index 4864004d52..6d42d27ddb 100644 --- a/shared-module/audiocore/WaveFile.c +++ b/shared-module/audiocore/WaveFile.c @@ -169,9 +169,9 @@ uint32_t audioio_wavefile_max_buffer_length(audioio_wavefile_obj_t *self) { } void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel) { - if (single_channel && channel == 1) { + if (single_channel_output && channel == 1) { return; } // We don't reset the buffer index in case we're looping and we have an odd number of buffer @@ -184,11 +184,11 @@ void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t *self, } audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { - if (!single_channel) { + if (!single_channel_output) { channel = 0; } @@ -265,13 +265,14 @@ audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t * return self->bytes_remaining == 0 ? GET_BUFFER_DONE : GET_BUFFER_MORE_DATA; } -void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t *self, bool single_channel, +void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) { *single_buffer = false; + // In WAV files, 8-bit samples are always unsigned, and larger samples are always signed. *samples_signed = self->bits_per_sample > 8; *max_buffer_length = 512; - if (single_channel) { + if (single_channel_output) { *spacing = self->channel_count; } else { *spacing = 1; diff --git a/shared-module/audiocore/WaveFile.h b/shared-module/audiocore/WaveFile.h index 80b2fd3a56..986359e16e 100644 --- a/shared-module/audiocore/WaveFile.h +++ b/shared-module/audiocore/WaveFile.h @@ -57,14 +57,14 @@ typedef struct { // These are not available from Python because it may be called in an interrupt. void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel); audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); // length in bytes -void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t *self, bool single_channel, +void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing); diff --git a/shared-module/audiocore/__init__.c b/shared-module/audiocore/__init__.c index 605eac70b9..b85586851b 100644 --- a/shared-module/audiocore/__init__.c +++ b/shared-module/audiocore/__init__.c @@ -50,24 +50,24 @@ uint8_t audiosample_channel_count(mp_obj_t sample_obj) { return proto->channel_count(MP_OBJ_TO_PTR(sample_obj)); } -void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel) { +void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel_output, uint8_t audio_channel) { const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj); - proto->reset_buffer(MP_OBJ_TO_PTR(sample_obj), single_channel, audio_channel); + proto->reset_buffer(MP_OBJ_TO_PTR(sample_obj), single_channel_output, audio_channel); } audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj); - return proto->get_buffer(MP_OBJ_TO_PTR(sample_obj), single_channel, channel, buffer, buffer_length); + return proto->get_buffer(MP_OBJ_TO_PTR(sample_obj), single_channel_output, channel, buffer, buffer_length); } -void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, +void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) { const audiosample_p_t *proto = mp_proto_get_or_throw(MP_QSTR_protocol_audiosample, sample_obj); - proto->get_buffer_structure(MP_OBJ_TO_PTR(sample_obj), single_channel, single_buffer, + proto->get_buffer_structure(MP_OBJ_TO_PTR(sample_obj), single_channel_output, single_buffer, samples_signed, max_buffer_length, spacing); } diff --git a/shared-module/audiocore/__init__.h b/shared-module/audiocore/__init__.h index 69955a25f7..e57602519c 100644 --- a/shared-module/audiocore/__init__.h +++ b/shared-module/audiocore/__init__.h @@ -43,12 +43,12 @@ typedef uint32_t (*audiosample_sample_rate_fun)(mp_obj_t); typedef uint8_t (*audiosample_bits_per_sample_fun)(mp_obj_t); typedef uint8_t (*audiosample_channel_count_fun)(mp_obj_t); typedef void (*audiosample_reset_buffer_fun)(mp_obj_t, - bool single_channel, uint8_t audio_channel); + bool single_channel_output, uint8_t audio_channel); typedef audioio_get_buffer_result_t (*audiosample_get_buffer_fun)(mp_obj_t, - bool single_channel, uint8_t channel, uint8_t **buffer, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); typedef void (*audiosample_get_buffer_structure_fun)(mp_obj_t, - bool single_channel, bool *single_buffer, + bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing); @@ -65,12 +65,12 @@ typedef struct _audiosample_p_t { uint32_t audiosample_sample_rate(mp_obj_t sample_obj); uint8_t audiosample_bits_per_sample(mp_obj_t sample_obj); uint8_t audiosample_channel_count(mp_obj_t sample_obj); -void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel); +void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel_output, uint8_t audio_channel); audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); -void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, +void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing); diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index 20731933c4..d6569c66f0 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -94,7 +94,7 @@ bool common_hal_audiomixer_mixer_get_playing(audiomixer_mixer_obj_t *self) { } void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel) { for (uint8_t i = 0; i < self->voice_count; i++) { common_hal_audiomixer_mixervoice_stop(self->voice[i]); @@ -280,11 +280,11 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, } audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { - if (!single_channel) { + if (!single_channel_output) { channel = 0; } @@ -351,13 +351,13 @@ audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t * return GET_BUFFER_MORE_DATA; } -void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t *self, bool single_channel, +void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) { *single_buffer = false; *samples_signed = self->samples_signed; *max_buffer_length = self->len; - if (single_channel) { + if (single_channel_output) { *spacing = self->channel_count; } else { *spacing = 1; diff --git a/shared-module/audiomixer/Mixer.h b/shared-module/audiomixer/Mixer.h index ceb04772ec..c9228d9f13 100644 --- a/shared-module/audiomixer/Mixer.h +++ b/shared-module/audiomixer/Mixer.h @@ -55,14 +55,14 @@ typedef struct { // These are not available from Python because it may be called in an interrupt. void audiomixer_mixer_reset_buffer(audiomixer_mixer_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel); audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); // length in bytes -void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t *self, bool single_channel, +void audiomixer_mixer_get_buffer_structure(audiomixer_mixer_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing); diff --git a/shared-module/audiomp3/MP3Decoder.c b/shared-module/audiomp3/MP3Decoder.c index 2b623233e0..27c69f735b 100644 --- a/shared-module/audiomp3/MP3Decoder.c +++ b/shared-module/audiomp3/MP3Decoder.c @@ -295,9 +295,9 @@ bool audiomp3_mp3file_samples_signed(audiomp3_mp3file_obj_t *self) { } void audiomp3_mp3file_reset_buffer(audiomp3_mp3file_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel) { - if (single_channel && channel == 1) { + if (single_channel_output && channel == 1) { return; } // We don't reset the buffer index in case we're looping and we have an odd number of buffer @@ -314,14 +314,14 @@ void audiomp3_mp3file_reset_buffer(audiomp3_mp3file_obj_t *self, } audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **bufptr, uint32_t *buffer_length) { if (!self->inbuf) { return GET_BUFFER_ERROR; } - if (!single_channel) { + if (!single_channel_output) { channel = 0; } @@ -363,13 +363,13 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t * return GET_BUFFER_MORE_DATA; } -void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t *self, bool single_channel, +void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) { *single_buffer = false; *samples_signed = true; *max_buffer_length = self->frame_buffer_size; - if (single_channel) { + if (single_channel_output) { *spacing = self->channel_count; } else { *spacing = 1; diff --git a/shared-module/audiomp3/MP3Decoder.h b/shared-module/audiomp3/MP3Decoder.h index c1e6178465..0f2256a417 100644 --- a/shared-module/audiomp3/MP3Decoder.h +++ b/shared-module/audiomp3/MP3Decoder.h @@ -58,14 +58,14 @@ typedef struct { // These are not available from Python because it may be called in an interrupt. void audiomp3_mp3file_reset_buffer(audiomp3_mp3file_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel); audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); // length in bytes -void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t *self, bool single_channel, +void audiomp3_mp3file_get_buffer_structure(audiomp3_mp3file_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing); diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index 6de7fe7883..4871ce81a0 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -176,6 +176,28 @@ void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _d } break; + case DISPLAYIO_COLORSPACE_BGR565_SWAPPED: + pixel = __builtin_bswap16(pixel); + MP_FALLTHROUGH; + case DISPLAYIO_COLORSPACE_BGR565: { + uint32_t b8 = (pixel >> 11) << 3; + uint32_t g8 = ((pixel >> 5) << 2) & 0xff; + uint32_t r8 = (pixel << 3) & 0xff; + pixel = (r8 << 16) | (g8 << 8) | b8; + } + break; + + case DISPLAYIO_COLORSPACE_BGR555_SWAPPED: + pixel = __builtin_bswap16(pixel); + MP_FALLTHROUGH; + case DISPLAYIO_COLORSPACE_BGR555: { + uint32_t b8 = (pixel >> 10) << 3; + uint32_t g8 = ((pixel >> 5) << 3) & 0xff; + uint32_t r8 = (pixel << 3) & 0xff; + pixel = (r8 << 16) | (g8 << 8) | b8; + } + break; + case DISPLAYIO_COLORSPACE_RGB888: break; } diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index a5c4795a40..ec8ad7ba5a 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -258,6 +258,8 @@ static void _add_layer(displayio_group_t *self, mp_obj_t layer) { tilegrid->in_group = true; } displayio_tilegrid_update_transform(tilegrid, &self->absolute_transform); + displayio_tilegrid_set_hidden_by_parent( + tilegrid, self->hidden || self->hidden_by_parent); return; } native_layer = mp_obj_cast_to_native_base(layer, &displayio_group_type); @@ -269,6 +271,8 @@ static void _add_layer(displayio_group_t *self, mp_obj_t layer) { group->in_group = true; } displayio_group_update_transform(group, &self->absolute_transform); + displayio_group_set_hidden_by_parent( + group, self->hidden || self->hidden_by_parent); return; } mp_raise_ValueError(translate("Layer must be a Group or TileGrid subclass.")); diff --git a/shared-module/keypad/Event.c b/shared-module/keypad/Event.c new file mode 100644 index 0000000000..d938002cc5 --- /dev/null +++ b/shared-module/keypad/Event.c @@ -0,0 +1,44 @@ +/* + * 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-module/keypad/Event.h" + +void common_hal_keypad_event_construct(keypad_event_obj_t *self, mp_int_t key_number, bool pressed) { + self->key_number = key_number; + self->pressed = pressed; +} + +mp_int_t common_hal_keypad_event_get_key_number(keypad_event_obj_t *self) { + return self->key_number; +} + +bool common_hal_keypad_event_get_pressed(keypad_event_obj_t *self) { + return self->pressed; +} + +bool common_hal_keypad_event_get_released(keypad_event_obj_t *self) { + return !self->pressed; +} diff --git a/shared-module/keypad/Event.h b/shared-module/keypad/Event.h new file mode 100644 index 0000000000..3c47b0e636 --- /dev/null +++ b/shared-module/keypad/Event.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_MODULE_KEYPAD_EVENT_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENT_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint16_t key_number; + bool pressed; +} keypad_event_obj_t; + + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENT_H diff --git a/shared-module/keypad/EventQueue.c b/shared-module/keypad/EventQueue.c new file mode 100644 index 0000000000..5986a29ce8 --- /dev/null +++ b/shared-module/keypad/EventQueue.c @@ -0,0 +1,94 @@ +/* + * 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-bindings/keypad/Event.h" +#include "shared-module/keypad/EventQueue.h" + +// Key number is lower 15 bits of a 16-bit value. +#define EVENT_PRESSED (1 << 15) +#define EVENT_KEY_NUM_MASK ((1 << 15) - 1) + +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events) { + // Event queue is 16-bit values. + ringbuf_alloc(&self->encoded_events, max_events * 2, false); + self->overflowed = false; +} + +mp_obj_t common_hal_keypad_eventqueue_get(keypad_eventqueue_obj_t *self) { + int encoded_event = ringbuf_get16(&self->encoded_events); + if (encoded_event == -1) { + return MP_ROM_NONE; + } + + keypad_event_obj_t *event = m_new_obj(keypad_event_obj_t); + event->base.type = &keypad_event_type; + common_hal_keypad_event_construct(event, encoded_event & EVENT_KEY_NUM_MASK, encoded_event & EVENT_PRESSED); + return MP_OBJ_FROM_PTR(event); +} + +bool common_hal_keypad_eventqueue_get_into(keypad_eventqueue_obj_t *self, keypad_event_obj_t *event) { + int encoded_event = ringbuf_get16(&self->encoded_events); + if (encoded_event == -1) { + return false; + } + + // "Construct" using the existing event. + common_hal_keypad_event_construct(event, encoded_event & EVENT_KEY_NUM_MASK, encoded_event & EVENT_PRESSED); + return true; +} + +bool common_hal_keypad_eventqueue_get_overflowed(keypad_eventqueue_obj_t *self) { + return self->overflowed; +} + +void common_hal_keypad_eventqueue_set_overflowed(keypad_eventqueue_obj_t *self, bool overflowed) { + self->overflowed = overflowed; +} + +void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self) { + ringbuf_clear(&self->encoded_events); + common_hal_keypad_eventqueue_set_overflowed(self, false); +} + +size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self) { + return ringbuf_num_filled(&self->encoded_events); +} + +bool keypad_eventqueue_record(keypad_eventqueue_obj_t *self, mp_uint_t key_number, bool pressed) { + if (ringbuf_num_empty(&self->encoded_events) == 0) { + // Queue is full. Set the overflow flag. The caller will decide what else to do. + common_hal_keypad_eventqueue_set_overflowed(self, true); + return false; + } + + uint16_t encoded_event = key_number & EVENT_KEY_NUM_MASK; + if (pressed) { + encoded_event |= EVENT_PRESSED; + } + ringbuf_put16(&self->encoded_events, encoded_event); + + return true; +} diff --git a/shared-module/keypad/EventQueue.h b/shared-module/keypad/EventQueue.h new file mode 100644 index 0000000000..ce5e55ef3b --- /dev/null +++ b/shared-module/keypad/EventQueue.h @@ -0,0 +1,41 @@ +/* + * 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_MODULE_KEYPAD_EVENTQUEUE_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENTQUEUE_H + +#include "py/obj.h" +#include "py/ringbuf.h" + +typedef struct { + mp_obj_base_t base; + ringbuf_t encoded_events; + bool overflowed; +} keypad_eventqueue_obj_t; + +bool keypad_eventqueue_record(keypad_eventqueue_obj_t *self, mp_uint_t key_number, bool pressed); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_EVENTQUEUE_H diff --git a/shared-module/keypad/KeyMatrix.c b/shared-module/keypad/KeyMatrix.c new file mode 100644 index 0000000000..677a0b7142 --- /dev/null +++ b/shared-module/keypad/KeyMatrix.c @@ -0,0 +1,182 @@ +/* + * 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 + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/keypad/__init__.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" + +static mp_uint_t row_column_to_key_number(keypad_keymatrix_obj_t *self, mp_uint_t row, mp_uint_t column) { + return row * self->column_digitalinouts->len + column; +} + +void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint_t num_row_pins, mcu_pin_obj_t *row_pins[], mp_uint_t num_column_pins, mcu_pin_obj_t *column_pins[], bool columns_to_anodes, mp_float_t interval, size_t max_events) { + + mp_obj_t row_dios[num_row_pins]; + for (size_t row = 0; row < num_row_pins; row++) { + digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t); + dio->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(dio, row_pins[row]); + common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN); + row_dios[row] = dio; + } + self->row_digitalinouts = mp_obj_new_tuple(num_row_pins, row_dios); + + mp_obj_t column_dios[num_column_pins]; + for (size_t column = 0; column < num_column_pins; column++) { + digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t); + dio->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(dio, column_pins[column]); + common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN); + column_dios[column] = dio; + } + self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios); + + self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_column_pins, false, false); + self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * num_row_pins * num_column_pins, false, false); + + self->columns_to_anodes = columns_to_anodes; + + keypad_eventqueue_obj_t *events = m_new_obj(keypad_eventqueue_obj_t); + events->base.type = &keypad_eventqueue_type; + common_hal_keypad_eventqueue_construct(events, max_events); + self->events = events; + + self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) + self->last_scan_ticks = port_get_raw_ticks(NULL); + + // Add self to the list of active keypad scanners. + keypad_register_scanner((keypad_scanner_obj_t *)self); + + supervisor_enable_tick(); +} + +void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self) { + if (common_hal_keypad_keymatrix_deinited(self)) { + return; + } + + // Remove self from the list of active keypad scanners first. + keypad_deregister_scanner((keypad_scanner_obj_t *)self); + + for (size_t row = 0; row < common_hal_keypad_keymatrix_get_row_count(self); row++) { + common_hal_digitalio_digitalinout_deinit(self->row_digitalinouts->items[row]); + } + self->row_digitalinouts = MP_ROM_NONE; + + for (size_t column = 0; column < common_hal_keypad_keymatrix_get_column_count(self); column++) { + common_hal_digitalio_digitalinout_deinit(self->column_digitalinouts->items[column]); + } + self->column_digitalinouts = MP_ROM_NONE; +} + +bool common_hal_keypad_keymatrix_deinited(keypad_keymatrix_obj_t *self) { + return self->row_digitalinouts == MP_ROM_NONE; +} + +size_t common_hal_keypad_keymatrix_get_key_count(keypad_keymatrix_obj_t *self) { + return common_hal_keypad_keymatrix_get_row_count(self) * common_hal_keypad_keymatrix_get_column_count(self); +} + +size_t common_hal_keypad_keymatrix_get_row_count(keypad_keymatrix_obj_t *self) { + return self->row_digitalinouts->len; +} + +size_t common_hal_keypad_keymatrix_get_column_count(keypad_keymatrix_obj_t *self) { + return self->column_digitalinouts->len; +} + +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) { + return row_column_to_key_number(self, row, column); +} + +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) { + const size_t num_columns = common_hal_keypad_keymatrix_get_column_count(self); + *row = key_number / num_columns; + *column = key_number % num_columns; +} + +mp_obj_t common_hal_keypad_keymatrix_get_events(keypad_keymatrix_obj_t *self) { + return MP_OBJ_FROM_PTR(self->events); +} + +void common_hal_keypad_keymatrix_reset(keypad_keymatrix_obj_t *self) { + const size_t key_count = common_hal_keypad_keymatrix_get_key_count(self); + + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + memset(self->previously_pressed, false, key_count); + memset(self->currently_pressed, false, key_count); + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +void keypad_keymatrix_scan(keypad_keymatrix_obj_t *self) { + uint64_t now = port_get_raw_ticks(NULL); + if (now - self->last_scan_ticks < self->interval_ticks) { + // Too soon. Wait longer to debounce. + return; + } + + self->last_scan_ticks = now; + + // On entry, all pins are set to inputs with a pull-up or pull-down, + // depending on the diode orientation. + for (size_t row = 0; row < common_hal_keypad_keymatrix_get_row_count(self); row++) { + // Switch this row to an output and set level appropriately + // Set low if columns_to_anodes is true, else set high. + common_hal_digitalio_digitalinout_switch_to_output( + self->row_digitalinouts->items[row], !self->columns_to_anodes, DRIVE_MODE_PUSH_PULL); + + for (size_t column = 0; column < common_hal_keypad_keymatrix_get_column_count(self); column++) { + mp_uint_t key_number = row_column_to_key_number(self, row, column); + const bool previous = self->currently_pressed[key_number]; + self->previously_pressed[key_number] = previous; + + // Get the current state, by reading whether the column got pulled to the row value or not. + // If low and columns_to_anodes is true, the key is pressed. + // If high and columns_to_anodes is false, the key is pressed. + const bool current = + common_hal_digitalio_digitalinout_get_value(self->column_digitalinouts->items[column]) != + self->columns_to_anodes; + self->currently_pressed[key_number] = current; + + // Record any transitions. + if (previous != current) { + keypad_eventqueue_record(self->events, key_number, current); + } + } + + // Switch the row back to an input, pulled appropriately + common_hal_digitalio_digitalinout_switch_to_input( + self->row_digitalinouts->items[row], self->columns_to_anodes ? PULL_UP : PULL_DOWN); + } +} diff --git a/shared-module/keypad/KeyMatrix.h b/shared-module/keypad/KeyMatrix.h new file mode 100644 index 0000000000..bd7e2d23e2 --- /dev/null +++ b/shared-module/keypad/KeyMatrix.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_MODULE_KEYPAD_KEYMATRIX_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-module/keypad/__init__.h" +#include "shared-module/keypad/EventQueue.h" + +typedef struct { + mp_obj_base_t base; + // All scanners have a next field here, to keep a linked list of active scanners. + keypad_scanner_obj_t *next; + mp_obj_tuple_t *row_digitalinouts; + mp_obj_tuple_t *column_digitalinouts; + mp_uint_t interval_ticks; + uint64_t last_scan_ticks; + bool *previously_pressed; + bool *currently_pressed; + keypad_eventqueue_obj_t *events; + bool columns_to_anodes; +} keypad_keymatrix_obj_t; + +void keypad_keymatrix_scan(keypad_keymatrix_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYMATRIX_H diff --git a/shared-module/keypad/Keys.c b/shared-module/keypad/Keys.c new file mode 100644 index 0000000000..1f232a03ed --- /dev/null +++ b/shared-module/keypad/Keys.c @@ -0,0 +1,133 @@ +/* + * 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 + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/Keys.h" +#include "shared-bindings/keypad/__init__.h" +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" + +void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pins, mcu_pin_obj_t *pins[], bool value_when_pressed, bool pull, mp_float_t interval, size_t max_events) { + mp_obj_t dios[num_pins]; + + for (size_t i = 0; i < num_pins; i++) { + digitalio_digitalinout_obj_t *dio = m_new_obj(digitalio_digitalinout_obj_t); + dio->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(dio, pins[i]); + if (pull) { + common_hal_digitalio_digitalinout_set_pull(dio, value_when_pressed ? PULL_DOWN : PULL_UP); + } + dios[i] = dio; + } + + self->digitalinouts = mp_obj_new_tuple(num_pins, dios); + self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * num_pins, false, false); + self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * num_pins, false, false); + self->value_when_pressed = value_when_pressed; + + self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) + self->last_scan_ticks = port_get_raw_ticks(NULL); + + keypad_eventqueue_obj_t *events = m_new_obj(keypad_eventqueue_obj_t); + events->base.type = &keypad_eventqueue_type; + common_hal_keypad_eventqueue_construct(events, max_events); + self->events = events; + + // Add self to the list of active keypad scanners. + keypad_register_scanner((keypad_scanner_obj_t *)self); + + supervisor_enable_tick(); +} + +void common_hal_keypad_keys_deinit(keypad_keys_obj_t *self) { + if (common_hal_keypad_keys_deinited(self)) { + return; + } + + // Remove self from the list of active keypad scanners first. + keypad_deregister_scanner((keypad_scanner_obj_t *)self); + + for (size_t key = 0; key < common_hal_keypad_keys_get_key_count(self); key++) { + common_hal_digitalio_digitalinout_deinit(self->digitalinouts->items[key]); + } + self->digitalinouts = MP_ROM_NONE; + +} + +bool common_hal_keypad_keys_deinited(keypad_keys_obj_t *self) { + return self->digitalinouts == MP_ROM_NONE; +} + +size_t common_hal_keypad_keys_get_key_count(keypad_keys_obj_t *self) { + return self->digitalinouts->len; +} + +mp_obj_t common_hal_keypad_keys_get_events(keypad_keys_obj_t *self) { + return MP_OBJ_FROM_PTR(self->events); +} + +void common_hal_keypad_keys_reset(keypad_keys_obj_t *self) { + const size_t key_count = common_hal_keypad_keys_get_key_count(self); + + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + memset(self->previously_pressed, false, key_count); + memset(self->currently_pressed, false, key_count); + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +void keypad_keys_scan(keypad_keys_obj_t *self) { + uint64_t now = port_get_raw_ticks(NULL); + if (now - self->last_scan_ticks < self->interval_ticks) { + // Too soon. Wait longer to debounce. + return; + } + + self->last_scan_ticks = now; + + const size_t key_count = common_hal_keypad_keys_get_key_count(self); + + for (mp_uint_t key_number = 0; key_number < key_count; key_number++) { + // Remember the previous up/down state. + const bool previous = self->currently_pressed[key_number]; + self->previously_pressed[key_number] = previous; + + // Get the current state. + const bool current = + common_hal_digitalio_digitalinout_get_value(self->digitalinouts->items[key_number]) == + self->value_when_pressed; + self->currently_pressed[key_number] = current; + + // Record any transitions. + if (previous != current) { + keypad_eventqueue_record(self->events, key_number, current); + } + } +} diff --git a/shared-module/keypad/Keys.h b/shared-module/keypad/Keys.h new file mode 100644 index 0000000000..3edae32439 --- /dev/null +++ b/shared-module/keypad/Keys.h @@ -0,0 +1,52 @@ +/* + * 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_MODULE_KEYPAD_KEYS_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-module/keypad/__init__.h" +#include "shared-module/keypad/EventQueue.h" + +typedef struct { + mp_obj_base_t base; + // All scanners have a next field here, to keep a linked list of active scanners. + keypad_scanner_obj_t *next; + mp_obj_tuple_t *digitalinouts; + mp_uint_t interval_ticks; + uint64_t last_scan_ticks; + bool *previously_pressed; + bool *currently_pressed; + keypad_eventqueue_obj_t *events; + bool value_when_pressed; +} keypad_keys_obj_t; + +void keypad_keys_scan(keypad_keys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_KEYS_H diff --git a/shared-module/keypad/ShiftRegisterKeys.c b/shared-module/keypad/ShiftRegisterKeys.c new file mode 100644 index 0000000000..074f226998 --- /dev/null +++ b/shared-module/keypad/ShiftRegisterKeys.c @@ -0,0 +1,156 @@ +/* + * 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 + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/keypad/EventQueue.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" +#include "shared-bindings/keypad/__init__.h" +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" + +void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_t *self, mcu_pin_obj_t *clock_pin, mcu_pin_obj_t *data_pin, 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) { + + digitalio_digitalinout_obj_t *clock = m_new_obj(digitalio_digitalinout_obj_t); + clock->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(clock, clock_pin); + common_hal_digitalio_digitalinout_switch_to_output(clock, false, DRIVE_MODE_PUSH_PULL); + self->clock = clock; + + digitalio_digitalinout_obj_t *data = m_new_obj(digitalio_digitalinout_obj_t); + data->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(data, data_pin); + common_hal_digitalio_digitalinout_switch_to_input(data, PULL_NONE); + self->data = data; + + digitalio_digitalinout_obj_t *latch = m_new_obj(digitalio_digitalinout_obj_t); + latch->base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(latch, latch_pin); + common_hal_digitalio_digitalinout_switch_to_output(latch, true, DRIVE_MODE_PUSH_PULL); + self->latch = latch; + self->value_to_latch = value_to_latch; + + self->currently_pressed = (bool *)gc_alloc(sizeof(bool) * key_count, false, false); + self->previously_pressed = (bool *)gc_alloc(sizeof(bool) * key_count, false, false); + self->value_when_pressed = value_when_pressed; + self->key_count = key_count; + + self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) + self->last_scan_ticks = port_get_raw_ticks(NULL); + + keypad_eventqueue_obj_t *events = m_new_obj(keypad_eventqueue_obj_t); + events->base.type = &keypad_eventqueue_type; + common_hal_keypad_eventqueue_construct(events, max_events); + self->events = events; + + // Add self to the list of active keypad scanners. + keypad_register_scanner((keypad_scanner_obj_t *)self); + + supervisor_enable_tick(); +} + +void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self) { + if (common_hal_keypad_shiftregisterkeys_deinited(self)) { + return; + } + + // Remove self from the list of active keypad scanners first. + keypad_deregister_scanner((keypad_scanner_obj_t *)self); + + + common_hal_digitalio_digitalinout_deinit(self->clock); + self->clock = MP_ROM_NONE; + + common_hal_digitalio_digitalinout_deinit(self->data); + self->data = MP_ROM_NONE; + + common_hal_digitalio_digitalinout_deinit(self->latch); + self->latch = MP_ROM_NONE; +} + +bool common_hal_keypad_shiftregisterkeys_deinited(keypad_shiftregisterkeys_obj_t *self) { + return self->clock == MP_ROM_NONE; +} + +size_t common_hal_keypad_shiftregisterkeys_get_key_count(keypad_shiftregisterkeys_obj_t *self) { + return self->key_count; +} + +mp_obj_t common_hal_keypad_shiftregisterkeys_get_events(keypad_shiftregisterkeys_obj_t *self) { + return MP_OBJ_FROM_PTR(self->events); +} + +void common_hal_keypad_shiftregisterkeys_reset(keypad_shiftregisterkeys_obj_t *self) { + const size_t key_count = common_hal_keypad_shiftregisterkeys_get_key_count(self); + + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + memset(self->previously_pressed, false, key_count); + memset(self->currently_pressed, false, key_count); + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +void keypad_shiftregisterkeys_scan(keypad_shiftregisterkeys_obj_t *self) { + uint64_t now = port_get_raw_ticks(NULL); + if (now - self->last_scan_ticks < self->interval_ticks) { + // Too soon. Wait longer to debounce. + return; + } + + self->last_scan_ticks = now; + + // Latch (freeze) the current state of the input pins. + common_hal_digitalio_digitalinout_set_value(self->latch, self->value_to_latch); + + const size_t key_count = common_hal_keypad_shiftregisterkeys_get_key_count(self); + + for (mp_uint_t key_number = 0; key_number < key_count; key_number++) { + // Zero-th data appears on on the data pin immediately, without shifting. + common_hal_digitalio_digitalinout_set_value(self->clock, false); + + // Remember the previous up/down state. + const bool previous = self->currently_pressed[key_number]; + self->previously_pressed[key_number] = previous; + + // Get the current state. + const bool current = + common_hal_digitalio_digitalinout_get_value(self->data) == self->value_when_pressed; + self->currently_pressed[key_number] = current; + + // Trigger a shift to get the next bit. + common_hal_digitalio_digitalinout_set_value(self->clock, true); + + // Record any transitions. + if (previous != current) { + keypad_eventqueue_record(self->events, key_number, current); + } + } + + // Start reading the input pins again. + common_hal_digitalio_digitalinout_set_value(self->latch, !self->value_to_latch); +} diff --git a/shared-module/keypad/ShiftRegisterKeys.h b/shared-module/keypad/ShiftRegisterKeys.h new file mode 100644 index 0000000000..b04c622679 --- /dev/null +++ b/shared-module/keypad/ShiftRegisterKeys.h @@ -0,0 +1,56 @@ +/* + * 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_MODULE_KEYPAD_SHIFTREGISTERKEYS_H +#define MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_SHIFTREGISTERKEYS_H + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "common-hal/digitalio/DigitalInOut.h" +#include "shared-module/keypad/__init__.h" +#include "shared-module/keypad/EventQueue.h" + +typedef struct { + mp_obj_base_t base; + // All scanners have a next field here, to keep a linked list of active scanners. + keypad_scanner_obj_t *next; + digitalio_digitalinout_obj_t *clock; + digitalio_digitalinout_obj_t *data; + digitalio_digitalinout_obj_t *latch; + size_t key_count; + mp_uint_t interval_ticks; + uint64_t last_scan_ticks; + bool *previously_pressed; + bool *currently_pressed; + keypad_eventqueue_obj_t *events; + bool value_when_pressed; + bool value_to_latch; +} keypad_shiftregisterkeys_obj_t; + +void keypad_shiftregisterkeys_scan(keypad_shiftregisterkeys_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_KEYPAD_SHIFTREGISTERKEYS_H diff --git a/shared-module/keypad/__init__.c b/shared-module/keypad/__init__.c new file mode 100644 index 0000000000..fc41396617 --- /dev/null +++ b/shared-module/keypad/__init__.c @@ -0,0 +1,96 @@ +/* + * 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 "shared-bindings/keypad/Keys.h" +#include "shared-bindings/keypad/KeyMatrix.h" +#include "shared-bindings/keypad/ShiftRegisterKeys.h" +#include "supervisor/shared/lock.h" +#include "supervisor/shared/tick.h" + +supervisor_lock_t keypad_scanners_linked_list_lock; + +void keypad_tick(void) { + // Fast path. Return immediately there are no scanners. + if (!MP_STATE_VM(keypad_scanners_linked_list)) { + return; + } + + // Skip scanning if someone else has the lock. Don't wait for the lock. + if (supervisor_try_lock(&keypad_scanners_linked_list_lock)) { + mp_obj_t scanner = MP_STATE_VM(keypad_scanners_linked_list); + while (scanner) { + if (mp_obj_is_type(scanner, &keypad_keys_type)) { + keypad_keys_scan((keypad_keys_obj_t *)scanner); + } else if (mp_obj_is_type(scanner, &keypad_keymatrix_type)) { + keypad_keymatrix_scan((keypad_keymatrix_obj_t *)scanner); + } else if (mp_obj_is_type(scanner, &keypad_shiftregisterkeys_type)) { + keypad_shiftregisterkeys_scan((keypad_shiftregisterkeys_obj_t *)scanner); + } + + scanner = ((keypad_scanner_obj_t *)scanner)->next; + } + supervisor_release_lock(&keypad_scanners_linked_list_lock); + } +} + +void keypad_reset(void) { + if (MP_STATE_VM(keypad_scanners_linked_list)) { + supervisor_disable_tick(); + } + + MP_STATE_VM(keypad_scanners_linked_list) = NULL; + keypad_scanners_linked_list_lock = false; +} + +// Register a Keys, KeyMatrix, etc. that will be scanned in the background +void keypad_register_scanner(keypad_scanner_obj_t *scanner) { + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + scanner->next = MP_STATE_VM(keypad_scanners_linked_list); + MP_STATE_VM(keypad_scanners_linked_list) = scanner; + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} + +// Remove scanner from the list of active scanners. +void keypad_deregister_scanner(keypad_scanner_obj_t *scanner) { + supervisor_acquire_lock(&keypad_scanners_linked_list_lock); + if (MP_STATE_VM(keypad_scanners_linked_list) == scanner) { + // Scanner is at the front; splice it out. + MP_STATE_VM(keypad_scanners_linked_list) = scanner->next; + scanner->next = NULL; + } else { + keypad_scanner_obj_t *current = MP_STATE_VM(keypad_scanners_linked_list); + while (current) { + if (current->next == scanner) { + // Splice myself out. + current->next = scanner->next; + scanner->next = NULL; + break; + } + current = current->next; + } + } + supervisor_release_lock(&keypad_scanners_linked_list_lock); +} diff --git a/shared-module/keypad/__init__.h b/shared-module/keypad/__init__.h new file mode 100644 index 0000000000..c5b8fe3a0a --- /dev/null +++ b/shared-module/keypad/__init__.h @@ -0,0 +1,50 @@ +/* + * 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_MODULE_KEYPAD_H +#define SHARED_MODULE_KEYPAD_H + +#include "py/obj.h" +#include "supervisor/shared/lock.h" + +// All scanners must have a next field immediately following base. +// This is an ad hoc "superclass" struct for scanners, though they do +// not actually have a superclass relationship. +typedef struct _keypad_scanner_obj_t { + mp_obj_base_t base; + struct _keypad_scanner_obj_t *next; +} keypad_scanner_obj_t; + +extern supervisor_lock_t keypad_scanners_linked_list_lock; + +void keypad_tick(void); +void keypad_reset(void); + +void keypad_register_scanner(keypad_scanner_obj_t *scanner); +void keypad_deregister_scanner(keypad_scanner_obj_t *scanner); + + +#endif // SHARED_MODULE_KEYPAD_H diff --git a/shared-module/synthio/MidiTrack.c b/shared-module/synthio/MidiTrack.c index 0827945ab5..b2693a0c59 100644 --- a/shared-module/synthio/MidiTrack.c +++ b/shared-module/synthio/MidiTrack.c @@ -165,7 +165,7 @@ uint8_t common_hal_synthio_miditrack_get_channel_count(synthio_miditrack_obj_t * } void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self, - bool single_channel, uint8_t channel) { + bool single_channel_output, uint8_t channel) { self->next_span = 0; } @@ -174,7 +174,7 @@ STATIC const uint16_t notes[] = {8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544, 13290, 14080, 14917, 15804}; // 9th octave audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self, - bool single_channel, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { if (self->next_span >= self->total_spans) { *buffer_length = 0; @@ -202,7 +202,7 @@ audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t GET_BUFFER_DONE : GET_BUFFER_MORE_DATA; } -void synthio_miditrack_get_buffer_structure(synthio_miditrack_obj_t *self, bool single_channel, +void synthio_miditrack_get_buffer_structure(synthio_miditrack_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing) { *single_buffer = true; diff --git a/shared-module/synthio/MidiTrack.h b/shared-module/synthio/MidiTrack.h index bd058daaee..0af174ebc1 100644 --- a/shared-module/synthio/MidiTrack.h +++ b/shared-module/synthio/MidiTrack.h @@ -49,16 +49,16 @@ typedef struct { // These are not available from Python because it may be called in an interrupt. void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel); audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self, - bool single_channel, + bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); // length in bytes -void synthio_miditrack_get_buffer_structure(synthio_miditrack_obj_t *self, bool single_channel, +void synthio_miditrack_get_buffer_structure(synthio_miditrack_obj_t *self, bool single_channel_output, bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing); diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index b4d8667ca6..3ffb22cc3d 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -40,6 +40,10 @@ size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, uint32_t total_num_read = tud_cdc_n_read(self->idx, data, len); if (wait_forever || wait_for_timeout) { + // Continue filling the buffer past what we already read. + len -= total_num_read; + data += total_num_read; + // Read more if we have time. // Use special routine to avoid pulling in uint64-float-compatible math routines. uint64_t timeout_ms = float_to_uint64(self->timeout * 1000); // Junk value if timeout < 0. @@ -78,6 +82,10 @@ size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t tud_cdc_n_write_flush(self->idx); if (wait_forever || wait_for_timeout) { + // Continue writing the rest of the buffer. + len -= total_num_written; + data += total_num_written; + // Write more if we have time. // Use special routine to avoid pulling in uint64-float-compatible math routines. uint64_t timeout_ms = float_to_uint64(self->write_timeout * 1000); // Junk value if write_timeout < 0. diff --git a/supervisor/shared/autoreload.c b/supervisor/shared/autoreload.c index c61c4220d7..19b683fff7 100644 --- a/supervisor/shared/autoreload.c +++ b/supervisor/shared/autoreload.c @@ -30,6 +30,7 @@ #include "py/reload.h" #include "supervisor/shared/tick.h" +supervisor_allocation *next_code_allocation; #include "shared-bindings/supervisor/Runtime.h" static volatile uint32_t autoreload_delay_ms = 0; diff --git a/supervisor/shared/autoreload.h b/supervisor/shared/autoreload.h index fbd482c19a..41f9faea85 100644 --- a/supervisor/shared/autoreload.h +++ b/supervisor/shared/autoreload.h @@ -29,6 +29,24 @@ #include +#include "supervisor/memory.h" + +enum { + SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS = 0x1, + SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR = 0x2, + SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS = 0x4, + SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_ERROR = 0x8, + SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD = 0x10, + SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET = 0x20, +}; + +typedef struct { + uint8_t options; + char filename[]; +} next_code_info_t; + +extern supervisor_allocation *next_code_allocation; + extern volatile bool reload_requested; void autoreload_tick(void); diff --git a/supervisor/shared/bluetooth.c b/supervisor/shared/bluetooth.c deleted file mode 100644 index 00095c4f6b..0000000000 --- a/supervisor/shared/bluetooth.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * 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. - */ - -#if !CIRCUITPY_BLE_FILE_SERVICE -void supervisor_start_bluetooth(void) { -} - -void supervisor_bluetooth_background(void) { -} - -#else - -#include - -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" - -#include "shared-bindings/_bleio/__init__.h" -#include "shared-bindings/_bleio/Adapter.h" -#include "shared-bindings/_bleio/Characteristic.h" -#include "shared-bindings/_bleio/Service.h" -#include "shared-bindings/_bleio/UUID.h" - -#include "common-hal/_bleio/__init__.h" - -#include "supervisor/shared/autoreload.h" - -#include "py/mpstate.h" - - - -bleio_service_obj_t supervisor_ble_service; -bleio_uuid_obj_t supervisor_ble_service_uuid; -bleio_characteristic_obj_t supervisor_ble_version_characteristic; -bleio_uuid_obj_t supervisor_ble_version_uuid; -bleio_characteristic_obj_t supervisor_ble_filename_characteristic; -bleio_uuid_obj_t supervisor_ble_filename_uuid; -bleio_characteristic_obj_t supervisor_ble_length_characteristic; -bleio_uuid_obj_t supervisor_ble_length_uuid; -bleio_characteristic_obj_t supervisor_ble_contents_characteristic; -bleio_uuid_obj_t supervisor_ble_contents_uuid; - -// This is the base UUID for CircuitPython services and characteristics. -const uint8_t circuitpython_base_uuid[16] = {0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x00, 0xaf, 0xad }; -// This standard advertisement advertises the CircuitPython editing service and a CIRCUITPY short name. -uint8_t circuitpython_advertising_data[] = { 0x02, 0x01, 0x06, 0x02, 0x0a, 0x00, 0x11, 0x07, 0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x01, 0xaf, 0xad, 0x06, 0x08, 0x43, 0x49, 0x52, 0x43, 0x55 }; -// This scan response advertises the full CIRCUITPYXXXX device name. -uint8_t circuitpython_scan_response_data[15] = {0x0e, 0x09, 0x43, 0x49, 0x52, 0x43, 0x55, 0x49, 0x54, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00}; -mp_obj_list_t service_list; -mp_obj_t service_list_items[1]; -mp_obj_list_t characteristic_list; -mp_obj_t characteristic_list_items[4]; - -STATIC void supervisor_bluetooth_start_advertising(void) { - bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); - if (is_connected) { - return; - } - // TODO: switch to Adafruit short UUID for the advertisement and add manufacturing data to distinguish ourselves from arduino. - _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj, - true, - false, 0, - 1.0, - circuitpython_advertising_data, - sizeof(circuitpython_advertising_data), - circuitpython_scan_response_data, - sizeof(circuitpython_scan_response_data)); -} - -void supervisor_start_bluetooth(void) { - common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true); - - supervisor_ble_service_uuid.base.type = &bleio_uuid_type; - common_hal_bleio_uuid_construct(&supervisor_ble_service_uuid, 0x0100, circuitpython_base_uuid); - - // We know we'll only be 1 characteristic so we can statically allocate it. - characteristic_list.base.type = &mp_type_list; - characteristic_list.alloc = sizeof(characteristic_list_items) / sizeof(characteristic_list_items[0]); - characteristic_list.len = 0; - characteristic_list.items = characteristic_list_items; - mp_seq_clear(characteristic_list.items, 0, characteristic_list.alloc, sizeof(*characteristic_list.items)); - - _common_hal_bleio_service_construct(&supervisor_ble_service, &supervisor_ble_service_uuid, false /* is secondary */, &characteristic_list); - - // File length - supervisor_ble_version_uuid.base.type = &bleio_uuid_type; - common_hal_bleio_uuid_construct(&supervisor_ble_version_uuid, 0x0203, circuitpython_base_uuid); - common_hal_bleio_characteristic_construct(&supervisor_ble_version_characteristic, - &supervisor_ble_service, - 0, // handle (for remote only) - &supervisor_ble_version_uuid, - CHAR_PROP_READ, - SECURITY_MODE_OPEN, - SECURITY_MODE_NO_ACCESS, - 4, // max length - true, // fixed length - NULL); // no initial value - - uint32_t version = 1; - mp_buffer_info_t bufinfo; - bufinfo.buf = &version; - bufinfo.len = sizeof(version); - common_hal_bleio_characteristic_set_value(&supervisor_ble_version_characteristic, &bufinfo); - - // Active filename. - supervisor_ble_filename_uuid.base.type = &bleio_uuid_type; - common_hal_bleio_uuid_construct(&supervisor_ble_filename_uuid, 0x0200, circuitpython_base_uuid); - common_hal_bleio_characteristic_construct(&supervisor_ble_filename_characteristic, - &supervisor_ble_service, - 0, // handle (for remote only) - &supervisor_ble_filename_uuid, - CHAR_PROP_READ | CHAR_PROP_WRITE, - SECURITY_MODE_OPEN, - SECURITY_MODE_OPEN, - 500, // max length - false, // fixed length - NULL); // no initial value - - char code_py[] = "/code.py"; - bufinfo.buf = code_py; - bufinfo.len = sizeof(code_py); - common_hal_bleio_characteristic_set_value(&supervisor_ble_filename_characteristic, &bufinfo); - - // File length - supervisor_ble_length_uuid.base.type = &bleio_uuid_type; - common_hal_bleio_uuid_construct(&supervisor_ble_length_uuid, 0x0202, circuitpython_base_uuid); - common_hal_bleio_characteristic_construct(&supervisor_ble_length_characteristic, - &supervisor_ble_service, - 0, // handle (for remote only) - &supervisor_ble_length_uuid, - CHAR_PROP_NOTIFY | CHAR_PROP_READ, - SECURITY_MODE_OPEN, - SECURITY_MODE_NO_ACCESS, - 4, // max length - true, // fixed length - NULL); // no initial value - - // File actions - supervisor_ble_contents_uuid.base.type = &bleio_uuid_type; - common_hal_bleio_uuid_construct(&supervisor_ble_contents_uuid, 0x0201, circuitpython_base_uuid); - common_hal_bleio_characteristic_construct(&supervisor_ble_contents_characteristic, - &supervisor_ble_service, - 0, // handle (for remote only) - &supervisor_ble_contents_uuid, - CHAR_PROP_NOTIFY | CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_WRITE, - SECURITY_MODE_OPEN, - SECURITY_MODE_OPEN, - 500, // max length - false, // fixed length - NULL); // no initial value - - supervisor_bluetooth_start_advertising(); - vm_used_ble = false; -} - -FIL active_file; -volatile bool new_filename; -volatile bool run_ble_background; -bool was_connected; - -STATIC void update_file_length(void) { - int32_t file_length = -1; - mp_buffer_info_t bufinfo; - bufinfo.buf = &file_length; - bufinfo.len = sizeof(file_length); - if (active_file.obj.fs != 0) { - file_length = (int32_t)f_size(&active_file); - } - common_hal_bleio_characteristic_set_value(&supervisor_ble_length_characteristic, &bufinfo); -} - -STATIC void open_current_file(void) { - if (active_file.obj.fs != 0) { - return; - } - uint16_t max_len = supervisor_ble_filename_characteristic.max_length; - uint8_t path[max_len]; - size_t length = common_hal_bleio_characteristic_get_value(&supervisor_ble_filename_characteristic, path, max_len - 1); - path[length] = '\0'; - - FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; - f_open(fs, &active_file, (char *)path, FA_READ | FA_WRITE); - - update_file_length(); -} - -STATIC void close_current_file(void) { - f_close(&active_file); -} - -uint32_t current_command[1024 / sizeof(uint32_t)]; -volatile size_t current_offset; - -void supervisor_bluetooth_background(void) { - if (!run_ble_background) { - return; - } - bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); - if (!was_connected && is_connected) { - open_current_file(); - } else if (was_connected && !is_connected) { - close_current_file(); - new_filename = false; - } - was_connected = is_connected; - run_ble_background = false; - if (!is_connected) { - supervisor_bluetooth_start_advertising(); - return; - } - if (new_filename) { - close_current_file(); - open_current_file(); - - new_filename = false; - // get length and set the characteristic for it - } - uint16_t current_length = ((uint16_t *)current_command)[0]; - if (current_length > 0 && current_length == current_offset) { - uint16_t command = ((uint16_t *)current_command)[1]; - - if (command == 1) { - uint16_t max_len = 20; // supervisor_ble_contents_characteristic.max_length; - uint8_t buf[max_len]; - mp_buffer_info_t bufinfo; - bufinfo.buf = buf; - f_lseek(&active_file, 0); - while (f_read(&active_file, buf, max_len, &bufinfo.len) == FR_OK) { - if (bufinfo.len == 0) { - break; - } - common_hal_bleio_characteristic_set_value(&supervisor_ble_contents_characteristic, &bufinfo); - } - } else if (command == 2) { // patch - uint32_t offset = current_command[1]; - uint32_t remove_length = current_command[2]; - uint32_t insert_length = current_command[3]; - uint32_t file_length = (int32_t)f_size(&active_file); - // uint32_t data_shift_length = fileLength - offset - remove_length; - int32_t data_shift = insert_length - remove_length; - uint32_t new_length = file_length + data_shift; - - // TODO: Make these loops smarter to read and write on sector boundaries. - if (data_shift < 0) { - for (uint32_t shift_offset = offset + insert_length; shift_offset < new_length; shift_offset++) { - uint8_t data; - UINT actual; - f_lseek(&active_file, shift_offset - data_shift); - f_read(&active_file, &data, 1, &actual); - f_lseek(&active_file, shift_offset); - f_write(&active_file, &data, 1, &actual); - } - f_truncate(&active_file); - } else if (data_shift > 0) { - f_lseek(&active_file, file_length); - // Fill end with 0xff so we don't need to erase. - uint8_t data = 0xff; - for (size_t i = 0; i < (size_t)data_shift; i++) { - UINT actual; - f_write(&active_file, &data, 1, &actual); - } - for (uint32_t shift_offset = new_length - 1; shift_offset >= offset + insert_length; shift_offset--) { - UINT actual; - f_lseek(&active_file, shift_offset - data_shift); - f_read(&active_file, &data, 1, &actual); - f_lseek(&active_file, shift_offset); - f_write(&active_file, &data, 1, &actual); - } - } - - f_lseek(&active_file, offset); - uint8_t *data = (uint8_t *)(current_command + 4); - UINT written; - f_write(&active_file, data, insert_length, &written); - f_sync(&active_file); - // Notify the new file length. - update_file_length(); - - // Trigger an autoreload - autoreload_now(); - } - current_offset = 0; - } -} - -#endif // #else diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c new file mode 100644 index 0000000000..df57bf691d --- /dev/null +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -0,0 +1,235 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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 + +#include "supervisor/shared/bluetooth/bluetooth.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#if defined(CIRCUITPY_BOOT_BUTTON) +#include "shared-bindings/digitalio/DigitalInOut.h" +#endif +#include "shared-bindings/microcontroller/Processor.h" +#include "shared-bindings/microcontroller/ResetReason.h" +#include "shared-module/storage/__init__.h" + +#include "common-hal/_bleio/__init__.h" + +#include "supervisor/shared/status_leds.h" +#include "supervisor/shared/tick.h" + +#include "py/mpstate.h" + +#if CIRCUITPY_BLE_FILE_SERVICE +#include "supervisor/shared/bluetooth/file_transfer.h" +#include "bluetooth/ble_drv.h" +#endif + +#if CIRCUITPY_SERIAL_BLE +#include "supervisor/shared/bluetooth/serial.h" +#include "bluetooth/ble_drv.h" +#endif + +// This standard advertisement advertises the CircuitPython editing service and a CIRCUITPY short name. +const uint8_t public_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags + 0x02, 0x0a, 0xd8, // 3-5 TX power level -40 + #if CIRCUITPY_BLE_FILE_SERVICE + 0x03, 0x02, 0xbb, 0xfe, // 6 - 9 Incomplete service list (File Transfer service) + #endif + 0x0e, 0xff, 0x22, 0x08, // 10 - 13 Adafruit Manufacturer Data + 0x0a, 0x04, 0x00, // 14 - 16 Creator ID / Creation ID + CIRCUITPY_CREATOR_ID & 0xff, // 17 - 20 Creator ID + (CIRCUITPY_CREATOR_ID >> 8) & 0xff, + (CIRCUITPY_CREATOR_ID >> 16) & 0xff, + (CIRCUITPY_CREATOR_ID >> 24) & 0xff, + CIRCUITPY_CREATION_ID & 0xff, // 21 - 24 Creation ID + (CIRCUITPY_CREATION_ID >> 8) & 0xff, + (CIRCUITPY_CREATION_ID >> 16) & 0xff, + (CIRCUITPY_CREATION_ID >> 24) & 0xff, + 0x05, 0x08, 0x43, 0x49, 0x52, 0x43 // 25 - 31 - Short name +}; +const uint8_t private_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags + 0x02, 0x0a, 0x00 // 3-5 TX power level 0 +}; +// This scan response advertises the full CIRCPYXXXX device name. +uint8_t circuitpython_scan_response_data[] = { + 0x0a, 0x09, 0x43, 0x49, 0x52, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00, + #if CIRCUITPY_SERIAL_BLE + 0x11, 0x06, 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 0x01, 0x00, 0x40, 0x6e, + #endif +}; + +bool boot_in_discovery_mode = false; +bool advertising = false; + +STATIC void supervisor_bluetooth_start_advertising(void) { + #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE + return; + #else + bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); + if (is_connected) { + return; + } + bool bonded = common_hal_bleio_adapter_is_bonded_to_central(&common_hal_bleio_adapter_obj); + #if CIRCUITPY_USB + // Don't advertise when we have USB instead of BLE. + if (!bonded && !boot_in_discovery_mode) { + return; + } + #endif + uint32_t timeout = 0; + float interval = 0.1f; + int tx_power = 0; + const uint8_t *adv = private_advertising_data; + size_t adv_len = sizeof(private_advertising_data); + const uint8_t *scan_response = NULL; + size_t scan_response_len = 0; + // Advertise with less power when doing so publicly to reduce who can hear us. This will make it + // harder for someone with bad intentions to pair from a distance. + if (!bonded) { + tx_power = -40; + adv = public_advertising_data; + adv_len = sizeof(public_advertising_data); + scan_response = circuitpython_scan_response_data; + scan_response_len = sizeof(circuitpython_scan_response_data); + } + uint32_t status = _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj, + true, + bonded, // Advertise anonymously if we are bonded + timeout, + interval, + adv, + adv_len, + scan_response, + scan_response_len, + tx_power, + NULL); + // This may fail if we are already advertising. + advertising = status == NRF_SUCCESS; + #endif +} + +#define BLE_DISCOVERY_DATA_GUARD 0xbb0000bb +#define BLE_DISCOVERY_DATA_GUARD_MASK 0xff0000ff + +void supervisor_bluetooth_init(void) { + uint32_t reset_state = port_get_saved_word(); + uint32_t ble_mode = 0; + if ((reset_state & BLE_DISCOVERY_DATA_GUARD_MASK) == BLE_DISCOVERY_DATA_GUARD) { + ble_mode = (reset_state & ~BLE_DISCOVERY_DATA_GUARD_MASK) >> 8; + } + const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason(); + boot_in_discovery_mode = false; + if (reset_reason != RESET_REASON_POWER_ON && + reset_reason != RESET_REASON_RESET_PIN && + reset_reason != RESET_REASON_UNKNOWN && + reset_reason != RESET_REASON_SOFTWARE) { + return; + } + + if (ble_mode == 0) { + port_set_saved_word(BLE_DISCOVERY_DATA_GUARD | (0x01 << 8)); + } + // Wait for a while to allow for reset. + + #ifdef CIRCUITPY_BOOT_BUTTON + digitalio_digitalinout_obj_t boot_button; + common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); + common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); + #endif + uint64_t start_ticks = supervisor_ticks_ms64(); + uint64_t diff = 0; + if (ble_mode != 0) { + #ifdef CIRCUITPY_STATUS_LED + new_status_color(0x0000ff); + #endif + common_hal_bleio_adapter_erase_bonding(&common_hal_bleio_adapter_obj); + boot_in_discovery_mode = true; + reset_state = 0x0; + } + while (diff < 1000) { + #ifdef CIRCUITPY_STATUS_LED + // Blink on for 100, off for 100, on for 100, off for 100 and on for 200 + bool led_on = ble_mode != 0 || (diff % 150) <= 75; + if (led_on) { + new_status_color(0x0000ff); + } else { + new_status_color(BLACK); + } + #endif + #ifdef CIRCUITPY_BOOT_BUTTON + if (!common_hal_digitalio_digitalinout_get_value(&boot_button)) { + boot_in_discovery_mode = true; + break; + } + #endif + diff = supervisor_ticks_ms64() - start_ticks; + } + #if CIRCUITPY_STATUS_LED + new_status_color(BLACK); + status_led_deinit(); + #endif + port_set_saved_word(reset_state); +} + +STATIC bool was_connected; +void supervisor_bluetooth_background(void) { + bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); + if (was_connected && !is_connected) { + #if CIRCUITPY_BLE_FILE_SERVICE + supervisor_bluetooth_file_transfer_disconnected(); + #endif + } + was_connected = is_connected; + if (!is_connected) { + supervisor_bluetooth_start_advertising(); + return; + } + + #if CIRCUITPY_BLE_FILE_SERVICE + supervisor_bluetooth_file_transfer_background(); + #endif +} + +void supervisor_start_bluetooth(void) { + #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE + return; + #endif + + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true); + + #if CIRCUITPY_BLE_FILE_SERVICE + supervisor_start_bluetooth_file_transfer(); + #endif + + #if CIRCUITPY_SERIAL_BLE + supervisor_start_bluetooth_serial(); + #endif + + // Kick off advertisments + supervisor_bluetooth_background(); +} diff --git a/supervisor/shared/bluetooth.h b/supervisor/shared/bluetooth/bluetooth.h similarity index 96% rename from supervisor/shared/bluetooth.h rename to supervisor/shared/bluetooth/bluetooth.h index 55f9c86fa5..a8f3e8b159 100644 --- a/supervisor/shared/bluetooth.h +++ b/supervisor/shared/bluetooth/bluetooth.h @@ -27,7 +27,10 @@ #ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H #define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H +#include + void supervisor_bluetooth_background(void); +void supervisor_bluetooth_init(void); void supervisor_start_bluetooth(void); #endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H diff --git a/supervisor/shared/bluetooth/file_transfer.c b/supervisor/shared/bluetooth/file_transfer.c new file mode 100644 index 0000000000..3bd306c5a7 --- /dev/null +++ b/supervisor/shared/bluetooth/file_transfer.c @@ -0,0 +1,559 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 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 + +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-module/storage/__init__.h" + +#include "bluetooth/ble_drv.h" + +#include "common-hal/_bleio/__init__.h" + +#include "supervisor/shared/autoreload.h" +#include "supervisor/shared/bluetooth/file_transfer_protocol.h" +#include "supervisor/shared/tick.h" +#include "supervisor/usb.h" + +#include "py/mpstate.h" + +STATIC bleio_service_obj_t supervisor_ble_service; +STATIC bleio_uuid_obj_t supervisor_ble_service_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_version_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_version_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_transfer_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_transfer_uuid; + +// This is the base UUID for the file transfer service. +const uint8_t file_transfer_base_uuid[16] = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x00, 0xaf, 0xad }; + +STATIC mp_obj_list_t characteristic_list; +STATIC mp_obj_t characteristic_list_items[2]; +// 2 * 10 ringbuf packets, 512 for a disk sector and 12 for the file transfer write header. +#define PACKET_BUFFER_SIZE (2 * 10 + 512 + 12) +// uint32_t so its aligned +STATIC uint32_t _buffer[PACKET_BUFFER_SIZE / 4 + 1]; +STATIC uint32_t _outgoing1[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC uint32_t _outgoing2[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC ble_drv_evt_handler_entry_t static_handler_entry; +STATIC bleio_packet_buffer_obj_t _transfer_packet_buffer; + +void supervisor_start_bluetooth_file_transfer(void) { + supervisor_ble_service_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_service_uuid, 0xfebb, NULL); + + // We know we'll only be 1 characteristic so we can statically allocate it. + characteristic_list.base.type = &mp_type_list; + characteristic_list.alloc = sizeof(characteristic_list_items) / sizeof(characteristic_list_items[0]); + characteristic_list.len = 0; + characteristic_list.items = characteristic_list_items; + mp_seq_clear(characteristic_list.items, 0, characteristic_list.alloc, sizeof(*characteristic_list.items)); + + _common_hal_bleio_service_construct(&supervisor_ble_service, &supervisor_ble_service_uuid, false /* is secondary */, &characteristic_list); + + // Version number + supervisor_ble_version_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_version_uuid, 0x0100, file_transfer_base_uuid); + common_hal_bleio_characteristic_construct(&supervisor_ble_version_characteristic, + &supervisor_ble_service, + 0, // handle (for remote only) + &supervisor_ble_version_uuid, + CHAR_PROP_READ, + SECURITY_MODE_OPEN, + SECURITY_MODE_NO_ACCESS, + 4, // max length + true, // fixed length + NULL, // no initial value + NULL); // no description + + uint32_t version = 1; + mp_buffer_info_t bufinfo; + bufinfo.buf = &version; + bufinfo.len = sizeof(version); + common_hal_bleio_characteristic_set_value(&supervisor_ble_version_characteristic, &bufinfo); + + // Active filename. + supervisor_ble_transfer_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_transfer_uuid, 0x0200, file_transfer_base_uuid); + common_hal_bleio_characteristic_construct(&supervisor_ble_transfer_characteristic, + &supervisor_ble_service, + 0, // handle (for remote only) + &supervisor_ble_transfer_uuid, + CHAR_PROP_READ | CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_NOTIFY, + SECURITY_MODE_ENC_NO_MITM, + SECURITY_MODE_ENC_NO_MITM, + BLE_GATTS_VAR_ATTR_LEN_MAX, // max length + false, // fixed length + NULL, // no initial valuen + NULL); + + _common_hal_bleio_packet_buffer_construct( + &_transfer_packet_buffer, &supervisor_ble_transfer_characteristic, + _buffer, PACKET_BUFFER_SIZE, + _outgoing1, _outgoing2, BLE_GATTS_VAR_ATTR_LEN_MAX, + &static_handler_entry); +} + +#define COMMAND_SIZE 1024 + +#define ANY_COMMAND 0x00 +#define THIS_COMMAND 0x01 + +// Used by read and write. +STATIC FIL active_file; +STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) { + struct read_command *command = (struct read_command *)raw_buf; + size_t header_size = 12; + size_t response_size = 16; + uint8_t data_buffer[response_size]; + struct read_data response; + response.command = READ_DATA; + response.status = STATUS_OK; + if (command->path_length > (COMMAND_SIZE - response_size - 1)) { // -1 for the null we'll write + // TODO: throw away any more packets of path. + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0); + return ANY_COMMAND; + } + // We need to receive another packet to have the full path. + if (command_len < header_size + command->path_length) { + return THIS_COMMAND; + } + + char *path = (char *)((uint8_t *)command) + header_size; + path[command->path_length] = '\0'; + + FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FRESULT result = f_open(fs, &active_file, path, FA_READ); + if (result != FR_OK) { + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0); + return ANY_COMMAND; + } + uint32_t total_length = f_size(&active_file); + // Write out the response header. + uint32_t offset = command->chunk_offset; + uint32_t chunk_size = command->chunk_size; + chunk_size = MIN(chunk_size, total_length - offset); + response.chunk_offset = offset; + response.total_length = total_length; + response.data_size = chunk_size; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0); + f_lseek(&active_file, offset); + // Write out the chunk contents. We can do this in small pieces because PacketBuffer + // will assemble them into larger packets of its own. + size_t chunk_end = offset + chunk_size; + while (offset < chunk_end) { + size_t quantity_read; + size_t read_amount = MIN(response_size, chunk_end - offset); + f_read(&active_file, data_buffer, read_amount, &quantity_read); + offset += quantity_read; + // TODO: Do something if the read fails + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, data_buffer, quantity_read, NULL, 0); + } + if (offset >= total_length) { + f_close(&active_file); + return ANY_COMMAND; + } + return READ_PACING; +} + +STATIC uint8_t _process_read_pacing(const uint8_t *command, size_t command_len) { + size_t response_size = 4 * sizeof(uint32_t); + uint32_t response[response_size / sizeof(uint32_t)]; + uint8_t *response_bytes = (uint8_t *)response; + response_bytes[0] = READ_DATA; + response_bytes[1] = STATUS_OK; + uint32_t offset = ((uint32_t *)command)[1]; + uint32_t chunk_size = ((uint32_t *)command)[2]; + uint32_t total_length = f_size(&active_file); + // Write out the response header. + chunk_size = MIN(chunk_size, total_length - offset); + response[1] = offset; + response[2] = total_length; + response[3] = chunk_size; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0); + f_lseek(&active_file, offset); + // Write out the chunk contents. We can do this in small pieces because PacketBuffer + // will assemble them into larger packets of its own. + size_t chunk_offset = 0; + while (chunk_offset < chunk_size) { + size_t quantity_read; + size_t read_size = MIN(chunk_size - chunk_offset, response_size); + FRESULT result = f_read(&active_file, response, read_size, &quantity_read); + if (quantity_read == 0 || result != FR_OK) { + // TODO: If we can't read everything, then the file must have been shortened. Maybe we + // should return 0s to pad it out. + break; + } + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, quantity_read, NULL, 0); + chunk_offset += quantity_read; + } + if ((offset + chunk_size) >= total_length) { + f_close(&active_file); + return ANY_COMMAND; + } + return READ_PACING; +} + +// Used by write and write data to know when the write is complete. +STATIC size_t total_write_length; + +STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) { + struct write_command *command = (struct write_command *)raw_buf; + size_t header_size = 12; + struct write_pacing response; + response.command = WRITE_PACING; + response.status = STATUS_OK; + if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write + // TODO: throw away any more packets of path. + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0); + return ANY_COMMAND; + } + // We need to receive another packet to have the full path. + if (command_len < header_size + command->path_length) { + return THIS_COMMAND; + } + total_write_length = command->total_length; + + char *path = (char *)command->path; + path[command->path_length] = '\0'; + + // Check to see if USB has already been mounted. If not, then we "eject" from USB until we're done. + #if CIRCUITPY_USB_MSC + if (storage_usb_enabled() && !usb_msc_lock()) { + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0); + return ANY_COMMAND; + } + #endif + + FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + FRESULT result = f_open(fs, &active_file, path, FA_WRITE | FA_OPEN_ALWAYS); + if (result != FR_OK) { + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0); + #if CIRCUITPY_USB_MSC + usb_msc_unlock(); + #endif + return ANY_COMMAND; + } + // Write out the pacing response. + + // Align the next chunk to a sector boundary. + uint32_t offset = command->offset; + size_t chunk_size = MIN(total_write_length - offset, 512 - (offset % 512)); + // Special case when truncating the file. (Deleting stuff off the end.) + if (chunk_size == 0) { + f_lseek(&active_file, offset); + f_truncate(&active_file); + f_close(&active_file); + #if CIRCUITPY_USB_MSC + usb_msc_unlock(); + #endif + } + response.offset = offset; + response.free_space = chunk_size; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0); + if (chunk_size == 0) { + // Don't reload until everything is written out of the packet buffer. + common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer); + // Trigger an autoreload + autoreload_now(); + return ANY_COMMAND; + } + + return WRITE_DATA; +} + +STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) { + struct write_data *command = (struct write_data *)raw_buf; + size_t header_size = 12; + struct write_pacing response; + response.command = WRITE_PACING; + response.status = STATUS_OK; + if (command->data_size > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write + // TODO: throw away any more packets of path. + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0); + #if CIRCUITPY_USB_MSC + usb_msc_unlock(); + #endif + return ANY_COMMAND; + } + // We need to receive another packet to have the full path. + if (command_len < header_size + command->data_size) { + return THIS_COMMAND; + } + uint32_t offset = command->offset; + f_lseek(&active_file, offset); + UINT actual; + f_write(&active_file, command->data, command->data_size, &actual); + if (actual < command->data_size) { // -1 for the null we'll write + // TODO: throw away any more packets of path. + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0); + #if CIRCUITPY_USB_MSC + usb_msc_unlock(); + #endif + return ANY_COMMAND; + } + offset += command->data_size; + // Align the next chunk to a sector boundary. + size_t chunk_size = MIN(total_write_length - offset, 512); + response.offset = offset; + response.free_space = chunk_size; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0); + if (total_write_length == offset) { + f_truncate(&active_file); + f_close(&active_file); + #if CIRCUITPY_USB_MSC + usb_msc_unlock(); + #endif + // Don't reload until everything is written out of the packet buffer. + common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer); + // Trigger an autoreload + autoreload_now(); + return ANY_COMMAND; + } + return WRITE_DATA; +} + +STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) { + const struct delete_command *command = (struct delete_command *)raw_buf; + size_t header_size = 4; + struct delete_status response; + response.command = DELETE_STATUS; + response.status = STATUS_OK; + if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write + // TODO: throw away any more packets of path. + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_status), NULL, 0); + return ANY_COMMAND; + } + // We need to receive another packet to have the full path. + if (command_len < header_size + command->path_length) { + return THIS_COMMAND; + } + FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + char *path = (char *)((uint8_t *)command) + header_size; + path[command->path_length] = '\0'; + FRESULT result = f_unlink(fs, path); + if (result != FR_OK) { + response.status = STATUS_ERROR; + } + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_status), NULL, 0); + return ANY_COMMAND; +} + +STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) { + const struct mkdir_command *command = (struct mkdir_command *)raw_buf; + size_t header_size = 4; + struct mkdir_status response; + response.command = MKDIR_STATUS; + response.status = STATUS_OK; + if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write + // TODO: throw away any more packets of path. + response.status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_status), NULL, 0); + return ANY_COMMAND; + } + // We need to receive another packet to have the full path. + if (command_len < header_size + command->path_length) { + return THIS_COMMAND; + } + FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + char *path = (char *)((uint8_t *)command) + header_size; + // TODO: Check that the final character is a `/` + path[command->path_length - 1] = '\0'; + FRESULT result = f_mkdir(fs, path); + if (result != FR_OK) { + response.status = STATUS_ERROR; + } + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_status), NULL, 0); + return ANY_COMMAND; +} + +STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) { + const struct listdir_command *command = (struct listdir_command *)raw_buf; + struct listdir_entry *entry = (struct listdir_entry *)raw_buf; + size_t header_size = 4; + size_t response_size = 5 * sizeof(uint32_t); + // We reuse the command buffer so that we can produce long packets without + // making the stack large. + if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write + // TODO: throw away any more packets of path. + entry->command = LISTDIR_ENTRY; + entry->status = STATUS_ERROR; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)entry, response_size, NULL, 0); + return ANY_COMMAND; + } + // We need to receive another packet to have the full path. + if (command_len < header_size + command->path_length) { + return THIS_COMMAND; + } + + FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + char *path = (char *)command->path; + // -1 because fatfs doesn't want a trailing / + path[command->path_length - 1] = '\0'; + // mp_printf(&mp_plat_print, "list %s\n", path); + FF_DIR dir; + FRESULT res = f_opendir(fs, &dir, path); + + entry->command = LISTDIR_ENTRY; + entry->status = STATUS_OK; + entry->path_length = 0; + entry->entry_number = 0; + entry->entry_count = 0; + entry->flags = 0; + + if (res != FR_OK) { + entry->status = STATUS_ERROR_NO_FILE; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)entry, response_size, NULL, 0); + return ANY_COMMAND; + } + FILINFO file_info; + res = f_readdir(&dir, &file_info); + char *fn = file_info.fname; + size_t total_entries = 0; + while (res == FR_OK && fn[0] != 0) { + res = f_readdir(&dir, &file_info); + total_entries += 1; + } + // Rewind the directory. + f_readdir(&dir, NULL); + entry->entry_count = total_entries; + for (size_t i = 0; i < total_entries; i++) { + res = f_readdir(&dir, &file_info); + entry->entry_number = i; + if ((file_info.fattrib & AM_DIR) != 0) { + entry->flags = 1; // Directory + entry->file_size = 0; + } else { + entry->flags = 0; + entry->file_size = file_info.fsize; + } + + size_t name_length = strlen(file_info.fname); + entry->path_length = name_length; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)entry, response_size, NULL, 0); + size_t fn_offset = 0; + while (fn_offset < name_length) { + size_t fn_size = MIN(name_length - fn_offset, 4); + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, ((uint8_t *)file_info.fname) + fn_offset, fn_size, NULL, 0); + fn_offset += fn_size; + } + } + f_closedir(&dir); + entry->path_length = 0; + entry->entry_number = entry->entry_count; + entry->flags = 0; + entry->file_size = 0; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)entry, response_size, NULL, 0); + return ANY_COMMAND; +} + +// Background state that must live across background calls. After the _process +// helpers to force them to not use them. +STATIC uint8_t current_command[COMMAND_SIZE] __attribute__ ((aligned(4))); +STATIC volatile size_t current_offset; +STATIC uint8_t next_command; +STATIC bool running = false; +void supervisor_bluetooth_file_transfer_background(void) { + if (running) { + return; + } + running = true; + mp_int_t size = 1; + while (size > 0) { + size = common_hal_bleio_packet_buffer_readinto(&_transfer_packet_buffer, current_command + current_offset, COMMAND_SIZE - current_offset); + + if (size == 0) { + break; + } + // TODO: If size < 0 return an error. + current_offset += size; + // mp_printf(&mp_plat_print, "buffer[:%d]:", current_offset); + // for (size_t i = 0; i < current_offset; i++) { + // mp_printf(&mp_plat_print, " (%x %c)", current_command[i], current_command[i]); + // } + // mp_printf(&mp_plat_print, "\n"); + uint8_t current_state = current_command[0]; + // mp_printf(&mp_plat_print, "current command 0x%02x\n", current_state); + // Check for protocol error. + if (next_command != ANY_COMMAND && next_command != THIS_COMMAND && ((current_state & 0xf) != 0) && current_state != next_command) { + uint8_t response[2]; + response[0] = next_command; + response[1] = STATUS_ERROR_PROTOCOL; + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, response, 2, NULL, 0); + break; + } + switch (current_state) { + case READ: + next_command = _process_read(current_command, current_offset); + break; + case READ_PACING: + next_command = _process_read_pacing(current_command, current_offset); + break; + case WRITE: + next_command = _process_write(current_command, current_offset); + break; + case WRITE_DATA: + next_command = _process_write_data(current_command, current_offset); + break; + case DELETE: + next_command = _process_delete(current_command, current_offset); + break; + case MKDIR: + next_command = _process_mkdir(current_command, current_offset); + break; + case LISTDIR: + next_command = _process_listdir(current_command, current_offset); + break; + } + // Preserve the offset if we are waiting for more from this command. + if (next_command != THIS_COMMAND) { + current_offset = 0; + } + } + running = false; +} + +void supervisor_bluetooth_file_transfer_disconnected(void) { + next_command = ANY_COMMAND; + current_offset = 0; + f_close(&active_file); +} diff --git a/supervisor/shared/bluetooth/file_transfer.h b/supervisor/shared/bluetooth/file_transfer.h new file mode 100644 index 0000000000..e27924e7fa --- /dev/null +++ b/supervisor/shared/bluetooth/file_transfer.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H + +#include + +void supervisor_bluetooth_file_transfer_background(void); +void supervisor_start_bluetooth_file_transfer(void); +void supervisor_bluetooth_file_transfer_disconnected(void); + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H diff --git a/supervisor/shared/bluetooth/file_transfer_protocol.h b/supervisor/shared/bluetooth/file_transfer_protocol.h new file mode 100644 index 0000000000..2c8fe1e5e7 --- /dev/null +++ b/supervisor/shared/bluetooth/file_transfer_protocol.h @@ -0,0 +1,149 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H + +#include + +// See https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer +// for full protocol documentation and a Python client API. + +// 0x00 - 0x0f are never used by the protocol as a command +#define READ 0x10 +struct read_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint32_t chunk_offset; + uint32_t chunk_size; + uint8_t path[]; +}; + +#define READ_DATA 0x11 +struct read_data { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t chunk_offset; + uint32_t total_length; + uint32_t data_size; + uint8_t data[]; +}; + +#define READ_PACING 0x12 +struct read_pacing { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t chunk_offset; + uint32_t chunk_size; +}; + +#define WRITE 0x20 +struct write_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint32_t offset; + uint32_t total_length; + uint8_t path[]; +}; + +#define WRITE_PACING 0x21 +struct write_pacing { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t offset; + uint32_t free_space; +}; + +#define WRITE_DATA 0x22 +struct write_data { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t offset; + uint32_t data_size; + uint8_t data[]; +}; + +#define DELETE 0x30 +struct delete_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint8_t path[]; +}; + +#define DELETE_STATUS 0x31 +struct delete_status { + uint8_t command; + uint8_t status; +}; + +#define MKDIR 0x40 +struct mkdir_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint8_t path[]; +}; + +#define MKDIR_STATUS 0x41 +struct mkdir_status { + uint8_t command; + uint8_t status; +}; + +#define LISTDIR 0x50 +struct listdir_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint8_t path[]; +}; + +#define LISTDIR_ENTRY 0x51 +struct listdir_entry { + uint8_t command; + uint8_t status; + uint16_t path_length; + uint32_t entry_number; + uint32_t entry_count; + uint32_t flags; + uint32_t file_size; + uint8_t path[]; +}; + +#define STATUS_OK 0x01 +#define STATUS_ERROR 0x02 +#define STATUS_ERROR_NO_FILE 0x03 +#define STATUS_ERROR_PROTOCOL 0x04 + + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H diff --git a/supervisor/shared/bluetooth/serial.c b/supervisor/shared/bluetooth/serial.c new file mode 100644 index 0000000000..e1d26b89ce --- /dev/null +++ b/supervisor/shared/bluetooth/serial.c @@ -0,0 +1,153 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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 + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-module/storage/__init__.h" + +#include "common-hal/_bleio/__init__.h" + +#include "py/mpstate.h" + +STATIC bleio_service_obj_t supervisor_ble_serial_service; +STATIC bleio_uuid_obj_t supervisor_ble_serial_service_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_rx_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_rx_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_tx_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_tx_uuid; + +// This is the base UUID for the nordic uart service. +const uint8_t nordic_uart_base_uuid[16] = {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 0x00, 0x00, 0x40, 0x6e }; + +STATIC mp_obj_list_t characteristic_list; +STATIC mp_obj_t characteristic_list_items[2]; + +STATIC uint32_t _outgoing1[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC uint32_t _outgoing2[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC ble_drv_evt_handler_entry_t rx_static_handler_entry; +STATIC ble_drv_evt_handler_entry_t tx_static_handler_entry; +STATIC bleio_packet_buffer_obj_t _tx_packet_buffer; +STATIC uint32_t _incoming[64]; +STATIC bleio_characteristic_buffer_obj_t _rx_buffer; + +void supervisor_start_bluetooth_serial(void) { + supervisor_ble_serial_service_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_serial_service_uuid, 0x0001, nordic_uart_base_uuid); + + // We know we'll only be N characteristics so we can statically allocate it. + characteristic_list.base.type = &mp_type_list; + characteristic_list.alloc = sizeof(characteristic_list_items) / sizeof(characteristic_list_items[0]); + characteristic_list.len = 0; + characteristic_list.items = characteristic_list_items; + mp_seq_clear(characteristic_list.items, 0, characteristic_list.alloc, sizeof(*characteristic_list.items)); + + _common_hal_bleio_service_construct(&supervisor_ble_serial_service, &supervisor_ble_serial_service_uuid, false /* is secondary */, &characteristic_list); + + // RX + supervisor_ble_rx_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_rx_uuid, 0x0002, nordic_uart_base_uuid); + common_hal_bleio_characteristic_construct(&supervisor_ble_rx_characteristic, + &supervisor_ble_serial_service, + 0, // handle (for remote only) + &supervisor_ble_rx_uuid, + CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE, + SECURITY_MODE_NO_ACCESS, + SECURITY_MODE_ENC_NO_MITM, + BLE_GATTS_VAR_ATTR_LEN_MAX, // max length + false, // fixed length + NULL, // no initial value + "CircuitPython Serial"); + + // TX + supervisor_ble_tx_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_tx_uuid, 0x0003, nordic_uart_base_uuid); + common_hal_bleio_characteristic_construct(&supervisor_ble_tx_characteristic, + &supervisor_ble_serial_service, + 0, // handle (for remote only) + &supervisor_ble_tx_uuid, + CHAR_PROP_NOTIFY, + SECURITY_MODE_ENC_NO_MITM, + SECURITY_MODE_NO_ACCESS, + BLE_GATTS_VAR_ATTR_LEN_MAX, // max length + false, // fixed length + NULL, // no initial value + "CircuitPython Serial"); + + // Use a PacketBuffer to transmit so that we glom characters to transmit + // together and save BLE overhead. + _common_hal_bleio_packet_buffer_construct( + &_tx_packet_buffer, &supervisor_ble_tx_characteristic, + NULL, 0, + _outgoing1, _outgoing2, BLE_GATTS_VAR_ATTR_LEN_MAX, + &tx_static_handler_entry); + + // Use a CharacteristicBuffer for rx so we can read a single character at a time. + _common_hal_bleio_characteristic_buffer_construct(&_rx_buffer, + &supervisor_ble_rx_characteristic, + 0.1f, + (uint8_t *)_incoming, sizeof(_incoming) * sizeof(uint32_t), + &rx_static_handler_entry); +} + +bool ble_serial_connected(void) { + return _tx_packet_buffer.conn_handle != BLE_CONN_HANDLE_INVALID; +} + +bool ble_serial_available(void) { + return !common_hal_bleio_characteristic_buffer_deinited(&_rx_buffer) && common_hal_bleio_characteristic_buffer_rx_characters_available(&_rx_buffer); +} + +char ble_serial_read_char(void) { + if (common_hal_bleio_characteristic_buffer_deinited(&_rx_buffer)) { + return -1; + } + uint8_t c; + common_hal_bleio_characteristic_buffer_read(&_rx_buffer, &c, 1, NULL); + return c; +} + +void ble_serial_write(const char *text, size_t len) { + if (common_hal_bleio_packet_buffer_deinited(&_tx_packet_buffer)) { + return; + } + size_t sent = 0; + while (sent < len) { + uint16_t packet_size = MIN(len, (size_t)common_hal_bleio_packet_buffer_get_outgoing_packet_length(&_tx_packet_buffer)); + mp_int_t written = common_hal_bleio_packet_buffer_write(&_tx_packet_buffer, (const uint8_t *)text + sent, packet_size, NULL, 0); + // Error, so we drop characters to transmit. + if (written < 0) { + break; + } + sent += written; + } +} diff --git a/supervisor/shared/bluetooth/serial.h b/supervisor/shared/bluetooth/serial.h new file mode 100644 index 0000000000..b5bbcccf10 --- /dev/null +++ b/supervisor/shared/bluetooth/serial.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H + +#include + +void supervisor_start_bluetooth_serial(void); + +bool ble_serial_connected(void); +bool ble_serial_available(void); +char ble_serial_read_char(void); +void ble_serial_write(const char *text, size_t len); + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H diff --git a/supervisor/shared/lock.c b/supervisor/shared/lock.c new file mode 100644 index 0000000000..36971badad --- /dev/null +++ b/supervisor/shared/lock.c @@ -0,0 +1,51 @@ +/* + * 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 "shared-bindings/microcontroller/__init__.h" +#include "supervisor/shared/lock.h" + +void supervisor_acquire_lock(supervisor_lock_t *lock) { + while (!supervisor_try_lock(lock)) { + RUN_BACKGROUND_TASKS; + } +} + +bool supervisor_try_lock(supervisor_lock_t *lock) { + bool grabbed_lock = false; + common_hal_mcu_disable_interrupts(); + if (!*lock) { + *lock = true; + grabbed_lock = true; + } + common_hal_mcu_enable_interrupts(); + return grabbed_lock; +} + +void supervisor_release_lock(supervisor_lock_t *lock) { + common_hal_mcu_disable_interrupts(); + *lock = false; + common_hal_mcu_enable_interrupts(); +} diff --git a/ports/nrf/bluetooth/ble_uart.h b/supervisor/shared/lock.h similarity index 74% rename from ports/nrf/bluetooth/ble_uart.h rename to supervisor/shared/lock.h index d86e6293ae..89f81cfdfb 100644 --- a/ports/nrf/bluetooth/ble_uart.h +++ b/supervisor/shared/lock.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2017 Glenn Ruben Bakke + * 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 @@ -24,17 +24,13 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_UART_H -#define MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_UART_H +#ifndef MICROPY_INCLUDED_SUPERVISOR_LOCK_H +#define MICROPY_INCLUDED_SUPERVISOR_LOCK_H -#include +typedef volatile bool supervisor_lock_t; -#include "ble_drv.h" +void supervisor_acquire_lock(supervisor_lock_t *lock); +bool supervisor_try_lock(supervisor_lock_t *lock); +void supervisor_release_lock(supervisor_lock_t *lock); -void ble_uart_init(void); -bool ble_uart_connected(void); -char ble_uart_rx_chr(void); -bool ble_uart_stdin_any(void); -void ble_uart_stdout_tx_str(const char *text); - -#endif // MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_UART_H +#endif // MICROPY_INCLUDED_SUPERVISOR_LOCK_H diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c index d03b69e937..3cdaeff3bb 100644 --- a/supervisor/shared/memory.c +++ b/supervisor/shared/memory.c @@ -36,6 +36,8 @@ enum { CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT = // stack + heap 2 + // next_code_allocation + + 1 #if INTERNAL_FLASH_FILESYSTEM == 0 + 1 diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index e2a77a3f29..2b6e2c266b 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -28,7 +28,7 @@ #include "mphalport.h" -#if defined(MICROPY_HW_LED_STATUS) || defined(CIRCUITPY_BOOT_BUTTON) +#if defined(CIRCUITPY_BOOT_BUTTON) #include "shared-bindings/digitalio/DigitalInOut.h" #endif #include "shared-bindings/microcontroller/Processor.h" @@ -79,8 +79,8 @@ safe_mode_t wait_for_safe_mode_reset(void) { bool boot_in_safe_mode = false; while (diff < 1000) { #ifdef CIRCUITPY_STATUS_LED - // Blink on for 100, off for 100, on for 100, off for 100 and on for 200 - bool led_on = diff > 100 && diff / 100 != 2 && diff / 100 != 4; + // Blink on for 100, off for 100 + bool led_on = (diff % 250) < 125; if (led_on) { new_status_color(SAFE_MODE); } else { @@ -102,7 +102,8 @@ safe_mode_t wait_for_safe_mode_reset(void) { if (boot_in_safe_mode) { return USER_SAFE_MODE; } - port_set_saved_word(SAFE_MODE_DATA_GUARD); + // Restore the original state of the saved word if no reset occured during our wait period. + port_set_saved_word(reset_state); return NO_SAFE_MODE; } diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index f3a0d5a976..35feacb527 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -36,6 +36,10 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/usb_cdc/__init__.h" +#if CIRCUITPY_SERIAL_BLE +#include "supervisor/shared/bluetooth/serial.h" +#endif + #include "tusb.h" #ifdef NRF_DEBUG_PRINT @@ -93,11 +97,24 @@ bool serial_connected(void) { #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) return true; - #elif CIRCUITPY_USB_CDC - return usb_cdc_console_enabled() && tud_cdc_connected(); - #else - return tud_cdc_connected(); #endif + + #if CIRCUITPY_SERIAL_BLE + if (ble_serial_connected()) { + return true; + } + #endif + + #if CIRCUITPY_USB_CDC + if (usb_cdc_console_enabled() && tud_cdc_connected()) { + return true; + } + #else + if (tud_cdc_connected()) { + return true; + } + #endif + return false; } char serial_read(void) { @@ -117,7 +134,15 @@ char serial_read(void) { char text; common_hal_busio_uart_read(&debug_uart, (uint8_t *)&text, 1, &uart_errcode); return text; - #elif CIRCUITPY_USB_CDC + #endif + + #if CIRCUITPY_SERIAL_BLE + if (ble_serial_available() > 0) { + return ble_serial_read_char(); + } + #endif + + #if CIRCUITPY_USB_CDC if (!usb_cdc_console_enabled()) { return -1; } @@ -133,14 +158,29 @@ bool serial_bytes_available(void) { #endif #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) - return common_hal_busio_uart_rx_characters_available(&debug_uart) || (tud_cdc_available() > 0); - #elif CIRCUITPY_USB_CDC - if (!usb_cdc_console_enabled()) { - return 0; + if (common_hal_busio_uart_rx_characters_available(&debug_uart)) { + return true; } #endif - return tud_cdc_available() > 0; + + #if CIRCUITPY_SERIAL_BLE + if (ble_serial_available()) { + return true; + } + #endif + + #if CIRCUITPY_USB_CDC + if (usb_cdc_console_enabled() && tud_cdc_available() > 0) { + return true; + } + #else + if (tud_cdc_available() > 0) { + return true; + } + #endif + return false; } + void serial_write_substring(const char *text, uint32_t length) { if (length == 0) { return; @@ -156,6 +196,20 @@ void serial_write_substring(const char *text, uint32_t length) { } #endif + #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) + int uart_errcode; + + common_hal_busio_uart_write(&debug_uart, (const uint8_t *)text, length, &uart_errcode); + #endif + + #ifdef NRF_DEBUG_PRINT + _debug_print_substr(text, length); + #endif + + #if CIRCUITPY_SERIAL_BLE + ble_serial_write(text, length); + #endif + #if CIRCUITPY_USB_CDC if (!usb_cdc_console_enabled()) { return; @@ -171,17 +225,6 @@ void serial_write_substring(const char *text, uint32_t length) { } usb_background(); } - - #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) - int uart_errcode; - - common_hal_busio_uart_write(&debug_uart, (const uint8_t *)text, length, &uart_errcode); - #endif - - #ifdef NRF_DEBUG_PRINT - _debug_print_substr(text, length); - #endif - } void serial_write(const char *text) { diff --git a/supervisor/shared/tick.c b/supervisor/shared/tick.c index f40151988e..05b728826e 100644 --- a/supervisor/shared/tick.c +++ b/supervisor/shared/tick.c @@ -36,11 +36,6 @@ #include "supervisor/shared/autoreload.h" #include "supervisor/shared/stack.h" -#if CIRCUITPY_BLEIO -#include "supervisor/shared/bluetooth.h" -#include "common-hal/_bleio/__init__.h" -#endif - #if CIRCUITPY_DISPLAYIO #include "shared-module/displayio/__init__.h" #endif @@ -53,6 +48,10 @@ #include "shared-module/gamepadshift/__init__.h" #endif +#if CIRCUITPY_KEYPAD +#include "shared-module/keypad/__init__.h" +#endif + #if CIRCUITPY_NETWORK #include "shared-module/network/__init__.h" #endif @@ -86,11 +85,6 @@ void supervisor_background_tasks(void *unused) { #endif filesystem_background(); - #if CIRCUITPY_BLEIO - supervisor_bluetooth_background(); - bleio_background(); - #endif - port_background_task(); assert_heap_ok(); @@ -108,9 +102,11 @@ void supervisor_tick(void) { #if CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS > 0 filesystem_tick(); #endif + #ifdef CIRCUITPY_AUTORELOAD_DELAY_MS autoreload_tick(); #endif + #ifdef CIRCUITPY_GAMEPAD_TICKS if (!(port_get_raw_ticks(NULL) & CIRCUITPY_GAMEPAD_TICKS)) { #if CIRCUITPY_GAMEPAD @@ -121,6 +117,11 @@ void supervisor_tick(void) { #endif } #endif + + #if CIRCUITPY_KEYPAD + keypad_tick(); + #endif + background_callback_add(&tick_callback, supervisor_background_tasks, NULL); } diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 0e1c3472eb..9a276aa6cc 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -293,7 +293,7 @@ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { // Compare mp_interrupt_char with wanted_char and ignore if not matched if (mp_interrupt_char == wanted_char) { tud_cdc_n_read_flush(itf); // flush read fifo - mp_keyboard_interrupt(); + mp_sched_keyboard_interrupt(); } } diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index d2ecd7a1ec..fe88f8c831 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -34,6 +34,7 @@ #include "lib/oofatfs/ff.h" #include "py/mpstate.h" +#include "shared-module/storage/__init__.h" #include "supervisor/filesystem.h" #include "supervisor/shared/autoreload.h" @@ -41,16 +42,29 @@ static bool ejected[1] = {true}; -void usb_msc_mount(void) { - // Reset the ejection tracking every time we're plugged into USB. This allows for us to battery - // power the device, eject, unplug and plug it back in to get the drive. +// Lock to track if something else is using the filesystem when USB is plugged in. If so, the drive +// will be made available once the lock is released. +static bool _usb_msc_lock = false; +static bool _usb_connected_while_locked = false; + +STATIC void _usb_msc_uneject(void) { for (uint8_t i = 0; i < sizeof(ejected); i++) { ejected[i] = false; } } -void usb_msc_umount(void) { +void usb_msc_mount(void) { + // Reset the ejection tracking every time we're plugged into USB. This allows for us to battery + // power the device, eject, unplug and plug it back in to get the drive. + if (_usb_msc_lock) { + _usb_connected_while_locked = true; + return; + } + _usb_msc_uneject(); + _usb_connected_while_locked = false; +} +void usb_msc_umount(void) { } bool usb_msc_ejected(void) { @@ -61,6 +75,25 @@ bool usb_msc_ejected(void) { return all_ejected; } +bool usb_msc_lock(void) { + if ((storage_usb_enabled() && !usb_msc_ejected()) || _usb_msc_lock) { + return false; + } + _usb_msc_lock = true; + return true; +} + +void usb_msc_unlock(void) { + if (!_usb_msc_lock) { + // Mismatched unlock. + return; + } + if (_usb_connected_while_locked) { + _usb_msc_uneject(); + } + _usb_msc_lock = false; +} + // The root FS is always at the end of the list. static fs_user_mount_t *get_vfs(int lun) { // TODO(tannewt): Return the mount which matches the lun where 0 is the end diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 0815a9ffc4..0f53e1ebeb 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -7,7 +7,8 @@ SRC_SUPERVISOR = \ supervisor/shared/cpu.c \ supervisor/shared/filesystem.c \ supervisor/shared/flash.c \ - supervisor/shared/memory.c \ + supervisor/shared/lock.c \ + supervisor/shared/memory.c \ supervisor/shared/micropython.c \ supervisor/shared/safe_mode.c \ supervisor/shared/stack.c \ @@ -27,7 +28,17 @@ SPI_FLASH_FILESYSTEM ?= 0 CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM) ifeq ($(CIRCUITPY_BLEIO),1) - SRC_SUPERVISOR += supervisor/shared/bluetooth.c supervisor/bluetooth.c + SRC_SUPERVISOR += supervisor/shared/bluetooth/bluetooth.c + CIRCUITPY_CREATOR_ID ?= $(USB_VID) + CIRCUITPY_CREATION_ID ?= $(USB_PID) + CFLAGS += -DCIRCUITPY_CREATOR_ID=$(CIRCUITPY_CREATOR_ID) + CFLAGS += -DCIRCUITPY_CREATION_ID=$(CIRCUITPY_CREATION_ID) + ifeq ($(CIRCUITPY_BLE_FILE_SERVICE),1) + SRC_SUPERVISOR += supervisor/shared/bluetooth/file_transfer.c + endif + ifeq ($(CIRCUITPY_SERIAL_BLE),1) + SRC_SUPERVISOR += supervisor/shared/bluetooth/serial.c + endif endif # Choose which flash filesystem impl to use. diff --git a/supervisor/usb.h b/supervisor/usb.h index e0feb58ea8..19180ef758 100644 --- a/supervisor/usb.h +++ b/supervisor/usb.h @@ -78,6 +78,11 @@ void usb_setup_with_vm(void); void usb_msc_mount(void); void usb_msc_umount(void); bool usb_msc_ejected(void); + +// Locking MSC prevents presenting the drive on plug-in when in use by something +// else (likely BLE.) +bool usb_msc_lock(void); +void usb_msc_unlock(void); #endif #endif // MICROPY_INCLUDED_SUPERVISOR_USB_H diff --git a/tests/basics/array1.py b/tests/basics/array1.py index 5b3f475786..f21ad4bd75 100644 --- a/tests/basics/array1.py +++ b/tests/basics/array1.py @@ -41,14 +41,23 @@ except ValueError: # equality (CPython requires both sides are array) print(bytes(array.array('b', [0x61, 0x62, 0x63])) == b'abc') print(array.array('b', [0x61, 0x62, 0x63]) == b'abc') +print(array.array('B', [0x61, 0x62, 0x63]) == b'abc') print(array.array('b', [0x61, 0x62, 0x63]) != b'abc') print(array.array('b', [0x61, 0x62, 0x63]) == b'xyz') print(array.array('b', [0x61, 0x62, 0x63]) != b'xyz') print(b'abc' == array.array('b', [0x61, 0x62, 0x63])) +print(b'abc' == array.array('B', [0x61, 0x62, 0x63])) print(b'abc' != array.array('b', [0x61, 0x62, 0x63])) print(b'xyz' == array.array('b', [0x61, 0x62, 0x63])) print(b'xyz' != array.array('b', [0x61, 0x62, 0x63])) +compatible_typecodes = [] +for t in ["b", "h", "i", "l", "q"]: + compatible_typecodes.append((t, t)) + compatible_typecodes.append((t, t.upper())) +for a, b in compatible_typecodes: + print(array.array(a, [1, 2]) == array.array(b, [1, 2])) + class X(array.array): pass @@ -57,3 +66,24 @@ print(X('b', [0x61, 0x62, 0x63]) == b'abc') print(X('b', [0x61, 0x62, 0x63]) != b'abc') print(X('b', [0x61, 0x62, 0x63]) == array.array('b', [0x61, 0x62, 0x63])) print(X('b', [0x61, 0x62, 0x63]) != array.array('b', [0x61, 0x62, 0x63])) + +# other comparisons +for typecode in ["B", "H", "I", "L", "Q"]: + a = array.array(typecode, [1, 1]) + print(a < a) + print(a <= a) + print(a > a) + print(a >= a) + + al = array.array(typecode, [1, 0]) + ab = array.array(typecode, [1, 2]) + + print(a < al) + print(a <= al) + print(a > al) + print(a >= al) + + print(a < ab) + print(a <= ab) + print(a > ab) + print(a >= ab) diff --git a/tests/basics/array_micropython.py b/tests/basics/array_micropython.py index 6b3dc7a93b..44dc1d83d8 100644 --- a/tests/basics/array_micropython.py +++ b/tests/basics/array_micropython.py @@ -17,3 +17,15 @@ print(a[0]) a = array.array('P') a.append(1) print(a[0]) + +# comparison between mismatching binary layouts is not implemented +typecodes = ["b", "h", "i", "l", "q", "P", "O", "S", "f", "d"] +for a in typecodes: + for b in typecodes: + if a == b and a not in ["f", "d"]: + continue + try: + array.array(a) == array.array(b) + print('FAIL') + except NotImplementedError: + pass diff --git a/tests/basics/async_syntaxerror.py b/tests/basics/async_syntaxerror.py new file mode 100644 index 0000000000..ddd2c4b59e --- /dev/null +++ b/tests/basics/async_syntaxerror.py @@ -0,0 +1,19 @@ +# test syntax errors using async + +try: + exec +except NameError: + print("SKIP") + raise SystemExit + + +def test_syntax(code): + try: + exec(code) + print("no SyntaxError") + except SyntaxError: + print("SyntaxError") + + +test_syntax("async for x in (): x") +test_syntax("async with x: x") diff --git a/tests/basics/async_syntaxerror.py.exp b/tests/basics/async_syntaxerror.py.exp new file mode 100644 index 0000000000..5275689b41 --- /dev/null +++ b/tests/basics/async_syntaxerror.py.exp @@ -0,0 +1,2 @@ +SyntaxError +SyntaxError diff --git a/tests/basics/bytearray1.py b/tests/basics/bytearray1.py index b598500264..d12292e879 100644 --- a/tests/basics/bytearray1.py +++ b/tests/basics/bytearray1.py @@ -27,6 +27,26 @@ print(bytearray([1]) == b"1") print(b"1" == bytearray([1])) print(bytearray() == bytearray()) +b1 = bytearray([1, 2, 3]) +b2 = bytearray([1, 2, 3]) +b3 = bytearray([1, 3]) +print(b1 == b2) +print(b2 != b3) +print(b1 <= b2) +print(b1 <= b3) +print(b1 < b3) +print(b1 >= b2) +print(b3 >= b2) +print(b3 > b2) +print(b1 != b2) +print(b2 == b3) +print(b1 > b2) +print(b1 > b3) +print(b1 >= b3) +print(b1 < b2) +print(b3 < b2) +print(b3 <= b2) + # comparison with other type should return False print(bytearray() == 1) diff --git a/tests/basics/exception1.py b/tests/basics/exception1.py index d83764cb93..a0ca8a74e2 100644 --- a/tests/basics/exception1.py +++ b/tests/basics/exception1.py @@ -1,3 +1,5 @@ +# test basic properties of exceptions + print(repr(IndexError())) print(str(IndexError())) @@ -12,3 +14,6 @@ s = StopIteration() print(s.value) s = StopIteration(1, 2, 3) print(s.value) + +print(OSError().errno) +print(OSError(1, "msg").errno) diff --git a/tests/basics/is_isnot.py b/tests/basics/is_isnot.py index 990190aa41..55459cb0a6 100644 --- a/tests/basics/is_isnot.py +++ b/tests/basics/is_isnot.py @@ -1,14 +1,4 @@ -print(1 is 1) -print(1 is 2) -print(1 is not 1) -print(1 is not 2) - - print([1, 2] is [1, 2]) a = [1, 2] b = a print(b is a) - -# TODO: strings require special "is" handling, postponed -# until qstr refactor. -#print("a" is "a") diff --git a/tests/basics/is_isnot_literal.py b/tests/basics/is_isnot_literal.py new file mode 100644 index 0000000000..1626fa949f --- /dev/null +++ b/tests/basics/is_isnot_literal.py @@ -0,0 +1,13 @@ +# test "is" and "is not" with literal arguments +# these raise a SyntaxWarning in CPython because the results are +# implementation dependent; see https://bugs.python.org/issue34850 + +print(1 is 1) +print(1 is 2) +print(1 is not 1) +print(1 is not 2) + +print("a" is "a") +print("a" is "b") +print("a" is not "a") +print("a" is not "b") diff --git a/tests/basics/is_isnot_literal.py.exp b/tests/basics/is_isnot_literal.py.exp new file mode 100644 index 0000000000..fc4b8f8129 --- /dev/null +++ b/tests/basics/is_isnot_literal.py.exp @@ -0,0 +1,8 @@ +True +False +False +True +True +False +False +True diff --git a/tests/basics/memoryview_gc.py b/tests/basics/memoryview_gc.py index d366cbbb15..5cd6124ada 100644 --- a/tests/basics/memoryview_gc.py +++ b/tests/basics/memoryview_gc.py @@ -21,3 +21,13 @@ for i in range(100000): # check that the memoryview is still what we want print(list(m)) + +# check that creating a memoryview of a memoryview retains the underlying data +m = None +gc.collect() # cleanup from previous test +m = memoryview(memoryview(bytearray(i for i in range(50)))[5:-5]) +print(sum(m), list(m[:10])) +gc.collect() +for i in range(10): + list(range(10)) # allocate memory to overwrite any reclaimed heap +print(sum(m), list(m[:10])) diff --git a/tests/basics/op_error.py b/tests/basics/op_error.py index 63c35db3f5..98a3ab025a 100644 --- a/tests/basics/op_error.py +++ b/tests/basics/op_error.py @@ -29,18 +29,10 @@ except TypeError: print('TypeError') # unsupported subscription -try: - 1[0] -except TypeError: - print('TypeError') try: 1[0] = 1 except TypeError: print('TypeError') -try: - ''[''] -except TypeError: - print('TypeError') try: 'a'[0] = 1 except TypeError: @@ -50,12 +42,6 @@ try: except TypeError: print('TypeError') -# not callable -try: - 1() -except TypeError: - print('TypeError') - # not an iterator try: next(1) diff --git a/tests/basics/op_error_literal.py b/tests/basics/op_error_literal.py new file mode 100644 index 0000000000..00244ee2b2 --- /dev/null +++ b/tests/basics/op_error_literal.py @@ -0,0 +1,18 @@ +# test errors from bad operations with literals +# these raise a SyntaxWarning in CPython; see https://bugs.python.org/issue15248 + +# unsupported subscription +try: + 1[0] +except TypeError: + print("TypeError") +try: + ""[""] +except TypeError: + print("TypeError") + +# not callable +try: + 1() +except TypeError: + print("TypeError") diff --git a/tests/basics/op_error_literal.py.exp b/tests/basics/op_error_literal.py.exp new file mode 100644 index 0000000000..a8ea4fb10b --- /dev/null +++ b/tests/basics/op_error_literal.py.exp @@ -0,0 +1,3 @@ +TypeError +TypeError +TypeError diff --git a/tests/basics/subclass_native3.py b/tests/basics/subclass_native3.py index 6745b77bb2..ac5aabfed7 100644 --- a/tests/basics/subclass_native3.py +++ b/tests/basics/subclass_native3.py @@ -1,6 +1,10 @@ +# test subclassing a native exception + + class MyExc(Exception): pass + e = MyExc(100, "Some error") print(e) print(repr(e)) @@ -20,3 +24,19 @@ try: raise MyExc("Some error2") except: print("Caught user exception") + + +class MyStopIteration(StopIteration): + pass + + +print(MyStopIteration().value) +print(MyStopIteration(1).value) + + +class MyOSError(OSError): + pass + + +print(MyOSError().errno) +print(MyOSError(1, "msg").errno) diff --git a/tests/circuitpython-manual/alarm/deepsleep.py b/tests/circuitpython-manual/alarm/deepsleep.py new file mode 100644 index 0000000000..ab4f9ea70a --- /dev/null +++ b/tests/circuitpython-manual/alarm/deepsleep.py @@ -0,0 +1,81 @@ +import alarm +import board +import time +import digitalio +import neopixel + +## WAKING PINS - uncomment appropriate pin per microcontroller +wake_pin = board.X1 # STM32F4 Pyboard +# wake_pin = board.GP0 # RP2040 Pico +# wake_pin = board.A4 # NRF52840 Feather +# wake_pin = board.IO5 # ESP32-S2 Saola + +## LED - use on RP2040 Pico, STM32F4 Pyboard +## PinAlarms blink 1x, TimeAlarms 2x, Startup 3x +led_pin = board.LED +led = digitalio.DigitalInOut(led_pin) +led.direction = digitalio.Direction.OUTPUT + + +def blink(num_blinks): + for i in range(num_blinks): + led.value = True + time.sleep(0.2) + led.value = False + time.sleep(0.2) + + +def show_timealarm(): + blink(2) + + +def show_pinalarm(): + blink(1) + + +def show_noalarm(): + blink(3) + + +## Comment out above if using Neopixel + +## NEOPIXEL - use on Circuitplayground Bluefruit, ESP32-S2 Saola +## TimeAlarms are red, PinAlarms are blue, Default is white +# np = neopixel.NeoPixel(board.NEOPIXEL, 1) +# def show_timealarm(): +# np[0] = (50, 0, 0) +# time.sleep(1) +# np[0] = (0, 0, 0) +# def show_pinalarm(): +# np[0] = (0, 0, 50) +# time.sleep(1) +# np[0] = (0, 0, 0) +# def show_noalarm(): +# np[0] = (50, 50, 50) +# time.sleep(1) +# np[0] = (0, 0, 0) +## Comment out above if using LED + +## Show which alarm woke the chip +print("Wake alarm:") +print(alarm.wake_alarm) +if isinstance(alarm.wake_alarm, alarm.time.TimeAlarm): + show_timealarm() +elif isinstance(alarm.wake_alarm, alarm.pin.PinAlarm): + show_pinalarm() +else: + show_noalarm() + +## USB enumeration may take 4-5s per restart +time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) + +## Deep sleep pin alarms may only accept a single configuration. +pin_alarm = alarm.pin.PinAlarm( + pin=wake_pin, value=True, edge=True, pull=True +) # STM32 must be this exact config +# pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=False, pull=True) # NRF and ESP32S2 must use level, not edge +# pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=True, pull=True) # RP2040 supports any config + +alarm.exit_and_deep_sleep_until_alarms(time_alarm, pin_alarm) +# alarm.exit_and_deep_sleep_until_alarms(pin_alarm) # Using TimeAlarm will reduce performance on the RP2040 +# alarm.exit_and_deep_sleep_until_alarms(time_alarm) # Using PinAlarm will reduce performance on the ESP32-S2 diff --git a/tests/circuitpython-manual/alarm/esp/boot.py b/tests/circuitpython-manual/alarm/esp/boot.py new file mode 100644 index 0000000000..0b34adb7be --- /dev/null +++ b/tests/circuitpython-manual/alarm/esp/boot.py @@ -0,0 +1,11 @@ +import board +import digitalio +import storage + +switch = digitalio.DigitalInOut(board.IO10) + +switch.direction = digitalio.Direction.INPUT +switch.pull = digitalio.Pull.UP + +# If the switch pin is connected to ground CircuitPython can write to the drive +storage.remount("/", switch.value) diff --git a/tests/circuitpython-manual/alarm/esp/code.py b/tests/circuitpython-manual/alarm/esp/code.py new file mode 100644 index 0000000000..d4a577cbd9 --- /dev/null +++ b/tests/circuitpython-manual/alarm/esp/code.py @@ -0,0 +1,19 @@ +import alarm +import board +import time +import microcontroller +import storage + +temperature = microcontroller.cpu.temperature +print("Temperature:", temperature) + +try: + with open("/log.txt", "a") as sdc: + sdc.write("{}\n".format(temperature)) +except OSError as e: + print("Cannot write to fs, is IO10 grounded?") + +## USB enumeration may take 4-5s per restart +time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) + +alarm.exit_and_deep_sleep_until_alarms(time_alarm) diff --git a/tests/circuitpython-manual/alarm/lightsleep.py b/tests/circuitpython-manual/alarm/lightsleep.py new file mode 100644 index 0000000000..b2878db4bb --- /dev/null +++ b/tests/circuitpython-manual/alarm/lightsleep.py @@ -0,0 +1,77 @@ +import alarm +import board +import time +import digitalio +import neopixel + +## WAKING PINS - uncomment appropriate pin per microcontroller +wake_pin = board.X1 # STM32F4 Pyboard +# wake_pin = board.GP0 # RP2040 Pico +# wake_pin = board.A4 # NRF52840 Feather +# wake_pin = board.IO5 # ESP32-S2 Saola + +## LED - use on RP2040 Pico, STM32F4 Pyboard +## PinAlarms blink 1x, TimeAlarms 2x, Startup 3x +led_pin = board.LED +led = digitalio.DigitalInOut(led_pin) +led.direction = digitalio.Direction.OUTPUT + + +def blink(num_blinks): + for i in range(num_blinks): + led.value = True + time.sleep(0.2) + led.value = False + time.sleep(0.2) + + +def show_timealarm(): + blink(2) + + +def show_pinalarm(): + blink(1) + + +def show_noalarm(): + blink(3) + + +## Comment out above if using Neopixel + +## NEOPIXEL - use on Circuitplayground Bluefruit, ESP32-S2 Saola +## TimeAlarms are red, PinAlarms are blue, Default is white +# np = neopixel.NeoPixel(board.NEOPIXEL, 1) +# def show_timealarm(): +# np[0] = (50, 0, 0) +# time.sleep(1) +# np[0] = (0, 0, 0) +# def show_pinalarm(): +# np[0] = (0, 0, 50) +# time.sleep(1) +# np[0] = (0, 0, 0) +# def show_noalarm(): +# np[0] = (50, 50, 50) +# time.sleep(1) +# np[0] = (0, 0, 0) +## Comment out above if using LED + +## PinAlarm only needs to be set once +pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=True, pull=True) # STM32, RP2040 +# pin_alarm = alarm.pin.PinAlarm(pin=wake_pin, value=False, edge=False, pull=True) # NRF, ESP32-S2, RP2040 + +while True: + ## TimeAlarms must be reset each time you sleep, since they use monotonic time + time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) + ret_alarm = alarm.light_sleep_until_alarms(pin_alarm, time_alarm) + + print("Returned alarm vs global alarm:") # These should be the same + print(ret_alarm) + print(alarm.wake_alarm) + + if isinstance(ret_alarm, alarm.time.TimeAlarm): + show_timealarm() + elif isinstance(ret_alarm, alarm.pin.PinAlarm): + show_pinalarm() + else: + show_noalarm() diff --git a/tests/circuitpython-manual/alarm/nrf/code.py b/tests/circuitpython-manual/alarm/nrf/code.py new file mode 100644 index 0000000000..b4379dc79d --- /dev/null +++ b/tests/circuitpython-manual/alarm/nrf/code.py @@ -0,0 +1,29 @@ +import time +import adafruit_ble +from adafruit_ble_eddystone import uid, url +import alarm + +np = neopixel.NeoPixel(board.NEOPIXEL, 1) + +radio = adafruit_ble.BLERadio() +# Reuse the BLE address as our Eddystone instance id. +eddystone_uid = uid.EddystoneUID(radio.address_bytes) +eddystone_url = url.EddystoneURL("https://adafru.it/discord") + +while True: + np[0] = (50, 0, 0) + # Alternate between advertising our ID and our URL. + radio.start_advertising(eddystone_uid) + time.sleep(0.5) + radio.stop_advertising() + + radio.start_advertising(eddystone_url) + time.sleep(0.5) + radio.stop_advertising() + + ## USB enumeration may take 4-5s per restart + time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) + + np[0] = (0, 0, 0) + + alarm.exit_and_deep_sleep_until_alarms(time_alarm) diff --git a/tests/circuitpython-manual/alarm/rp/boot.py b/tests/circuitpython-manual/alarm/rp/boot.py new file mode 100644 index 0000000000..93a7a66040 --- /dev/null +++ b/tests/circuitpython-manual/alarm/rp/boot.py @@ -0,0 +1,11 @@ +import board +import digitalio +import storage + +switch = digitalio.DigitalInOut(board.GP4) + +switch.direction = digitalio.Direction.INPUT +switch.pull = digitalio.Pull.UP + +# If the switch pin is connected to ground CircuitPython can write to the drive +storage.remount("/", switch.value) diff --git a/tests/circuitpython-manual/alarm/rp/code.py b/tests/circuitpython-manual/alarm/rp/code.py new file mode 100644 index 0000000000..4a2c7e8085 --- /dev/null +++ b/tests/circuitpython-manual/alarm/rp/code.py @@ -0,0 +1,19 @@ +import alarm +import board +import time +import microcontroller +import storage + +temperature = microcontroller.cpu.temperature +print("Temperature:", temperature) + +try: + with open("/log.txt", "a") as sdc: + sdc.write("{}\n".format(temperature)) +except OSError as e: + print("Cannot write to fs, is GP4 grounded?") + +## USB enumeration may take 4-5s per restart +time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) + +alarm.exit_and_deep_sleep_until_alarms(time_alarm) diff --git a/tests/circuitpython-manual/alarm/stm/code.py b/tests/circuitpython-manual/alarm/stm/code.py new file mode 100644 index 0000000000..342e42f87e --- /dev/null +++ b/tests/circuitpython-manual/alarm/stm/code.py @@ -0,0 +1,29 @@ +import alarm +import board +import time +import microcontroller +import sdioio +import storage +import neopixel + +np = neopixel.NeoPixel(board.NEOPIXEL, 1) +np[0] = (50, 0, 0) + +# SD Card +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") + +with open("/sd/log.txt", "a") as sdc: + temperature = microcontroller.cpu.temperature + print("Temperature:", temperature) + sdc.write("{}\n".format(temperature)) + +## USB enumeration may take 4-5s per restart +time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 10) + +np[0] = (0, 0, 0) + +alarm.exit_and_deep_sleep_until_alarms(time_alarm) diff --git a/tests/cpydiff/core_function_moduleattr.py b/tests/cpydiff/core_function_moduleattr.py new file mode 100644 index 0000000000..71747c1f40 --- /dev/null +++ b/tests/cpydiff/core_function_moduleattr.py @@ -0,0 +1,13 @@ +""" +categories: Core,Functions +description: Function objects do not have the ``__module__`` attribute +cause: MicroPython is optimized for reduced code size and RAM usage. +workaround: Use ``sys.modules[function.__globals__['__name__']]`` for non-builtin modules. +""" + + +def f(): + pass + + +print(f.__module__) diff --git a/tests/cpydiff/module_array_comparison.py b/tests/cpydiff/module_array_comparison.py new file mode 100644 index 0000000000..a442af3f5b --- /dev/null +++ b/tests/cpydiff/module_array_comparison.py @@ -0,0 +1,9 @@ +""" +categories: Modules,array +description: Comparison between different typecodes not supported +cause: Code size +workaround: Compare individual elements +""" +import array + +array.array("b", [1, 2]) == array.array("i", [1, 2]) diff --git a/tests/cpydiff/module_array_constructor.py b/tests/cpydiff/module_array_constructor.py new file mode 100644 index 0000000000..08cf2ef2d1 --- /dev/null +++ b/tests/cpydiff/module_array_constructor.py @@ -0,0 +1,10 @@ +""" +categories: Modules,array +description: Overflow checking is not implemented +cause: MicroPython implements implicit truncation in order to reduce code size and execution time +workaround: If CPython compatibility is needed then mask the value explicitly +""" +import array + +a = array.array("b", [257]) +print(a) diff --git a/tests/cpydiff/modules_random_getrandbits.py b/tests/cpydiff/modules_random_getrandbits.py new file mode 100644 index 0000000000..523e3a329d --- /dev/null +++ b/tests/cpydiff/modules_random_getrandbits.py @@ -0,0 +1,12 @@ +""" +categories: Modules,random +description: ``getrandbits`` method can only return a maximum of 32 bits at a time. +cause: PRNG's internal state is only 32bits so it can only return a maximum of 32 bits of data at a time. +workaround: If you need a number that has more than 32 bits then utilize the random module from micropython-lib. +""" + +import random + + +x = random.getrandbits(64) +print("{}".format(x)) diff --git a/tests/cpydiff/modules_random_randint.py b/tests/cpydiff/modules_random_randint.py new file mode 100644 index 0000000000..b05908a157 --- /dev/null +++ b/tests/cpydiff/modules_random_randint.py @@ -0,0 +1,12 @@ +""" +categories: Modules,random +description: ``randint`` method can only return an integer that is at most the native word size. +cause: PRNG is only able to generate 32 bits of state at a time. The result is then cast into a native sized int instead of a full int object. +workaround: If you need integers larger than native wordsize use the random module from micropython-lib. +""" + +import random + + +x = random.randint(2 ** 128 - 1, 2 ** 128) +print("x={}".format(x)) diff --git a/tests/cpydiff/types_exception_attrs.py b/tests/cpydiff/types_exception_attrs.py new file mode 100644 index 0000000000..ad72b62a61 --- /dev/null +++ b/tests/cpydiff/types_exception_attrs.py @@ -0,0 +1,9 @@ +""" +categories: Types,Exception +description: All exceptions have readable ``value`` and ``errno`` attributes, not just ``StopIteration`` and ``OSError``. +cause: MicroPython is optimised to reduce code size. +workaround: Only use ``value`` on ``StopIteration`` exceptions, and ``errno`` on ``OSError`` exceptions. Do not use or rely on these attributes on other exceptions. +""" +e = Exception(1) +print(e.value) +print(e.errno) diff --git a/tests/cpydiff/types_int_bit_length.py b/tests/cpydiff/types_int_bit_length.py new file mode 100644 index 0000000000..2e907745a9 --- /dev/null +++ b/tests/cpydiff/types_int_bit_length.py @@ -0,0 +1,9 @@ +""" +categories: Types,int +description: ``bit_length`` method doesn't exist. +cause: bit_length method is not implemented. +workaround: Avoid using this method on MicroPython. +""" + +x = 255 +print("{} is {} bits long.".format(x, x.bit_length())) diff --git a/tests/extmod/btree1.py b/tests/extmod/btree1.py index 4890d92b42..1f4853eaa3 100644 --- a/tests/extmod/btree1.py +++ b/tests/extmod/btree1.py @@ -65,7 +65,7 @@ print(db.seq(1, b"qux")) try: db.seq(b"foo1") except OSError as e: - print(e.args[0] == uerrno.EINVAL) + print(e.errno == uerrno.EINVAL) print(list(db.keys())) print(list(db.values())) diff --git a/tests/extmod/btree_error.py b/tests/extmod/btree_error.py index 00e07ec8c7..b64769e884 100644 --- a/tests/extmod/btree_error.py +++ b/tests/extmod/btree_error.py @@ -27,7 +27,7 @@ class Device(uio.IOBase): try: db = btree.open(Device(), pagesize=511) except OSError as er: - print("OSError", er.args[0] == uerrno.EINVAL) + print("OSError", er.errno == uerrno.EINVAL) # Valid pagesize, device returns error on read; errno comes from Device.readinto try: diff --git a/tests/extmod/btree_gc.py b/tests/extmod/btree_gc.py index 153f4e7d78..1845aa0640 100644 --- a/tests/extmod/btree_gc.py +++ b/tests/extmod/btree_gc.py @@ -21,3 +21,6 @@ for i in range(N): db[b"thekey" + str(i)] = b"thelongvalue" + str(i) print(db[b"thekey" + str(i)]) gc.collect() + +# Reclaim memory allocated by the db object. +db.close() diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index e658f1345a..cd7f5ec015 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -1,9 +1,14 @@ try: - import framebuf + import framebuf, usys except ImportError: print("SKIP") raise SystemExit +# This test and its .exp file is based on a little-endian architecture. +if usys.byteorder != "little": + print("SKIP") + raise SystemExit + def printbuf(): print("--8<--") diff --git a/tests/extmod/framebuf_subclass.py b/tests/extmod/framebuf_subclass.py index aad5d2a1e9..a9e3c5efc7 100644 --- a/tests/extmod/framebuf_subclass.py +++ b/tests/extmod/framebuf_subclass.py @@ -1,11 +1,16 @@ # test subclassing framebuf.FrameBuffer try: - import framebuf + import framebuf, usys except ImportError: print("SKIP") raise SystemExit +# This test and its .exp file is based on a little-endian architecture. +if usys.byteorder != "little": + print("SKIP") + raise SystemExit + class FB(framebuf.FrameBuffer): def __init__(self, n): diff --git a/tests/extmod/uasyncio_cancel_wait_on_finished.py b/tests/extmod/uasyncio_cancel_wait_on_finished.py new file mode 100644 index 0000000000..66b36dd60d --- /dev/null +++ b/tests/extmod/uasyncio_cancel_wait_on_finished.py @@ -0,0 +1,41 @@ +# Test cancelling a task that is waiting on a task that just finishes. + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def sleep_task(): + print("sleep_task sleep") + await asyncio.sleep(0) + print("sleep_task wake") + + +async def wait_task(t): + print("wait_task wait") + await t + print("wait_task wake") + + +async def main(): + waiting_task = asyncio.create_task(wait_task(asyncio.create_task(sleep_task()))) + + print("main sleep") + await asyncio.sleep(0) + print("main sleep") + await asyncio.sleep(0) + + waiting_task.cancel() + print("main wait") + try: + await waiting_task + except asyncio.CancelledError as er: + print(repr(er)) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_cancel_wait_on_finished.py.exp b/tests/extmod/uasyncio_cancel_wait_on_finished.py.exp new file mode 100644 index 0000000000..60e871bfe5 --- /dev/null +++ b/tests/extmod/uasyncio_cancel_wait_on_finished.py.exp @@ -0,0 +1,7 @@ +main sleep +sleep_task sleep +wait_task wait +main sleep +sleep_task wake +main wait +CancelledError() diff --git a/tests/extmod/uctypes_le_float.py b/tests/extmod/uctypes_le_float.py index 89e9a9e0ab..5255632c91 100644 --- a/tests/extmod/uctypes_le_float.py +++ b/tests/extmod/uctypes_le_float.py @@ -22,3 +22,19 @@ print("%.4f" % S.f64) S.uf64 = 12.34 print("%.4f" % S.uf64) + +# array of float/double +desc = { + "af32": (uctypes.ARRAY | 0, uctypes.FLOAT32 | 2), + "af64": (uctypes.ARRAY | 0, uctypes.FLOAT64 | 2), +} +data = bytearray(16) +S = uctypes.struct(uctypes.addressof(data), desc, uctypes.LITTLE_ENDIAN) + +S.af32[0] = 1 +S.af32[1] = 2 +print("%.4f %.4f" % (S.af32[0], S.af32[1]), data) + +S.af64[0] = 1 +S.af64[1] = 2 +print("%.4f %.4f" % (S.af64[0], S.af64[1]), data) diff --git a/tests/extmod/uctypes_le_float.py.exp b/tests/extmod/uctypes_le_float.py.exp index a35a1da2dc..e93fcb0d97 100644 --- a/tests/extmod/uctypes_le_float.py.exp +++ b/tests/extmod/uctypes_le_float.py.exp @@ -1,3 +1,5 @@ 12.3400 12.3400 12.3400 +1.0000 2.0000 bytearray(b'\x00\x00\x80?\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00') +1.0000 2.0000 bytearray(b'\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@') diff --git a/tests/extmod/uctypes_sizeof_float.py b/tests/extmod/uctypes_sizeof_float.py index 351632d76b..f1a4c88e2d 100644 --- a/tests/extmod/uctypes_sizeof_float.py +++ b/tests/extmod/uctypes_sizeof_float.py @@ -6,3 +6,5 @@ except ImportError: print(uctypes.sizeof({"f": uctypes.FLOAT32})) print(uctypes.sizeof({"f": uctypes.FLOAT64})) +print(uctypes.sizeof({"f": (uctypes.ARRAY | 0, uctypes.FLOAT32 | 2)})) +print(uctypes.sizeof({"f": (uctypes.ARRAY | 0, uctypes.FLOAT64 | 2)})) diff --git a/tests/extmod/uctypes_sizeof_float.py.exp b/tests/extmod/uctypes_sizeof_float.py.exp index de78180725..82776b54ab 100644 --- a/tests/extmod/uctypes_sizeof_float.py.exp +++ b/tests/extmod/uctypes_sizeof_float.py.exp @@ -1,2 +1,4 @@ 4 8 +8 +16 diff --git a/tests/extmod/uhashlib_final.py b/tests/extmod/uhashlib_final.py new file mode 100644 index 0000000000..f562cc1780 --- /dev/null +++ b/tests/extmod/uhashlib_final.py @@ -0,0 +1,35 @@ +try: + import uhashlib +except ImportError: + print("SKIP") + raise SystemExit + + +for algo_name in ("md5", "sha1", "sha256"): + algo = getattr(uhashlib, algo_name, None) + if not algo: + continue + + # Running .digest() several times in row is not supported. + h = algo(b"123") + h.digest() + try: + h.digest() + print("fail") + except ValueError: + # Expected path, don't print anything so test output is the + # same even if the algorithm is not implemented on the port. + pass + + # Partial digests are not supported. + h = algo(b"123") + h.digest() + try: + h.update(b"456") + print("fail") + except ValueError: + # Expected path, don't print anything so test output is the + # same even if the algorithm is not implemented on the port. + pass + +print("done") diff --git a/tests/extmod/uhashlib_final.py.exp b/tests/extmod/uhashlib_final.py.exp new file mode 100644 index 0000000000..19f86f493a --- /dev/null +++ b/tests/extmod/uhashlib_final.py.exp @@ -0,0 +1 @@ +done diff --git a/tests/extmod/urandom_basic.py b/tests/extmod/urandom_basic.py index 180197398f..f7f5a6d691 100644 --- a/tests/extmod/urandom_basic.py +++ b/tests/extmod/urandom_basic.py @@ -22,8 +22,11 @@ r = random.getrandbits(16) random.seed(1) print(random.getrandbits(16) == r) -# check that it throws an error for zero bits +# check that zero bits works +print(random.getrandbits(0)) + +# check that it throws an error for negative bits try: - random.getrandbits(0) + random.getrandbits(-1) except ValueError: print("ValueError") diff --git a/tests/extmod/urandom_basic.py.exp b/tests/extmod/urandom_basic.py.exp new file mode 100644 index 0000000000..d629828d79 --- /dev/null +++ b/tests/extmod/urandom_basic.py.exp @@ -0,0 +1,4 @@ +True +True +0 +ValueError diff --git a/tests/extmod/uselect_poll_basic.py b/tests/extmod/uselect_poll_basic.py index 07328365b3..97fbd6fd15 100644 --- a/tests/extmod/uselect_poll_basic.py +++ b/tests/extmod/uselect_poll_basic.py @@ -33,7 +33,7 @@ poller.unregister(s) try: poller.modify(s, select.POLLIN) except OSError as e: - assert e.args[0] == errno.ENOENT + assert e.errno == errno.ENOENT # poll after closing the socket, should return POLLNVAL poller.register(s) diff --git a/tests/extmod/usocket_tcp_basic.py b/tests/extmod/usocket_tcp_basic.py index 368dfe3c98..c2fe8cd14c 100644 --- a/tests/extmod/usocket_tcp_basic.py +++ b/tests/extmod/usocket_tcp_basic.py @@ -14,4 +14,4 @@ s = socket.socket() try: s.recv(1) except OSError as er: - print("ENOTCONN:", er.args[0] == errno.ENOTCONN) + print("ENOTCONN:", er.errno == errno.ENOTCONN) diff --git a/tests/extmod/usocket_udp_nonblock.py b/tests/extmod/usocket_udp_nonblock.py index 7dc0e562a3..bc560de142 100644 --- a/tests/extmod/usocket_udp_nonblock.py +++ b/tests/extmod/usocket_udp_nonblock.py @@ -17,4 +17,4 @@ s.settimeout(0) try: s.recv(1) except OSError as er: - print("EAGAIN:", er.args[0] == errno.EAGAIN) + print("EAGAIN:", er.errno == errno.EAGAIN) diff --git a/tests/extmod/vfs_fat_fileio1.py b/tests/extmod/vfs_fat_fileio1.py index 18a9690d41..10e92d6940 100644 --- a/tests/extmod/vfs_fat_fileio1.py +++ b/tests/extmod/vfs_fat_fileio1.py @@ -59,22 +59,22 @@ f.close() # allowed try: f.write("world!") except OSError as e: - print(e.args[0] == uerrno.EINVAL) + print(e.errno == uerrno.EINVAL) try: f.read() except OSError as e: - print(e.args[0] == uerrno.EINVAL) + print(e.errno == uerrno.EINVAL) try: f.flush() except OSError as e: - print(e.args[0] == uerrno.EINVAL) + print(e.errno == uerrno.EINVAL) try: open("foo_file.txt", "x") except OSError as e: - print(e.args[0] == uerrno.EEXIST) + print(e.errno == uerrno.EEXIST) with open("foo_file.txt", "a") as f: f.write("world!") @@ -106,7 +106,7 @@ vfs.mkdir("foo_dir") try: vfs.rmdir("foo_file.txt") except OSError as e: - print(e.args[0] == 20) # uerrno.ENOTDIR + print(e.errno == 20) # uerrno.ENOTDIR vfs.remove("foo_file.txt") print(list(vfs.ilistdir())) diff --git a/tests/extmod/vfs_fat_fileio2.py b/tests/extmod/vfs_fat_fileio2.py index e83cfd9204..297d75a481 100644 --- a/tests/extmod/vfs_fat_fileio2.py +++ b/tests/extmod/vfs_fat_fileio2.py @@ -53,22 +53,22 @@ uos.chdir("/ramdisk") try: vfs.mkdir("foo_dir") except OSError as e: - print(e.args[0] == uerrno.EEXIST) + print(e.errno == uerrno.EEXIST) try: vfs.remove("foo_dir") except OSError as e: - print(e.args[0] == uerrno.EISDIR) + print(e.errno == uerrno.EISDIR) try: vfs.remove("no_file.txt") except OSError as e: - print(e.args[0] == uerrno.ENOENT) + print(e.errno == uerrno.ENOENT) try: vfs.rename("foo_dir", "/null/file") except OSError as e: - print(e.args[0] == uerrno.ENOENT) + print(e.errno == uerrno.ENOENT) # file in dir with open("foo_dir/file-in-dir.txt", "w+t") as f: @@ -84,7 +84,7 @@ with open("foo_dir/sub_file.txt", "w") as f: try: vfs.rmdir("foo_dir") except OSError as e: - print(e.args[0] == uerrno.EACCES) + print(e.errno == uerrno.EACCES) # trim full path vfs.rename("foo_dir/file-in-dir.txt", "foo_dir/file.txt") @@ -113,5 +113,5 @@ try: f = open("large_file.txt", "wb") f.write(bytearray(bsize * free)) except OSError as e: - print("ENOSPC:", e.args[0] == 28) # uerrno.ENOSPC + print("ENOSPC:", e.errno == 28) # uerrno.ENOSPC f.close() diff --git a/tests/extmod/vfs_fat_more.py b/tests/extmod/vfs_fat_more.py index 8eba6a08db..1b7b04ee63 100644 --- a/tests/extmod/vfs_fat_more.py +++ b/tests/extmod/vfs_fat_more.py @@ -92,7 +92,7 @@ for exist in ("", "/", "dir", "/dir", "dir/subdir"): try: uos.mkdir(exist) except OSError as er: - print("mkdir OSError", er.args[0] == 17) # EEXIST + print("mkdir OSError", er.errno == 17) # EEXIST uos.chdir("/") print(uos.stat("test5.txt")[:-3]) diff --git a/tests/extmod/vfs_fat_ramdisk.py b/tests/extmod/vfs_fat_ramdisk.py index 439becc60c..4b79391102 100644 --- a/tests/extmod/vfs_fat_ramdisk.py +++ b/tests/extmod/vfs_fat_ramdisk.py @@ -61,7 +61,7 @@ print("getcwd:", vfs.getcwd()) try: vfs.stat("no_file.txt") except OSError as e: - print(e.args[0] == uerrno.ENOENT) + print(e.errno == uerrno.ENOENT) with vfs.open("foo_file.txt", "w") as f: f.write("hello!") @@ -84,7 +84,7 @@ with vfs.open("sub_file.txt", "w") as f: try: vfs.chdir("sub_file.txt") except OSError as e: - print(e.args[0] == uerrno.ENOENT) + print(e.errno == uerrno.ENOENT) vfs.chdir("..") print("getcwd:", vfs.getcwd()) @@ -98,4 +98,4 @@ print(list(vfs.ilistdir(b""))) try: vfs.ilistdir(b"no_exist") except OSError as e: - print("ENOENT:", e.args[0] == uerrno.ENOENT) + print("ENOENT:", e.errno == uerrno.ENOENT) diff --git a/tests/float/bytearray_construct.py b/tests/float/bytearray_construct_endian.py similarity index 85% rename from tests/float/bytearray_construct.py rename to tests/float/bytearray_construct_endian.py index 257d37d1be..47f2b793c0 100644 --- a/tests/float/bytearray_construct.py +++ b/tests/float/bytearray_construct_endian.py @@ -9,4 +9,4 @@ except ImportError: print("SKIP") raise SystemExit -print(bytearray(array("f", [1, 2.3]))) +print(bytearray(array("f", [1, 2.5]))) diff --git a/tests/float/bytes_construct.py b/tests/float/bytes_construct_endian.py similarity index 63% rename from tests/float/bytes_construct.py rename to tests/float/bytes_construct_endian.py index 0806087b0e..4e15acc8bc 100644 --- a/tests/float/bytes_construct.py +++ b/tests/float/bytes_construct_endian.py @@ -1,4 +1,4 @@ -# test construction of bytearray from array with float type +# test construction of bytes from array with float type try: from uarray import array @@ -9,4 +9,4 @@ except ImportError: print("SKIP") raise SystemExit -print(bytes(array("f", [1, 2.3]))) +print(bytes(array("f", [1, 2.5]))) diff --git a/tests/float/float_array.py b/tests/float/float_array.py index 3c2189869b..219b6b86ae 100644 --- a/tests/float/float_array.py +++ b/tests/float/float_array.py @@ -22,4 +22,4 @@ def test(a): test(array("f")) test(array("d")) -print("{:.4f}".format(array("f", b"\xcc\xcc\xcc=")[0])) +print("{:.4f}".format(array("f", bytes(array("I", [0x3DCCCCCC])))[0])) diff --git a/tests/import/import_pkg7.py.exp b/tests/import/import_pkg7.py.exp new file mode 100644 index 0000000000..8f21a615f6 --- /dev/null +++ b/tests/import/import_pkg7.py.exp @@ -0,0 +1,8 @@ +pkg __name__: pkg7 +pkg __name__: pkg7.subpkg1 +pkg __name__: pkg7.subpkg1.subpkg2 +mod1 +mod2 +mod1.foo +mod2.bar +ImportError diff --git a/tests/import/pkg7/subpkg1/subpkg2/mod3.py b/tests/import/pkg7/subpkg1/subpkg2/mod3.py index 0aa916d208..b0f4279fcf 100644 --- a/tests/import/pkg7/subpkg1/subpkg2/mod3.py +++ b/tests/import/pkg7/subpkg1/subpkg2/mod3.py @@ -7,5 +7,5 @@ print(bar) # attempted relative import beyond top-level package try: from .... import mod1 -except ValueError: - print("ValueError") +except ImportError: + print("ImportError") diff --git a/tests/micropython/viper_misc2.py b/tests/micropython/viper_misc2.py new file mode 100644 index 0000000000..8f0be487d6 --- /dev/null +++ b/tests/micropython/viper_misc2.py @@ -0,0 +1,21 @@ +# Miscellaneous viper tests + +# Test correct use of registers in load and store +@micropython.viper +def expand(dest: ptr8, source: ptr8, length: int): + n = 0 + for x in range(0, length, 2): + c = source[x] + d = source[x + 1] + dest[n] = (c & 0xE0) | ((c & 0x1C) >> 1) + n += 1 + dest[n] = ((c & 3) << 6) | ((d & 0xE0) >> 4) + n += 1 + dest[n] = ((d & 0x1C) << 3) | ((d & 3) << 2) + n += 1 + + +source = b"\xaa\xaa\xff\xff" +dest = bytearray(len(source) // 2 * 3) +expand(dest, source, len(source)) +print(dest) diff --git a/tests/micropython/viper_misc2.py.exp b/tests/micropython/viper_misc2.py.exp new file mode 100644 index 0000000000..eff2f5e41d --- /dev/null +++ b/tests/micropython/viper_misc2.py.exp @@ -0,0 +1 @@ +bytearray(b'\xa4\x8aH\xee\xce\xec') diff --git a/tests/run-multitests.py b/tests/run-multitests.py index cdb2730eda..b4afc1e526 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -32,8 +32,10 @@ import sys class multitest: @staticmethod def flush(): - if hasattr(sys.stdout, "flush"): + try: sys.stdout.flush() + except AttributeError: + pass @staticmethod def skip(): print("SKIP") @@ -160,7 +162,17 @@ class PyInstanceSubProcess(PyInstance): class PyInstancePyboard(PyInstance): + @staticmethod + def map_device_shortcut(device): + if device[0] == "a" and device[1:].isdigit(): + return "/dev/ttyACM" + device[1:] + elif device[0] == "u" and device[1:].isdigit(): + return "/dev/ttyUSB" + device[1:] + else: + return device + def __init__(self, device): + device = self.map_device_shortcut(device) self.device = device self.pyb = pyboard.Pyboard(device) self.pyb.enter_raw_repl() @@ -233,6 +245,7 @@ def trace_instance_output(instance_idx, line): if cmd_args.trace_output: t_ms = round((time.time() - trace_t0) * 1000) print("{:6} i{} :".format(t_ms, instance_idx), line) + sys.stdout.flush() def run_test_on_instances(test_file, num_instances, instances): diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index 8b71ae64ce..bcdbe69abb 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -85,7 +85,7 @@ def run_benchmark_on_target(target, script): def run_benchmarks(target, param_n, param_m, n_average, test_list): skip_complex = run_feature_test(target, "complex") != "complex" - skip_native = run_feature_test(target, "native_check") != "" + skip_native = run_feature_test(target, "native_check") != "native" for test_file in sorted(test_list): print(test_file + ": ", end="") diff --git a/tests/thread/stress_aes.py b/tests/thread/stress_aes.py index 2b974d5e51..673337563d 100644 --- a/tests/thread/stress_aes.py +++ b/tests/thread/stress_aes.py @@ -237,7 +237,7 @@ class LockedCounter: count = LockedCounter() -def thread_entry(): +def thread_entry(n_loop): global count aes = AES(256) @@ -246,7 +246,7 @@ def thread_entry(): data = bytearray(128) # from now on we don't use the heap - for loop in range(5): + for loop in range(n_loop): # encrypt aes.set_key(key) aes.set_iv(iv) @@ -267,8 +267,20 @@ def thread_entry(): if __name__ == "__main__": - n_thread = 20 + import sys + + if sys.platform == "rp2": + n_thread = 1 + n_loop = 2 + elif sys.platform in ("esp32", "pyboard"): + n_thread = 2 + n_loop = 2 + else: + n_thread = 20 + n_loop = 5 for i in range(n_thread): - _thread.start_new_thread(thread_entry, ()) + _thread.start_new_thread(thread_entry, (n_loop,)) + thread_entry(n_loop) while count.value < n_thread: time.sleep(1) + print("done") diff --git a/tests/thread/stress_create.py b/tests/thread/stress_create.py index eda768fa7b..877424cdf5 100644 --- a/tests/thread/stress_create.py +++ b/tests/thread/stress_create.py @@ -1,9 +1,13 @@ # stress test for creating many threads try: - import utime as time + import utime + + sleep_ms = utime.sleep_ms except ImportError: import time + + sleep_ms = lambda t: time.sleep(t / 1000) import _thread @@ -16,9 +20,11 @@ while thread_num < 500: try: _thread.start_new_thread(thread_entry, (thread_num,)) thread_num += 1 - except MemoryError: - pass + except (MemoryError, OSError) as er: + # Cannot create a new thead at this stage, yield for a bit to + # let existing threads run to completion and free up resources. + sleep_ms(50) # wait for the last threads to terminate -time.sleep(1) +sleep_ms(500) print("done") diff --git a/tests/thread/thread_exc1.py b/tests/thread/thread_exc1.py index 3c3a1e8be9..6dcfda121e 100644 --- a/tests/thread/thread_exc1.py +++ b/tests/thread/thread_exc1.py @@ -27,7 +27,12 @@ n_finished = 0 # spawn threads for i in range(n_thread): - _thread.start_new_thread(thread_entry, ()) + while True: + try: + _thread.start_new_thread(thread_entry, ()) + break + except OSError: + pass # busy wait for threads to finish while n_finished < n_thread: diff --git a/tests/thread/thread_exit1.py b/tests/thread/thread_exit1.py index 6179716d14..a6b00ca1f7 100644 --- a/tests/thread/thread_exit1.py +++ b/tests/thread/thread_exit1.py @@ -15,8 +15,13 @@ def thread_entry(): _thread.exit() -_thread.start_new_thread(thread_entry, ()) -_thread.start_new_thread(thread_entry, ()) +for i in range(2): + while True: + try: + _thread.start_new_thread(thread_entry, ()) + break + except OSError: + pass # wait for threads to finish time.sleep(1) diff --git a/tests/thread/thread_exit2.py b/tests/thread/thread_exit2.py index ba9852b5c2..2381fa5ebc 100644 --- a/tests/thread/thread_exit2.py +++ b/tests/thread/thread_exit2.py @@ -15,8 +15,13 @@ def thread_entry(): raise SystemExit -_thread.start_new_thread(thread_entry, ()) -_thread.start_new_thread(thread_entry, ()) +for i in range(2): + while True: + try: + _thread.start_new_thread(thread_entry, ()) + break + except OSError: + pass # wait for threads to finish time.sleep(1) diff --git a/tests/thread/thread_heap_lock.py b/tests/thread/thread_heap_lock.py new file mode 100644 index 0000000000..2837e0f366 --- /dev/null +++ b/tests/thread/thread_heap_lock.py @@ -0,0 +1,26 @@ +# test interaction of micropython.heap_lock with threads + +import _thread, micropython + +lock1 = _thread.allocate_lock() +lock2 = _thread.allocate_lock() + + +def thread_entry(): + lock1.acquire() + print([1, 2, 3]) + lock2.release() + + +lock1.acquire() +lock2.acquire() + +_thread.start_new_thread(thread_entry, ()) + +micropython.heap_lock() +lock1.release() +lock2.acquire() +micropython.heap_unlock() + +lock1.release() +lock2.release() diff --git a/tests/thread/thread_heap_lock.py.exp b/tests/thread/thread_heap_lock.py.exp new file mode 100644 index 0000000000..b5d8bb58d9 --- /dev/null +++ b/tests/thread/thread_heap_lock.py.exp @@ -0,0 +1 @@ +[1, 2, 3] diff --git a/tests/thread/thread_lock5.py b/tests/thread/thread_lock5.py new file mode 100644 index 0000000000..830b5efa82 --- /dev/null +++ b/tests/thread/thread_lock5.py @@ -0,0 +1,16 @@ +# test _thread lock objects where a lock is acquired/released by a different thread + +import _thread + + +def thread_entry(): + print("thread about to release lock") + lock.release() + + +lock = _thread.allocate_lock() +lock.acquire() +_thread.start_new_thread(thread_entry, ()) +lock.acquire() +print("main has lock") +lock.release() diff --git a/tests/thread/thread_stacksize1.py b/tests/thread/thread_stacksize1.py index 7473c578ed..2c70b90b5b 100644 --- a/tests/thread/thread_stacksize1.py +++ b/tests/thread/thread_stacksize1.py @@ -41,7 +41,12 @@ n_finished = 0 # set stack size and spawn a few threads _thread.stack_size(sz) for i in range(n_thread): - _thread.start_new_thread(thread_entry, ()) + while True: + try: + _thread.start_new_thread(thread_entry, ()) + break + except OSError: + pass # reset stack size to default (for subsequent scripts on baremetal) _thread.stack_size() diff --git a/tests/thread/thread_start1.py b/tests/thread/thread_start1.py index f08ff05a8e..6f08a135e4 100644 --- a/tests/thread/thread_start1.py +++ b/tests/thread/thread_start1.py @@ -20,8 +20,13 @@ def thread_entry(n): foo() -_thread.start_new_thread(thread_entry, (10,)) -_thread.start_new_thread(thread_entry, (20,)) +for i in range(2): + while True: + try: + _thread.start_new_thread(thread_entry, ((i + 1) * 10,)) + break + except OSError: + pass # wait for threads to finish time.sleep(1) diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index f7dd6172ed..9a451ebd65 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -27,6 +27,19 @@ RuntimeError: RuntimeError: # repl ame__ +mport + +builtins micropython _thread array +btree cexample cmath collections +cppexample ffi framebuf gc +hashlib math sys termios +ubinascii uctypes uerrno uheapq +uio ujson ulab uos +urandom ure uselect ustruct +utime utimeq uzlib +ime + +utime utimeq argv atexit byteorder exc_info exit getsizeof implementation maxsize diff --git a/tests/unix/ffi_float.py b/tests/unix/ffi_float.py index d039398965..03bd9f7f17 100644 --- a/tests/unix/ffi_float.py +++ b/tests/unix/ffi_float.py @@ -35,6 +35,15 @@ print("%.6f" % strtod("1.23", None)) # test passing double and float args libm = ffi_open(("libm.so", "libm.so.6", "libc.so.0", "libc.so.6", "libc.dylib")) tgamma = libm.func("d", "tgamma", "d") -for fun in (tgamma,): +for fun_name in ("tgamma",): + fun = globals()[fun_name] for val in (0.5, 1, 1.0, 1.5, 4, 4.0): - print("%.6f" % fun(val)) + print(fun_name, "%.5f" % fun(val)) + +# test passing 2x float/double args +powf = libm.func("f", "powf", "ff") +pow = libm.func("d", "pow", "dd") +for fun_name in ("powf", "pow"): + fun = globals()[fun_name] + for args in ((0, 1), (1, 0), (2, 0.5), (3, 4)): + print(fun_name, "%.5f" % fun(*args)) diff --git a/tests/unix/ffi_float.py.exp b/tests/unix/ffi_float.py.exp index b9d7da2bdb..3d90914315 100644 --- a/tests/unix/ffi_float.py.exp +++ b/tests/unix/ffi_float.py.exp @@ -1,8 +1,16 @@ 1.230000 1.230000 -1.772454 -1.000000 -1.000000 -0.886227 -6.000000 -6.000000 +tgamma 1.77245 +tgamma 1.00000 +tgamma 1.00000 +tgamma 0.88623 +tgamma 6.00000 +tgamma 6.00000 +powf 0.00000 +powf 1.00000 +powf 1.41421 +powf 81.00000 +pow 0.00000 +pow 1.00000 +pow 1.41421 +pow 81.00000 diff --git a/tests/unix/ffi_lib.c b/tests/unix/ffi_lib.c new file mode 100644 index 0000000000..35340536ae --- /dev/null +++ b/tests/unix/ffi_lib.c @@ -0,0 +1,33 @@ +#include + +int8_t f8i(int8_t x) { + return x ^ 1; +} + +uint8_t f8u(uint8_t x) { + return x ^ 1; +} + +int16_t f16i(int16_t x) { + return x ^ 1; +} + +uint16_t f16u(uint16_t x) { + return x ^ 1; +} + +int32_t f32i(int32_t x) { + return x ^ 1; +} + +uint32_t f32u(uint32_t x) { + return x ^ 1; +} + +int64_t f64i(int64_t x) { + return x ^ 1; +} + +uint64_t f64u(uint64_t x) { + return x ^ 1; +} diff --git a/tests/unix/ffi_types.py.exp b/tests/unix/ffi_types.py.exp new file mode 100644 index 0000000000..d6324477d6 --- /dev/null +++ b/tests/unix/ffi_types.py.exp @@ -0,0 +1,136 @@ +f8i(0) = 1 +f8i(7f) = 7e +f8i(80) = -7f +f8i(ff) = -2 +f8i(100) = 1 +f8i(7fff) = -2 +f8i(8000) = 1 +f8i(ffff) = -2 +f8i(10000) = 1 +f8i(7fffffff) = -2 +f8i(80000000) = 1 +f8i(ffffffff) = -2 +f8i(100000000) = 1 +f8i(7fffffffffffffff) = -2 +f8i(8000000000000000) = 1 +f8i(ffffffffffffffff) = -2 +f8i(10000000000000000) = 1 +f8u(0) = 1 +f8u(7f) = 7e +f8u(80) = 81 +f8u(ff) = fe +f8u(100) = 1 +f8u(7fff) = fe +f8u(8000) = 1 +f8u(ffff) = fe +f8u(10000) = 1 +f8u(7fffffff) = fe +f8u(80000000) = 1 +f8u(ffffffff) = fe +f8u(100000000) = 1 +f8u(7fffffffffffffff) = fe +f8u(8000000000000000) = 1 +f8u(ffffffffffffffff) = fe +f8u(10000000000000000) = 1 +f16i(0) = 1 +f16i(7f) = 7e +f16i(80) = 81 +f16i(ff) = fe +f16i(100) = 101 +f16i(7fff) = 7ffe +f16i(8000) = -7fff +f16i(ffff) = -2 +f16i(10000) = 1 +f16i(7fffffff) = -2 +f16i(80000000) = 1 +f16i(ffffffff) = -2 +f16i(100000000) = 1 +f16i(7fffffffffffffff) = -2 +f16i(8000000000000000) = 1 +f16i(ffffffffffffffff) = -2 +f16i(10000000000000000) = 1 +f16u(0) = 1 +f16u(7f) = 7e +f16u(80) = 81 +f16u(ff) = fe +f16u(100) = 101 +f16u(7fff) = 7ffe +f16u(8000) = 8001 +f16u(ffff) = fffe +f16u(10000) = 1 +f16u(7fffffff) = fffe +f16u(80000000) = 1 +f16u(ffffffff) = fffe +f16u(100000000) = 1 +f16u(7fffffffffffffff) = fffe +f16u(8000000000000000) = 1 +f16u(ffffffffffffffff) = fffe +f16u(10000000000000000) = 1 +f32i(0) = 1 +f32i(7f) = 7e +f32i(80) = 81 +f32i(ff) = fe +f32i(100) = 101 +f32i(7fff) = 7ffe +f32i(8000) = 8001 +f32i(ffff) = fffe +f32i(10000) = 10001 +f32i(7fffffff) = 7ffffffe +f32i(80000000) = -7fffffff +f32i(ffffffff) = -2 +f32i(100000000) = 1 +f32i(7fffffffffffffff) = -2 +f32i(8000000000000000) = 1 +f32i(ffffffffffffffff) = -2 +f32i(10000000000000000) = 1 +f32u(0) = 1 +f32u(7f) = 7e +f32u(80) = 81 +f32u(ff) = fe +f32u(100) = 101 +f32u(7fff) = 7ffe +f32u(8000) = 8001 +f32u(ffff) = fffe +f32u(10000) = 10001 +f32u(7fffffff) = 7ffffffe +f32u(80000000) = 80000001 +f32u(ffffffff) = fffffffe +f32u(100000000) = 1 +f32u(7fffffffffffffff) = fffffffe +f32u(8000000000000000) = 1 +f32u(ffffffffffffffff) = fffffffe +f32u(10000000000000000) = 1 +f64i(0) = 1 +f64i(7f) = 7e +f64i(80) = 81 +f64i(ff) = fe +f64i(100) = 101 +f64i(7fff) = 7ffe +f64i(8000) = 8001 +f64i(ffff) = fffe +f64i(10000) = 10001 +f64i(7fffffff) = 7ffffffe +f64i(80000000) = 80000001 +f64i(ffffffff) = fffffffe +f64i(100000000) = 100000001 +f64i(7fffffffffffffff) = 7ffffffffffffffe +f64i(8000000000000000) = -7fffffffffffffff +f64i(ffffffffffffffff) = -2 +f64i(10000000000000000) = 1 +f64u(0) = 1 +f64u(7f) = 7e +f64u(80) = 81 +f64u(ff) = fe +f64u(100) = 101 +f64u(7fff) = 7ffe +f64u(8000) = 8001 +f64u(ffff) = fffe +f64u(10000) = 10001 +f64u(7fffffff) = 7ffffffe +f64u(80000000) = 80000001 +f64u(ffffffff) = fffffffe +f64u(100000000) = 100000001 +f64u(7fffffffffffffff) = 7ffffffffffffffe +f64u(8000000000000000) = 8000000000000001 +f64u(ffffffffffffffff) = fffffffffffffffe +f64u(10000000000000000) = 1 diff --git a/tools/ci.sh b/tools/ci.sh index c018b55002..3039573322 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -109,7 +109,7 @@ function ci_esp32_build { make ${MAKEOPTS} -C ports/esp32 submodules make ${MAKEOPTS} -C ports/esp32 make ${MAKEOPTS} -C ports/esp32 clean - make ${MAKEOPTS} -C ports/esp32 USER_C_MODULES=../../../examples/usercmodule/micropython.cmake + make ${MAKEOPTS} -C ports/esp32 USER_C_MODULES=../../../examples/usercmodule/micropython.cmake FROZEN_MANIFEST=$(pwd)/ports/esp32/boards/manifest.py if [ -d $IDF_PATH/components/esp32s2 ]; then make ${MAKEOPTS} -C ports/esp32 BOARD=GENERIC_S2 fi @@ -138,6 +138,20 @@ function ci_esp8266_build { make ${MAKEOPTS} -C ports/esp8266 BOARD=GENERIC_1M } +######################################################################################## +# ports/mimxrt + +function ci_mimxrt_setup { + ci_gcc_arm_setup +} + +function ci_mimxrt_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/mimxrt submodules + make ${MAKEOPTS} -C ports/mimxrt BOARD=MIMXRT1020_EVK + make ${MAKEOPTS} -C ports/mimxrt BOARD=TEENSY40 +} + ######################################################################################## # ports/nrf @@ -181,6 +195,8 @@ function ci_qemu_arm_build { make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 make ${MAKEOPTS} -C ports/qemu-arm clean make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test test + make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test clean + make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test BOARD=sabrelite test } ######################################################################################## @@ -243,6 +259,10 @@ function ci_stm32_nucleo_build { BUILD_WB55=ports/stm32/build-NUCLEO_WB55 python3 ports/stm32/mboot/mboot_pack_dfu.py -k $BOARD_WB55/mboot_keys.h unpack-dfu $BUILD_WB55/firmware.pack.dfu $BUILD_WB55/firmware.unpack.dfu diff $BUILD_WB55/firmware.unpack.dfu $BUILD_WB55/firmware.dfu + # Test unpack-dfu command works without a secret key + tail -n +2 $BOARD_WB55/mboot_keys.h > $BOARD_WB55/mboot_keys_no_sk.h + python3 ports/stm32/mboot/mboot_pack_dfu.py -k $BOARD_WB55/mboot_keys_no_sk.h unpack-dfu $BUILD_WB55/firmware.pack.dfu $BUILD_WB55/firmware.unpack_no_sk.dfu + diff $BUILD_WB55/firmware.unpack.dfu $BUILD_WB55/firmware.unpack_no_sk.dfu } ######################################################################################## @@ -273,6 +293,19 @@ CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS=( CFLAGS_EXTRA="-DMICROPY_STACKLESS=1 -DMICROPY_STACKLESS_STRICT=1 -DMICROPY_PY_SYS_SETTRACE=1" ) +CI_UNIX_OPTS_QEMU_MIPS=( + CROSS_COMPILE=mips-linux-gnu- + VARIANT=coverage + MICROPY_STANDALONE=1 + LDFLAGS_EXTRA="-static" +) + +CI_UNIX_OPTS_QEMU_ARM=( + CROSS_COMPILE=arm-linux-gnueabi- + VARIANT=coverage + MICROPY_STANDALONE=1 +) + function ci_unix_build_helper { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix "$@" submodules @@ -280,6 +313,10 @@ function ci_unix_build_helper { make ${MAKEOPTS} -C ports/unix "$@" } +function ci_unix_build_ffi_lib_helper { + $1 $2 -shared -o tests/unix/ffi_lib.so tests/unix/ffi_lib.c +} + function ci_unix_run_tests_helper { make -C ports/unix "$@" test } @@ -326,6 +363,7 @@ function ci_unix_minimal_run_tests { function ci_unix_standard_build { ci_unix_build_helper VARIANT=standard + ci_unix_build_ffi_lib_helper gcc } function ci_unix_standard_run_tests { @@ -346,6 +384,7 @@ function ci_unix_coverage_setup { function ci_unix_coverage_build { ci_unix_build_helper VARIANT=coverage + ci_unix_build_ffi_lib_helper gcc } function ci_unix_coverage_run_tests { @@ -370,6 +409,7 @@ function ci_unix_32bit_setup { function ci_unix_coverage_32bit_build { ci_unix_build_helper VARIANT=coverage MICROPY_FORCE_32BIT=1 + ci_unix_build_ffi_lib_helper gcc -m32 } function ci_unix_coverage_32bit_run_tests { @@ -383,6 +423,7 @@ function ci_unix_coverage_32bit_run_native_mpy_tests { function ci_unix_nanbox_build { # Use Python 2 to check that it can run the build scripts ci_unix_build_helper PYTHON=python2 VARIANT=nanbox + ci_unix_build_ffi_lib_helper gcc -m32 } function ci_unix_nanbox_run_tests { @@ -391,6 +432,7 @@ function ci_unix_nanbox_run_tests { function ci_unix_float_build { ci_unix_build_helper VARIANT=standard CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT" + ci_unix_build_ffi_lib_helper gcc } function ci_unix_float_run_tests { @@ -459,6 +501,46 @@ function ci_unix_macos_run_tests { (cd tests && ./run-tests.py --exclude 'uasyncio_(basic|heaplock|lock|wait_task)' --exclude 'import_pkg7.py' --exclude 'urandom_basic.py') } +function ci_unix_qemu_mips_setup { + sudo apt-get update + sudo apt-get install gcc-mips-linux-gnu g++-mips-linux-gnu + sudo apt-get install qemu-user + qemu-mips --version +} + +function ci_unix_qemu_mips_build { + # qemu-mips on GitHub Actions will seg-fault if not linked statically + ci_unix_build_helper "${CI_UNIX_OPTS_QEMU_MIPS[@]}" +} + +function ci_unix_qemu_mips_run_tests { + # Issues with MIPS tests: + # - (i)listdir does not work, it always returns the empty list (it's an issue with the underlying C call) + # - ffi tests do not work + file ./ports/unix/micropython-coverage + (cd tests && MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests.py --exclude 'vfs_posix.py' --exclude 'ffi_(callback|float|float2).py') +} + +function ci_unix_qemu_arm_setup { + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi + sudo apt-get install qemu-user + qemu-arm --version +} + +function ci_unix_qemu_arm_build { + ci_unix_build_helper "${CI_UNIX_OPTS_QEMU_ARM[@]}" + ci_unix_build_ffi_lib_helper arm-linux-gnueabi-gcc +} + +function ci_unix_qemu_arm_run_tests { + # Issues with ARM tests: + # - (i)listdir does not work, it always returns the empty list (it's an issue with the underlying C call) + export QEMU_LD_PREFIX=/usr/arm-linux-gnueabi + file ./ports/unix/micropython-coverage + (cd tests && MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests.py --exclude 'vfs_posix.py') +} + ######################################################################################## # ports/windows @@ -475,19 +557,19 @@ function ci_windows_build { # ports/zephyr function ci_zephyr_setup { - docker pull zephyrprojectrtos/ci:v0.11.13 + docker pull zephyrprojectrtos/ci:v0.17.3 docker run --name zephyr-ci -d -it \ -v "$(pwd)":/micropython \ - -e ZEPHYR_SDK_INSTALL_DIR=/opt/sdk/zephyr-sdk-0.12.2 \ + -e ZEPHYR_SDK_INSTALL_DIR=/opt/toolchains/zephyr-sdk-0.12.4 \ -e ZEPHYR_TOOLCHAIN_VARIANT=zephyr \ -e ZEPHYR_BASE=/zephyrproject/zephyr \ -w /micropython/ports/zephyr \ - zephyrprojectrtos/ci:v0.11.13 + zephyrprojectrtos/ci:v0.17.3 docker ps -a } function ci_zephyr_install { - docker exec zephyr-ci west init --mr v2.5.0 /zephyrproject + docker exec zephyr-ci west init --mr v2.6.0 /zephyrproject docker exec -w /zephyrproject zephyr-ci west update docker exec -w /zephyrproject zephyr-ci west zephyr-export } diff --git a/tools/makemanifest.py b/tools/makemanifest.py index 117d1536e8..51de01dd8a 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -199,7 +199,7 @@ def mkdir(filename): def freeze_internal(kind, path, script, opt): path = convert_path(path) if not os.path.isdir(path): - raise FreezeError("freeze path must be a directory") + raise FreezeError("freeze path must be a directory: {}".format(path)) if script is None and kind == KIND_AS_STR: if any(f[0] == KIND_AS_STR for f in manifest_list): raise FreezeError("can only freeze one str directory") diff --git a/tools/metrics.py b/tools/metrics.py index 98291e25a9..c857d0733e 100755 --- a/tools/metrics.py +++ b/tools/metrics.py @@ -67,6 +67,7 @@ port_data = { "8": PortData("esp8266", "esp8266", "build-GENERIC/firmware.elf"), "3": PortData("esp32", "esp32", "build-GENERIC/micropython.elf"), "r": PortData("nrf", "nrf", "build-pca10040/firmware.elf"), + "p": PortData("rp2", "rp2", "build-PICO/firmware.elf"), "d": PortData("samd", "samd", "build-ADAFRUIT_ITSYBITSY_M4_EXPRESS/firmware.elf"), } diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 28dad27ddc..05a9db48da 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -513,6 +513,7 @@ class RawCodeNative(RawCode): if config.native_arch in ( MP_NATIVE_ARCH_X86, MP_NATIVE_ARCH_X64, + MP_NATIVE_ARCH_ARMV6, MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, ): diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 9c6424690b..daa9eabca4 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -73,6 +73,7 @@ R_XTENSA_SLOT0_OP = 20 R_ARM_BASE_PREL = 25 # aka R_ARM_GOTPC R_ARM_GOT_BREL = 26 # aka R_ARM_GOT32 R_ARM_THM_JUMP24 = 30 +R_X86_64_GOTPCREL = 9 R_X86_64_REX_GOTPCRELX = 42 R_386_GOT32X = 43 @@ -132,7 +133,7 @@ ARCH_DATA = { | MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE, 2, 8, - (R_X86_64_REX_GOTPCRELX,), + (R_X86_64_GOTPCREL, R_X86_64_REX_GOTPCRELX), asm_jump_x86, ), "armv7m": ArchData( @@ -536,7 +537,10 @@ def do_relocation_text(env, text_addr, r): # Relcation pointing to GOT reloc = addr = env.got_entries[s.name].offset - elif env.arch.name == "EM_X86_64" and r_info_type == R_X86_64_REX_GOTPCRELX: + elif env.arch.name == "EM_X86_64" and r_info_type in ( + R_X86_64_GOTPCREL, + R_X86_64_REX_GOTPCRELX, + ): # Relcation pointing to GOT got_entry = env.got_entries[s.name] addr = env.got_section.addr + got_entry.offset diff --git a/tools/pyboard.py b/tools/pyboard.py index b390886599..3fca47b0b7 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2014-2019 Damien P. George +# SPDX-FileCopyrightText: Copyright (c) 2014-2021 Damien P. George # SPDX-FileCopyrightText: Copyright (c) 2017 Paul Sokolovsky # SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) # @@ -66,7 +66,7 @@ def stdout_write_bytes(b): stdout.flush() -class PyboardError(BaseException): +class PyboardError(Exception): pass @@ -142,7 +142,7 @@ class ProcessToSerial: import subprocess self.subp = subprocess.Popen( - cmd.split(), + cmd, bufsize=0, shell=True, preexec_fn=os.setsid, @@ -233,7 +233,10 @@ class ProcessPtyToTerminal: class Pyboard: - def __init__(self, device, baudrate=115200, user="micro", password="python", wait=0): + def __init__( + self, device, baudrate=115200, user="micro", password="python", wait=0, exclusive=True + ): + self.in_raw_repl = False self.use_raw_paste = True if device.startswith("exec:"): self.serial = ProcessToSerial(device[len("exec:") :]) @@ -245,10 +248,15 @@ class Pyboard: else: import serial + # Set options, and exclusive if pyserial supports it + serial_kwargs = {"baudrate": baudrate, "interCharTimeout": 1} + if serial.__version__ >= "3.3": + serial_kwargs["exclusive"] = exclusive + delayed = False for attempt in range(wait + 1): try: - self.serial = serial.Serial(device, baudrate=baudrate, interCharTimeout=1) + self.serial = serial.Serial(device, **serial_kwargs) break except (OSError, IOError): # Py2 and Py3 have different errors if wait == 0: @@ -295,7 +303,7 @@ class Pyboard: time.sleep(0.01) return data - def enter_raw_repl(self): + def enter_raw_repl(self, soft_reset=True): self.serial.write(b"\r\x03\x03") # ctrl-C twice: interrupt any running program # flush input (without relying on serial.flushInput()) @@ -305,25 +313,33 @@ class Pyboard: n = self.serial.inWaiting() self.serial.write(b"\r\x01") # ctrl-A: enter raw REPL - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") - if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): - print(data) - raise PyboardError("could not enter raw repl") - self.serial.write(b"\x04") # ctrl-D: soft reset - data = self.read_until(1, b"soft reboot\r\n") - if not data.endswith(b"soft reboot\r\n"): - print(data) - raise PyboardError("could not enter raw repl") - # By splitting this into 2 reads, it allows boot.py to print stuff, - # which will show up after the soft reboot and before the raw REPL. + if soft_reset: + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): + print(data) + raise PyboardError("could not enter raw repl") + + self.serial.write(b"\x04") # ctrl-D: soft reset + + # Waiting for "soft reboot" independently to "raw REPL" (done below) + # allows boot.py to print, which will show up after "soft reboot" + # and before "raw REPL". + data = self.read_until(1, b"soft reboot\r\n") + if not data.endswith(b"soft reboot\r\n"): + print(data) + raise PyboardError("could not enter raw repl") + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n") if not data.endswith(b"raw REPL; CTRL-B to exit\r\n"): print(data) raise PyboardError("could not enter raw repl") + self.in_raw_repl = True + def exit_raw_repl(self): self.serial.write(b"\r\x02") # ctrl-B: enter friendly REPL + self.in_raw_repl = False def follow(self, timeout, data_consumer=None): # wait for normal output @@ -631,6 +647,11 @@ def main(): action="store_true", help="Do not follow the output after running the scripts.", ) + group.add_argument( + "--no-exclusive", + action="store_true", + help="Do not try to open the serial device for exclusive access.", + ) cmd_parser.add_argument( "-f", "--filesystem", @@ -643,7 +664,9 @@ def main(): # open the connection to the pyboard try: - pyb = Pyboard(args.device, args.baudrate, args.user, args.password, args.wait) + pyb = Pyboard( + args.device, args.baudrate, args.user, args.password, args.wait, not args.no_exclusive + ) except PyboardError as er: print(er) sys.exit(1) @@ -683,7 +706,7 @@ def main(): # do filesystem commands, if given if args.filesystem: filesystem_command(pyb, args.files) - args.files.clear() + del args.files[:] # run the command, if given if args.command is not None: diff --git a/tools/pydfu.py b/tools/pydfu.py index fc8bc53f01..26e40f5613 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -24,10 +24,6 @@ import usb.core import usb.util import zlib -# VID/PID -__VID = 0x0483 -__PID = 0xDF11 - # USB request __TIMEOUT __TIMEOUT = 4000 @@ -111,10 +107,10 @@ def find_dfu_cfg_descr(descr): return None -def init(): +def init(**kwargs): """Initializes the found DFU device so that we can program it.""" global __dev, __cfg_descr - devices = get_dfu_devices(idVendor=__VID, idProduct=__PID) + devices = get_dfu_devices(**kwargs) if not devices: raise ValueError("No DFU device found") if len(devices) > 1: @@ -564,15 +560,13 @@ def cli_progress(addr, offset, size): def main(): """Test program for verifying this files functionality.""" global __verbose - global __VID - global __PID # Parse CMD args parser = argparse.ArgumentParser(description="DFU Python Util") parser.add_argument( "-l", "--list", help="list available DFU devices", action="store_true", default=False ) - parser.add_argument("--vid", help="USB Vendor ID", type=lambda x: int(x, 0), default=__VID) - parser.add_argument("--pid", help="USB Product ID", type=lambda x: int(x, 0), default=__PID) + parser.add_argument("--vid", help="USB Vendor ID", type=lambda x: int(x, 0), default=None) + parser.add_argument("--pid", help="USB Product ID", type=lambda x: int(x, 0), default=None) parser.add_argument( "-m", "--mass-erase", help="mass erase device", action="store_true", default=False ) @@ -587,14 +581,18 @@ def main(): __verbose = args.verbose - __VID = args.vid - __PID = args.pid + kwargs = {} + if args.vid: + kwargs["idVendor"] = args.vid + + if args.pid: + kwargs["idProduct"] = args.pid if args.list: - list_dfu_devices(idVendor=__VID, idProduct=__PID) + list_dfu_devices(**kwargs) return - init() + init(**kwargs) command_run = False if args.mass_erase: diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index abc6f74d91..2000c6a566 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -129,9 +129,12 @@ argparser = argparse.ArgumentParser( description="Convert native MicroPython tests to tinytest/upytesthelper C code" ) argparser.add_argument("--stdin", action="store_true", help="read list of tests from stdin") +argparser.add_argument("--exclude", action="append", help="exclude test by name") args = argparser.parse_args() if not args.stdin: + if args.exclude: + exclude_tests += tuple(args.exclude) for group in test_dirs: tests += [test for test in glob("{}/*.py".format(group)) if test not in exclude_tests] else: diff --git a/tools/upip.py b/tools/upip.py index e3bd842333..95cd8d5cc7 100644 --- a/tools/upip.py +++ b/tools/upip.py @@ -60,7 +60,7 @@ def _makedirs(name, mode=0o777): os.mkdir(s) ret = True except OSError as e: - if e.args[0] != errno.EEXIST and e.args[0] != errno.EISDIR: + if e.errno != errno.EEXIST and e.errno != errno.EISDIR: raise e ret = False return ret