From 1b7f3d11e73be5cb0f7a97a528fedad87624b97f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 8 Feb 2021 10:57:41 -0500 Subject: wip --- shared-bindings/supervisor/Runtime.c | 111 ++++++++++++++++++++++++++--------- shared-bindings/supervisor/Runtime.h | 8 +-- 2 files changed, 87 insertions(+), 32 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/supervisor/Runtime.c b/shared-bindings/supervisor/Runtime.c index 8e0259a3b..cfda620bc 100755 --- a/shared-bindings/supervisor/Runtime.c +++ b/shared-bindings/supervisor/Runtime.c @@ -55,43 +55,96 @@ STATIC supervisor_run_reason_t _run_reason; //| serial_connected: bool //| """Returns the USB serial communication status (read-only).""" //| +STATIC mp_obj_t supervisor_runtime_get_serial_connected(mp_obj_t self){ + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial_connected()); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial_connected_obj, supervisor_runtime_get_serial_connected); -STATIC mp_obj_t supervisor_get_serial_connected(mp_obj_t self){ - if (!common_hal_get_serial_connected()) { - return mp_const_false; - } - else { - return mp_const_true; - } +//| serial2: io.BytesIO +//| """Returns the USB secondary serial communication channel. +//| Raises `NotImplementedError` if it does not exist. +//| """ +//| +STATIC mp_obj_t supervisor_runtime_get_serial2(mp_obj_t self){ +#if CIRCUITPY_USB_SERIAL2 + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial2()); +#else + mp_raise_NotImplementedError(translate("serial2 not available")); +#endif } -MP_DEFINE_CONST_FUN_OBJ_1(supervisor_get_serial_connected_obj, supervisor_get_serial_connected); +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial2_obj, supervisor_runtime_get_serial2); + +const mp_obj_property_t supervisor_runtime_serial2_connected_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial2_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; -const mp_obj_property_t supervisor_serial_connected_obj = { +const mp_obj_property_t supervisor_runtime_serial_connected_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&supervisor_get_serial_connected_obj, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial_connected_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; +//| serial2_connected: bool +//| """Returns the USB secondary serial communication status (read-only). +//| Raises `NotImplementedError` if there is no secondary serial channel. +//| """ +//| +STATIC mp_obj_t supervisor_runtime_get_serial2_connected(mp_obj_t self){ +#if CIRCUITPY_USB_SERIAL2 + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial2_connected()); +#else + mp_raise_NotImplementedError(translate("serial2 not available")); +#endif +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial2_connected_obj, supervisor_runtime_get_serial2_connected); + +const mp_obj_property_t supervisor_runtime_serial2_connected_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial2_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; //| serial_bytes_available: int //| """Returns the whether any bytes are available to read //| on the USB serial input. Allows for polling to see whether //| to call the built-in input() or wait. (read-only)""" //| -STATIC mp_obj_t supervisor_get_serial_bytes_available(mp_obj_t self){ - if (!common_hal_get_serial_bytes_available()) { - return mp_const_false; - } - else { - return mp_const_true; - } +STATIC mp_obj_t supervisor_runtime_get_serial_bytes_available(mp_obj_t self){ + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial_bytes_available()); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial_bytes_available_obj, supervisor_runtime_get_serial_bytes_available); + +const mp_obj_property_t supervisor_runtime_serial_bytes_available_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial_bytes_available_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +//| serial2_bytes_available: int +//| """Returns the whether any bytes are available to read +//| on the secondary USB serial input (read-only). +//| Raises `NotImplementedError` if there is no secondary serial input. +//| """ +//| +STATIC mp_obj_t supervisor_runtime_get_serial2_bytes_available(mp_obj_t self){ +#if CIRCUITPY_USB_SERIAL2 + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial2_bytes_available()); +#else + mp_raise_NotImplementedError(translate("serial2 not available")); +#endif } -MP_DEFINE_CONST_FUN_OBJ_1(supervisor_get_serial_bytes_available_obj, supervisor_get_serial_bytes_available); +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial2_bytes_available_obj, supervisor_runtime_get_serial2_bytes_available); -const mp_obj_property_t supervisor_serial_bytes_available_obj = { +const mp_obj_property_t supervisor_runtime_serial2_bytes_available_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&supervisor_get_serial_bytes_available_obj, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial2_bytes_available_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; @@ -100,26 +153,28 @@ const mp_obj_property_t supervisor_serial_bytes_available_obj = { //| run_reason: RunReason //| """Returns why CircuitPython started running this particular time.""" //| -STATIC mp_obj_t supervisor_get_run_reason(mp_obj_t self) { +STATIC mp_obj_t supervisor_runtime_get_run_reason(mp_obj_t self) { return cp_enum_find(&supervisor_run_reason_type, _run_reason); } -MP_DEFINE_CONST_FUN_OBJ_1(supervisor_get_run_reason_obj, supervisor_get_run_reason); +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_run_reason_obj, supervisor_runtime_get_run_reason); -const mp_obj_property_t supervisor_run_reason_obj = { +const mp_obj_property_t supervisor_runtime_run_reason_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&supervisor_get_run_reason_obj, + .proxy = {(mp_obj_t)&supervisor_runtime_get_run_reason_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; -void supervisor_set_run_reason(supervisor_run_reason_t run_reason) { +void supervisor_runtime_set_run_reason(supervisor_runtime_run_reason_t run_reason) { _run_reason = run_reason; } STATIC const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_serial_connected_obj) }, - { MP_ROM_QSTR(MP_QSTR_serial_bytes_available), MP_ROM_PTR(&supervisor_serial_bytes_available_obj) }, - { MP_ROM_QSTR(MP_QSTR_run_reason), MP_ROM_PTR(&supervisor_run_reason_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_runtime_serial_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial2_connected), MP_ROM_PTR(&supervisor_runtime_serial2_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial_bytes_available), MP_ROM_PTR(&supervisor_runtime_serial_bytes_available_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial2_bytes_available), MP_ROM_PTR(&supervisor_runtime_serial2_bytes_available_obj) }, + { MP_ROM_QSTR(MP_QSTR_run_reason), MP_ROM_PTR(&supervisor_runtime_run_reason_obj) }, }; STATIC MP_DEFINE_CONST_DICT(supervisor_runtime_locals_dict, supervisor_runtime_locals_dict_table); diff --git a/shared-bindings/supervisor/Runtime.h b/shared-bindings/supervisor/Runtime.h index 51ed7604d..6874ac744 100755 --- a/shared-bindings/supervisor/Runtime.h +++ b/shared-bindings/supervisor/Runtime.h @@ -36,12 +36,12 @@ extern const mp_obj_type_t supervisor_runtime_type; void supervisor_set_run_reason(supervisor_run_reason_t run_reason); -bool common_hal_get_serial_connected(void); +bool common_hal_supervisor_runtime_get_serial_connected(void); -bool common_hal_get_serial_bytes_available(void); +bool common_hal_supervisor_runtime_get_serial_bytes_available(void); //TODO: placeholders for future functions -//bool common_hal_get_repl_active(void); -//bool common_hal_get_usb_enumerated(void); +//bool common_hal_get_supervisor_runtime_repl_active(void); +//bool common_hal_get_supervisor_runtime_usb_enumerated(void); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_SUPERVISOR_RUNTIME_H -- cgit v1.2.3 From d54b5861a30b1b1a352e1c22fc67e729ec95390d Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 12 Feb 2021 19:01:14 -0500 Subject: wip --- ports/atmel-samd/mpconfigport.mk | 4 +- ports/nrf/bluetooth/ble_uart.c | 4 +- ports/nrf/common-hal/supervisor/Runtime.c | 14 --- ports/nrf/supervisor/serial.c | 10 +- ports/raspberrypi/common-hal/pwmio/PWMOut.c | 2 + py/circuitpy_defns.mk | 3 + py/circuitpy_mpconfig.h | 8 ++ py/circuitpy_mpconfig.mk | 32 +++--- shared-bindings/usb_cdc/Serial.c | 139 ++++++++++++++++++++++++ shared-bindings/usb_cdc/Serial.h | 40 +++++++ shared-bindings/usb_cdc/__init__.c | 57 ++++++++++ shared-bindings/usb_cdc/__init__.h | 34 ++++++ shared-module/usb_cdc/Serial.c | 44 ++++++++ shared-module/usb_cdc/Serial.h | 38 +++++++ shared-module/usb_cdc/__init__.c | 55 ++++++++++ shared-module/usb_cdc/__init__.h | 32 ++++++ supervisor/shared/serial.c | 10 -- supervisor/supervisor.mk | 162 ++++++++++++++++------------ tools/gen_usb_descriptor.py | 2 +- 19 files changed, 566 insertions(+), 124 deletions(-) create mode 100644 shared-bindings/usb_cdc/Serial.c create mode 100644 shared-bindings/usb_cdc/Serial.h create mode 100644 shared-bindings/usb_cdc/__init__.c create mode 100644 shared-bindings/usb_cdc/__init__.h create mode 100644 shared-module/usb_cdc/Serial.c create mode 100644 shared-module/usb_cdc/Serial.h create mode 100644 shared-module/usb_cdc/__init__.c create mode 100644 shared-module/usb_cdc/__init__.h (limited to 'shared-bindings') diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 9839a2aa4..e47acfb36 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -49,9 +49,9 @@ CIRCUITPY_SDCARDIO ?= 0 CIRCUITPY_FRAMEBUFFERIO ?= 0 # SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic. -# Because of that, there aren't enough endpoints for serial2. +# Because of that, there aren't enough endpoints for a secondary CDC serial connection. USB_MSC_EP_NUM_OUT = 1 -CIRCUITPY_USB_SERIAL2 = 0 +CIRCUITPY_USB_CDC = 0 CIRCUITPY_ULAB = 0 diff --git a/ports/nrf/bluetooth/ble_uart.c b/ports/nrf/bluetooth/ble_uart.c index 1e7a319bd..1b87b82d1 100644 --- a/ports/nrf/bluetooth/ble_uart.c +++ b/ports/nrf/bluetooth/ble_uart.c @@ -40,7 +40,7 @@ #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" -#if CIRCUITPY_SERIAL_BLE +#if CIRCUITPY_REPL_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"; @@ -190,4 +190,4 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { } } -#endif // CIRCUITPY_SERIAL_BLE +#endif // CIRCUITPY_REPL_BLE diff --git a/ports/nrf/common-hal/supervisor/Runtime.c b/ports/nrf/common-hal/supervisor/Runtime.c index a8f50f70e..def609cda 100755 --- a/ports/nrf/common-hal/supervisor/Runtime.c +++ b/ports/nrf/common-hal/supervisor/Runtime.c @@ -35,17 +35,3 @@ bool common_hal_supervisor_runtime_get_serial_connected(void) { bool common_hal_get_supervisor_runtime_serial_bytes_available(void) { return (bool) serial_bytes_available(); } - -#if CIRCUITPY_USB_SERIAL2 -mp_obj_t common_hal_supervisor_runtime_get_serial2(void) { - return (bool) serial_connected(); -} - -bool common_hal_supervisor_runtime_get_serial2_connected(void) { - return (bool) serial_connected(); -} - -bool common_hal_get_supervisor_runtime_serial2_bytes_available(void) { - return (bool) serial_bytes_available(); -} -#endif diff --git a/ports/nrf/supervisor/serial.c b/ports/nrf/supervisor/serial.c index b19e9267c..2c2c0116f 100644 --- a/ports/nrf/supervisor/serial.c +++ b/ports/nrf/supervisor/serial.c @@ -28,15 +28,15 @@ #include "supervisor/serial.h" -#if CIRCUITPY_SERIAL_BLE +#if CIRCUITPY_REPL_BLE #include "ble_uart.h" -#elif CIRCUITPY_SERIAL_UART +#elif CIRCUITPY_REPL_UART #include #include "nrf_gpio.h" #include "nrfx_uarte.h" #endif -#if CIRCUITPY_SERIAL_BLE +#if CIRCUITPY_REPL_BLE void serial_init(void) { ble_uart_init(); @@ -58,7 +58,7 @@ void serial_write(const char *text) { ble_uart_stdout_tx_str(text); } -#elif CIRCUITPY_SERIAL_UART +#elif CIRCUITPY_REPL_UART uint8_t serial_received_char; nrfx_uarte_t serial_instance = NRFX_UARTE_INSTANCE(0); @@ -124,4 +124,4 @@ void serial_write_substring(const char *text, uint32_t len) { } } -#endif // CIRCUITPY_SERIAL_UART +#endif // CIRCUITPY_REPL_UART diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c index 3d8979a03..799ee887f 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c @@ -164,6 +164,7 @@ void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t* self) { extern void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t* self, uint16_t duty) { self->duty_cycle = duty; uint16_t actual_duty = duty * self->top / ((1 << 16) - 1); + mp_printf(&mp_plat_print, "actual_duty: %d, self->top: %d\n", actual_duty, top); /// *** pwm_set_chan_level(self->slice, self->channel, actual_duty); } @@ -200,6 +201,7 @@ void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t* self, uint32_t fr } else { uint32_t top = common_hal_mcu_processor_get_frequency() / frequency; self->actual_frequency = common_hal_mcu_processor_get_frequency() / top; + mp_printf(&mp_plat_print, "high speed self->top: %d\n", top); /// *** self->top = top; pwm_set_clkdiv_int_frac(self->slice, 1, 0); pwm_set_wrap(self->slice, self->top - 1); diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index f907bf7ae..c6dfbb5f2 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -286,6 +286,9 @@ endif ifeq ($(CIRCUITPY_UHEAP),1) SRC_PATTERNS += uheap/% endif +ifeq ($(CIRCUITPY_USB_CDC),1) +SRC_PATTERNS += usb_cdc/% +endif ifeq ($(CIRCUITPY_USB_HID),1) SRC_PATTERNS += usb_hid/% endif diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index ee23c7156..7db864a42 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -736,6 +736,13 @@ extern const struct _mp_obj_module_t uheap_module; #define UHEAP_MODULE #endif +#if CIRCUITPY_USB_CDC +extern const struct _mp_obj_module_t usb_cdc_module; +#define USB_CDC_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_usb_cdc),(mp_obj_t)&usb_cdc_module }, +#else +#define USB_CDC_MODULE +#endif + #if CIRCUITPY_USB_HID extern const struct _mp_obj_module_t usb_hid_module; #define USB_HID_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module }, @@ -875,6 +882,7 @@ extern const struct _mp_obj_module_t msgpack_module; SUPERVISOR_MODULE \ TOUCHIO_MODULE \ UHEAP_MODULE \ + USB_CDC_MODULE \ USB_HID_MODULE \ USB_MIDI_MODULE \ USTACK_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index b63bbcb3e..1a12bc50b 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -232,6 +232,15 @@ 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 @@ -264,18 +273,6 @@ CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO) CIRCUITPY_SDIOIO ?= 0 CFLAGS += -DCIRCUITPY_SDIOIO=$(CIRCUITPY_SDIOIO) -# Second USB CDC serial channel. -CIRCUITPY_SERIAL2 ?= -CFLAGS += -DCIRCUITPY_SERIAL2=$(CIRCUITPY_SERIAL2) - -# REPL over BLE -CIRCUITPY_SERIAL_BLE ?= 0 -CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) - -# REPL over UART -CIRCUITPY_SERIAL_UART ?= 0 -CFLAGS += -DCIRCUITPY_SERIAL_UART=$(CIRCUITPY_SERIAL_UART) - CIRCUITPY_SHARPDISPLAY ?= $(CIRCUITPY_FRAMEBUFFERIO) CFLAGS += -DCIRCUITPY_SHARPDISPLAY=$(CIRCUITPY_SHARPDISPLAY) @@ -315,6 +312,10 @@ CFLAGS += -DCIRCUITPY_TOUCHIO=$(CIRCUITPY_TOUCHIO) CIRCUITPY_UHEAP ?= 0 CFLAGS += -DCIRCUITPY_UHEAP=$(CIRCUITPY_UHEAP) +# Secondary CDC is usually available if there are at least 8 endpoints. +CIRCUITPY_USB_CDC ?= $(shell expr $(USB_NUM_EP) '>=' 8) +CFLAGS += -DCIRCUITPY_USB_CDC=$(CIRCUITPY_USB_CDC) + CIRCUITPY_USB_HID ?= 1 CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID) @@ -345,9 +346,6 @@ CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) CIRCUITPY_USB_MSC ?= 1 CFLAGS += -DCIRCUITPY_USB_MSC=$(CIRCUITPY_USB_MSC) -CIRCUITPY_USB_SERIAL ?= 1 -CFLAGS += -DCIRCUITPY_USB_SERIAL=$(CIRCUITPY_USB_SERIAL) - CIRCUITPY_USB_VENDOR ?= 0 CFLAGS += -DCIRCUITPY_USB_VENDOR=$(CIRCUITPY_USB_VENDOR) @@ -355,10 +353,6 @@ ifndef USB_NUM_EP $(error "USB_NUM_EP (number of USB endpoint pairs)must be defined") endif -# Secondary CDC is usually available if there are at least 8 endpoints. -CIRCUITPY_USB_SERIAL2 ?= $(shell expr $(USB_NUM_EP) '>=' 8) -CFLAGS += -DCIRCUITPY_USB_SERIAL2=$(CIRCUITPY_USB_SERIAL2) - # For debugging. CIRCUITPY_USTACK ?= 0 CFLAGS += -DCIRCUITPY_USTACK=$(CIRCUITPY_USTACK) diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c new file mode 100644 index 000000000..72e9482a2 --- /dev/null +++ b/shared-bindings/usb_cdc/Serial.c @@ -0,0 +1,139 @@ +/* + * 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 "shared-bindings/usb_cdc/Serial.h" +#include "shared-bindings/util.h" + +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "supervisor/shared/translate.h" + +//| class Serial: +//| """Receives cdc commands over USB""" +//| +//| def __init__(self) -> None: +//| """You cannot create an instance of `usb_cdc.Serial`. +//| +//| Serial objects are constructed for every corresponding entry in the USB +//| descriptor and added to the ``usb_cdc.ports`` tuple.""" +//| ... +//| + +// These are standard stream methods. Code is in py/stream.c. +// +//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: +//| """Read characters. If ``nbytes`` is specified then read at most that many +//| bytes. Otherwise, read everything that arrives until the connection +//| times out. Providing the number of bytes expected is highly recommended +//| because it will be faster. +//| +//| :return: Data read +//| :rtype: bytes or None""" +//| ... +//| +//| def readinto(self, buf: WriteableBuffer, nbytes: Optional[int] = None) -> Optional[bytes]: +//| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most +//| that many bytes. Otherwise, read at most ``len(buf)`` bytes. +//| +//| :return: number of bytes read and stored into ``buf`` +//| :rtype: bytes or None""" +//| ... +//| +//| def write(self, buf: ReadableBuffer) -> Optional[int]: +//| """Write the buffer of bytes to the bus. +//| +//| :return: the number of bytes written +//| :rtype: int or None""" +//| ... +//| + +// These three methods are used by the shared stream methods. +STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + return common_hal_usb_cdc_serial_read(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_cdc_serial_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + const byte *buf = buf_in; + + return common_hal_usb_cdc_serial_write(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_cdc_serial_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_cdc_serial_bytes_available(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } + if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_cdc_serial_ready_to_tx(self)) { + ret |= MP_IOCTL_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_dict_table); + +STATIC const mp_stream_p_t usb_cdc_serial_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = usb_cdc_serial_read, + .write = usb_cdc_serial_write, + .ioctl = usb_cdc_serial_ioctl, + .is_text = false, +}; + +const mp_obj_type_t usb_cdc_serial_type = { + { &mp_type_type }, + .name = MP_QSTR_Serial, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &usb_cdc_serial_stream_p, + .locals_dict = (mp_obj_dict_t*)&usb_cdc_serial_locals_dict, +}; diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h new file mode 100644 index 000000000..1ef4167d5 --- /dev/null +++ b/shared-bindings/usb_cdc/Serial.h @@ -0,0 +1,40 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H + +#include "shared-module/usb_cdc/Serial.h" + +extern const mp_obj_type_t usb_cdc_serial_type; + +extern size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode); +extern uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_clear_buffer(usb_cdc_serial_obj_t *self); +extern size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode); +extern bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-bindings/usb_cdc/__init__.c b/shared-bindings/usb_cdc/__init__.c new file mode 100644 index 000000000..d6a70b177 --- /dev/null +++ b/shared-bindings/usb_cdc/__init__.c @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbertfor Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/usb_cdc/__init__.h" +#include "shared-bindings/usb_cdc/Serial.h" + +#include "py/runtime.h" + +//| """USB CDC Serial streams +//| +//| The `usb_cdc` module allows access to USB CDC (serial) communications. +//| +//| serial: Tuple[Serial, ...] +//| """Tuple of all CDC streams. Each item is a `Serial`.""" +//| + +mp_map_elem_t usb_cdc_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_cdc) }, + { MP_ROM_QSTR(MP_QSTR_Serial), MP_OBJ_FROM_PTR(&usb_cdc_serial_type) }, + { MP_ROM_QSTR(MP_QSTR_serial), mp_const_empty_tuple }, +}; + +// This isn't const so we can set the streams dynamically. +MP_DEFINE_MUTABLE_DICT(usb_cdc_module_globals, usb_cdc_module_globals_table); + +const mp_obj_module_t usb_cdc_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&usb_cdc_module_globals, +}; diff --git a/shared-bindings/usb_cdc/__init__.h b/shared-bindings/usb_cdc/__init__.h new file mode 100644 index 000000000..7938e9172 --- /dev/null +++ b/shared-bindings/usb_cdc/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H + +#include "py/obj.h" + +extern mp_obj_dict_t usb_cdc_module_globals; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c new file mode 100644 index 000000000..663b23c25 --- /dev/null +++ b/shared-module/usb_cdc/Serial.c @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-module/usb_cdc/Serial.h" +#include "tusb.h" + +size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { + return tud_cdc_n_read(self->idx, data, len); +} + +uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_available(self->idx); +} + +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + return tud_cdc_n_write(self->idx, data, len); +} + +bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_connected(self->idx); +} diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h new file mode 100644 index 000000000..7e3c53e8a --- /dev/null +++ b/shared-module/usb_cdc/Serial.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SHARED_MODULE_USB_CDC_SERIAL_H +#define SHARED_MODULE_USB_CDC_SERIAL_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + // Which CDC device? + uint8_t idx; +} usb_cdc_serial_obj_t; + +#endif // SHARED_MODULE_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c new file mode 100644 index 000000000..ae2412cdd --- /dev/null +++ b/shared-module/usb_cdc/__init__.c @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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/usb_cdc/__init__.h" + +#include "genhdr/autogen_usb_descriptor.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "py/objtuple.h" +#include "shared-bindings/usb_cdc/Serial.h" +#include "tusb.h" + +supervisor_allocation* usb_cdc_allocation; + +static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC]; + +void usb_cdc_init(void) { + + serial_obj_ptrs *usb_cdc_serial_obj_t[CFG_TUD_CDC]; + + for (size_t i = 0; i < CFG_TUD_CDC; i++) { + serial_objs[i].base.type = &usb_cdc_serial_type; + serial_objs[i].idx = i; + serial_obj_ptrs[i] = &serial_objs[i]; + } + + serials_tuple->base.type = mp_obj_new_tuple(CFG_TUD_CDC, serials); + + repl->base.type = + repl->idx = 0; mp_map_lookup(&usb_cdc_module_globals.map, MP_ROM_QSTR(MP_QSTR_serial), MP_MAP_LOOKUP)->value = MP_OBJ_FROM_PTR(serials_tuple); +} diff --git a/shared-module/usb_cdc/__init__.h b/shared-module/usb_cdc/__init__.h new file mode 100644 index 000000000..02b48e116 --- /dev/null +++ b/shared-module/usb_cdc/__init__.h @@ -0,0 +1,32 @@ +/* + * 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_USB_CDC___INIT___H +#define SHARED_MODULE_USB_CDC___INIT___H + +void usb_cdc_init(void); + +#endif /* SHARED_MODULE_USB_CDC___INIT___H */ diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 40151cbf6..9cc12de51 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -148,13 +148,3 @@ void serial_write_substring(const char* text, uint32_t length) { void serial_write(const char* text) { serial_write_substring(text, strlen(text)); } - -#if CIRCUITPY_USB_SERIAL2 -bool serial2_bytes_available(void) { - return tud_cdc_n_available(1) > 0; -} - -bool serial2_connected(void) { - return tud_cdc_n_connected(1); -} -#endif diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index b36b9bc00..374b7a72b 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -33,84 +33,100 @@ endif # (Right now INTERNAL_FLASH_FILESYSTEM and (Q)SPI_FLASH_FILESYSTEM are mutually exclusive. # But that might not be true in the future.) ifdef EXTERNAL_FLASH_DEVICES - CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \ - -DEXTERNAL_FLASH_DEVICE_COUNT=$(EXTERNAL_FLASH_DEVICE_COUNT) - - SRC_SUPERVISOR += supervisor/shared/external_flash/external_flash.c - ifeq ($(SPI_FLASH_FILESYSTEM),1) - SRC_SUPERVISOR += supervisor/shared/external_flash/spi_flash.c - else - endif - ifeq ($(QSPI_FLASH_FILESYSTEM),1) - SRC_SUPERVISOR += supervisor/qspi_flash.c supervisor/shared/external_flash/qspi_flash.c - endif + CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \ + -DEXTERNAL_FLASH_DEVICE_COUNT=$(EXTERNAL_FLASH_DEVICE_COUNT) + + SRC_SUPERVISOR += supervisor/shared/external_flash/external_flash.c + ifeq ($(SPI_FLASH_FILESYSTEM),1) + SRC_SUPERVISOR += supervisor/shared/external_flash/spi_flash.c + endif + ifeq ($(QSPI_FLASH_FILESYSTEM),1) + SRC_SUPERVISOR += supervisor/qspi_flash.c supervisor/shared/external_flash/qspi_flash.c + endif else - ifeq ($(DISABLE_FILESYSTEM),1) - SRC_SUPERVISOR += supervisor/stub/internal_flash.c - else - SRC_SUPERVISOR += supervisor/internal_flash.c - endif + ifeq ($(DISABLE_FILESYSTEM),1) + SRC_SUPERVISOR += supervisor/stub/internal_flash.c + else + SRC_SUPERVISOR += supervisor/internal_flash.c + endif endif ifeq ($(USB),FALSE) - ifeq ($(wildcard supervisor/serial.c),) - SRC_SUPERVISOR += supervisor/stub/serial.c - else - SRC_SUPERVISOR += supervisor/serial.c - endif + ifeq ($(wildcard supervisor/serial.c),) + SRC_SUPERVISOR += supervisor/stub/serial.c + else + SRC_SUPERVISOR += supervisor/serial.c + endif else - SRC_SUPERVISOR += \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_control.c \ - lib/tinyusb/src/class/msc/msc_device.c \ - lib/tinyusb/src/class/cdc/cdc_device.c \ - lib/tinyusb/src/tusb.c \ - supervisor/shared/serial.c \ - supervisor/shared/workflow.c \ - supervisor/usb.c \ - supervisor/shared/usb/usb_desc.c \ - supervisor/shared/usb/usb.c \ - supervisor/shared/usb/usb_msc_flash.c \ - $(BUILD)/autogen_usb_descriptor.c - - ifeq ($(CIRCUITPY_USB_HID), 1) - SRC_SUPERVISOR += \ - lib/tinyusb/src/class/hid/hid_device.c \ - shared-bindings/usb_hid/__init__.c \ - shared-bindings/usb_hid/Device.c \ - shared-module/usb_hid/__init__.c \ - shared-module/usb_hid/Device.c - endif - - ifeq ($(CIRCUITPY_USB_MIDI), 1) - SRC_SUPERVISOR += \ - lib/tinyusb/src/class/midi/midi_device.c \ - shared-bindings/usb_midi/__init__.c \ - shared-bindings/usb_midi/PortIn.c \ - shared-bindings/usb_midi/PortOut.c \ - shared-module/usb_midi/__init__.c \ - shared-module/usb_midi/PortIn.c \ - shared-module/usb_midi/PortOut.c - endif - - ifeq ($(CIRCUITPY_USB_VENDOR), 1) - SRC_SUPERVISOR += \ - lib/tinyusb/src/class/vendor/vendor_device.c - endif - - CFLAGS += -DUSB_AVAILABLE + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + lib/tinyusb/src/tusb.c \ + supervisor/shared/serial.c \ + supervisor/shared/workflow.c \ + supervisor/usb.c \ + supervisor/shared/usb/usb_desc.c \ + supervisor/shared/usb/usb.c \ + $(BUILD)/autogen_usb_descriptor.c \ + + ifeq ($(CIRCUITPY_USB_CDC), 1) + SRC_SUPERVISOR += \ + shared-bindings/usb_cdc/__init__.c \ + shared-bindings/usb_cdc/Serial.c \ + shared-module/usb_cdc/__init__.c \ + shared-module/usb_cdc/Serial.c \ + + endif + + ifeq ($(CIRCUITPY_USB_HID), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/hid/hid_device.c \ + shared-bindings/usb_hid/__init__.c \ + shared-bindings/usb_hid/Device.c \ + shared-module/usb_hid/__init__.c \ + shared-module/usb_hid/Device.c \ + + endif + + ifeq ($(CIRCUITPY_USB_MIDI), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/midi/midi_device.c \ + shared-bindings/usb_midi/__init__.c \ + shared-bindings/usb_midi/PortIn.c \ + shared-bindings/usb_midi/PortOut.c \ + shared-module/usb_midi/__init__.c \ + shared-module/usb_midi/PortIn.c \ + shared-module/usb_midi/PortOut.c \ + + endif + + ifeq ($(CIRCUITPY_USB_MSC), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/msc/msc_device.c \ + supervisor/shared/usb/usb_msc_flash.c \ + + endif + + ifeq ($(CIRCUITPY_USB_VENDOR), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/vendor/vendor_device.c \ + + endif + + CFLAGS += -DUSB_AVAILABLE endif SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) ifeq ($(CIRCUITPY_DISPLAYIO), 1) - SRC_SUPERVISOR += \ - supervisor/shared/display.c + SRC_SUPERVISOR += \ + supervisor/shared/display.c - ifeq ($(CIRCUITPY_TERMINALIO), 1) - SUPERVISOR_O += $(BUILD)/autogen_display_resources.o - endif + ifeq ($(CIRCUITPY_TERMINALIO), 1) + SUPERVISOR_O += $(BUILD)/autogen_display_resources.o + endif endif USB_INTERFACE_NAME ?= "CircuitPython" @@ -126,16 +142,20 @@ endif # It gets added automatically. USB_WEBUSB_URL ?= "circuitpython.org" +ifeq ($(CIRCUITPY_REPL_USB),1) +USB_DEVICES += CDC +endif + +ifeq ($(CIRCUITPY_USB_HID),1) +USB_DEVICES += HID +endif ifeq ($(CIRCUITPY_USB_MIDI),1) USB_DEVICES += AUDIO endif ifeq ($(CIRCUITPY_USB_MSC),1) USB_DEVICES += MSC endif -ifeq ($(CIRCUITPY_USB_SERIAL),1) -USB_DEVICES += CDC -endif -ifeq ($(CIRCUITPY_USB_SERIAL2),1) +ifeq ($(CIRCUITPY_USB_CDC),1) # Inform TinyUSB there are two CDC devices. CFLAGS += -DCFG_TUD_CDC=2 USB_DEVICES += CDC2 diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py index b056f853a..3f4249f0a 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -830,7 +830,6 @@ extern uint16_t const * const string_desc_arr [{string_descriptor_length}]; rhport0_mode="OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED" if args.highspeed else "OPT_MODE_DEVICE", - hid_num_devices=len(args.hid_devices), msc_vendor=args.manufacturer[:8], msc_product=args.product[:16], ) @@ -844,6 +843,7 @@ extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}]; #define USB_HID_NUM_DEVICES {hid_num_devices} """.format( hid_report_descriptor_length=len(bytes(combined_hid_report_descriptor)), + hid_num_devices=len(args.hid_devices), ) ) -- cgit v1.2.3 From 0b8f1b9a9039ab2ad4d43cc1d9bc388518ab0d38 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 15 Feb 2021 20:06:18 -0500 Subject: wip: usb_cdc.serials --- main.c | 26 ++++++++++-------- ports/atmel-samd/Makefile | 2 +- ports/raspberrypi/common-hal/pwmio/PWMOut.c | 2 -- shared-bindings/usb_cdc/__init__.c | 9 +++---- shared-bindings/usb_cdc/__init__.h | 4 +-- shared-module/usb_cdc/Serial.h | 2 +- shared-module/usb_cdc/__init__.c | 42 +++++++++++++++-------------- shared-module/usb_cdc/__init__.h | 4 ++- supervisor/shared/usb/usb.c | 5 +++- 9 files changed, 51 insertions(+), 45 deletions(-) (limited to 'shared-bindings') diff --git a/main.c b/main.c index 7bfa565df..7df6e293a 100755 --- a/main.c +++ b/main.c @@ -69,6 +69,19 @@ #include "shared-bindings/alarm/__init__.h" #endif +#if CIRCUITPY_BLEIO +#include "shared-bindings/_bleio/__init__.h" +#include "supervisor/shared/bluetooth.h" +#endif + +#if CIRCUITPY_BOARD +#include "shared-module/board/__init__.h" +#endif + +#if CIRCUITPY_CANIO +#include "common-hal/canio/CAN.h" +#endif + #if CIRCUITPY_DISPLAYIO #include "shared-module/displayio/__init__.h" #endif @@ -81,17 +94,8 @@ #include "shared-module/network/__init__.h" #endif -#if CIRCUITPY_BOARD -#include "shared-module/board/__init__.h" -#endif - -#if CIRCUITPY_BLEIO -#include "shared-bindings/_bleio/__init__.h" -#include "supervisor/shared/bluetooth.h" -#endif - -#if CIRCUITPY_CANIO -#include "common-hal/canio/CAN.h" +#if CIRCUITPY_USB_CDC +#include "shared-module/usb_cdc/__init__.h" #endif #if CIRCUITPY_WIFI diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 58c1c0d60..48724af0c 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -122,7 +122,7 @@ CFLAGS += -ftree-vrp $(echo PERIPHERALS_CHIP_FAMILY=$(PERIPHERALS_CHIP_FAMILY)) #Debugging/Optimization ifeq ($(DEBUG), 1) - CFLAGS += -ggdb3 -Og + CFLAGS += -ggdb3 -Og -Os # You may want to disable -flto if it interferes with debugging. CFLAGS += -flto -flto-partition=none # You may want to enable these flags to make setting breakpoints easier. diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c index 6e23e236b..e861bab5a 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c @@ -165,7 +165,6 @@ void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t* self) { extern void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t* self, uint16_t duty) { self->duty_cycle = duty; uint16_t actual_duty = duty * self->top / ((1 << 16) - 1); - mp_printf(&mp_plat_print, "actual_duty: %d, self->top: %d\n", actual_duty, top); /// *** pwm_set_chan_level(self->slice, self->channel, actual_duty); } @@ -209,7 +208,6 @@ void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t* self, uint32_t fr } else { uint32_t top = common_hal_mcu_processor_get_frequency() / frequency; self->actual_frequency = common_hal_mcu_processor_get_frequency() / top; - mp_printf(&mp_plat_print, "high speed self->top: %d\n", top); /// *** self->top = top; pwm_set_clkdiv_int_frac(self->slice, 1, 0); pwm_set_wrap(self->slice, self->top - 1); diff --git a/shared-bindings/usb_cdc/__init__.c b/shared-bindings/usb_cdc/__init__.c index d6a70b177..6ded44c4f 100644 --- a/shared-bindings/usb_cdc/__init__.c +++ b/shared-bindings/usb_cdc/__init__.c @@ -38,18 +38,17 @@ //| //| The `usb_cdc` module allows access to USB CDC (serial) communications. //| -//| serial: Tuple[Serial, ...] +//| serials: Tuple[Serial, ...] //| """Tuple of all CDC streams. Each item is a `Serial`.""" //| -mp_map_elem_t usb_cdc_module_globals_table[] = { +static const mp_map_elem_t usb_cdc_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_cdc) }, { MP_ROM_QSTR(MP_QSTR_Serial), MP_OBJ_FROM_PTR(&usb_cdc_serial_type) }, - { MP_ROM_QSTR(MP_QSTR_serial), mp_const_empty_tuple }, + { MP_ROM_QSTR(MP_QSTR_serials), MP_OBJ_FROM_PTR(&usb_cdc_serials_tuple) }, }; -// This isn't const so we can set the streams dynamically. -MP_DEFINE_MUTABLE_DICT(usb_cdc_module_globals, usb_cdc_module_globals_table); +static MP_DEFINE_CONST_DICT(usb_cdc_module_globals, usb_cdc_module_globals_table); const mp_obj_module_t usb_cdc_module = { .base = { &mp_type_module }, diff --git a/shared-bindings/usb_cdc/__init__.h b/shared-bindings/usb_cdc/__init__.h index 7938e9172..e81d243e6 100644 --- a/shared-bindings/usb_cdc/__init__.h +++ b/shared-bindings/usb_cdc/__init__.h @@ -27,8 +27,6 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H -#include "py/obj.h" - -extern mp_obj_dict_t usb_cdc_module_globals; +#include "shared-module/usb_cdc/__init__.h" #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h index 7e3c53e8a..60a1d1922 100644 --- a/shared-module/usb_cdc/Serial.h +++ b/shared-module/usb_cdc/Serial.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * 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 diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index ae2412cdd..ecb5777b2 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2018 hathach for Adafruit Industries + * 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,32 +24,34 @@ * THE SOFTWARE. */ -#include "shared-bindings/usb_cdc/__init__.h" - #include "genhdr/autogen_usb_descriptor.h" +#include "py/gc.h" #include "py/obj.h" #include "py/mphal.h" #include "py/runtime.h" #include "py/objtuple.h" +#include "shared-bindings/usb_cdc/__init__.h" #include "shared-bindings/usb_cdc/Serial.h" #include "tusb.h" -supervisor_allocation* usb_cdc_allocation; - -static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC]; +#if CFG_TUD_CDC != 2 +#error CFG_TUD_CDC must be exactly 2 +#endif -void usb_cdc_init(void) { - - serial_obj_ptrs *usb_cdc_serial_obj_t[CFG_TUD_CDC]; - - for (size_t i = 0; i < CFG_TUD_CDC; i++) { - serial_objs[i].base.type = &usb_cdc_serial_type; - serial_objs[i].idx = i; - serial_obj_ptrs[i] = &serial_objs[i]; +static const usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { + { .base.type = &usb_cdc_serial_type, + .idx = 0, + }, { + .base.type = &usb_cdc_serial_type, + .idx = 1, } - - serials_tuple->base.type = mp_obj_new_tuple(CFG_TUD_CDC, serials); - - repl->base.type = - repl->idx = 0; mp_map_lookup(&usb_cdc_module_globals.map, MP_ROM_QSTR(MP_QSTR_serial), MP_MAP_LOOKUP)->value = MP_OBJ_FROM_PTR(serials_tuple); -} +}; + +const mp_rom_obj_tuple_t usb_cdc_serials_tuple = { + .base.type = &mp_type_tuple, + .len = CFG_TUD_CDC, + .items = { + &serial_objs[0], + &serial_objs[1], + }, +}; diff --git a/shared-module/usb_cdc/__init__.h b/shared-module/usb_cdc/__init__.h index 02b48e116..9de3eb2fa 100644 --- a/shared-module/usb_cdc/__init__.h +++ b/shared-module/usb_cdc/__init__.h @@ -27,6 +27,8 @@ #ifndef SHARED_MODULE_USB_CDC___INIT___H #define SHARED_MODULE_USB_CDC___INIT___H -void usb_cdc_init(void); +#include "py/objtuple.h" + +extern const mp_rom_obj_tuple_t usb_cdc_serials_tuple; #endif /* SHARED_MODULE_USB_CDC___INIT___H */ diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 43564d5d5..af48f4673 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -26,7 +26,6 @@ #include "py/objstr.h" #include "shared-bindings/microcontroller/Processor.h" -#include "shared-module/usb_midi/__init__.h" #include "supervisor/background_callback.h" #include "supervisor/port.h" #include "supervisor/serial.h" @@ -35,6 +34,10 @@ #include "lib/utils/interrupt_char.h" #include "lib/mp-readline/readline.h" +#if CIRCUITPY_USB_MIDI +#include "shared-module/usb_midi/__init__.h" +#endif + #include "tusb.h" #if CIRCUITPY_USB_VENDOR -- cgit v1.2.3 From c26de0136a0ad746bf0ace600a1a9d3ff428c4c6 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 16 Feb 2021 17:11:37 -0500 Subject: works! no timeouts --- ports/cxd56/mpconfigport.mk | 3 + shared-bindings/usb_cdc/Serial.c | 122 +++++++++++++++++++++++++++++++++---- shared-bindings/usb_cdc/Serial.h | 13 +++- shared-bindings/usb_cdc/__init__.c | 7 ++- shared-module/usb_cdc/Serial.c | 27 ++++++-- tools/gen_usb_descriptor.py | 87 ++++++++++++++++++-------- 6 files changed, 213 insertions(+), 46 deletions(-) (limited to 'shared-bindings') diff --git a/ports/cxd56/mpconfigport.mk b/ports/cxd56/mpconfigport.mk index e1ffc79d0..ab39e3bb7 100644 --- a/ports/cxd56/mpconfigport.mk +++ b/ports/cxd56/mpconfigport.mk @@ -7,6 +7,9 @@ USB_CDC_EP_NUM_DATA_IN = 1 USB_MSC_EP_NUM_OUT = 5 USB_MSC_EP_NUM_IN = 4 +# Number of USB endpoint pairs. +USB_NUM_EP = 5 + MPY_TOOL_LONGINT_IMPL = -mlongint-impl=mpz CIRCUITPY_AUDIOBUSIO = 0 diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 72e9482a2..03fb94f31 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -67,12 +67,16 @@ //| ... //| //| def write(self, buf: ReadableBuffer) -> Optional[int]: -//| """Write the buffer of bytes to the bus. +//| """Write as many bytes as possible from the buffer of bytes. //| //| :return: the number of bytes written //| :rtype: int or None""" //| ... //| +//| def flush(self) -> None: +//| """Force out any unwritten bytes, waiting until they are written.""" +//| ... +//| // These three methods are used by the shared stream methods. STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { @@ -97,27 +101,121 @@ STATIC mp_uint_t usb_cdc_serial_write(mp_obj_t self_in, const void *buf_in, mp_u STATIC mp_uint_t usb_cdc_serial_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_uint_t ret; - if (request == MP_IOCTL_POLL) { - mp_uint_t flags = arg; - ret = 0; - if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_cdc_serial_bytes_available(self) > 0) { - ret |= MP_IOCTL_POLL_RD; - } - if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_cdc_serial_ready_to_tx(self)) { - ret |= MP_IOCTL_POLL_WR; + switch (request) { + case MP_IOCTL_POLL: { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_cdc_serial_get_in_waiting(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } + if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_cdc_serial_get_out_waiting(self) == 0) { + ret |= MP_IOCTL_POLL_WR; + } + break; } - } else { - *errcode = MP_EINVAL; - ret = MP_STREAM_ERROR; + + case MP_STREAM_FLUSH: + common_hal_usb_cdc_serial_flush(self); + break; + + default: + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; } return ret; } +//| connected: bool +//| """True if this Serial is connected to a host. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_connected(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_usb_cdc_serial_get_connected(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_connected_obj, usb_cdc_serial_get_connected); + +const mp_obj_property_t usb_cdc_serial__connected_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| in_waiting: int +//| """Returns the number of bytes waiting to be read on the USB serial input. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_in_waiting(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_usb_cdc_serial_get_in_waiting(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_in_waiting_obj, usb_cdc_serial_get_in_waiting); + +const mp_obj_property_t usb_cdc_serial_in_waiting_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_in_waiting_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| out_waiting: int +//| """Returns the number of bytes waiting to be written on the USB serial output. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_out_waiting(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_usb_cdc_serial_get_out_waiting(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_out_waiting_obj, usb_cdc_serial_get_out_waiting); + +const mp_obj_property_t usb_cdc_serial_out_waiting_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_out_waiting_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| def reset_input_buffer(self) -> None: +//| """Clears any unread bytes.""" +//| ... +//| +STATIC mp_obj_t usb_cdc_serial_reset_input_buffer(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_reset_input_buffer(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_input_buffer_obj, usb_cdc_serial_reset_input_buffer); + +//| def reset_output_buffer(self) -> None: +//| """Clears any unwritten bytes.""" +//| ... +//| +STATIC mp_obj_t usb_cdc_serial_reset_output_buffer(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_reset_output_buffer(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial_reset_output_buffer); + + STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { // Standard stream methods. + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)}, + { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj)}, { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + + // Other pyserial-inspired attributes. + { MP_OBJ_NEW_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&usb_cdc_serial_in_waiting_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_out_waiting), MP_ROM_PTR(&usb_cdc_serial_out_waiting_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, + + // Not in pyserial protocol. + { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_get_connected_obj) }, + + + }; STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_dict_table); diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 1ef4167d5..6e54b9ad3 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -32,9 +32,16 @@ extern const mp_obj_type_t usb_cdc_serial_type; extern size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode); -extern uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self); -extern void common_hal_usb_cdc_serial_clear_buffer(usb_cdc_serial_obj_t *self); extern size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode); -extern bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self); + +extern uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self); +extern uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self); + +extern void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self); +extern uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self); + +extern uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self); + +extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-bindings/usb_cdc/__init__.c b/shared-bindings/usb_cdc/__init__.c index 6ded44c4f..eb9c25e00 100644 --- a/shared-bindings/usb_cdc/__init__.c +++ b/shared-bindings/usb_cdc/__init__.c @@ -36,10 +36,13 @@ //| """USB CDC Serial streams //| -//| The `usb_cdc` module allows access to USB CDC (serial) communications. +//| The `usb_cdc` module allows access to USB CDC (serial) communications.""" //| //| serials: Tuple[Serial, ...] -//| """Tuple of all CDC streams. Each item is a `Serial`.""" +//| """Tuple of all CDC streams. Each item is a `Serial`. +//| ``serials[0]`` is the USB REPL connection. +//| ``serials[1]`` is a second USB serial connection, unconnected to the REPL. +//| """ //| static const mp_map_elem_t usb_cdc_module_globals_table[] = { diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index 663b23c25..bd3972c8d 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -31,14 +31,33 @@ size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, return tud_cdc_n_read(self->idx, data, len); } -uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self) { +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + uint32_t num_written = tud_cdc_n_write(self->idx, data, len); + tud_cdc_n_write_flush(self->idx); + return num_written; +} + +uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) { return tud_cdc_n_available(self->idx); } -size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { - return tud_cdc_n_write(self->idx, data, len); +uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self) { + // Return number of FIFO bytes currently occupied. + return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(self->idx); +} + +void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self) { + tud_cdc_n_read_flush(self->idx); +} + +uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_write_clear(self->idx); +} + +uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_write_flush(self->idx); } -bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self) { +bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) { return tud_cdc_n_connected(self->idx); } diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py index 3f4249f0a..089d00ff2 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -76,12 +76,27 @@ parser.add_argument( default=0, help="endpoint number of CDC NOTIFICATION", ) +parser.add_argument( + "--cdc2_ep_num_notification", + type=int, + default=0, + help="endpoint number of CDC2 NOTIFICATION", +) parser.add_argument( "--cdc_ep_num_data_out", type=int, default=0, help="endpoint number of CDC DATA OUT" ) parser.add_argument( "--cdc_ep_num_data_in", type=int, default=0, help="endpoint number of CDC DATA IN" ) +parser.add_argument( + "--cdc2_ep_num_data_out", + type=int, + default=0, + help="endpoint number of CDC2 DATA OUT", +) +parser.add_argument( + "--cdc2_ep_num_data_in", type=int, default=0, help="endpoint number of CDC2 DATA IN" +) parser.add_argument( "--msc_ep_num_out", type=int, default=0, help="endpoint number of MSC OUT" ) @@ -146,36 +161,41 @@ if not args.renumber_endpoints: if include_cdc: if args.cdc_ep_num_notification == 0: raise ValueError("CDC notification endpoint number must not be 0") - elif args.cdc_ep_num_data_out == 0: + if args.cdc_ep_num_data_out == 0: raise ValueError("CDC data OUT endpoint number must not be 0") - elif args.cdc_ep_num_data_in == 0: + if args.cdc_ep_num_data_in == 0: raise ValueError("CDC data IN endpoint number must not be 0") if include_cdc2: - raise ValueError("Second CDC not supported without renumbering endpoints") + if args.cdc2_ep_num_notification == 0: + raise ValueError("CDC2 notification endpoint number must not be 0") + if args.cdc2_ep_num_data_out == 0: + raise ValueError("CDC2 data OUT endpoint number must not be 0") + if args.cdc2_ep_num_data_in == 0: + raise ValueError("CDC2 data IN endpoint number must not be 0") if include_msc: if args.msc_ep_num_out == 0: raise ValueError("MSC endpoint OUT number must not be 0") - elif args.msc_ep_num_in == 0: + if args.msc_ep_num_in == 0: raise ValueError("MSC endpoint IN number must not be 0") if include_hid: if args.args.hid_ep_num_out == 0: raise ValueError("HID endpoint OUT number must not be 0") - elif args.hid_ep_num_in == 0: + if args.hid_ep_num_in == 0: raise ValueError("HID endpoint IN number must not be 0") if include_audio: if args.args.midi_ep_num_out == 0: raise ValueError("MIDI endpoint OUT number must not be 0") - elif args.midi_ep_num_in == 0: + if args.midi_ep_num_in == 0: raise ValueError("MIDI endpoint IN number must not be 0") if include_vendor: if args.vendor_ep_num_out == 0: raise ValueError("VENDOR endpoint OUT number must not be 0") - elif args.vendor_ep_num_in == 0: + if args.vendor_ep_num_in == 0: raise ValueError("VENDOR endpoint IN number must not be 0") @@ -228,18 +248,22 @@ device = standard.DeviceDescriptor( def make_cdc_union(name): return cdc.Union( description="{} comm".format(name), - bMasterInterface=0x00, # Adjust this after interfaces are renumbered. + # Set bMasterInterface and bSlaveInterface_list to proper values after interfaces are renumbered. + bMasterInterface=0x00, bSlaveInterface_list=[0x01], - ) # Adjust this after interfaces are renumbered. + ) def make_cdc_call_management(name): + # Set bDataInterface to proper value after interfaces are renumbered. return cdc.CallManagement( description="{} comm".format(name), bmCapabilities=0x01, bDataInterface=0x01 - ) # Adjust this after interfaces are renumbered. + ) -def make_cdc_comm_interface(name, cdc_union): +def make_cdc_comm_interface( + name, cdc_union, cdc_call_management, cdc_ep_num_notification +): return standard.InterfaceDescriptor( description="{} comm".format(name), bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class @@ -255,7 +279,7 @@ def make_cdc_comm_interface(name, cdc_union): cdc_union, standard.EndpointDescriptor( description="{} comm in".format(name), - bEndpointAddress=args.cdc_ep_num_notification + bEndpointAddress=cdc_ep_num_notification | standard.EndpointDescriptor.DIRECTION_IN, bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, wMaxPacketSize=0x0040, @@ -265,7 +289,7 @@ def make_cdc_comm_interface(name, cdc_union): ) -def make_cdc_data_interface(name): +def make_cdc_data_interface(name, cdc_ep_num_data_in, cdc_ep_num_data_out): return standard.InterfaceDescriptor( description="{} data".format(name), bInterfaceClass=cdc.CDC_CLASS_DATA, @@ -273,7 +297,7 @@ def make_cdc_data_interface(name): subdescriptors=[ standard.EndpointDescriptor( description="{} data out".format(name), - bEndpointAddress=args.cdc_ep_num_data_out + bEndpointAddress=cdc_ep_num_data_out | standard.EndpointDescriptor.DIRECTION_OUT, bmAttributes=standard.EndpointDescriptor.TYPE_BULK, bInterval=0, @@ -281,7 +305,7 @@ def make_cdc_data_interface(name): ), standard.EndpointDescriptor( description="{} data in".format(name), - bEndpointAddress=args.cdc_ep_num_data_in + bEndpointAddress=cdc_ep_num_data_in | standard.EndpointDescriptor.DIRECTION_IN, bmAttributes=standard.EndpointDescriptor.TYPE_BULK, bInterval=0, @@ -294,16 +318,24 @@ def make_cdc_data_interface(name): if include_cdc: cdc_union = make_cdc_union("CDC") cdc_call_management = make_cdc_call_management("CDC") - cdc_comm_interface = make_cdc_comm_interface("CDC", cdc_union) - cdc_data_interface = make_cdc_data_interface("CDC") + cdc_comm_interface = make_cdc_comm_interface( + "CDC", cdc_union, cdc_call_management, args.cdc_ep_num_notification + ) + cdc_data_interface = make_cdc_data_interface( + "CDC", args.cdc_ep_num_data_in, args.cdc_ep_num_data_out + ) -cdc_interfaces = [cdc_comm_interface, cdc_data_interface] + cdc_interfaces = [cdc_comm_interface, cdc_data_interface] if include_cdc2: cdc2_union = make_cdc_union("CDC2") cdc2_call_management = make_cdc_call_management("CDC2") - cdc2_comm_interface = make_cdc_comm_interface("CDC2", cdc2_union) - cdc2_data_interface = make_cdc_data_interface("CDC2") + cdc2_comm_interface = make_cdc_comm_interface( + "CDC2", cdc2_union, cdc2_call_management, args.cdc2_ep_num_notification + ) + cdc2_data_interface = make_cdc_data_interface( + "CDC2", args.cdc2_ep_num_data_in, args.cdc2_ep_num_data_out + ) cdc2_interfaces = [cdc2_comm_interface, cdc2_data_interface] @@ -842,10 +874,10 @@ extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}]; #define USB_HID_NUM_DEVICES {hid_num_devices} """.format( - hid_report_descriptor_length=len(bytes(combined_hid_report_descriptor)), - hid_num_devices=len(args.hid_devices), + hid_report_descriptor_length=len(bytes(combined_hid_report_descriptor)), + hid_num_devices=len(args.hid_devices), + ) ) -) if include_vendor: h_file.write( @@ -876,7 +908,10 @@ if include_hid: c_file.write( """\ const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ -""".format(HID_DESCRIPTOR_LENGTH=hid_descriptor_length)) +""".format( + HID_DESCRIPTOR_LENGTH=hid_descriptor_length + ) + ) for b in bytes(combined_hid_report_descriptor): c_file.write("0x{:02x}, ".format(b)) @@ -895,7 +930,9 @@ const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ static uint8_t {name}_report_buffer[{report_length}]; """.format( name=name.lower(), - report_length=hid_report_descriptors.HID_DEVICE_DATA[name].report_length, + report_length=hid_report_descriptors.HID_DEVICE_DATA[ + name + ].report_length, ) ) -- cgit v1.2.3 From ed49c02feb7e206e962bf8a40cd69771b83e25cf Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 17 Feb 2021 23:24:11 -0500 Subject: add timeout; finish up for PR --- py/stream.c | 32 ++++++++---- py/stream.h | 4 +- shared-bindings/_bleio/CharacteristicBuffer.c | 4 +- shared-bindings/busio/UART.c | 4 +- shared-bindings/usb_cdc/Serial.c | 74 ++++++++++++++++++--------- shared-bindings/usb_cdc/Serial.h | 3 ++ shared-module/usb_cdc/Serial.c | 30 +++++++++++ shared-module/usb_cdc/Serial.h | 4 +- shared-module/usb_cdc/__init__.c | 4 +- 9 files changed, 119 insertions(+), 40 deletions(-) (limited to 'shared-bindings') diff --git a/py/stream.c b/py/stream.c index ae702bb1b..0813c1d7f 100644 --- a/py/stream.c +++ b/py/stream.c @@ -99,14 +99,22 @@ const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) { // What to do if sz < -1? Python docs don't specify this case. - // CPython does a readall, but here we silently let negatives through, - // and they will cause a MemoryError. + // CPython does a readall, let's do the same. mp_int_t sz; - if (n_args == 1 || args[1] == mp_const_none || ((sz = mp_obj_get_int(args[1])) == -1)) { - return stream_readall(args[0]); - } - const mp_stream_p_t *stream_p = mp_get_stream(args[0]); + if (stream_p->pyserial_read_compatibility) { + // Pyserial defaults to sz=1 if not specified. + if (n_args == 1) { + sz = 1; + } else { + // Pyserial treats negative size as 0. + sz = MAX(0, mp_obj_get_int(args[1])); + } + } else { + if (n_args == 1 || args[1] == mp_const_none || (sz = mp_obj_get_int(args[1])) <= -1) { + return stream_readall(args[0]); + } + } #if MICROPY_PY_BUILTINS_STR_UNICODE if (stream_p->is_text) { @@ -284,7 +292,7 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { // https://docs.python.org/3/library/socket.html#socket.socket.recv_into mp_uint_t len = bufinfo.len; if (n_args > 2) { - if (mp_get_stream(args[0])->pyserial_compatibility) { + if (mp_get_stream(args[0])->pyserial_readinto_compatibility) { mp_raise_ValueError(translate("length argument not allowed for this type")); } len = mp_obj_get_int(args[2]); @@ -297,7 +305,10 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); if (error != 0) { if (mp_is_nonblocking_error(error)) { - return mp_const_none; + // pyserial readinto never returns None, just 0. + return mp_get_stream(args[0])->pyserial_dont_return_none_compatibility + ? MP_OBJ_NEW_SMALL_INT(0) + : mp_const_none; } mp_raise_OSError(error); } else { @@ -323,7 +334,10 @@ STATIC mp_obj_t stream_readall(mp_obj_t self_in) { // If we read nothing, return None, just like read(). // Otherwise, return data read so far. if (total_size == 0) { - return mp_const_none; + // pyserial read() never returns None, just b''. + return stream_p->pyserial_dont_return_none_compatibility + ? mp_const_empty_bytes + : mp_const_none; } break; } diff --git a/py/stream.h b/py/stream.h index be6b23d40..c05dcfc50 100644 --- a/py/stream.h +++ b/py/stream.h @@ -72,7 +72,9 @@ typedef struct _mp_stream_p_t { mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode); mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); mp_uint_t is_text : 1; // default is bytes, set this for text stream - bool pyserial_compatibility: 1; // adjust API to match pyserial more closely + bool pyserial_readinto_compatibility: 1; // Disallow size parameter in readinto() + bool pyserial_read_compatibility: 1; // Disallow omitting read(size) size parameter + bool pyserial_dont_return_none_compatibility: 1; // Don't return None for read() or readinto() } mp_stream_p_t; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); diff --git a/shared-bindings/_bleio/CharacteristicBuffer.c b/shared-bindings/_bleio/CharacteristicBuffer.c index 4d6c836c1..333b275ff 100644 --- a/shared-bindings/_bleio/CharacteristicBuffer.c +++ b/shared-bindings/_bleio/CharacteristicBuffer.c @@ -231,8 +231,8 @@ STATIC const mp_stream_p_t characteristic_buffer_stream_p = { .write = bleio_characteristic_buffer_write, .ioctl = bleio_characteristic_buffer_ioctl, .is_text = false, - // Match PySerial when possible, such as disallowing optional length argument for .readinto() - .pyserial_compatibility = true, + // Disallow readinto() size parameter. + .pyserial_readinto_compatibility = true, }; diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index bf0b7e721..f48109fde 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -415,8 +415,8 @@ STATIC const mp_stream_p_t uart_stream_p = { .write = busio_uart_write, .ioctl = busio_uart_ioctl, .is_text = false, - // Match PySerial when possible, such as disallowing optional length argument for .readinto() - .pyserial_compatibility = true, + // Disallow optional length argument for .readinto() + .pyserial_readinto_compatibility = true, }; const mp_obj_type_t busio_uart_type = { diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 03fb94f31..64f851a48 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -41,36 +41,34 @@ //| def __init__(self) -> None: //| """You cannot create an instance of `usb_cdc.Serial`. //| -//| Serial objects are constructed for every corresponding entry in the USB +//| Serial objects are pre-constructed for each CDC device in the USB //| descriptor and added to the ``usb_cdc.ports`` tuple.""" //| ... //| -// These are standard stream methods. Code is in py/stream.c. -// -//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: -//| """Read characters. If ``nbytes`` is specified then read at most that many -//| bytes. Otherwise, read everything that arrives until the connection -//| times out. Providing the number of bytes expected is highly recommended -//| because it will be faster. +//| def read(self, size: int = 1) -> bytes: +//| """Read at most ``size`` bytes. If ``size`` exceeds the internal buffer size +//| only the bytes in the buffer will be read. If `timeout` is > 0 or ``None``, +//| and fewer than ``size`` bytes are available, keep waiting until the timeout +//| expires or ``size`` bytes are available. //| //| :return: Data read -//| :rtype: bytes or None""" +//| :rtype: bytes""" //| ... //| -//| def readinto(self, buf: WriteableBuffer, nbytes: Optional[int] = None) -> Optional[bytes]: +//| def readinto(self, buf: WriteableBuffer) -> bytes: //| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most -//| that many bytes. Otherwise, read at most ``len(buf)`` bytes. +//| that many bytes, subject to `timeout`. Otherwise, read at most ``len(buf)`` bytes. //| //| :return: number of bytes read and stored into ``buf`` -//| :rtype: bytes or None""" +//| :rtype: bytes""" //| ... //| -//| def write(self, buf: ReadableBuffer) -> Optional[int]: +//| def write(self, buf: ReadableBuffer) -> int: //| """Write as many bytes as possible from the buffer of bytes. //| //| :return: the number of bytes written -//| :rtype: int or None""" +//| :rtype: int""" //| ... //| //| def flush(self) -> None: @@ -79,7 +77,7 @@ //| // These three methods are used by the shared stream methods. -STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_read_stream(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); byte *buf = buf_in; @@ -91,16 +89,16 @@ STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t s return common_hal_usb_cdc_serial_read(self, buf, size, errcode); } -STATIC mp_uint_t usb_cdc_serial_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_write_stream(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); const byte *buf = buf_in; return common_hal_usb_cdc_serial_write(self, buf, size, errcode); } -STATIC mp_uint_t usb_cdc_serial_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_ioctl_stream(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_uint_t ret; + mp_uint_t ret = 0; switch (request) { case MP_IOCTL_POLL: { mp_uint_t flags = arg; @@ -134,7 +132,7 @@ STATIC mp_obj_t usb_cdc_serial_get_connected(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_connected_obj, usb_cdc_serial_get_connected); -const mp_obj_property_t usb_cdc_serial__connected_obj = { +const mp_obj_property_t usb_cdc_serial_connected_obj = { .base.type = &mp_type_property, .proxy = {(mp_obj_t)&usb_cdc_serial_get_connected_obj, (mp_obj_t)&mp_const_none_obj, @@ -195,6 +193,32 @@ STATIC mp_obj_t usb_cdc_serial_reset_output_buffer(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial_reset_output_buffer); +//| timeout: Optional[float] +//| """The initial value of `timeout` is ``None``. If ``None``, wait indefinitely to satisfy +//| the conditions of a read operation. If 0, do not wait. If > 0, wait only ``timeout`` seconds.""" +//| +STATIC mp_obj_t usb_cdc_serial_get_timeout(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t timeout = common_hal_usb_cdc_serial_get_timeout(self); + return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->timeout); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_timeout_obj, usb_cdc_serial_get_timeout); + +STATIC mp_obj_t usb_cdc_serial_set_timeout(mp_obj_t self_in, mp_obj_t timeout_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_set_timeout(self, + timeout_in == mp_const_none ? -1.0f : mp_obj_get_float(timeout_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_cdc_serial_set_timeout_obj, usb_cdc_serial_set_timeout); + +const mp_obj_property_t usb_cdc_serial_timeout_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_timeout_obj, + (mp_obj_t)&usb_cdc_serial_set_timeout_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { // Standard stream methods. @@ -210,9 +234,10 @@ STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_out_waiting), MP_ROM_PTR(&usb_cdc_serial_out_waiting_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&usb_cdc_serial_timeout_obj) }, // Not in pyserial protocol. - { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_get_connected_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_connected_obj) }, @@ -221,10 +246,13 @@ STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_di STATIC const mp_stream_p_t usb_cdc_serial_stream_p = { MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) - .read = usb_cdc_serial_read, - .write = usb_cdc_serial_write, - .ioctl = usb_cdc_serial_ioctl, + .read = usb_cdc_serial_read_stream, + .write = usb_cdc_serial_write_stream, + .ioctl = usb_cdc_serial_ioctl_stream, .is_text = false, + .pyserial_read_compatibility = true, + .pyserial_readinto_compatibility = true, + .pyserial_dont_return_none_compatibility = true, }; const mp_obj_type_t usb_cdc_serial_type = { diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 6e54b9ad3..149d2c2d8 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -44,4 +44,7 @@ extern uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self); extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); +extern mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index bd3972c8d..f569a1913 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -24,10 +24,32 @@ * THE SOFTWARE. */ +#include "lib/utils/interrupt_char.h" #include "shared-module/usb_cdc/Serial.h" +#include "supervisor/shared/tick.h" + #include "tusb.h" size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { + if (self->timeout < 0.0f) { + while (tud_cdc_n_available(self->idx) < len) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + return 0; + } + } + } else if (self->timeout > 0.0f) { + uint64_t timeout_ms = self->timeout * 1000; + uint64_t start_ticks = supervisor_ticks_ms64(); + while (tud_cdc_n_available(self->idx) < len && + supervisor_ticks_ms64() - start_ticks <= timeout_ms) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + return 0; + } + } + } + // Timeout of 0.0f falls through to here with no waiting or unnecessary calculation. return tud_cdc_n_read(self->idx, data, len); } @@ -61,3 +83,11 @@ uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) { bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) { return tud_cdc_n_connected(self->idx); } + +mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) { + return self->timeout; +} + +void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) { + self->timeout = timeout; +} diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h index 60a1d1922..ad4bed1a3 100644 --- a/shared-module/usb_cdc/Serial.h +++ b/shared-module/usb_cdc/Serial.h @@ -31,8 +31,8 @@ typedef struct { mp_obj_base_t base; - // Which CDC device? - uint8_t idx; + mp_float_t timeout; // if negative, wait forever. + uint8_t idx; // which CDC device? } usb_cdc_serial_obj_t; #endif // SHARED_MODULE_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index ecb5777b2..9eae18811 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -38,11 +38,13 @@ #error CFG_TUD_CDC must be exactly 2 #endif -static const usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { +static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { { .base.type = &usb_cdc_serial_type, + .timeout = -1.0f, .idx = 0, }, { .base.type = &usb_cdc_serial_type, + .timeout = -1.0f, .idx = 1, } }; -- cgit v1.2.3 From 9cf7d73c6c71df1e43d11d4f2f604a76f59365a8 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 16 Feb 2021 18:37:11 -0600 Subject: core: add bit_transpose function .. this version can only handle exactly 8 bits "across". The restriction may be relaxed in a future revision. --- locale/circuitpython.pot | 8 +++ ports/raspberrypi/mpconfigport.mk | 1 + py/circuitpy_defns.mk | 5 ++ py/circuitpy_mpconfig.h | 9 ++++ py/circuitpy_mpconfig.mk | 3 ++ shared-bindings/_bit_transpose/__init__.c | 87 +++++++++++++++++++++++++++++++ shared-bindings/_bit_transpose/__init__.h | 32 ++++++++++++ shared-module/_bit_transpose/__init__.c | 83 +++++++++++++++++++++++++++++ shared-module/_bit_transpose/__init__.h | 27 ++++++++++ 9 files changed, 255 insertions(+) create mode 100644 shared-bindings/_bit_transpose/__init__.c create mode 100644 shared-bindings/_bit_transpose/__init__.h create mode 100644 shared-module/_bit_transpose/__init__.c create mode 100644 shared-module/_bit_transpose/__init__.h (limited to 'shared-bindings') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index e936dd1f8..11335fd32 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1092,6 +1092,10 @@ msgstr "" msgid "Initialization failed due to lack of memory" msgstr "" +#: shared-bindings/_bit_transpose/__init__.c +msgid "Input buffer must be a multiple of 8 bytes" +msgstr "" + #: ports/atmel-samd/common-hal/pulseio/PulseIn.c msgid "Input taking too long" msgstr "" @@ -1659,6 +1663,10 @@ msgstr "" msgid "Out of sockets" msgstr "" +#: shared-bindings/_bit_transpose/__init__.c +msgid "Output buffer must be at least as big as input buffer" +msgstr "" + #: shared-bindings/audiobusio/PDMIn.c msgid "Oversample must be multiple of 8." msgstr "" diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index dac7231da..6bd617f37 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -24,6 +24,7 @@ CIRCUITPY_NEOPIXEL_WRITE = 0 endif CIRCUITPY_FULL_BUILD = 1 +CIRCUITPY_BIT_TRANSPOSE = 1 CIRCUITPY_PWMIO = 1 # Things that need to be implemented. diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index f907bf7ae..e04031e5a 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -132,6 +132,10 @@ endif ifeq ($(CIRCUITPY_AUDIOMP3),1) SRC_PATTERNS += audiomp3/% endif +ifeq ($(CIRCUITPY_BIT_TRANSPOSE),1) +$(info BIT_TRANSPOSE enabled) +SRC_PATTERNS += _bit_transpose/% +endif ifeq ($(CIRCUITPY_BITBANGIO),1) SRC_PATTERNS += bitbangio/% endif @@ -440,6 +444,7 @@ SRC_BINDINGS_ENUMS += \ util.c SRC_SHARED_MODULE_ALL = \ + _bit_transpose/__init__.c \ _bleio/Address.c \ _bleio/Attribute.c \ _bleio/ScanEntry.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index ee23c7156..be95eabe2 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -299,6 +299,14 @@ extern const struct _mp_obj_module_t audiopwmio_module; #define BINASCII_MODULE #endif +#if CIRCUITPY_BIT_TRANSPOSE +extern const struct _mp_obj_module_t bit_transpose_module; +#define BIT_TRANSPOSE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__bit_transpose),(mp_obj_t)&bit_transpose_module }, +#else +#define BIT_TRANSPOSE_MODULE +#endif + + #if CIRCUITPY_BITBANGIO #define BITBANGIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_bitbangio), (mp_obj_t)&bitbangio_module }, extern const struct _mp_obj_module_t bitbangio_module; @@ -819,6 +827,7 @@ extern const struct _mp_obj_module_t msgpack_module; AUDIOMP3_MODULE \ AUDIOPWMIO_MODULE \ BINASCII_MODULE \ + BIT_TRANSPOSE_MODULE \ BITBANGIO_MODULE \ BLEIO_MODULE \ BOARD_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index d9c7c0d33..f06e99b57 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -89,6 +89,9 @@ CFLAGS += -DCIRCUITPY_AUDIOMP3=$(CIRCUITPY_AUDIOMP3) CIRCUITPY_BINASCII ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BINASCII=$(CIRCUITPY_BINASCII) +CIRCUITPY_BIT_TRANSPOSE ?= 0 +CFLAGS += -DCIRCUITPY_BIT_TRANSPOSE=$(CIRCUITPY_BIT_TRANSPOSE) + CIRCUITPY_BITBANGIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO) diff --git a/shared-bindings/_bit_transpose/__init__.c b/shared-bindings/_bit_transpose/__init__.c new file mode 100644 index 000000000..38f66eb77 --- /dev/null +++ b/shared-bindings/_bit_transpose/__init__.c @@ -0,0 +1,87 @@ +/* + * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Roy Hooper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/_bit_transpose/__init__.h" + +//| """A fast bit transposition function for parallel NeoPixel strips +//| +//| When driving multiple NeoPixel strips from a shift register, the bits +//| must be re-ordered in a specific way. This module offers a low-level +//| routine for performing the transformation.""" +//| + +//| def bit_transpose(input: _typing.ReadableBuffer, *, output: Optional[_typing.WritableBuffer]=None): +//| """Convert a sequence of 8*N pixel values into a single stream of bytes suitable for sending via a parallel conversion method (PioPixl8) +//| +//| Returns the output buffer if specified (which must be big enough to hold the result), otherwise a freshly allocated buffer.""" +//| ... +//| +STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_input, ARG_output }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_input, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + { MP_QSTR_output, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = mp_const_none } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t input_bufinfo; + mp_buffer_info_t output_bufinfo; + + mp_get_buffer_raise(args[ARG_input].u_obj, &input_bufinfo, MP_BUFFER_READ); + int n = input_bufinfo.len; + if (n % 8 != 0) { + mp_raise_ValueError(translate("Input buffer must be a multiple of 8 bytes")); + } + mp_obj_t output = args[ARG_output].u_obj; + + if (!output || output == mp_const_none) { + output = mp_obj_new_bytearray_of_zeros(n); + } + mp_get_buffer_raise(output, &output_bufinfo, MP_BUFFER_WRITE); + int m = output_bufinfo.len; + if (m < n) { + mp_raise_ValueError(translate("Output buffer must be at least as big as input buffer")); + } + common_hal_bit_transpose_bit_transpose(output_bufinfo.buf, input_bufinfo.buf, input_bufinfo.len); + return output; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bit_transpose_bit_transpose_obj, 1, bit_transpose); + +STATIC const mp_rom_map_elem_t bit_transpose_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__bit_transpose) }, + { MP_ROM_QSTR(MP_QSTR_bit_transpose), MP_ROM_PTR(&bit_transpose_bit_transpose_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bit_transpose_module_globals, bit_transpose_module_globals_table); + +const mp_obj_module_t bit_transpose_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&bit_transpose_module_globals, +}; diff --git a/shared-bindings/_bit_transpose/__init__.h b/shared-bindings/_bit_transpose/__init__.h new file mode 100644 index 000000000..75d57d980 --- /dev/null +++ b/shared-bindings/_bit_transpose/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include + +void common_hal_bit_transpose_bit_transpose(uint8_t *result, const uint8_t *src, size_t n); diff --git a/shared-module/_bit_transpose/__init__.c b/shared-module/_bit_transpose/__init__.c new file mode 100644 index 000000000..e30f989b9 --- /dev/null +++ b/shared-module/_bit_transpose/__init__.c @@ -0,0 +1,83 @@ +/* + * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/_bit_transpose/__init__.h" + +#include +#include +#include + +// adapted from "Hacker's Delight" - Figure 7-2 Transposing an 8x8-bit matrix +// basic idea is: +// > First, treat the 8x8-bit matrix as 16 2x2-bit matrices, and transpose each +// > of the 16 2x2-bit matrices. Second, treat the matrix as four 2x2 submatrices +// > whose elements are 2x2-bit matrices and transpose each of the four 2x2 +// > submatrices. Finally, treat the matrix as a 2x2 matrix whose elements are +// > 4x4-bit matrices, and transpose the 2x2 matrix. These transformations are +// > illustrated below. +// We want a different definition of bit/byte order, deal with strides differently, etc. +// so the code is heavily re-worked compared to the original. +static void transpose8(uint32_t *result, const uint8_t *src, int src_stride) { + uint32_t x, y, t; + + y = *src; src += src_stride; + y |= (*src << 8); src += src_stride; + y |= (*src << 16); src += src_stride; + y |= (*src << 24); src += src_stride; + x = *src; src += src_stride; + x |= (*src << 8); src += src_stride; + x |= (*src << 16); src += src_stride; + x |= (*src << 24); src += src_stride; + + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + x = __builtin_bswap32(x); + y = __builtin_bswap32(y); +#endif + result[0] = x; + result[1] = y; +} + +static void bit_transpose(uint32_t *result, const uint8_t *src, size_t src_stride, size_t n) { + for(size_t i=0; i Date: Thu, 18 Feb 2021 11:33:13 -0600 Subject: bit_transpose: Support from 2 to 7 strands, not just 8 --- locale/circuitpython.pot | 10 +++- shared-bindings/_bit_transpose/__init__.c | 26 +++++++---- shared-bindings/_bit_transpose/__init__.h | 2 +- shared-module/_bit_transpose/__init__.c | 78 +++++++++++++++++++++++++++++-- 4 files changed, 99 insertions(+), 17 deletions(-) (limited to 'shared-bindings') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 11335fd32..eef1b07f2 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1093,7 +1093,8 @@ msgid "Initialization failed due to lack of memory" msgstr "" #: shared-bindings/_bit_transpose/__init__.c -msgid "Input buffer must be a multiple of 8 bytes" +#, c-format +msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" msgstr "" #: ports/atmel-samd/common-hal/pulseio/PulseIn.c @@ -1664,7 +1665,8 @@ msgid "Out of sockets" msgstr "" #: shared-bindings/_bit_transpose/__init__.c -msgid "Output buffer must be at least as big as input buffer" +#, c-format +msgid "Output buffer must be at least %d bytes" msgstr "" #: shared-bindings/audiobusio/PDMIn.c @@ -3473,6 +3475,10 @@ msgstr "" msgid "not enough arguments for format string" msgstr "" +#: shared-bindings/_bit_transpose/__init__.c +msgid "num_strands must be from 2 to 8 (inclusive)" +msgstr "" + #: extmod/ulab/code/ulab_create.c msgid "number of points must be at least 2" msgstr "" diff --git a/shared-bindings/_bit_transpose/__init__.c b/shared-bindings/_bit_transpose/__init__.c index 38f66eb77..979dff145 100644 --- a/shared-bindings/_bit_transpose/__init__.c +++ b/shared-bindings/_bit_transpose/__init__.c @@ -43,9 +43,10 @@ //| ... //| STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_input, ARG_output }; + enum { ARG_input, ARG_num_strands, ARG_output }; static const mp_arg_t allowed_args[] = { { MP_QSTR_input, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + { MP_QSTR_num_strands, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 8 } }, { MP_QSTR_output, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = mp_const_none } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -55,21 +56,28 @@ STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_buffer_info_t output_bufinfo; mp_get_buffer_raise(args[ARG_input].u_obj, &input_bufinfo, MP_BUFFER_READ); - int n = input_bufinfo.len; - if (n % 8 != 0) { - mp_raise_ValueError(translate("Input buffer must be a multiple of 8 bytes")); + int num_strands = args[ARG_num_strands].u_int; + + if (num_strands < 2 || num_strands > 8) { + mp_raise_ValueError(translate("num_strands must be from 2 to 8 (inclusive)")); + } + + int inlen = input_bufinfo.len; + if (inlen % num_strands != 0) { + mp_raise_ValueError_varg(translate("Input buffer length (%d) must be a multiple of the strand count (%d)"), inlen, num_strands); } mp_obj_t output = args[ARG_output].u_obj; + int outlen = 8 * (inlen / num_strands); if (!output || output == mp_const_none) { - output = mp_obj_new_bytearray_of_zeros(n); + output = mp_obj_new_bytearray_of_zeros(outlen); } mp_get_buffer_raise(output, &output_bufinfo, MP_BUFFER_WRITE); - int m = output_bufinfo.len; - if (m < n) { - mp_raise_ValueError(translate("Output buffer must be at least as big as input buffer")); + int avail = output_bufinfo.len; + if (avail < outlen) { + mp_raise_ValueError_varg(translate("Output buffer must be at least %d bytes"), outlen); } - common_hal_bit_transpose_bit_transpose(output_bufinfo.buf, input_bufinfo.buf, input_bufinfo.len); + common_hal_bit_transpose_bit_transpose(output_bufinfo.buf, input_bufinfo.buf, inlen, num_strands); return output; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bit_transpose_bit_transpose_obj, 1, bit_transpose); diff --git a/shared-bindings/_bit_transpose/__init__.h b/shared-bindings/_bit_transpose/__init__.h index 75d57d980..5fcf11d45 100644 --- a/shared-bindings/_bit_transpose/__init__.h +++ b/shared-bindings/_bit_transpose/__init__.h @@ -29,4 +29,4 @@ #include #include -void common_hal_bit_transpose_bit_transpose(uint8_t *result, const uint8_t *src, size_t n); +void common_hal_bit_transpose_bit_transpose(uint8_t *result, const uint8_t *src, size_t inlen, size_t num_strands); diff --git a/shared-module/_bit_transpose/__init__.c b/shared-module/_bit_transpose/__init__.c index e30f989b9..517d2d840 100644 --- a/shared-module/_bit_transpose/__init__.c +++ b/shared-module/_bit_transpose/__init__.c @@ -30,6 +30,12 @@ #include #include +#ifdef __GNUC__ +#define FALLTHROUGH __attribute__((fallthrough)) +#else +#define FALLTHROUGH ((void)0) /* FALLTHROUGH */ +#endif + // adapted from "Hacker's Delight" - Figure 7-2 Transposing an 8x8-bit matrix // basic idea is: // > First, treat the 8x8-bit matrix as 16 2x2-bit matrices, and transpose each @@ -40,7 +46,57 @@ // > illustrated below. // We want a different definition of bit/byte order, deal with strides differently, etc. // so the code is heavily re-worked compared to the original. -static void transpose8(uint32_t *result, const uint8_t *src, int src_stride) { +static void transpose_var(uint32_t *result, const uint8_t *src, int src_stride, int num_strands) { + uint32_t x = 0, y = 0, t; + + src += (num_strands-1) * src_stride; + + switch(num_strands) { + case 7: + x |= *src << 16; + src -= src_stride; + FALLTHROUGH; + case 6: + x |= *src << 8; + src -= src_stride; + FALLTHROUGH; + case 5: + x |= *src; + src -= src_stride; + FALLTHROUGH; + case 4: + y |= *src << 24; + src -= src_stride; + FALLTHROUGH; + case 3: + y |= *src << 16; + src -= src_stride; + FALLTHROUGH; + case 2: + y |= *src << 8; + src -= src_stride; + y |= *src; + } + + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + x = __builtin_bswap32(x); + y = __builtin_bswap32(y); +#endif + result[0] = x; + result[1] = y; +} + +static void transpose_8(uint32_t *result, const uint8_t *src, int src_stride) { uint32_t x, y, t; y = *src; src += src_stride; @@ -70,14 +126,26 @@ static void transpose8(uint32_t *result, const uint8_t *src, int src_stride) { result[1] = y; } -static void bit_transpose(uint32_t *result, const uint8_t *src, size_t src_stride, size_t n) { +static void bit_transpose_8(uint32_t *result, const uint8_t *src, size_t src_stride, size_t n) { for(size_t i=0; i Date: Thu, 18 Feb 2021 11:43:43 -0600 Subject: _bit_transpose: fix docs --- shared-bindings/_bit_transpose/__init__.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/_bit_transpose/__init__.c b/shared-bindings/_bit_transpose/__init__.c index 979dff145..0b9d31fbe 100644 --- a/shared-bindings/_bit_transpose/__init__.c +++ b/shared-bindings/_bit_transpose/__init__.c @@ -36,8 +36,13 @@ //| routine for performing the transformation.""" //| -//| def bit_transpose(input: _typing.ReadableBuffer, *, output: Optional[_typing.WritableBuffer]=None): -//| """Convert a sequence of 8*N pixel values into a single stream of bytes suitable for sending via a parallel conversion method (PioPixl8) +//| def bit_transpose(input: _typing.ReadableBuffer, *, num_strands:int = 8, output: Optional[_typing.WriteableBuffer]=None) -> WriteableBuffer: +//| """Convert a sequence of pixel values into a single stream of bytes suitable for sending via a parallel conversion method (PioPixl8) +//| +//| The number of bytes in the input buffer must be a multiple of the number of strands. +//| +//| If specified output buffer must be big enough for the output, ``len(input) * 8 // num_strands``. To get a properly sized buffer, +//| you can also pass in ``output=None`` once, then re-use the return value in future calls. //| //| Returns the output buffer if specified (which must be big enough to hold the result), otherwise a freshly allocated buffer.""" //| ... -- cgit v1.2.3 From 7fd45678939147da1c3034f212331d1d755ddae6 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 18 Feb 2021 15:41:23 -0600 Subject: bitops: rename from _bit_transpose, describe the algorithm --- locale/circuitpython.pot | 12 +-- ports/raspberrypi/mpconfigport.mk | 2 +- py/circuitpy_defns.mk | 8 +- py/circuitpy_mpconfig.h | 10 +- py/circuitpy_mpconfig.mk | 4 +- shared-bindings/_bit_transpose/__init__.c | 100 -------------------- shared-bindings/_bit_transpose/__init__.h | 32 ------- shared-bindings/bitops/__init__.c | 101 ++++++++++++++++++++ shared-bindings/bitops/__init__.h | 32 +++++++ shared-module/_bit_transpose/__init__.c | 151 ------------------------------ shared-module/_bit_transpose/__init__.h | 27 ------ shared-module/bitops/__init__.c | 151 ++++++++++++++++++++++++++++++ shared-module/bitops/__init__.h | 27 ++++++ 13 files changed, 329 insertions(+), 328 deletions(-) delete mode 100644 shared-bindings/_bit_transpose/__init__.c delete mode 100644 shared-bindings/_bit_transpose/__init__.h create mode 100644 shared-bindings/bitops/__init__.c create mode 100644 shared-bindings/bitops/__init__.h delete mode 100644 shared-module/_bit_transpose/__init__.c delete mode 100644 shared-module/_bit_transpose/__init__.h create mode 100644 shared-module/bitops/__init__.c create mode 100644 shared-module/bitops/__init__.h (limited to 'shared-bindings') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index eef1b07f2..b1bc1a390 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1092,7 +1092,7 @@ msgstr "" msgid "Initialization failed due to lack of memory" msgstr "" -#: shared-bindings/_bit_transpose/__init__.c +#: shared-bindings/bitops/__init__.c #, c-format msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" msgstr "" @@ -1664,7 +1664,7 @@ msgstr "" msgid "Out of sockets" msgstr "" -#: shared-bindings/_bit_transpose/__init__.c +#: shared-bindings/bitops/__init__.c #, c-format msgid "Output buffer must be at least %d bytes" msgstr "" @@ -3475,10 +3475,6 @@ msgstr "" msgid "not enough arguments for format string" msgstr "" -#: shared-bindings/_bit_transpose/__init__.c -msgid "num_strands must be from 2 to 8 (inclusive)" -msgstr "" - #: extmod/ulab/code/ulab_create.c msgid "number of points must be at least 2" msgstr "" @@ -4115,6 +4111,10 @@ msgstr "" msgid "watchdog timeout must be greater than 0" msgstr "" +#: shared-bindings/bitops/__init__.c +msgid "width must be from 2 to 8 (inclusive)" +msgstr "" + #: shared-bindings/rgbmatrix/RGBMatrix.c msgid "width must be greater than zero" msgstr "" diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 6bd617f37..b93e4dcc7 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -24,7 +24,7 @@ CIRCUITPY_NEOPIXEL_WRITE = 0 endif CIRCUITPY_FULL_BUILD = 1 -CIRCUITPY_BIT_TRANSPOSE = 1 +CIRCUITPY_BITOPS = 1 CIRCUITPY_PWMIO = 1 # Things that need to be implemented. diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index e04031e5a..c1cf86362 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -132,9 +132,9 @@ endif ifeq ($(CIRCUITPY_AUDIOMP3),1) SRC_PATTERNS += audiomp3/% endif -ifeq ($(CIRCUITPY_BIT_TRANSPOSE),1) -$(info BIT_TRANSPOSE enabled) -SRC_PATTERNS += _bit_transpose/% +ifeq ($(CIRCUITPY_BITOPS),1) +$(info BITOPS enabled) +SRC_PATTERNS += bitops/% endif ifeq ($(CIRCUITPY_BITBANGIO),1) SRC_PATTERNS += bitbangio/% @@ -444,7 +444,6 @@ SRC_BINDINGS_ENUMS += \ util.c SRC_SHARED_MODULE_ALL = \ - _bit_transpose/__init__.c \ _bleio/Address.c \ _bleio/Attribute.c \ _bleio/ScanEntry.c \ @@ -471,6 +470,7 @@ SRC_SHARED_MODULE_ALL = \ bitbangio/OneWire.c \ bitbangio/SPI.c \ bitbangio/__init__.c \ + bitops/__init__.c \ board/__init__.c \ adafruit_bus_device/__init__.c \ adafruit_bus_device/I2CDevice.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index be95eabe2..d72146c42 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -299,11 +299,11 @@ extern const struct _mp_obj_module_t audiopwmio_module; #define BINASCII_MODULE #endif -#if CIRCUITPY_BIT_TRANSPOSE -extern const struct _mp_obj_module_t bit_transpose_module; -#define BIT_TRANSPOSE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__bit_transpose),(mp_obj_t)&bit_transpose_module }, +#if CIRCUITPY_BITOPS +extern const struct _mp_obj_module_t bitops_module; +#define BITOPS_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_bitops),(mp_obj_t)&bitops_module }, #else -#define BIT_TRANSPOSE_MODULE +#define BITOPS_MODULE #endif @@ -827,7 +827,7 @@ extern const struct _mp_obj_module_t msgpack_module; AUDIOMP3_MODULE \ AUDIOPWMIO_MODULE \ BINASCII_MODULE \ - BIT_TRANSPOSE_MODULE \ + BITOPS_MODULE \ BITBANGIO_MODULE \ BLEIO_MODULE \ BOARD_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index f06e99b57..6244ed136 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -89,8 +89,8 @@ CFLAGS += -DCIRCUITPY_AUDIOMP3=$(CIRCUITPY_AUDIOMP3) CIRCUITPY_BINASCII ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BINASCII=$(CIRCUITPY_BINASCII) -CIRCUITPY_BIT_TRANSPOSE ?= 0 -CFLAGS += -DCIRCUITPY_BIT_TRANSPOSE=$(CIRCUITPY_BIT_TRANSPOSE) +CIRCUITPY_BITOPS ?= 0 +CFLAGS += -DCIRCUITPY_BITOPS=$(CIRCUITPY_BITOPS) CIRCUITPY_BITBANGIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO) diff --git a/shared-bindings/_bit_transpose/__init__.c b/shared-bindings/_bit_transpose/__init__.c deleted file mode 100644 index 0b9d31fbe..000000000 --- a/shared-bindings/_bit_transpose/__init__.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython - * - * The MIT License (MIT) - * - * Copyright (c) 2018 Roy Hooper - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/obj.h" -#include "py/runtime.h" - -#include "shared-bindings/_bit_transpose/__init__.h" - -//| """A fast bit transposition function for parallel NeoPixel strips -//| -//| When driving multiple NeoPixel strips from a shift register, the bits -//| must be re-ordered in a specific way. This module offers a low-level -//| routine for performing the transformation.""" -//| - -//| def bit_transpose(input: _typing.ReadableBuffer, *, num_strands:int = 8, output: Optional[_typing.WriteableBuffer]=None) -> WriteableBuffer: -//| """Convert a sequence of pixel values into a single stream of bytes suitable for sending via a parallel conversion method (PioPixl8) -//| -//| The number of bytes in the input buffer must be a multiple of the number of strands. -//| -//| If specified output buffer must be big enough for the output, ``len(input) * 8 // num_strands``. To get a properly sized buffer, -//| you can also pass in ``output=None`` once, then re-use the return value in future calls. -//| -//| Returns the output buffer if specified (which must be big enough to hold the result), otherwise a freshly allocated buffer.""" -//| ... -//| -STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_input, ARG_num_strands, ARG_output }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_input, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, - { MP_QSTR_num_strands, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 8 } }, - { MP_QSTR_output, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = mp_const_none } }, - }; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_buffer_info_t input_bufinfo; - mp_buffer_info_t output_bufinfo; - - mp_get_buffer_raise(args[ARG_input].u_obj, &input_bufinfo, MP_BUFFER_READ); - int num_strands = args[ARG_num_strands].u_int; - - if (num_strands < 2 || num_strands > 8) { - mp_raise_ValueError(translate("num_strands must be from 2 to 8 (inclusive)")); - } - - int inlen = input_bufinfo.len; - if (inlen % num_strands != 0) { - mp_raise_ValueError_varg(translate("Input buffer length (%d) must be a multiple of the strand count (%d)"), inlen, num_strands); - } - mp_obj_t output = args[ARG_output].u_obj; - - int outlen = 8 * (inlen / num_strands); - if (!output || output == mp_const_none) { - output = mp_obj_new_bytearray_of_zeros(outlen); - } - mp_get_buffer_raise(output, &output_bufinfo, MP_BUFFER_WRITE); - int avail = output_bufinfo.len; - if (avail < outlen) { - mp_raise_ValueError_varg(translate("Output buffer must be at least %d bytes"), outlen); - } - common_hal_bit_transpose_bit_transpose(output_bufinfo.buf, input_bufinfo.buf, inlen, num_strands); - return output; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bit_transpose_bit_transpose_obj, 1, bit_transpose); - -STATIC const mp_rom_map_elem_t bit_transpose_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__bit_transpose) }, - { MP_ROM_QSTR(MP_QSTR_bit_transpose), MP_ROM_PTR(&bit_transpose_bit_transpose_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(bit_transpose_module_globals, bit_transpose_module_globals_table); - -const mp_obj_module_t bit_transpose_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&bit_transpose_module_globals, -}; diff --git a/shared-bindings/_bit_transpose/__init__.h b/shared-bindings/_bit_transpose/__init__.h deleted file mode 100644 index 5fcf11d45..000000000 --- a/shared-bindings/_bit_transpose/__init__.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython - * - * The MIT License (MIT) - * - * Copyright (c) 2021 Jeff Epler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#pragma once - -#include -#include - -void common_hal_bit_transpose_bit_transpose(uint8_t *result, const uint8_t *src, size_t inlen, size_t num_strands); diff --git a/shared-bindings/bitops/__init__.c b/shared-bindings/bitops/__init__.c new file mode 100644 index 000000000..fc5784259 --- /dev/null +++ b/shared-bindings/bitops/__init__.c @@ -0,0 +1,101 @@ +/* + * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Roy Hooper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/bitops/__init__.h" + +//| """Routines for low-level manipulation of binary data""" +//| +//| + +//| def bit_transpose(input: _typing.ReadableBuffer, *, width:int = 8, output: Optional[_typing.WriteableBuffer]=None) -> WriteableBuffer: +//| """"Transpose" a buffer by assembling each output byte with bits taken from each of ``width`` different input bytes. +//| +//| This can be useful to convert a sequence of pixel values into a single +//| stream of bytes suitable for sending via a parallel conversion method. +//| +//| The number of bytes in the input buffer must be a multiple of the width, +//| and the width can be any value from 2 to 8. If the width is fewer than 8, +//| then the remaining (less significant) bits of the output are set to zero. +//| +//| Let ``stride = len(input)//width``. Then the first byte is made out of the +//| most significant bits of ``[input[0], input[stride], input[2*stride], ...]``. +//| The second byte is made out of the second bits, and so on until the 8th output +//| byte which is made of the first bits of ``input[1], input[1+stride, +//| input[2*stride], ...]``. +//| +//| The required output buffer size is ``len(input) * 8 // width``. +//| +//| Returns the output buffer.""" +//| ... + +STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_input, ARG_width, ARG_output }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_input, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_output, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 8 } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int width = args[ARG_width].u_int; + if (width < 2 || width > 8) { + mp_raise_ValueError(translate("width must be from 2 to 8 (inclusive)")); + } + + mp_buffer_info_t input_bufinfo; + mp_get_buffer_raise(args[ARG_input].u_obj, &input_bufinfo, MP_BUFFER_READ); + int inlen = input_bufinfo.len; + if (inlen % width != 0) { + mp_raise_ValueError_varg(translate("Input buffer length (%d) must be a multiple of the strand count (%d)"), inlen, width); + } + + mp_buffer_info_t output_bufinfo; + mp_get_buffer_raise(args[ARG_output].u_obj, &output_bufinfo, MP_BUFFER_WRITE); + int avail = output_bufinfo.len; + int outlen = 8 * (inlen / width); + if (avail < outlen) { + mp_raise_ValueError_varg(translate("Output buffer must be at least %d bytes"), outlen); + } + common_hal_bitops_bit_transpose(output_bufinfo.buf, input_bufinfo.buf, inlen, width); + return args[ARG_output].u_obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bitops_bit_transpose_obj, 1, bit_transpose); + +STATIC const mp_rom_map_elem_t bitops_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bitops) }, + { MP_ROM_QSTR(MP_QSTR_bit_transpose), MP_ROM_PTR(&bitops_bit_transpose_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(bitops_module_globals, bitops_module_globals_table); + +const mp_obj_module_t bitops_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&bitops_module_globals, +}; diff --git a/shared-bindings/bitops/__init__.h b/shared-bindings/bitops/__init__.h new file mode 100644 index 000000000..6654cac5e --- /dev/null +++ b/shared-bindings/bitops/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include + +void common_hal_bitops_bit_transpose(uint8_t *result, const uint8_t *src, size_t inlen, size_t num_strands); diff --git a/shared-module/_bit_transpose/__init__.c b/shared-module/_bit_transpose/__init__.c deleted file mode 100644 index 517d2d840..000000000 --- a/shared-module/_bit_transpose/__init__.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This file is part of the Circuit Python project, https://github.com/adafruit/circuitpython - * - * The MIT License (MIT) - * - * Copyright (c) 2021 Jeff Epler - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "shared-bindings/_bit_transpose/__init__.h" - -#include -#include -#include - -#ifdef __GNUC__ -#define FALLTHROUGH __attribute__((fallthrough)) -#else -#define FALLTHROUGH ((void)0) /* FALLTHROUGH */ -#endif - -// adapted from "Hacker's Delight" - Figure 7-2 Transposing an 8x8-bit matrix -// basic idea is: -// > First, treat the 8x8-bit matrix as 16 2x2-bit matrices, and transpose each -// > of the 16 2x2-bit matrices. Second, treat the matrix as four 2x2 submatrices -// > whose elements are 2x2-bit matrices and transpose each of the four 2x2 -// > submatrices. Finally, treat the matrix as a 2x2 matrix whose elements are -// > 4x4-bit matrices, and transpose the 2x2 matrix. These transformations are -// > illustrated below. -// We want a different definition of bit/byte order, deal with strides differently, etc. -// so the code is heavily re-worked compared to the original. -static void transpose_var(uint32_t *result, const uint8_t *src, int src_stride, int num_strands) { - uint32_t x = 0, y = 0, t; - - src += (num_strands-1) * src_stride; - - switch(num_strands) { - case 7: - x |= *src << 16; - src -= src_stride; - FALLTHROUGH; - case 6: - x |= *src << 8; - src -= src_stride; - FALLTHROUGH; - case 5: - x |= *src; - src -= src_stride; - FALLTHROUGH; - case 4: - y |= *src << 24; - src -= src_stride; - FALLTHROUGH; - case 3: - y |= *src << 16; - src -= src_stride; - FALLTHROUGH; - case 2: - y |= *src << 8; - src -= src_stride; - y |= *src; - } - - t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); - t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); - - t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); - t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); - - t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); - y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); - x = t; - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - x = __builtin_bswap32(x); - y = __builtin_bswap32(y); -#endif - result[0] = x; - result[1] = y; -} - -static void transpose_8(uint32_t *result, const uint8_t *src, int src_stride) { - uint32_t x, y, t; - - y = *src; src += src_stride; - y |= (*src << 8); src += src_stride; - y |= (*src << 16); src += src_stride; - y |= (*src << 24); src += src_stride; - x = *src; src += src_stride; - x |= (*src << 8); src += src_stride; - x |= (*src << 16); src += src_stride; - x |= (*src << 24); src += src_stride; - - t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); - t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); - - t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); - t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); - - t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); - y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); - x = t; - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - x = __builtin_bswap32(x); - y = __builtin_bswap32(y); -#endif - result[0] = x; - result[1] = y; -} - -static void bit_transpose_8(uint32_t *result, const uint8_t *src, size_t src_stride, size_t n) { - for(size_t i=0; i +#include +#include + +#ifdef __GNUC__ +#define FALLTHROUGH __attribute__((fallthrough)) +#else +#define FALLTHROUGH ((void)0) /* FALLTHROUGH */ +#endif + +// adapted from "Hacker's Delight" - Figure 7-2 Transposing an 8x8-bit matrix +// basic idea is: +// > First, treat the 8x8-bit matrix as 16 2x2-bit matrices, and transpose each +// > of the 16 2x2-bit matrices. Second, treat the matrix as four 2x2 submatrices +// > whose elements are 2x2-bit matrices and transpose each of the four 2x2 +// > submatrices. Finally, treat the matrix as a 2x2 matrix whose elements are +// > 4x4-bit matrices, and transpose the 2x2 matrix. These transformations are +// > illustrated below. +// We want a different definition of bit/byte order, deal with strides differently, etc. +// so the code is heavily re-worked compared to the original. +static void transpose_var(uint32_t *result, const uint8_t *src, int src_stride, int num_strands) { + uint32_t x = 0, y = 0, t; + + src += (num_strands-1) * src_stride; + + switch(num_strands) { + case 7: + x |= *src << 16; + src -= src_stride; + FALLTHROUGH; + case 6: + x |= *src << 8; + src -= src_stride; + FALLTHROUGH; + case 5: + x |= *src; + src -= src_stride; + FALLTHROUGH; + case 4: + y |= *src << 24; + src -= src_stride; + FALLTHROUGH; + case 3: + y |= *src << 16; + src -= src_stride; + FALLTHROUGH; + case 2: + y |= *src << 8; + src -= src_stride; + y |= *src; + } + + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + x = __builtin_bswap32(x); + y = __builtin_bswap32(y); +#endif + result[0] = x; + result[1] = y; +} + +static void transpose_8(uint32_t *result, const uint8_t *src, int src_stride) { + uint32_t x, y, t; + + y = *src; src += src_stride; + y |= (*src << 8); src += src_stride; + y |= (*src << 16); src += src_stride; + y |= (*src << 24); src += src_stride; + x = *src; src += src_stride; + x |= (*src << 8); src += src_stride; + x |= (*src << 16); src += src_stride; + x |= (*src << 24); src += src_stride; + + t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7); + t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7); + + t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14); + t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14); + + t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F); + y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F); + x = t; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + x = __builtin_bswap32(x); + y = __builtin_bswap32(y); +#endif + result[0] = x; + result[1] = y; +} + +static void bit_transpose_8(uint32_t *result, const uint8_t *src, size_t src_stride, size_t n) { + for(size_t i=0; i Date: Thu, 18 Feb 2021 15:44:51 -0600 Subject: bitops: doc correction --- shared-bindings/bitops/__init__.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitops/__init__.c b/shared-bindings/bitops/__init__.c index fc5784259..0ea556fa2 100644 --- a/shared-bindings/bitops/__init__.c +++ b/shared-bindings/bitops/__init__.c @@ -33,7 +33,7 @@ //| //| -//| def bit_transpose(input: _typing.ReadableBuffer, *, width:int = 8, output: Optional[_typing.WriteableBuffer]=None) -> WriteableBuffer: +//| def bit_transpose(input: _typing.ReadableBuffer, output: _typing.WriteableBuffer, width:int = 8) -> WriteableBuffer: //| """"Transpose" a buffer by assembling each output byte with bits taken from each of ``width`` different input bytes. //| //| This can be useful to convert a sequence of pixel values into a single @@ -59,7 +59,7 @@ STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t static const mp_arg_t allowed_args[] = { { MP_QSTR_input, MP_ARG_OBJ | MP_ARG_REQUIRED }, { MP_QSTR_output, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 8 } }, + { MP_QSTR_width, MP_ARG_INT, { .u_int = 8 } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); -- cgit v1.2.3 From ffae89b1914272cdb5978a0837991e33732bbe59 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 18 Feb 2021 16:10:57 -0600 Subject: bitops: fix argument parsing --- shared-bindings/bitops/__init__.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitops/__init__.c b/shared-bindings/bitops/__init__.c index 0ea556fa2..0d40e53ca 100644 --- a/shared-bindings/bitops/__init__.c +++ b/shared-bindings/bitops/__init__.c @@ -55,7 +55,7 @@ //| ... STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_input, ARG_width, ARG_output }; + enum { ARG_input, ARG_output, ARG_width }; static const mp_arg_t allowed_args[] = { { MP_QSTR_input, MP_ARG_OBJ | MP_ARG_REQUIRED }, { MP_QSTR_output, MP_ARG_OBJ | MP_ARG_REQUIRED }, @@ -66,7 +66,7 @@ STATIC mp_obj_t bit_transpose(size_t n_args, const mp_obj_t *pos_args, mp_map_t int width = args[ARG_width].u_int; if (width < 2 || width > 8) { - mp_raise_ValueError(translate("width must be from 2 to 8 (inclusive)")); + mp_raise_ValueError_varg(translate("width must be from 2 to 8 (inclusive), not %d"), width); } mp_buffer_info_t input_bufinfo; -- cgit v1.2.3 From 5c758523c0cd967beccc5e84457b154d9c9e071a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 18 Feb 2021 17:19:34 -0600 Subject: requested changes --- shared-bindings/bitops/__init__.c | 4 ++-- shared-module/bitops/__init__.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitops/__init__.c b/shared-bindings/bitops/__init__.c index 0d40e53ca..3c567b8ed 100644 --- a/shared-bindings/bitops/__init__.c +++ b/shared-bindings/bitops/__init__.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2018 Roy Hooper + * Copyright (c) 2021 Jeff Epler for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,7 +33,7 @@ //| //| -//| def bit_transpose(input: _typing.ReadableBuffer, output: _typing.WriteableBuffer, width:int = 8) -> WriteableBuffer: +//| def bit_transpose(input: ReadableBuffer, output: WriteableBuffer, width:int = 8) -> WriteableBuffer: //| """"Transpose" a buffer by assembling each output byte with bits taken from each of ``width`` different input bytes. //| //| This can be useful to convert a sequence of pixel values into a single diff --git a/shared-module/bitops/__init__.c b/shared-module/bitops/__init__.c index 66898e5be..cf0ac7bd1 100644 --- a/shared-module/bitops/__init__.c +++ b/shared-module/bitops/__init__.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Jeff Epler + * Copyright (c) 2021 Jeff Epler for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal -- cgit v1.2.3 From b12ccefbe6afd2c8b1743ce7759880ef3b01e9ed Mon Sep 17 00:00:00 2001 From: microDev <70126934+microDev1@users.noreply.github.com> Date: Fri, 19 Feb 2021 18:36:00 +0530 Subject: uart implementation for rp2040 --- locale/circuitpython.pot | 15 +- ports/raspberrypi/common-hal/busio/UART.c | 464 ++++++++++-------------------- ports/raspberrypi/common-hal/busio/UART.h | 18 +- shared-bindings/busio/UART.c | 8 +- 4 files changed, 175 insertions(+), 330 deletions(-) (limited to 'shared-bindings') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 0a79520b8..bffc3005c 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -335,6 +335,10 @@ msgstr "" msgid "All UART peripherals are in use" msgstr "" +#: ports/raspberrypi/common-hal/busio/UART.c +msgid "All UART peripherals in use" +msgstr "" + #: ports/atmel-samd/common-hal/audioio/AudioOut.c msgid "All event channels in use" msgstr "" @@ -1277,6 +1281,7 @@ msgstr "" #: ports/mimxrt10xx/common-hal/busio/UART.c ports/nrf/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/busio/SPI.c +#: ports/raspberrypi/common-hal/busio/UART.c msgid "Invalid pins" msgstr "" @@ -1824,6 +1829,10 @@ msgstr "" msgid "RS485 inversion specified when not in RS485 mode" msgstr "" +#: ports/raspberrypi/common-hal/busio/UART.c +msgid "RS485 is not supported on this board" +msgstr "" + #: ports/cxd56/common-hal/rtc/RTC.c ports/esp32s2/common-hal/rtc/RTC.c #: ports/mimxrt10xx/common-hal/rtc/RTC.c ports/nrf/common-hal/rtc/RTC.c #: ports/raspberrypi/common-hal/rtc/RTC.c @@ -2124,10 +2133,6 @@ msgstr "" msgid "UART Re-init error" msgstr "" -#: ports/raspberrypi/common-hal/busio/UART.c -msgid "UART not yet supported" -msgstr "" - #: ports/stm/common-hal/busio/UART.c msgid "UART write error" msgstr "" @@ -2455,7 +2460,7 @@ msgid "binary op %q not implemented" msgstr "" #: shared-bindings/busio/UART.c -msgid "bits must be 7, 8 or 9" +msgid "bits must be between 5 and 8" msgstr "" #: shared-bindings/audiomixer/Mixer.c diff --git a/ports/raspberrypi/common-hal/busio/UART.c b/ports/raspberrypi/common-hal/busio/UART.c index f9a75b499..660a473ae 100644 --- a/ports/raspberrypi/common-hal/busio/UART.c +++ b/ports/raspberrypi/common-hal/busio/UART.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries + * 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,238 +24,135 @@ * THE SOFTWARE. */ -#include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/busio/UART.h" -#include "mpconfigport.h" -#include "lib/utils/interrupt_char.h" -#include "py/gc.h" +#include "py/stream.h" #include "py/mperrno.h" #include "py/runtime.h" -#include "py/stream.h" -#include "supervisor/shared/translate.h" #include "supervisor/shared/tick.h" +#include "lib/utils/interrupt_char.h" -#define UART_DEBUG(...) (void)0 -// #define UART_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) - -// Do-nothing callback needed so that usart_async code will enable rx interrupts. -// See comment below re usart_async_register_callback() -// static void usart_async_rxc_callback(const struct usart_async_descriptor *const descr) { -// // Nothing needs to be done by us. -// } +#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h" #define NO_PIN 0xff +#define UART_INST(uart) (((uart) ? uart1 : uart0)) + +#define TX 0 +#define RX 1 +#define CTS 2 +#define RTS 3 + +typedef enum { + STATUS_FREE = 0, + STATUS_IN_USE, + STATUS_NEVER_RESET +} uart_status_t; + +static uart_status_t uart_status[2]; + +void uart_reset(void) { + for (uint8_t num = 0; num < 2; num++) { + if (uart_status[num] == STATUS_IN_USE) { + uart_status[num] = STATUS_FREE; + uart_deinit(UART_INST(num)); + } + } +} + +void never_reset_uart(uint8_t num) { + uart_status[num] = STATUS_NEVER_RESET; +} + +static uint8_t get_free_uart() { + uint8_t num; + for (num = 0; num < 2; num++) { + if (uart_status[num] == STATUS_FREE) { + break; + } + if (num) { + mp_raise_RuntimeError(translate("All UART peripherals in use")); + } + } + return num; +} + +static uint8_t pin_init(const uint8_t uart, const mcu_pin_obj_t * pin, const uint8_t pin_type) { + if (pin == NULL) { + return NO_PIN; + } + if (!(((pin->number & 3) == pin_type) && ((((pin->number + 4) & 8) >> 3) == uart))) { + mp_raise_ValueError(translate("Invalid pins")); + } + gpio_set_function(pin->number, GPIO_FUNC_UART); + return pin->number; +} + void common_hal_busio_uart_construct(busio_uart_obj_t *self, - const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, - const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, - const mcu_pin_obj_t * rs485_dir, bool rs485_invert, - uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, - bool sigint_enabled) { - mp_raise_NotImplementedError(translate("UART not yet supported")); - -// Sercom* sercom = NULL; -// uint8_t sercom_index = 255; // Unset index -// uint32_t rx_pinmux = 0; -// uint8_t rx_pad = 255; // Unset pad -// uint32_t tx_pinmux = 0; -// uint8_t tx_pad = 255; // Unset pad - -// if ((rts != NULL) || (cts != NULL) || (rs485_dir != NULL) || (rs485_invert)) { -// mp_raise_ValueError(translate("RTS/CTS/RS485 Not yet supported on this device")); -// } - -// if (bits > 8) { -// mp_raise_NotImplementedError(translate("bytes > 8 bits not supported")); -// } - -// bool have_tx = tx != NULL; -// bool have_rx = rx != NULL; -// if (!have_tx && !have_rx) { -// mp_raise_ValueError(translate("tx and rx cannot both be None")); -// } - -// self->baudrate = baudrate; -// self->character_bits = bits; -// self->timeout_ms = timeout * 1000; - -// // This assignment is only here because the usart_async routines take a *const argument. -// struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - -// for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) { -// Sercom* potential_sercom = NULL; -// if (have_tx) { -// sercom_index = tx->sercom[i].index; -// if (sercom_index >= SERCOM_INST_NUM) { -// continue; -// } -// potential_sercom = sercom_insts[sercom_index]; -// #ifdef SAMD21 -// if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 || -// !(tx->sercom[i].pad == 0 || -// tx->sercom[i].pad == 2)) { -// continue; -// } -// #endif -// #ifdef SAM_D5X_E5X -// if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 || -// !(tx->sercom[i].pad == 0)) { -// continue; -// } -// #endif -// tx_pinmux = PINMUX(tx->number, (i == 0) ? MUX_C : MUX_D); -// tx_pad = tx->sercom[i].pad; -// if (rx == NULL) { -// sercom = potential_sercom; -// break; -// } -// } -// for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) { -// if (((!have_tx && rx->sercom[j].index < SERCOM_INST_NUM && -// sercom_insts[rx->sercom[j].index]->USART.CTRLA.bit.ENABLE == 0) || -// sercom_index == rx->sercom[j].index) && -// rx->sercom[j].pad != tx_pad) { -// rx_pinmux = PINMUX(rx->number, (j == 0) ? MUX_C : MUX_D); -// rx_pad = rx->sercom[j].pad; -// sercom = sercom_insts[rx->sercom[j].index]; -// sercom_index = rx->sercom[j].index; -// break; -// } -// } -// if (sercom != NULL) { -// break; -// } -// } -// if (sercom == NULL) { -// mp_raise_ValueError(translate("Invalid pins")); -// } -// if (!have_tx) { -// tx_pad = 0; -// if (rx_pad == 0) { -// tx_pad = 2; -// } -// } -// if (!have_rx) { -// rx_pad = (tx_pad + 1) % 4; -// } - -// // Set up clocks on SERCOM. -// samd_peripherals_sercom_clock_init(sercom, sercom_index); - -// if (rx && receiver_buffer_size > 0) { -// self->buffer_length = receiver_buffer_size; -// // Initially allocate the UART's buffer in the long-lived part of the -// // heap. UARTs are generally long-lived objects, but the "make long- -// // lived" machinery is incapable of moving internal pointers like -// // self->buffer, so do it manually. (However, as long as internal -// // pointers like this are NOT moved, allocating the buffer -// // in the long-lived pool is not strictly necessary) -// self->buffer = (uint8_t *) gc_alloc(self->buffer_length * sizeof(uint8_t), false, true); -// if (self->buffer == NULL) { -// common_hal_busio_uart_deinit(self); -// mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), self->buffer_length * sizeof(uint8_t)); -// } -// } else { -// self->buffer_length = 0; -// self->buffer = NULL; -// } - -// if (usart_async_init(usart_desc_p, sercom, self->buffer, self->buffer_length, NULL) != ERR_NONE) { -// mp_raise_ValueError(translate("Could not initialize UART")); -// } - -// // usart_async_init() sets a number of defaults based on a prototypical SERCOM -// // which don't necessarily match what we need. After calling it, set the values -// // specific to this instantiation of UART. - -// // Set pads computed for this SERCOM. -// // TXPO: -// // 0x0: TX pad 0; no RTS/CTS -// // 0x1: TX pad 2; no RTS/CTS -// // 0x2: TX pad 0; RTS: pad 2, CTS: pad 3 (not used by us right now) -// // So divide by 2 to map pad to value. -// // RXPO: -// // 0x0: RX pad 0 -// // 0x1: RX pad 1 -// // 0x2: RX pad 2 -// // 0x3: RX pad 3 - -// // Doing a group mask and set of the registers saves 60 bytes over setting the bitfields individually. - -// sercom->USART.CTRLA.reg &= ~(SERCOM_USART_CTRLA_TXPO_Msk | -// SERCOM_USART_CTRLA_RXPO_Msk | -// SERCOM_USART_CTRLA_FORM_Msk); -// sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(tx_pad / 2) | -// SERCOM_USART_CTRLA_RXPO(rx_pad) | -// (parity == BUSIO_UART_PARITY_NONE ? 0 : SERCOM_USART_CTRLA_FORM(1)); - -// // Enable tx and/or rx based on whether the pins were specified. -// // CHSIZE is 0 for 8 bits, 5, 6, 7 for 5, 6, 7 bits. 1 for 9 bits, but we don't support that. -// sercom->USART.CTRLB.reg &= ~(SERCOM_USART_CTRLB_TXEN | -// SERCOM_USART_CTRLB_RXEN | -// SERCOM_USART_CTRLB_PMODE | -// SERCOM_USART_CTRLB_SBMODE | -// SERCOM_USART_CTRLB_CHSIZE_Msk); -// sercom->USART.CTRLB.reg |= (have_tx ? SERCOM_USART_CTRLB_TXEN : 0) | -// (have_rx ? SERCOM_USART_CTRLB_RXEN : 0) | -// (parity == BUSIO_UART_PARITY_ODD ? SERCOM_USART_CTRLB_PMODE : 0) | -// (stop > 1 ? SERCOM_USART_CTRLB_SBMODE : 0) | -// SERCOM_USART_CTRLB_CHSIZE(bits % 8); - -// // Set baud rate -// common_hal_busio_uart_set_baudrate(self, baudrate); - -// // Turn on rx interrupt handling. The UART async driver has its own set of internal callbacks, -// // which are set up by uart_async_init(). These in turn can call user-specified callbacks. -// // In fact, the actual interrupts are not enabled unless we set up a user-specified callback. -// // This is confusing. It's explained in the Atmel START User Guide -> Implementation Description -> -// // Different read function behavior in some asynchronous drivers. As of this writing: -// // http://start.atmel.com/static/help/index.html?GUID-79201A5A-226F-4FBB-B0B8-AB0BE0554836 -// // Look at the ASFv4 code example for async USART. -// usart_async_register_callback(usart_desc_p, USART_ASYNC_RXC_CB, usart_async_rxc_callback); - - -// if (have_tx) { -// gpio_set_pin_direction(tx->number, GPIO_DIRECTION_OUT); -// gpio_set_pin_pull_mode(tx->number, GPIO_PULL_OFF); -// gpio_set_pin_function(tx->number, tx_pinmux); -// self->tx_pin = tx->number; -// claim_pin(tx); -// } else { -// self->tx_pin = NO_PIN; -// } - -// if (have_rx) { -// gpio_set_pin_direction(rx->number, GPIO_DIRECTION_IN); -// gpio_set_pin_pull_mode(rx->number, GPIO_PULL_OFF); -// gpio_set_pin_function(rx->number, rx_pinmux); -// self->rx_pin = rx->number; -// claim_pin(rx); -// } else { -// self->rx_pin = NO_PIN; -// } - -// usart_async_enable(usart_desc_p); + const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, + const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, + const mcu_pin_obj_t * rs485_dir, bool rs485_invert, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, + mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, + bool sigint_enabled) { + + if ((rs485_dir != NULL) || (rs485_invert)) { + mp_raise_NotImplementedError(translate("RS485 is not supported on this board")); + } + + uint8_t uart_id = get_free_uart(); + + self->tx_pin = pin_init(uart_id, tx, TX); + self->rx_pin = pin_init(uart_id, rx, RX); + self->cts_pin = pin_init(uart_id, cts, CTS); + self->rts_pin = pin_init(uart_id, rts, RTS); + + self->uart = UART_INST(uart_id); + self->baudrate = baudrate; + self->timeout_ms = timeout * 1000; + + uart_init(self->uart, self->baudrate); + uart_set_fifo_enabled(self->uart, true); + uart_set_format(self->uart, bits, stop, parity); + uart_set_hw_flow(self->uart, (cts != NULL), (rts != NULL)); } bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { - return self->rx_pin == NO_PIN && self->tx_pin == NO_PIN; + return self->tx_pin == NO_PIN && self->rx_pin == NO_PIN; } void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { if (common_hal_busio_uart_deinited(self)) { return; } - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // usart_async_disable(usart_desc_p); - // usart_async_deinit(usart_desc_p); - reset_pin_number(self->rx_pin); + uart_deinit(self->uart); reset_pin_number(self->tx_pin); - self->rx_pin = NO_PIN; + reset_pin_number(self->rx_pin); + reset_pin_number(self->cts_pin); + reset_pin_number(self->rts_pin); self->tx_pin = NO_PIN; + self->rx_pin = NO_PIN; + self->cts_pin = NO_PIN; + self->rts_pin = NO_PIN; +} + +// Write characters. +size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + if (self->tx_pin == NO_PIN) { + mp_raise_ValueError(translate("No TX pin")); + } + + while (len > 0) { + if (uart_is_writable(self->uart)) { + // Write and advance. + uart_get_hw(self->uart)->dr = *data++; + // Decrease how many chars left to write. + len--; + } + RUN_BACKGROUND_TASKS; + } + + return len; } // Read characters. @@ -264,87 +161,53 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t mp_raise_ValueError(translate("No RX pin")); } - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - if (len == 0) { // Nothing to read. return 0; } - // struct io_descriptor *io; - // usart_async_get_io_descriptor(usart_desc_p, &io); - size_t total_read = 0; - // uint64_t start_ticks = supervisor_ticks_ms64(); - - // // Busy-wait until timeout or until we've read enough chars. - // while (supervisor_ticks_ms64() - start_ticks <= self->timeout_ms) { - // // Read as many chars as we can right now, up to len. - // size_t num_read = io_read(io, data, len); - - // // Advance pointer in data buffer, and decrease how many chars left to read. - // data += num_read; - // len -= num_read; - // total_read += num_read; - // if (len == 0) { - // // Don't need to read any more: data buf is full. - // break; - // } - // if (num_read > 0) { - // // Reset the timeout on every character read. - // start_ticks = supervisor_ticks_ms64(); - // } - // RUN_BACKGROUND_TASKS; - // // Allow user to break out of a timeout with a KeyboardInterrupt. - // if (mp_hal_is_interrupted()) { - // break; - // } - // // If we are zero timeout, make sure we don't loop again (in the event - // // we read in under 1ms) - // if (self->timeout_ms == 0) { - // break; - // } - // } - - // if (total_read == 0) { - // *errcode = EAGAIN; - // return MP_STREAM_ERROR; - // } - - return total_read; -} - -// Write characters. -size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { - if (self->tx_pin == NO_PIN) { - mp_raise_ValueError(translate("No TX pin")); + uint64_t start_ticks = supervisor_ticks_ms64(); + + // Busy-wait until timeout or until we've read enough chars. + while (supervisor_ticks_ms64() - start_ticks <= self->timeout_ms) { + if (uart_is_readable(self->uart)) { + // Read and advance. + *data++ = uart_get_hw(self->uart)->dr; + + // Decrease how many chars left to read. + len--; + total_read++; + + // Reset the timeout on every character read. + start_ticks = supervisor_ticks_ms64(); + } + + RUN_BACKGROUND_TASKS; + + // Allow user to break out of a timeout with a KeyboardInterrupt. + if (mp_hal_is_interrupted()) { + break; + } + + // Don't need to read any more: data buf is full. + if (len == 0) { + break; + } + + // If we are zero timeout, make sure we don't loop again (in the event + // we read in under 1ms) + if (self->timeout_ms == 0) { + break; + } } - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - - // struct io_descriptor *io; - // usart_async_get_io_descriptor(usart_desc_p, &io); - - // // Start writing characters. This is non-blocking and will - // // return immediately after setting up the write. - // if (io_write(io, data, len) < 0) { - // *errcode = MP_EAGAIN; - // return MP_STREAM_ERROR; - // } - - // // Busy-wait until all characters transmitted. - // struct usart_async_status async_status; - // while (true) { - // usart_async_get_status(usart_desc_p, &async_status); - // if (async_status.txcnt >= len) { - // break; - // } - // RUN_BACKGROUND_TASKS; - // } + if (total_read == 0) { + *errcode = EAGAIN; + return MP_STREAM_ERROR; + } - return len; + return total_read; } uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { @@ -352,18 +215,8 @@ uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { } void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // usart_async_set_baud_rate(usart_desc_p, - // // Samples and ARITHMETIC vs FRACTIONAL must correspond to USART_SAMPR in - // // hpl_sercom_config.h. - // _usart_async_calculate_baud_rate(baudrate, // e.g. 9600 baud - // PROTOTYPE_SERCOM_USART_ASYNC_CLOCK_FREQUENCY, - // 16, // samples - // USART_BAUDRATE_ASYNCH_ARITHMETIC, - // 0 // fraction - not used for ARITHMETIC - // )); self->baudrate = baudrate; + uart_set_baudrate(self->uart, baudrate); } mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { @@ -375,30 +228,15 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou } uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // struct usart_async_status async_status; - // usart_async_get_status(usart_desc_p, &async_status); - // return async_status.rxcnt; - return 0; + return uart_is_readable(self->uart); } -void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { - // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // usart_async_flush_rx_buffer(usart_desc_p); - -} +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) {} // True if there are no characters still to be written. bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { if (self->tx_pin == NO_PIN) { return false; } - return false; - // // This assignment is only here because the usart_async routines take a *const argument. - // struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const) &self->usart_desc; - // struct usart_async_status async_status; - // usart_async_get_status(usart_desc_p, &async_status); - // return !(async_status.flags & USART_ASYNC_STATUS_BUSY); + return uart_is_writable(self->uart); } diff --git a/ports/raspberrypi/common-hal/busio/UART.h b/ports/raspberrypi/common-hal/busio/UART.h index 43ed9bee0..03613daeb 100644 --- a/ports/raspberrypi/common-hal/busio/UART.h +++ b/ports/raspberrypi/common-hal/busio/UART.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries + * 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,21 +27,23 @@ #ifndef MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H #define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H +#include "py/obj.h" #include "common-hal/microcontroller/Pin.h" -#include "py/obj.h" +#include "src/rp2_common/hardware_uart/include/hardware/uart.h" typedef struct { mp_obj_base_t base; - // struct usart_async_descriptor usart_desc; - uint8_t rx_pin; uint8_t tx_pin; - uint8_t character_bits; - bool rx_error; + uint8_t rx_pin; + uint8_t cts_pin; + uint8_t rts_pin; uint32_t baudrate; uint32_t timeout_ms; - uint32_t buffer_length; - uint8_t* buffer; + uart_inst_t * uart; } busio_uart_obj_t; +extern void uart_reset(void); +extern void never_reset_uart(uint8_t num); + #endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index bf0b7e721..151081d31 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -55,7 +55,7 @@ //| :param ~microcontroller.Pin rs485_dir: the output pin for rs485 direction setting, or ``None`` if rs485 not in use. //| :param bool rs485_invert: rs485_dir pin active high when set. Active low otherwise. //| :param int baudrate: the transmit and receive speed. -//| :param int bits: the number of bits per byte, 7, 8 or 9. +//| :param int bits: the number of bits per byte, 5 to 8. //| :param Parity parity: the parity used for error checking. //| :param int stop: the number of stop bits, 1 or 2. //| :param float timeout: the timeout in seconds to wait for the first character and between subsequent characters when reading. Raises ``ValueError`` if timeout >100 seconds. @@ -110,10 +110,10 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, co mp_raise_ValueError(translate("tx and rx cannot both be None")); } - uint8_t bits = args[ARG_bits].u_int; - if (bits < 7 || bits > 9) { - mp_raise_ValueError(translate("bits must be 7, 8 or 9")); + if (args[ARG_bits].u_int < 5 || args[ARG_bits].u_int > 8) { + mp_raise_ValueError(translate("bits must be between 5 and 8")); } + uint8_t bits = args[ARG_bits].u_int; busio_uart_parity_t parity = BUSIO_UART_PARITY_NONE; if (args[ARG_parity].u_obj == &busio_uart_parity_even_obj) { -- cgit v1.2.3 From 9d4442e298f4e86fb0686d7986ba627231dd591b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 19 Feb 2021 14:15:31 -0500 Subject: handle reads/writes larger than buffers; add .write_timeout --- shared-bindings/usb_cdc/Serial.c | 30 ++++++++++++++- shared-bindings/usb_cdc/Serial.h | 3 ++ shared-module/usb_cdc/Serial.c | 80 +++++++++++++++++++++++++++++++++------- shared-module/usb_cdc/Serial.h | 5 ++- shared-module/usb_cdc/__init__.c | 2 + 5 files changed, 104 insertions(+), 16 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 64f851a48..c813dce5b 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -56,7 +56,7 @@ //| :rtype: bytes""" //| ... //| -//| def readinto(self, buf: WriteableBuffer) -> bytes: +//| def readinto(self, buf: WriteableBuffer) -> int: //| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most //| that many bytes, subject to `timeout`. Otherwise, read at most ``len(buf)`` bytes. //| @@ -219,6 +219,33 @@ const mp_obj_property_t usb_cdc_serial_timeout_obj = { (mp_obj_t)&mp_const_none_obj}, }; +//| write_timeout: Optional[float] +//| """The initial value of `write_timeout` is ``None``. If ``None``, wait indefinitely to finish +//| writing all the bytes passed to ``write()``.If 0, do not wait. +//| If > 0, wait only ``write_timeout`` seconds.""" +//| +STATIC mp_obj_t usb_cdc_serial_get_write_timeout(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t write_timeout = common_hal_usb_cdc_serial_get_write_timeout(self); + return (write_timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->write_timeout); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_write_timeout_obj, usb_cdc_serial_get_write_timeout); + +STATIC mp_obj_t usb_cdc_serial_set_write_timeout(mp_obj_t self_in, mp_obj_t write_timeout_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_set_write_timeout(self, + write_timeout_in == mp_const_none ? -1.0f : mp_obj_get_float(write_timeout_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_cdc_serial_set_write_timeout_obj, usb_cdc_serial_set_write_timeout); + +const mp_obj_property_t usb_cdc_serial_write_timeout_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_write_timeout_obj, + (mp_obj_t)&usb_cdc_serial_set_write_timeout_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { // Standard stream methods. @@ -235,6 +262,7 @@ STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&usb_cdc_serial_timeout_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write_timeout), MP_ROM_PTR(&usb_cdc_serial_write_timeout_obj) }, // Not in pyserial protocol. { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_connected_obj) }, diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 149d2c2d8..cdf5c3a91 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -47,4 +47,7 @@ extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); extern mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self); extern void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout); +extern mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index f569a1913..6037fddcc 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -31,32 +31,78 @@ #include "tusb.h" size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { - if (self->timeout < 0.0f) { - while (tud_cdc_n_available(self->idx) < len) { + + const bool wait_forever = self->timeout < 0.0f; + const bool wait_for_timeout = self->timeout > 0.0f; + + // Read up to len bytes immediately. + // The number of bytes read will not be larger than what is already in the TinyUSB FIFO. + uint32_t total_num_read = tud_cdc_n_read(self->idx, data, len); + + if (wait_forever || wait_for_timeout) { + // Read more if we have time. + uint64_t timeout_ms = self->timeout * 1000; // Junk value if timeout < 0. + uint64_t start_ticks = supervisor_ticks_ms64(); + + uint32_t num_read = 0; + while (total_num_read < len && + (wait_forever || supervisor_ticks_ms64() - start_ticks <= timeout_ms)) { + + // Wait for a bit, and check for ctrl-C. RUN_BACKGROUND_TASKS; if (mp_hal_is_interrupted()) { return 0; } + + // Advance buffer pointer and reduce number of bytes that need to be read. + len -= num_read; + data += num_read; + + // Try to read another batch of bytes. + num_read = tud_cdc_n_read(self->idx, data, len); + total_num_read += num_read; } - } else if (self->timeout > 0.0f) { - uint64_t timeout_ms = self->timeout * 1000; + } + + return total_num_read; +} + +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + const bool wait_forever = self->write_timeout < 0.0f; + const bool wait_for_timeout = self->write_timeout > 0.0f; + + // Write as many bytes as possible immediately. + // The number of bytes written at once will not be larger than what can fit in the TinyUSB FIFO. + uint32_t total_num_written = tud_cdc_n_write(self->idx, data, len); + tud_cdc_n_write_flush(self->idx); + + if (wait_forever || wait_for_timeout) { + // Write more if we have time. + uint64_t timeout_ms = self->write_timeout * 1000; // Junk value if write_timeout < 0. uint64_t start_ticks = supervisor_ticks_ms64(); - while (tud_cdc_n_available(self->idx) < len && - supervisor_ticks_ms64() - start_ticks <= timeout_ms) { + + uint32_t num_written = 0; + while (total_num_written < len && + (wait_forever || supervisor_ticks_ms64() - start_ticks <= timeout_ms)) { + + // Wait for a bit, and check for ctrl-C. RUN_BACKGROUND_TASKS; if (mp_hal_is_interrupted()) { return 0; } + + // Advance buffer pointer and reduce number of bytes that need to be written. + len -= num_written; + data += num_written; + + // Try to write another batch of bytes. + num_written = tud_cdc_n_write(self->idx, data, len); + tud_cdc_n_write_flush(self->idx); + total_num_written += num_written; } } - // Timeout of 0.0f falls through to here with no waiting or unnecessary calculation. - return tud_cdc_n_read(self->idx, data, len); -} -size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { - uint32_t num_written = tud_cdc_n_write(self->idx, data, len); - tud_cdc_n_write_flush(self->idx); - return num_written; + return total_num_written; } uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) { @@ -91,3 +137,11 @@ mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) { void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) { self->timeout = timeout; } + +mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self) { + return self->write_timeout; +} + +void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout) { + self->write_timeout = write_timeout; +} diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h index ad4bed1a3..ddf78eefa 100644 --- a/shared-module/usb_cdc/Serial.h +++ b/shared-module/usb_cdc/Serial.h @@ -31,8 +31,9 @@ typedef struct { mp_obj_base_t base; - mp_float_t timeout; // if negative, wait forever. - uint8_t idx; // which CDC device? + mp_float_t timeout; // if negative, wait forever. + mp_float_t write_timeout; // if negative, wait forever. + uint8_t idx; // which CDC device? } usb_cdc_serial_obj_t; #endif // SHARED_MODULE_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index 9eae18811..fe05cb107 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -41,10 +41,12 @@ static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { { .base.type = &usb_cdc_serial_type, .timeout = -1.0f, + .write_timeout = -1.0f, .idx = 0, }, { .base.type = &usb_cdc_serial_type, .timeout = -1.0f, + .write_timeout = -1.0f, .idx = 1, } }; -- cgit v1.2.3 From b7200286425363c430b7f4466c27e53ea67f7887 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 23 Feb 2021 23:23:14 -0600 Subject: Add bitmaptools module --- locale/circuitpython.pot | 9 +- py/circuitpy_defns.mk | 4 + py/circuitpy_mpconfig.h | 9 +- py/circuitpy_mpconfig.mk | 3 + shared-bindings/bitmaptools/__init__.c | 261 +++++++++++++++++++++++++++++++++ shared-bindings/bitmaptools/__init__.h | 42 ++++++ shared-module/bitmaptools/__init__.c | 174 ++++++++++++++++++++++ 7 files changed, 499 insertions(+), 3 deletions(-) create mode 100644 shared-bindings/bitmaptools/__init__.c create mode 100644 shared-bindings/bitmaptools/__init__.h create mode 100644 shared-module/bitmaptools/__init__.c (limited to 'shared-bindings') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index f9ef4ad13..834aa56aa 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1852,7 +1852,7 @@ msgstr "" msgid "Read-only filesystem" msgstr "" -#: shared-module/displayio/Bitmap.c +#: shared-module/bitmaptools/__init__.c shared-module/displayio/Bitmap.c msgid "Read-only object" msgstr "" @@ -2683,6 +2683,10 @@ msgstr "" msgid "circle can only be registered in one parent" msgstr "" +#: shared-bindings/bitmaptools/__init__.c +msgid "clip point must be (x,y) tuple" +msgstr "" + #: shared-bindings/msgpack/ExtType.c msgid "code outside range 0~127" msgstr "" @@ -3682,6 +3686,7 @@ msgstr "" #: ports/esp32s2/boards/targett_module_clip_wrover/mpconfigboard.h #: ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.h #: ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.h +#: ports/esp32s2/boards/unexpectedmaker_tinys2/mpconfigboard.h msgid "pressing boot button at start up.\n" msgstr "" @@ -3830,7 +3835,7 @@ msgstr "" msgid "sosfilt requires iterable arguments" msgstr "" -#: shared-bindings/displayio/Bitmap.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/displayio/Bitmap.c msgid "source palette too large" msgstr "" diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 943c99f93..bb177c5d0 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -139,6 +139,9 @@ endif ifeq ($(CIRCUITPY_BITBANG_APA102),1) SRC_PATTERNS += bitbangio/SPI% endif +ifeq ($(CIRCUITPY_BITMAPTOOLS),1) +SRC_PATTERNS += bitmaptools/% +endif ifeq ($(CIRCUITPY_BITOPS),1) SRC_PATTERNS += bitops/% endif @@ -472,6 +475,7 @@ SRC_SHARED_MODULE_ALL = \ bitbangio/OneWire.c \ bitbangio/SPI.c \ bitbangio/__init__.c \ + bitmaptools/__init__.c \ bitops/__init__.c \ board/__init__.c \ adafruit_bus_device/__init__.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index cbe668289..3eda3b004 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -306,6 +306,13 @@ extern const struct _mp_obj_module_t bitbangio_module; #define BITBANGIO_MODULE #endif +#if CIRCUITPY_BITMAPTOOLS +#define BITMAPTOOLS_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_bitmaptools), (mp_obj_t)&bitmaptools_module }, +extern const struct _mp_obj_module_t bitmaptools_module; +#else +#define BITMAPTOOLS_MODULE +#endif + #if CIRCUITPY_BITOPS extern const struct _mp_obj_module_t bitops_module; #define BITOPS_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_bitops),(mp_obj_t)&bitops_module }, @@ -313,7 +320,6 @@ extern const struct _mp_obj_module_t bitops_module; #define BITOPS_MODULE #endif - #if CIRCUITPY_BLEIO #define BLEIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__bleio), (mp_obj_t)&bleio_module }, extern const struct _mp_obj_module_t bleio_module; @@ -835,6 +841,7 @@ extern const struct _mp_obj_module_t msgpack_module; AUDIOPWMIO_MODULE \ BINASCII_MODULE \ BITBANGIO_MODULE \ + BITMAPTOOLS_MODULE \ BITOPS_MODULE \ BLEIO_MODULE \ BOARD_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index ad6386636..d071aff05 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -95,6 +95,9 @@ CFLAGS += -DCIRCUITPY_BITBANG_APA102=$(CIRCUITPY_BITBANG_APA102) CIRCUITPY_BITBANGIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO) +CIRCUITPY_BITMAPTOOLS ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_BITMAPTOOLS=$(CIRCUITPY_BITMAPTOOLS) + CIRCUITPY_BITOPS ?= 0 CFLAGS += -DCIRCUITPY_BITOPS=$(CIRCUITPY_BITOPS) diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c new file mode 100644 index 000000000..7f7e16bcb --- /dev/null +++ b/shared-bindings/bitmaptools/__init__.c @@ -0,0 +1,261 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Kevin Matocha + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/displayio/Bitmap.h" +#include "shared-bindings/bitmaptools/__init__.h" + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +STATIC int16_t validate_point(mp_obj_t point, int16_t default_value) { + // Checks if point is None and returns default_value, otherwise decodes integer value + if ( point == mp_const_none ) { + return default_value; + } + return mp_obj_get_int(point); +} + +STATIC void extract_tuple(mp_obj_t xy_tuple, int16_t *x, int16_t *y, int16_t x_default, int16_t y_default) { + // Helper function for rotozoom + // Extract x,y values from a tuple or default if None + if ( xy_tuple == mp_const_none ) { + *x = x_default; + *y = y_default; + } else if ( !MP_OBJ_IS_OBJ(xy_tuple) ) { + mp_raise_ValueError(translate("clip point must be (x,y) tuple")); + } else { + mp_obj_t* items; + mp_obj_get_array_fixed_n(xy_tuple, 2, &items); + *x = mp_obj_get_int(items[0]); + *y = mp_obj_get_int(items[1]); + } +} + +STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tuple, int16_t *clip0_x, int16_t *clip0_y, + mp_obj_t clip1_tuple, int16_t *clip1_x, int16_t *clip1_y) { + // Helper function for rotozoom + // 1. Extract the clip x,y points from the two clip tuples + // 2. Rearrange values such that clip0_ < clip1_ + // 3. Constrain the clip points to within the bitmap + + extract_tuple(clip0_tuple, clip0_x, clip0_y, 0, 0); + extract_tuple(clip1_tuple, clip1_x, clip1_y, bitmap->width, bitmap->height); + + // Ensure the value for clip0 is less than clip1 (for both x and y) + if ( *clip0_x > *clip1_x ) { + int16_t temp_value = *clip0_x; // swap values + *clip0_x = *clip1_x; + *clip1_x = temp_value; + } + if ( *clip0_y > *clip1_y ) { + int16_t temp_value = *clip0_y; // swap values + *clip0_y = *clip1_y; + *clip1_y = temp_value; + } + + // Constrain the clip window to within the bitmap boundaries + if (*clip0_x < 0) { + *clip0_x = 0; + } + if (*clip0_y < 0) { + *clip0_y = 0; + } + if (*clip0_x > bitmap->width) { + *clip0_x = bitmap->width; + } + if (*clip0_y > bitmap->height) { + *clip0_y = bitmap->height; + } + if (*clip1_x < 0) { + *clip1_x = 0; + } + if (*clip1_y < 0) { + *clip1_y = 0; + } + if (*clip1_x > bitmap->width) { + *clip1_x = bitmap->width; + } + if (*clip1_y > bitmap->height) { + *clip1_y = bitmap->height; + } + +} + + +//| +//| def rotozoom(dest_bitmap: Bitmap, ox: int, oy: int, +//| dest_clip0: Tuple[int, int], +//| dest_clip1: Tuple[int, int], +//| source_bitmap: Bitmap, +//| px: int, py: int, +//| source_clip0: Tuple[int, int], +//| source_clip1: Tuple[int, int], +//| angle: float, +//| scale: float, +//| skip_index: int) -> None: +//| """Inserts the source bitmap region into the destination bitmap with rotation (angle), scale +//| and clipping (both on source and destination bitmaps). +//| +//| :param bitmap dest_bitmap: The bitmap that will be copied into +//| :param int ox: Horizontal pixel location in destination bitmap where source bitmap +//| point (px,py) is placed +//| :param int oy: Vertical pixel location in destination bitmap where source bitmap +//| point (px,py) is placed +//| :param dest_clip0: First corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :type dest_clip0: Tuple[int,int] +//| :param dest_clip1: second corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :type dest_clip1: Tuple[int,int] +//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied +//| :param int px: Horizontal pixel location in source bitmap that is placed into the +//| destination bitmap at (ox,oy) +//| :param int py: Vertical pixel location in source bitmap that is placed into the +//| destination bitmap at (ox,oy) +//| :param source_clip0: First corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :type source_clip0: Tuple[int,int] +//| :param source_clip1: second corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :type source_clip1: Tuple[int,int] +//| :param float angle: angle of rotation, in radians (positive is clockwise direction) +//| :param float scale: scaling factor +//| :param int skip_index: bitmap palette index in the source that will not be copied, +//| set to None to copy all pixels""" +//| ... +//| +STATIC mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args){ + enum {ARG_dest_bitmap, ARG_ox, ARG_oy, ARG_dest_clip0, ARG_dest_clip1, + ARG_source_bitmap, ARG_px, ARG_py, + ARG_source_clip0, ARG_source_clip1, + ARG_angle, ARG_scale, ARG_skip_index}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_ox, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->width / 2 + {MP_QSTR_oy, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->height / 2 + {MP_QSTR_dest_clip0, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + {MP_QSTR_dest_clip1, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_px, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->width / 2 + {MP_QSTR_py, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->height / 2 + {MP_QSTR_source_clip0, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + {MP_QSTR_source_clip1, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + + {MP_QSTR_angle, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to 0.0 + {MP_QSTR_scale, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to 1.0 + {MP_QSTR_skip_index, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj=mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + + displayio_bitmap_t *source = MP_OBJ_TO_PTR(args[ARG_source_bitmap].u_obj); // the source bitmap + + // ensure that the destination bitmap has at least as many `bits_per_value` as the source + if (destination->bits_per_value < source->bits_per_value) { + mp_raise_ValueError(translate("source palette too large")); + } + + // Confirm the destination location target (ox,oy); if None, default to bitmap midpoint + int16_t ox, oy; + ox = validate_point(args[ARG_ox].u_obj, destination->width / 2); + oy = validate_point(args[ARG_oy].u_obj, destination->height / 2); + + // Confirm the source location target (px,py); if None, default to bitmap midpoint + int16_t px, py; + px = validate_point(args[ARG_px].u_obj, source->width / 2); + py = validate_point(args[ARG_py].u_obj, source->height / 2); + + // Validate the clipping regions for the destination bitmap + int16_t dest_clip0_x, dest_clip0_y, dest_clip1_x, dest_clip1_y; + + validate_clip_region(destination, args[ARG_dest_clip0].u_obj, &dest_clip0_x, &dest_clip0_y, + args[ARG_dest_clip1].u_obj, &dest_clip1_x, &dest_clip1_y); + + // Validate the clipping regions for the source bitmap + int16_t source_clip0_x, source_clip0_y, source_clip1_x, source_clip1_y; + + validate_clip_region(source, args[ARG_source_clip0].u_obj, &source_clip0_x, &source_clip0_y, + args[ARG_source_clip1].u_obj, &source_clip1_x, &source_clip1_y); + + // Confirm the angle value + float angle=0.0; + if ( args[ARG_angle].u_obj != mp_const_none ) { + angle = mp_obj_get_float(args[ARG_angle].u_obj); + } + + // Confirm the scale value + float scale=1.0; + if ( args[ARG_scale].u_obj != mp_const_none ) { + scale = mp_obj_get_float(args[ARG_scale].u_obj); + } + if (scale < 0) { // ensure scale >= 0 + scale = 1.0; + } + + uint32_t skip_index; + bool skip_index_none; // Flag whether input skip_value was None + if (args[ARG_skip_index].u_obj == mp_const_none ) { + skip_index = 0; + skip_index_none = true; + } else { + skip_index = mp_obj_get_int(args[ARG_skip_index].u_obj); + skip_index_none = false; + } + + common_hal_bitmaptools_rotozoom(destination, ox, oy, + dest_clip0_x, dest_clip0_y, + dest_clip1_x, dest_clip1_y, + source, px, py, + source_clip0_x, source_clip0_y, + source_clip1_x, source_clip1_y, + angle, + scale, + skip_index, skip_index_none); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_rotozoom_obj, 0, bitmaptools_obj_rotozoom); +// requires at least 2 arguments (destination bitmap and source bitmap) + + +STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_rotozoom), MP_ROM_PTR(&bitmaptools_rotozoom_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(bitmaptools_module_globals, bitmaptools_module_globals_table); + + +const mp_obj_module_t bitmaptools_module = { + .base = {&mp_type_module }, + .globals = (mp_obj_dict_t*)&bitmaptools_module_globals, +}; diff --git a/shared-bindings/bitmaptools/__init__.h b/shared-bindings/bitmaptools/__init__.h new file mode 100644 index 000000000..e2bb6938b --- /dev/null +++ b/shared-bindings/bitmaptools/__init__.h @@ -0,0 +1,42 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Kevin Matocha + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H +#define MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H + +#include "py/obj.h" + +void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16_t oy, + int16_t dest_clip0_x, int16_t dest_clip0_y, + int16_t dest_clip1_x, int16_t dest_clip1_y, + displayio_bitmap_t *source, int16_t px, int16_t py, + int16_t source_clip0_x, int16_t source_clip0_y, + int16_t source_clip1_x, int16_t source_clip1_y, + float angle, + float scale, + uint32_t skip_index, bool skip_index_none); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H diff --git a/shared-module/bitmaptools/__init__.c b/shared-module/bitmaptools/__init__.c new file mode 100644 index 000000000..7dc4024ef --- /dev/null +++ b/shared-module/bitmaptools/__init__.c @@ -0,0 +1,174 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Kevin Matocha + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "shared-bindings/displayio/Bitmap.h" + +#include "py/runtime.h" + +#include "math.h" + +void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16_t oy, + int16_t dest_clip0_x, int16_t dest_clip0_y, + int16_t dest_clip1_x, int16_t dest_clip1_y, + displayio_bitmap_t *source, int16_t px, int16_t py, + int16_t source_clip0_x, int16_t source_clip0_y, + int16_t source_clip1_x, int16_t source_clip1_y, + float angle, + float scale, + uint32_t skip_index, bool skip_index_none) { + + // Copies region from source to the destination bitmap, including rotation, + // scaling and clipping of either the source or destination regions + // + // *self: destination bitmap + // ox: the (ox, oy) destination point where the source (px,py) point is placed + // oy: + // dest_clip0: (x,y) is the corner of the clip window on the destination bitmap + // dest_clip1: (x,y) is the other corner of the clip window of the destination bitmap + // *source: the source bitmap + // px: the (px, py) point of rotation of the source bitmap + // py: + // source_clip0: (x,y) is the corner of the clip window on the source bitmap + // source_clip1: (x,y) is the other of the clip window on the source bitmap + // angle: angle of rotation in radians, positive is clockwise + // scale: scale factor + // skip_index: color index that should be ignored (and not copied over) + // skip_index_none: if skip_index_none is True, then all color indexes should be copied + // (that is, no color indexes should be skipped) + + + // Copy complete "source" bitmap into "self" bitmap at location x,y in the "self" + // Add a boolean to determine if all values are copied, or only if non-zero + // If skip_value is encountered in the source bitmap, it will not be copied. + // If skip_value is `None`, then all pixels are copied. + + + // # Credit from https://github.com/wernsey/bitmap + // # MIT License from + // # * Copyright (c) 2017 Werner Stoop + // # + // # * + // # * #### `void bm_rotate_blit(Bitmap *dst, int ox, int oy, Bitmap *src, int px, int py, double angle, double scale);` + // # * + // # * Rotates a source bitmap `src` around a pivot point `px,py` and blits it onto a destination bitmap `dst`. + // # * + // # * The bitmap is positioned such that the point `px,py` on the source is at the offset `ox,oy` on the destination. + // # * + // # * The `angle` is clockwise, in radians. The bitmap is also scaled by the factor `scale`. + // # + // # void bm_rotate_blit(Bitmap *dst, int ox, int oy, Bitmap *src, int px, int py, double angle, double scale); + + + // # /* + // # Reference: + // # "Fast Bitmap Rotation and Scaling" By Steven Mortimer, Dr Dobbs' Journal, July 01, 2001 + // # http://www.drdobbs.com/architecture-and-design/fast-bitmap-rotation-and-scaling/184416337 + // # See also http://www.efg2.com/Lab/ImageProcessing/RotateScanline.htm + // # */ + + + if (self->read_only) { + mp_raise_RuntimeError(translate("Read-only object")); + } + + int16_t x,y; + + int16_t minx = dest_clip1_x; + int16_t miny = dest_clip1_y; + int16_t maxx = dest_clip0_x; + int16_t maxy = dest_clip0_y; + + float sinAngle = sinf(angle); + float cosAngle = cosf(angle); + + float dx, dy; + + /* Compute the position of where each corner on the source bitmap + will be on the destination to get a bounding box for scanning */ + dx = -cosAngle * px * scale + sinAngle * py * scale + ox; + dy = -sinAngle * px * scale - cosAngle * py * scale + oy; + if(dx < minx) minx = (int16_t)dx; + if(dx > maxx) maxx = (int16_t)dx; + if(dy < miny) miny = (int16_t)dy; + if(dy > maxy) maxy = (int16_t)dy; + + dx = cosAngle * (source->width - px) * scale + sinAngle * py * scale + ox; + dy = sinAngle * (source->width - px) * scale - cosAngle * py * scale + oy; + if(dx < minx) minx = (int16_t)dx; + if(dx > maxx) maxx = (int16_t)dx; + if(dy < miny) miny = (int16_t)dy; + if(dy > maxy) maxy = (int16_t)dy; + + dx = cosAngle * (source->width - px) * scale - sinAngle * (source->height - py) * scale + ox; + dy = sinAngle * (source->width - px) * scale + cosAngle * (source->height - py) * scale + oy; + if(dx < minx) minx = (int16_t)dx; + if(dx > maxx) maxx = (int16_t)dx; + if(dy < miny) miny = (int16_t)dy; + if(dy > maxy) maxy = (int16_t)dy; + + dx = -cosAngle * px * scale - sinAngle * (source->height - py) * scale + ox; + dy = -sinAngle * px * scale + cosAngle * (source->height - py) * scale + oy; + if(dx < minx) minx = (int16_t)dx; + if(dx > maxx) maxx = (int16_t)dx; + if(dy < miny) miny = (int16_t)dy; + if(dy > maxy) maxy = (int16_t)dy; + + /* Clipping */ + if(minx < dest_clip0_x) minx = dest_clip0_x; + if(maxx > dest_clip1_x - 1) maxx = dest_clip1_x - 1; + if(miny < dest_clip0_y) miny = dest_clip0_y; + if(maxy > dest_clip1_y - 1) maxy = dest_clip1_y - 1; + + float dvCol = cosAngle / scale; + float duCol = sinAngle / scale; + + float duRow = dvCol; + float dvRow = -duCol; + + float startu = px - (ox * dvCol + oy * duCol); + float startv = py - (ox * dvRow + oy * duRow); + + float rowu = startu + miny * duCol; + float rowv = startv + miny * dvCol; + + for(y = miny; y <= maxy; y++) { + float u = rowu + minx * duRow; + float v = rowv + minx * dvRow; + for(x = minx; x <= maxx; x++) { + if(u >= source_clip0_x && u < source_clip1_x && v >= source_clip0_y && v < source_clip1_y) { + uint32_t c = common_hal_displayio_bitmap_get_pixel(source, u, v); + if( (skip_index_none) || (c != skip_index) ) { + common_hal_displayio_bitmap_set_pixel(self, x, y, c); + } + } + u += duRow; + v += dvRow; + } + rowu += duCol; + rowv += dvCol; + } +} -- cgit v1.2.3 From 18658b77f302109284cc36099298f0317becb5af Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 24 Feb 2021 01:00:13 -0600 Subject: Sphinx docstring updates --- shared-bindings/bitmaptools/__init__.c | 73 +++++++++++++++------------------- 1 file changed, 33 insertions(+), 40 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 7f7e16bcb..8f552e2e0 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -32,6 +32,9 @@ #include "py/obj.h" #include "py/runtime.h" +//| """Collection of bitmap manipulation tools""" +//| + STATIC int16_t validate_point(mp_obj_t point, int16_t default_value) { // Checks if point is None and returns default_value, otherwise decodes integer value if ( point == mp_const_none ) { @@ -106,48 +109,38 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl } - //| -//| def rotozoom(dest_bitmap: Bitmap, ox: int, oy: int, -//| dest_clip0: Tuple[int, int], -//| dest_clip1: Tuple[int, int], -//| source_bitmap: Bitmap, -//| px: int, py: int, -//| source_clip0: Tuple[int, int], -//| source_clip1: Tuple[int, int], -//| angle: float, -//| scale: float, -//| skip_index: int) -> None: -//| """Inserts the source bitmap region into the destination bitmap with rotation (angle), scale -//| and clipping (both on source and destination bitmaps). +//| def rotozoom( +//| dest_bitmap: Bitmap, ox: int, oy: int, +//| dest_clip0: Tuple[int, int], dest_clip1: Tuple[int, int], +//| source_bitmap: Bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], +//| angle: float, scale: float, skip_index: int) -> None: +//| """Inserts the source bitmap region into the destination bitmap with rotation +//| (angle), scale and clipping (both on source and destination bitmaps). //| -//| :param bitmap dest_bitmap: The bitmap that will be copied into -//| :param int ox: Horizontal pixel location in destination bitmap where source bitmap -//| point (px,py) is placed -//| :param int oy: Vertical pixel location in destination bitmap where source bitmap -//| point (px,py) is placed -//| :param dest_clip0: First corner of rectangular destination clipping -//| region that constrains region of writing into destination bitmap -//| :type dest_clip0: Tuple[int,int] -//| :param dest_clip1: second corner of rectangular destination clipping -//| region that constrains region of writing into destination bitmap -//| :type dest_clip1: Tuple[int,int] -//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied -//| :param int px: Horizontal pixel location in source bitmap that is placed into the -//| destination bitmap at (ox,oy) -//| :param int py: Vertical pixel location in source bitmap that is placed into the -//| destination bitmap at (ox,oy) -//| :param source_clip0: First corner of rectangular destination clipping -//| region that constrains region of writing into destination bitmap -//| :type source_clip0: Tuple[int,int] -//| :param source_clip1: second corner of rectangular destination clipping -//| region that constrains region of writing into destination bitmap -//| :type source_clip1: Tuple[int,int] -//| :param float angle: angle of rotation, in radians (positive is clockwise direction) -//| :param float scale: scaling factor -//| :param int skip_index: bitmap palette index in the source that will not be copied, -//| set to None to copy all pixels""" -//| ... +//| :param bitmap dest_bitmap: The bitmap that will be copied into +//| :param int ox: Horizontal pixel location in destination bitmap where source bitmap +//| point (px,py) is placed +//| :param int oy: Vertical pixel location in destination bitmap where source bitmap +//| point (px,py) is placed +//| :param Tuple[int,int] dest_clip0: First corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :param Tuple[int,int] dest_clip1: Second corner of rectangular destination clipping +//| region that constrains region of writing into destination bitmap +//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied +//| :param int px: Horizontal pixel location in source bitmap that is placed into the +//| destination bitmap at (ox,oy) +//| :param int py: Vertical pixel location in source bitmap that is placed into the +//| destination bitmap at (ox,oy) +//| :param Tuple[int,int] source_clip0: First corner of rectangular source clipping +//| region that constrains region of reading from the source bitmap +//| :param Tuple[int,int] source_clip1: Second corner of rectangular source clipping +//| region that constrains region of reading from the source bitmap +//| :param float angle: Angle of rotation, in radians (positive is clockwise direction) +//| :param float scale: Scaling factor +//| :param int skip_index: Bitmap palette index in the source that will not be copied, +//| set to None to copy all pixels""" +//| ... //| STATIC mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args){ enum {ARG_dest_bitmap, ARG_ox, ARG_oy, ARG_dest_clip0, ARG_dest_clip1, -- cgit v1.2.3 From af5ad50125f41fea40103a63232b807ea56654ad Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 24 Feb 2021 01:18:30 -0600 Subject: More sphinx fixes --- shared-bindings/bitmaptools/__init__.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 8f552e2e0..63a81e01f 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -111,9 +111,9 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl //| //| def rotozoom( -//| dest_bitmap: Bitmap, ox: int, oy: int, +//| dest_bitmap: bitmap, ox: int, oy: int, //| dest_clip0: Tuple[int, int], dest_clip1: Tuple[int, int], -//| source_bitmap: Bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], +//| source_bitmap: bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], //| angle: float, scale: float, skip_index: int) -> None: //| """Inserts the source bitmap region into the destination bitmap with rotation //| (angle), scale and clipping (both on source and destination bitmaps). -- cgit v1.2.3 From 2815c6dafa77fea930f02fac60caf13f7c4584ea Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 24 Feb 2021 07:49:34 -0600 Subject: More sphinx attempts --- shared-bindings/bitmaptools/__init__.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 63a81e01f..029995f17 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -109,11 +109,12 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl } +//| """:py:class:`~displayio.Bitmap`""" //| //| def rotozoom( -//| dest_bitmap: bitmap, ox: int, oy: int, +//| dest_bitmap: Bitmap, ox: int, oy: int, //| dest_clip0: Tuple[int, int], dest_clip1: Tuple[int, int], -//| source_bitmap: bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], +//| source_bitmap: Bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], //| angle: float, scale: float, skip_index: int) -> None: //| """Inserts the source bitmap region into the destination bitmap with rotation //| (angle), scale and clipping (both on source and destination bitmaps). -- cgit v1.2.3 From e858db07f0f335f36d9780322ab38b025b00d7df Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 24 Feb 2021 07:59:32 -0600 Subject: Another sphinx try --- shared-bindings/bitmaptools/__init__.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 029995f17..ede74621a 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -109,7 +109,7 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl } -//| """:py:class:`~displayio.Bitmap`""" +//| """:py:class:`Bitmap`""" //| //| def rotozoom( //| dest_bitmap: Bitmap, ox: int, oy: int, -- cgit v1.2.3 From cd4d55a57309230eae02df54db65d9b095d78f07 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 24 Feb 2021 08:05:40 -0600 Subject: yet another sphinx try --- shared-bindings/bitmaptools/__init__.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index ede74621a..dce082f9b 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -109,12 +109,11 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl } -//| """:py:class:`Bitmap`""" //| //| def rotozoom( -//| dest_bitmap: Bitmap, ox: int, oy: int, +//| dest_bitmap: displayio.Bitmap, ox: int, oy: int, //| dest_clip0: Tuple[int, int], dest_clip1: Tuple[int, int], -//| source_bitmap: Bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], +//| source_bitmap: displayio.Bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], //| angle: float, scale: float, skip_index: int) -> None: //| """Inserts the source bitmap region into the destination bitmap with rotation //| (angle), scale and clipping (both on source and destination bitmaps). -- cgit v1.2.3 From 5d7fdafcdec126f4f552689df25cecf30b39265d Mon Sep 17 00:00:00 2001 From: microDev <70126934+microDev1@users.noreply.github.com> Date: Thu, 25 Feb 2021 00:48:36 +0530 Subject: implement suggested changes - add internal buffering - rtc initialization fix --- locale/circuitpython.pot | 23 +++--- ports/mimxrt10xx/common-hal/busio/UART.c | 8 +-- ports/nrf/common-hal/busio/UART.c | 8 ++- ports/raspberrypi/common-hal/busio/UART.c | 115 +++++++++++++++++++----------- ports/raspberrypi/common-hal/busio/UART.h | 6 +- ports/raspberrypi/supervisor/port.c | 7 ++ shared-bindings/busio/UART.c | 6 +- 7 files changed, 107 insertions(+), 66 deletions(-) (limited to 'shared-bindings') diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index bffc3005c..6a009f63d 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -332,11 +332,8 @@ msgid "All SPI peripherals are in use" msgstr "" #: ports/esp32s2/common-hal/busio/UART.c ports/nrf/common-hal/busio/UART.c -msgid "All UART peripherals are in use" -msgstr "" - #: ports/raspberrypi/common-hal/busio/UART.c -msgid "All UART peripherals in use" +msgid "All UART peripherals are in use" msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c @@ -947,6 +944,7 @@ msgid "Failed to acquire mutex, err 0x%04x" msgstr "" #: ports/mimxrt10xx/common-hal/busio/UART.c ports/nrf/common-hal/busio/UART.c +#: ports/raspberrypi/common-hal/busio/UART.c msgid "Failed to allocate RX buffer" msgstr "" @@ -1202,7 +1200,8 @@ msgstr "" msgid "Invalid bits per value" msgstr "" -#: ports/nrf/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c +#: ports/nrf/common-hal/busio/UART.c ports/raspberrypi/common-hal/busio/UART.c +#: ports/stm/common-hal/busio/UART.c msgid "Invalid buffer size" msgstr "" @@ -1277,8 +1276,7 @@ msgstr "" #: ports/esp32s2/common-hal/busio/I2C.c ports/esp32s2/common-hal/busio/SPI.c #: ports/esp32s2/common-hal/busio/UART.c ports/esp32s2/common-hal/canio/CAN.c #: ports/mimxrt10xx/common-hal/busio/I2C.c -#: ports/mimxrt10xx/common-hal/busio/SPI.c -#: ports/mimxrt10xx/common-hal/busio/UART.c ports/nrf/common-hal/busio/I2C.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c ports/nrf/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/busio/SPI.c #: ports/raspberrypi/common-hal/busio/UART.c @@ -1330,7 +1328,8 @@ msgstr "" msgid "Invalid wave file" msgstr "" -#: ports/stm/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/UART.c ports/nrf/common-hal/busio/UART.c +#: ports/raspberrypi/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "Invalid word/bit length" msgstr "" @@ -1820,7 +1819,7 @@ msgstr "" msgid "RNG Init Error" msgstr "" -#: ports/nrf/common-hal/busio/UART.c +#: ports/nrf/common-hal/busio/UART.c ports/raspberrypi/common-hal/busio/UART.c msgid "RS485 Not yet supported on this device" msgstr "" @@ -1829,10 +1828,6 @@ msgstr "" msgid "RS485 inversion specified when not in RS485 mode" msgstr "" -#: ports/raspberrypi/common-hal/busio/UART.c -msgid "RS485 is not supported on this board" -msgstr "" - #: ports/cxd56/common-hal/rtc/RTC.c ports/esp32s2/common-hal/rtc/RTC.c #: ports/mimxrt10xx/common-hal/rtc/RTC.c ports/nrf/common-hal/rtc/RTC.c #: ports/raspberrypi/common-hal/rtc/RTC.c @@ -2460,7 +2455,7 @@ msgid "binary op %q not implemented" msgstr "" #: shared-bindings/busio/UART.c -msgid "bits must be between 5 and 8" +msgid "bits must be in range 5 to 9" msgstr "" #: shared-bindings/audiomixer/Mixer.c diff --git a/ports/mimxrt10xx/common-hal/busio/UART.c b/ports/mimxrt10xx/common-hal/busio/UART.c index 03a6e8f3e..503da14c2 100644 --- a/ports/mimxrt10xx/common-hal/busio/UART.c +++ b/ports/mimxrt10xx/common-hal/busio/UART.c @@ -93,6 +93,10 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->character_bits = bits; self->timeout_ms = timeout * 1000; + if (self->character_bits != 7 && self->character_bits != 8) { + mp_raise_ValueError(translate("Invalid word/bit length")); + } + // We are transmitting one direction if one pin is NULL and the other isn't. bool is_onedirection = (rx == NULL) != (tx == NULL); bool uart_taken = false; @@ -154,10 +158,6 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, mp_raise_ValueError(translate("Hardware in use, try alternative pins")); } - if(self->rx == NULL && self->tx == NULL) { - mp_raise_ValueError(translate("Invalid pins")); - } - if (is_onedirection && ((rts != NULL) || (cts != NULL))) { mp_raise_ValueError(translate("Both RX and TX required for flow control")); } diff --git a/ports/nrf/common-hal/busio/UART.c b/ports/nrf/common-hal/busio/UART.c index 6ecf7e0ba..491e360e4 100644 --- a/ports/nrf/common-hal/busio/UART.c +++ b/ports/nrf/common-hal/busio/UART.c @@ -70,9 +70,9 @@ static uint32_t get_nrf_baud (uint32_t baudrate) { { 14400, NRF_UARTE_BAUDRATE_14400 }, { 19200, NRF_UARTE_BAUDRATE_19200 }, { 28800, NRF_UARTE_BAUDRATE_28800 }, - { 31250, NRF_UARTE_BAUDRATE_31250 }, + { 31250, NRF_UARTE_BAUDRATE_31250 }, { 38400, NRF_UARTE_BAUDRATE_38400 }, - { 56000, NRF_UARTE_BAUDRATE_56000 }, + { 56000, NRF_UARTE_BAUDRATE_56000 }, { 57600, NRF_UARTE_BAUDRATE_57600 }, { 76800, NRF_UARTE_BAUDRATE_76800 }, { 115200, NRF_UARTE_BAUDRATE_115200 }, @@ -144,6 +144,10 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { + if (bits != 8) { + mp_raise_ValueError(translate("Invalid word/bit length")); + } + if ((rs485_dir != NULL) || (rs485_invert)) { mp_raise_ValueError(translate("RS485 Not yet supported on this device")); } diff --git a/ports/raspberrypi/common-hal/busio/UART.c b/ports/raspberrypi/common-hal/busio/UART.c index 660a473ae..e319b4ccf 100644 --- a/ports/raspberrypi/common-hal/busio/UART.c +++ b/ports/raspberrypi/common-hal/busio/UART.c @@ -31,18 +31,15 @@ #include "py/runtime.h" #include "supervisor/shared/tick.h" #include "lib/utils/interrupt_char.h" +#include "common-hal/microcontroller/Pin.h" +#include "src/rp2_common/hardware_irq/include/hardware/irq.h" #include "src/rp2_common/hardware_gpio/include/hardware/gpio.h" #define NO_PIN 0xff #define UART_INST(uart) (((uart) ? uart1 : uart0)) -#define TX 0 -#define RX 1 -#define CTS 2 -#define RTS 3 - typedef enum { STATUS_FREE = 0, STATUS_IN_USE, @@ -51,7 +48,7 @@ typedef enum { static uart_status_t uart_status[2]; -void uart_reset(void) { +void reset_uart(void) { for (uint8_t num = 0; num < 2; num++) { if (uart_status[num] == STATUS_IN_USE) { uart_status[num] = STATUS_FREE; @@ -71,7 +68,7 @@ static uint8_t get_free_uart() { break; } if (num) { - mp_raise_RuntimeError(translate("All UART peripherals in use")); + mp_raise_RuntimeError(translate("All UART peripherals are in use")); } } return num; @@ -84,10 +81,21 @@ static uint8_t pin_init(const uint8_t uart, const mcu_pin_obj_t * pin, const uin if (!(((pin->number & 3) == pin_type) && ((((pin->number + 4) & 8) >> 3) == uart))) { mp_raise_ValueError(translate("Invalid pins")); } + claim_pin(pin); gpio_set_function(pin->number, GPIO_FUNC_UART); return pin->number; } +static ringbuf_t ringbuf[2]; + +static void uart0_callback(void) { + while (uart_is_readable(uart0) && !ringbuf_put(&ringbuf[0], (uint8_t)uart_get_hw(uart0)->dr)) {} +} + +static void uart1_callback(void) { + while (uart_is_readable(uart1) && !ringbuf_put(&ringbuf[1], (uint8_t)uart_get_hw(uart1)->dr)) {} +} + void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, @@ -96,25 +104,56 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, bool sigint_enabled) { + if (bits > 8) { + mp_raise_ValueError(translate("Invalid word/bit length")); + } + + if (receiver_buffer_size == 0) { + mp_raise_ValueError(translate("Invalid buffer size")); + } + if ((rs485_dir != NULL) || (rs485_invert)) { - mp_raise_NotImplementedError(translate("RS485 is not supported on this board")); + mp_raise_NotImplementedError(translate("RS485 Not yet supported on this device")); } uint8_t uart_id = get_free_uart(); - self->tx_pin = pin_init(uart_id, tx, TX); - self->rx_pin = pin_init(uart_id, rx, RX); - self->cts_pin = pin_init(uart_id, cts, CTS); - self->rts_pin = pin_init(uart_id, rts, RTS); + self->tx_pin = pin_init(uart_id, tx, 0); + self->rx_pin = pin_init(uart_id, rx, 1); + self->cts_pin = pin_init(uart_id, cts, 2); + self->rts_pin = pin_init(uart_id, rts, 3); self->uart = UART_INST(uart_id); + self->uart_id = uart_id; self->baudrate = baudrate; self->timeout_ms = timeout * 1000; uart_init(self->uart, self->baudrate); - uart_set_fifo_enabled(self->uart, true); + uart_set_fifo_enabled(self->uart, false); uart_set_format(self->uart, bits, stop, parity); uart_set_hw_flow(self->uart, (cts != NULL), (rts != NULL)); + + if (rx != NULL) { + // Initially allocate the UART's buffer in the long-lived part of the + // heap. UARTs are generally long-lived objects, but the "make long- + // lived" machinery is incapable of moving internal pointers like + // self->buffer, so do it manually. (However, as long as internal + // pointers like this are NOT moved, allocating the buffer + // in the long-lived pool is not strictly necessary) + // (This is a macro.) + if (!ringbuf_alloc(&ringbuf[uart_id], receiver_buffer_size, true)) { + mp_raise_msg(&mp_type_MemoryError, translate("Failed to allocate RX buffer")); + } + if (uart_id) { + self->uart_irq_id = UART1_IRQ; + irq_set_exclusive_handler(self->uart_irq_id, uart1_callback); + } else { + self->uart_irq_id = UART0_IRQ; + irq_set_exclusive_handler(self->uart_irq_id, uart0_callback); + } + irq_set_enabled(self->uart_irq_id, true); + uart_set_irq_enables(self->uart, true, false); + } } bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { @@ -126,6 +165,7 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { return; } uart_deinit(self->uart); + ringbuf_free(&ringbuf[self->uart_id]); reset_pin_number(self->tx_pin); reset_pin_number(self->rx_pin); reset_pin_number(self->cts_pin); @@ -169,36 +209,25 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t size_t total_read = 0; uint64_t start_ticks = supervisor_ticks_ms64(); - // Busy-wait until timeout or until we've read enough chars. - while (supervisor_ticks_ms64() - start_ticks <= self->timeout_ms) { - if (uart_is_readable(self->uart)) { - // Read and advance. - *data++ = uart_get_hw(self->uart)->dr; - - // Decrease how many chars left to read. - len--; - total_read++; - - // Reset the timeout on every character read. + // Busy-wait for all bytes received or timeout + while (len > 0 && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) { + // Reset the timeout on every character read. + if (ringbuf_num_filled(&ringbuf[self->uart_id])) { + // Prevent conflict with uart irq. + irq_set_enabled(self->uart_irq_id, false); + // Copy as much received data as available, up to len bytes. + size_t num_read = ringbuf_get_n(&ringbuf[self->uart_id], data, len); + // Re-enable irq. + irq_set_enabled(self->uart_irq_id, true); + len-=num_read; + data+=num_read; + total_read+=num_read; start_ticks = supervisor_ticks_ms64(); } - RUN_BACKGROUND_TASKS; - // Allow user to break out of a timeout with a KeyboardInterrupt. if (mp_hal_is_interrupted()) { - break; - } - - // Don't need to read any more: data buf is full. - if (len == 0) { - break; - } - - // If we are zero timeout, make sure we don't loop again (in the event - // we read in under 1ms) - if (self->timeout_ms == 0) { - break; + return 0; } } @@ -228,12 +257,16 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou } uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { - return uart_is_readable(self->uart); + return ringbuf_num_filled(&ringbuf[self->uart_id]); } -void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) {} +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { + // Prevent conflict with uart irq. + irq_set_enabled(self->uart_irq_id, false); + ringbuf_clear(&ringbuf[self->uart_id]); + irq_set_enabled(self->uart_irq_id, true); +} -// True if there are no characters still to be written. bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { if (self->tx_pin == NO_PIN) { return false; diff --git a/ports/raspberrypi/common-hal/busio/UART.h b/ports/raspberrypi/common-hal/busio/UART.h index 03613daeb..da6170596 100644 --- a/ports/raspberrypi/common-hal/busio/UART.h +++ b/ports/raspberrypi/common-hal/busio/UART.h @@ -28,7 +28,7 @@ #define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H #include "py/obj.h" -#include "common-hal/microcontroller/Pin.h" +#include "py/ringbuf.h" #include "src/rp2_common/hardware_uart/include/hardware/uart.h" @@ -38,12 +38,14 @@ typedef struct { uint8_t rx_pin; uint8_t cts_pin; uint8_t rts_pin; + uint8_t uart_id; + uint8_t uart_irq_id; uint32_t baudrate; uint32_t timeout_ms; uart_inst_t * uart; } busio_uart_obj_t; -extern void uart_reset(void); +extern void reset_uart(void); extern void never_reset_uart(uint8_t num); #endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_BUSIO_UART_H diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 36d2e8275..3b8790ea0 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -39,6 +39,9 @@ #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/pwmio/PWMOut.h" +#include "common-hal/rtc/RTC.h" +#include "common-hal/busio/UART.h" + #include "supervisor/shared/safe_mode.h" #include "supervisor/shared/stack.h" #include "supervisor/shared/tick.h" @@ -78,6 +81,9 @@ safe_mode_t port_init(void) { // Reset everything into a known state before board_init. reset_port(); + // Initialize RTC + common_hal_rtc_init(); + // For the tick. hardware_alarm_claim(0); hardware_alarm_set_callback(0, _tick_callback); @@ -95,6 +101,7 @@ void reset_port(void) { #if CIRCUITPY_BUSIO reset_i2c(); reset_spi(); + reset_uart(); #endif #if CIRCUITPY_PWMIO diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 151081d31..50d233f2b 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -55,7 +55,7 @@ //| :param ~microcontroller.Pin rs485_dir: the output pin for rs485 direction setting, or ``None`` if rs485 not in use. //| :param bool rs485_invert: rs485_dir pin active high when set. Active low otherwise. //| :param int baudrate: the transmit and receive speed. -//| :param int bits: the number of bits per byte, 5 to 8. +//| :param int bits: the number of bits per byte, 5 to 9. //| :param Parity parity: the parity used for error checking. //| :param int stop: the number of stop bits, 1 or 2. //| :param float timeout: the timeout in seconds to wait for the first character and between subsequent characters when reading. Raises ``ValueError`` if timeout >100 seconds. @@ -110,8 +110,8 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, co mp_raise_ValueError(translate("tx and rx cannot both be None")); } - if (args[ARG_bits].u_int < 5 || args[ARG_bits].u_int > 8) { - mp_raise_ValueError(translate("bits must be between 5 and 8")); + if (args[ARG_bits].u_int < 5 || args[ARG_bits].u_int > 9) { + mp_raise_ValueError(translate("bits must be in range 5 to 9")); } uint8_t bits = args[ARG_bits].u_int; -- cgit v1.2.3 From c883bb773bbd226d4e18a7b4213b61157e358545 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 24 Feb 2021 16:03:50 -0600 Subject: Rearrange input parameters --- shared-bindings/bitmaptools/__init__.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index dce082f9b..cf48d12dc 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -111,14 +111,16 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl //| //| def rotozoom( -//| dest_bitmap: displayio.Bitmap, ox: int, oy: int, -//| dest_clip0: Tuple[int, int], dest_clip1: Tuple[int, int], -//| source_bitmap: displayio.Bitmap, px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], +//| dest_bitmap: displayio.Bitmap, source_bitmap: displayio.Bitmap, +//| *, +//| ox: int, oy: int, dest_clip0: Tuple[int, int], dest_clip1: Tuple[int, int], +//| px: int, py: int, source_clip0: Tuple[int, int], source_clip1: Tuple[int, int], //| angle: float, scale: float, skip_index: int) -> None: //| """Inserts the source bitmap region into the destination bitmap with rotation //| (angle), scale and clipping (both on source and destination bitmaps). //| -//| :param bitmap dest_bitmap: The bitmap that will be copied into +//| :param bitmap dest_bitmap: Destination bitmap that will be copied into +//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied //| :param int ox: Horizontal pixel location in destination bitmap where source bitmap //| point (px,py) is placed //| :param int oy: Vertical pixel location in destination bitmap where source bitmap @@ -127,7 +129,6 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl //| region that constrains region of writing into destination bitmap //| :param Tuple[int,int] dest_clip1: Second corner of rectangular destination clipping //| region that constrains region of writing into destination bitmap -//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied //| :param int px: Horizontal pixel location in source bitmap that is placed into the //| destination bitmap at (ox,oy) //| :param int py: Vertical pixel location in source bitmap that is placed into the @@ -143,19 +144,20 @@ STATIC void validate_clip_region(displayio_bitmap_t *bitmap, mp_obj_t clip0_tupl //| ... //| STATIC mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args){ - enum {ARG_dest_bitmap, ARG_ox, ARG_oy, ARG_dest_clip0, ARG_dest_clip1, - ARG_source_bitmap, ARG_px, ARG_py, - ARG_source_clip0, ARG_source_clip1, + enum {ARG_dest_bitmap, ARG_source_bitmap, + ARG_ox, ARG_oy, ARG_dest_clip0, ARG_dest_clip1, + ARG_px, ARG_py, ARG_source_clip0, ARG_source_clip1, ARG_angle, ARG_scale, ARG_skip_index}; static const mp_arg_t allowed_args[] = { {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_ox, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->width / 2 {MP_QSTR_oy, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->height / 2 {MP_QSTR_dest_clip0, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, {MP_QSTR_dest_clip1, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, {MP_QSTR_px, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->width / 2 {MP_QSTR_py, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->height / 2 {MP_QSTR_source_clip0, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, -- cgit v1.2.3 From 52bc935fa7aa9e5c94f75fec8b2fa746c83f0be7 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 25 Feb 2021 16:50:57 -0800 Subject: A few minor fixes for corner cases * Always clear the peripheral interrupt so we don't hang when full * Store the ringbuf in the object so it gets collected when we're alive * Make UART objects have a finaliser so they are deinit when their memory is freed * Copy bytes into the ringbuf from the FIFO after we read to ensure the interrupt is enabled ASAP * Copy bytes into the ringbuf from the FIFO before measuring our rx available because the interrupt is based on a threshold (not > 0). For example, a single byte won't trigger an interrupt. --- ports/raspberrypi/common-hal/busio/UART.c | 57 +++++++++++++++++++++++-------- ports/raspberrypi/common-hal/busio/UART.h | 1 + py/gc.c | 2 +- py/malloc.c | 6 ++-- py/misc.h | 8 +++-- shared-bindings/busio/UART.c | 3 +- tests/manual/busio/uart_echo.py | 18 ++++++++++ 7 files changed, 72 insertions(+), 23 deletions(-) create mode 100644 tests/manual/busio/uart_echo.py (limited to 'shared-bindings') diff --git a/ports/raspberrypi/common-hal/busio/UART.c b/ports/raspberrypi/common-hal/busio/UART.c index 1f0f4e2f6..1bc121177 100644 --- a/ports/raspberrypi/common-hal/busio/UART.c +++ b/ports/raspberrypi/common-hal/busio/UART.c @@ -73,18 +73,27 @@ static uint8_t pin_init(const uint8_t uart, const mcu_pin_obj_t * pin, const uin return pin->number; } -static ringbuf_t ringbuf[NUM_UARTS]; +static busio_uart_obj_t* active_uarts[NUM_UARTS]; -static void uart0_callback(void) { - while (uart_is_readable(uart0) && ringbuf_num_empty(&ringbuf[0]) > 0) { - ringbuf_put(&ringbuf[0], (uint8_t)uart_get_hw(uart0)->dr); +static void _copy_into_ringbuf(ringbuf_t* r, uart_inst_t* uart) { + while (uart_is_readable(uart) && ringbuf_num_empty(r) > 0) { + ringbuf_put(r, (uint8_t) uart_get_hw(uart)->dr); } } +static void shared_callback(busio_uart_obj_t *self) { + _copy_into_ringbuf(&self->ringbuf, self->uart); + // We always clear the interrupt so it doesn't continue to fire because we + // may not have read everything available. + uart_get_hw(self->uart)->icr = UART_UARTICR_RXIC_BITS; +} + +static void uart0_callback(void) { + shared_callback(active_uarts[0]); +} + static void uart1_callback(void) { - while (uart_is_readable(uart1) && ringbuf_num_empty(&ringbuf[1]) > 0) { - ringbuf_put(&ringbuf[1], (uint8_t)uart_get_hw(uart1)->dr); - } + shared_callback(active_uarts[1]); } void common_hal_busio_uart_construct(busio_uart_obj_t *self, @@ -138,9 +147,10 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // pointers like this are NOT moved, allocating the buffer // in the long-lived pool is not strictly necessary) // (This is a macro.) - if (!ringbuf_alloc(&ringbuf[uart_id], receiver_buffer_size, true)) { + if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size, true)) { mp_raise_msg(&mp_type_MemoryError, translate("Failed to allocate RX buffer")); } + active_uarts[uart_id] = self; if (uart_id == 1) { self->uart_irq_id = UART1_IRQ; irq_set_exclusive_handler(self->uart_irq_id, uart1_callback); @@ -149,7 +159,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, irq_set_exclusive_handler(self->uart_irq_id, uart0_callback); } irq_set_enabled(self->uart_irq_id, true); - uart_set_irq_enables(self->uart, true, false); + uart_set_irq_enables(self->uart, true /* rx has data */, false /* tx needs data */); } } @@ -162,7 +172,8 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { return; } uart_deinit(self->uart); - ringbuf_free(&ringbuf[self->uart_id]); + ringbuf_free(&self->ringbuf); + active_uarts[self->uart_id] = NULL; uart_status[self->uart_id] = STATUS_FREE; reset_pin_number(self->tx_pin); reset_pin_number(self->rx_pin); @@ -208,7 +219,7 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t irq_set_enabled(self->uart_irq_id, false); // Copy as much received data as available, up to len bytes. - size_t total_read = ringbuf_get_n(&ringbuf[self->uart_id], data, len); + size_t total_read = ringbuf_get_n(&self->ringbuf, data, len); // Check if we still need to read more data. if (len > total_read) { @@ -218,7 +229,7 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t while (len > 0 && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) { if (uart_is_readable(self->uart)) { // Read and advance. - *data++ = uart_get_hw(self->uart)->dr; + data[total_read] = uart_get_hw(self->uart)->dr; // Adjust the counters. len--; @@ -230,11 +241,16 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t RUN_BACKGROUND_TASKS; // Allow user to break out of a timeout with a KeyboardInterrupt. if (mp_hal_is_interrupted()) { - return 0; + break; } } } + // Now that we've emptied the ringbuf some, fill it up with anything in the + // FIFO. This ensures that we'll empty the FIFO as much as possible and + // reset the interrupt when we catch up. + _copy_into_ringbuf(&self->ringbuf, self->uart); + // Re-enable irq. irq_set_enabled(self->uart_irq_id, true); @@ -264,13 +280,24 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou } uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { - return ringbuf_num_filled(&ringbuf[self->uart_id]); + // Prevent conflict with uart irq. + irq_set_enabled(self->uart_irq_id, false); + // The UART only interrupts after a threshold so make sure to copy anything + // out of its FIFO before measuring how many bytes we've received. + _copy_into_ringbuf(&self->ringbuf, self->uart); + irq_set_enabled(self->uart_irq_id, false); + return ringbuf_num_filled(&self->ringbuf); } void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { // Prevent conflict with uart irq. irq_set_enabled(self->uart_irq_id, false); - ringbuf_clear(&ringbuf[self->uart_id]); + ringbuf_clear(&self->ringbuf); + + // Throw away the FIFO contents too. + while (uart_is_readable(self->uart)) { + (void) uart_get_hw(self->uart)->dr; + } irq_set_enabled(self->uart_irq_id, true); } diff --git a/ports/raspberrypi/common-hal/busio/UART.h b/ports/raspberrypi/common-hal/busio/UART.h index da6170596..4c07de240 100644 --- a/ports/raspberrypi/common-hal/busio/UART.h +++ b/ports/raspberrypi/common-hal/busio/UART.h @@ -43,6 +43,7 @@ typedef struct { uint32_t baudrate; uint32_t timeout_ms; uart_inst_t * uart; + ringbuf_t ringbuf; } busio_uart_obj_t; extern void reset_uart(void); diff --git a/py/gc.c b/py/gc.c index 69327060f..2aa2668dc 100755 --- a/py/gc.c +++ b/py/gc.c @@ -192,7 +192,7 @@ void gc_init(void *start, void *end) { } void gc_deinit(void) { - // Run any finalizers before we stop using the heap. + // Run any finalisers before we stop using the heap. gc_sweep_all(); MP_STATE_MEM(gc_pool_start) = 0; diff --git a/py/malloc.c b/py/malloc.c index 8d5141ee0..c923e89a6 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -57,7 +57,7 @@ #undef free #undef realloc #define malloc_ll(b, ll) gc_alloc((b), false, (ll)) -#define malloc_with_finaliser(b) gc_alloc((b), true, false) +#define malloc_with_finaliser(b, ll) gc_alloc((b), true, (ll)) #define free gc_free #define realloc(ptr, n) gc_realloc(ptr, n, true) #define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv) @@ -103,8 +103,8 @@ void *m_malloc_maybe(size_t num_bytes, bool long_lived) { } #if MICROPY_ENABLE_FINALISER -void *m_malloc_with_finaliser(size_t num_bytes) { - void *ptr = malloc_with_finaliser(num_bytes); +void *m_malloc_with_finaliser(size_t num_bytes, bool long_lived) { + void *ptr = malloc_with_finaliser(num_bytes, long_lived); if (ptr == NULL && num_bytes != 0) { m_malloc_fail(num_bytes); } diff --git a/py/misc.h b/py/misc.h index 6ed256e70..0fef7e249 100644 --- a/py/misc.h +++ b/py/misc.h @@ -72,11 +72,13 @@ typedef unsigned int uint; #define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type*)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num), false)) #define m_new_ll_obj_var_maybe(obj_type, var_type, var_num) ((obj_type*)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num), true)) #if MICROPY_ENABLE_FINALISER -#define m_new_obj_with_finaliser(type) ((type*)(m_malloc_with_finaliser(sizeof(type)))) -#define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type*)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num))) +#define m_new_obj_with_finaliser(type) ((type*)(m_malloc_with_finaliser(sizeof(type), false))) +#define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type*)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num), false)) +#define m_new_ll_obj_with_finaliser(type) ((type*)(m_malloc_with_finaliser(sizeof(type), true))) #else #define m_new_obj_with_finaliser(type) m_new_obj(type) #define m_new_obj_var_with_finaliser(type, var_type, var_num) m_new_obj_var(type, var_type, var_num) +#define m_new_ll_obj_with_finaliser(type) m_new_ll_obj(type) #endif #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #define m_renew(type, ptr, old_num, new_num) ((type*)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num)))) @@ -93,7 +95,7 @@ typedef unsigned int uint; void *m_malloc(size_t num_bytes, bool long_lived); void *m_malloc_maybe(size_t num_bytes, bool long_lived); -void *m_malloc_with_finaliser(size_t num_bytes); +void *m_malloc_with_finaliser(size_t num_bytes, bool long_lived); void *m_malloc0(size_t num_bytes, bool long_lived); #if MICROPY_MALLOC_USES_ALLOCATED_SIZE void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes); diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 50d233f2b..2f8fc9c32 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -82,7 +82,7 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, co // This is needed to avoid crashes with certain UART implementations which // cannot accomodate being moved after creation. (See // https://github.com/adafruit/circuitpython/issues/1056) - busio_uart_obj_t *self = m_new_ll_obj(busio_uart_obj_t); + busio_uart_obj_t *self = m_new_ll_obj_with_finaliser(busio_uart_obj_t); self->base.type = &busio_uart_type; enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_timeout, ARG_receiver_buffer_size, ARG_rts, ARG_cts, ARG_rs485_dir,ARG_rs485_invert}; @@ -387,6 +387,7 @@ const mp_obj_type_t busio_uart_parity_type = { }; STATIC const mp_rom_map_elem_t busio_uart_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&busio_uart_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_uart_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&busio_uart___exit___obj) }, diff --git a/tests/manual/busio/uart_echo.py b/tests/manual/busio/uart_echo.py new file mode 100644 index 000000000..f55d4db9e --- /dev/null +++ b/tests/manual/busio/uart_echo.py @@ -0,0 +1,18 @@ +import busio +import board +import time + +i = 0 + +u = busio.UART(tx=board.TX, rx=board.RX) + +while True: + u.write(str(i).encode("utf-8")) + time.sleep(0.1) + print(i, u.in_waiting) # should be the number of digits + time.sleep(0.1) + print(i, u.in_waiting) # should be the number of digits + r = u.read(64 + 10) + print(i, u.in_waiting) # should be 0 + print(len(r), r) + i += 1 -- cgit v1.2.3 From e9953754ea5e5a249b01c354af0109a12af1da47 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sun, 21 Feb 2021 17:11:55 +0100 Subject: Add displayio.Group.sort() method --- shared-bindings/displayio/Group.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'shared-bindings') diff --git a/shared-bindings/displayio/Group.c b/shared-bindings/displayio/Group.c index 386e270ab..a7f42d2b4 100644 --- a/shared-bindings/displayio/Group.c +++ b/shared-bindings/displayio/Group.c @@ -328,6 +328,21 @@ STATIC mp_obj_t group_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t valu return mp_const_none; } +//| def sort(self, key: function, reverse: bool) -> None: +//| """Sort the members of the group.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + displayio_group_t *self = native_group(pos_args[0]); + mp_obj_t *args = m_new(mp_obj_t, n_args); + for (size_t i = 1; i < n_args; ++i) { + args[i] = pos_args[i]; + } + args[0] = self->members; + return mp_obj_list_sort(n_args, pos_args, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_group_sort_obj, 1, displayio_group_obj_sort); + STATIC const mp_rom_map_elem_t displayio_group_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_hidden), MP_ROM_PTR(&displayio_group_hidden_obj) }, { MP_ROM_QSTR(MP_QSTR_scale), MP_ROM_PTR(&displayio_group_scale_obj) }, @@ -338,6 +353,7 @@ STATIC const mp_rom_map_elem_t displayio_group_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_index), MP_ROM_PTR(&displayio_group_index_obj) }, { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&displayio_group_pop_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&displayio_group_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&displayio_group_sort_obj) }, }; STATIC MP_DEFINE_CONST_DICT(displayio_group_locals_dict, displayio_group_locals_dict_table); -- cgit v1.2.3 From 38fb7b511b7834bdd28bc34a6714ace7df59543b Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sat, 27 Feb 2021 20:51:49 +0100 Subject: Remove max_size from displayio.Group Still accept it as an argument. Add deprecation note. --- shared-bindings/displayio/Group.c | 9 ++------- shared-bindings/displayio/Group.h | 2 +- shared-module/displayio/Group.c | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/displayio/Group.c b/shared-bindings/displayio/Group.c index a7f42d2b4..d61307938 100644 --- a/shared-bindings/displayio/Group.c +++ b/shared-bindings/displayio/Group.c @@ -42,7 +42,7 @@ //| """Create a Group of a given size and scale. Scale is in one dimension. For example, scale=2 //| leads to a layer's pixel being 2x2 pixels when in the group. //| -//| :param int max_size: The maximum group size. +//| :param int max_size: Ignored. Will be removed in 7.x. //| :param int scale: Scale of layer pixels in one dimension. //| :param int x: Initial x position within the parent. //| :param int y: Initial y position within the parent.""" @@ -59,11 +59,6 @@ STATIC mp_obj_t displayio_group_make_new(const mp_obj_type_t *type, size_t n_arg 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_int_t max_size = args[ARG_max_size].u_int; - if (max_size < 1) { - mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_max_size); - } - mp_int_t scale = args[ARG_scale].u_int; if (scale < 1) { mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_scale); @@ -71,7 +66,7 @@ STATIC mp_obj_t displayio_group_make_new(const mp_obj_type_t *type, size_t n_arg displayio_group_t *self = m_new_obj(displayio_group_t); self->base.type = &displayio_group_type; - common_hal_displayio_group_construct(self, max_size, scale, args[ARG_x].u_int, args[ARG_y].u_int); + common_hal_displayio_group_construct(self, scale, args[ARG_x].u_int, args[ARG_y].u_int); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/displayio/Group.h b/shared-bindings/displayio/Group.h index 942a207f2..69c73bf4d 100644 --- a/shared-bindings/displayio/Group.h +++ b/shared-bindings/displayio/Group.h @@ -33,7 +33,7 @@ extern const mp_obj_type_t displayio_group_type; displayio_group_t* native_group(mp_obj_t group_obj); -void common_hal_displayio_group_construct(displayio_group_t* self, uint32_t max_size, uint32_t scale, mp_int_t x, mp_int_t y); +void common_hal_displayio_group_construct(displayio_group_t* self, uint32_t scale, mp_int_t x, mp_int_t y); uint32_t common_hal_displayio_group_get_scale(displayio_group_t* self); void common_hal_displayio_group_set_scale(displayio_group_t* self, uint32_t scale); bool common_hal_displayio_group_get_hidden(displayio_group_t* self); diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 97bb86c9a..9f08eac65 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -35,7 +35,7 @@ #endif -void common_hal_displayio_group_construct(displayio_group_t* self, uint32_t max_size, uint32_t scale, mp_int_t x, mp_int_t y) { +void common_hal_displayio_group_construct(displayio_group_t* self, uint32_t scale, mp_int_t x, mp_int_t y) { mp_obj_list_t *members = mp_obj_new_list(0, NULL); displayio_group_construct(self, members, scale, x, y); } -- cgit v1.2.3 From 24473b79838412e7be0c65e6dfae45146cf15a6b Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sat, 27 Feb 2021 21:13:55 +0100 Subject: Separate out mp_obj_list_insert for use in display.Group Note that for some reason this makes the binary 500 bytes larger! --- py/objlist.c | 20 +++++++++++--------- py/objlist.h | 1 + shared-bindings/displayio/Group.c | 2 +- shared-module/displayio/Group.c | 6 +----- 4 files changed, 14 insertions(+), 15 deletions(-) (limited to 'shared-bindings') diff --git a/py/objlist.c b/py/objlist.c index 309e0e542..7aa4ee89c 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -270,7 +270,7 @@ STATIC mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { return mp_const_none; // return None, as per CPython } -mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { +inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { if (self->len == 0) { mp_raise_IndexError_varg(translate("pop from empty %q"), MP_QSTR_list); } @@ -375,6 +375,15 @@ STATIC mp_obj_t list_index(size_t n_args, const mp_obj_t *args) { return mp_seq_index_obj(self->items, self->len, n_args, args); } +inline void mp_obj_list_insert(mp_obj_list_t *self, size_t index, mp_obj_t obj) { + mp_obj_list_append(MP_OBJ_FROM_PTR(self), mp_const_none); + + for (size_t i = self->len - 1; i > index; --i) { + self->items[i] = self->items[i - 1]; + } + self->items[index] = obj; +} + STATIC mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); @@ -389,14 +398,7 @@ STATIC mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { if ((size_t)index > self->len) { index = self->len; } - - mp_obj_list_append(self_in, mp_const_none); - - for (mp_int_t i = self->len-1; i > index; i--) { - self->items[i] = self->items[i-1]; - } - self->items[index] = obj; - + mp_obj_list_insert(self, index, obj); return mp_const_none; } diff --git a/py/objlist.h b/py/objlist.h index cec23e06c..eb005e81c 100644 --- a/py/objlist.h +++ b/py/objlist.h @@ -37,5 +37,6 @@ typedef struct _mp_obj_list_t { void mp_obj_list_init(mp_obj_list_t *o, size_t n); mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index); +void mp_obj_list_insert(mp_obj_list_t *self, size_t index, mp_obj_t obj); #endif // MICROPY_INCLUDED_PY_OBJLIST_H diff --git a/shared-bindings/displayio/Group.c b/shared-bindings/displayio/Group.c index d61307938..c1c05504d 100644 --- a/shared-bindings/displayio/Group.c +++ b/shared-bindings/displayio/Group.c @@ -333,7 +333,7 @@ STATIC mp_obj_t displayio_group_obj_sort(size_t n_args, const mp_obj_t *pos_args for (size_t i = 1; i < n_args; ++i) { args[i] = pos_args[i]; } - args[0] = self->members; + args[0] = MP_OBJ_FROM_PTR(self->members); return mp_obj_list_sort(n_args, pos_args, kw_args); } MP_DEFINE_CONST_FUN_OBJ_KW(displayio_group_sort_obj, 1, displayio_group_obj_sort); diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 9f08eac65..42efeb18c 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -314,11 +314,7 @@ static void _remove_layer(displayio_group_t* self, size_t index) { void common_hal_displayio_group_insert(displayio_group_t* self, size_t index, mp_obj_t layer) { _add_layer(self, layer); - mp_obj_list_append(self->members, mp_const_none); - for (size_t i = self->members->len - 1; i > index; i--) { - self->members->items[i] = self->members->items[i - 1]; - } - self->members->items[index] = layer; + mp_obj_list_insert(self->members, index, layer); } mp_obj_t common_hal_displayio_group_pop(displayio_group_t* self, size_t index) { -- cgit v1.2.3 From f722df70c86a4aa58c83c4dd75e91ddcd1c97b99 Mon Sep 17 00:00:00 2001 From: ajs256 <67526318+ajs256@users.noreply.github.com> Date: Mon, 1 Mar 2021 09:06:24 -0800 Subject: Fix formatting in SPI docs Close #4293 by changing `..note::` to `.. note::`. --- shared-bindings/busio/SPI.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shared-bindings') diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index e47564c8c..cbc6b5c08 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -57,7 +57,7 @@ //| //| """Construct an SPI object on the given pins. //| -//| ..note:: The SPI peripherals allocated in order of desirability, if possible, +//| .. note:: The SPI peripherals allocated in order of desirability, if possible, //| such as highest speed and not shared use first. For instance, on the nRF52840, //| there is a single 32MHz SPI peripheral, and multiple 8MHz peripherals, //| some of which may also be used for I2C. The 32MHz SPI peripheral is returned -- cgit v1.2.3